From ef6192da066dcf3c65bc9a672ecf78c30894ac95 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Sat, 21 Mar 2009 18:23:04 +0100 Subject: [PATCH] in place option for unexpand Cc: address@hidden --- src/unexpand.c | 116 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 95 insertions(+), 21 deletions(-) diff --git a/src/unexpand.c b/src/unexpand.c index 9f6a6d7..93f1ce8 100644 --- a/src/unexpand.c +++ b/src/unexpand.c @@ -85,11 +85,23 @@ static bool have_read_stdin; /* The desired exit status. */ static int exit_status; +/* Write back file if true */ +static bool in_place; + /* For long options that have no equivalent short option, use a 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 +}; + +struct in_out_files +{ + FILE *infile; + FILE *outfile; + char *temp; + struct stat st; }; static struct option const longopts[] = @@ -97,6 +109,7 @@ 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}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -128,6 +141,9 @@ Mandatory arguments to long options are mandatory for short options too.\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\ "), stdout); + fputs (_("\ + --in-place edit files in place\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); emit_bug_reporting_address (); @@ -234,41 +250,90 @@ 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) +static struct in_out_files * +next_file (struct in_out_files *iofs) { static char *prev_file; char *file; + char pattern[] = "unexpandXXXXXX"; + char *tmpdir = NULL; - if (fp) + if (iofs->infile) { - if (ferror (fp)) + if (ferror (iofs->infile)) { 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 (iofs->infile); /* Also clear EOF. */ + else { - error (0, errno, "%s", prev_file); - exit_status = EXIT_FAILURE; - } + if (fclose (iofs->infile) != 0) + { + error (0, errno, "%s", prev_file); + exit_status = EXIT_FAILURE; + } + if (in_place && (fclose (iofs->outfile) != 0)) + { + 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; + } + } } while ((file = *file_list++) != NULL) { if (STREQ (file, "-")) { + if (in_place) + error (EXIT_FAILURE, 0, _("in place and reading stdin cannot be combined")); have_read_stdin = true; prev_file = file; - return stdin; + iofs->infile = stdin; + iofs->outfile = stdout; + return iofs; } - fp = fopen (file, "r"); - if (fp) + iofs->infile = fopen (file, "r"); + fstat(fileno (iofs->infile), &(iofs->st)); + if (iofs->infile) { prev_file = file; - return fp; + 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) + { + error (EXIT_FAILURE, 0, _("creating a tmpfile failed")); + } + } + else + { + iofs->outfile = stdout; + } + return iofs; } error (0, errno, "%s", file); exit_status = EXIT_FAILURE; @@ -276,21 +341,25 @@ next_file (FILE *fp) return NULL; } -/* Change blanks to tabs, writing to stdout. +/* Change blanks to tabs, writing to stdout or a file if in place defined. Read each file in `file_list', in order. */ static void unexpand (void) { + struct in_out_files *iofs; + iofs = xmalloc (sizeof(struct in_out_files)); + iofs->infile = NULL; + /* Input stream. */ - FILE *fp = next_file (NULL); + iofs = next_file (iofs); /* 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 (!iofs->infile) return; /* The worst case is a non-blank character, then one blank, then a @@ -306,7 +375,6 @@ unexpand (void) /* If true, perform translations. */ bool convert = true; - /* The following variables have valid values only when CONVERT is true: */ @@ -335,7 +403,7 @@ unexpand (void) do { - while ((c = getc (fp)) < 0 && (fp = next_file (fp))) + while ((c = getc (iofs->infile)) < 0 && (iofs = next_file (iofs))) continue; if (convert) @@ -422,7 +490,7 @@ unexpand (void) if (pending) { - if (fwrite (pending_blank, 1, pending, stdout) != pending) + if (fwrite (pending_blank, 1, pending, iofs->outfile) != pending) error (EXIT_FAILURE, errno, _("write error")); pending = 0; one_blank_before_tab_stop = false; @@ -438,17 +506,20 @@ unexpand (void) return; } - if (putchar (c) < 0) + if (putc(c, iofs->outfile) < 0) error (EXIT_FAILURE, errno, _("write error")); } while (c != '\n'); } + free (iofs->temp); + free (iofs); } int main (int argc, char **argv) { bool have_tabval = false; + in_place = false; uintmax_t tabval IF_LINT (= 0); int c; @@ -487,6 +558,9 @@ main (int argc, char **argv) case CONVERT_FIRST_ONLY_OPTION: convert_first_only = true; break; + case IN_PLACE_OPTION: + in_place = true; + break; case ',': if (have_tabval) add_tab_stop (tabval); -- 1.6.2