xref: /netbsd-src/bin/cat/cat.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: cat.c,v 1.14 1997/06/26 23:07:19 kleink 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 #ifndef lint
40 static char copyright[] =
41 "@(#) Copyright (c) 1989, 1993\n\
42 	The Regents of the University of California.  All rights reserved.\n";
43 #endif /* not lint */
44 
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)cat.c	8.2 (Berkeley) 4/27/95";
48 #else
49 static char rcsid[] = "$NetBSD: cat.c,v 1.14 1997/06/26 23:07:19 kleink Exp $";
50 #endif
51 #endif /* not lint */
52 
53 #include <sys/param.h>
54 #include <sys/stat.h>
55 
56 #include <locale.h>
57 #include <ctype.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 
66 int bflag, eflag, nflag, sflag, tflag, vflag;
67 int rval;
68 char *filename;
69 
70 void cook_args __P((char *argv[]));
71 void cook_buf __P((FILE *));
72 void raw_args __P((char *argv[]));
73 void raw_cat __P((int));
74 
75 int
76 main(argc, argv)
77 	int argc;
78 	char *argv[];
79 {
80 	extern int optind;
81 	int ch;
82 
83 	setlocale(LC_ALL, "");
84 
85 	while ((ch = getopt(argc, argv, "benstuv")) != -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 'n':
94 			nflag = 1;
95 			break;
96 		case 's':
97 			sflag = 1;
98 			break;
99 		case 't':
100 			tflag = vflag = 1;	/* -t implies -v */
101 			break;
102 		case 'u':
103 			setbuf(stdout, (char *)NULL);
104 			break;
105 		case 'v':
106 			vflag = 1;
107 			break;
108 		default:
109 		case '?':
110 			(void)fprintf(stderr,
111 			    "usage: cat [-benstuv] [-] [file ...]\n");
112 			exit(1);
113 		}
114 	argv += optind;
115 
116 	if (bflag || eflag || nflag || sflag || tflag || vflag)
117 		cook_args(argv);
118 	else
119 		raw_args(argv);
120 	if (fclose(stdout))
121 		err(1, "stdout");
122 	exit(rval);
123 }
124 
125 void
126 cook_args(argv)
127 	char **argv;
128 {
129 	FILE *fp;
130 
131 	fp = stdin;
132 	filename = "stdin";
133 	do {
134 		if (*argv) {
135 			if (!strcmp(*argv, "-"))
136 				fp = stdin;
137 			else if ((fp = fopen(*argv, "r")) == NULL) {
138 				warn("%s", *argv);
139 				rval = 1;
140 				++argv;
141 				continue;
142 			}
143 			filename = *argv++;
144 		}
145 		cook_buf(fp);
146 		if (fp != stdin)
147 			(void)fclose(fp);
148 	} while (*argv);
149 }
150 
151 void
152 cook_buf(fp)
153 	FILE *fp;
154 {
155 	int ch, gobble, line, prev;
156 
157 	line = gobble = 0;
158 	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
159 		if (prev == '\n') {
160 			if (ch == '\n') {
161 				if (sflag) {
162 					if (!gobble && putchar(ch) == EOF)
163 						break;
164 					gobble = 1;
165 					continue;
166 				}
167 				if (nflag && !bflag) {
168 					(void)fprintf(stdout, "%6d\t", ++line);
169 					if (ferror(stdout))
170 						break;
171 				}
172 			} else if (nflag) {
173 				(void)fprintf(stdout, "%6d\t", ++line);
174 				if (ferror(stdout))
175 					break;
176 			}
177 		}
178 		gobble = 0;
179 		if (ch == '\n') {
180 			if (eflag)
181 				if (putchar('$') == EOF)
182 					break;
183 		} else if (ch == '\t') {
184 			if (tflag) {
185 				if (putchar('^') == EOF || putchar('I') == EOF)
186 					break;
187 				continue;
188 			}
189 		} else if (vflag) {
190 			if (!isascii(ch)) {
191 				if (putchar('M') == EOF || putchar('-') == EOF)
192 					break;
193 				ch = toascii(ch);
194 			}
195 			if (iscntrl(ch)) {
196 				if (putchar('^') == EOF ||
197 				    putchar(ch == '\177' ? '?' :
198 				    ch | 0100) == EOF)
199 					break;
200 				continue;
201 			}
202 		}
203 		if (putchar(ch) == EOF)
204 			break;
205 	}
206 	if (ferror(fp)) {
207 		warn("%s", filename);
208 		rval = 1;
209 		clearerr(fp);
210 	}
211 	if (ferror(stdout))
212 		err(1, "stdout");
213 }
214 
215 void
216 raw_args(argv)
217 	char **argv;
218 {
219 	int fd;
220 
221 	fd = fileno(stdin);
222 	filename = "stdin";
223 	do {
224 		if (*argv) {
225 			if (!strcmp(*argv, "-"))
226 				fd = fileno(stdin);
227 			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
228 				warn("%s", *argv);
229 				rval = 1;
230 				++argv;
231 				continue;
232 			}
233 			filename = *argv++;
234 		}
235 		raw_cat(fd);
236 		if (fd != fileno(stdin))
237 			(void)close(fd);
238 	} while (*argv);
239 }
240 
241 void
242 raw_cat(rfd)
243 	int rfd;
244 {
245 	int nr, nw, off, wfd;
246 	static int bsize;
247 	static char *buf;
248 	struct stat sbuf;
249 
250 	wfd = fileno(stdout);
251 	if (buf == NULL) {
252 		if (fstat(wfd, &sbuf))
253 			err(1, "%s", filename);
254 		bsize = MAX(sbuf.st_blksize, 1024);
255 		if ((buf = malloc((u_int)bsize)) == NULL)
256 			err(1, "cannot allocate buffer");
257 	}
258 	while ((nr = read(rfd, buf, bsize)) > 0)
259 		for (off = 0; nr; nr -= nw, off += nw)
260 			if ((nw = write(wfd, buf + off, nr)) < 0)
261 				err(1, "stdout");
262 	if (nr < 0) {
263 		warn("%s", filename);
264 		rval = 1;
265 	}
266 }
267