LCOV - code coverage report
Current view: top level - src - cat.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 213 241 88.4 %
Date: 2018-01-30 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /* cat -- concatenate files and print on the standard output.
       2             :    Copyright (C) 88, 90, 91, 1995-2007 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : /* Differences from the Unix cat:
      18             :    * Always unbuffered, -u is ignored.
      19             :    * Usually much faster than other versions of cat, the difference
      20             :    is especially apparent when using the -v option.
      21             : 
      22             :    By tege@sics.se, Torbjorn Granlund, advised by rms, Richard Stallman.  */
      23             : 
      24             : #include <config.h>
      25             : 
      26             : #include <stdio.h>
      27             : #include <getopt.h>
      28             : #include <sys/types.h>
      29             : 
      30             : #if HAVE_STROPTS_H
      31             : # include <stropts.h>
      32             : #endif
      33             : #if HAVE_SYS_IOCTL_H
      34             : # include <sys/ioctl.h>
      35             : #endif
      36             : 
      37             : #include "system.h"
      38             : #include "error.h"
      39             : #include "full-write.h"
      40             : #include "quote.h"
      41             : #include "safe-read.h"
      42             : 
      43             : /* The official name of this program (e.g., no `g' prefix).  */
      44             : #define PROGRAM_NAME "cat"
      45             : 
      46             : #define AUTHORS "Torbjorn Granlund", "Richard M. Stallman"
      47             : 
      48             : /* Undefine, to avoid warning about redefinition on some systems.  */
      49             : #undef max
      50             : #define max(h,i) ((h) > (i) ? (h) : (i))
      51             : 
      52             : /* Name under which this program was invoked.  */
      53             : char *program_name;
      54             : 
      55             : /* Name of input file.  May be "-".  */
      56             : static char const *infile;
      57             : 
      58             : /* Descriptor on which input file is open.  */
      59             : static int input_desc;
      60             : 
      61             : /* Buffer for line numbers.
      62             :    An 11 digit counter may overflow within an hour on a P2/466,
      63             :    an 18 digit counter needs about 1000y */
      64             : #define LINE_COUNTER_BUF_LEN 20
      65             : static char line_buf[LINE_COUNTER_BUF_LEN] =
      66             :   {
      67             :     ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
      68             :     ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0',
      69             :     '\t', '\0'
      70             :   };
      71             : 
      72             : /* Position in `line_buf' where printing starts.  This will not change
      73             :    unless the number of lines is larger than 999999.  */
      74             : static char *line_num_print = line_buf + LINE_COUNTER_BUF_LEN - 8;
      75             : 
      76             : /* Position of the first digit in `line_buf'.  */
      77             : static char *line_num_start = line_buf + LINE_COUNTER_BUF_LEN - 3;
      78             : 
      79             : /* Position of the last digit in `line_buf'.  */
      80             : static char *line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3;
      81             : 
      82             : /* Preserves the `cat' function's local `newlines' between invocations.  */
      83             : static int newlines2 = 0;
      84             : 
      85             : void
      86           9 : usage (int status)
      87             : {
      88           9 :   if (status != EXIT_SUCCESS)
      89           8 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
      90             :              program_name);
      91             :   else
      92             :     {
      93           1 :       printf (_("\
      94             : Usage: %s [OPTION] [FILE]...\n\
      95             : "),
      96             :               program_name);
      97           1 :       fputs (_("\
      98             : Concatenate FILE(s), or standard input, to standard output.\n\
      99             : \n\
     100             :   -A, --show-all           equivalent to -vET\n\
     101             :   -b, --number-nonblank    number nonempty output lines\n\
     102             :   -e                       equivalent to -vE\n\
     103             :   -E, --show-ends          display $ at end of each line\n\
     104             :   -n, --number             number all output lines\n\
     105             :   -s, --squeeze-blank      suppress repeated empty output lines\n\
     106             : "), stdout);
     107           1 :       fputs (_("\
     108             :   -t                       equivalent to -vT\n\
     109             :   -T, --show-tabs          display TAB characters as ^I\n\
     110             :   -u                       (ignored)\n\
     111             :   -v, --show-nonprinting   use ^ and M- notation, except for LFD and TAB\n\
     112             : "), stdout);
     113           1 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     114           1 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     115           1 :       fputs (_("\
     116             : \n\
     117             : With no FILE, or when FILE is -, read standard input.\n\
     118             : "), stdout);
     119           1 :       printf (_("\
     120             : \n\
     121             : Examples:\n\
     122             :   %s f - g  Output f's contents, then standard input, then g's contents.\n\
     123             :   %s        Copy standard input to standard output.\n\
     124             : "),
     125             :               program_name, program_name);
     126           1 :       emit_bug_reporting_address ();
     127             :     }
     128           9 :   exit (status);
     129             : }
     130             : 
     131             : /* Compute the next line number.  */
     132             : 
     133             : static void
     134          26 : next_line_num (void)
     135             : {
     136          26 :   char *endp = line_num_end;
     137             :   do
     138             :     {
     139          26 :       if ((*endp)++ < '9')
     140          25 :         return;
     141           1 :       *endp-- = '0';
     142             :     }
     143           1 :   while (endp >= line_num_start);
     144           1 :   if (line_num_start > line_buf)
     145           1 :     *--line_num_start = '1';
     146             :   else
     147           0 :     *line_buf = '>';
     148           1 :   if (line_num_start < line_num_print)
     149           0 :     line_num_print--;
     150             : }
     151             : 
     152             : /* Plain cat.  Copies the file behind `input_desc' to STDOUT_FILENO.
     153             :    Return true if successful.  */
     154             : 
     155             : static bool
     156          51 : simple_cat (
     157             :      /* Pointer to the buffer, used by reads and writes.  */
     158             :      char *buf,
     159             : 
     160             :      /* Number of characters preferably read or written by each read and write
     161             :         call.  */
     162             :      size_t bufsize)
     163             : {
     164             :   /* Actual number of characters read, and therefore written.  */
     165             :   size_t n_read;
     166             : 
     167             :   /* Loop until the end of the file.  */
     168             : 
     169             :   for (;;)
     170             :     {
     171             :       /* Read a block of input.  */
     172             : 
     173          69 :       n_read = safe_read (input_desc, buf, bufsize);
     174          51 :       if (n_read == SAFE_READ_ERROR)
     175             :         {
     176           3 :           error (0, errno, "%s", infile);
     177           3 :           return false;
     178             :         }
     179             : 
     180             :       /* End of this file?  */
     181             : 
     182          48 :       if (n_read == 0)
     183          30 :         return true;
     184             : 
     185             :       /* Write this block out.  */
     186             : 
     187             :       {
     188             :         /* The following is ok, since we know that 0 < n_read.  */
     189          18 :         size_t n = n_read;
     190          18 :         if (full_write (STDOUT_FILENO, buf, n) != n)
     191           0 :           error (EXIT_FAILURE, errno, _("write error"));
     192             :       }
     193             :     }
     194             : }
     195             : 
     196             : /* Write any pending output to STDOUT_FILENO.
     197             :    Pending is defined to be the *BPOUT - OUTBUF bytes starting at OUTBUF.
     198             :    Then set *BPOUT to OUTPUT if it's not already that value.  */
     199             : 
     200             : static inline void
     201          61 : write_pending (char *outbuf, char **bpout)
     202             : {
     203          61 :   size_t n_write = *bpout - outbuf;
     204          61 :   if (0 < n_write)
     205             :     {
     206          30 :       if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write)
     207           0 :         error (EXIT_FAILURE, errno, _("write error"));
     208          30 :       *bpout = outbuf;
     209             :     }
     210          61 : }
     211             : 
     212             : /* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC.
     213             :    Return true if successful.
     214             :    Called if any option more than -u was specified.
     215             : 
     216             :    A newline character is always put at the end of the buffer, to make
     217             :    an explicit test for buffer end unnecessary.  */
     218             : 
     219             : static bool
     220          33 : cat (
     221             :      /* Pointer to the beginning of the input buffer.  */
     222             :      char *inbuf,
     223             : 
     224             :      /* Number of characters read in each read call.  */
     225             :      size_t insize,
     226             : 
     227             :      /* Pointer to the beginning of the output buffer.  */
     228             :      char *outbuf,
     229             : 
     230             :      /* Number of characters written by each write call.  */
     231             :      size_t outsize,
     232             : 
     233             :      /* Variables that have values according to the specified options.  */
     234             :      bool show_nonprinting,
     235             :      bool show_tabs,
     236             :      bool number,
     237             :      bool number_nonblank,
     238             :      bool show_ends,
     239             :      bool squeeze_blank)
     240             : {
     241             :   /* Last character read from the input buffer.  */
     242             :   unsigned char ch;
     243             : 
     244             :   /* Pointer to the next character in the input buffer.  */
     245             :   char *bpin;
     246             : 
     247             :   /* Pointer to the first non-valid byte in the input buffer, i.e. the
     248             :      current end of the buffer.  */
     249             :   char *eob;
     250             : 
     251             :   /* Pointer to the position where the next character shall be written.  */
     252             :   char *bpout;
     253             : 
     254             :   /* Number of characters read by the last read call.  */
     255             :   size_t n_read;
     256             : 
     257             :   /* Determines how many consecutive newlines there have been in the
     258             :      input.  0 newlines makes NEWLINES -1, 1 newline makes NEWLINES 1,
     259             :      etc.  Initially 0 to indicate that we are at the beginning of a
     260             :      new line.  The "state" of the procedure is determined by
     261             :      NEWLINES.  */
     262          33 :   int newlines = newlines2;
     263             : 
     264             : #ifdef FIONREAD
     265             :   /* If nonzero, use the FIONREAD ioctl, as an optimization.
     266             :      (On Ultrix, it is not supported on NFS file systems.)  */
     267          33 :   bool use_fionread = true;
     268             : #endif
     269             : 
     270             :   /* The inbuf pointers are initialized so that BPIN > EOB, and thereby input
     271             :      is read immediately.  */
     272             : 
     273          33 :   eob = inbuf;
     274          33 :   bpin = eob + 1;
     275             : 
     276          33 :   bpout = outbuf;
     277             : 
     278          13 :   for (;;)
     279             :     {
     280             :       do
     281             :         {
     282             :           /* Write if there are at least OUTSIZE bytes in OUTBUF.  */
     283             : 
     284         251 :           if (outbuf + outsize <= bpout)
     285             :             {
     286           0 :               char *wp = outbuf;
     287             :               size_t remaining_bytes;
     288             :               do
     289             :                 {
     290           0 :                   if (full_write (STDOUT_FILENO, wp, outsize) != outsize)
     291           0 :                     error (EXIT_FAILURE, errno, _("write error"));
     292           0 :                   wp += outsize;
     293           0 :                   remaining_bytes = bpout - wp;
     294             :                 }
     295           0 :               while (outsize <= remaining_bytes);
     296             : 
     297             :               /* Move the remaining bytes to the beginning of the
     298             :                  buffer.  */
     299             : 
     300           0 :               memmove (outbuf, wp, remaining_bytes);
     301           0 :               bpout = outbuf + remaining_bytes;
     302             :             }
     303             : 
     304             :           /* Is INBUF empty?  */
     305             : 
     306         251 :           if (bpin > eob)
     307             :             {
     308          63 :               bool input_pending = false;
     309             : #ifdef FIONREAD
     310          63 :               int n_to_read = 0;
     311             : 
     312             :               /* Is there any input to read immediately?
     313             :                  If not, we are about to wait,
     314             :                  so write all buffered output before waiting.  */
     315             : 
     316          63 :               if (use_fionread
     317          63 :                   && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
     318             :                 {
     319             :                   /* Ultrix returns EOPNOTSUPP on NFS;
     320             :                      HP-UX returns ENOTTY on pipes.
     321             :                      SunOS returns EINVAL and
     322             :                      More/BSD returns ENODEV on special files
     323             :                      like /dev/null.
     324             :                      Irix-5 returns ENOSYS on pipes.  */
     325           3 :                   if (errno == EOPNOTSUPP || errno == ENOTTY
     326           2 :                       || errno == EINVAL || errno == ENODEV
     327           2 :                       || errno == ENOSYS)
     328           1 :                     use_fionread = false;
     329             :                   else
     330             :                     {
     331           2 :                       error (0, errno, _("cannot do ioctl on %s"), quote (infile));
     332           2 :                       newlines2 = newlines;
     333          35 :                       return false;
     334             :                     }
     335             :                 }
     336          61 :               if (n_to_read != 0)
     337          30 :                 input_pending = true;
     338             : #endif
     339             : 
     340          61 :               if (input_pending)
     341          30 :                 write_pending (outbuf, &bpout);
     342             : 
     343             :               /* Read more input into INBUF.  */
     344             : 
     345          61 :               n_read = safe_read (input_desc, inbuf, insize);
     346          61 :               if (n_read == SAFE_READ_ERROR)
     347             :                 {
     348          30 :                   error (0, errno, "%s", infile);
     349          30 :                   write_pending (outbuf, &bpout);
     350          30 :                   newlines2 = newlines;
     351          30 :                   return false;
     352             :                 }
     353          31 :               if (n_read == 0)
     354             :                 {
     355           1 :                   write_pending (outbuf, &bpout);
     356           1 :                   newlines2 = newlines;
     357           1 :                   return true;
     358             :                 }
     359             : 
     360             :               /* Update the pointers and insert a sentinel at the buffer
     361             :                  end.  */
     362             : 
     363          30 :               bpin = inbuf;
     364          30 :               eob = bpin + n_read;
     365          30 :               *eob = '\n';
     366             :             }
     367             :           else
     368             :             {
     369             :               /* It was a real (not a sentinel) newline.  */
     370             : 
     371             :               /* Was the last line empty?
     372             :                  (i.e. have two or more consecutive newlines been read?)  */
     373             : 
     374         188 :               if (++newlines > 0)
     375             :                 {
     376         185 :                   if (newlines >= 2)
     377             :                     {
     378             :                       /* Limit this to 2 here.  Otherwise, with lots of
     379             :                          consecutive newlines, the counter could wrap
     380             :                          around at INT_MAX.  */
     381         156 :                       newlines = 2;
     382             : 
     383             :                       /* Are multiple adjacent empty lines to be substituted
     384             :                          by single ditto (-s), and this was the second empty
     385             :                          line?  */
     386         156 :                       if (squeeze_blank)
     387             :                         {
     388           7 :                           ch = *bpin++;
     389           7 :                           continue;
     390             :                         }
     391             :                     }
     392             : 
     393             :                   /* Are line numbers to be written at empty lines (-n)?  */
     394             : 
     395         178 :                   if (number & !number_nonblank)
     396             :                     {
     397          24 :                       next_line_num ();
     398          24 :                       bpout = stpcpy (bpout, line_num_print);
     399             :                     }
     400             :                 }
     401             : 
     402             :               /* Output a currency symbol if requested (-e).  */
     403             : 
     404         181 :               if (show_ends)
     405          63 :                 *bpout++ = '$';
     406             : 
     407             :               /* Output the newline.  */
     408             : 
     409         181 :               *bpout++ = '\n';
     410             :             }
     411         211 :           ch = *bpin++;
     412             :         }
     413         218 :       while (ch == '\n');
     414             : 
     415             :       /* Are we at the beginning of a line, and line numbers are requested?  */
     416             : 
     417          13 :       if (newlines >= 0 && number)
     418             :         {
     419           2 :           next_line_num ();
     420           2 :           bpout = stpcpy (bpout, line_num_print);
     421             :         }
     422             : 
     423             :       /* Here CH cannot contain a newline character.  */
     424             : 
     425             :       /* The loops below continue until a newline character is found,
     426             :          which means that the buffer is empty or that a proper newline
     427             :          has been found.  */
     428             : 
     429             :       /* If quoting, i.e. at least one of -v, -e, or -t specified,
     430             :          scan for chars that need conversion.  */
     431          13 :       if (show_nonprinting)
     432             :         {
     433             :           for (;;)
     434             :             {
     435         102 :               if (ch >= 32)
     436             :                 {
     437          43 :                   if (ch < 127)
     438          38 :                     *bpout++ = ch;
     439           5 :                   else if (ch == 127)
     440             :                     {
     441           0 :                       *bpout++ = '^';
     442           0 :                       *bpout++ = '?';
     443             :                     }
     444             :                   else
     445             :                     {
     446           5 :                       *bpout++ = 'M';
     447           5 :                       *bpout++ = '-';
     448           5 :                       if (ch >= 128 + 32)
     449             :                         {
     450           3 :                           if (ch < 128 + 127)
     451           2 :                             *bpout++ = ch - 128;
     452             :                           else
     453             :                             {
     454           1 :                               *bpout++ = '^';
     455           1 :                               *bpout++ = '?';
     456             :                             }
     457             :                         }
     458             :                       else
     459             :                         {
     460           2 :                           *bpout++ = '^';
     461           2 :                           *bpout++ = ch - 128 + 64;
     462             :                         }
     463             :                     }
     464             :                 }
     465          13 :               else if (ch == '\t' && !show_tabs)
     466           2 :                 *bpout++ = '\t';
     467          11 :               else if (ch == '\n')
     468             :                 {
     469          10 :                   newlines = -1;
     470          10 :                   break;
     471             :                 }
     472             :               else
     473             :                 {
     474           1 :                   *bpout++ = '^';
     475           1 :                   *bpout++ = ch + 64;
     476             :                 }
     477             : 
     478          46 :               ch = *bpin++;
     479             :             }
     480             :         }
     481             :       else
     482             :         {
     483             :           /* Not quoting, neither of -v, -e, or -t specified.  */
     484             :           for (;;)
     485             :             {
     486           9 :               if (ch == '\t' && show_tabs)
     487             :                 {
     488           1 :                   *bpout++ = '^';
     489           1 :                   *bpout++ = ch + 64;
     490             :                 }
     491           5 :               else if (ch != '\n')
     492           2 :                 *bpout++ = ch;
     493             :               else
     494             :                 {
     495           3 :                   newlines = -1;
     496           3 :                   break;
     497             :                 }
     498             : 
     499           3 :               ch = *bpin++;
     500             :             }
     501             :         }
     502             :     }
     503             : }
     504             : 
     505             : int
     506          59 : main (int argc, char **argv)
     507             : {
     508             :   /* Optimal size of i/o operations of output.  */
     509             :   size_t outsize;
     510             : 
     511             :   /* Optimal size of i/o operations of input.  */
     512             :   size_t insize;
     513             : 
     514          59 :   size_t page_size = getpagesize ();
     515             : 
     516             :   /* Pointer to the input buffer.  */
     517             :   char *inbuf;
     518             : 
     519             :   /* Pointer to the output buffer.  */
     520             :   char *outbuf;
     521             : 
     522          59 :   bool ok = true;
     523             :   int c;
     524             : 
     525             :   /* Index in argv to processed argument.  */
     526             :   int argind;
     527             : 
     528             :   /* Device number of the output (file or whatever).  */
     529             :   dev_t out_dev;
     530             : 
     531             :   /* I-node number of the output.  */
     532             :   ino_t out_ino;
     533             : 
     534             :   /* True if the output file should not be the same as any input file.  */
     535          59 :   bool check_redirection = true;
     536             : 
     537             :   /* Nonzero if we have ever read standard input.  */
     538          59 :   bool have_read_stdin = false;
     539             : 
     540             :   struct stat stat_buf;
     541             : 
     542             :   /* Variables that are set according to the specified options.  */
     543          59 :   bool number = false;
     544          59 :   bool number_nonblank = false;
     545          59 :   bool squeeze_blank = false;
     546          59 :   bool show_ends = false;
     547          59 :   bool show_nonprinting = false;
     548          59 :   bool show_tabs = false;
     549          59 :   int file_open_mode = O_RDONLY;
     550             : 
     551             :   static struct option const long_options[] =
     552             :   {
     553             :     {"number-nonblank", no_argument, NULL, 'b'},
     554             :     {"number", no_argument, NULL, 'n'},
     555             :     {"squeeze-blank", no_argument, NULL, 's'},
     556             :     {"show-nonprinting", no_argument, NULL, 'v'},
     557             :     {"show-ends", no_argument, NULL, 'E'},
     558             :     {"show-tabs", no_argument, NULL, 'T'},
     559             :     {"show-all", no_argument, NULL, 'A'},
     560             :     {GETOPT_HELP_OPTION_DECL},
     561             :     {GETOPT_VERSION_OPTION_DECL},
     562             :     {NULL, 0, NULL, 0}
     563             :   };
     564             : 
     565             :   initialize_main (&argc, &argv);
     566          59 :   program_name = argv[0];
     567          59 :   setlocale (LC_ALL, "");
     568             :   bindtextdomain (PACKAGE, LOCALEDIR);
     569             :   textdomain (PACKAGE);
     570             : 
     571             :   /* Arrange to close stdout if we exit via the
     572             :      case_GETOPT_HELP_CHAR or case_GETOPT_VERSION_CHAR code.
     573             :      Normally STDOUT_FILENO is used rather than stdout, so
     574             :      close_stdout does nothing.  */
     575          59 :   atexit (close_stdout);
     576             : 
     577             :   /* Parse command line options.  */
     578             : 
     579         150 :   while ((c = getopt_long (argc, argv, "benstuvAET", long_options, NULL))
     580             :          != -1)
     581             :     {
     582          42 :       switch (c)
     583             :         {
     584          10 :         case 'b':
     585          10 :           number = true;
     586          10 :           number_nonblank = true;
     587          10 :           break;
     588             : 
     589          13 :         case 'e':
     590          13 :           show_ends = true;
     591          13 :           show_nonprinting = true;
     592          13 :           break;
     593             : 
     594           2 :         case 'n':
     595           2 :           number = true;
     596           2 :           break;
     597             : 
     598           1 :         case 's':
     599           1 :           squeeze_blank = true;
     600           1 :           break;
     601             : 
     602           1 :         case 't':
     603           1 :           show_tabs = true;
     604           1 :           show_nonprinting = true;
     605           1 :           break;
     606             : 
     607           0 :         case 'u':
     608             :           /* We provide the -u feature unconditionally.  */
     609           0 :           break;
     610             : 
     611           1 :         case 'v':
     612           1 :           show_nonprinting = true;
     613           1 :           break;
     614             : 
     615           1 :         case 'A':
     616           1 :           show_nonprinting = true;
     617           1 :           show_ends = true;
     618           1 :           show_tabs = true;
     619           1 :           break;
     620             : 
     621           1 :         case 'E':
     622           1 :           show_ends = true;
     623           1 :           break;
     624             : 
     625           2 :         case 'T':
     626           2 :           show_tabs = true;
     627           2 :           break;
     628             : 
     629           1 :         case_GETOPT_HELP_CHAR;
     630             : 
     631           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     632             : 
     633           8 :         default:
     634           8 :           usage (EXIT_FAILURE);
     635             :         }
     636             :     }
     637             : 
     638             :   /* Get device, i-node number, and optimal blocksize of output.  */
     639             : 
     640          49 :   if (fstat (STDOUT_FILENO, &stat_buf) < 0)
     641           0 :     error (EXIT_FAILURE, errno, _("standard output"));
     642             : 
     643          49 :   outsize = ST_BLKSIZE (stat_buf);
     644             :   /* Input file can be output file for non-regular files.
     645             :      fstat on pipes returns S_IFSOCK on some systems, S_IFIFO
     646             :      on others, so the checking should not be done for those types,
     647             :      and to allow things like cat < /dev/tty > /dev/tty, checking
     648             :      is not done for device files either.  */
     649             : 
     650          49 :   if (S_ISREG (stat_buf.st_mode))
     651             :     {
     652          49 :       out_dev = stat_buf.st_dev;
     653          49 :       out_ino = stat_buf.st_ino;
     654             :     }
     655             :   else
     656             :     {
     657           0 :       check_redirection = false;
     658             : #ifdef lint  /* Suppress `used before initialized' warning.  */
     659             :       out_dev = 0;
     660             :       out_ino = 0;
     661             : #endif
     662             :     }
     663             : 
     664          49 :   if (! (number | show_ends | squeeze_blank))
     665             :     {
     666          23 :       file_open_mode |= O_BINARY;
     667             :       if (O_BINARY && ! isatty (STDOUT_FILENO))
     668             :         freopen (NULL, "wb", stdout);
     669             :     }
     670             : 
     671             :   /* Check if any of the input files are the same as the output file.  */
     672             : 
     673             :   /* Main loop.  */
     674             : 
     675          49 :   infile = "-";
     676          49 :   argind = optind;
     677             : 
     678             :   do
     679             :     {
     680          72 :       if (argind < argc)
     681          45 :         infile = argv[argind];
     682             : 
     683          72 :       if (STREQ (infile, "-"))
     684             :         {
     685          60 :           have_read_stdin = true;
     686          60 :           input_desc = STDIN_FILENO;
     687             :           if ((file_open_mode & O_BINARY) && ! isatty (STDIN_FILENO))
     688             :             freopen (NULL, "rb", stdin);
     689             :         }
     690             :       else
     691             :         {
     692          12 :           input_desc = open (infile, file_open_mode);
     693          12 :           if (input_desc < 0)
     694             :             {
     695           6 :               error (0, errno, "%s", infile);
     696           6 :               ok = false;
     697           6 :               continue;
     698             :             }
     699             :         }
     700             : 
     701          66 :       if (fstat (input_desc, &stat_buf) < 0)
     702             :         {
     703           0 :           error (0, errno, "%s", infile);
     704           0 :           ok = false;
     705           0 :           goto contin;
     706             :         }
     707          66 :       insize = ST_BLKSIZE (stat_buf);
     708             : 
     709             :       /* Compare the device and i-node numbers of this input file with
     710             :          the corresponding values of the (output file associated with)
     711             :          stdout, and skip this input file if they coincide.  Input
     712             :          files cannot be redirected to themselves.  */
     713             : 
     714          66 :       if (check_redirection
     715          66 :           && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino
     716           0 :           && (input_desc != STDIN_FILENO))
     717             :         {
     718           0 :           error (0, 0, _("%s: input file is output file"), infile);
     719           0 :           ok = false;
     720           0 :           goto contin;
     721             :         }
     722             : 
     723             :       /* Select which version of `cat' to use.  If any format-oriented
     724             :          options were given use `cat'; otherwise use `simple_cat'.  */
     725             : 
     726         132 :       if (! (number | show_ends | show_nonprinting
     727          66 :              | show_tabs | squeeze_blank))
     728             :         {
     729          33 :           insize = max (insize, outsize);
     730          33 :           inbuf = xmalloc (insize + page_size - 1);
     731             : 
     732          33 :           ok &= simple_cat (ptr_align (inbuf, page_size), insize);
     733             :         }
     734             :       else
     735             :         {
     736          33 :           inbuf = xmalloc (insize + 1 + page_size - 1);
     737             : 
     738             :           /* Why are
     739             :              (OUTSIZE - 1 + INSIZE * 4 + LINE_COUNTER_BUF_LEN + PAGE_SIZE - 1)
     740             :              bytes allocated for the output buffer?
     741             : 
     742             :              A test whether output needs to be written is done when the input
     743             :              buffer empties or when a newline appears in the input.  After
     744             :              output is written, at most (OUTSIZE - 1) bytes will remain in the
     745             :              buffer.  Now INSIZE bytes of input is read.  Each input character
     746             :              may grow by a factor of 4 (by the prepending of M-^).  If all
     747             :              characters do, and no newlines appear in this block of input, we
     748             :              will have at most (OUTSIZE - 1 + INSIZE * 4) bytes in the buffer.
     749             :              If the last character in the preceding block of input was a
     750             :              newline, a line number may be written (according to the given
     751             :              options) as the first thing in the output buffer. (Done after the
     752             :              new input is read, but before processing of the input begins.)
     753             :              A line number requires seldom more than LINE_COUNTER_BUF_LEN
     754             :              positions.
     755             : 
     756             :              Align the output buffer to a page size boundary, for efficency on
     757             :              some paging implementations, so add PAGE_SIZE - 1 bytes to the
     758             :              request to make room for the alignment.  */
     759             : 
     760          66 :           outbuf = xmalloc (outsize - 1 + insize * 4 + LINE_COUNTER_BUF_LEN
     761          33 :                             + page_size - 1);
     762             : 
     763          66 :           ok &= cat (ptr_align (inbuf, page_size), insize,
     764          33 :                      ptr_align (outbuf, page_size), outsize, show_nonprinting,
     765             :                      show_tabs, number, number_nonblank, show_ends,
     766             :                      squeeze_blank);
     767             : 
     768          33 :           free (outbuf);
     769             :         }
     770             : 
     771          66 :       free (inbuf);
     772             : 
     773          66 :     contin:
     774          66 :       if (!STREQ (infile, "-") && close (input_desc) < 0)
     775             :         {
     776           0 :           error (0, errno, "%s", infile);
     777           0 :           ok = false;
     778             :         }
     779             :     }
     780          72 :   while (++argind < argc);
     781             : 
     782          49 :   if (have_read_stdin && close (STDIN_FILENO) < 0)
     783           0 :     error (EXIT_FAILURE, errno, _("closing standard input"));
     784             : 
     785          49 :   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
     786             : }

Generated by: LCOV version 1.10