Don't include version.h.
[platform/upstream/coreutils.git] / src / cat.c
index a2b1c14..390abc4 100644 (file)
--- a/src/cat.c
+++ b/src/cat.c
@@ -1,5 +1,5 @@
 /* cat -- concatenate files and print on the standard output.
-   Copyright (C) 1988, 1990, 1991 Free Software Foundation, Inc.
+   Copyright (C) 1988, 1990, 1991, 1995 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
    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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-\f
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
 /* Differences from the Unix cat:
    * Always unbuffered, -u is ignored.
-   * 100 times faster with -v -u.
-   * 20 times faster with -v.
+   * Usually much faster than other versions of cat, the difference
+   is especially apparent when using the -v option.
 
    By tege@sics.se, Torbjorn Granlund, advised by rms, Richard Stallman. */
 
+#include <config.h>
+
 #include <stdio.h>
 #include <getopt.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #endif
 #include "system.h"
+#include "error.h"
 
+/* Undefine, to avoid warning about redefinition on some systems.  */
+#undef max
 #define max(h,i) ((h) > (i) ? (h) : (i))
 
 char *stpcpy ();
 char *xmalloc ();
-void error ();
-
-static void cat ();
-static void next_line_num ();
-static void simple_cat ();
+int full_write ();
+int safe_read ();
 
 /* Name under which this program was invoked.  */
 char *program_name;
