xref: /netbsd-src/bin/cat/cat.c (revision 5e4c038a45edbc7d63b7c2daa76e29f88b64a4e3)
1 /* $NetBSD: cat.c,v 1.31 2002/05/09 02:19:42 simonb 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.31 2002/05/09 02:19:42 simonb 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 	setprogname(argv[0]);
83 	(void)setlocale(LC_ALL, "");
84 
85 	while ((ch = getopt(argc, argv, "befnstuv")) != -1)
86 		switch (ch) {
87 		case 'b':
88 			bflag = nflag = 1;	/* -b implies -n */
89 			break;
90 		case 'e':
91 			eflag = vflag = 1;	/* -e implies -v */
92 			break;
93 		case 'f':
94 			fflag = 1;
95 			break;
96 		case 'n':
97 			nflag = 1;
98 			break;
99 		case 's':
100 			sflag = 1;
101 			break;
102 		case 't':
103 			tflag = vflag = 1;	/* -t implies -v */
104 			break;
105 		case 'u':
106 			setbuf(stdout, NULL);
107 			break;
108 		case 'v':
109 			vflag = 1;
110 			break;
111 		default:
112 		case '?':
113 			(void)fprintf(stderr,
114 			    "usage: cat [-befnstuv] [-] [file ...]\n");
115 			exit(1);
116 			/* NOTREACHED */
117 		}
118 	argv += optind;
119 
120 	if (bflag || eflag || nflag || sflag || tflag || vflag)
121 		cook_args(argv);
122 	else
123 		raw_args(argv);
124 	if (fclose(stdout))
125 		err(1, "stdout");
126 	exit(rval);
127 	/* NOTREACHED */
128 }
129 
130 void
131 cook_args(char **argv)
132 {
133 	FILE *fp;
134 
135 	fp = stdin;
136 	filename = "stdin";
137 	do {
138 		if (*argv) {
139 			if (!strcmp(*argv, "-"))
140 				fp = stdin;
141 			else if ((fp = fopen(*argv,
142 			    fflag ? "rf" : "r")) == NULL) {
143 				warn("%s", *argv);
144 				rval = 1;
145 				++argv;
146 				continue;
147 			}
148 			filename = *argv++;
149 		}
150 		cook_buf(fp);
151 		if (fp != stdin)
152 			(void)fclose(fp);
153 	} while (*argv);
154 }
155 
156 void
157 cook_buf(FILE *fp)
158 {
159 	int ch, gobble, line, prev;
160 
161 	line = gobble = 0;
162 	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
163 		if (prev == '\n') {
164 			if (ch == '\n') {
165 				if (sflag) {
166 					if (!gobble && putchar(ch) == EOF)
167 						break;
168 					gobble = 1;
169 					continue;
170 				}
171 				if (nflag) {
172 					if (!bflag) {
173 						(void)fprintf(stdout,
174 						    "%6d\t", ++line);
175 						if (ferror(stdout))
176 							break;
177 					} else if (eflag) {
178 						(void)fprintf(stdout,
179 						    "%6s\t", "");
180 						if (ferror(stdout))
181 							break;
182 					}
183 				}
184 			} else if (nflag) {
185 				(void)fprintf(stdout, "%6d\t", ++line);
186 				if (ferror(stdout))
187 					break;
188 			}
189 		}
190 		gobble = 0;
191 		if (ch == '\n') {
192 			if (eflag)
193 				if (putchar('$') == EOF)
194 					break;
195 		} else if (ch == '\t') {
196 			if (tflag) {
197 				if (putchar('^') == EOF || putchar('I') == EOF)
198 					break;
199 				continue;
200 			}
201 		} else if (vflag) {
202 			if (!isascii(ch)) {
203 				if (putchar('M') == EOF || putchar('-') == EOF)
204 					break;
205 				ch = toascii(ch);
206 			}
207 			if (iscntrl(ch)) {
208 				if (putchar('^') == EOF ||
209 				    putchar(ch == '\177' ? '?' :
210 				    ch | 0100) == EOF)
211 					break;
212 				continue;
213 			}
214 		}
215 		if (putchar(ch) == EOF)
216 			break;
217 	}
218 	if (ferror(fp)) {
219 		warn("%s", filename);
220 		rval = 1;
221 		clearerr(fp);
222 	}
223 	if (ferror(stdout))
224 		err(1, "stdout");
225 }
226 
227 void
228 raw_args(char **argv)
229 {
230 	int fd;
231 
232 	fd = fileno(stdin);
233 	filename = "stdin";
234 	do {
235 		if (*argv) {
236 			if (!strcmp(*argv, "-"))
237 				fd = fileno(stdin);
238 			else if (fflag) {
239 				struct stat st;
240 				fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
241 				if (fd < 0)
242 					goto skip;
243 
244 				if (fstat(fd, &st) == -1) {
245 					close(fd);
246 					goto skip;
247 				}
248 				if (!S_ISREG(st.st_mode)) {
249 					close(fd);
250 					errno = EFTYPE;
251 					goto skip;
252 				}
253 			}
254 			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
255 skip:
256 				warn("%s", *argv);
257 				rval = 1;
258 				++argv;
259 				continue;
260 			}
261 			filename = *argv++;
262 		}
263 		raw_cat(fd);
264 		if (fd != fileno(stdin))
265 			(void)close(fd);
266 	} while (*argv);
267 }
268 
269 void
270 raw_cat(int rfd)
271 {
272 	static char *buf;
273 	static char fb_buf[BUFSIZ];
274 	static size_t bsize;
275 
276 	struct stat sbuf;
277 	ssize_t nr, nw, off;
278 	int wfd;
279 
280 	wfd = fileno(stdout);
281 	if (buf == NULL) {
282 		if (fstat(wfd, &sbuf) == 0) {
283 			bsize = MAX(sbuf.st_blksize, BUFSIZ);
284 			buf = malloc(bsize);
285 		}
286 		if (buf == NULL) {
287 			buf = fb_buf;
288 			bsize = BUFSIZ;
289 		}
290 	}
291 	while ((nr = read(rfd, buf, bsize)) > 0)
292 		for (off = 0; nr; nr -= nw, off += nw)
293 			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
294 				err(1, "stdout");
295 	if (nr < 0) {
296 		warn("%s", filename);
297 		rval = 1;
298 	}
299 }
300