maint: update all copyright year number ranges
[platform/upstream/coreutils.git] / src / tac.c
index 2898199..20a45bf 100644 (file)
--- a/src/tac.c
+++ b/src/tac.c
@@ -1,5 +1,5 @@
 /* tac - concatenate and print files in reverse
-   Copyright (C) 1988-1991, 1995-2006, 2008-2011 Free Software Foundation, Inc.
+   Copyright (C) 1988-2013 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
@@ -51,7 +51,7 @@ tac -r -s '.\|
 #include "stdlib--.h"
 #include "xfreopen.h"
 
-/* The official name of this program (e.g., no `g' prefix).  */
+/* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "tac"
 
 #define AUTHORS \
@@ -83,32 +83,32 @@ static char const *separator;
 /* True if we have ever read standard input.  */
 static bool have_read_stdin = false;
 
-/* If true, print `separator' along with the record preceding it
+/* If true, print 'separator' along with the record preceding it
    in the file; otherwise with the record following it. */
 static bool separator_ends_record;
 
-/* 0 if `separator' is to be matched as a regular expression;
-   otherwise, the length of `separator', used as a sentinel to
+/* 0 if 'separator' is to be matched as a regular expression;
+   otherwise, the length of 'separator', used as a sentinel to
    stop the search. */
 static size_t sentinel_length;
 
-/* The length of a match with `separator'.  If `sentinel_length' is 0,
-   `match_length' is computed every time a match succeeds;
-   otherwise, it is simply the length of `separator'. */
+/* The length of a match with 'separator'.  If 'sentinel_length' is 0,
+   'match_length' is computed every time a match succeeds;
+   otherwise, it is simply the length of 'separator'. */
 static size_t match_length;
 
 /* The input buffer. */
 static char *G_buffer;
 
-/* The number of bytes to read at once into `buffer'. */
+/* The number of bytes to read at once into 'buffer'. */
 static size_t read_size;
 
-/* The size of `buffer'.  This is read_size * 2 + sentinel_length + 2.
-   The extra 2 bytes allow `past_end' to have a value beyond the
-   end of `G_buffer' and `match_start' to run off the front of `G_buffer'. */
+/* The size of 'buffer'.  This is read_size * 2 + sentinel_length + 2.
+   The extra 2 bytes allow 'past_end' to have a value beyond the
+   end of 'G_buffer' and 'match_start' to run off the front of 'G_buffer'. */
 static size_t G_buffer_size;
 
-/* The compiled regular expression representing `separator'. */
+/* The compiled regular expression representing 'separator'. */
 static struct re_pattern_buffer compiled_separator;
 static char compiled_separator_fastmap[UCHAR_MAX + 1];
 static struct re_registers regs;
