Don't include version.h.
[platform/upstream/coreutils.git] / src / cat.c
index 70d1c68..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 "version.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;
@@ -58,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'.  */
@@ -74,471 +75,235 @@ static int newlines2 = 0;
 static int exit_stat = 0;
 
 static void
-usage ()
+usage (int status)
 {
-  fprintf (stderr, "\
-Usage: %s [-benstuvAET] [--number] [--number-nonblank] [--squeeze-blank]\n\
-       [--show-nonprinting] [--show-ends] [--show-tabs] [--show-all]\n\
-       [--help] [--version] [file...]\n",
-          program_name);
-
-  exit (2);
+  if (status != 0)
+    fprintf (stderr, _("Try `%s --help' for more information.\n"),
+            program_name);
+  else
+    {
+      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\
+"));
+    }
+  exit (status);
 }
 
-\f
-void
-main (argc, argv)
-     int argc;
-     char *argv[];
-{
-  /* 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;
+/* Compute the next line number.  */
 
-  /* Pointer to the output buffer.  */
-  unsigned char *outbuf;
+static void
+next_line_num (void)
+{
+  char *endp = line_num_end;
+  do
+    {
+      if ((*endp)++ < '9')
+       return;
+      *endp-- = '0';
+    }
+  while (endp >= line_num_start);
+  *--line_num_start = '1';
+  if (line_num_start < line_num_print)
+    line_num_print--;
+}
 
-  int c;
+/* Plain cat.  Copies the file behind `input_desc' to the file behind
+   `output_desc'.  */
 
-  /* Index in argv to processed argument.  */
-  int argind;
+static void
+simple_cat (
+     /* Pointer to the buffer, used by reads and writes.  */
+     unsigned char *buf,
 
-  /* Device number of the output (file or whatever).  */
-  int out_dev;
+     /* Number of characters preferably read or written by each read and write
+        call.  */
+     int bufsize)
+{
+  /* Actual number of characters read, and therefore written.  */
+  int n_read;
 
-  /* I-node number of the output.  */
-  int out_ino;
+  /* Loop until the end of the file.  */
 
-  /* Nonzero if the output file should not be the same as any input file. */
-  int check_redirection = 1;
+  for (;;)
+    {
+      /* Read a block of input.  */
 
-  /* Nonzero if we have ever read standard input. */
-  int have_read_stdin = 0;
+      n_read = safe_read (input_desc, buf, bufsize);
+      if (n_read < 0)
+       {
+         error (0, errno, "%s", infile);
+         exit_stat = 1;
+         return;
+       }
 
-  struct stat stat_buf;
+      /* End of this file?  */
 
-  /* 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 (n_read == 0)
+       break;
 
-/* If non-zero, call cat, otherwise call simple_cat to do the actual work. */
-  int options = 0;
+      /* Write this block out.  */
 
-  /* If non-zero, display usage information and exit.  */
-  static int show_help;
+      if (full_write (output_desc, buf, n_read) < 0)
+       error (1, errno, _("write error"));
+    }
+}
 
-  /* If non-zero, print the version on standard output then exit.  */
-  static int show_version;
+/* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC.
+   Called if any option more than -u was specified.
 
-  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}
-  };
+   A newline character is always put at the end of the buffer, to make
+   an explicit test for buffer end unnecessary.  */
 
-  program_name = argv[0];
+static void
+cat (
+     /* Pointer to the beginning of the input buffer.  */
+     unsigned char *inbuf,
 
-  /* Parse command line options.  */
+     /* Number of characters read in each read call.  */
+     int insize,
 
-  while ((c = getopt_long (argc, argv, "benstuvAET", long_options, (int *) 0))
-        != EOF)
-    {
-      switch (c)
-       {
-       case 0:
-         break;
+     /* Pointer to the beginning of the output buffer.  */
+     unsigned char *outbuf,
 
-       case 'b':
-         ++options;
-         numbers = 1;
-         numbers_at_empty_lines = 0;
-         break;
+     /* Number of characters written by each write call.  */
+     int outsize,
 
-       case 'e':
-         ++options;
-         mark_line_ends = 1;
-         quote = 1;
-         break;
+     /* 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)
+{
+  /* Last character read from the input buffer.  */
+  unsigned char ch;
 
-       case 'n':
-         ++options;
-         numbers = 1;
-         break;
+  /* Pointer to the next character in the input buffer.  */
+  unsigned char *bpin;
 
-       case 's':
-         ++options;
-         squeeze_empty_lines = 1;
-         break;
+  /* Pointer to the first non-valid byte in the input buffer, i.e. the
+     current end of the buffer.  */
+  unsigned char *eob;
 
-       case 't':
-         ++options;
-         output_tabs = 0;
-         quote = 1;
-         break;
+  /* Pointer to the position where the next character shall be written.  */
+  unsigned char *bpout;
 
-       case 'u':
-         /* We provide the -u feature unconditionally.  */
-         break;
+  /* Number of characters read by the last read call.  */
+  int n_read;
 
-       case 'v':
-         ++options;
-         quote = 1;
-         break;
+  /* Determines how many consecutive newlines there have been in the
+     input.  0 newlines makes NEWLINES -1, 1 newline makes NEWLINES 1,
+     etc.  Initially 0 to indicate that we are at the beginning of a
+     new line.  The "state" of the procedure is determined by
+     NEWLINES.  */
+  int newlines = newlines2;
 
-       case 'A':
-         ++options;
-         quote = 1;
-         mark_line_ends = 1;
-         output_tabs = 0;
-         break;
+#ifdef FIONREAD
+  /* If nonzero, use the FIONREAD ioctl, as an optimization.
+     (On Ultrix, it is not supported on NFS filesystems.)  */
+  int use_fionread = 1;
+#endif
 
-       case 'E':
-         ++options;
-         mark_line_ends = 1;
-         break;
+  /* The inbuf pointers are initialized so that BPIN > EOB, and thereby input
+     is read immediately.  */
 
-       case 'T':
-         ++options;
-         output_tabs = 0;
-         break;
+  eob = inbuf;
+  bpin = eob + 1;
 
-       default:
-         usage ();
-       }
-    }
+  bpout = outbuf;
 
-  if (show_version)
+  for (;;)
     {
-      printf ("%s\n", version_string);
-      exit (0);
-    }
+      do
+       {
+         /* Write if there are at least OUTSIZE bytes in OUTBUF.  */
 
-  if (show_help)
-    usage ();
+         if (bpout - outbuf >= outsize)
+           {
+             unsigned char *wp = outbuf;
+             do
+               {
+                 if (full_write (output_desc, wp, outsize) < 0)
+                   error (1, errno, _("write error"));
+                 wp += outsize;
+               }
+             while (bpout - wp >= outsize);
 
-  output_desc = 1;
+             /* Move the remaining bytes to the beginning of the
+                buffer.  */
 
-  /* Get device, i-node number, and optimal blocksize of output.  */
+             memmove (outbuf, wp, bpout - wp);
+             bpout = outbuf + (bpout - wp);
+           }
 
-  if (fstat (output_desc, &stat_buf) < 0)
-    error (1, errno, "standard output");
+         /* Is INBUF empty?  */
 
-  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 (bpin > eob)
+           {
+#ifdef FIONREAD
+             int n_to_read = 0;
 
-  if (S_ISREG (stat_buf.st_mode))
-    {
-      out_dev = stat_buf.st_dev;
-      out_ino = stat_buf.st_ino;
-    }
-  else
-    check_redirection = 0;
+             /* Is there any input to read immediately?
+                If not, we are about to wait,
+                so write all buffered output before waiting.  */
 
-  /* Check if any of the input files are the same as the output file.  */
+             if (use_fionread
+                 && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
+               {
+                 /* Ultrix returns EOPNOTSUPP on NFS;
+                    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);
+                     exit_stat = 1;
+                     newlines2 = newlines;
+                     return;
+                   }
+               }
+             if (n_to_read == 0)
+#endif
+               {
+                 int n_write = bpout - outbuf;
 
-  /* Main loop.  */
-
-  infile = "-";
-  argind = optind;
-
-  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,
-        --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 (++argind < argc);
-
-  if (have_read_stdin && close (0) < 0)
-    error (1, errno, "-");
-  if (close (1) < 0)
-    error (1, errno, "write error");
-
-  exit (exit_stat);
-}
-\f
-/* Plain cat.  Copies the file behind `input_desc' to the file behind
-   `output_desc'.  */
-
-static void
-simple_cat (buf, bufsize)
-     /* Pointer to the buffer, used by reads and writes.  */
-     unsigned char *buf;
-
-     /* Number of characters preferably read or written by each read and write
-        call.  */
-     int bufsize;
-{
-  /* Actual number of characters read, and therefore written.  */
-  int n_read;
-
-  /* Loop until the end of the file.  */
-
-  for (;;)
-    {
-      /* Read a block of input.  */
-
-      n_read = read (input_desc, buf, bufsize);
-      if (n_read < 0)
-       {
-         error (0, errno, "%s", infile);
-         exit_stat = 1;
-         return;
-       }
-
-      /* End of this file?  */
-
-      if (n_read == 0)
-       break;
-
-      /* Write this block out.  */
-
-      if (write (output_desc, buf, n_read) != n_read)
-       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.
-
-   A newline character is always put at the end of the buffer, to make
-   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)
-
-     /* Pointer to the beginning of the input buffer.  */
-     unsigned char *inbuf;
-
-     /* Number of characters read in each read call.  */
-     int insize;
-
-     /* Pointer to the beginning of the output buffer.  */
-     unsigned char *outbuf;
-
-     /* Number of characters written by each write call.  */
-     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;
-{
-  /* Last character read from the input buffer.  */
-  unsigned char ch;
-
-  /* Pointer to the next character in the input buffer.  */
-  unsigned char *bpin;
-
-  /* Pointer to the first non-valid byte in the input buffer, i.e. the
-     current end of the buffer.  */
-  unsigned char *eob;
-
-  /* Pointer to the position where the next character shall be written.  */
-  unsigned char *bpout;
-
-  /* Number of characters read by the last read call.  */
-  int n_read;
-
-  /* Determines how many consecutive newlines there have been in the
-     input.  0 newlines makes NEWLINES -1, 1 newline makes NEWLINES 1,
-     etc.  Initially 0 to indicate that we are at the beginning of a
-     new line.  The "state" of the procedure is determined by
-     NEWLINES.  */
-  int newlines = newlines2;
-
-#ifdef FIONREAD
-  /* If nonzero, use the FIONREAD ioctl, as an optimization.
-     (On Ultrix, it is not supported on NFS filesystems.)  */
-  int use_fionread = 1;
-#endif
-
-  /* The inbuf pointers are initialized so that BPIN > EOB, and thereby input
-     is read immediately.  */
-
-  eob = inbuf;
-  bpin = eob + 1;
-
-  bpout = outbuf;
-
-  for (;;)
-    {
-      do
-       {
-         /* Write if there are at least OUTSIZE bytes in OUTBUF.  */
-
-         if (bpout - outbuf >= outsize)
-           {
-             unsigned char *wp = outbuf;
-             do
-               {
-                 if (write (output_desc, wp, outsize) != outsize)
-                   error (1, errno, "write error");
-                 wp += outsize;
-               }
-             while (bpout - wp >= outsize);
-
-             /* Move the remaining bytes to the beginning of the
-                buffer.  */
-
-             bcopy (wp, outbuf, bpout - wp);
-             bpout = outbuf + (bpout - wp);
-           }
-
-         /* Is INBUF empty?  */
-
-         if (bpin > eob)
-           {
-#ifdef FIONREAD
-             int n_to_read = 0;
-
-             /* Is there any input to read immediately?
-                If not, we are about to wait,
-                so write all buffered output before waiting.  */
-
-             if (use_fionread
-                 && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
-               {
-                 /* Ultrix returns EOPNOTSUPP on NFS;
-                    HP-UX returns ENOTTY on pipes.
-                    SunOS returns EINVAL and
-                    More/BSD returns ENODEV on special files
-                    like /dev/null.  */
-                 if (errno == EOPNOTSUPP || errno == ENOTTY
-                     || errno == EINVAL || errno == ENODEV)
-                   use_fionread = 0;
-                 else
-                   {
-                     error (0, errno, "cannot do ioctl on `%s'", infile);
-                     exit_stat = 1;
-                     newlines2 = newlines;
-                     return;
-                   }
-               }
-             if (n_to_read == 0)
-#endif
-               {
-                 int n_write = bpout - outbuf;
-
-                 if (write (output_desc, outbuf, n_write) != n_write)
-                   error (1, errno, "write error");
-                 bpout = outbuf;
-               }
+                 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);
@@ -673,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;
-  do
-    {
-      if ((*endp)++ < '9')
-       return;
-      *endp-- = '0';
-    }
-  while (endp >= line_num_start);
-  *--line_num_start = '1';
-  if (line_num_start < line_num_print)
-    line_num_print--;
+  /* 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 (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 (++argind < argc);
+
+  if (have_read_stdin && close (0) < 0)
+    error (1, errno, "-");
+  if (close (1) < 0)
+    error (1, errno, _("write error"));
+
+  exit (exit_stat);
 }