From d2efb1fb0ebf49b06df59f35a2589ced96d4468c Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sun, 7 Nov 2010 20:24:03 +0100 Subject: [PATCH] unexpand: support --in-place option * src/unexpand.c: In-place editing introduced. * lib/in-place.c: A new file, shared in-place functionality. * lib/in-place.h: A new file, header for in-place. * m4/in-place.m4: A new file, necessary m4 macros that the in-place will build. * m4/prereq.m4: Directives for automake to notice in-place files. * src/Makefile.am: The unexpand is using functions copy & remove. * src/system.h: New function emit_backup_info. * src/cp.c: Print emit_backup_info instead of own copy of the message. * src/ln.c: Print emit_backup_info instead of own copy of the message. * src/mv.c: Print emit_backup_info instead of own copy of the message. --- lib/in-place.c | 340 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/in-place.h | 24 ++++ m4/in-place.m4 | 18 +++ m4/prereq.m4 | 1 + src/Makefile.am | 5 + src/cp.c | 20 +--- src/install.c | 14 +-- src/ln.c | 19 +--- src/mv.c | 14 +-- src/system.h | 24 ++++ src/unexpand.c | 107 +++++++++++++---- 11 files changed, 498 insertions(+), 88 deletions(-) create mode 100644 lib/in-place.c create mode 100644 lib/in-place.h create mode 100644 m4/in-place.m4 diff --git a/lib/in-place.c b/lib/in-place.c new file mode 100644 index 0000000..60c292c --- /dev/null +++ b/lib/in-place.c @@ -0,0 +1,340 @@ +/* in-place.c -- support functions for --in-place option + + Copyright (C) 2010 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Sami Kerola. */ + +#include +#include +#include +#include +#include +#include + +#include "in-place.h" +#include "backupfile.h" +#include "../src/copy.h" +#include "../src/cp-hash.h" +#include "../src/remove.h" +#include "root-dev-ino.h" +#include "filenamecat.h" +#include "tempname.h" +#include "quote.h" +#include "error.h" +#include "gettext.h" +#include "closeout.h" +#include "argmatch.h" +#include "xalloc.h" + +#define _(msgid) gettext (msgid) + +/* Path to mktemp file, needed when signal handler does cleanup. */ +const char *in_place_tmpfile; + +/* The set of signals that are caught. */ +static sigset_t caught_signals; + +/* Settings to put mktemp file back in place. */ +struct cp_options x; + +/* Critical section status. */ +struct cs_status +{ + bool valid; + sigset_t sigs; +}; + +/* Enter a critical section. */ +static struct cs_status +cs_enter (void) +{ + struct cs_status status; + status.valid = (sigprocmask (SIG_BLOCK, &caught_signals, &status.sigs) == 0); + return status; +} + +/* Leave a critical section. */ +static void +cs_leave (struct cs_status status) +{ + if (status.valid) + { + /* Ignore failure when restoring the signal mask. */ + sigprocmask (SIG_SETMASK, &status.sigs, NULL); + } +} + +static void +exit_cleanup (void) +{ + if (in_place_tmpfile != NULL) + { + /* Clean up any remaining temporary files in a critical section so + that a signal handler does not try to clean them too. */ + struct cs_status cs = cs_enter (); + unlink(in_place_tmpfile); + cs_leave (cs); + } + + close_stdout (); +} + +/* Handle interrupts and hangups. */ +static void +sighandler (int sig) +{ + if (! SA_NOCLDSTOP) + signal (sig, SIG_IGN); + + exit_cleanup (); + + signal (sig, SIG_DFL); + raise (sig); +} + +static void +cp_option_init (struct cp_options *x) +{ + cp_options_default (x); + x->copy_as_regular = true; + x->reflink_mode = REFLINK_NEVER; + x->dereference = DEREF_NEVER; + x->unlink_dest_before_opening = false; + x->unlink_dest_after_failed_open = false; + x->hard_link = false; + x->interactive = I_UNSPECIFIED; + x->move_mode = false; + x->one_file_system = false; + + x->preserve_ownership = false; + x->preserve_links = false; + x->preserve_mode = false; + x->preserve_timestamps = false; + x->preserve_security_context = false; + x->require_preserve = false; + x->require_preserve_context = false; + x->preserve_xattr = false; + x->require_preserve_xattr = false; + + x->data_copy_required = true; + x->recursive = false; + x->sparse_mode = SPARSE_AUTO; + x->symbolic_link = false; + x->set_mode = false; + x->mode = 0; + x->stdin_tty = false; + + x->open_dangling_dest_symlink = false; + x->update = false; + x->reduce_diagnostics = false; + x->verbose = false; + x->dest_info = NULL; + x->src_info = NULL; +} + +static void +rm_option_init (struct rm_options *x) +{ + x->ignore_missing_files = false; + x->recursive = false; + x->one_file_system = true; + + /* In place should never prompt for removal */ + x->interactive = RMI_NEVER; + x->stdin_tty = false; + + x->verbose = false; + + /* Since this program may well have to process additional command + line arguments after any call to `rm', that function must preserve + the initial working directory, in case one of those is a + `.'-relative name. */ + x->require_restore_cwd = true; + + { + static struct dev_ino dev_ino_buf; + x->root_dev_ino = get_root_dev_ino (&dev_ino_buf); + if (x->root_dev_ino == NULL) + error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), + quote ("/")); + } +} + +void +in_place_init(const struct in_place_options *y) +{ + char *backup_suffix_string; + + cp_option_init (&x); + if (y->make_backups) + { + if (y->backup_suffix_string) + simple_backup_suffix = xstrdup (y->backup_suffix_string); + x.backup_type = xget_version (_("backup type"), + y->version_control_string); + } + else + { + x.backup_type = no_backups; + } + + hash_init (); + + in_place_tmpfile = NULL; + + { + 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 = ARRAY_CARDINALITY (sig) }; + +#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 + } + signal (SIGCHLD, SIG_DFL); /* Don't inherit CHLD handling from parent. */ + + /* The signal mask is known, so it is safe to invoke exit_cleanup. */ + atexit (exit_cleanup); +} + +int +in_place_open(struct iofiles *iofile, const struct in_place_options *y) +{ + char *template = "in-place.XXXXXXXXXX"; + char *dest_dir; + char *dest_name; + char *env = getenv ("TMPDIR"); + int fd; + + iofile->in = fopen (iofile->in_name, "r"); + if (iofile->in == NULL) + { + error (0, errno, "%s", iofile->in_name); + exit (EXIT_FAILURE); + } + + dest_dir = (env && *env ? env : "/tmp"); + dest_name = file_name_concat (dest_dir, template, NULL); + /* Static lengths are not stylish but they work. */ + fd = gen_tempname_len (dest_name, 0, 10, 0, (size_t) 10); + if (fd < 0) + { + error (0, errno, _("failed to create file via template %s"), + quote (template)); + exit (EXIT_FAILURE); + } + + in_place_tmpfile = dest_name; + + iofile->out = fdopen(fd, "w"); + if (iofile->out == NULL) + { + error (0, errno, "%s", dest_name); + return false; + } + iofile->out_name = dest_name; + + return true; +} + +int +in_place_close(struct iofiles *iofile, const struct in_place_options *y) +{ + bool unused; + struct rm_options rm_options; + enum RM_status status; + char const *dir[2]; + + rm_option_init (&rm_options); + rm_options.verbose = x.verbose; + dir[0] = iofile->out_name; + dir[1] = NULL; + + if (fclose(iofile->in)) + { + error (0, errno, "%s", iofile->in_name); + return false; + } + if (fclose(iofile->out)) + { + error (0, errno, "%s", iofile->out_name); + return false; + } + + + /* Return value of the copy is not meaningful for in place. */ + /* FIXME: If backup is asked file permissions are 0600, which + come from mktemp. The permissions should be same as + destination file had. Unfortunately the copy_internal + dst_backup string is not reachable, but if it would copy_acl + could be used. For time being this remains as bug. */ + copy (iofile->out_name, iofile->in_name, false, &x, &unused, &unused); + + /* Why copy instead of moving file? With copy destination + permissions can survice, move will use source permissisons + which in case of mktemp are rather restrictive, and wrong to + be used as default. */ + status = rm ((void*) dir, &rm_options); + assert (VALID_STATUS (status)); + if (status == RM_ERROR) + return false; + + in_place_tmpfile = NULL; + + return true; +} diff --git a/lib/in-place.h b/lib/in-place.h new file mode 100644 index 0000000..cd563b7 --- /dev/null +++ b/lib/in-place.h @@ -0,0 +1,24 @@ +#include +#include + +/* This struct is expected to be globally available if in place + editing is attempted to use. */ +struct iofiles { + char *in_name; + FILE *in; + char *out_name; + FILE *out; +}; + +/* In place editing needs to receive few getopt arguments. */ +struct in_place_options +{ + bool is_in_place_called; + bool make_backups; + char *backup_suffix_string; + char *version_control_string; +}; + +void in_place_init (const struct in_place_options *); +int in_place_open (struct iofiles *, const struct in_place_options *); +int in_place_close (struct iofiles *, const struct in_place_options *); diff --git a/m4/in-place.m4 b/m4/in-place.m4 new file mode 100644 index 0000000..a564934 --- /dev/null +++ b/m4/in-place.m4 @@ -0,0 +1,18 @@ +# For in-place option + +dnl Copyright (C) 2010 Free Software Foundation, Inc. + +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Written by Sami Kerola. + +AC_DEFUN([gl_IN_PLACE], +[ + AC_LIBSOURCES([in-place.c, in-place.h]) + AC_LIBOBJ([in-place]) + + dnl Prerequisites of lib/in-place.c. + AC_REQUIRE([AC_INLINE]) +]) diff --git a/m4/prereq.m4 b/m4/prereq.m4 index 8caea38..731f16e 100644 --- a/m4/prereq.m4 +++ b/m4/prereq.m4 @@ -42,4 +42,5 @@ AC_DEFUN([gl_PREREQ], AC_REQUIRE([gl_FUNC_XFTS]) AC_REQUIRE([gl_STRINTCMP]) AC_REQUIRE([gl_STRNUMCMP]) + AC_REQUIRE([gl_IN_PLACE]) ]) diff --git a/src/Makefile.am b/src/Makefile.am index 00c7ff7..4f05950 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -299,10 +299,12 @@ copy_LDADD = cp_LDADD += $(copy_LDADD) ginstall_LDADD += $(copy_LDADD) mv_LDADD += $(copy_LDADD) +unexpand_LDADD += $(copy_LDADD) remove_LDADD = mv_LDADD += $(remove_LDADD) rm_LDADD += $(remove_LDADD) +unexpand_LDADD += $(remove_LDADD) # for eaccess, euidaccess copy_LDADD += $(LIB_EACCESS) @@ -320,6 +322,7 @@ mkfifo_LDADD += $(LIB_SELINUX) mknod_LDADD += $(LIB_SELINUX) runcon_LDADD += $(LIB_SELINUX) stat_LDADD += $(LIB_SELINUX) +unexpand_LDADD += $(LIB_SELINUX) # for gettime, settime, utimecmp, utimens copy_LDADD += $(LIB_CLOCK_GETTIME) @@ -501,6 +504,8 @@ sha512sum_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS) ginstall_CPPFLAGS = -DENABLE_MATCHPATHCON=1 $(AM_CPPFLAGS) +unexpand_SOURCES = unexpand.c remove.c $(copy_sources) + # Ensure we don't link against libcoreutils.a as that lib is # not compiled with -fPIC which causes issues on 64 bit at least libstdbuf_so_LDADD = diff --git a/src/cp.c b/src/cp.c index 5b14f3a..7c7bec1 100644 --- a/src/cp.c +++ b/src/cp.c @@ -241,25 +241,7 @@ When --reflink[=always] is specified, perform a lightweight copy, where the\n\ data blocks are copied only when modified. If this is not possible the copy\n\ fails, or if --reflink=auto is specified, fall back to a standard copy.\n\ "), 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); - fputs (_("\ -\n\ -As a special case, cp makes a backup of SOURCE when the force and backup\n\ -options are given and SOURCE and DEST are the same name for an existing,\n\ -regular file.\n\ -"), stdout); + emit_backup_info (); emit_ancillary_info (); } exit (status); diff --git a/src/install.c b/src/install.c index 467e500..fede4b3 100644 --- a/src/install.c +++ b/src/install.c @@ -991,19 +991,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\ 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_backup_info (); emit_ancillary_info (); } exit (status); diff --git a/src/ln.c b/src/ln.c index 672964c..c90ff81 100644 --- a/src/ln.c +++ b/src/ln.c @@ -378,24 +378,7 @@ Mandatory arguments to long options are mandatory for short options too.\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); - printf (_("\ -Using -s ignores -L and -P. Otherwise, the last option specified controls\n\ -behavior when the source is a symbolic link, defaulting to %s.\n\ -\n\ -"), LINK_FOLLOWS_SYMLINKS ? "-L" : "-P"); - 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_backup_info (); emit_ancillary_info (); } exit (status); diff --git a/src/mv.c b/src/mv.c index 91aadfa..d700e26 100644 --- a/src/mv.c +++ b/src/mv.c @@ -318,19 +318,7 @@ If you specify more than one of -i, -f, -n, only the final one takes effect.\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_backup_info (); emit_ancillary_info (); } exit (status); diff --git a/src/system.h b/src/system.h index 9e14681..506e228 100644 --- a/src/system.h +++ b/src/system.h @@ -591,6 +591,30 @@ Otherwise, units default to 1024 bytes (or 512 if POSIXLY_CORRECT is set).\n\ } static inline void +emit_backup_info (void) +{ + 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); + fputs (_("\ +\n\ +As a special case, cp makes a backup of SOURCE when the force and backup\n\ +options are given and SOURCE and DEST are the same name for an existing,\n\ +regular file.\n\ +"), stdout); +} + +static inline void emit_ancillary_info (void) { printf (_("\nReport %s bugs to %s\n"), last_component (program_name), diff --git a/src/unexpand.c b/src/unexpand.c index 14b8df0..e05e479 100644 --- a/src/unexpand.c +++ b/src/unexpand.c @@ -44,6 +44,7 @@ #include "fadvise.h" #include "quote.h" #include "xstrndup.h" +#include "in-place.h" /* The official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "unexpand" @@ -54,6 +55,9 @@ read on the line. */ static bool convert_entire_line; +/* In and output files and streams. */ +struct iofiles iofile; + /* If nonzero, the size of all tab stops. If zero, use `tab_list' instead. */ static size_t tab_size; @@ -91,7 +95,8 @@ static int exit_status; non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum { - CONVERT_FIRST_ONLY_OPTION = CHAR_MAX + 1 + CONVERT_FIRST_ONLY_OPTION = CHAR_MAX + 1, + IN_PLACE_OPTION }; static struct option const longopts[] = @@ -99,6 +104,9 @@ static struct option const longopts[] = {"tabs", required_argument, NULL, 't'}, {"all", no_argument, NULL, 'a'}, {"first-only", no_argument, NULL, CONVERT_FIRST_ONLY_OPTION}, + {"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} @@ -129,9 +137,14 @@ Mandatory arguments to long options are mandatory for short options too.\n\ --first-only convert only leading sequences of blanks (overrides -a)\n\ -t, --tabs=N have tabs N characters apart instead of 8 (enables -a)\n\ -t, --tabs=LIST use comma separated LIST of tab positions (enables -a)\n\ + --in-place edit files in place\n\ + --backup[=CONTROL] make a backup of each in place edited file\n\ + -b like --backup but does not accept an argument\n\ + -S, --suffix=SUFFIX override the usual backup suffix\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); + emit_backup_info (); emit_ancillary_info (); } exit (status); @@ -236,64 +249,85 @@ validate_tab_stops (uintmax_t const *tabs, size_t entries) Open a filename of `-' as the standard input. Return NULL if there are no more input files. */ -static FILE * -next_file (FILE *fp) +bool +next_file(struct iofiles *iofile, struct in_place_options *y) { static char *prev_file; char *file; - if (fp) + if (iofile->in) { - if (ferror (fp)) + if (ferror (iofile->in)) { error (0, errno, "%s", prev_file); exit_status = EXIT_FAILURE; } if (STREQ (prev_file, "-")) - clearerr (fp); /* Also clear EOF. */ - else if (fclose (fp) != 0) + clearerr (iofile->in); /* Also clear EOF. */ + else if (y->is_in_place_called) + { + if ((in_place_close(iofile, y)) != 0) + exit_status = EXIT_FAILURE; + } + else if (fclose (iofile->in) != 0) { error (0, errno, "%s", prev_file); exit_status = EXIT_FAILURE; } } - while ((file = *file_list++) != NULL) + while ((iofile->in_name = *file_list++) != NULL) { - if (STREQ (file, "-")) + if (STREQ (iofile->in_name, "-")) { have_read_stdin = true; - fp = stdin; + iofile->in = stdin; + if (y->is_in_place_called) + { + fprintf (stderr, + _("warning: reading from stdin, in-place editing is turned off\n")); + y->is_in_place_called = false; + } + } + else if (y->is_in_place_called) + { + if ((in_place_open(iofile, y)) != 0) + exit_status = EXIT_FAILURE; } else - fp = fopen (file, "r"); - if (fp) + iofile->in = fopen (iofile->in_name, "r"); + if (iofile->in) { - prev_file = file; - fadvise (fp, FADVISE_SEQUENTIAL); - return fp; + prev_file = iofile->in_name; + fadvise (iofile->in, FADVISE_SEQUENTIAL); + return true; } error (0, errno, "%s", file); exit_status = EXIT_FAILURE; } - return NULL; + return false; } /* Change blanks to tabs, writing to stdout. Read each file in `file_list', in order. */ static void -unexpand (void) +unexpand (struct in_place_options *y) { - /* Input stream. */ - FILE *fp = next_file (NULL); + /* Input and output file handles and paths. */ + iofile.in_name = NULL; + iofile.in = NULL; + iofile.out_name = NULL; + iofile.out = stdout; + + next_file (&iofile, y); /* The array of pending blanks. In non-POSIX locales, blanks can include characters other than spaces, so the blanks must be stored, not merely counted. */ char *pending_blank; - if (!fp) + if (!iofile.in) return; /* The worst case is a non-blank character, then one blank, then a @@ -338,7 +372,8 @@ unexpand (void) do { - while ((c = getc (fp)) < 0 && (fp = next_file (fp))) + while ((c = getc (iofile.in)) < 0 && + (next_file (&iofile, y))) continue; if (convert) @@ -425,7 +460,7 @@ unexpand (void) if (pending) { - if (fwrite (pending_blank, 1, pending, stdout) != pending) + if (fwrite (pending_blank, 1, pending, iofile.out) != pending) error (EXIT_FAILURE, errno, _("write error")); pending = 0; one_blank_before_tab_stop = false; @@ -441,7 +476,7 @@ unexpand (void) return; } - if (putchar (c) < 0) + if (fputc (c, iofile.out) < 0) error (EXIT_FAILURE, errno, _("write error")); } while (c != '\n'); @@ -452,8 +487,13 @@ int main (int argc, char **argv) { bool have_tabval = false; + bool make_backups = false; uintmax_t tabval IF_LINT ( = 0); int c; + struct in_place_options y; + y.is_in_place_called = false; + y.backup_suffix_string = NULL; + y.version_control_string = NULL; /* If true, cancel the effect of any -a (explicit or implicit in -t), so that only leading blanks will be considered. */ @@ -473,7 +513,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, ",0123456789abt:S:", longopts, NULL)) != -1) { switch (c) @@ -490,6 +530,20 @@ main (int argc, char **argv) case CONVERT_FIRST_ONLY_OPTION: convert_first_only = true; break; + case IN_PLACE_OPTION: + y.is_in_place_called = true; + break; + case 'b': + y.make_backups = true; + if (optarg) + y.version_control_string = optarg; + if (y.backup_suffix_string == NULL) + y.backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); + break; + case 'S': + y.make_backups = true; + y.backup_suffix_string = optarg; + break; case ',': if (have_tabval) add_tab_stop (tabval); @@ -509,6 +563,9 @@ main (int argc, char **argv) } } + if (y.is_in_place_called) + in_place_init (&y); + if (convert_first_only) convert_entire_line = false; @@ -526,7 +583,7 @@ main (int argc, char **argv) file_list = (optind < argc ? &argv[optind] : stdin_argv); - unexpand (); + unexpand (&y); if (have_read_stdin && fclose (stdin) != 0) error (EXIT_FAILURE, errno, "-"); -- 1.7.3.2