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/11/bin/cat/cat.c 337733 2018-08-14 01:45:22Z 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>
54288602Shrs#include <netdb.h>
5583482Sdillon#endif
561556Srgrimes
571556Srgrimes#include <ctype.h>
581556Srgrimes#include <err.h>
59337733Skevans#include <errno.h>
601556Srgrimes#include <fcntl.h>
6118578Sache#include <locale.h>
621556Srgrimes#include <stdio.h>
631556Srgrimes#include <stdlib.h>
6478732Sdd#include <string.h>
651556Srgrimes#include <unistd.h>
66306201Sache#include <wchar.h>
67306201Sache#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;
172288630Sbdrewery	fd = -1;
17383482Sdillon	while ((path = argv[i]) != NULL || i == 0) {
17483482Sdillon		if (path == NULL || strcmp(path, "-") == 0) {
17583482Sdillon			filename = "stdin";
17683961Sru			fd = STDIN_FILENO;
17783482Sdillon		} else {
17883482Sdillon			filename = path;
17983482Sdillon			fd = open(path, O_RDONLY);
18083482Sdillon#ifndef NO_UDOM_SUPPORT
18183482Sdillon			if (fd < 0 && errno == EOPNOTSUPP)
18283962Sru				fd = udom_open(path, O_RDONLY);
18383482Sdillon#endif
1841556Srgrimes		}
18583482Sdillon		if (fd < 0) {
18683482Sdillon			warn("%s", path);
18783482Sdillon			rval = 1;
18883482Sdillon		} else if (cooked) {
18983961Sru			if (fd == STDIN_FILENO)
19083961Sru				cook_cat(stdin);
19183961Sru			else {
19283961Sru				fp = fdopen(fd, "r");
19383961Sru				cook_cat(fp);
19483961Sru				fclose(fp);
19583961Sru			}
19683482Sdillon		} else {
19783482Sdillon			raw_cat(fd);
19883961Sru			if (fd != STDIN_FILENO)
19983961Sru				close(fd);
20083482Sdillon		}
20183482Sdillon		if (path == NULL)
20283482Sdillon			break;
20383482Sdillon		++i;
20483482Sdillon	}
2051556Srgrimes}
2061556Srgrimes
20783482Sdillonstatic void
20890106Simpcook_cat(FILE *fp)
2091556Srgrimes{
21090106Simp	int ch, gobble, line, prev;
211306201Sache	wint_t wch;
2121556Srgrimes
21383961Sru	/* Reset EOF condition on stdin. */
21483961Sru	if (fp == stdin && feof(stdin))
21583961Sru		clearerr(stdin);
21683961Sru
2171556Srgrimes	line = gobble = 0;
2181556Srgrimes	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
2191556Srgrimes		if (prev == '\n') {
22098169Stjr			if (sflag) {
22198169Stjr				if (ch == '\n') {
22298169Stjr					if (gobble)
22398169Stjr						continue;
2241556Srgrimes					gobble = 1;
22598169Stjr				} else
22698169Stjr					gobble = 0;
22798169Stjr			}
228330738Seadler			if (nflag && (!bflag || ch != '\n')) {
229330738Seadler				(void)fprintf(stdout, "%6d\t", ++line);
230330738Seadler				if (ferror(stdout))
231330738Seadler					break;
2321556Srgrimes			}
2331556Srgrimes		}
2341556Srgrimes		if (ch == '\n') {
23598169Stjr			if (eflag && putchar('$') == EOF)
23698169Stjr				break;
2371556Srgrimes		} else if (ch == '\t') {
2381556Srgrimes			if (tflag) {
2391556Srgrimes				if (putchar('^') == EOF || putchar('I') == EOF)
2401556Srgrimes					break;
2411556Srgrimes				continue;
2421556Srgrimes			}
2431556Srgrimes		} else if (vflag) {
244306201Sache			(void)ungetc(ch, fp);
245306201Sache			/*
246306201Sache			 * Our getwc(3) doesn't change file position
247306201Sache			 * on error.
248306201Sache			 */
249306201Sache			if ((wch = getwc(fp)) == WEOF) {
250306201Sache				if (ferror(fp) && errno == EILSEQ) {
251306201Sache					clearerr(fp);
252306201Sache					/* Resync attempt. */
253306201Sache					memset(&fp->_mbstate, 0, sizeof(mbstate_t));
254306201Sache					if ((ch = getc(fp)) == EOF)
255306201Sache						break;
256306201Sache					wch = ch;
257306201Sache					goto ilseq;
258306201Sache				} else
259306201Sache					break;
260306201Sache			}
261306201Sache			if (!iswascii(wch) && !iswprint(wch)) {
262306201Sacheilseq:
2631556Srgrimes				if (putchar('M') == EOF || putchar('-') == EOF)
2641556Srgrimes					break;
265306201Sache				wch = toascii(wch);
2661556Srgrimes			}
267306201Sache			if (iswcntrl(wch)) {
268306201Sache				ch = toascii(wch);
269306201Sache				ch = (ch == '\177') ? '?' : (ch | 0100);
270306201Sache				if (putchar('^') == EOF || putchar(ch) == EOF)
2711556Srgrimes					break;
2721556Srgrimes				continue;
2731556Srgrimes			}
274306201Sache			if (putwchar(wch) == WEOF)
275306201Sache				break;
276306201Sache			ch = -1;
277306201Sache			continue;
2781556Srgrimes		}
2791556Srgrimes		if (putchar(ch) == EOF)
2801556Srgrimes			break;
2811556Srgrimes	}
2821556Srgrimes	if (ferror(fp)) {
2831556Srgrimes		warn("%s", filename);
28411145Sbde		rval = 1;
2851556Srgrimes		clearerr(fp);
2861556Srgrimes	}
2871556Srgrimes	if (ferror(stdout))
2881556Srgrimes		err(1, "stdout");
2891556Srgrimes}
2901556Srgrimes
29183482Sdillonstatic void
29290106Simpraw_cat(int rfd)
2931556Srgrimes{
29490106Simp	int off, wfd;
29539065Simp	ssize_t nr, nw;
29639065Simp	static size_t bsize;
29791079Smarkm	static char *buf = NULL;
2981556Srgrimes	struct stat sbuf;
2991556Srgrimes
3001556Srgrimes	wfd = fileno(stdout);
3011556Srgrimes	if (buf == NULL) {
3021556Srgrimes		if (fstat(wfd, &sbuf))
303238653Sjh			err(1, "stdout");
304184471Sivoras		if (S_ISREG(sbuf.st_mode)) {
305184471Sivoras			/* If there's plenty of RAM, use a large copy buffer */
306184471Sivoras			if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD)
307238652Sjh				bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
308184471Sivoras			else
309184471Sivoras				bsize = BUFSIZE_SMALL;
310184471Sivoras		} else
311238652Sjh			bsize = MAX(sbuf.st_blksize,
312238652Sjh			    (blksize_t)sysconf(_SC_PAGESIZE));
31339138Simp		if ((buf = malloc(bsize)) == NULL)
314184471Sivoras			err(1, "malloc() failure of IO buffer");
3151556Srgrimes	}
3161556Srgrimes	while ((nr = read(rfd, buf, bsize)) > 0)
3171556Srgrimes		for (off = 0; nr; nr -= nw, off += nw)
31839138Simp			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
3191556Srgrimes				err(1, "stdout");
32011145Sbde	if (nr < 0) {
3211556Srgrimes		warn("%s", filename);
32211145Sbde		rval = 1;
32311145Sbde	}
3241556Srgrimes}
32583482Sdillon
32683482Sdillon#ifndef NO_UDOM_SUPPORT
32783482Sdillon
32883482Sdillonstatic int
32990106Simpudom_open(const char *path, int flags)
33083482Sdillon{
331288602Shrs	struct addrinfo hints, *res, *res0;
332288602Shrs	char rpath[PATH_MAX];
333288645Ssbruno	int fd = -1;
334288645Ssbruno	int error;
33583482Sdillon
33683482Sdillon	/*
337288602Shrs	 * Construct the unix domain socket address and attempt to connect.
33883482Sdillon	 */
339288602Shrs	bzero(&hints, sizeof(hints));
340288602Shrs	hints.ai_family = AF_LOCAL;
341288602Shrs	if (realpath(path, rpath) == NULL)
342288602Shrs		return (-1);
343288602Shrs	error = getaddrinfo(rpath, NULL, &hints, &res0);
344288602Shrs	if (error) {
345288602Shrs		warn("%s", gai_strerror(error));
346288602Shrs		errno = EINVAL;
347288602Shrs		return (-1);
348288602Shrs	}
349288602Shrs	for (res = res0; res != NULL; res = res->ai_next) {
350288602Shrs		fd = socket(res->ai_family, res->ai_socktype,
351288602Shrs		    res->ai_protocol);
352288602Shrs		if (fd < 0) {
353288602Shrs			freeaddrinfo(res0);
35499022Stjr			return (-1);
35599022Stjr		}
356288602Shrs		error = connect(fd, res->ai_addr, res->ai_addrlen);
357288602Shrs		if (error == 0)
358288602Shrs			break;
359288602Shrs		else {
36083482Sdillon			close(fd);
36183482Sdillon			fd = -1;
36283482Sdillon		}
36383482Sdillon	}
364288602Shrs	freeaddrinfo(res0);
36583482Sdillon
36683482Sdillon	/*
36783482Sdillon	 * handle the open flags by shutting down appropriate directions
36883482Sdillon	 */
36983482Sdillon	if (fd >= 0) {
37083482Sdillon		switch(flags & O_ACCMODE) {
37183482Sdillon		case O_RDONLY:
37291079Smarkm			if (shutdown(fd, SHUT_WR) == -1)
373132433Stjr				warn(NULL);
37483482Sdillon			break;
37583482Sdillon		case O_WRONLY:
37691079Smarkm			if (shutdown(fd, SHUT_RD) == -1)
377132433Stjr				warn(NULL);
37883482Sdillon			break;
37983482Sdillon		default:
38083482Sdillon			break;
38183482Sdillon		}
38283482Sdillon	}
383238652Sjh	return (fd);
38483482Sdillon}
38583482Sdillon
38683482Sdillon#endif
387