xref: /openbsd/bin/cat/cat.c (revision cca36db2)
1 /*	$OpenBSD: cat.c,v 1.20 2009/10/27 23:59:19 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/param.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 extern char *__progname;
50 
51 int bflag, eflag, nflag, sflag, tflag, vflag;
52 int rval;
53 char *filename;
54 
55 void cook_args(char *argv[]);
56 void cook_buf(FILE *);
57 void raw_args(char *argv[]);
58 void raw_cat(int);
59 
60 int
61 main(int argc, char *argv[])
62 {
63 	int ch;
64 
65 	setlocale(LC_ALL, "");
66 
67 	while ((ch = getopt(argc, argv, "benstuv")) != -1)
68 		switch (ch) {
69 		case 'b':
70 			bflag = nflag = 1;	/* -b implies -n */
71 			break;
72 		case 'e':
73 			eflag = vflag = 1;	/* -e implies -v */
74 			break;
75 		case 'n':
76 			nflag = 1;
77 			break;
78 		case 's':
79 			sflag = 1;
80 			break;
81 		case 't':
82 			tflag = vflag = 1;	/* -t implies -v */
83 			break;
84 		case 'u':
85 			setbuf(stdout, NULL);
86 			break;
87 		case 'v':
88 			vflag = 1;
89 			break;
90 		default:
91 			(void)fprintf(stderr,
92 			    "usage: %s [-benstuv] [file ...]\n", __progname);
93 			exit(1);
94 			/* NOTREACHED */
95 		}
96 	argv += optind;
97 
98 	if (bflag || eflag || nflag || sflag || tflag || vflag)
99 		cook_args(argv);
100 	else
101 		raw_args(argv);
102 	if (fclose(stdout))
103 		err(1, "stdout");
104 	exit(rval);
105 	/* NOTREACHED */
106 }
107 
108 void
109 cook_args(char **argv)
110 {
111 	FILE *fp;
112 
113 	fp = stdin;
114 	filename = "stdin";
115 	do {
116 		if (*argv) {
117 			if (!strcmp(*argv, "-"))
118 				fp = stdin;
119 			else if ((fp = fopen(*argv, "r")) == NULL) {
120 				warn("%s", *argv);
121 				rval = 1;
122 				++argv;
123 				continue;
124 			}
125 			filename = *argv++;
126 		}
127 		cook_buf(fp);
128 		if (fp == stdin)
129 			clearerr(fp);
130 		else
131 			(void)fclose(fp);
132 	} while (*argv);
133 }
134 
135 void
136 cook_buf(FILE *fp)
137 {
138 	int ch, gobble, line, prev;
139 
140 	line = gobble = 0;
141 	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
142 		if (prev == '\n') {
143 			if (sflag) {
144 				if (ch == '\n') {
145 					if (gobble)
146 						continue;
147 					gobble = 1;
148 				} else
149 					gobble = 0;
150 			}
151 			if (nflag && (!bflag || ch != '\n')) {
152 				(void)fprintf(stdout, "%6d\t", ++line);
153 				if (ferror(stdout))
154 					break;
155 			}
156 		}
157 		if (ch == '\n') {
158 			if (eflag && putchar('$') == EOF)
159 				break;
160 		} else if (ch == '\t') {
161 			if (tflag) {
162 				if (putchar('^') == EOF || putchar('I') == EOF)
163 					break;
164 				continue;
165 			}
166 		} else if (vflag) {
167 			if (!isascii(ch)) {
168 				if (putchar('M') == EOF || putchar('-') == EOF)
169 					break;
170 				ch = toascii(ch);
171 			}
172 			if (iscntrl(ch)) {
173 				if (putchar('^') == EOF ||
174 				    putchar(ch == '\177' ? '?' :
175 				    ch | 0100) == EOF)
176 					break;
177 				continue;
178 			}
179 		}
180 		if (putchar(ch) == EOF)
181 			break;
182 	}
183 	if (ferror(fp)) {
184 		warn("%s", filename);
185 		rval = 1;
186 		clearerr(fp);
187 	}
188 	if (ferror(stdout))
189 		err(1, "stdout");
190 }
191 
192 void
193 raw_args(char **argv)
194 {
195 	int fd;
196 
197 	fd = fileno(stdin);
198 	filename = "stdin";
199 	do {
200 		if (*argv) {
201 			if (!strcmp(*argv, "-"))
202 				fd = fileno(stdin);
203 			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
204 				warn("%s", *argv);
205 				rval = 1;
206 				++argv;
207 				continue;
208 			}
209 			filename = *argv++;
210 		}
211 		raw_cat(fd);
212 		if (fd != fileno(stdin))
213 			(void)close(fd);
214 	} while (*argv);
215 }
216 
217 void
218 raw_cat(int rfd)
219 {
220 	int wfd;
221 	ssize_t nr, nw, off;
222 	static size_t bsize;
223 	static char *buf = NULL;
224 	struct stat sbuf;
225 
226 	wfd = fileno(stdout);
227 	if (buf == NULL) {
228 		if (fstat(wfd, &sbuf))
229 			err(1, "stdout");
230 		bsize = MAX(sbuf.st_blksize, BUFSIZ);
231 		if ((buf = malloc(bsize)) == NULL)
232 			err(1, "malloc");
233 	}
234 	while ((nr = read(rfd, buf, bsize)) != -1 && nr != 0)
235 		for (off = 0; nr; nr -= nw, off += nw)
236 			if ((nw = write(wfd, buf + off, (size_t)nr)) == 0 ||
237 			     nw == -1)
238 				err(1, "stdout");
239 	if (nr < 0) {
240 		warn("%s", filename);
241 		rval = 1;
242 	}
243 }
244