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