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 <netdb.h> 55 #endif 56 57 #include <ctype.h> 58 #include <err.h> 59 #include <errno.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) { 230 if (!bflag || ch != '\n') { 231 (void)fprintf(stdout, "%6d\t", ++line); 232 if (ferror(stdout)) 233 break; 234 } else if (eflag) { 235 (void)fprintf(stdout, "%6s\t", ""); 236 if (ferror(stdout)) 237 break; 238 } 239 } 240 } 241 if (ch == '\n') { 242 if (eflag && putchar('$') == EOF) 243 break; 244 } else if (ch == '\t') { 245 if (tflag) { 246 if (putchar('^') == EOF || putchar('I') == EOF) 247 break; 248 continue; 249 } 250 } else if (vflag) { 251 (void)ungetc(ch, fp); 252 /* 253 * Our getwc(3) doesn't change file position 254 * on error. 255 */ 256 if ((wch = getwc(fp)) == WEOF) { 257 if (ferror(fp) && errno == EILSEQ) { 258 clearerr(fp); 259 /* Resync attempt. */ 260 memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 261 if ((ch = getc(fp)) == EOF) 262 break; 263 wch = ch; 264 goto ilseq; 265 } else 266 break; 267 } 268 if (!iswascii(wch) && !iswprint(wch)) { 269 ilseq: 270 if (putchar('M') == EOF || putchar('-') == EOF) 271 break; 272 wch = toascii(wch); 273 } 274 if (iswcntrl(wch)) { 275 ch = toascii(wch); 276 ch = (ch == '\177') ? '?' : (ch | 0100); 277 if (putchar('^') == EOF || putchar(ch) == EOF) 278 break; 279 continue; 280 } 281 if (putwchar(wch) == WEOF) 282 break; 283 ch = -1; 284 continue; 285 } 286 if (putchar(ch) == EOF) 287 break; 288 } 289 if (ferror(fp)) { 290 warn("%s", filename); 291 rval = 1; 292 clearerr(fp); 293 } 294 if (ferror(stdout)) 295 err(1, "stdout"); 296 } 297 298 static void 299 raw_cat(int rfd) 300 { 301 int off, wfd; 302 ssize_t nr, nw; 303 static size_t bsize; 304 static char *buf = NULL; 305 struct stat sbuf; 306 307 wfd = fileno(stdout); 308 if (buf == NULL) { 309 if (fstat(wfd, &sbuf)) 310 err(1, "stdout"); 311 if (S_ISREG(sbuf.st_mode)) { 312 /* If there's plenty of RAM, use a large copy buffer */ 313 if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) 314 bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 315 else 316 bsize = BUFSIZE_SMALL; 317 } else 318 bsize = MAX(sbuf.st_blksize, 319 (blksize_t)sysconf(_SC_PAGESIZE)); 320 if ((buf = malloc(bsize)) == NULL) 321 err(1, "malloc() failure of IO buffer"); 322 } 323 while ((nr = read(rfd, buf, bsize)) > 0) 324 for (off = 0; nr; nr -= nw, off += nw) 325 if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) 326 err(1, "stdout"); 327 if (nr < 0) { 328 warn("%s", filename); 329 rval = 1; 330 } 331 } 332 333 #ifndef NO_UDOM_SUPPORT 334 335 static int 336 udom_open(const char *path, int flags) 337 { 338 struct addrinfo hints, *res, *res0; 339 char rpath[PATH_MAX]; 340 int fd = -1; 341 int error; 342 343 /* 344 * Construct the unix domain socket address and attempt to connect. 345 */ 346 bzero(&hints, sizeof(hints)); 347 hints.ai_family = AF_LOCAL; 348 if (realpath(path, rpath) == NULL) 349 return (-1); 350 error = getaddrinfo(rpath, NULL, &hints, &res0); 351 if (error) { 352 warn("%s", gai_strerror(error)); 353 errno = EINVAL; 354 return (-1); 355 } 356 for (res = res0; res != NULL; res = res->ai_next) { 357 fd = socket(res->ai_family, res->ai_socktype, 358 res->ai_protocol); 359 if (fd < 0) { 360 freeaddrinfo(res0); 361 return (-1); 362 } 363 error = connect(fd, res->ai_addr, res->ai_addrlen); 364 if (error == 0) 365 break; 366 else { 367 close(fd); 368 fd = -1; 369 } 370 } 371 freeaddrinfo(res0); 372 373 /* 374 * handle the open flags by shutting down appropriate directions 375 */ 376 if (fd >= 0) { 377 switch(flags & O_ACCMODE) { 378 case O_RDONLY: 379 if (shutdown(fd, SHUT_WR) == -1) 380 warn(NULL); 381 break; 382 case O_WRONLY: 383 if (shutdown(fd, SHUT_RD) == -1) 384 warn(NULL); 385 break; 386 default: 387 break; 388 } 389 } 390 return (fd); 391 } 392 393 #endif 394