bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

tail fixes for conformance to POSIX 1003.2-1992


From: Paul Eggert
Subject: tail fixes for conformance to POSIX 1003.2-1992
Date: Fri, 10 Sep 2004 13:57:41 -0700
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

The recent discussion in austin-review-l caused me to look again at
"tail", and I found that it didn't conform to POSIX 1003.2-1992
correctly in obscure cases like "tail - file".  I installed this
patch.

2004-09-10  Paul Eggert  <address@hidden>

        * NEWS: "tail" now handles obscure POSIX 1003.2-1992 cases better.
        * src/tail.c (parse_obsolete_option): Renamed from
        parse_obsolescent_option, since the options are obsolete now.
        Remove bool *arg; just exit if there's an error.  Revamp to follow
        POSIX 1003.2-1992 more precisely, to handle cases like "tail -
        file" and "tail -10 -- file" correctly when we are conforming to
        the older standard.
        (main): Adjust to this change.
        * tests/tail/Test.pm (test_vector): minus-* requires
        _POSIX2_VERSION=199209 now, to work correctly if there is
        an input file.  err-1 and err-3 no longer errors if there
        is another file.

Index: NEWS
===================================================================
RCS file: /home/eggert/coreutils/cu/NEWS,v
retrieving revision 1.233
diff -p -u -r1.233 NEWS
--- NEWS        9 Sep 2004 00:27:45 -0000       1.233
+++ NEWS        10 Sep 2004 20:52:35 -0000
@@ -98,6 +98,10 @@ GNU coreutils NEWS                      
 
   "tail -f" no longer mishandles pipes and fifos.  With no operands,
   tail now ignores -f if standard input is a pipe, as POSIX requires.
+  When conforming to POSIX 1003.2-1992, tail now handles some obscure
+  cases more correctly, e.g., "tail +cl file" now reads the file "+cl"
+  rather than reporting an error, and "tail - file" no longer reads
+  standard input.
 
   tee now exits when it gets a SIGPIPE signal, as POSIX requires.
   To get tee's old behavior, use the shell command "(trap '' PIPE; tee)".
Index: src/tail.c
===================================================================
RCS file: /home/eggert/coreutils/cu/src/tail.c,v
retrieving revision 1.226
diff -p -u -r1.226 tail.c
--- src/tail.c  2 Aug 2004 22:20:24 -0000       1.226
+++ src/tail.c  10 Sep 2004 20:36:53 -0000
@@ -1359,147 +1359,107 @@ tail_file (struct File_spec *f, uintmax_
   return ok;
 }
 
-/* If the command line arguments are of the obsolescent form and the
-   option string is well-formed, set *OK to true, set *N_UNITS, the
-   globals COUNT_LINES, FOREVER, and FROM_START, and return true.
-   Otherwise, if the command line arguments appear to be of the
-   obsolescent form but the option string is malformed, set *OK to
-   false, don't modify any other parameter or global variable, and
-   return true.  Otherwise, return false and don't modify any parameter
-   or global variable.  */
+/* If obsolete usage is allowed, and the command line arguments are of
+   the obsolete form and the option string is well-formed, set
+   *N_UNITS, the globals COUNT_LINES, FOREVER, and FROM_START, and
+   return true.  If the command line arguments are obviously incorrect
+   (e.g., because obsolete usage is not allowed and the arguments are
+   incorrect for non-obsolete usage), report an error and exit.
+   Otherwise, return false and don't modify any parameter or global
+   variable.  */
 
 static bool
