1 /*- 2 * Copyright (c) 1991, 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 * Edward Sze-Tyan Wang. 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 * 3. 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) 1991, 1993 The Regents of the University of California. All rights reserved. 33 * @(#)tail.c 8.1 (Berkeley) 6/6/93 34 * $FreeBSD: src/usr.bin/tail/tail.c,v 1.6.2.2 2001/12/19 20:29:31 iedowse Exp $ 35 */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <errno.h> 40 #include <unistd.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <err.h> 45 #include "extern.h" 46 47 int Fflag, fflag, qflag, rflag, rval, no_files; 48 const char *fname; 49 50 #ifndef BOOTSTRAPPING 51 file_info_t *files; 52 #endif 53 54 static void obsolete(char **); 55 static void usage(void); 56 static void getarg(int, enum STYLE, enum STYLE, enum STYLE *, int *, off_t *); 57 58 int 59 main(int argc, char **argv) 60 { 61 struct stat sb; 62 FILE *fp; 63 off_t off = 0; 64 enum STYLE style; 65 int style_set; 66 int i, ch; 67 #ifndef BOOTSTRAPPING 68 file_info_t *file; 69 #endif 70 71 obsolete(argv); 72 style_set = 0; 73 while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1) 74 switch(ch) { 75 case 'F': /* -F is superset of (and implies) -f */ 76 #ifndef BOOTSTRAPPING 77 Fflag = fflag = 1; 78 #endif 79 break; 80 case 'b': 81 getarg(512, FBYTES, RBYTES, &style, &style_set, &off); 82 break; 83 case 'c': 84 getarg(1, FBYTES, RBYTES, &style, &style_set, &off); 85 break; 86 case 'f': 87 #ifndef BOOTSTRAPPING 88 fflag = 1; 89 #endif 90 break; 91 case 'n': 92 getarg(1, FLINES, RLINES, &style, &style_set, &off); 93 break; 94 case 'q': 95 qflag = 1; 96 break; 97 case 'r': 98 rflag = 1; 99 break; 100 case '?': 101 default: 102 usage(); 103 } 104 argc -= optind; 105 argv += optind; 106 107 no_files = argc ? argc : 1; 108 109 /* 110 * If displaying in reverse, don't permit follow option, and convert 111 * style values. 112 */ 113 if (rflag) { 114 if (fflag) 115 usage(); 116 if (style == FBYTES) 117 style = RBYTES; 118 else if (style == FLINES) 119 style = RLINES; 120 } 121 122 /* 123 * If style not specified, the default is the whole file for -r, and 124 * the last 10 lines if not -r. 125 */ 126 if (!style_set) { 127 if (rflag) { 128 off = 0; 129 style = REVERSE; 130 } else { 131 off = 10; 132 style = RLINES; 133 } 134 } 135 136 #ifndef BOOTSTRAPPING 137 if (*argv && fflag) { 138 files = malloc(no_files * sizeof(struct file_info)); 139 if (files == NULL) 140 err(1, "Couldn't malloc space for files descriptors."); 141 142 for (file = files; (fname = *argv++) != NULL; file++) { 143 file->file_name = strdup(fname); 144 if (! file->file_name) 145 errx(1, "Couldn't alloc space for file name."); 146 file->fp = fopen(file->file_name, "r"); 147 if (file->fp == NULL || 148 fstat(fileno(file->fp), &file->st) == -1) { 149 file->fp = NULL; 150 ierr(); 151 } 152 } 153 follow(files, style, off); 154 for (i = 0, file = files; i < no_files; i++, file++) 155 free(file->file_name); 156 free(files); 157 } else 158 #endif 159 if (*argv) { 160 for (i = 0; (fname = *argv++) != NULL; ++i) { 161 if ((fp = fopen(fname, "r")) == NULL || 162 fstat(fileno(fp), &sb)) { 163 ierr(); 164 continue; 165 } 166 if (argc > 1) { 167 showfilename(i, fname); 168 (void)fflush(stdout); 169 } 170 171 if (rflag) 172 reverse(fp, style, off, &sb); 173 else 174 forward(fp, style, off, &sb); 175 (void)fclose(fp); 176 } 177 } else { 178 fname = "stdin"; 179 180 if (fstat(fileno(stdin), &sb)) { 181 ierr(); 182 exit(1); 183 } 184 185 /* 186 * Determine if input is a pipe. 4.4BSD will set the SOCKET 187 * bit in the st_mode field for pipes. Fix this then. 188 */ 189 if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 190 errno == ESPIPE) { 191 errno = 0; 192 fflag = 0; /* POSIX.2 requires this. */ 193 } 194 195 if (rflag) 196 reverse(stdin, style, off, &sb); 197 else 198 forward(stdin, style, off, &sb); 199 } 200 exit(rval); 201 } 202 203 /* 204 * Tail's options are weird. First, -n10 is the same as -n-10, not 205 * -n+10. Second, the number options are 1 based and not offsets, 206 * so -n+1 is the first line, and -c-1 is the last byte. Third, the 207 * number options for the -r option specify the number of things that 208 * get displayed, not the starting point in the file. The one major 209 * incompatibility in this version as compared to historical versions 210 * is that the 'r' option couldn't be modified by the -lbc options, 211 * i.e. it was always done in lines. This version treats -rc as a 212 * number of characters in reverse order. Finally, the default for 213 * -r is the entire file, not 10 lines. 214 */ 215 static void 216 getarg(int units, enum STYLE forward_style, enum STYLE backward_style, 217 enum STYLE *style, int *style_set, off_t *off) 218 { 219 char *p; 220 221 if (*style_set) 222 usage(); 223 224 *off = strtoll(optarg, &p, 10) * units; 225 if (*p != '\0') 226 errx(1, "illegal offset -- %s", optarg); 227 switch (optarg[0]) { 228 case '+': 229 if (*off != 0) 230 *off -= (units); 231 *style = forward_style; 232 break; 233 case '-': 234 *off = -*off; 235 /* FALLTHROUGH */ 236 default: 237 *style = backward_style; 238 break; 239 } 240 241 *style_set = 1; 242 } 243 244 /* 245 * Convert the obsolete argument form into something that getopt can handle. 246 * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't 247 * the option argument for a -b, -c or -n option gets converted. 248 */ 249 static void 250 obsolete(char **argv) 251 { 252 char *ap, *p, *t; 253 size_t len; 254 char *start; 255 256 while ((ap = *++argv) != NULL) { 257 /* Return if "--" or not an option of any form. */ 258 if (ap[0] != '-') { 259 if (ap[0] != '+') 260 return; 261 } else if (ap[1] == '-') 262 return; 263 264 switch(*++ap) { 265 /* Old-style option. */ 266 case '0': case '1': case '2': case '3': case '4': 267 case '5': case '6': case '7': case '8': case '9': 268 269 /* Malloc space for dash, new option and argument. */ 270 len = strlen(*argv); 271 if ((start = p = malloc(len + 3)) == NULL) 272 err(1, "malloc"); 273 *p++ = '-'; 274 275 /* 276 * Go to the end of the option argument. Save off any 277 * trailing options (-3lf) and translate any trailing 278 * output style characters. 279 */ 280 t = *argv + len - 1; 281 if (*t == 'F' || *t == 'f' || *t == 'r') { 282 *p++ = *t; 283 *t-- = '\0'; 284 } 285 switch(*t) { 286 case 'b': 287 *p++ = 'b'; 288 *t = '\0'; 289 break; 290 case 'c': 291 *p++ = 'c'; 292 *t = '\0'; 293 break; 294 case 'l': 295 *t = '\0'; 296 /* FALLTHROUGH */ 297 case '0': case '1': case '2': case '3': case '4': 298 case '5': case '6': case '7': case '8': case '9': 299 *p++ = 'n'; 300 break; 301 default: 302 errx(1, "illegal option -- %s", *argv); 303 } 304 *p++ = *argv[0]; 305 (void)strcpy(p, ap); 306 *argv = start; 307 continue; 308 309 /* 310 * Options w/ arguments, skip the argument and continue 311 * with the next option. 312 */ 313 case 'b': 314 case 'c': 315 case 'n': 316 if (!ap[1]) 317 ++argv; 318 /* FALLTHROUGH */ 319 /* Options w/o arguments, continue with the next option. */ 320 case 'F': 321 case 'f': 322 case 'r': 323 continue; 324 325 /* Illegal option, return and let getopt handle it. */ 326 default: 327 return; 328 } 329 } 330 } 331 332 static void 333 usage(void) 334 { 335 (void)fprintf(stderr, 336 "usage: tail [-F | -f | -r] [-b # | -c # | -n #] [file ...]\n"); 337 exit(1); 338 } 339