diff --git a/src/cat.c b/src/cat.c index d216c33d9..a4421ee8c 100644 --- a/src/cat.c +++ b/src/cat.c @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - + /* Differences from the Unix cat: * Always unbuffered, -u is ignored. * Usually much faster than other versions of cat, the difference @@ -43,10 +43,6 @@ char *xmalloc (); int full_write (); int safe_read (); -static void cat (); -static void next_line_num (); -static void simple_cat (); - /* Name under which this program was invoked. */ char *program_name; @@ -114,7 +110,336 @@ With no FILE, or when FILE is -, read standard input.\n\ exit (status); } - +/* Compute the next line number. */ + +static void +next_line_num () +{ + 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--; +} + +/* Plain cat. Copies the file behind `input_desc' to the file behind + `output_desc'. */ + +static void +simple_cat ( + /* 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 = safe_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 (full_write (output_desc, buf, n_read) < 0) + error (1, errno, _("write error")); + } +} + +/* 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 ( + /* 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 (full_write (output_desc, wp, outsize) < 0) + error (1, errno, _("write error")); + wp += outsize; + } + while (bpout - wp >= outsize); + + /* Move the remaining bytes to the beginning of the + buffer. */ + + memmove (outbuf, wp, 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. + 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; + + if (full_write (output_desc, outbuf, n_write) < 0) + error (1, errno, _("write error")); + bpout = outbuf; + } + + /* Read more input into INBUF. */ + + n_read = safe_read (input_desc, inbuf, insize); + if (n_read < 0) + { + error (0, errno, "%s", infile); + exit_stat = 1; + newlines2 = newlines; + return; + } + if (n_read == 0) + { + newlines2 = newlines; + return; + } + + /* Update the pointers and insert a sentinel at the buffer + end. */ + + bpin = inbuf; + eob = bpin + n_read; + *eob = '\n'; + } + else + { + /* It was a real (not a sentinel) newline. */ + + /* Was the last line empty? + (i.e. have two or more consecutive newlines been read?) */ + + if (++newlines > 0) + { + /* Are multiple adjacent empty lines to be substituted by + single ditto (-s), and this was the second empty line? */ + + if (squeeze_empty_lines && newlines >= 2) + { + ch = *bpin++; + continue; + } + + /* Are line numbers to be written at empty lines (-n)? */ + + if (numbers && numbers_at_empty_lines) + { + next_line_num (); + bpout = (unsigned char *) stpcpy (bpout, line_num_print); + } + } + + /* Output a currency symbol if requested (-e). */ + + if (mark_line_ends) + *bpout++ = '$'; + + /* Output the newline. */ + + *bpout++ = '\n'; + } + ch = *bpin++; + } + while (ch == '\n'); + + /* Are we at the beginning of a line, and line numbers are requested? */ + + if (newlines >= 0 && numbers) + { + next_line_num (); + bpout = (unsigned char *) stpcpy (bpout, line_num_print); + } + + /* Here CH cannot contain a newline character. */ + + /* The loops below continue until a newline character is found, + which means that the buffer is empty or that a proper newline + has been found. */ + + /* If quoting, i.e. at least one of -v, -e, or -t specified, + scan for chars that need conversion. */ + if (quote) + for (;;) + { + if (ch >= 32) + { + if (ch < 127) + *bpout++ = ch; + else if (ch == 127) + *bpout++ = '^', + *bpout++ = '?'; + else + { + *bpout++ = 'M', + *bpout++ = '-'; + if (ch >= 128 + 32) + if (ch < 128 + 127) + *bpout++ = ch - 128; + else + *bpout++ = '^', + *bpout++ = '?'; + else + *bpout++ = '^', + *bpout++ = ch - 128 + 64; + } + } + else if (ch == '\t' && output_tabs) + *bpout++ = '\t'; + else if (ch == '\n') + { + newlines = -1; + break; + } + else + *bpout++ = '^', + *bpout++ = ch + 64; + + ch = *bpin++; + } + else + /* Not quoting, neither of -v, -e, or -t specified. */ + for (;;) + { + if (ch == '\t' && !output_tabs) + *bpout++ = '^', + *bpout++ = ch + 64; + else if (ch != '\n') + *bpout++ = ch; + else + { + newlines = -1; + break; + } + + ch = *bpin++; + } + } +} + void main (argc, argv) int argc; @@ -397,336 +722,3 @@ main (argc, argv) exit (exit_stat); } - -/* 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 = safe_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 (full_write (output_desc, buf, n_read) < 0) - error (1, errno, _("write error")); - } -} - -/* 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 (full_write (output_desc, wp, outsize) < 0) - error (1, errno, _("write error")); - wp += outsize; - } - while (bpout - wp >= outsize); - - /* Move the remaining bytes to the beginning of the - buffer. */ - - memmove (outbuf, wp, 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. - 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; - - if (full_write (output_desc, outbuf, n_write) < 0) - error (1, errno, _("write error")); - bpout = outbuf; - } - - /* Read more input into INBUF. */ - - n_read = safe_read (input_desc, inbuf, insize); - if (n_read < 0) - { - error (0, errno, "%s", infile); - exit_stat = 1; - newlines2 = newlines; - return; - } - if (n_read == 0) - { - newlines2 = newlines; - return; - } - - /* Update the pointers and insert a sentinel at the buffer - end. */ - - bpin = inbuf; - eob = bpin + n_read; - *eob = '\n'; - } - else - { - /* It was a real (not a sentinel) newline. */ - - /* Was the last line empty? - (i.e. have two or more consecutive newlines been read?) */ - - if (++newlines > 0) - { - /* Are multiple adjacent empty lines to be substituted by - single ditto (-s), and this was the second empty line? */ - - if (squeeze_empty_lines && newlines >= 2) - { - ch = *bpin++; - continue; - } - - /* Are line numbers to be written at empty lines (-n)? */ - - if (numbers && numbers_at_empty_lines) - { - next_line_num (); - bpout = (unsigned char *) stpcpy (bpout, line_num_print); - } - } - - /* Output a currency symbol if requested (-e). */ - - if (mark_line_ends) - *bpout++ = '$'; - - /* Output the newline. */ - - *bpout++ = '\n'; - } - ch = *bpin++; - } - while (ch == '\n'); - - /* Are we at the beginning of a line, and line numbers are requested? */ - - if (newlines >= 0 && numbers) - { - next_line_num (); - bpout = (unsigned char *) stpcpy (bpout, line_num_print); - } - - /* Here CH cannot contain a newline character. */ - - /* The loops below continue until a newline character is found, - which means that the buffer is empty or that a proper newline - has been found. */ - - /* If quoting, i.e. at least one of -v, -e, or -t specified, - scan for chars that need conversion. */ - if (quote) - for (;;) - { - if (ch >= 32) - { - if (ch < 127) - *bpout++ = ch; - else if (ch == 127) - *bpout++ = '^', - *bpout++ = '?'; - else - { - *bpout++ = 'M', - *bpout++ = '-'; - if (ch >= 128 + 32) - if (ch < 128 + 127) - *bpout++ = ch - 128; - else - *bpout++ = '^', - *bpout++ = '?'; - else - *bpout++ = '^', - *bpout++ = ch - 128 + 64; - } - } - else if (ch == '\t' && output_tabs) - *bpout++ = '\t'; - else if (ch == '\n') - { - newlines = -1; - break; - } - else - *bpout++ = '^', - *bpout++ = ch + 64; - - ch = *bpin++; - } - else - /* Not quoting, neither of -v, -e, or -t specified. */ - for (;;) - { - if (ch == '\t' && !output_tabs) - *bpout++ = '^', - *bpout++ = ch + 64; - else if (ch != '\n') - *bpout++ = ch; - else - { - newlines = -1; - break; - } - - ch = *bpin++; - } - } -} - -/* Compute the next line number. */ - -static void -next_line_num () -{ - 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--; -}