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