xref: /freebsd/bin/cat/cat.c (revision 41466b50c1d5bfd1cf6adaae547a579a75d7c04e)
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 #ifndef NO_UDOM_SUPPORT
54 #include <sys/socket.h>
55 #include <sys/un.h>
56 #include <errno.h>
57 #endif
58 
59 #include <ctype.h>
60 #include <err.h>
61 #include <fcntl.h>
62 #include <locale.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67 #include <stddef.h>
68 
69 int bflag, eflag, nflag, sflag, tflag, vflag;
70 int rval;
71 const char *filename;
72 
73 int main __P((int argc, char *argv[]));
74 
75 static void scanfiles __P((char **argv, int cooked));
76 static void cook_cat __P((FILE *));
77 static void raw_cat __P((int));
78 
79 #ifndef NO_UDOM_SUPPORT
80 static int udom_open __P((const char *path, int flags));
81 #endif
82 
83 int
84 main(argc, argv)
85 	int argc;
86 	char *argv[];
87 {
88 	int ch;
89 
90 	setlocale(LC_CTYPE, "");
91 
92 	while ((ch = getopt(argc, argv, "benstuv")) != -1)
93 		switch (ch) {
94 		case 'b':
95 			bflag = nflag = 1;	/* -b implies -n */
96 			break;
97 		case 'e':
98 			eflag = vflag = 1;	/* -e implies -v */
99 			break;
100 		case 'n':
101 			nflag = 1;
102 			break;
103 		case 's':
104 			sflag = 1;
105 			break;
106 		case 't':
107 			tflag = vflag = 1;	/* -t implies -v */
108 			break;
109 		case 'u':
110 			setbuf(stdout, NULL);
111 			break;
112 		case 'v':
113 			vflag = 1;
114 			break;
115 		default:
116 			(void)fprintf(stderr,
117 			    "usage: cat [-benstuv] [-] [file ...]\n");
118 			exit(1);
119 		}
120 	argv += optind;
121 
122 	if (bflag || eflag || nflag || sflag || tflag || vflag)
123 		scanfiles(argv, 1);
124 	else
125 		scanfiles(argv, 0);
126 	if (fclose(stdout))
127 		err(1, "stdout");
128 	exit(rval);
129 }
130 
131 void
132 scanfiles(argv, cooked)
133     char **argv;
134     int cooked;
135 {
136 	int i = 0;
137 	char *path;
138 	FILE *fp;
139 
140 	while ((path = argv[i]) != NULL || i == 0) {
141 		int fd;
142 
143 		if (path == NULL || strcmp(path, "-") == 0) {
144 			filename = "stdin";
145 			fd = STDIN_FILENO;
146 		} else {
147 			filename = path;
148 			fd = open(path, O_RDONLY);
149 #ifndef NO_UDOM_SUPPORT
150 			if (fd < 0 && errno == EOPNOTSUPP)
151 				fd = udom_open(path, O_RDONLY);
152 #endif
153 		}
154 		if (fd < 0) {
155 			warn("%s", path);
156 			rval = 1;
157 		} else if (cooked) {
158 			if (fd == STDIN_FILENO)
159 				cook_cat(stdin);
160 			else {
161 				fp = fdopen(fd, "r");
162 				cook_cat(fp);
163 				fclose(fp);
164 			}
165 		} else {
166 			raw_cat(fd);
167 			if (fd != STDIN_FILENO)
168 				close(fd);
169 		}
170 		if (path == NULL)
171 			break;
172 		++i;
173 	}
174 }
175 
176 static void
177 cook_cat(fp)
178 	register FILE *fp;
179 {
180 	register int ch, gobble, line, prev;
181 
182 	/* Reset EOF condition on stdin. */
183 	if (fp == stdin && feof(stdin))
184 		clearerr(stdin);
185 
186 	line = gobble = 0;
187 	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
188 		if (prev == '\n') {
189 			if (ch == '\n') {
190 				if (sflag) {
191 					if (!gobble && putchar(ch) == EOF)
192 						break;
193 					gobble = 1;
194 					continue;
195 				}
196 				if (nflag && !bflag) {
197 					(void)fprintf(stdout, "%6d\t", ++line);
198 					if (ferror(stdout))
199 						break;
200 				}
201 			} else if (nflag) {
202 				(void)fprintf(stdout, "%6d\t", ++line);
203 				if (ferror(stdout))
204 					break;
205 			}
206 		}
207 		gobble = 0;
208 		if (ch == '\n') {
209 			if (eflag)
210 				if (putchar('$') == EOF)
211 					break;
212 		} else if (ch == '\t') {
213 			if (tflag) {
214 				if (putchar('^') == EOF || putchar('I') == EOF)
215 					break;
216 				continue;
217 			}
218 		} else if (vflag) {
219 			if (!isascii(ch) && !isprint(ch)) {
220 				if (putchar('M') == EOF || putchar('-') == EOF)
221 					break;
222 				ch = toascii(ch);
223 			}
224 			if (iscntrl(ch)) {
225 				if (putchar('^') == EOF ||
226 				    putchar(ch == '\177' ? '?' :
227 				    ch | 0100) == EOF)
228 					break;
229 				continue;
230 			}
231 		}
232 		if (putchar(ch) == EOF)
233 			break;
234 	}
235 	if (ferror(fp)) {
236 		warn("%s", filename);
237 		rval = 1;
238 		clearerr(fp);
239 	}
240 	if (ferror(stdout))
241 		err(1, "stdout");
242 }
243 
244 static void
245 raw_cat(rfd)
246 	register int rfd;
247 {
248 	register int off, wfd;
249 	ssize_t nr, nw;
250 	static size_t bsize;
251 	static char *buf;
252 	struct stat sbuf;
253 
254 	wfd = fileno(stdout);
255 	if (buf == NULL) {
256 		if (fstat(wfd, &sbuf))
257 			err(1, "%s", filename);
258 		bsize = MAX(sbuf.st_blksize, 1024);
259 		if ((buf = malloc(bsize)) == NULL)
260 			err(1, "buffer");
261 	}
262 	while ((nr = read(rfd, buf, bsize)) > 0)
263 		for (off = 0; nr; nr -= nw, off += nw)
264 			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
265 				err(1, "stdout");
266 	if (nr < 0) {
267 		warn("%s", filename);
268 		rval = 1;
269 	}
270 }
271 
272 #ifndef NO_UDOM_SUPPORT
273 
274 static int
275 udom_open(path, flags)
276     const char *path;
277     int flags;
278 {
279 	struct sockaddr_un sou;
280 	int fd;
281 	int len;
282 
283 	bzero(&sou, sizeof(sou));
284 
285 	/*
286 	 * Construct the unix domain socket address and attempt to connect
287 	 */
288 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
289 		sou.sun_family = AF_UNIX;
290 		snprintf(sou.sun_path, sizeof(sou.sun_path), "%s", path);
291 		len = strlen(sou.sun_path);
292 		len = offsetof(struct sockaddr_un, sun_path[len+1]);
293 
294 		if (connect(fd, (void *)&sou, len) < 0) {
295 			close(fd);
296 			fd = -1;
297 		}
298 	}
299 
300 	/*
301 	 * handle the open flags by shutting down appropriate directions
302 	 */
303 	if (fd >= 0) {
304 		switch(flags & O_ACCMODE) {
305 		case O_RDONLY:
306 			shutdown(fd, SHUT_WR);
307 			break;
308 		case O_WRONLY:
309 			shutdown(fd, SHUT_RD);
310 			break;
311 		default:
312 			break;
313 		}
314 	}
315 	return(fd);
316 }
317 
318 #endif
319 
320