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. */
-\f
+
/* 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 <sys/ioctl.h>
#endif
#include "system.h"
-#include "version.h"
#include "error.h"
/* Undefine, to avoid warning about redefinition on some systems. */
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;
static int exit_stat = 0;
static void
-usage (status)
- int status;
+usage (int status)
{
if (status != 0)
fprintf (stderr, _("Try `%s --help' for more information.\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 (2);
- }
- }
+ bpout = outbuf;
- if (show_version)
+ for (;;)
{
- printf ("cat - %s\n", version_string);
- 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"));
+ do
+ {
+ /* Write if there are at least OUTSIZE bytes in OUTBUF. */
- 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 (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);
- 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
- }
+ /* Move the remaining bytes to the beginning of the
+ buffer. */
- /* Check if any of the input files are the same as the output file. */
+ memmove (outbuf, wp, bpout - wp);
+ bpout = outbuf + (bpout - wp);
+ }
- /* Main loop. */
+ /* Is INBUF empty? */
- infile = "-";
- argind = optind;
+ if (bpin > eob)
+ {
+#ifdef FIONREAD
+ int n_to_read = 0;
- 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 = 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"));
- }
-}
-\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 (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. */
+ /* 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)
}
}
-/* 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);
}