Home | History | Annotate | Download | only in toolbox
      1 /* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1989, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Kevin Fall.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/param.h>
     36 #include <sys/stat.h>
     37 
     38 #include <ctype.h>
     39 #include <errno.h>
     40 #include <fcntl.h>
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <unistd.h>
     45 
     46 #define CAT_BUFSIZ (4096)
     47 
     48 static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag;
     49 static int rval;
     50 static const char *filename;
     51 
     52 static void
     53 cook_buf(FILE *fp)
     54 {
     55 	int ch, gobble, line, prev;
     56 	int stdout_err = 0;
     57 
     58 	line = gobble = 0;
     59 	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
     60 		if (prev == '\n') {
     61 			if (ch == '\n') {
     62 				if (sflag) {
     63 					if (!gobble && putchar(ch) == EOF)
     64 						break;
     65 					gobble = 1;
     66 					continue;
     67 				}
     68 				if (nflag) {
     69 					if (!bflag) {
     70 						if (fprintf(stdout,
     71 						    "%6d\t", ++line) < 0) {
     72 							stdout_err++;
     73 							break;
     74 						}
     75 					} else if (eflag) {
     76 						if (fprintf(stdout,
     77 						    "%6s\t", "") < 0) {
     78 							stdout_err++;
     79 							break;
     80 						}
     81 					}
     82 				}
     83 			} else if (nflag) {
     84 				if (fprintf(stdout, "%6d\t", ++line) < 0) {
     85 					stdout_err++;
     86 					break;
     87 				}
     88 			}
     89 		}
     90 		gobble = 0;
     91 		if (ch == '\n') {
     92 			if (eflag)
     93 				if (putchar('$') == EOF)
     94 					break;
     95 		} else if (ch == '\t') {
     96 			if (tflag) {
     97 				if (putchar('^') == EOF || putchar('I') == EOF)
     98 					break;
     99 				continue;
    100 			}
    101 		} else if (vflag) {
    102 			if (!isascii(ch)) {
    103 				if (putchar('M') == EOF || putchar('-') == EOF)
    104 					break;
    105 				ch = (ch) & 0x7f;
    106 			}
    107 			if (iscntrl(ch)) {
    108 				if (putchar('^') == EOF ||
    109 				    putchar(ch == '\177' ? '?' :
    110 				    ch | 0100) == EOF)
    111 					break;
    112 				continue;
    113 			}
    114 		}
    115 		if (putchar(ch) == EOF)
    116 			break;
    117 	}
    118 	if (stdout_err) {
    119 		perror(filename);
    120 		rval = 1;
    121 	}
    122 }
    123 
    124 static void
    125 cook_args(char **argv)
    126 {
    127 	FILE *fp;
    128 
    129 	fp = stdin;
    130 	filename = "stdin";
    131 	do {
    132 		if (*argv) {
    133 			if (!strcmp(*argv, "-"))
    134 				fp = stdin;
    135 			else if ((fp = fopen(*argv,
    136 			    fflag ? "rf" : "r")) == NULL) {
    137 				perror("fopen");
    138 				rval = 1;
    139 				++argv;
    140 				continue;
    141 			}
    142 			filename = *argv++;
    143 		}
    144 		cook_buf(fp);
    145 		if (fp != stdin)
    146 			fclose(fp);
    147 	} while (*argv);
    148 }
    149 
    150 static void
    151 raw_cat(int rfd)
    152 {
    153 	static char *buf;
    154 	static char fb_buf[CAT_BUFSIZ];
    155 	static size_t bsize;
    156 
    157 	struct stat sbuf;
    158 	ssize_t nr, nw, off;
    159 	int wfd;
    160 
    161 	wfd = fileno(stdout);
    162 	if (buf == NULL) {
    163 		if (fstat(wfd, &sbuf) == 0) {
    164 			bsize = sbuf.st_blksize > CAT_BUFSIZ ?
    165 			    sbuf.st_blksize : CAT_BUFSIZ;
    166 			buf = malloc(bsize);
    167 		}
    168 		if (buf == NULL) {
    169 			buf = fb_buf;
    170 			bsize = CAT_BUFSIZ;
    171 		}
    172 	}
    173 	while ((nr = read(rfd, buf, bsize)) > 0)
    174 		for (off = 0; nr; nr -= nw, off += nw)
    175 			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
    176 			{
    177 				perror("write");
    178 				exit(EXIT_FAILURE);
    179 			}
    180 	if (nr < 0) {
    181 		fprintf(stderr,"%s: invalid length\n", filename);
    182 		rval = 1;
    183 	}
    184 }
    185 
    186 static void
    187 raw_args(char **argv)
    188 {
    189 	int fd;
    190 
    191 	fd = fileno(stdin);
    192 	filename = "stdin";
    193 	do {
    194 		if (*argv) {
    195 			if (!strcmp(*argv, "-"))
    196 				fd = fileno(stdin);
    197 			else if (fflag) {
    198 				struct stat st;
    199 				fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
    200 				if (fd < 0)
    201 					goto skip;
    202 
    203 				if (fstat(fd, &st) == -1) {
    204 					close(fd);
    205 					goto skip;
    206 				}
    207 				if (!S_ISREG(st.st_mode)) {
    208 					close(fd);
    209 					errno = EINVAL;
    210 					goto skipnomsg;
    211 				}
    212 			}
    213 			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
    214 skip:
    215 				perror(*argv);
    216 skipnomsg:
    217 				rval = 1;
    218 				++argv;
    219 				continue;
    220 			}
    221 			filename = *argv++;
    222 		}
    223 		raw_cat(fd);
    224 		if (fd != fileno(stdin))
    225 			close(fd);
    226 	} while (*argv);
    227 }
    228 
    229 int
    230 cat_main(int argc, char *argv[])
    231 {
    232 	int ch;
    233 	struct flock stdout_lock;
    234 
    235 	while ((ch = getopt(argc, argv, "beflnstv")) != -1)
    236 		switch (ch) {
    237 		case 'b':
    238 			bflag = nflag = 1;	/* -b implies -n */
    239 			break;
    240 		case 'e':
    241 			eflag = vflag = 1;	/* -e implies -v */
    242 			break;
    243 		case 'f':
    244 			fflag = 1;
    245 			break;
    246 		case 'l':
    247 			lflag = 1;
    248 			break;
    249 		case 'n':
    250 			nflag = 1;
    251 			break;
    252 		case 's':
    253 			sflag = 1;
    254 			break;
    255 		case 't':
    256 			tflag = vflag = 1;	/* -t implies -v */
    257 			break;
    258 		case 'v':
    259 			vflag = 1;
    260 			break;
    261 		default:
    262 		case '?':
    263 			fprintf(stderr,
    264 				"usage: cat [-beflnstv] [-] [file ...]\n");
    265 			exit(EXIT_FAILURE);
    266 		}
    267 	argv += optind;
    268 
    269 	if (lflag) {
    270 		stdout_lock.l_len = 0;
    271 		stdout_lock.l_start = 0;
    272 		stdout_lock.l_type = F_WRLCK;
    273 		stdout_lock.l_whence = SEEK_SET;
    274 		if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1)
    275 		{
    276 			perror("fcntl");
    277 			exit(EXIT_FAILURE);
    278 		}
    279 	}
    280 
    281 	if (bflag || eflag || nflag || sflag || tflag || vflag)
    282 		cook_args(argv);
    283 	else
    284 		raw_args(argv);
    285 	if (fclose(stdout))
    286 	{
    287 		perror("fclose");
    288 		exit(EXIT_FAILURE);
    289 	}
    290 	exit(rval);
    291 }
    292