From 04b61ac97f21498f597f131485c58f629b69865d Mon Sep 17 00:00:00 2001
From: Sami Kerola
Date: Mon, 13 Apr 2009 17:57:04 +0200
Subject: [PATCH] in-place option that uses copy.c
Cc: address@hidden
---
src/Makefile.am | 3 +
src/unexpand.c | 283 ++++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 244 insertions(+), 42 deletions(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index 9aaf739..d869aaa 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -102,6 +102,7 @@ runcon_LDADD = $(LDADD) $(LIB_SELINUX)
pathchk_LDADD = $(LDADD) $(LIB_EACCESS)
rm_LDADD = $(LDADD) $(LIB_EACCESS)
test_LDADD = $(LDADD) $(LIB_EACCESS)
+unexpand_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX)
# This is for the '[' program. Automake transliterates '[' to '_'.
__LDADD = $(LDADD) $(LIB_EACCESS)
@@ -155,6 +156,7 @@ vdir_LDADD += $(LIB_ACL)
cp_LDADD += $(LIB_ACL) $(LIB_XATTR)
mv_LDADD += $(LIB_ACL) $(LIB_XATTR)
ginstall_LDADD += $(LIB_ACL) $(LIB_XATTR)
+unexpand += $(LIB_ACL) $(LIB_XATTR)
stat_LDADD = $(LDADD) $(LIB_SELINUX)
@@ -265,6 +267,7 @@ mkdir_SOURCES = mkdir.c prog-fprintf.c
rmdir_SOURCES = rmdir.c prog-fprintf.c
uname_SOURCES = uname.c uname-uname.c
+unexpand_SOURCES = unexpand.c $(copy_sources)
arch_SOURCES = uname.c uname-arch.c
md5sum_SOURCES = md5sum.c
diff --git a/src/unexpand.c b/src/unexpand.c
index 93f1ce8..0da7d68 100644
--- a/src/unexpand.c
+++ b/src/unexpand.c
@@ -38,10 +38,16 @@
#include
#include
#include
+#include
+#include
+
#include "system.h"
#include "error.h"
#include "quote.h"
#include "xstrndup.h"
+#include "backupfile.h"
+#include "copy.h"
+#include "cp-hash.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "unexpand"
@@ -85,8 +91,23 @@ static bool have_read_stdin;
/* The desired exit status. */
static int exit_status;
-/* Write back file if true */
-static bool in_place;
+/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
+ present. */
+#ifndef SA_NOCLDSTOP
+# define SA_NOCLDSTOP 0
+/* No sigprocmask. Always 'return' zero. */
+# define sigprocmask(How, Set, Oset) (0)
+# define sigset_t int
+# if ! HAVE_SIGINTERRUPT
+# define siginterrupt(sig, flag) /* empty */
+# endif
+#endif
+
+/* The set of signals that are caught. */
+static sigset_t caught_signals;
+
+/* Write back file(s) if true */
+static bool in_place = false;
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
@@ -101,15 +122,18 @@ struct in_out_files
FILE *infile;
FILE *outfile;
char *temp;
- struct stat st;
};
+struct in_out_files *iofs;
static struct option const longopts[] =
{
{"tabs", required_argument, NULL, 't'},
{"all", no_argument, NULL, 'a'},
{"first-only", no_argument, NULL, CONVERT_FIRST_ONLY_OPTION},
+ {"output", required_argument, NULL, 'o'},
{"in-place", no_argument, NULL, IN_PLACE_OPTION},
+ {"backup", optional_argument, NULL, 'b'},
+ {"suffix", required_argument, NULL, 'S'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -142,16 +166,69 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-t, --tabs=LIST use comma separated LIST of tab positions (enables -a)\n\
"), stdout);
fputs (_("\
- --in-place edit files in place\n\
+ --backup[=CONTROL] make a backup of each existing destination file\n\
+ -b like --backup but does not accept an argument\n\
+ -S, --suffix=SUFFIX override the usual backup suffix\n\
+ --in-place edit files in place\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ fputs (_("\
+\n\
+The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+The version control method may be selected via the --backup option or through\n\
+the VERSION_CONTROL environment variable. Here are the values:\n\
+\n\
+"), stdout);
+ fputs (_("\
+ none, off never make backups (even if --backup is given)\n\
+ numbered, t make numbered backups\n\
+ existing, nil numbered if numbered backups exist, simple otherwise\n\
+ simple, never always make simple backups\n\
+"), stdout);
emit_bug_reporting_address ();
}
exit (status);
}
-/* Add tab stop TABVAL to the end of `tab_list'. */
+static void
+cp_option_init (struct cp_options *x)
+{
+ bool selinux_enabled = (0 < is_selinux_enabled ());
+
+ cp_options_default (x);
+ x->copy_as_regular = false;
+ x->dereference = DEREF_ALWAYS;
+ x->unlink_dest_before_opening = false;
+ x->unlink_dest_after_failed_open = false;
+ x->hard_link = false;
+ x->interactive = I_UNSPECIFIED;
+ x->move_mode = true;
+ x->one_file_system = false;
+ x->preserve_ownership = false;
+ x->preserve_links = true;
+ x->preserve_mode = false;
+ x->preserve_timestamps = false;
+ if (selinux_enabled)
+ x->preserve_security_context = true;
+ x->reduce_diagnostics = false;
+ x->require_preserve = false;
+ x->require_preserve_context = false;
+ x->preserve_xattr = false;
+ x->require_preserve_xattr = false;
+ x->recursive = false;
+ x->sparse_mode = SPARSE_AUTO;
+ x->symbolic_link = false;
+ x->set_mode = false;
+ x->mode = 0;
+ x->stdin_tty = isatty (STDIN_FILENO);
+
+ x->open_dangling_dest_symlink = false;
+ x->update = false;
+ x->verbose = false;
+ x->dest_info = NULL;
+ x->src_info = NULL;
+}
static void
add_tab_stop (uintmax_t tabval)
@@ -251,12 +328,12 @@ validate_tab_stops (uintmax_t const *tabs, size_t entries)
Return NULL if there are no more input files. */
static struct in_out_files *
-next_file (struct in_out_files *iofs)
+next_file (struct in_out_files *iofs, const struct cp_options *x)
{
static char *prev_file;
char *file;
- char pattern[] = "unexpandXXXXXX";
- char *tmpdir = NULL;
+ bool copy_into_self;
+ bool rename_succeeded;
if (iofs->infile)
{
@@ -274,20 +351,18 @@ next_file (struct in_out_files *iofs)
error (0, errno, "%s", prev_file);
exit_status = EXIT_FAILURE;
}
- if (in_place && (fclose (iofs->outfile) != 0))
+ if (in_place)
{
- error (0, errno, "%s", iofs->temp);
- exit_status = EXIT_FAILURE;
- }
- if (rename(iofs->temp, prev_file))
- {
- error (0, errno, "moving a temporary file in place failed");
- exit_status = EXIT_FAILURE;
- }
- if (chmod(prev_file, iofs->st.st_mode))
- {
- error (0, errno, "resetting file mode failed");
- exit_status = EXIT_FAILURE;
+ if(fclose(iofs->outfile))
+ {
+ error (0, errno, "$s", iofs->temp);
+ exit_status = EXIT_FAILURE;
+ }
+ /* Return value of copy is insignificant in this case, the
+ source is always a file. */
+ copy (iofs->temp, prev_file, false, x, ©_into_self,
+ &rename_succeeded);
+ strncpy(iofs->temp, "XXXXXX", strlen(iofs->temp) - 6);
}
}
}
@@ -305,24 +380,11 @@ next_file (struct in_out_files *iofs)
return iofs;
}
iofs->infile = fopen (file, "r");
- fstat(fileno (iofs->infile), &(iofs->st));
if (iofs->infile)
{
prev_file = file;
if (in_place)
{
- if (! ((tmpdir=getenv("TMPDIR"))))
- {
- tmpdir=getenv("TMP");
- }
- if (!tmpdir)
- {
- tmpdir = "/tmp";
- }
- iofs->temp = xmalloc(strlen(tmpdir) + strlen(pattern) + 2);
- strcpy(iofs->temp, tmpdir);
- strcat(iofs->temp, "/");
- strcat(iofs->temp, pattern);
if (mkstemp(iofs->temp) == 0 ||
(iofs->outfile = fopen(iofs->temp, "w")) == NULL)
{
@@ -345,14 +407,32 @@ next_file (struct in_out_files *iofs)
Read each file in `file_list', in order. */
static void
-unexpand (void)
+unexpand (const struct cp_options *x)
{
- struct in_out_files *iofs;
+ char *tmpdir = NULL;
+ char pattern[] = "XXXXXX";
iofs = xmalloc (sizeof(struct in_out_files));
iofs->infile = NULL;
+ if (in_place)
+ {
+ if (! ((tmpdir=getenv("TMPDIR"))))
+ {
+ tmpdir=getenv("TMP");
+ }
+ if (!tmpdir)
+ {
+ tmpdir = "/tmp";
+ }
+ iofs->temp = xmalloc(strlen(tmpdir) + strlen(program_name) +
+ strlen(pattern) + 2);
+ strcpy(iofs->temp, tmpdir);
+ strcat(iofs->temp, "/");
+ strcat(iofs->temp, program_name);
+ strcat(iofs->temp, pattern);
+ }
/* Input stream. */
- iofs = next_file (iofs);
+ iofs = next_file (iofs, x);
/* The array of pending blanks. In non-POSIX locales, blanks can
include characters other than spaces, so the blanks must be
@@ -403,7 +483,7 @@ unexpand (void)
do
{
- while ((c = getc (iofs->infile)) < 0 && (iofs = next_file (iofs)))
+ while ((c = getc (iofs->infile)) < 0 && (iofs = next_file (iofs, x)))
continue;
if (convert)
@@ -511,14 +591,50 @@ unexpand (void)
}
while (c != '\n');
}
- free (iofs->temp);
+}
+
+void
+cleanup(void)
+{
+ struct in_out_files volatile *local = (struct in_out_files *) &iofs;
+ sigset_t sigs;
+
+ /* Try protect entering to cleanup several times. */
+ sigprocmask (SIG_BLOCK, &caught_signals, &sigs);
+
+ if (in_place && local->temp)
+ {
+ /* Usually non-existing is deleted, not very smart. */
+ unlink (local->temp);
+ free (local->temp);
+ }
+ if (iofs)
free (iofs);
+
+ fclose(stdout);
+}
+
+void
+sighandler (int sig)
+{
+ if (! SA_NOCLDSTOP)
+ signal (sig, SIG_IGN);
+
+ cleanup ();
+
+ signal (sig, SIG_DFL);
+ raise(sig);
}
int
main (int argc, char **argv)
{
bool have_tabval = false;
+ bool make_backups = false;
+ char *outfile = NULL;
+ char *backup_suffix_string = NULL;
+ char *version_control_string = NULL;
+ struct cp_options x;
in_place = false;
uintmax_t tabval IF_LINT (= 0);
int c;
@@ -533,7 +649,61 @@ main (int argc, char **argv)
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- atexit (close_stdout);
+ {
+ size_t i;
+ static int const sig[] =
+ {
+ /* The usual suspects. */
+ SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
+#ifdef SIGPOLL
+ SIGPOLL,
+#endif
+#ifdef SIGPROF
+ SIGPROF,
+#endif
+#ifdef SIGVTALRM
+ SIGVTALRM,
+#endif
+#ifdef SIGXCPU
+ SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+ SIGXFSZ,
+#endif
+ };
+ enum { nsigs = sizeof sig / sizeof sig[0] };
+
+#if SA_NOCLDSTOP
+ struct sigaction act;
+
+ sigemptyset (&caught_signals);
+ for (i = 0; i < nsigs; i++)
+ {
+ sigaction (sig[i], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[i]);
+ }
+
+ act.sa_handler = sighandler;
+ act.sa_mask = caught_signals;
+ act.sa_flags = 0;
+
+ for (i = 0; i < nsigs; i++)
+ if (sigismember (&caught_signals, sig[i]))
+ sigaction (sig[i], &act, NULL);
+#else
+ for (i = 0; i < nsigs; i++)
+ if (signal (sig[i], SIG_IGN) != SIG_IGN)
+ {
+ signal (sig[i], sighandler);
+ siginterrupt (sig[i], 1);
+ }
+#endif
+ }
+
+ atexit (cleanup);
+
+ cp_option_init(&x);
have_read_stdin = false;
exit_status = EXIT_SUCCESS;
@@ -541,7 +711,7 @@ main (int argc, char **argv)
tab_list = NULL;
first_free_tab = 0;
- while ((c = getopt_long (argc, argv, ",0123456789at:", longopts, NULL))
+ while ((c = getopt_long (argc, argv, ",0123456789at:o:", longopts, NULL))
!= -1)
{
switch (c)
@@ -551,10 +721,24 @@ main (int argc, char **argv)
case 'a':
convert_entire_line = true;
break;
+ case 'b':
+ make_backups = true;
+ if (optarg)
+ version_control_string = optarg;
+ break;
+ case 'o':
+ if (outfile && !STREQ (outfile, optarg))
+ error (EXIT_FAILURE, 0, _("multiple output files specified"));
+ outfile = optarg;
+ break;
case 't':
convert_entire_line = true;
parse_tab_stops (optarg);
break;
+ case 'S':
+ make_backups = true;
+ backup_suffix_string = optarg;
+ break;
case CONVERT_FIRST_ONLY_OPTION:
convert_first_only = true;
break;
@@ -580,6 +764,21 @@ main (int argc, char **argv)
}
}
+ /* Backups are meaningful only when output is going to a file. */
+ if (in_place)
+ {
+ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
+ if (backup_suffix_string)
+ simple_backup_suffix = xstrdup (backup_suffix_string);
+
+ x.backup_type = (make_backups
+ ? xget_version (_("backup type"),
+ version_control_string)
+ : no_backups);
+ }
+
+ hash_init ();
+
if (convert_first_only)
convert_entire_line = false;
@@ -597,7 +796,7 @@ main (int argc, char **argv)
file_list = (optind < argc ? &argv[optind] : stdin_argv);
- unexpand ();
+ unexpand (&x);
if (have_read_stdin && fclose (stdin) != 0)
error (EXIT_FAILURE, errno, "-");
--
1.6.2