(WRITTEN_BY): Rename from AUTHORS.
[platform/upstream/coreutils.git] / src / nl.c
index 5712e4a..63beb57 100644 (file)
--- a/src/nl.c
+++ b/src/nl.c
@@ -1,5 +1,5 @@
 /* nl -- number lines of files
-   Copyright (C) 1989, 1992, 1995 Free Software Foundation, Inc.
+   Copyright (C) 89, 92, 1995-2003 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
@@ -12,8 +12,8 @@
    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, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 \f
 /* Written by Scott Bartram (nancy!scott@uunet.uu.net)
    Revised by David MacKenzie (djm@gnu.ai.mit.edu) */
 #include <sys/types.h>
 #include <getopt.h>
 
-#if WITH_REGEX
-# include <regex.h>
-#else
-# include <rx.h>
-#endif
-
-#if HAVE_LIMITS_H
-# include <limits.h>
-#endif
-
-#ifndef UINT_MAX
-# define UINT_MAX ((unsigned int) ~(unsigned int) 0)
-#endif
+#include "system.h"
 
-#ifndef INT_MAX
-# define INT_MAX ((int) (UINT_MAX >> 1))
-#endif
+#include <regex.h>
 
-#include "linebuffer.h"
-#include "system.h"
-#include "version.h"
 #include "error.h"
+#include "linebuffer.h"
+#include "quote.h"
 #include "xstrtol.h"
 
+/* The official name of this program (e.g., no `g' prefix).  */
+#define PROGRAM_NAME "nl"
+
+#define WRITTEN_BY _("Written by Scott Bartram and David MacKenzie.")
+
 #ifndef TRUE
-#define TRUE   1
-#define FALSE  0
+# define TRUE   1
+# define FALSE  0
 #endif
 
 /* Line-number formats. */
@@ -71,9 +61,6 @@ enum section
   Header, Body, Footer, Text
 };
 
-char *xmalloc ();
-char *xrealloc ();
-
 /* The name this program was run with. */
 char *program_name;
 
@@ -111,19 +98,19 @@ static char *section_del = DEFAULT_SECTION_DELIMITERS;
 static char *header_del = NULL;
 
 /* Header section delimiter length.  */
-static int header_del_len;
+static size_t header_del_len;
 
 /* Body delimiter string.  */
 static char *body_del = NULL;
 
 /* Body section delimiter length.  */
-static int body_del_len;
+static size_t body_del_len;
 
 /* Footer delimiter string.  */
 static char *footer_del = NULL;
 
 /* Footer section delimiter length.  */
-static int footer_del_len;
+static size_t footer_del_len;
 
 /* Input buffer.  */
 static struct linebuffer line_buf;
@@ -158,12 +145,6 @@ static int line_no;
 /* Nonzero if we have ever read standard input. */
 static int have_read_stdin;
 
