1/* $NetBSD: cat.c,v 1.47 2008/07/20 00:52:39 lukem Exp $	*/
2
3/*
4 * Copyright (c) 1989, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kevin Fall.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#if HAVE_NBTOOL_CONFIG_H
36#include "nbtool_config.h"
37#endif
38
39#include <sys/cdefs.h>
40#if !defined(lint)
41__COPYRIGHT(
42"@(#) Copyright (c) 1989, 1993\
43 The Regents of the University of California.  All rights reserved.");
44#if 0
45static char sccsid[] = "@(#)cat.c	8.2 (Berkeley) 4/27/95";
46#else
47__RCSID("$NetBSD: cat.c,v 1.47 2008/07/20 00:52:39 lukem Exp $");
48#endif
49#endif /* not lint */
50
51#include <sys/param.h>
52#include <sys/stat.h>
53
54#include <ctype.h>
55#include <err.h>
56#include <errno.h>
57#include <fcntl.h>
58#include <locale.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63
64int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag;
65int rval;
66const char *filename;
67
68int main(int, char *[]);
69void cook_args(char *argv[]);
70void cook_buf(FILE *);
71void raw_args(char *argv[]);
72void raw_cat(int);
73
74int
75main(int argc, char *argv[])
76{
77	int ch;
78	struct flock stdout_lock;
79
80	setprogname(argv[0]);
81	(void)setlocale(LC_ALL, "");
82
83	while ((ch = getopt(argc, argv, "beflnstuv")) != -1)
84		switch (ch) {
85		case 'b':
86			bflag = nflag = 1;	/* -b implies -n */
87			break;
88		case 'e':
89			eflag = vflag = 1;	/* -e implies -v */
90			break;
91		case 'f':
92			fflag = 1;
93			break;
94		case 'l':
95			lflag = 1;
96			break;
97		case 'n':
98			nflag = 1;
99			break;
100		case 's':
101			sflag = 1;
102			break;
103		case 't':
104			tflag = vflag = 1;	/* -t implies -v */
105			break;
106		case 'u':
107			setbuf(stdout, NULL);
108			break;
109		case 'v':
110			vflag = 1;
111			break;
112		default:
113		case '?':
114			(void)fprintf(stderr,
115			    "usage: cat [-beflnstuv] [-] [file ...]\n");
116			exit(EXIT_FAILURE);
117			/* NOTREACHED */
118		}
119	argv += optind;
120
121	if (lflag) {
122		stdout_lock.l_len = 0;
123		stdout_lock.l_start = 0;
124		stdout_lock.l_type = F_WRLCK;
125		stdout_lock.l_whence = SEEK_SET;
126		if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1)
127			err(EXIT_FAILURE, "stdout");
128	}
129
130	if (bflag || eflag || nflag || sflag || tflag || vflag)
131		cook_args(argv);
132	else
133		raw_args(argv);
134	if (fclose(stdout))
135		err(EXIT_FAILURE, "stdout");
136	return (rval);
137}
138
139void
140cook_args(char **argv)
141{
142	FILE *fp;
143
144	fp = stdin;
145	filename = "stdin";
146	do {
147		if (*argv) {
148			if (!strcmp(*argv, "-"))
149				fp = stdin;
150			else if ((fp = fopen(*argv,
151			    fflag ? "rf" : "r")) == NULL) {
152				warn("%s", *argv);
153				rval = EXIT_FAILURE;
154				++argv;
155				continue;
156			}
157			filename = *argv++;
158		}
159		cook_buf(fp);
160		if (fp != stdin)
161			(void)fclose(fp);
162		else
163			clearerr(fp);
164	} while (*argv);
165}
166
167void
168cook_buf(FILE *fp)
169{
170	int ch, gobble, line, prev;
171
172	line = gobble = 0;
173	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
174		if (prev == '\n') {
175			if (ch == '\n') {
176				if (sflag) {
177					if (!gobble && nflag && !bflag)
178						(void)fprintf(stdout,
179							"%6d\t\n", ++line);
180					else if (!gobble && putchar(ch) == EOF)
181						break;
182					gobble = 1;
183					continue;
184				}
185				if (nflag) {
186					if (!bflag) {
187						(void)fprintf(stdout,
188						    "%6d\t", ++line);
189						if (ferror(stdout))
190							break;
191					} else if (eflag) {
192						(void)fprintf(stdout,
193						    "%6s\t", "");
194						if (ferror(stdout))
195							break;
196					}
197				}
198			} else if (nflag) {
199				(void)fprintf(stdout, "%6d\t", ++line);
200				if (ferror(stdout))
201					break;
202			}
203		}
204		gobble = 0;
205		if (ch == '\n') {
206			if (eflag)
207				if (putchar('$') == EOF)
208					break;
209		} else if (ch == '\t') {
210			if (tflag) {
211				if (putchar('^') == EOF || putchar('I') == EOF)
212					break;
213				continue;
214			}
215		} else if (vflag) {
216			if (!isascii(ch)) {
217				if (putchar('M') == EOF || putchar('-') == EOF)
218					break;
219				ch = toascii(ch);
220			}
221			if (iscntrl(ch)) {
222				if (putchar('^') == EOF ||
223				    putchar(ch == '\177' ? '?' :
224				    ch | 0100) == EOF)
225					break;
226				continue;
227			}
228		}
229		if (putchar(ch) == EOF)
230			break;
231	}
232	if (ferror(fp)) {
233		warn("%s", filename);
234		rval = EXIT_FAILURE;
235		clearerr(fp);
236	}
237	if (ferror(stdout))
238		err(EXIT_FAILURE, "stdout");
239}
240
241void
242raw_args(char **argv)
243{
244	int fd;
245
246	fd = fileno(stdin);
247	filename = "stdin";
248	do {
249		if (*argv) {
250			if (!strcmp(*argv, "-"))
251				fd = fileno(stdin);
252			else if (fflag) {
253				struct stat st;
254				fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
255				if (fd < 0)
256					goto skip;
257
258				if (fstat(fd, &st) == -1) {
259					close(fd);
260					goto skip;
261				}
262				if (!S_ISREG(st.st_mode)) {
263					close(fd);
264					warnx("%s: not a regular file", *argv);
265					goto skipnomsg;
266				}
267			}
268			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
269skip:
270				warn("%s", *argv);
271skipnomsg:
272				rval = EXIT_FAILURE;
273				++argv;
274				continue;
275			}
276			filename = *argv++;
277		}
278		raw_cat(fd);
279		if (fd != fileno(stdin))
280			(void)close(fd);
281	} while (*argv);
282}
283
284void
285raw_cat(int rfd)
286{
287	static char *buf;
288	static char fb_buf[BUFSIZ];
289	static size_t bsize;
290
291	ssize_t nr, nw, off;
292	int wfd;
293
294	wfd = fileno(stdout);
295	if (buf == NULL) {
296		struct stat sbuf;
297
298		if (fstat(wfd, &sbuf) == 0 &&
299		    sbuf.st_blksize > sizeof(fb_buf)) {
300			bsize = sbuf.st_blksize;
301			buf = malloc(bsize);
302		}
303		if (buf == NULL) {
304			bsize = sizeof(fb_buf);
305			buf = fb_buf;
306		}
307	}
308	while ((nr = read(rfd, buf, bsize)) > 0)
309		for (off = 0; nr; nr -= nw, off += nw)
310			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
311				err(EXIT_FAILURE, "stdout");
312	if (nr < 0) {
313		warn("%s", filename);
314		rval = EXIT_FAILURE;
315	}
316}
317