1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 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 0 36 #ifndef lint 37 static char const copyright[] = 38 "@(#) Copyright (c) 1989, 1993\n\ 39 The Regents of the University of California. All rights reserved.\n"; 40 #endif /* not lint */ 41 #endif 42 43 #ifndef lint 44 #if 0 45 static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95"; 46 #endif 47 #endif /* not lint */ 48 #include <sys/cdefs.h> 49 __FBSDID("$FreeBSD$"); 50 51 #include <sys/param.h> 52 #include <sys/stat.h> 53 #ifndef NO_UDOM_SUPPORT 54 #include <sys/socket.h> 55 #include <sys/un.h> 56 #include <netdb.h> 57 #endif 58 59 #include <ctype.h> 60 #include <err.h> 61 #include <errno.h> 62 #include <fcntl.h> 63 #include <locale.h> 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <unistd.h> 68 #include <wchar.h> 69 #include <wctype.h> 70 71 static int bflag, eflag, lflag, nflag, sflag, tflag, vflag; 72 static int rval; 73 static const char *filename; 74 75 static void usage(void) __dead2; 76 static void scanfiles(char *argv[], int cooked); 77 #ifndef BOOTSTRAP_CAT 78 static void cook_cat(FILE *); 79 #endif 80 static void raw_cat(int); 81 82 #ifndef NO_UDOM_SUPPORT 83 static int udom_open(const char *path, int flags); 84 #endif 85 86 /* 87 * Memory strategy threshold, in pages: if physmem is larger than this, 88 * use a large buffer. 89 */ 90 #define PHYSPAGES_THRESHOLD (32 * 1024) 91 92 /* Maximum buffer size in bytes - do not allow it to grow larger than this. */ 93 #define BUFSIZE_MAX (2 * 1024 * 1024) 94 95 /* 96 * Small (default) buffer size in bytes. It's inefficient for this to be 97 * smaller than MAXPHYS. 98 */ 99 #define BUFSIZE_SMALL (MAXPHYS) 100 101 102 /* 103 * For the bootstrapped cat binary (needed for locked appending to METALOG), we 104 * disable all flags except -l and -u to avoid non-portable function calls. 105 * In the future we may instead want to write a small portable bootstrap tool 106 * that locks the output file before writing to it. However, for now 107 * bootstrapping cat without multibyte support is the simpler solution. 108 */ 109 #ifdef BOOTSTRAP_CAT 110 #define SUPPORTED_FLAGS "lu" 111 #else 112 #define SUPPORTED_FLAGS "belnstuv" 113 #endif 114 115 int 116 main(int argc, char *argv[]) 117 { 118 int ch; 119 struct flock stdout_lock; 120 121 setlocale(LC_CTYPE, ""); 122 123 while ((ch = getopt(argc, argv, SUPPORTED_FLAGS)) != -1) 124 switch (ch) { 125 case 'b': 126 bflag = nflag = 1; /* -b implies -n */ 127 break; 128 case 'e': 129 eflag = vflag = 1; /* -e implies -v */ 130 break; 131 case 'l': 132 lflag = 1; 133 break; 134 case 'n': 135 nflag = 1; 136 break; 137 case 's': 138 sflag = 1; 139 break; 140 case 't': 141 tflag = vflag = 1; /* -t implies -v */ 142 break; 143 case 'u': 144 setbuf(stdout, NULL); 145 break; 146 case 'v': 147 vflag = 1; 148 break; 149 default: 150 usage(); 151 } 152 argv += optind; 153 154 if (lflag) { 155 stdout_lock.l_len = 0; 156 stdout_lock.l_start = 0; 157 stdout_lock.l_type = F_WRLCK; 158 stdout_lock.l_whence = SEEK_SET; 159 if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) 160 err(EXIT_FAILURE, "stdout"); 161 } 162 163 if (bflag || eflag || nflag || sflag || tflag || vflag) 164 scanfiles(argv, 1); 165 else 166 scanfiles(argv, 0); 167 if (fclose(stdout)) 168 err(1, "stdout"); 169 exit(rval); 170 /* NOTREACHED */ 171 } 172 173 static void 174 usage(void) 175 { 176 177 fprintf(stderr, "usage: cat [-" SUPPORTED_FLAGS "] [file ...]\n"); 178 exit(1); 179 /* NOTREACHED */ 180 } 181 182 static void 183 scanfiles(char *argv[], int cooked __unused) 184 { 185 int fd, i; 186 char *path; 187 #ifndef BOOTSTRAP_CAT 188 FILE *fp; 189 #endif 190 191 i = 0; 192 fd = -1; 193 while ((path = argv[i]) != NULL || i == 0) { 194 if (path == NULL || strcmp(path, "-") == 0) { 195 filename = "stdin"; 196 fd = STDIN_FILENO; 197 } else { 198 filename = path; 199 fd = open(path, O_RDONLY); 200 #ifndef NO_UDOM_SUPPORT 201 if (fd < 0 && errno == EOPNOTSUPP) 202 fd = udom_open(path, O_RDONLY); 203 #endif 204 } 205 if (fd < 0) { 206 warn("%s", path); 207 rval = 1; 208 #ifndef BOOTSTRAP_CAT 209 } else if (cooked) { 210 if (fd == STDIN_FILENO) 211 cook_cat(stdin); 212 else { 213 fp = fdopen(fd, "r"); 214 cook_cat(fp); 215 fclose(fp); 216 } 217 #endif 218 } else { 219 raw_cat(fd); 220 if (fd != STDIN_FILENO) 221 close(fd); 222 } 223 if (path == NULL) 224 break; 225 ++i; 226 } 227 } 228 229 #ifndef BOOTSTRAP_CAT 230 static void 231 cook_cat(FILE *fp) 232 { 233 int ch, gobble, line, prev; 234 wint_t wch; 235 236 /* Reset EOF condition on stdin. */ 237 if (fp == stdin && feof(stdin)) 238 clearerr(stdin); 239 240 line = gobble = 0; 241 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 242 if (prev == '\n') { 243 if (sflag) { 244 if (ch == '\n') { 245 if (gobble) 246 continue; 247 gobble = 1; 248 } else 249 gobble = 0; 250 } 251 if (nflag) { 252 if (!bflag || ch != '\n') { 253 (void)fprintf(stdout, "%6d\t", ++line); 254 if (ferror(stdout)) 255 break; 256 } else if (eflag) { 257 (void)fprintf(stdout, "%6s\t", ""); 258 if (ferror(stdout)) 259 break; 260 } 261 } 262 } 263 if (ch == '\n') { 264 if (eflag && putchar('$') == EOF) 265 break; 266 } else if (ch == '\t') { 267 if (tflag) { 268 if (putchar('^') == EOF || putchar('I') == EOF) 269 break; 270 continue; 271 } 272 } else if (vflag) { 273 (void)ungetc(ch, fp); 274 /* 275 * Our getwc(3) doesn't change file position 276 * on error. 277 */ 278 if ((wch = getwc(fp)) == WEOF) { 279 if (ferror(fp) && errno == EILSEQ) { 280 clearerr(fp); 281 /* Resync attempt. */ 282 memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 283 if ((ch = getc(fp)) == EOF) 284 break; 285 wch = ch; 286 goto ilseq; 287 } else 288 break; 289 } 290 if (!iswascii(wch) && !iswprint(wch)) { 291 ilseq: 292 if (putchar('M') == EOF || putchar('-') == EOF) 293 break; 294 wch = toascii(wch); 295 } 296 if (iswcntrl(wch)) { 297 ch = toascii(wch); 298 ch = (ch == '\177') ? '?' : (ch | 0100); 299 if (putchar('^') == EOF || putchar(ch) == EOF) 300 break; 301 continue; 302 } 303 if (putwchar(wch) == WEOF) 304 break; 305 ch = -1; 306 continue; 307 } 308 if (putchar(ch) == EOF) 309 break; 310 } 311 if (ferror(fp)) { 312 warn("%s", filename); 313 rval = 1; 314 clearerr(fp); 315 } 316 if (ferror(stdout)) 317 err(1, "stdout"); 318 } 319 #endif /* BOOTSTRAP_CAT */ 320 321 static void 322 raw_cat(int rfd) 323 { 324 long pagesize; 325 int off, wfd; 326 ssize_t nr, nw; 327 static size_t bsize; 328 static char *buf = NULL; 329 struct stat sbuf; 330 331 wfd = fileno(stdout); 332 if (buf == NULL) { 333 if (fstat(wfd, &sbuf)) 334 err(1, "stdout"); 335 if (S_ISREG(sbuf.st_mode)) { 336 /* If there's plenty of RAM, use a large copy buffer */ 337 if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) 338 bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 339 else 340 bsize = BUFSIZE_SMALL; 341 } else { 342 bsize = sbuf.st_blksize; 343 pagesize = sysconf(_SC_PAGESIZE); 344 if (pagesize > 0) 345 bsize = MAX(bsize, (size_t)pagesize); 346 } 347 if ((buf = malloc(bsize)) == NULL) 348 err(1, "malloc() failure of IO buffer"); 349 } 350 while ((nr = read(rfd, buf, bsize)) > 0) 351 for (off = 0; nr; nr -= nw, off += nw) 352 if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) 353 err(1, "stdout"); 354 if (nr < 0) { 355 warn("%s", filename); 356 rval = 1; 357 } 358 } 359 360 #ifndef NO_UDOM_SUPPORT 361 362 static int 363 udom_open(const char *path, int flags) 364 { 365 struct addrinfo hints, *res, *res0; 366 char rpath[PATH_MAX]; 367 int fd = -1; 368 int error; 369 370 /* 371 * Construct the unix domain socket address and attempt to connect. 372 */ 373 bzero(&hints, sizeof(hints)); 374 hints.ai_family = AF_LOCAL; 375 if (realpath(path, rpath) == NULL) 376 return (-1); 377 error = getaddrinfo(rpath, NULL, &hints, &res0); 378 if (error) { 379 warn("%s", gai_strerror(error)); 380 errno = EINVAL; 381 return (-1); 382 } 383 for (res = res0; res != NULL; res = res->ai_next) { 384 fd = socket(res->ai_family, res->ai_socktype, 385 res->ai_protocol); 386 if (fd < 0) { 387 freeaddrinfo(res0); 388 return (-1); 389 } 390 error = connect(fd, res->ai_addr, res->ai_addrlen); 391 if (error == 0) 392 break; 393 else { 394 close(fd); 395 fd = -1; 396 } 397 } 398 freeaddrinfo(res0); 399 400 /* 401 * handle the open flags by shutting down appropriate directions 402 */ 403 if (fd >= 0) { 404 switch(flags & O_ACCMODE) { 405 case O_RDONLY: 406 if (shutdown(fd, SHUT_WR) == -1) 407 warn(NULL); 408 break; 409 case O_WRONLY: 410 if (shutdown(fd, SHUT_RD) == -1) 411 warn(NULL); 412 break; 413 default: 414 break; 415 } 416 } 417 return (fd); 418 } 419 420 #endif 421