-/* If nonzero, display usage information and exit.  */
-static int show_help;
-
-/* If nonzero, print the version on standard output then exit.  */
-static int show_version;
-
 static struct option const longopts[] =
 {
   {"header-numbering", required_argument, NULL, 'h'},
@@ -177,14 +158,14 @@ static struct option const longopts[] =
   {"number-width", required_argument, NULL, 'w'},
   {"number-format", required_argument, NULL, 'n'},
   {"section-delimiter", required_argument, NULL, 'd'},
-  {"help", no_argument, &show_help, 1},
-  {"version", no_argument, &show_version, 1},
+  {GETOPT_HELP_OPTION_DECL},
+  {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
 };
 
 /* Print a usage message and quit. */
 
-static void
+void
 usage (int status)
 {
   if (status != 0)
@@ -196,27 +177,40 @@ usage (int status)
 Usage: %s [OPTION]... [FILE]...\n\
 "),
              program_name);
-      printf (_("\
+      fputs (_("\
 Write each FILE to standard output, with line numbers added.\n\
 With no FILE, or when FILE is -, read standard input.\n\
 \n\
+"), stdout);
+      fputs (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+      fputs (_("\
   -b, --body-numbering=STYLE      use STYLE for numbering body lines\n\
   -d, --section-delimiter=CC      use CC for separating logical pages\n\
   -f, --footer-numbering=STYLE    use STYLE for numbering footer lines\n\
+"), stdout);
+      fputs (_("\
   -h, --header-numbering=STYLE    use STYLE for numbering header lines\n\
   -i, --page-increment=NUMBER     line number increment at each line\n\
   -l, --join-blank-lines=NUMBER   group of NUMBER empty lines counted as one\n\
   -n, --number-format=FORMAT      insert line numbers according to FORMAT\n\
   -p, --no-renumber               do not reset line numbers at logical pages\n\
   -s, --number-separator=STRING   add STRING after (possible) line number\n\
+"), stdout);
+      fputs (_("\
   -v, --first-page=NUMBER         first line number on each logical page\n\
   -w, --number-width=NUMBER       use NUMBER columns for line numbers\n\
-      --help                      display this help and exit\n\
-      --version                   output version information and exit\n\
+"), stdout);
+      fputs (HELP_OPTION_DESCRIPTION, stdout);
+      fputs (VERSION_OPTION_DESCRIPTION, stdout);
+      fputs (_("\
 \n\
 By default, selects -v1 -i1 -l1 -sTAB -w6 -nrn -hn -bt -fn.  CC are\n\
 two delimiter characters for separating logical pages, a missing\n\
 second character implies :.  Type \\\\ for \\.  STYLE is one of:\n\
+"), stdout);
+      fputs (_("\
 \n\
   a         number all lines\n\
   t         number only nonempty lines\n\
@@ -229,9 +223,10 @@ FORMAT is one of:\n\
   rn   right justified, no leading zeros\n\
   rz   right justified, leading zeros\n\
 \n\
-"));
+"), stdout);
+      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
     }
-  exit (status);
+  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
 
 /* Build the printf format string, based on `lineno_format'. */
@@ -239,18 +234,21 @@ FORMAT is one of:\n\
 static void
 build_print_fmt (void)
 {
-  /* 12 = 10 chars for lineno_width, 1 for %, 1 for \0.  */
-  print_fmt = xmalloc (strlen (separator_str) + 12);
+  print_fmt = xmalloc (  1 /* for `%' */
+                      + 1 /* for `-' or `0' */
+                      + INT_STRLEN_BOUND (lineno_width)
+                      + 1 /* for `d' */
+                      + 1 /* for trailing NUL byte */ );
   switch (lineno_format)
     {
     case FORMAT_RIGHT_NOLZ:
-      sprintf (print_fmt, "%%%dd%s", lineno_width, separator_str);
+      sprintf (print_fmt, "%%%dd", lineno_width);
       break;
     case FORMAT_RIGHT_LZ:
-      sprintf (print_fmt, "%%0%dd%s", lineno_width, separator_str);
+      sprintf (print_fmt, "%%0%dd", lineno_width);
       break;
     case FORMAT_LEFT:
-      sprintf (print_fmt, "%%-%dd%s", lineno_width, separator_str);
+      sprintf (print_fmt, "%%-%dd", lineno_width);
       break;
     }
 }
@@ -276,13 +274,13 @@ build_type_arg (char **typep, struct re_pattern_buffer *regexp)
       *typep = optarg++;
       optlen = strlen (optarg);
       regexp->allocated = optlen * 2;
-      regexp->buffer = (unsigned char *) xmalloc (regexp->allocated);
+      regexp->buffer = xmalloc (regexp->allocated);
       regexp->translate = NULL;
       regexp->fastmap = xmalloc (256);
       regexp->fastmap_accurate = 0;
       errmsg = re_compile_pattern (optarg, optlen, regexp);
       if (errmsg)
-       error (1, 0, "%s", errmsg);
+       error (EXIT_FAILURE, 0, "%s", errmsg);
       break;
     default:
       rval = FALSE;
@@ -291,12 +289,13 @@ build_type_arg (char **typep, struct re_pattern_buffer *regexp)
   return rval;
 }
 
-/* Print and increment the line number. */
+/* Print the line number and separator; increment the line number. */
 
 static void
 print_lineno (void)
 {
   printf (print_fmt, line_no);
+  fputs (separator_str, stdout);
   line_no += page_incr;
 }
 
@@ -344,36 +343,35 @@ proc_text (void)
     case 'a':
       if (blank_join > 1)
        {
-         if (line_buf.length || ++blank_lines == blank_join)
+         if (1 < line_buf.length || ++blank_lines == blank_join)
            {
              print_lineno ();
              blank_lines = 0;
            }
          else
-           printf (print_no_line_fmt);
+           fputs (print_no_line_fmt, stdout);
        }
       else
        print_lineno ();
       break;
     case 't':
-      if (line_buf.length)
+      if (1 < line_buf.length)
        print_lineno ();
       else
-       printf (print_no_line_fmt);
+       fputs (print_no_line_fmt, stdout);
       break;
     case 'n':
-      printf (print_no_line_fmt);
+      fputs (print_no_line_fmt, stdout);
       break;
     case 'p':
-      if (re_search (current_regex, line_buf.buffer, line_buf.length,
-                    0, line_buf.length, (struct re_registers *) 0) < 0)
-       printf (print_no_line_fmt);
+      if (re_search (current_regex, line_buf.buffer, line_buf.length - 1,
+                    0, line_buf.length - 1, (struct re_registers *) 0) < 0)
+       fputs (print_no_line_fmt, stdout);
       else
        print_lineno ();
       break;
     }
   fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
-  putchar ('\n');
 }
 
 /* Return the type of line in `line_buf'. */
