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