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