@@ -57,7 +59,7 @@ static char line_buf[13] =
 {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0', '\t', '\0'};
 
 /* Position in `line_buf' where printing starts.  This will not change
-   unless the number of lines are more than 999999.  */
+   unless the number of lines is larger than 999999.  */
 static char *line_num_print = line_buf + 5;
 
 /* Position of the first digit in `line_buf'.  */
@@ -73,280 +75,68 @@ static int newlines2 = 0;
 static int exit_stat = 0;
 
 static void
-usage (reason)
-     char *reason;
-{
-  if (reason != NULL)
-    fprintf (stderr, "%s: %s\n", program_name, reason);
-
-  fprintf (stderr, "\
-Usage: %s [-benstuvAET] [--number] [--number-nonblank] [--squeeze-blank]\n\
-       [--show-nonprinting] [--show-ends] [--show-tabs] [--show-all]\n\
-       [file...]\n",
-          program_name);
-
-  exit (2);
-}
-
-\f
-void
-main (argc, argv)
-     int argc;
-     char *argv[];
+usage (int status)
 {
-  /* Optimal size of i/o operations of output.  */
-  int outsize;
-
-  /* Optimal size of i/o operations of input.  */
-  int insize;
-
-  /* Pointer to the input buffer.  */
-  unsigned char *inbuf;
-
-  /* Pointer to the output buffer.  */
-  unsigned char *outbuf;
-
-  int c;
-
-  /* Index in argv to processed argument.  */
-  int argind;
-
-  /* Device number of the output (file or whatever).  */
-  int out_dev;
-
-  /* I-node number of the output.  */
-  int out_ino;
-
-  /* Nonzero if the output file should not be the same as any input file. */
-  int check_redirection = 1;
-
-  /* Nonzero if we have ever read standard input. */
-  int have_read_stdin = 0;
-
-  struct stat stat_buf;
-
-  /* Variables that are set according to the specified options.  */
-  int numbers = 0;
-  int numbers_at_empty_lines = 1;
-  int squeeze_empty_lines = 0;
-  int mark_line_ends = 0;
-  int quote = 0;
-  int output_tabs = 1;
-  int options = 0;
-
-  static struct option const long_options[] =
-  {
-    {"number-nonblank", no_argument, NULL, 'b'},
-    {"number", no_argument, NULL, 'n'},
-    {"squeeze-blank", no_argument, NULL, 's'},
-    {"show-nonprinting", no_argument, NULL, 'v'},
-    {"show-ends", no_argument, NULL, 'E'},
-    {"show-tabs", no_argument, NULL, 'T'},
-    {"show-all", no_argument, NULL, 'A'},
-    {NULL, 0, NULL, 0}
-  };
-
-  program_name = argv[0];
-
-  /* Parse command line options.  */
-
-  while ((c = getopt_long (argc, argv, "benstuvAET", long_options, (int *) 0))
-        != EOF)
-    {
-      options++;
-      switch (c)
-       {
-       case 'b':
-         numbers = 1;
-         numbers_at_empty_lines = 0;
-         break;
-
-       case 'e':
-         mark_line_ends = 1;
-         quote = 1;
-         break;
-
-       case 'n':
-         numbers = 1;
-         break;
-
-       case 's':
-         squeeze_empty_lines = 1;
-         break;
-
-       case 't':
-         output_tabs = 0;
-         quote = 1;
-         break;
-
-       case 'u':
-         /* We provide the -u feature unconditionally.  */
-         options--;
-         break;
-
-       case 'v':
-         quote = 1;
-         break;
-
-       case 'A':
-         quote = 1;
-         mark_line_ends = 1;
-         output_tabs = 0;
-         break;
-
-       case 'E':
-         mark_line_ends = 1;
-         break;
-
-       case 'T':
-         output_tabs = 0;
-         break;
-
-       default:
-         usage ((char *) 0);
-       }
-    }
-
-  output_desc = 1;
-
-  /* Get device, i-node number, and optimal blocksize of output.  */
-
-  if (fstat (output_desc, &stat_buf) < 0)
-    error (1, errno, "standard output");
-
-  outsize = ST_BLKSIZE (stat_buf);
-  /* Input file can be output file for non-regular files.
-     fstat on pipes returns S_IFSOCK on some systems, S_IFIFO
-     on others, so the checking should not be done for those types,
-     and to allow things like cat < /dev/tty > /dev/tty, checking
-     is not done for device files either. */
-
-  if (S_ISREG (stat_buf.st_mode))
+  if (status != 0)
+    fprintf (stderr, _("Try `%s --help' for more information.\n"),
+            program_name);
+  else
     {
-      out_dev = stat_buf.st_dev;
-      out_ino = stat_buf.st_ino;
+      printf (_("\
+Usage: %s [OPTION] [FILE]...\n\
+"),
+             program_name);
+      printf (_("\
+Concatenate FILE(s), or standard input, to standard output.\n\
+\n\
+  -b, --number-nonblank    number nonblank output lines\n\
+  -e                       equivalent to -vE\n\
+  -n, --number             number all output lines\n\
+  -s, --squeeze-blank      never more than one single blank line\n\
+  -t                       equivalent to -vT\n\
+  -u                       (ignored)\n\
+  -v, --show-nonprinting   use ^ and M- notation, save for LFD and TAB\n\
+  -A, --show-all           equivalent to -vET\n\
+  -E, --show-ends          display $ at end of each line\n\
+  -T, --show-tabs          display TAB characters as ^I\n\
+      --help               display this help and exit\n\
+      --version            output version information and exit\n\
+\n\
+With no FILE, or when FILE is -, read standard input.\n\
+"));
     }
-  else
-    check_redirection = 0;
-
-  /* Check if any of the input files are the same as the output file.  */
-
-  /* Main loop.  */
+  exit (status);
+}
 
-  infile = "-";
-  argind = optind;
+/* Compute the next line number.  */
 
+static void
+next_line_num (void)
+{
+  char *endp = line_num_end;
   do
     {
-      if (argind < argc)
-       infile = argv[argind];
-
-      if (infile[0] == '-' && infile[1] == 0)
-       {
-         have_read_stdin = 1;
-         input_desc = 0;
-       }
-      else
-       {
-         input_desc = open (infile, O_RDONLY);
-         if (input_desc < 0)
-           {
-             error (0, errno, "%s", infile);
-             exit_stat = 1;
-             continue;
-           }
-       }
-
-      if (fstat (input_desc, &stat_buf) < 0)
-       {
-         error (0, errno, "%s", infile);
-         exit_stat = 1;
-         goto contin;
-       }
-      insize = ST_BLKSIZE (stat_buf);
-
-      /* Compare the device and i-node numbers of this input file with
-        the corresponding values of the (output file associated with)
-        stdout, and skip this input file if they coincide.  Input
-        files cannot be redirected to themselves.  */
-
-      if (check_redirection
-         && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino)
-       {
-         error (0, 0, "%s: input file is output file", infile);
-         exit_stat = 1;
-         goto contin;
-       }
-
-      /* Select which version of `cat' to use. If any options (more than -u)
-        were specified, use `cat', otherwise use `simple_cat'.  */
-
-      if (options == 0)
-       {
-         insize = max (insize, outsize);
-         inbuf = (unsigned char *) xmalloc (insize);
-
-         simple_cat (inbuf, insize);
-       }
-      else
-       {
-         inbuf = (unsigned char *) xmalloc (insize + 1);
-
-         /* Why are (OUTSIZE  - 1 + INSIZE * 4 + 13) bytes allocated for
-            the output buffer?
-
-            A test whether output needs to be written is done when the input
-            buffer empties or when a newline appears in the input.  After
-            output is written, at most (OUTSIZE - 1) bytes will remain in the
-            buffer.  Now INSIZE bytes of input is read.  Each input character
-            may grow by a factor of 4 (by the prepending of M-^).  If all
-            characters do, and no newlines appear in this block of input, we
-            will have at most (OUTSIZE - 1 + INSIZE) bytes in the buffer.  If
-            the last character in the preceding block of input was a
-            newline, a line number may be written (according to the given
-            options) as the first thing in the output buffer. (Done after the
-            new input is read, but before processing of the input begins.)  A
-            line number requires seldom more than 13 positions.  */
-
-         outbuf = (unsigned char *) xmalloc (outsize - 1 + insize * 4 + 13);
-
-         cat (inbuf, insize, outbuf, outsize, quote,
-              output_tabs, numbers, numbers_at_empty_lines, mark_line_ends,
-              squeeze_empty_lines);
-
-         free (outbuf);
-       }
-
-      free (inbuf);
-
-    contin:
-      if (strcmp (infile, "-") && close (input_desc) < 0)
-       {
-         error (0, errno, "%s", infile);
-         exit_stat = 1;
-       }
+      if ((*endp)++ < '9')
+       return;
+      *endp-- = '0';
     }
-  while (++argind < argc);
-
-  if (have_read_stdin && close (0) < 0)
-    error (1, errno, "-");
-  if (close (1) < 0)
-    error (1, errno, "write error");
-
-  exit (exit_stat);
+  while (endp >= line_num_start);
+  *--line_num_start = '1';
+  if (line_num_start < line_num_print)
+    line_num_print--;
 }
-\f
+
 /* Plain cat.  Copies the file behind `input_desc' to the file behind
    `output_desc'.  */
 
 static void
-simple_cat (buf, bufsize)
+simple_cat (
      /* Pointer to the buffer, used by reads and writes.  */
-     unsigned char *buf;
+     unsigned char *buf,
 
      /* Number of characters preferably read or written by each read and write
         call.  */
-     int bufsize;
+     int bufsize)
 {
   /* Actual number of characters read, and therefore written.  */
   int n_read;
@@ -357,7 +147,7 @@ simple_cat (buf, bufsize)
     {
       /* Read a block of input.  */
 
-      n_read = read (input_desc, buf, bufsize);
+      n_read = safe_read (input_desc, buf, bufsize);
       if (n_read < 0)
        {
          error (0, errno, "%s", infile);
@@ -372,11 +162,11 @@ simple_cat (buf, bufsize)
 
       /* Write this block out.  */
 
-      if (write (output_desc, buf, n_read) != n_read)
-       error (1, errno, "write error");
+      if (full_write (output_desc, buf, n_read) < 0)
+       error (1, errno, _("write error"));
     }
 }
-\f
+
 /* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC.
    Called if any option more than -u was specified.
 
@@ -384,29 +174,26 @@ simple_cat (buf, bufsize)
    an explicit test for buffer end unnecessary.  */
 
 static void
-cat (inbuf, insize, outbuf, outsize, quote,
-     output_tabs, numbers, numbers_at_empty_lines,
-     mark_line_ends, squeeze_empty_lines)
-
+cat (
      /* Pointer to the beginning of the input buffer.  */
-     unsigned char *inbuf;
+     unsigned char *inbuf,
 
      /* Number of characters read in each read call.  */
-     int insize;
+     int insize,
 
      /* Pointer to the beginning of the output buffer.  */
-     unsigned char *outbuf;
+     unsigned char *outbuf,
 
      /* Number of characters written by each write call.  */
-     int outsize;
+     int outsize,
 
      /* Variables that have values according to the specified options.  */
-     int quote;
-     int output_tabs;
-     int numbers;
-     int numbers_at_empty_lines;
-     int mark_line_ends;
-     int squeeze_empty_lines;
+     int quote,
+     int output_tabs,
+     int numbers,
+     int numbers_at_empty_lines,
+     int mark_line_ends,
+     int squeeze_empty_lines)
 {
   /* Last character read from the input buffer.  */
   unsigned char ch;
@@ -456,8 +243,8 @@ cat (inbuf, insize, outbuf, outsize, quote,
              unsigned char *wp = outbuf;
              do
                {
-                 if (write (output_desc, wp, outsize) != outsize)
-                   error (1, errno, "write error");
+                 if (full_write (output_desc, wp, outsize) < 0)
+                   error (1, errno, _("write error"));
                  wp += outsize;
                }
              while (bpout - wp >= outsize);
@@ -465,7 +252,7 @@ cat (inbuf, insize, outbuf, outsize, quote,
              /* Move the remaining bytes to the beginning of the
                 buffer.  */
 
-             bcopy (wp, outbuf, bpout - wp);
+             memmove (outbuf, wp, bpout - wp);
              bpout = outbuf + (bpout - wp);
            }
 
@@ -484,12 +271,21 @@ cat (inbuf, insize, outbuf, outsize, quote,
                  && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
                {
                  /* Ultrix returns EOPNOTSUPP on NFS;
-                    HP-UX returns ENOTTY on pipes. */
-                 if (errno == EOPNOTSUPP || errno == ENOTTY)
+                    HP-UX returns ENOTTY on pipes.
+                    SunOS returns EINVAL and
+                    More/BSD returns ENODEV on special files
+                    like /dev/null.
+                    Irix-5 returns ENOSYS on pipes.  */
+                 if (errno == EOPNOTSUPP || errno == ENOTTY
+                     || errno == EINVAL || errno == ENODEV
+#ifdef ENOSYS
+                     || errno == ENOSYS
+#endif
+                     )
                    use_fionread = 0;
                  else
                    {
-                     error (0, errno, "cannot do ioctl on `%s'", infile);
+                     error (0, errno, _("cannot do ioctl on `%s'"), infile);
                      exit_stat = 1;
                      newlines2 = newlines;
                      return;
@@ -500,14 +296,14 @@ cat (inbuf, insize, outbuf, outsize, quote,
                {
                  int n_write = bpout - outbuf;
 
-                 if (write (output_desc, outbuf, n_write) != n_write)
-                   error (1, errno, "write error");
+                 if (full_write (output_desc, outbuf, n_write) < 0)
+                   error (1, errno, _("write error"));
                  bpout = outbuf;
                }
 
              /* Read more input into INBUF.  */
 
-             n_read = read (input_desc, inbuf, insize);
+             n_read = safe_read (input_desc, inbuf, insize);
              if (n_read < 0)
                {
                  error (0, errno, "%s", infile);
@@ -642,20 +438,287 @@ cat (inbuf, insize, outbuf, outsize, quote,
     }
 }
 
-/* Compute the next line number.  */
-
-static void
-next_line_num ()
+void
+main (int argc, char **argv)
 {
-  char *endp = line_num_end;
+  /* Optimal size of i/o operations of output.  */
+  int outsize;
+
+  /* Optimal size of i/o operations of input.  */
+  int insize;
+
+  /* Pointer to the input buffer.  */
+  unsigned char *inbuf;
+
+  /* Pointer to the output buffer.  */
+  unsigned char *outbuf;
+
+  int c;
+
+  /* Index in argv to processed argument.  */
+  int argind;
+
+  /* Device number of the output (file or whatever).  */
+  int out_dev;
+
+  /* I-node number of the output.  */
+  int out_ino;
+
+  /* Nonzero if the output file should not be the same as any input file. */
+  int check_redirection = 1;
+
+  /* Nonzero if we have ever read standard input. */
+  int have_read_stdin = 0;
+
+  struct stat stat_buf;
+
+  /* Variables that are set according to the specified options.  */
+  int numbers = 0;
+  int numbers_at_empty_lines = 1;
+  int squeeze_empty_lines = 0;
+  int mark_line_ends = 0;
+  int quote = 0;
+  int output_tabs = 1;
+
+/* If nonzero, call cat, otherwise call simple_cat to do the actual work. */
+  int options = 0;
+
+  /* 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 long_options[] =
+  {
+    {"number-nonblank", no_argument, NULL, 'b'},
+    {"number", no_argument, NULL, 'n'},
+    {"squeeze-blank", no_argument, NULL, 's'},
+    {"show-nonprinting", no_argument, NULL, 'v'},
+    {"show-ends", no_argument, NULL, 'E'},
+    {"show-tabs", no_argument, NULL, 'T'},
+    {"show-all", no_argument, NULL, 'A'},
+    {"help", no_argument, &show_help, 1},
+    {"version", no_argument, &show_version, 1},
+    {NULL, 0, NULL, 0}
+  };
+
+  program_name = argv[0];
+  setlocale (LC_ALL, "");
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
+  /* Parse command line options.  */
+
+  while ((c = getopt_long (argc, argv, "benstuvAET", long_options, (int *) 0))
+        != EOF)
+    {
+      switch (c)
+       {
+       case 0:
+         break;
+
+       case 'b':
+         ++options;
+         numbers = 1;
+         numbers_at_empty_lines = 0;
+         break;
+
+       case 'e':
+         ++options;
+         mark_line_ends = 1;
+         quote = 1;
+         break;
+
+       case 'n':
+         ++options;
+         numbers = 1;
+         break;
+
+       case 's':
+         ++options;
+         squeeze_empty_lines = 1;
+         break;
+
+       case 't':
+         ++options;
+         output_tabs = 0;
+         quote = 1;
+         break;
+
+       case 'u':
+         /* We provide the -u feature unconditionally.  */
+         break;
+
+       case 'v':
+         ++options;
+         quote = 1;
+         break;
+
+       case 'A':
+         ++options;
+         quote = 1;
+         mark_line_ends = 1;
+         output_tabs = 0;
+         break;
+
+       case 'E':
+         ++options;
+         mark_line_ends = 1;
+         break;
+
+       case 'T':
+         ++options;
+         output_tabs = 0;
+         break;
+
+       default:
+         usage (2);
+       }
+    }
+
+  if (show_version)
+    {
+      printf ("cat - %s\n", PACKAGE_VERSION);
+      exit (0);
+    }
+
+  if (show_help)
+    usage (0);
+
+  output_desc = 1;
+
+  /* Get device, i-node number, and optimal blocksize of output.  */
+
+  if (fstat (output_desc, &stat_buf) < 0)
+    error (1, errno, _("standard output"));
+
+  outsize = ST_BLKSIZE (stat_buf);
+  /* Input file can be output file for non-regular files.
+     fstat on pipes returns S_IFSOCK on some systems, S_IFIFO
+     on others, so the checking should not be done for those types,
+     and to allow things like cat < /dev/tty > /dev/tty, checking
+     is not done for device files either. */
+
+  if (S_ISREG (stat_buf.st_mode))
+    {
+      out_dev = stat_buf.st_dev;
+      out_ino = stat_buf.st_ino;
+    }
+  else
+    {
+      check_redirection = 0;
+#ifdef lint  /* Suppress `used before initialized' warning.  */
+      out_dev = 0;
+      out_ino = 0;
+#endif
+    }
+
+  /* Check if any of the input files are the same as the output file.  */
+
+  /* Main loop.  */
+
+  infile = "-";
+  argind = optind;
+
   do
     {
-      if ((*endp)++ < '9')
-       return;
-      *endp-- = '0';
+      if (argind < argc)
+       infile = argv[argind];
+
+      if (infile[0] == '-' && infile[1] == 0)
+       {
+         have_read_stdin = 1;
+         input_desc = 0;
+       }
+      else
+       {
+         input_desc = open (infile, O_RDONLY);
+         if (input_desc < 0)
+           {
+             error (0, errno, "%s", infile);
+             exit_stat = 1;
+             continue;
+           }
+       }
+
+      if (fstat (input_desc, &stat_buf) < 0)
+       {
+         error (0, errno, "%s", infile);
+         exit_stat = 1;
+         goto contin;
+       }
+      insize = ST_BLKSIZE (stat_buf);
+
+      /* Compare the device and i-node numbers of this input file with
+        the corresponding values of the (output file associated with)
+        stdout, and skip this input file if they coincide.  Input
+        files cannot be redirected to themselves.  */
+
+      if (check_redirection
+         && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino
+         && (input_desc != fileno (stdin) || output_desc != fileno (stdout)))
+       {
+         error (0, 0, _("%s: input file is output file"), infile);
+         exit_stat = 1;
+         goto contin;
+       }
+
+      /* Select which version of `cat' to use. If any options (more than -u,
+        --version, or --help) were specified, use `cat', otherwise use
+        `simple_cat'.  */
+
+      if (options == 0)
+       {
+         insize = max (insize, outsize);
+         inbuf = (unsigned char *) xmalloc (insize);
+
+         simple_cat (inbuf, insize);
+       }
+      else
+       {
+         inbuf = (unsigned char *) xmalloc (insize + 1);
+
+         /* Why are (OUTSIZE  - 1 + INSIZE * 4 + 13) bytes allocated for
+            the output buffer?
+
+            A test whether output needs to be written is done when the input
+            buffer empties or when a newline appears in the input.  After
+            output is written, at most (OUTSIZE - 1) bytes will remain in the
+            buffer.  Now INSIZE bytes of input is read.  Each input character
+            may grow by a factor of 4 (by the prepending of M-^).  If all
+            characters do, and no newlines appear in this block of input, we
+            will have at most (OUTSIZE - 1 + INSIZE) bytes in the buffer.  If
+            the last character in the preceding block of input was a
+            newline, a line number may be written (according to the given
+            options) as the first thing in the output buffer. (Done after the
+            new input is read, but before processing of the input begins.)  A
+            line number requires seldom more than 13 positions.  */
+
+         outbuf = (unsigned char *) xmalloc (outsize - 1 + insize * 4 + 13);
+
+         cat (inbuf, insize, outbuf, outsize, quote,
+              output_tabs, numbers, numbers_at_empty_lines, mark_line_ends,
+              squeeze_empty_lines);
+
+         free (outbuf);
+       }
+
+      free (inbuf);
+
+    contin:
+      if (strcmp (infile, "-") && close (input_desc) < 0)
+       {
+         error (0, errno, "%s", infile);
+         exit_stat = 1;
+       }
     }
-  while (endp >= line_num_start);
-  *--line_num_start = '1';
-  if (line_num_start < line_num_print)
-    line_num_print--;
+  while (++argind < argc);
+
+  if (have_read_stdin && close (0) < 0)
+    error (1, errno, "-");
+  if (close (1) < 0)
+    error (1, errno, _("write error"));
+
+  exit (exit_stat);
 }