xref: /openbsd/bin/cat/cat.c (revision cecf84d4)
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 
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 	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 
110 void
111 cook_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 
137 void
138 cook_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 
194 void
195 raw_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 
219 void
220 raw_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