-parse_obsolescent_option (int argc, const char *const *argv,
-                         uintmax_t *n_units, bool *ok)
+parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
 {
   const char *p = argv[1];
-  const char *n_string = NULL;
+  const char *n_string;
   const char *n_string_end;
   bool obsolete_usage;
-
   bool t_from_start;
-  bool t_count_lines;
-  bool t_forever;
-
-  /* With the obsolescent form, there is one option string and
-     (technically) at most one file argument.  But we allow two or more
-     by default.  */
-  if (argc < 2)
+  bool t_count_lines = true;
+  bool t_forever = false;
+  int len;
+
+  /* With the obsolete form, there is one option string and
+     at most one file argument, possibly preceded by "--".  */
+  if (! ((2 <= argc && argc <= 3) || (argc == 4 && STREQ (argv[2], "--"))))
     return false;
 
   obsolete_usage = (posix2_version () < 200112);
 
-  /* If P starts with `+' and the POSIX version predates 1003.1-2001,
-     or if P starts with `-N' (where N is a digit), or `-l', then it
-     is obsolescent.  Return false otherwise.  */
-  if (! ((p[0] == '+' && obsolete_usage)
-        || (p[0] == '-' && (p[1] == 'l' || ISDIGIT (p[1])))))
-    return false;
+  switch (*p++)
+    {
+    default:
+      return false;
 
-  if (*p == '+')
-    t_from_start = true;
-  else if (*p == '-')
-    t_from_start = false;
-  else
-    return false;
+    case '+':
+      /* Leading "+" is a file name in the non-obsolete form.  */
+      if (!obsolete_usage)
+       return false;
+
+      t_from_start = true;
+      break;
+
+    case '-':
+      /* Plain "-" is standard input in the non-obsolete form.  */
+      if (!obsolete_usage && !*p)
+       return false;
+
+      /* Plain "-c" is required to be the non-obsolete option.  For
+         plain "-f" POSIX 1003.2-1992 is ambiguous; assume the
+         non-obsolete form.  */
+      if ((p[0] == 'c' || p[0] == 'f') && !p[1])
+       return false;
 
-  ++p;
-  if (ISDIGIT (*p))
-    {
-      n_string = p;
-      do
-       {
-         ++p;
-       }
-      while (ISDIGIT (*p));
+      t_from_start = false;
+      break;
     }
+
+  n_string = p;
+  while (ISDIGIT (*p))
+    p++;
   n_string_end = p;
 
-  t_count_lines = true;
-  if (*p == 'c' || *p == 'b')
+  switch (*p)
     {
-      t_count_lines = false;
-      ++p;
-    }
-  else if (*p == 'l')
-    {
-      ++p;
+    case 'c': t_count_lines = false;   /* Fall through.  */
+    case 'l': p++; break;
     }
 
-  t_forever = false;
   if (*p == 'f')
     {
       t_forever = true;
       ++p;
     }
 
-  if (*p != '\0')
-    {
-      /* If (argv[1] begins with a `+' or if it begins with `-' followed
-        by a digit), but has an invalid suffix character, give a diagnostic
-        and indicate to caller that this *is* of the obsolescent form,
-        but that it's an invalid option.  */
-      if (t_from_start || n_string)
-       {
-         error (0, 0,
-                _("%c: invalid suffix character in obsolescent option"), *p);
-         *ok = false;
-         return true;
-       }
+  if (*p)
+    return false;
 
-      /* Otherwise, it might be a valid non-obsolescent option like -n.  */
-      return false;
-    }
+  len = n_string_end - n_string;
 
-  *ok = true;
-  if (n_string == NULL)
+  if (n_string == n_string_end)
     *n_units = DEFAULT_N_LINES;
-  else
-    {
-      strtol_error s_err;
-      uintmax_t tmp;
-      char *end;
-
-      s_err = xstrtoumax (n_string, &end, 10, &tmp,
-                         *n_string_end == 'b' ? "b" : NULL);
-      if (s_err == LONGINT_OK)
-       *n_units = tmp;
-      else
-       {
-         /* Extract a NUL-terminated string for the error message.  */
-         size_t len = n_string_end - n_string;
-         char *n_string_tmp = xmalloc (len + 1);
+  else if (xstrtoumax (n_string, NULL, 10, n_units, NULL) != LONGINT_OK)
+    error (EXIT_FAILURE, 0, _("number `%.*s' too large"), len, n_string);
 
-         strncpy (n_string_tmp, n_string, len);
-         n_string_tmp[len] = '\0';
-
-         error (0, 0,
-                _("%s: %s is so large that it is not representable"),
-                n_string_tmp, (t_count_lines
-                               ? _("number of lines")
-                               : _("number of bytes")));
-         free (n_string_tmp);
-         *ok = false;
-       }
-    }
-
-  if (*ok)
+  if (!obsolete_usage)
     {
-      if (! obsolete_usage)
+      if (len == 0)
        {
-         int n_string_len = n_string_end - n_string;
-         error (0, 0, _("`%s' option is obsolete; use `%s-%c %.*s'"),
-                argv[1], t_forever ? " -f" : "", t_count_lines ? 'n' : 'c',
-                n_string_len, n_string);
-         usage (EXIT_FAILURE);
+         len = 2;
+         n_string = "10";
        }
-
-      /* Set globals.  */
-      from_start = t_from_start;
-      count_lines = t_count_lines;
-      forever = t_forever;
+      error (0, 0, _("`%s' option is obsolete; use `%s-%c %.*s'"),
+            argv[1], t_forever ? "-f " : "", t_count_lines ? 'n' : 'c',
+            len, n_string);
+      usage (EXIT_FAILURE);
     }
 
+  /* Set globals.  */
+  from_start = t_from_start;
+  count_lines = t_count_lines;
+  forever = t_forever;
+
   return true;
 }
 
@@ -1665,22 +1625,10 @@ main (int argc, char **argv)
 
   have_read_stdin = false;
 
-  {
-    bool ok;
-
-    if (parse_obsolescent_option (argc,
-                                 (const char *const *) argv,
-                                 &n_units, &ok))
-      {
-       if (!ok)
-         exit (EXIT_FAILURE);
-       optind = 2;
-      }
-    else
-      {
-       parse_options (argc, argv, &n_units, &header_mode, &sleep_interval);
-      }
-  }
+  if (parse_obsolete_option (argc, argv, &n_units))
+    optind = 2;
+  else
+    parse_options (argc, argv, &n_units, &header_mode, &sleep_interval);
 
   /* To start printing with item N_UNITS from the start of the file, skip
      N_UNITS - 1 items.  `tail -n +0' is actually meaningless, but for Unix
Index: tests/tail/Test.pm
===================================================================
RCS file: /home/eggert/coreutils/cu/tests/tail/Test.pm,v
retrieving revision 1.12
diff -p -u -r1.12 Test.pm
--- tests/tail/Test.pm  22 Jul 2004 20:54:53 -0000      1.12
+++ tests/tail/Test.pm  10 Sep 2004 20:38:07 -0000
@@ -79,11 +79,13 @@ sub test_vector
     {
       my ($test_name, $flags, $in, $exp, $ret) = @$t;
 
-      $test_name =~ /^(obs|err-[134])/
+      $test_name =~ /^(obs|err-[134]|minus-)/
        and $Test::env{$test_name} = ['_POSIX2_VERSION=199209'];
 
       # If you run the minus* tests with a FILE arg they'd hang.
-      if ($test_name =~ /^minus/)
+      # If you run the err-1 or err-3 tests with a FILE, they'd misinterpret
+      # the arg unless we are using the obsolete form.
+      if ($test_name =~ /^(minus|err-[13])/)
        {
          $Test::input_via{$test_name} = {REDIR => 0, PIPE => 0};
        }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]