1 | /* $NetBSD: cat.c,v 1.57 2016/06/16 00:52:37 sevan 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 |
45 | static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95" ; |
46 | #else |
47 | __RCSID("$NetBSD: cat.c,v 1.57 2016/06/16 00:52:37 sevan 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 | |
64 | static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag; |
65 | static size_t bsize; |
66 | static int rval; |
67 | static const char *filename; |
68 | |
69 | void cook_args(char *argv[]); |
70 | void cook_buf(FILE *); |
71 | void raw_args(char *argv[]); |
72 | void raw_cat(int); |
73 | |
74 | int |
75 | main(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, "B:beflnstuv" )) != -1) |
84 | switch (ch) { |
85 | case 'B': |
86 | bsize = (size_t)strtol(optarg, NULL, 0); |
87 | break; |
88 | case 'b': |
89 | bflag = nflag = 1; /* -b implies -n */ |
90 | break; |
91 | case 'e': |
92 | eflag = vflag = 1; /* -e implies -v */ |
93 | break; |
94 | case 'f': |
95 | fflag = 1; |
96 | break; |
97 | case 'l': |
98 | lflag = 1; |
99 | break; |
100 | case 'n': |
101 | nflag = 1; |
102 | break; |
103 | case 's': |
104 | sflag = 1; |
105 | break; |
106 | case 't': |
107 | tflag = vflag = 1; /* -t implies -v */ |
108 | break; |
109 | case 'u': |
110 | setbuf(stdout, NULL); |
111 | break; |
112 | case 'v': |
113 | vflag = 1; |
114 | break; |
115 | default: |
116 | case '?': |
117 | (void)fprintf(stderr, |
118 | "Usage: %s [-beflnstuv] [-B bsize] [-] " |
119 | "[file ...]\n" , getprogname()); |
120 | return EXIT_FAILURE; |
121 | } |
122 | argv += optind; |
123 | |
124 | if (lflag) { |
125 | stdout_lock.l_len = 0; |
126 | stdout_lock.l_start = 0; |
127 | stdout_lock.l_type = F_WRLCK; |
128 | stdout_lock.l_whence = SEEK_SET; |
129 | if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) |
130 | err(EXIT_FAILURE, "stdout" ); |
131 | } |
132 | |
133 | if (bflag || eflag || nflag || sflag || tflag || vflag) |
134 | cook_args(argv); |
135 | else |
136 | raw_args(argv); |
137 | if (fclose(stdout)) |
138 | err(EXIT_FAILURE, "stdout" ); |
139 | return rval; |
140 | } |
141 | |
142 | void |
143 | cook_args(char **argv) |
144 | { |
145 | FILE *fp; |
146 | |
147 | fp = stdin; |
148 | filename = "stdin" ; |
149 | do { |
150 | if (*argv) { |
151 | if (!strcmp(*argv, "-" )) |
152 | fp = stdin; |
153 | else if ((fp = fopen(*argv, |
154 | fflag ? "rf" : "r" )) == NULL) { |
155 | warn("%s" , *argv); |
156 | rval = EXIT_FAILURE; |
157 | ++argv; |
158 | continue; |
159 | } |
160 | filename = *argv++; |
161 | } |
162 | cook_buf(fp); |
163 | if (fp != stdin) |
164 | (void)fclose(fp); |
165 | else |
166 | clearerr(fp); |
167 | } while (*argv); |
168 | } |
169 | |
170 | void |
171 | cook_buf(FILE *fp) |
172 | { |
173 | int ch, gobble, line, prev; |
174 | |
175 | line = gobble = 0; |
176 | for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { |
177 | if (prev == '\n') { |
178 | if (sflag) { |
179 | if (ch == '\n') { |
180 | if (gobble) |
181 | continue; |
182 | gobble = 1; |
183 | } else |
184 | gobble = 0; |
185 | } |
186 | if (nflag) { |
187 | if (!bflag || ch != '\n') { |
188 | (void)fprintf(stdout, |
189 | "%6d\t" , ++line); |
190 | if (ferror(stdout)) |
191 | break; |
192 | } else if (eflag) { |
193 | (void)fprintf(stdout, |
194 | "%6s\t" , "" ); |
195 | if (ferror(stdout)) |
196 | break; |
197 | } |
198 | } |
199 | } |
200 | if (ch == '\n') { |
201 | if (eflag) |
202 | if (putchar('$') == EOF) |
203 | break; |
204 | } else if (ch == '\t') { |
205 | if (tflag) { |
206 | if (putchar('^') == EOF || putchar('I') == EOF) |
207 | break; |
208 | continue; |
209 | } |
210 | } else if (vflag) { |
211 | if (!isascii(ch)) { |
212 | if (putchar('M') == EOF || putchar('-') == EOF) |
213 | break; |
214 | ch = toascii(ch); |
215 | } |
216 | if (iscntrl(ch)) { |
217 | if (putchar('^') == EOF || |
218 | putchar(ch == '\177' ? '?' : |
219 | ch | 0100) == EOF) |
220 | break; |
221 | continue; |
222 | } |
223 | } |
224 | if (putchar(ch) == EOF) |
225 | break; |
226 | } |
227 | if (ferror(fp)) { |
228 | warn("%s" , filename); |
229 | rval = EXIT_FAILURE; |
230 | clearerr(fp); |
231 | } |
232 | if (ferror(stdout)) |
233 | err(EXIT_FAILURE, "stdout" ); |
234 | } |
235 | |
236 | void |
237 | raw_args(char **argv) |
238 | { |
239 | int fd; |
240 | |
241 | fd = fileno(stdin); |
242 | filename = "stdin" ; |
243 | do { |
244 | if (*argv) { |
245 | if (!strcmp(*argv, "-" )) { |
246 | fd = fileno(stdin); |
247 | if (fd < 0) |
248 | goto skip; |
249 | } else if (fflag) { |
250 | struct stat st; |
251 | fd = open(*argv, O_RDONLY|O_NONBLOCK, 0); |
252 | if (fd < 0) |
253 | goto skip; |
254 | |
255 | if (fstat(fd, &st) == -1) { |
256 | close(fd); |
257 | goto skip; |
258 | } |
259 | if (!S_ISREG(st.st_mode)) { |
260 | close(fd); |
261 | warnx("%s: not a regular file" , *argv); |
262 | goto skipnomsg; |
263 | } |
264 | } |
265 | else if ((fd = open(*argv, O_RDONLY, 0)) < 0) { |
266 | skip: |
267 | warn("%s" , *argv); |
268 | skipnomsg: |
269 | rval = EXIT_FAILURE; |
270 | ++argv; |
271 | continue; |
272 | } |
273 | filename = *argv++; |
274 | } else if (fd < 0) { |
275 | err(EXIT_FAILURE, "stdin" ); |
276 | } |
277 | raw_cat(fd); |
278 | if (fd != fileno(stdin)) |
279 | (void)close(fd); |
280 | } while (*argv); |
281 | } |
282 | |
283 | void |
284 | raw_cat(int rfd) |
285 | { |
286 | static char *buf; |
287 | static char fb_buf[BUFSIZ]; |
288 | |
289 | ssize_t nr, nw, off; |
290 | int wfd; |
291 | |
292 | wfd = fileno(stdout); |
293 | if (wfd < 0) |
294 | err(EXIT_FAILURE, "stdout" ); |
295 | if (buf == NULL) { |
296 | struct stat sbuf; |
297 | |
298 | if (bsize == 0) { |
299 | if (fstat(wfd, &sbuf) == 0 && sbuf.st_blksize > 0 && |
300 | (size_t)sbuf.st_blksize > sizeof(fb_buf)) |
301 | bsize = sbuf.st_blksize; |
302 | } |
303 | if (bsize > sizeof(fb_buf)) { |
304 | buf = malloc(bsize); |
305 | if (buf == NULL) |
306 | warnx("malloc, using %zu buffer" , bsize); |
307 | } |
308 | if (buf == NULL) { |
309 | bsize = sizeof(fb_buf); |
310 | buf = fb_buf; |
311 | } |
312 | } |
313 | while ((nr = read(rfd, buf, bsize)) > 0) |
314 | for (off = 0; nr; nr -= nw, off += nw) |
315 | if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) |
316 | err(EXIT_FAILURE, "stdout" ); |
317 | if (nr < 0) { |
318 | warn("%s" , filename); |
319 | rval = EXIT_FAILURE; |
320 | } |
321 | } |
322 | |