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 * @(#) Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved. 33 * @(#)cat.c 8.2 (Berkeley) 4/27/95 34 * $FreeBSD: src/bin/cat/cat.c,v 1.14.2.8 2002/06/29 05:09:26 tjr Exp $ 35 * $DragonFly: src/bin/cat/cat.c,v 1.16 2007/02/04 21:06:34 pavalos Exp $ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/stat.h> 40 #ifndef NO_UDOM_SUPPORT 41 #include <sys/socket.h> 42 #include <sys/un.h> 43 #include <errno.h> 44 #endif 45 46 #include <ctype.h> 47 #include <err.h> 48 #include <fcntl.h> 49 #include <locale.h> 50 #include <stddef.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 static int bflag, eflag, nflag, sflag, tflag, vflag, rval; 57 static const char *filename; 58 59 static void scanfiles(char **, int); 60 static void cook_cat(FILE *); 61 static void raw_cat(int); 62 63 #ifndef NO_UDOM_SUPPORT 64 static int udom_open(const char *, int); 65 #endif 66 67 static void 68 usage(void) 69 { 70 fprintf(stderr, 71 "usage: cat [-benstuv] [file ...]\n"); 72 exit(EXIT_FAILURE); 73 } 74 75 int 76 main(int argc, char **argv) 77 { 78 int ch; 79 80 setlocale(LC_CTYPE, ""); 81 82 while ((ch = getopt(argc, argv, "benstuv")) != -1) 83 switch (ch) { 84 case 'b': 85 bflag = nflag = 1; /* -b implies -n */ 86 break; 87 case 'e': 88 eflag = vflag = 1; /* -e implies -v */ 89 break; 90 case 'n': 91 nflag = 1; 92 break; 93 case 's': 94 sflag = 1; 95 break; 96 case 't': 97 tflag = vflag = 1; /* -t implies -v */ 98 break; 99 case 'u': 100 setbuf(stdout, NULL); 101 break; 102 case 'v': 103 vflag = 1; 104 break; 105 default: 106 usage(); 107 /* NOTREACHED */ 108 } 109 argv += optind; 110 111 if (bflag || eflag || nflag || sflag || tflag || vflag) 112 scanfiles(argv, 1); 113 else 114 scanfiles(argv, 0); 115 if (fclose(stdout)) 116 err(1, "stdout"); 117 exit(rval); 118 /* NOTREACHED */ 119 } 120 121 static void 122 scanfiles(char **argv, int cooked) 123 { 124 char *path; 125 FILE *fp; 126 int fd; 127 int i; 128 129 i = 0; 130 131 while ((path = argv[i]) != NULL || i == 0) { 132 if (path == NULL || strcmp(path, "-") == 0) { 133 filename = "stdin"; 134 fd = STDIN_FILENO; 135 } else { 136 filename = path; 137 fd = open(path, O_RDONLY); 138 #ifndef NO_UDOM_SUPPORT 139 if (fd < 0 && errno == EOPNOTSUPP) 140 fd = udom_open(path, O_RDONLY); 141 #endif 142 } 143 if (fd < 0) { 144 warn("%s", path); 145 rval = 1; 146 } else if (cooked) { 147 if (fd == STDIN_FILENO) { 148 cook_cat(stdin); 149 } else { 150 fp = fdopen(fd, "r"); 151 if (fp == NULL) 152 err(1, "%s", path); 153 cook_cat(fp); 154 fclose(fp); 155 } 156 } else { 157 raw_cat(fd); 158 if (fd != STDIN_FILENO) 159 close(fd); 160 } 161 if (path == NULL) 162 break; 163 ++i; 164 } 165 } 166 167 static void 168 cook_cat(FILE *fp) 169 { 170 int ch, gobble, line, prev; 171 172 /* Reset EOF condition on stdin. */ 173 if (fp == stdin && feof(stdin)) 174 clearerr(stdin); 175 176 line = gobble = 0; 177 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 178 if (prev == '\n') { 179 if (sflag) { 180 if (ch == '\n') { 181 if (gobble) 182 continue; 183 gobble = 1; 184 } else 185 gobble = 0; 186 } 187 if (nflag && (!bflag || ch != '\n')) { 188 fprintf(stdout, "%6d\t", ++line); 189 if (ferror(stdout)) 190 break; 191 } 192 } 193 if (ch == '\n') { 194 if (eflag && putchar('$') == EOF) 195 break; 196 } else if (ch == '\t') { 197 if (tflag) { 198 if (putchar('^') == EOF || putchar('I') == EOF) 199 break; 200 continue; 201 } 202 } else if (vflag) { 203 if (!isascii(ch) && !isprint(ch)) { 204 if (putchar('M') == EOF || putchar('-') == EOF) 205 break; 206 ch = toascii(ch); 207 } 208 if (iscntrl(ch)) { 209 if (putchar('^') == EOF || 210 putchar(ch == '\177' ? '?' : 211 ch | 0100) == EOF) 212 break; 213 continue; 214 } 215 } 216 if (putchar(ch) == EOF) 217 break; 218 } 219 if (ferror(fp)) { 220 warn("%s", filename); 221 rval = 1; 222 clearerr(fp); 223 } 224 if (ferror(stdout)) 225 err(1, "stdout"); 226 } 227 228 static void 229 raw_cat(int rfd) 230 { 231 int off; 232 size_t nbsize; 233 ssize_t nr, nw; 234 struct stat rst; 235 static struct stat ost; 236 static size_t bsize; 237 static char *buf; 238 239 /* 240 * Figure out the block size to use. Use the larger of stdout vs 241 * the passed descriptor. The minimum blocksize we use is BUFSIZ. 242 */ 243 if (ost.st_blksize == 0) { 244 if (fstat(STDOUT_FILENO, &ost)) 245 err(1, "<stdout>"); 246 if (ost.st_blksize < BUFSIZ) 247 ost.st_blksize = BUFSIZ; 248 } 249 /* 250 * note: rst.st_blksize can be 0, but it is handled ok. 251 */ 252 if (fstat(rfd, &rst)) 253 err(1, "%s", filename); 254 255 nbsize = MAX(ost.st_blksize, rst.st_blksize); 256 if (bsize != nbsize) { 257 bsize = nbsize; 258 if ((buf = realloc(buf, bsize)) == NULL) 259 err(1, "malloc failed"); 260 } 261 while ((nr = read(rfd, buf, bsize)) > 0) { 262 for (off = 0; nr; nr -= nw, off += nw) { 263 nw = write(STDOUT_FILENO, buf + off, nr); 264 if (nw < 0) 265 err(1, "stdout"); 266 } 267 } 268 if (nr < 0) { 269 warn("%s", filename); 270 rval = 1; 271 } 272 } 273 274 #ifndef NO_UDOM_SUPPORT 275 276 static int 277 udom_open(const char *path, int flags) 278 { 279 struct sockaddr_un sou; 280 int fd; 281 unsigned int len; 282 283 bzero(&sou, sizeof(sou)); 284 285 /* 286 * Construct the unix domain socket address and attempt to connect 287 */ 288 fd = socket(AF_UNIX, SOCK_STREAM, 0); 289 if (fd >= 0) { 290 sou.sun_family = AF_UNIX; 291 if ((len = strlcpy(sou.sun_path, path, 292 sizeof(sou.sun_path))) >= sizeof(sou.sun_path)) { 293 errno = ENAMETOOLONG; 294 return (-1); 295 } 296 len = offsetof(struct sockaddr_un, sun_path[len+1]); 297 298 if (connect(fd, (void *)&sou, len) < 0) { 299 close(fd); 300 fd = -1; 301 } 302 } 303 304 /* 305 * handle the open flags by shutting down appropriate directions 306 */ 307 if (fd >= 0) { 308 switch (flags & O_ACCMODE) { 309 case O_RDONLY: 310 if (shutdown(fd, SHUT_WR) == -1) 311 warn(NULL); 312 break; 313 case O_WRONLY: 314 if (shutdown(fd, SHUT_RD) == -1) 315 warn(NULL); 316 break; 317 default: 318 break; 319 } 320 } 321 return(fd); 322 } 323 324 #endif 325