xref: /netbsd-src/bin/cat/cat.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
1 /* $NetBSD: cat.c,v 1.27 2001/07/29 22:40:57 wiz 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __COPYRIGHT(
42 "@(#) Copyright (c) 1989, 1993\n\
43 	The Regents of the University of California.  All rights reserved.\n");
44 #endif /* not lint */
45 
46 #ifndef lint
47 #if 0
48 static char sccsid[] = "@(#)cat.c	8.2 (Berkeley) 4/27/95";
49 #else
50 __RCSID("$NetBSD: cat.c,v 1.27 2001/07/29 22:40:57 wiz Exp $");
51 #endif
52 #endif /* not lint */
53 
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 
57 #include <ctype.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <locale.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 
67 int bflag, eflag, fflag, nflag, sflag, tflag, vflag;
68 int rval;
69 const char *filename;
70 
71 int main(int, char *[]);
72 void cook_args(char *argv[]);
73 void cook_buf(FILE *);
74 void raw_args(char *argv[]);
75 void raw_cat(int);
76 
77 int
78 main(int argc, char *argv[])
79 {
80 	int ch;
81 
82 	(void)setlocale(LC_ALL, "");
83 
84 	while ((ch = getopt(argc, argv, "befnstuv")) != -1)
85 		switch (ch) {
86 		case 'b':
87 			bflag = nflag = 1;	/* -b implies -n */
88 			break;
89 		case 'e':
90 			eflag = vflag = 1;	/* -e implies -v */
91 			break;
92 		case 'f':
93 			fflag = 1;
94 			break;
95 		case 'n':
96 			nflag = 1;
97 			break;
98 		case 's':
99 			sflag = 1;
100 			break;
101 		case 't':
102 			tflag = vflag = 1;	/* -t implies -v */
103 			break;
104 		case 'u':
105 			setbuf(stdout, NULL);
106 			break;
107 		case 'v':
108 			vflag = 1;
109 			break;
110 		default:
111 		case '?':
112 			(void)fprintf(stderr,
113 			    "usage: cat [-befnstuv] [-] [file ...]\n");
114 			exit(1);
115 			/* NOTREACHED */
116 		}
117 	argv += optind;
118 
119 	if (bflag || eflag || nflag || sflag || tflag || vflag)
120 		cook_args(argv);
121 	else
122 		raw_args(argv);
123 	if (fclose(stdout))
124 		err(1, "stdout");
125 	exit(rval);
126 	/* NOTREACHED */
127 }
128 
129 void
130 cook_args(char **argv)
131 {
132 	FILE *fp;
133 
134 	fp = stdin;
135 	filename = "stdin";
136 	do {
137 		if (*argv) {
138 			if (!strcmp(*argv, "-"))
139 				fp = stdin;
140 			else if ((fp = fopen(*argv,
141 			    fflag ? "rf" : "r")) == NULL) {
142 				warn("%s", *argv);
143 				rval = 1;
144 				++argv;
145 				continue;
146 			}
147 			filename = *argv++;
148 		}
149 		cook_buf(fp);
150 		if (fp != stdin)
151 			(void)fclose(fp);
152 	} while (*argv);
153 }
154 
155 void
156 cook_buf(FILE *fp)
157 {
158 	int ch, gobble, line, prev;
159 
160 	line = gobble = 0;
161 	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
162 		if (prev == '\n') {
163 			if (ch == '\n') {
164 				if (sflag) {
165 					if (!gobble && putchar(ch) == EOF)
166 						break;
167 					gobble = 1;
168 					continue;
169 				}
170 				if (nflag) {
171 					if (!bflag) {
172 						(void)fprintf(stdout,
173 						    "%6d\t", ++line);
174 						if (ferror(stdout))
175 							break;
176 					} else if (eflag) {
177 						(void)fprintf(stdout,
178 						    "%6s\t", "");
179 						if (ferror(stdout))
180 							break;
181 					}
182 				}
183 			} else if (nflag) {
184 				(void)fprintf(stdout, "%6d\t", ++line);
185 				if (ferror(stdout))
186 					break;
187 			}
188 		}
189 		gobble = 0;
190 		if (ch == '\n') {
191 			if (eflag)
192 				if (putchar('$') == EOF)
193 					break;
194 		} else if (ch == '\t') {
195 			if (tflag) {
196 				if (putchar('^') == EOF || putchar('I') == EOF)
197 					break;
198 				continue;
199 			}
200 		} else if (vflag) {
201 			if (!isascii(ch)) {
202 				if (putchar('M') == EOF || putchar('-') == EOF)
203 					break;
204 				ch = toascii(ch);
205 			}
206 			if (iscntrl(ch)) {
207 				if (putchar('^') == EOF ||
208 				    putchar(ch == '\177' ? '?' :
209 				    ch | 0100) == EOF)
210 					break;
211 				continue;
212 			}
213 		}
214 		if (putchar(ch) == EOF)
215 			break;
216 	}
217 	if (ferror(fp)) {
218 		warn("%s", filename);
219 		rval = 1;
220 		clearerr(fp);
221 	}
222 	if (ferror(stdout))
223 		err(1, "stdout");
224 }
225 
226 void
227 raw_args(char **argv)
228 {
229 	int fd;
230 
231 	fd = fileno(stdin);
232 	filename = "stdin";
233 	do {
234 		if (*argv) {
235 			if (!strcmp(*argv, "-"))
236 				fd = fileno(stdin);
237 			else if (fflag) {
238 				struct stat st;
239 				fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
240 				if (fd < 0)
241 					goto skip;
242 
243 				if (fstat(fd, &st) == -1) {
244 					close(fd);
245 					goto skip;
246 				}
247 				if (!S_ISREG(st.st_mode)) {
248 					close(fd);
249 					errno = EFTYPE;
250 					goto skip;
251 				}
252 			}
253 			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
254 skip:
255 				warn("%s", *argv);
256 				rval = 1;
257 				++argv;
258 				continue;
259 			}
260 			filename = *argv++;
261 		}
262 		raw_cat(fd);
263 		if (fd != fileno(stdin))
264 			(void)close(fd);
265 	} while (*argv);
266 }
267 
268 void
269 raw_cat(int rfd)
270 {
271 	int wfd;
272 	static char *buf;
273 	static char fb_buf[BUFSIZ];
274 	struct stat sbuf;
275 	static size_t bsize;
276 	ssize_t nr, nw, off;
277 
278 	wfd = fileno(stdout);
279 	if (buf == NULL) {
280 		if (fstat(wfd, &sbuf) == 0) {
281 			bsize = MIN(sbuf.st_blksize, SSIZE_MAX);
282 			bsize = MAX(bsize, BUFSIZ);
283 			buf = malloc(bsize);
284 		}
285 		if (buf == NULL) {
286 			buf = fb_buf;
287 			bsize = BUFSIZ;
288 		}
289 	}
290 	while ((nr = read(rfd, buf, bsize)) > 0)
291 		for (off = 0; nr; nr -= nw, off += nw)
292 			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
293 				err(1, "stdout");
294 	if (nr < 0) {
295 		warn("%s", filename);
296 		rval = 1;
297 	}
298 }
299