cat.c revision 306200
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/10/bin/cat/cat.c 306200 2016-09-22 16:46:59Z ache $"); 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#endif 56 57#include <ctype.h> 58#include <err.h> 59#include <fcntl.h> 60#include <locale.h> 61#include <stddef.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 while ((path = argv[i]) != NULL || i == 0) { 173 if (path == NULL || strcmp(path, "-") == 0) { 174 filename = "stdin"; 175 fd = STDIN_FILENO; 176 } else { 177 filename = path; 178 fd = open(path, O_RDONLY); 179#ifndef NO_UDOM_SUPPORT 180 if (fd < 0 && errno == EOPNOTSUPP) 181 fd = udom_open(path, O_RDONLY); 182#endif 183 } 184 if (fd < 0) { 185 warn("%s", path); 186 rval = 1; 187 } else if (cooked) { 188 if (fd == STDIN_FILENO) 189 cook_cat(stdin); 190 else { 191 fp = fdopen(fd, "r"); 192 cook_cat(fp); 193 fclose(fp); 194 } 195 } else { 196 raw_cat(fd); 197 if (fd != STDIN_FILENO) 198 close(fd); 199 } 200 if (path == NULL) 201 break; 202 ++i; 203 } 204} 205 206static void 207cook_cat(FILE *fp) 208{ 209 int ch, gobble, line, prev; 210 wint_t wch; 211 212 /* Reset EOF condition on stdin. */ 213 if (fp == stdin && feof(stdin)) 214 clearerr(stdin); 215 216 line = gobble = 0; 217 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 218 if (prev == '\n') { 219 if (sflag) { 220 if (ch == '\n') { 221 if (gobble) 222 continue; 223 gobble = 1; 224 } else 225 gobble = 0; 226 } 227 if (nflag && (!bflag || ch != '\n')) { 228 (void)fprintf(stdout, "%6d\t", ++line); 229 if (ferror(stdout)) 230 break; 231 } 232 } 233 if (ch == '\n') { 234 if (eflag && putchar('$') == EOF) 235 break; 236 } else if (ch == '\t') { 237 if (tflag) { 238 if (putchar('^') == EOF || putchar('I') == EOF) 239 break; 240 continue; 241 } 242 } else if (vflag) { 243 (void)ungetc(ch, fp); 244 /* 245 * Our getwc(3) doesn't change file position 246 * on error. 247 */ 248 if ((wch = getwc(fp)) == WEOF) { 249 if (ferror(fp) && errno == EILSEQ) { 250 clearerr(fp); 251 /* Resync attempt. */ 252 memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 253 if ((ch = getc(fp)) == EOF) 254 break; 255 wch = ch; 256 goto ilseq; 257 } else 258 break; 259 } 260 if (!iswascii(wch) && !iswprint(wch)) { 261ilseq: 262 if (putchar('M') == EOF || putchar('-') == EOF) 263 break; 264 wch = toascii(wch); 265 } 266 if (iswcntrl(wch)) { 267 ch = toascii(wch); 268 ch = (ch == '\177') ? '?' : (ch | 0100); 269 if (putchar('^') == EOF || putchar(ch) == EOF) 270 break; 271 continue; 272 } 273 if (putwchar(wch) == WEOF) 274 break; 275 ch = -1; 276 continue; 277 } 278 if (putchar(ch) == EOF) 279 break; 280 } 281 if (ferror(fp)) { 282 warn("%s", filename); 283 rval = 1; 284 clearerr(fp); 285 } 286 if (ferror(stdout)) 287 err(1, "stdout"); 288} 289 290static void 291raw_cat(int rfd) 292{ 293 int off, wfd; 294 ssize_t nr, nw; 295 static size_t bsize; 296 static char *buf = NULL; 297 struct stat sbuf; 298 299 wfd = fileno(stdout); 300 if (buf == NULL) { 301 if (fstat(wfd, &sbuf)) 302 err(1, "stdout"); 303 if (S_ISREG(sbuf.st_mode)) { 304 /* If there's plenty of RAM, use a large copy buffer */ 305 if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) 306 bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 307 else 308 bsize = BUFSIZE_SMALL; 309 } else 310 bsize = MAX(sbuf.st_blksize, 311 (blksize_t)sysconf(_SC_PAGESIZE)); 312 if ((buf = malloc(bsize)) == NULL) 313 err(1, "malloc() failure of IO buffer"); 314 } 315 while ((nr = read(rfd, buf, bsize)) > 0) 316 for (off = 0; nr; nr -= nw, off += nw) 317 if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) 318 err(1, "stdout"); 319 if (nr < 0) { 320 warn("%s", filename); 321 rval = 1; 322 } 323} 324 325#ifndef NO_UDOM_SUPPORT 326 327static int 328udom_open(const char *path, int flags) 329{ 330 struct sockaddr_un sou; 331 int fd; 332 unsigned int len; 333 334 bzero(&sou, sizeof(sou)); 335 336 /* 337 * Construct the unix domain socket address and attempt to connect 338 */ 339 fd = socket(AF_UNIX, SOCK_STREAM, 0); 340 if (fd >= 0) { 341 sou.sun_family = AF_UNIX; 342 if ((len = strlcpy(sou.sun_path, path, 343 sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) { 344 errno = ENAMETOOLONG; 345 return (-1); 346 } 347 len = offsetof(struct sockaddr_un, sun_path[len+1]); 348 349 if (connect(fd, (void *)&sou, len) < 0) { 350 close(fd); 351 fd = -1; 352 } 353 } 354 355 /* 356 * handle the open flags by shutting down appropriate directions 357 */ 358 if (fd >= 0) { 359 switch(flags & O_ACCMODE) { 360 case O_RDONLY: 361 if (shutdown(fd, SHUT_WR) == -1) 362 warn(NULL); 363 break; 364 case O_WRONLY: 365 if (shutdown(fd, SHUT_RD) == -1) 366 warn(NULL); 367 break; 368 default: 369 break; 370 } 371 } 372 return (fd); 373} 374 375#endif 376