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