1 /*- 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kevin Fall. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #if 0 34 #ifndef lint 35 static char const copyright[] = 36 "@(#) Copyright (c) 1989, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 #endif 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95"; 44 #endif 45 #endif /* not lint */ 46 #include <sys/cdefs.h> 47 __FBSDID("$FreeBSD$"); 48 49 #include <sys/param.h> 50 #include <sys/stat.h> 51 #ifndef NO_UDOM_SUPPORT 52 #include <sys/socket.h> 53 #include <sys/un.h> 54 #include <errno.h> 55 #include <netdb.h> 56 #endif 57 58 #include <ctype.h> 59 #include <err.h> 60 #include <fcntl.h> 61 #include <locale.h> 62 #include <stddef.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <unistd.h> 67 #include <wchar.h> 68 #include <wctype.h> 69 70 static int bflag, eflag, lflag, nflag, sflag, tflag, vflag; 71 static int rval; 72 static const char *filename; 73 74 static void usage(void) __dead2; 75 static void scanfiles(char *argv[], int cooked); 76 static void cook_cat(FILE *); 77 static void raw_cat(int); 78 79 #ifndef NO_UDOM_SUPPORT 80 static int udom_open(const char *path, int flags); 81 #endif 82 83 /* 84 * Memory strategy threshold, in pages: if physmem is larger than this, 85 * use a large buffer. 86 */ 87 #define PHYSPAGES_THRESHOLD (32 * 1024) 88 89 /* Maximum buffer size in bytes - do not allow it to grow larger than this. */ 90 #define BUFSIZE_MAX (2 * 1024 * 1024) 91 92 /* 93 * Small (default) buffer size in bytes. It's inefficient for this to be 94 * smaller than MAXPHYS. 95 */ 96 #define BUFSIZE_SMALL (MAXPHYS) 97 98 int 99 main(int argc, char *argv[]) 100 { 101 int ch; 102 struct flock stdout_lock; 103 104 setlocale(LC_CTYPE, ""); 105 106 while ((ch = getopt(argc, argv, "belnstuv")) != -1) 107 switch (ch) { 108 case 'b': 109 bflag = nflag = 1; /* -b implies -n */ 110 break; 111 case 'e': 112 eflag = vflag = 1; /* -e implies -v */ 113 break; 114 case 'l': 115 lflag = 1; 116 break; 117 case 'n': 118 nflag = 1; 119 break; 120 case 's': 121 sflag = 1; 122 break; 123 case 't': 124 tflag = vflag = 1; /* -t implies -v */ 125 break; 126 case 'u': 127 setbuf(stdout, NULL); 128 break; 129 case 'v': 130 vflag = 1; 131 break; 132 default: 133 usage(); 134 } 135 argv += optind; 136 137 if (lflag) { 138 stdout_lock.l_len = 0; 139 stdout_lock.l_start = 0; 140 stdout_lock.l_type = F_WRLCK; 141 stdout_lock.l_whence = SEEK_SET; 142 if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) 143 err(EXIT_FAILURE, "stdout"); 144 } 145 146 if (bflag || eflag || nflag || sflag || tflag || vflag) 147 scanfiles(argv, 1); 148 else 149 scanfiles(argv, 0); 150 if (fclose(stdout)) 151 err(1, "stdout"); 152 exit(rval); 153 /* NOTREACHED */ 154 } 155 156 static void 157 usage(void) 158 { 159 160 fprintf(stderr, "usage: cat [-belnstuv] [file ...]\n"); 161 exit(1); 162 /* NOTREACHED */ 163 } 164 165 static void 166 scanfiles(char *argv[], int cooked) 167 { 168 int fd, i; 169 char *path; 170 FILE *fp; 171 172 i = 0; 173 fd = -1; 174 while ((path = argv[i]) != NULL || i == 0) { 175 if (path == NULL || strcmp(path, "-") == 0) { 176 filename = "stdin"; 177 fd = STDIN_FILENO; 178 } else { 179 filename = path; 180 fd = open(path, O_RDONLY); 181 #ifndef NO_UDOM_SUPPORT 182 if (fd < 0 && errno == EOPNOTSUPP) 183 fd = udom_open(path, O_RDONLY); 184 #endif 185 } 186 if (fd < 0) { 187 warn("%s", path); 188 rval = 1; 189 } else if (cooked) { 190 if (fd == STDIN_FILENO) 191 cook_cat(stdin); 192 else { 193 fp = fdopen(fd, "r"); 194 cook_cat(fp); 195 fclose(fp); 196 } 197 } else { 198 raw_cat(fd); 199 if (fd != STDIN_FILENO) 200 close(fd); 201 } 202 if (path == NULL) 203 break; 204 ++i; 205 } 206 } 207 208 static void 209 cook_cat(FILE *fp) 210 { 211 int ch, gobble, line, prev; 212 wint_t wch; 213 214 /* Reset EOF condition on stdin. */ 215 if (fp == stdin && feof(stdin)) 216 clearerr(stdin); 217 218 line = gobble = 0; 219 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 220 if (prev == '\n') { 221 if (sflag) { 222 if (ch == '\n') { 223 if (gobble) 224 continue; 225 gobble = 1; 226 } else 227 gobble = 0; 228 } 229 if (nflag && (!bflag || ch != '\n')) { 230 (void)fprintf(stdout, "%6d\t", ++line); 231 if (ferror(stdout)) 232 break; 233 } 234 } 235 if (ch == '\n') { 236 if (eflag && putchar('$') == EOF) 237 break; 238 } else if (ch == '\t') { 239 if (tflag) { 240 if (putchar('^') == EOF || putchar('I') == EOF) 241 break; 242 continue; 243 } 244 } else if (vflag) { 245 (void)ungetc(ch, fp); 246 /* 247 * Our getwc(3) doesn't change file position 248 * on error. 249 */ 250 if ((wch = getwc(fp)) == WEOF) { 251 if (ferror(fp) && errno == EILSEQ) { 252 clearerr(fp); 253 /* Resync attempt. */ 254 memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 255 if ((ch = getc(fp)) == EOF) 256 break; 257 wch = ch; 258 goto ilseq; 259 } else 260 break; 261 } 262 if (!iswascii(wch) && !iswprint(wch)) { 263 ilseq: 264 if (putchar('M') == EOF || putchar('-') == EOF) 265 break; 266 wch = toascii(wch); 267 } 268 if (iswcntrl(wch)) { 269 ch = toascii(wch); 270 ch = (ch == '\177') ? '?' : (ch | 0100); 271 if (putchar('^') == EOF || putchar(ch) == EOF) 272 break; 273 continue; 274 } 275 if (putwchar(wch) == WEOF) 276 break; 277 ch = -1; 278 continue; 279 } 280 if (putchar(ch) == EOF) 281 break; 282 } 283 if (ferror(fp)) { 284 warn("%s", filename); 285 rval = 1; 286 clearerr(fp); 287 } 288 if (ferror(stdout)) 289 err(1, "stdout"); 290 } 291 292 static void 293 raw_cat(int rfd) 294 { 295 int off, wfd; 296 ssize_t nr, nw; 297 static size_t bsize; 298 static char *buf = NULL; 299 struct stat sbuf; 300 301 wfd = fileno(stdout); 302 if (buf == NULL) { 303 if (fstat(wfd, &sbuf)) 304 err(1, "stdout"); 305 if (S_ISREG(sbuf.st_mode)) { 306 /* If there's plenty of RAM, use a large copy buffer */ 307 if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) 308 bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 309 else 310 bsize = BUFSIZE_SMALL; 311 } else 312 bsize = MAX(sbuf.st_blksize, 313 (blksize_t)sysconf(_SC_PAGESIZE)); 314 if ((buf = malloc(bsize)) == NULL) 315 err(1, "malloc() failure of IO buffer"); 316 } 317 while ((nr = read(rfd, buf, bsize)) > 0) 318 for (off = 0; nr; nr -= nw, off += nw) 319 if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) 320 err(1, "stdout"); 321 if (nr < 0) { 322 warn("%s", filename); 323 rval = 1; 324 } 325 } 326 327 #ifndef NO_UDOM_SUPPORT 328 329 static int 330 udom_open(const char *path, int flags) 331 { 332 struct addrinfo hints, *res, *res0; 333 char rpath[PATH_MAX]; 334 int fd = -1; 335 int error; 336 337 /* 338 * Construct the unix domain socket address and attempt to connect. 339 */ 340 bzero(&hints, sizeof(hints)); 341 hints.ai_family = AF_LOCAL; 342 if (realpath(path, rpath) == NULL) 343 return (-1); 344 error = getaddrinfo(rpath, NULL, &hints, &res0); 345 if (error) { 346 warn("%s", gai_strerror(error)); 347 errno = EINVAL; 348 return (-1); 349 } 350 for (res = res0; res != NULL; res = res->ai_next) { 351 fd = socket(res->ai_family, res->ai_socktype, 352 res->ai_protocol); 353 if (fd < 0) { 354 freeaddrinfo(res0); 355 return (-1); 356 } 357 error = connect(fd, res->ai_addr, res->ai_addrlen); 358 if (error == 0) 359 break; 360 else { 361 close(fd); 362 fd = -1; 363 } 364 } 365 freeaddrinfo(res0); 366 367 /* 368 * handle the open flags by shutting down appropriate directions 369 */ 370 if (fd >= 0) { 371 switch(flags & O_ACCMODE) { 372 case O_RDONLY: 373 if (shutdown(fd, SHUT_WR) == -1) 374 warn(NULL); 375 break; 376 case O_WRONLY: 377 if (shutdown(fd, SHUT_RD) == -1) 378 warn(NULL); 379 break; 380 default: 381 break; 382 } 383 } 384 return (fd); 385 } 386 387 #endif 388