@@ -381,15 +379,17 @@ proc_text (void)
 static enum section
 check_section (void)
 {
-  if (line_buf.length < 2 || memcmp (line_buf.buffer, section_del, 2))
+  size_t len = line_buf.length - 1;
+
+  if (len < 2 || memcmp (line_buf.buffer, section_del, 2))
     return Text;
-  if (line_buf.length == header_del_len
+  if (len == header_del_len
       && !memcmp (line_buf.buffer, header_del, header_del_len))
     return Header;
-  if (line_buf.length == body_del_len
+  if (len == body_del_len
       && !memcmp (line_buf.buffer, body_del, body_del_len))
     return Body;
-  if (line_buf.length == footer_del_len
+  if (len == footer_del_len
       && !memcmp (line_buf.buffer, footer_del, footer_del_len))
     return Footer;
   return Text;
@@ -400,7 +400,7 @@ check_section (void)
 static void
 process_file (FILE *fp)
 {
-  while (readline (&line_buf, fp))
+  while (readlinebuffer (&line_buf, fp))
     {
       switch ((int) check_section ())
        {
@@ -428,7 +428,7 @@ nl_file (const char *file)
 {
   FILE *stream;
 
-  if (!strcmp (file, "-"))
+  if (STREQ (file, "-"))
     {
       have_read_stdin = 1;
       stream = stdin;
@@ -450,7 +450,7 @@ nl_file (const char *file)
       error (0, errno, "%s", file);
       return 1;
     }
-  if (!strcmp (file, "-"))
+  if (STREQ (file, "-"))
     clearerr (stream);         /* Also clear EOF. */
   else if (fclose (stream) == EOF)
     {
@@ -460,16 +460,25 @@ nl_file (const char *file)
   return 0;
 }
 
-void
+int
 main (int argc, char **argv)
 {
   int c, exit_status = 0;
+  size_t len;
+  int fail = 0;
 
+  initialize_main (&argc, &argv);
   program_name = argv[0];
+  setlocale (LC_ALL, "");
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
+  atexit (close_stdout);
+
   have_read_stdin = 0;
 
   while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
-                          (int *) 0)) != EOF)
+                          NULL)) != -1)
     {
       switch (c)
        {
@@ -478,35 +487,59 @@ main (int argc, char **argv)
 
        case 'h':
          if (build_type_arg (&header_type, &header_regex) != TRUE)
-           usage (2);
+           {
+             error (0, 0, _("invalid header numbering style: %s"),
+                    quote (optarg));
+             fail = 1;
+           }
          break;
        case 'b':
          if (build_type_arg (&body_type, &body_regex) != TRUE)
-           usage (2);
+           {
+             error (0, 0, _("invalid body numbering style: %s"),
+                    quote (optarg));
+             fail = 1;
+           }
          break;
        case 'f':
          if (build_type_arg (&footer_type, &footer_regex) != TRUE)
-           usage (2);
+           {
+             error (0, 0, _("invalid footer numbering style: %s"),
+                    quote (optarg));
+             fail = 1;
+           }
          break;
        case 'v':
          {
            long int tmp_long;
-           if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
+           if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
                /* Allow it to be negative.  */
                || tmp_long > INT_MAX)
-             error (1, 0, _("invalid starting line number: `%s'"),
-                    optarg);
-           starting_line_number = (int) tmp_long;
+             {
+               error (0, 0, _("invalid starting line number: %s"),
+                      quote (optarg));
+               fail = 1;
+             }
+           else
+             {
+               starting_line_number = (int) tmp_long;
+             }
          }
          break;
        case 'i':
          {
            long int tmp_long;
-           if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
+           if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
                || tmp_long <= 0 || tmp_long > INT_MAX)
-             error (1, 0, _("invalid line number increment: `%s'"),
-                    optarg);
-           page_incr = (int) tmp_long;
+             {
+               error (0, 0, _("invalid line number increment: %s"),
+                      quote (optarg));
+               fail = 1;
+             }
+           else
+             {
+               page_incr = (int) tmp_long;
+             }
          }
          break;
        case 'p':
@@ -515,11 +548,17 @@ main (int argc, char **argv)
        case 'l':
          {
            long int tmp_long;
-           if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
+           if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
                || tmp_long <= 0 || tmp_long > INT_MAX)
-             error (1, 0, _("invalid number of blank lines: `%s'"),
-                    optarg);
-           blank_join = (int) tmp_long;
+             {
+               error (0, 0, _("invalid number of blank lines: %s"),
+                      quote (optarg));
+               fail = 1;
+             }
+           else
+             {
+               blank_join = (int) tmp_long;
+             }
          }
          break;
        case 's':
@@ -528,71 +567,59 @@ main (int argc, char **argv)
        case 'w':
          {
            long int tmp_long;
-           if (xstrtol (optarg, NULL, 10, &tmp_long, NULL) != LONGINT_OK
+           if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
                || tmp_long <= 0 || tmp_long > INT_MAX)
-             error (1, 0, _("invalid line number field width: `%s'"),
-                    optarg);
-           lineno_width = (int) tmp_long;
+             {
+               error (0, 0, _("invalid line number field width: %s"),
+                      quote (optarg));
+               fail = 1;
+             }
+           else
+             {
+               lineno_width = (int) tmp_long;
+             }
          }
          break;
        case 'n':
-         switch (*optarg)
+         if (STREQ (optarg, "ln"))
+           lineno_format = FORMAT_LEFT;
+         else if (STREQ (optarg, "rn"))
+           lineno_format = FORMAT_RIGHT_NOLZ;
+         else if (STREQ (optarg, "rz"))
+           lineno_format = FORMAT_RIGHT_LZ;
+         else
            {
-           case 'l':
-             if (optarg[1] == 'n')
-               lineno_format = FORMAT_LEFT;
-             else
-               usage (2);
-             break;
-           case 'r':
-             switch (optarg[1])
-               {
-               case 'n':
-                 lineno_format = FORMAT_RIGHT_NOLZ;
-                 break;
-               case 'z':
-                 lineno_format = FORMAT_RIGHT_LZ;
-                 break;
-               default:
-                 usage (2);
-                 break;
-               }
-             break;
-           default:
-             usage (2);
-             break;
+             error (0, 0, _("invalid line numbering format: %s"),
+                    quote (optarg));
+             fail = 1;
            }
          break;
        case 'd':
          section_del = optarg;
          break;
+       case_GETOPT_HELP_CHAR;
+       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, WRITTEN_BY);
        default:
-         usage (2);
+         fail = 1;
          break;
        }
     }
 