@@ -127,8 +127,7 @@ void
 usage (int status)
 {
   if (status != EXIT_SUCCESS)
-    fprintf (stderr, _("Try `%s --help' for more information.\n"),
-             program_name);
+    emit_try_help ();
   else
     {
       printf (_("\
@@ -194,21 +193,21 @@ output (const char *start, const char *past_end)
 static bool
 tac_seekable (int input_fd, const char *file)
 {
-  /* Pointer to the location in `G_buffer' where the search for
+  /* Pointer to the location in 'G_buffer' where the search for
      the next separator will begin. */
   char *match_start;
 
-  /* Pointer to one past the rightmost character in `G_buffer' that
+  /* Pointer to one past the rightmost character in 'G_buffer' that
      has not been printed yet. */
   char *past_end;
 
-  /* Length of the record growing in `G_buffer'. */
+  /* Length of the record growing in 'G_buffer'. */
   size_t saved_record_size;
 
   /* Offset in the file of the next read. */
   off_t file_pos;
 
-  /* True if `output' has not been called yet for any file.
+  /* True if 'output' has not been called yet for any file.
      Only used when the separator is attached to the preceding record. */
   bool first_time = true;
   char first_char = *separator;        /* Speed optimization, non-regexp. */
@@ -221,7 +220,7 @@ tac_seekable (int input_fd, const char *file)
     return true;                       /* It's an empty file. */
 
   /* Arrange for the first read to lop off enough to leave the rest of the
-     file a multiple of `read_size'.  Since `read_size' can change, this may
+     file a multiple of 'read_size'.  Since 'read_size' can change, this may
      not always hold during the program run, but since it usually will, leave
      it here for i/o efficiency (page/sector boundaries and all that).
      Note: the efficiency gain has not been verified. */
@@ -229,7 +228,7 @@ tac_seekable (int input_fd, const char *file)
   if (saved_record_size == 0)
     saved_record_size = read_size;
   file_pos -= saved_record_size;
-  /* `file_pos' now points to the start of the last (probably partial) block
+  /* 'file_pos' now points to the start of the last (probably partial) block
      in the input file. */
 
   if (lseek (input_fd, file_pos, SEEK_SET) < 0)
@@ -248,12 +247,12 @@ tac_seekable (int input_fd, const char *file)
 
   while (true)
     {
-      /* Search backward from `match_start' - 1 to `G_buffer' for a match
-         with `separator'; for speed, use strncmp if `separator' contains no
+      /* Search backward from 'match_start' - 1 to 'G_buffer' for a match
+         with 'separator'; for speed, use strncmp if 'separator' contains no
          metacharacters.
-         If the match succeeds, set `match_start' to point to the start of
-         the match and `match_length' to the length of the match.
-         Otherwise, make `match_start' < `G_buffer'. */
+         If the match succeeds, set 'match_start' to point to the start of
+         the match and 'match_length' to the length of the match.
+         Otherwise, make 'match_start' < 'G_buffer'. */
       if (sentinel_length == 0)
         {
           size_t i = match_start - G_buffer;
@@ -282,15 +281,15 @@ tac_seekable (int input_fd, const char *file)
         }
       else
         {
-          /* `match_length' is constant for non-regexp boundaries. */
+          /* 'match_length' is constant for non-regexp boundaries. */
           while (*--match_start != first_char
                  || (match_length1 && strncmp (match_start + 1, separator1,
                                                match_length1)))
             /* Do nothing. */ ;
         }
 
-      /* Check whether we backed off the front of `G_buffer' without finding
-         a match for `separator'. */
+      /* Check whether we backed off the front of 'G_buffer' without finding
+         a match for 'separator'. */
       if (match_start < G_buffer)
         {
           if (file_pos == 0)
@@ -303,10 +302,10 @@ tac_seekable (int input_fd, const char *file)
           saved_record_size = past_end - G_buffer;
           if (saved_record_size > read_size)
             {
-              /* `G_buffer_size' is about twice `read_size', so since
-                 we want to read in another `read_size' bytes before
-                 the data already in `G_buffer', we need to increase
-                 `G_buffer_size'. */
+              /* 'G_buffer_size' is about twice 'read_size', so since
+                 we want to read in another 'read_size' bytes before
+                 the data already in 'G_buffer', we need to increase
+                 'G_buffer_size'. */
               char *newbuffer;
               size_t offset = sentinel_length ? sentinel_length : 1;
               ptrdiff_t match_start_offset = match_start - G_buffer;
@@ -340,7 +339,7 @@ tac_seekable (int input_fd, const char *file)
              The source and destination regions probably overlap.  */
           memmove (G_buffer + read_size, G_buffer, saved_record_size);
           past_end = G_buffer + read_size + saved_record_size;
-          /* For non-regexp searches, avoid unneccessary scanning. */
+          /* For non-regexp searches, avoid unnecessary scanning. */
           if (sentinel_length)
             match_start = G_buffer + read_size;
           else
@@ -354,12 +353,12 @@ tac_seekable (int input_fd, const char *file)
         }
       else
         {
-          /* Found a match of `separator'. */
+          /* Found a match of 'separator'. */
           if (separator_ends_record)
             {
               char *match_end = match_start + match_length;
 
-              /* If this match of `separator' isn't at the end of the
+              /* If this match of 'separator' isn't at the end of the
                  file, print the record. */
               if (!first_time || match_end != past_end)
                 output (match_end, past_end);
@@ -418,56 +417,84 @@ record_or_unlink_tempfile (char const *fn, FILE *fp ATTRIBUTE_UNUSED)
 
 #endif
 
-/* Copy from file descriptor INPUT_FD (corresponding to the named FILE) to
-   a temporary file, and set *G_TMP and *G_TEMPFILE to the resulting stream
-   and file name.  Return true if successful.  */
-
+/* A wrapper around mkstemp that gives us both an open stream pointer,
+   FP, and the corresponding FILE_NAME.  Always return the same FP/name
+   pair, rewinding/truncating it upon each reuse.  */
 static bool
-copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
+temp_stream (FILE **fp, char **file_name)
 {
-  static char *template = NULL;
-  static char const *tempdir;
-
-  if (template == NULL)
+  static char *tempfile = NULL;
+  static FILE *tmp_fp;
+  if (tempfile == NULL)
     {
-      char const * const Template = "tacXXXXXX";
-      tempdir = getenv ("TMPDIR");
+      char const *t = getenv ("TMPDIR");
+      char const *tempdir = t ? t : DEFAULT_TMPDIR;
+      tempfile = mfile_name_concat (tempdir, "tacXXXXXX", NULL);
       if (tempdir == NULL)
-        tempdir = DEFAULT_TMPDIR;
+        {
+          error (0, 0, _("memory exhausted"));
+          return false;
+        }
 
-      template = file_name_concat (tempdir, Template, NULL);
-    }
+      /* FIXME: there's a small window between a successful mkstemp call
+         and the unlink that's performed by record_or_unlink_tempfile.
+         If we're interrupted in that interval, this code fails to remove
+         the temporary file.  On systems that define DONT_UNLINK_WHILE_OPEN,
+         the window is much larger -- it extends to the atexit-called
+         unlink_tempfile.
+         FIXME: clean up upon fatal signal.  Don't block them, in case
+         $TMPFILE is a remote file system.  */
 
-  /* FIXME: there's a small window between a successful mkstemp call
-     and the unlink that's performed by record_or_unlink_tempfile.
-     If we're interrupted in that interval, this code fails to remove
-     the temporary file.  On systems that define DONT_UNLINK_WHILE_OPEN,
-     the window is much larger -- it extends to the atexit-called
-     unlink_tempfile.
-     FIXME: clean up upon fatal signal.  Don't block them, in case
-     $TMPFILE is a remote file system.  */
-
-  char *tempfile = xstrdup (template);
-  int fd = mkstemp (tempfile);
-  if (fd < 0)
-    {
-      error (0, errno, _("cannot create temporary file in %s"),
-             quote (tempdir));
-      free (tempfile);
-      return false;
-    }
+      int fd = mkstemp (tempfile);
+      if (fd < 0)
+        {
+          error (0, errno, _("failed to create temporary file in %s"),
+                 quote (tempdir));
+          goto Reset;
+        }
 
-  FILE *tmp = fdopen (fd, (O_BINARY ? "w+b" : "w+"));
-  if (! tmp)
+      tmp_fp = fdopen (fd, (O_BINARY ? "w+b" : "w+"));
+      if (! tmp_fp)
+        {
+          error (0, errno, _("failed to open %s for writing"),
+                 quote (tempfile));
+          close (fd);
+          unlink (tempfile);
+        Reset:
+          free (tempfile);
+          tempfile = NULL;
+          return false;
+        }
+
+      record_or_unlink_tempfile (tempfile, tmp_fp);
+    }
+  else
     {
-      error (0, errno, _("cannot open %s for writing"), quote (tempfile));
-      close (fd);
-      unlink (tempfile);
-      free (tempfile);
-      return false;
+      if (fseeko (tmp_fp, 0, SEEK_SET) < 0
+          || ftruncate (fileno (tmp_fp), 0) < 0)
+        {
+          error (0, errno, _("failed to rewind stream for %s"),
+                 quote (tempfile));
+          return false;
+        }
     }
 
-  record_or_unlink_tempfile (tempfile, tmp);
+  *fp = tmp_fp;
+  *file_name = tempfile;
+  return true;
+}
+
+/* Copy from file descriptor INPUT_FD (corresponding to the named FILE) to
+   a temporary file, and set *G_TMP and *G_TEMPFILE to the resulting stream
+   and file name.  Return true if successful.  */
+
+static bool
+copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
+{
+  FILE *fp;
+  char *file_name;
+  if (!temp_stream (&fp, &file_name))
+    return false;
 
   while (1)
     {
@@ -480,26 +507,25 @@ copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
           goto Fail;
         }
 
-      if (fwrite (G_buffer, 1, bytes_read, tmp) != bytes_read)
+      if (fwrite (G_buffer, 1, bytes_read, fp) != bytes_read)
         {
-          error (0, errno, _("%s: write error"), quotearg_colon (tempfile));
+          error (0, errno, _("%s: write error"), quotearg_colon (file_name));
           goto Fail;
         }
     }
 
-  if (fflush (tmp) != 0)
+  if (fflush (fp) != 0)
     {
-      error (0, errno, _("%s: write error"), quotearg_colon (tempfile));
+      error (0, errno, _("%s: write error"), quotearg_colon (file_name));
       goto Fail;
     }
 
-  *g_tmp = tmp;
-  *g_tempfile = tempfile;
+  *g_tmp = fp;
+  *g_tempfile = file_name;
   return true;
 
  Fail:
-  fclose (tmp);
-  free (tempfile);
+  fclose (fp);
   return false;
 }
 
@@ -511,16 +537,11 @@ tac_nonseekable (int input_fd, const char *file)
 {
   FILE *tmp_stream;
   char *tmp_file;
-  if (copy_to_temp (&tmp_stream, &tmp_file, input_fd, file))
-    {
-      if (tac_seekable (fileno (tmp_stream), tmp_file))
-        {
-          free (tmp_file);
-          return true;
-        }
-    }
+  if (!copy_to_temp (&tmp_stream, &tmp_file, input_fd, file))
+    return false;
 
-  return false;
+  bool ok = tac_seekable (fileno (tmp_stream), tmp_file);
+  return ok;
 }
 
 /* Print FILE in reverse, copying it to a temporary
@@ -548,7 +569,8 @@ tac_file (const char *filename)
       fd = open (filename, O_RDONLY | O_BINARY);
       if (fd < 0)
         {
-          error (0, errno, _("cannot open %s for reading"), quote (filename));
+          error (0, errno, _("failed to open %s for reading"),
+                 quote (filename));
           return false;
         }
     }
@@ -642,7 +664,7 @@ main (int argc, char **argv)
   G_buffer = xmalloc (G_buffer_size);
   if (sentinel_length)
     {
-      strcpy (G_buffer, separator);
+      memcpy (G_buffer, separator, sentinel_length + 1);
       G_buffer += sentinel_length;
     }
   else