1139969Simp/*-
21556Srgrimes * Copyright (c) 1989, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kevin Fall.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
33114301Sobrien#if 0
341556Srgrimes#ifndef lint
3520412Sstevestatic char const copyright[] =
361556Srgrimes"@(#) Copyright (c) 1989, 1993\n\
371556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381556Srgrimes#endif /* not lint */
39114301Sobrien#endif
401556Srgrimes
411556Srgrimes#ifndef lint
4235772Scharnier#if 0
4336000Scharnierstatic char sccsid[] = "@(#)cat.c	8.2 (Berkeley) 4/27/95";
4435772Scharnier#endif
451556Srgrimes#endif /* not lint */
4699109Sobrien#include <sys/cdefs.h>
4799109Sobrien__FBSDID("$FreeBSD: stable/10/bin/cat/cat.c 337734 2018-08-14 01:57:11Z kevans $");
481556Srgrimes
491556Srgrimes#include <sys/param.h>
501556Srgrimes#include <sys/stat.h>
5183482Sdillon#ifndef NO_UDOM_SUPPORT
5283482Sdillon#include <sys/socket.h>
5383482Sdillon#include <sys/un.h>
5483482Sdillon#endif
551556Srgrimes
561556Srgrimes#include <ctype.h>
571556Srgrimes#include <err.h>
58337734Skevans#include <errno.h>
591556Srgrimes#include <fcntl.h>
6018578Sache#include <locale.h>
61238652Sjh#include <stddef.h>
621556Srgrimes#include <stdio.h>
631556Srgrimes#include <stdlib.h>
6478732Sdd#include <string.h>
651556Srgrimes#include <unistd.h>
66306200Sache#include <wchar.h>
67306200Sache#include <wctype.h>
681556Srgrimes
69246083Sbrooksstatic int bflag, eflag, lflag, nflag, sflag, tflag, vflag;
70226961Sedstatic int rval;
71226961Sedstatic const char *filename;
721556Srgrimes
73249804Seadlerstatic void usage(void) __dead2;
74105781Smarkmstatic void scanfiles(char *argv[], int cooked);
7590106Simpstatic void cook_cat(FILE *);
7690106Simpstatic void raw_cat(int);
771556Srgrimes
7883482Sdillon#ifndef NO_UDOM_SUPPORT
7990106Simpstatic int udom_open(const char *path, int flags);
8083482Sdillon#endif
8183482Sdillon
82238652Sjh/*
83238652Sjh * Memory strategy threshold, in pages: if physmem is larger than this,
84238652Sjh * use a large buffer.
85238652Sjh */
86238652Sjh#define	PHYSPAGES_THRESHOLD (32 * 1024)
87184471Sivoras
88238652Sjh/* Maximum buffer size in bytes - do not allow it to grow larger than this. */
89238652Sjh#define	BUFSIZE_MAX (2 * 1024 * 1024)
90184471Sivoras
91238652Sjh/*
92238652Sjh * Small (default) buffer size in bytes. It's inefficient for this to be
93238652Sjh * smaller than MAXPHYS.
94238652Sjh */
95238652Sjh#define	BUFSIZE_SMALL (MAXPHYS)
96184471Sivoras
971556Srgrimesint
9890106Simpmain(int argc, char *argv[])
991556Srgrimes{
1001556Srgrimes	int ch;
101246083Sbrooks	struct flock stdout_lock;
1021556Srgrimes
10318578Sache	setlocale(LC_CTYPE, "");
10418578Sache
105246083Sbrooks	while ((ch = getopt(argc, argv, "belnstuv")) != -1)
1061556Srgrimes		switch (ch) {
1071556Srgrimes		case 'b':
1081556Srgrimes			bflag = nflag = 1;	/* -b implies -n */
1091556Srgrimes			break;
1101556Srgrimes		case 'e':
1111556Srgrimes			eflag = vflag = 1;	/* -e implies -v */
1121556Srgrimes			break;
113246083Sbrooks		case 'l':
114246083Sbrooks			lflag = 1;
115246083Sbrooks			break;
1161556Srgrimes		case 'n':
1171556Srgrimes			nflag = 1;
1181556Srgrimes			break;
1191556Srgrimes		case 's':
1201556Srgrimes			sflag = 1;
1211556Srgrimes			break;
1221556Srgrimes		case 't':
1231556Srgrimes			tflag = vflag = 1;	/* -t implies -v */
1241556Srgrimes			break;
1251556Srgrimes		case 'u':
12659239Sasmodai			setbuf(stdout, NULL);
1271556Srgrimes			break;
1281556Srgrimes		case 'v':
1291556Srgrimes			vflag = 1;
1301556Srgrimes			break;
13118546Simp		default:
13298216Sjmallett			usage();
1331556Srgrimes		}
1341556Srgrimes	argv += optind;
1351556Srgrimes
136246083Sbrooks	if (lflag) {
137246083Sbrooks		stdout_lock.l_len = 0;
138246083Sbrooks		stdout_lock.l_start = 0;
139246083Sbrooks		stdout_lock.l_type = F_WRLCK;
140246083Sbrooks		stdout_lock.l_whence = SEEK_SET;
141246083Sbrooks		if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1)
142246083Sbrooks			err(EXIT_FAILURE, "stdout");
143246083Sbrooks	}
144246083Sbrooks
1451556Srgrimes	if (bflag || eflag || nflag || sflag || tflag || vflag)
14683482Sdillon		scanfiles(argv, 1);
1471556Srgrimes	else
14883482Sdillon		scanfiles(argv, 0);
1491556Srgrimes	if (fclose(stdout))
1501556Srgrimes		err(1, "stdout");
1511556Srgrimes	exit(rval);
152101092Smarkm	/* NOTREACHED */
1531556Srgrimes}
1541556Srgrimes
15598216Sjmallettstatic void
15698216Sjmallettusage(void)
15798216Sjmallett{
158249804Seadler
159246083Sbrooks	fprintf(stderr, "usage: cat [-belnstuv] [file ...]\n");
16098216Sjmallett	exit(1);
161101092Smarkm	/* NOTREACHED */
16298216Sjmallett}
16398216Sjmallett
164105781Smarkmstatic void
165105781Smarkmscanfiles(char *argv[], int cooked)
1661556Srgrimes{
167238652Sjh	int fd, i;
16883482Sdillon	char *path;
16983961Sru	FILE *fp;
1701556Srgrimes
171238652Sjh	i = 0;
17283482Sdillon	while ((path = argv[i]) != NULL || i == 0) {
17383482Sdillon		if (path == NULL || strcmp(path, "-") == 0) {
17483482Sdillon			filename = "stdin";
17583961Sru			fd = STDIN_FILENO;
17683482Sdillon		} else {
17783482Sdillon			filename = path;
17883482Sdillon			fd = open(path, O_RDONLY);
17983482Sdillon#ifndef NO_UDOM_SUPPORT
18083482Sdillon			if (fd < 0 && errno == EOPNOTSUPP)
18183962Sru				fd = udom_open(path, O_RDONLY);
18283482Sdillon#endif
1831556Srgrimes		}
18483482Sdillon		if (fd < 0) {
18583482Sdillon			warn("%s", path);
18683482Sdillon			rval = 1;
18783482Sdillon		} else if (cooked) {
18883961Sru			if (fd == STDIN_FILENO)
18983961Sru				cook_cat(stdin);
19083961Sru			else {
19183961Sru				fp = fdopen(fd, "r");
19283961Sru				cook_cat(fp);
19383961Sru				fclose(fp);
19483961Sru			}
19583482Sdillon		} else {
19683482Sdillon			raw_cat(fd);
19783961Sru			if (fd != STDIN_FILENO)
19883961Sru				close(fd);
19983482Sdillon		}
20083482Sdillon		if (path == NULL)
20183482Sdillon			break;
20283482Sdillon		++i;
20383482Sdillon	}
2041556Srgrimes}
2051556Srgrimes
20683482Sdillonstatic void
20790106Simpcook_cat(FILE *fp)
2081556Srgrimes{
20990106Simp	int ch, gobble, line, prev;
210306200Sache	wint_t wch;
2111556Srgrimes
21283961Sru	/* Reset EOF condition on stdin. */
21383961Sru	if (fp == stdin && feof(stdin))
21483961Sru		clearerr(stdin);
21583961Sru
2161556Srgrimes	line = gobble = 0;
2171556Srgrimes	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
2181556Srgrimes		if (prev == '\n') {
21998169Stjr			if (sflag) {
22098169Stjr				if (ch == '\n') {
22198169Stjr					if (gobble)
22298169Stjr						continue;
2231556Srgrimes					gobble = 1;
22498169Stjr				} else
22598169Stjr					gobble = 0;
22698169Stjr			}
22798169Stjr			if (nflag && (!bflag || ch != '\n')) {
22898169Stjr				(void)fprintf(stdout, "%6d\t", ++line);
2291556Srgrimes				if (ferror(stdout))
2301556Srgrimes					break;
2311556Srgrimes			}
2321556Srgrimes		}
2331556Srgrimes		if (ch == '\n') {
23498169Stjr			if (eflag && putchar('$') == EOF)
23598169Stjr				break;
2361556Srgrimes		} else if (ch == '\t') {
2371556Srgrimes			if (tflag) {
2381556Srgrimes				if (putchar('^') == EOF || putchar('I') == EOF)
2391556Srgrimes					break;
2401556Srgrimes				continue;
2411556Srgrimes			}
2421556Srgrimes		} else if (vflag) {
243306200Sache			(void)ungetc(ch, fp);
244306200Sache			/*
245306200Sache			 * Our getwc(3) doesn't change file position
246306200Sache			 * on error.
247306200Sache			 */
248306200Sache			if ((wch = getwc(fp)) == WEOF) {
249306200Sache				if (ferror(fp) && errno == EILSEQ) {
250306200Sache					clearerr(fp);
251306200Sache					/* Resync attempt. */
252306200Sache					memset(&fp->_mbstate, 0, sizeof(mbstate_t));
253306200Sache					if ((ch = getc(fp)) == EOF)
254306200Sache						break;
255306200Sache					wch = ch;
256306200Sache					goto ilseq;
257306200Sache				} else
258306200Sache					break;
259306200Sache			}
260306200Sache			if (!iswascii(wch) && !iswprint(wch)) {
261306200Sacheilseq:
2621556Srgrimes				if (putchar('M') == EOF || putchar('-') == EOF)
2631556Srgrimes					break;
264306200Sache				wch = toascii(wch);
2651556Srgrimes			}
266306200Sache			if (iswcntrl(wch)) {
267306200Sache				ch = toascii(wch);
268306200Sache				ch = (ch == '\177') ? '?' : (ch | 0100);
269306200Sache				if (putchar('^') == EOF || putchar(ch) == EOF)
2701556Srgrimes					break;
2711556Srgrimes				continue;
2721556Srgrimes			}
273306200Sache			if (putwchar(wch) == WEOF)
274306200Sache				break;
275306200Sache			ch = -1;
276306200Sache			continue;
2771556Srgrimes		}
2781556Srgrimes		if (putchar(ch) == EOF)
2791556Srgrimes			break;
2801556Srgrimes	}
2811556Srgrimes	if (ferror(fp)) {
2821556Srgrimes		warn("%s", filename);
28311145Sbde		rval = 1;
2841556Srgrimes		clearerr(fp);
2851556Srgrimes	}
2861556Srgrimes	if (ferror(stdout))
2871556Srgrimes		err(1, "stdout");
2881556Srgrimes}
2891556Srgrimes
29083482Sdillonstatic void
29190106Simpraw_cat(int rfd)
2921556Srgrimes{
29390106Simp	int off, wfd;
29439065Simp	ssize_t nr, nw;
29539065Simp	static size_t bsize;
29691079Smarkm	static char *buf = NULL;
2971556Srgrimes	struct stat sbuf;
2981556Srgrimes
2991556Srgrimes	wfd = fileno(stdout);
3001556Srgrimes	if (buf == NULL) {
3011556Srgrimes		if (fstat(wfd, &sbuf))
302238653Sjh			err(1, "stdout");
303184471Sivoras		if (S_ISREG(sbuf.st_mode)) {
304184471Sivoras			/* If there's plenty of RAM, use a large copy buffer */
305184471Sivoras			if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD)
306238652Sjh				bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
307184471Sivoras			else
308184471Sivoras				bsize = BUFSIZE_SMALL;
309184471Sivoras		} else
310238652Sjh			bsize = MAX(sbuf.st_blksize,
311238652Sjh			    (blksize_t)sysconf(_SC_PAGESIZE));
31239138Simp		if ((buf = malloc(bsize)) == NULL)
313184471Sivoras			err(1, "malloc() failure of IO buffer");
3141556Srgrimes	}
3151556Srgrimes	while ((nr = read(rfd, buf, bsize)) > 0)
3161556Srgrimes		for (off = 0; nr; nr -= nw, off += nw)
31739138Simp			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
3181556Srgrimes				err(1, "stdout");
31911145Sbde	if (nr < 0) {
3201556Srgrimes		warn("%s", filename);
32111145Sbde		rval = 1;
32211145Sbde	}
3231556Srgrimes}
32483482Sdillon
32583482Sdillon#ifndef NO_UDOM_SUPPORT
32683482Sdillon
32783482Sdillonstatic int
32890106Simpudom_open(const char *path, int flags)
32983482Sdillon{
33083482Sdillon	struct sockaddr_un sou;
33183482Sdillon	int fd;
33291079Smarkm	unsigned int len;
33383482Sdillon
33483482Sdillon	bzero(&sou, sizeof(sou));
33583482Sdillon
33683482Sdillon	/*
33783482Sdillon	 * Construct the unix domain socket address and attempt to connect
33883482Sdillon	 */
33991079Smarkm	fd = socket(AF_UNIX, SOCK_STREAM, 0);
34091079Smarkm	if (fd >= 0) {
34183482Sdillon		sou.sun_family = AF_UNIX;
34299022Stjr		if ((len = strlcpy(sou.sun_path, path,
34399022Stjr		    sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) {
34499022Stjr			errno = ENAMETOOLONG;
34599022Stjr			return (-1);
34699022Stjr		}
34783482Sdillon		len = offsetof(struct sockaddr_un, sun_path[len+1]);
34883482Sdillon
34983482Sdillon		if (connect(fd, (void *)&sou, len) < 0) {
35083482Sdillon			close(fd);
35183482Sdillon			fd = -1;
35283482Sdillon		}
35383482Sdillon	}
35483482Sdillon
35583482Sdillon	/*
35683482Sdillon	 * handle the open flags by shutting down appropriate directions
35783482Sdillon	 */
35883482Sdillon	if (fd >= 0) {
35983482Sdillon		switch(flags & O_ACCMODE) {
36083482Sdillon		case O_RDONLY:
36191079Smarkm			if (shutdown(fd, SHUT_WR) == -1)
362132433Stjr				warn(NULL);
36383482Sdillon			break;
36483482Sdillon		case O_WRONLY:
36591079Smarkm			if (shutdown(fd, SHUT_RD) == -1)
366132433Stjr				warn(NULL);
36783482Sdillon			break;
36883482Sdillon		default:
36983482Sdillon			break;
37083482Sdillon		}
37183482Sdillon	}
372238652Sjh	return (fd);
37383482Sdillon}
37483482Sdillon
37583482Sdillon#endif
376