-  if (show_version)
-    {
-      printf ("nl - %s\n", version_string);
-      exit (0);
-    }
-
-  if (show_help)
-    usage (0);
+  if (fail)
+    usage (2);
 
   /* Initialize the section delimiters.  */
-  c = strlen (section_del);
+  len = strlen (section_del);
 
-  header_del_len = c * 3;
+  header_del_len = len * 3;
   header_del = xmalloc (header_del_len + 1);
   strcat (strcat (strcpy (header_del, section_del), section_del), section_del);
 
-  body_del_len = c * 2;
+  body_del_len = len * 2;
   body_del = xmalloc (body_del_len + 1);
   strcat (strcpy (body_del, section_del), section_del);
 
-  footer_del_len = c;
+  footer_del_len = len;
   footer_del = xmalloc (footer_del_len + 1);
   strcpy (footer_del, section_del);
 
@@ -600,10 +627,10 @@ main (int argc, char **argv)
   initbuffer (&line_buf);
 
   /* Initialize the printf format for unnumbered lines. */
-  c = strlen (separator_str);
-  print_no_line_fmt = xmalloc (lineno_width + c + 1);
-  memset (print_no_line_fmt, ' ', lineno_width + c);
-  print_no_line_fmt[lineno_width + c] = '\0';
+  len = strlen (separator_str);
+  print_no_line_fmt = xmalloc (lineno_width + len + 1);
+  memset (print_no_line_fmt, ' ', lineno_width + len);
+  print_no_line_fmt[lineno_width + len] = '\0';
 
   line_no = starting_line_number;
   current_type = body_type;
@@ -623,8 +650,6 @@ main (int argc, char **argv)
       error (0, errno, "-");
       exit_status = 1;
     }
-  if (ferror (stdout) || fclose (stdout) == EOF)
-    error (1, errno, _("write error"));
 
-  exit (exit_status);
+  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }