1/* $OpenBSD: cat.c,v 1.34 2022/02/09 01:58:57 cheloha Exp $ */ 2/* $NetBSD: cat.c,v 1.11 1995/09/07 06:12:54 jtc Exp $ */ 3 4/* 5 * Copyright (c) 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Kevin Fall. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <sys/types.h> 37#include <sys/stat.h> 38 39#include <ctype.h> 40#include <err.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <unistd.h> 47 48#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 49 50int bflag, eflag, nflag, sflag, tflag, vflag; 51int rval; 52 53void cat_file(const char *); 54void cook_buf(FILE *, const char *); 55void raw_cat(int, const char *); 56 57int 58main(int argc, char *argv[]) 59{ 60 int ch; 61 62 if (pledge("stdio rpath", NULL) == -1) 63 err(1, "pledge"); 64 65 while ((ch = getopt(argc, argv, "benstuv")) != -1) { 66 switch (ch) { 67 case 'b': 68 bflag = nflag = 1; /* -b implies -n */ 69 break; 70 case 'e': 71 eflag = vflag = 1; /* -e implies -v */ 72 break; 73 case 'n': 74 nflag = 1; 75 break; 76 case 's': 77 sflag = 1; 78 break; 79 case 't': 80 tflag = vflag = 1; /* -t implies -v */ 81 break; 82 case 'u': 83 setvbuf(stdout, NULL, _IONBF, 0); 84 break; 85 case 'v': 86 vflag = 1; 87 break; 88 default: 89 fprintf(stderr, "usage: %s [-benstuv] [file ...]\n", 90 getprogname()); 91 return 1; 92 } 93 } 94 argc -= optind; 95 argv += optind; 96 97 if (argc == 0) { 98 if (pledge("stdio", NULL) == -1) 99 err(1, "pledge"); 100 101 cat_file(NULL); 102 } else { 103 for (; *argv != NULL; argv++) 104 cat_file(*argv); 105 } 106 if (fclose(stdout)) 107 err(1, "stdout"); 108 return rval; 109} 110 111void 112cat_file(const char *path) 113{ 114 FILE *fp; 115 int fd; 116 117 if (bflag || eflag || nflag || sflag || tflag || vflag) { 118 if (path == NULL || strcmp(path, "-") == 0) { 119 cook_buf(stdin, "stdin"); 120 clearerr(stdin); 121 } else { 122 if ((fp = fopen(path, "r")) == NULL) { 123 warn("%s", path); 124 rval = 1; 125 return; 126 } 127 cook_buf(fp, path); 128 fclose(fp); 129 } 130 } else { 131 if (path == NULL || strcmp(path, "-") == 0) { 132 raw_cat(STDIN_FILENO, "stdin"); 133 } else { 134 if ((fd = open(path, O_RDONLY)) == -1) { 135 warn("%s", path); 136 rval = 1; 137 return; 138 } 139 raw_cat(fd, path); 140 close(fd); 141 } 142 } 143} 144 145void 146cook_buf(FILE *fp, const char *filename) 147{ 148 unsigned long long line; 149 int ch, gobble, prev; 150 151 line = gobble = 0; 152 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 153 if (prev == '\n') { 154 if (sflag) { 155 if (ch == '\n') { 156 if (gobble) 157 continue; 158 gobble = 1; 159 } else 160 gobble = 0; 161 } 162 if (nflag) { 163 if (!bflag || ch != '\n') { 164 fprintf(stdout, "%6llu\t", ++line); 165 if (ferror(stdout)) 166 break; 167 } else if (eflag) { 168 (void)fprintf(stdout, "%6s\t", ""); 169 if (ferror(stdout)) 170 break; 171 } 172 } 173 } 174 if (ch == '\n') { 175 if (eflag && putchar('$') == EOF) 176 break; 177 } else if (ch == '\t') { 178 if (tflag) { 179 if (putchar('^') == EOF || putchar('I') == EOF) 180 break; 181 continue; 182 } 183 } else if (vflag) { 184 if (!isascii(ch)) { 185 if (putchar('M') == EOF || putchar('-') == EOF) 186 break; 187 ch = toascii(ch); 188 } 189 if (iscntrl(ch)) { 190 if (putchar('^') == EOF || 191 putchar(ch == '\177' ? '?' : 192 ch | 0100) == EOF) 193 break; 194 continue; 195 } 196 } 197 if (putchar(ch) == EOF) 198 break; 199 } 200 if (ferror(fp)) { 201 warn("%s", filename); 202 rval = 1; 203 clearerr(fp); 204 } 205 if (ferror(stdout)) 206 err(1, "stdout"); 207} 208 209void 210raw_cat(int rfd, const char *filename) 211{ 212 int wfd; 213 ssize_t nr, nw, off; 214 static size_t bsize; 215 static char *buf = NULL; 216 struct stat sbuf; 217 218 wfd = fileno(stdout); 219 if (buf == NULL) { 220 if (fstat(wfd, &sbuf) == -1) 221 err(1, "stdout"); 222 bsize = MAXIMUM(sbuf.st_blksize, BUFSIZ); 223 if ((buf = malloc(bsize)) == NULL) 224 err(1, NULL); 225 } 226 while ((nr = read(rfd, buf, bsize)) != -1 && nr != 0) { 227 for (off = 0; nr; nr -= nw, off += nw) { 228 if ((nw = write(wfd, buf + off, nr)) == -1 || nw == 0) 229 err(1, "stdout"); 230 } 231 } 232 if (nr == -1) { 233 warn("%s", filename); 234 rval = 1; 235 } 236} 237