1/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma 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#include <sys/param.h> 36#include <sys/stat.h> 37 38#include <ctype.h> 39#include <errno.h> 40#include <fcntl.h> 41#include <stdio.h> 42#include <stdlib.h> 43#include <string.h> 44#include <unistd.h> 45 46#define CAT_BUFSIZ (4096) 47 48static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag; 49static int rval; 50static const char *filename; 51 52static void 53cook_buf(FILE *fp) 54{ 55 int ch, gobble, line, prev; 56 int stdout_err = 0; 57 58 line = gobble = 0; 59 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 60 if (prev == '\n') { 61 if (ch == '\n') { 62 if (sflag) { 63 if (!gobble && putchar(ch) == EOF) 64 break; 65 gobble = 1; 66 continue; 67 } 68 if (nflag) { 69 if (!bflag) { 70 if (fprintf(stdout, 71 "%6d\t", ++line) < 0) { 72 stdout_err++; 73 break; 74 } 75 } else if (eflag) { 76 if (fprintf(stdout, 77 "%6s\t", "") < 0) { 78 stdout_err++; 79 break; 80 } 81 } 82 } 83 } else if (nflag) { 84 if (fprintf(stdout, "%6d\t", ++line) < 0) { 85 stdout_err++; 86 break; 87 } 88 } 89 } 90 gobble = 0; 91 if (ch == '\n') { 92 if (eflag) 93 if (putchar('$') == EOF) 94 break; 95 } else if (ch == '\t') { 96 if (tflag) { 97 if (putchar('^') == EOF || putchar('I') == EOF) 98 break; 99 continue; 100 } 101 } else if (vflag) { 102 if (!isascii(ch)) { 103 if (putchar('M') == EOF || putchar('-') == EOF) 104 break; 105 ch = (ch) & 0x7f; 106 } 107 if (iscntrl(ch)) { 108 if (putchar('^') == EOF || 109 putchar(ch == '\177' ? '?' : 110 ch | 0100) == EOF) 111 break; 112 continue; 113 } 114 } 115 if (putchar(ch) == EOF) 116 break; 117 } 118 if (stdout_err) { 119 perror(filename); 120 rval = 1; 121 } 122} 123 124static void 125cook_args(char **argv) 126{ 127 FILE *fp; 128 129 fp = stdin; 130 filename = "stdin"; 131 do { 132 if (*argv) { 133 if (!strcmp(*argv, "-")) 134 fp = stdin; 135 else if ((fp = fopen(*argv, 136 fflag ? "rf" : "r")) == NULL) { 137 perror("fopen"); 138 rval = 1; 139 ++argv; 140 continue; 141 } 142 filename = *argv++; 143 } 144 cook_buf(fp); 145 if (fp != stdin) 146 fclose(fp); 147 } while (*argv); 148} 149 150static void 151raw_cat(int rfd) 152{ 153 static char *buf; 154 static char fb_buf[CAT_BUFSIZ]; 155 static size_t bsize; 156 157 struct stat sbuf; 158 ssize_t nr, nw, off; 159 int wfd; 160 161 wfd = fileno(stdout); 162 if (buf == NULL) { 163 if (fstat(wfd, &sbuf) == 0) { 164 bsize = sbuf.st_blksize > CAT_BUFSIZ ? 165 sbuf.st_blksize : CAT_BUFSIZ; 166 buf = malloc(bsize); 167 } 168 if (buf == NULL) { 169 buf = fb_buf; 170 bsize = CAT_BUFSIZ; 171 } 172 } 173 while ((nr = read(rfd, buf, bsize)) > 0) 174 for (off = 0; nr; nr -= nw, off += nw) 175 if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) 176 { 177 perror("write"); 178 exit(EXIT_FAILURE); 179 } 180 if (nr < 0) { 181 fprintf(stderr,"%s: invalid length\n", filename); 182 rval = 1; 183 } 184} 185 186static void 187raw_args(char **argv) 188{ 189 int fd; 190 191 fd = fileno(stdin); 192 filename = "stdin"; 193 do { 194 if (*argv) { 195 if (!strcmp(*argv, "-")) 196 fd = fileno(stdin); 197 else if (fflag) { 198 struct stat st; 199 fd = open(*argv, O_RDONLY|O_NONBLOCK, 0); 200 if (fd < 0) 201 goto skip; 202 203 if (fstat(fd, &st) == -1) { 204 close(fd); 205 goto skip; 206 } 207 if (!S_ISREG(st.st_mode)) { 208 close(fd); 209 errno = EINVAL; 210 goto skipnomsg; 211 } 212 } 213 else if ((fd = open(*argv, O_RDONLY, 0)) < 0) { 214skip: 215 perror(*argv); 216skipnomsg: 217 rval = 1; 218 ++argv; 219 continue; 220 } 221 filename = *argv++; 222 } 223 raw_cat(fd); 224 if (fd != fileno(stdin)) 225 close(fd); 226 } while (*argv); 227} 228 229int 230cat_main(int argc, char *argv[]) 231{ 232 int ch; 233 struct flock stdout_lock; 234 235 while ((ch = getopt(argc, argv, "beflnstv")) != -1) 236 switch (ch) { 237 case 'b': 238 bflag = nflag = 1; /* -b implies -n */ 239 break; 240 case 'e': 241 eflag = vflag = 1; /* -e implies -v */ 242 break; 243 case 'f': 244 fflag = 1; 245 break; 246 case 'l': 247 lflag = 1; 248 break; 249 case 'n': 250 nflag = 1; 251 break; 252 case 's': 253 sflag = 1; 254 break; 255 case 't': 256 tflag = vflag = 1; /* -t implies -v */ 257 break; 258 case 'v': 259 vflag = 1; 260 break; 261 default: 262 case '?': 263 fprintf(stderr, 264 "usage: cat [-beflnstv] [-] [file ...]\n"); 265 exit(EXIT_FAILURE); 266 } 267 argv += optind; 268 269 if (lflag) { 270 stdout_lock.l_len = 0; 271 stdout_lock.l_start = 0; 272 stdout_lock.l_type = F_WRLCK; 273 stdout_lock.l_whence = SEEK_SET; 274 if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) 275 { 276 perror("fcntl"); 277 exit(EXIT_FAILURE); 278 } 279 } 280 281 if (bflag || eflag || nflag || sflag || tflag || vflag) 282 cook_args(argv); 283 else 284 raw_args(argv); 285 if (fclose(stdout)) 286 { 287 perror("fclose"); 288 exit(EXIT_FAILURE); 289 } 290 exit(rval); 291} 292