cat.c revision 1.21
1/*	$OpenBSD: cat.c,v 1.21 2015/01/16 06:39:28 deraadt Exp $	*/
2/*	$NetBSD: cat.c,v 1.11 1995/09/07 06:12:54 jtc Exp $	*/
3
4/*
5 * Copyright (c) 1989, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Kevin Fall.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/types.h>
37#include <sys/stat.h>
38
39#include <ctype.h>
40#include <err.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <locale.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49#define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
50
51extern char *__progname;
52
53int bflag, eflag, nflag, sflag, tflag, vflag;
54int rval;
55char *filename;
56
57void cook_args(char *argv[]);
58void cook_buf(FILE *);
59void raw_args(char *argv[]);
60void raw_cat(int);
61
62int
63main(int argc, char *argv[])
64{
65	int ch;
66
67	setlocale(LC_ALL, "");
68
69	while ((ch = getopt(argc, argv, "benstuv")) != -1)
70		switch (ch) {
71		case 'b':
72			bflag = nflag = 1;	/* -b implies -n */
73			break;
74		case 'e':
75			eflag = vflag = 1;	/* -e implies -v */
76			break;
77		case 'n':
78			nflag = 1;
79			break;
80		case 's':
81			sflag = 1;
82			break;
83		case 't':
84			tflag = vflag = 1;	/* -t implies -v */
85			break;
86		case 'u':
87			setbuf(stdout, NULL);
88			break;
89		case 'v':
90			vflag = 1;
91			break;
92		default:
93			(void)fprintf(stderr,
94			    "usage: %s [-benstuv] [file ...]\n", __progname);
95			exit(1);
96			/* NOTREACHED */
97		}
98	argv += optind;
99
100	if (bflag || eflag || nflag || sflag || tflag || vflag)
101		cook_args(argv);
102	else
103		raw_args(argv);
104	if (fclose(stdout))
105		err(1, "stdout");
106	exit(rval);
107	/* NOTREACHED */
108}
109
110void
111cook_args(char **argv)
112{
113	FILE *fp;
114
115	fp = stdin;
116	filename = "stdin";
117	do {
118		if (*argv) {
119			if (!strcmp(*argv, "-"))
120				fp = stdin;
121			else if ((fp = fopen(*argv, "r")) == NULL) {
122				warn("%s", *argv);
123				rval = 1;
124				++argv;
125				continue;
126			}
127			filename = *argv++;
128		}
129		cook_buf(fp);
130		if (fp == stdin)
131			clearerr(fp);
132		else
133			(void)fclose(fp);
134	} while (*argv);
135}
136
137void
138cook_buf(FILE *fp)
139{
140	int ch, gobble, line, prev;
141
142	line = gobble = 0;
143	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
144		if (prev == '\n') {
145			if (sflag) {
146				if (ch == '\n') {
147					if (gobble)
148						continue;
149					gobble = 1;
150				} else
151					gobble = 0;
152			}
153			if (nflag && (!bflag || ch != '\n')) {
154				(void)fprintf(stdout, "%6d\t", ++line);
155				if (ferror(stdout))
156					break;
157			}
158		}
159		if (ch == '\n') {
160			if (eflag && putchar('$') == EOF)
161				break;
162		} else if (ch == '\t') {
163			if (tflag) {
164				if (putchar('^') == EOF || putchar('I') == EOF)
165					break;
166				continue;
167			}
168		} else if (vflag) {
169			if (!isascii(ch)) {
170				if (putchar('M') == EOF || putchar('-') == EOF)
171					break;
172				ch = toascii(ch);
173			}
174			if (iscntrl(ch)) {
175				if (putchar('^') == EOF ||
176				    putchar(ch == '\177' ? '?' :
177				    ch | 0100) == EOF)
178					break;
179				continue;
180			}
181		}
182		if (putchar(ch) == EOF)
183			break;
184	}
185	if (ferror(fp)) {
186		warn("%s", filename);
187		rval = 1;
188		clearerr(fp);
189	}
190	if (ferror(stdout))
191		err(1, "stdout");
192}
193
194void
195raw_args(char **argv)
196{
197	int fd;
198
199	fd = fileno(stdin);
200	filename = "stdin";
201	do {
202		if (*argv) {
203			if (!strcmp(*argv, "-"))
204				fd = fileno(stdin);
205			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
206				warn("%s", *argv);
207				rval = 1;
208				++argv;
209				continue;
210			}
211			filename = *argv++;
212		}
213		raw_cat(fd);
214		if (fd != fileno(stdin))
215			(void)close(fd);
216	} while (*argv);
217}
218
219void
220raw_cat(int rfd)
221{
222	int wfd;
223	ssize_t nr, nw, off;
224	static size_t bsize;
225	static char *buf = NULL;
226	struct stat sbuf;
227
228	wfd = fileno(stdout);
229	if (buf == NULL) {
230		if (fstat(wfd, &sbuf))
231			err(1, "stdout");
232		bsize = MAXIMUM(sbuf.st_blksize, BUFSIZ);
233		if ((buf = malloc(bsize)) == NULL)
234			err(1, "malloc");
235	}
236	while ((nr = read(rfd, buf, bsize)) != -1 && nr != 0)
237		for (off = 0; nr; nr -= nw, off += nw)
238			if ((nw = write(wfd, buf + off, (size_t)nr)) == 0 ||
239			     nw == -1)
240				err(1, "stdout");
241	if (nr < 0) {
242		warn("%s", filename);
243		rval = 1;
244	}
245}
246