cat.c revision 225736
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 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if 0
34#ifndef lint
35static char const copyright[] =
36"@(#) Copyright (c) 1989, 1993\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39#endif
40
41#ifndef lint
42#if 0
43static char sccsid[] = "@(#)cat.c	8.2 (Berkeley) 4/27/95";
44#endif
45#endif /* not lint */
46#include <sys/cdefs.h>
47__FBSDID("$FreeBSD: stable/9/bin/cat/cat.c 184471 2008-10-30 14:05:57Z ivoras $");
48
49#include <sys/param.h>
50#include <sys/stat.h>
51#ifndef NO_UDOM_SUPPORT
52#include <sys/socket.h>
53#include <sys/un.h>
54#include <errno.h>
55#endif
56
57#include <ctype.h>
58#include <err.h>
59#include <fcntl.h>
60#include <locale.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65#include <stddef.h>
66
67int bflag, eflag, nflag, sflag, tflag, vflag;
68int rval;
69const char *filename;
70
71static void usage(void);
72static void scanfiles(char *argv[], int cooked);
73static void cook_cat(FILE *);
74static void raw_cat(int);
75
76#ifndef NO_UDOM_SUPPORT
77static int udom_open(const char *path, int flags);
78#endif
79
80/* Memory strategy threshold, in pages: if physmem is larger then this, use a
81 * large buffer */
82#define PHYSPAGES_THRESHOLD (32*1024)
83
84/* Maximum buffer size in bytes - do not allow it to grow larger than this */
85#define BUFSIZE_MAX (2*1024*1024)
86
87/* Small (default) buffer size in bytes. It's inefficient for this to be
88 * smaller than MAXPHYS */
89#define BUFSIZE_SMALL (MAXPHYS)
90
91int
92main(int argc, char *argv[])
93{
94	int ch;
95
96	setlocale(LC_CTYPE, "");
97
98	while ((ch = getopt(argc, argv, "benstuv")) != -1)
99		switch (ch) {
100		case 'b':
101			bflag = nflag = 1;	/* -b implies -n */
102			break;
103		case 'e':
104			eflag = vflag = 1;	/* -e implies -v */
105			break;
106		case 'n':
107			nflag = 1;
108			break;
109		case 's':
110			sflag = 1;
111			break;
112		case 't':
113			tflag = vflag = 1;	/* -t implies -v */
114			break;
115		case 'u':
116			setbuf(stdout, NULL);
117			break;
118		case 'v':
119			vflag = 1;
120			break;
121		default:
122			usage();
123		}
124	argv += optind;
125
126	if (bflag || eflag || nflag || sflag || tflag || vflag)
127		scanfiles(argv, 1);
128	else
129		scanfiles(argv, 0);
130	if (fclose(stdout))
131		err(1, "stdout");
132	exit(rval);
133	/* NOTREACHED */
134}
135
136static void
137usage(void)
138{
139	fprintf(stderr, "usage: cat [-benstuv] [file ...]\n");
140	exit(1);
141	/* NOTREACHED */
142}
143
144static void
145scanfiles(char *argv[], int cooked)
146{
147	int i = 0;
148	char *path;
149	FILE *fp;
150
151	while ((path = argv[i]) != NULL || i == 0) {
152		int fd;
153
154		if (path == NULL || strcmp(path, "-") == 0) {
155			filename = "stdin";
156			fd = STDIN_FILENO;
157		} else {
158			filename = path;
159			fd = open(path, O_RDONLY);
160#ifndef NO_UDOM_SUPPORT
161			if (fd < 0 && errno == EOPNOTSUPP)
162				fd = udom_open(path, O_RDONLY);
163#endif
164		}
165		if (fd < 0) {
166			warn("%s", path);
167			rval = 1;
168		} else if (cooked) {
169			if (fd == STDIN_FILENO)
170				cook_cat(stdin);
171			else {
172				fp = fdopen(fd, "r");
173				cook_cat(fp);
174				fclose(fp);
175			}
176		} else {
177			raw_cat(fd);
178			if (fd != STDIN_FILENO)
179				close(fd);
180		}
181		if (path == NULL)
182			break;
183		++i;
184	}
185}
186
187static void
188cook_cat(FILE *fp)
189{
190	int ch, gobble, line, prev;
191
192	/* Reset EOF condition on stdin. */
193	if (fp == stdin && feof(stdin))
194		clearerr(stdin);
195
196	line = gobble = 0;
197	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
198		if (prev == '\n') {
199			if (sflag) {
200				if (ch == '\n') {
201					if (gobble)
202						continue;
203					gobble = 1;
204				} else
205					gobble = 0;
206			}
207			if (nflag && (!bflag || ch != '\n')) {
208				(void)fprintf(stdout, "%6d\t", ++line);
209				if (ferror(stdout))
210					break;
211			}
212		}
213		if (ch == '\n') {
214			if (eflag && putchar('$') == EOF)
215				break;
216		} else if (ch == '\t') {
217			if (tflag) {
218				if (putchar('^') == EOF || putchar('I') == EOF)
219					break;
220				continue;
221			}
222		} else if (vflag) {
223			if (!isascii(ch) && !isprint(ch)) {
224				if (putchar('M') == EOF || putchar('-') == EOF)
225					break;
226				ch = toascii(ch);
227			}
228			if (iscntrl(ch)) {
229				if (putchar('^') == EOF ||
230				    putchar(ch == '\177' ? '?' :
231				    ch | 0100) == EOF)
232					break;
233				continue;
234			}
235		}
236		if (putchar(ch) == EOF)
237			break;
238	}
239	if (ferror(fp)) {
240		warn("%s", filename);
241		rval = 1;
242		clearerr(fp);
243	}
244	if (ferror(stdout))
245		err(1, "stdout");
246}
247
248static void
249raw_cat(int rfd)
250{
251	int off, wfd;
252	ssize_t nr, nw;
253	static size_t bsize;
254	static char *buf = NULL;
255	struct stat sbuf;
256
257	wfd = fileno(stdout);
258	if (buf == NULL) {
259		if (fstat(wfd, &sbuf))
260			err(1, "%s", filename);
261		if (S_ISREG(sbuf.st_mode)) {
262			/* If there's plenty of RAM, use a large copy buffer */
263			if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD)
264				bsize = MIN(BUFSIZE_MAX, MAXPHYS*8);
265			else
266				bsize = BUFSIZE_SMALL;
267		} else
268			bsize = MAX(sbuf.st_blksize,
269					(blksize_t)sysconf(_SC_PAGESIZE));
270		if ((buf = malloc(bsize)) == NULL)
271			err(1, "malloc() failure of IO buffer");
272	}
273	while ((nr = read(rfd, buf, bsize)) > 0)
274		for (off = 0; nr; nr -= nw, off += nw)
275			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
276				err(1, "stdout");
277	if (nr < 0) {
278		warn("%s", filename);
279		rval = 1;
280	}
281}
282
283#ifndef NO_UDOM_SUPPORT
284
285static int
286udom_open(const char *path, int flags)
287{
288	struct sockaddr_un sou;
289	int fd;
290	unsigned int len;
291
292	bzero(&sou, sizeof(sou));
293
294	/*
295	 * Construct the unix domain socket address and attempt to connect
296	 */
297	fd = socket(AF_UNIX, SOCK_STREAM, 0);
298	if (fd >= 0) {
299		sou.sun_family = AF_UNIX;
300		if ((len = strlcpy(sou.sun_path, path,
301		    sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) {
302			errno = ENAMETOOLONG;
303			return (-1);
304		}
305		len = offsetof(struct sockaddr_un, sun_path[len+1]);
306
307		if (connect(fd, (void *)&sou, len) < 0) {
308			close(fd);
309			fd = -1;
310		}
311	}
312
313	/*
314	 * handle the open flags by shutting down appropriate directions
315	 */
316	if (fd >= 0) {
317		switch(flags & O_ACCMODE) {
318		case O_RDONLY:
319			if (shutdown(fd, SHUT_WR) == -1)
320				warn(NULL);
321			break;
322		case O_WRONLY:
323			if (shutdown(fd, SHUT_RD) == -1)
324				warn(NULL);
325			break;
326		default:
327			break;
328		}
329	}
330	return(fd);
331}
332
333#endif
334