148313Sbostic /*- 2*50453Sbostic * Copyright (c) 1991 The Regents of the University of California. 348313Sbostic * All rights reserved. 448313Sbostic * 5*50453Sbostic * This code is derived from software contributed to Berkeley by 6*50453Sbostic * Edward Sze-Tyan Wang. 7*50453Sbostic * 8*50453Sbostic * %sccs.include.redist.c% 921579Sdist */ 1021579Sdist 1117708Sbloom #ifndef lint 1221579Sdist char copyright[] = 13*50453Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 1421579Sdist All rights reserved.\n"; 1548313Sbostic #endif /* not lint */ 1621579Sdist 1721579Sdist #ifndef lint 18*50453Sbostic static char sccsid[] = "@(#)tail.c 5.5 (Berkeley) 07/21/91"; 1948313Sbostic #endif /* not lint */ 2021579Sdist 21*50453Sbostic #include <sys/types.h> 22*50453Sbostic #include <sys/stat.h> 23*50453Sbostic #include <errno.h> 24*50453Sbostic #include <unistd.h> 25*50453Sbostic #include <stdio.h> 26*50453Sbostic #include <stdlib.h> 27*50453Sbostic #include <string.h> 28*50453Sbostic #include "extern.h" 291214Sroot 30*50453Sbostic int fflag, rflag, rval; 31*50453Sbostic char *fname; 321214Sroot 33*50453Sbostic static void obsolete __P((char **)); 34*50453Sbostic static void usage __P((void)); 351214Sroot 36*50453Sbostic main(argc, argv) 37*50453Sbostic int argc; 38*50453Sbostic char **argv; 391214Sroot { 40*50453Sbostic struct stat sb; 41*50453Sbostic FILE *fp; 42*50453Sbostic long off; 43*50453Sbostic enum STYLE style; 44*50453Sbostic int ch; 45*50453Sbostic char *p, *num; 461214Sroot 47*50453Sbostic obsolete(argv); 48*50453Sbostic 49*50453Sbostic style = NOTSET; 50*50453Sbostic while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) 51*50453Sbostic switch(ch) { 52*50453Sbostic case 'b': 53*50453Sbostic if (style) 54*50453Sbostic usage(); 55*50453Sbostic off = strtol(num = optarg, &p, 10) * 512; 56*50453Sbostic if (*p) 57*50453Sbostic err("illegal offset -- %s", optarg); 58*50453Sbostic style = *num == '+' ? FBYTES : RBYTES; 59*50453Sbostic break; 60*50453Sbostic case 'c': 61*50453Sbostic if (style) 62*50453Sbostic usage(); 63*50453Sbostic off = strtol(num = optarg, &p, 10); 64*50453Sbostic if (*p) 65*50453Sbostic err("illegal offset -- %s", optarg); 66*50453Sbostic style = *num == '+' ? FBYTES : RBYTES; 67*50453Sbostic break; 68*50453Sbostic case 'f': 69*50453Sbostic fflag = 1; 70*50453Sbostic break; 71*50453Sbostic case 'n': 72*50453Sbostic if (style) 73*50453Sbostic usage(); 74*50453Sbostic off = strtol(num = optarg, &p, 10); 75*50453Sbostic if (*p) 76*50453Sbostic err("illegal offset -- %s", optarg); 77*50453Sbostic style = *num == '+' ? FLINES : RLINES; 78*50453Sbostic break; 79*50453Sbostic case 'r': 80*50453Sbostic rflag = 1; 81*50453Sbostic break; 82*50453Sbostic case '?': 83*50453Sbostic default: 84*50453Sbostic usage(); 851214Sroot } 86*50453Sbostic argc -= optind; 87*50453Sbostic argv += optind; 88*50453Sbostic 89*50453Sbostic /* 90*50453Sbostic * Don't permit follow option if displaying in reverse. An offset 91*50453Sbostic * with an explicit leading minus is meaningless. 92*50453Sbostic */ 93*50453Sbostic if (rflag) { 94*50453Sbostic if (fflag) 95*50453Sbostic usage(); 96*50453Sbostic if (style && *num == '-') 97*50453Sbostic err("illegal offset for -r option -- %s", num); 98*50453Sbostic if (style == FBYTES) 99*50453Sbostic style = RBYTES; 100*50453Sbostic if (style == FLINES) 101*50453Sbostic style = RLINES; 1021214Sroot } 1031214Sroot 104*50453Sbostic if (fname = *argv) { 105*50453Sbostic if ((fp = fopen(fname, "r")) == NULL) 106*50453Sbostic ierr(); 107*50453Sbostic } else { 108*50453Sbostic fp = stdin; 109*50453Sbostic fname = "stdin"; 1101214Sroot } 1111214Sroot 112*50453Sbostic if (fstat(fileno(fp), &sb)) 113*50453Sbostic ierr(); 1141214Sroot 115*50453Sbostic /* 116*50453Sbostic * Determine if input is a pipe. 4.4BSD will set the SOCKET 117*50453Sbostic * bit in the st_mode field for pipes. Fix this then. 118*50453Sbostic */ 119*50453Sbostic if (lseek(fileno(fp), 0L, SEEK_CUR) == -1 && errno == ESPIPE) { 120*50453Sbostic errno = 0; 121*50453Sbostic fflag = 0; /* POSIX.2 requires this. */ 122*50453Sbostic } 123*50453Sbostic 124*50453Sbostic /* 125*50453Sbostic * Tail's options are weird. First, -n10 is the same as -n-10, not 126*50453Sbostic * -n+10. Second, the number options for the -r option specify the 127*50453Sbostic * number of bytes/chars/lines that get displayed, not the offset from 128*50453Sbostic * the beginning/end of the file. Finally, the default for -r is the 129*50453Sbostic * entire file, not 10 lines. 130*50453Sbostic */ 131*50453Sbostic if (!style) 132*50453Sbostic if (rflag) { 133*50453Sbostic off = 0; 134*50453Sbostic style = REVERSE; 135*50453Sbostic } else { 136*50453Sbostic off = 10; 137*50453Sbostic style = RLINES; 1381214Sroot } 139*50453Sbostic else if (off < 0) 140*50453Sbostic off = -off; 141*50453Sbostic 142*50453Sbostic if (rflag) 143*50453Sbostic reverse(fp, style, off, &sb); 144*50453Sbostic else 145*50453Sbostic forward(fp, style, off, &sb); 146*50453Sbostic exit(rval); 147*50453Sbostic } 148*50453Sbostic 149*50453Sbostic /* 150*50453Sbostic * Convert the obsolete argument form into something that getopt can handle. 151*50453Sbostic * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 152*50453Sbostic * the option argument for a -b, -c or -n option gets converted. 153*50453Sbostic */ 154*50453Sbostic static void 155*50453Sbostic obsolete(argv) 156*50453Sbostic char **argv; 157*50453Sbostic { 158*50453Sbostic register char *ap, *p, *t; 159*50453Sbostic int len; 160*50453Sbostic char *start; 161*50453Sbostic 162*50453Sbostic while (ap = *++argv) { 163*50453Sbostic /* Return if "--" or not an option of any form. */ 164*50453Sbostic if (ap[0] != '-') { 165*50453Sbostic if (ap[0] != '+') 166*50453Sbostic return; 167*50453Sbostic } else if (ap[1] == '-') 168*50453Sbostic return; 169*50453Sbostic 170*50453Sbostic switch(*++ap) { 171*50453Sbostic /* Old-style option. */ 172*50453Sbostic case '0': case '1': case '2': case '3': case '4': 173*50453Sbostic case '5': case '6': case '7': case '8': case '9': 174*50453Sbostic 175*50453Sbostic /* Malloc space for dash, new option and argument. */ 176*50453Sbostic len = strlen(*argv); 177*50453Sbostic if ((start = p = malloc(len + 3)) == NULL) 178*50453Sbostic err("%s", strerror(errno)); 179*50453Sbostic *p++ = '-'; 180*50453Sbostic 181*50453Sbostic /* 182*50453Sbostic * Go to the end of the option argument. Save off any 183*50453Sbostic * trailing options (-3lf) and translate any trailing 184*50453Sbostic * output style characters. 185*50453Sbostic */ 186*50453Sbostic t = *argv + len - 1; 187*50453Sbostic if (*t == 'f' || *t == 'r') 188*50453Sbostic *p++ = *t--; 189*50453Sbostic switch(*t) { 190*50453Sbostic case 'b': 191*50453Sbostic *p++ = 'b'; 192*50453Sbostic *t = '\0'; 193*50453Sbostic break; 194*50453Sbostic case 'c': 195*50453Sbostic *p++ = 'c'; 196*50453Sbostic *t = '\0'; 197*50453Sbostic break; 198*50453Sbostic case 'l': 199*50453Sbostic *t = '\0'; 200*50453Sbostic /* FALLTHROUGH */ 201*50453Sbostic case '0': case '1': case '2': case '3': case '4': 202*50453Sbostic case '5': case '6': case '7': case '8': case '9': 203*50453Sbostic *p++ = 'n'; 204*50453Sbostic break; 205*50453Sbostic default: 206*50453Sbostic err("illegal option -- %s", *argv); 2071214Sroot } 208*50453Sbostic *p++ = *argv[0]; 209*50453Sbostic (void)strcpy(p, ap); 210*50453Sbostic *argv = start; 211*50453Sbostic continue; 2121214Sroot 213*50453Sbostic /* 214*50453Sbostic * Options w/ arguments, skip the argument and continue 215*50453Sbostic * with the next option. 216*50453Sbostic */ 217*50453Sbostic case 'b': 218*50453Sbostic case 'c': 219*50453Sbostic case 'n': 220*50453Sbostic if (!ap[1]) 221*50453Sbostic ++argv; 222*50453Sbostic /* FALLTHROUGH */ 223*50453Sbostic /* Options w/o arguments, continue with the next option. */ 224*50453Sbostic case 'f': 225*50453Sbostic case 'r': 226*50453Sbostic continue; 2271214Sroot 228*50453Sbostic /* Illegal option, return and let getopt handle it. */ 229*50453Sbostic default: 230*50453Sbostic return; 2311214Sroot } 2321214Sroot } 2331214Sroot } 2341214Sroot 235*50453Sbostic static void 236*50453Sbostic usage() 237*50453Sbostic { 238*50453Sbostic (void)fprintf(stderr, 239*50453Sbostic "usage: tail [-f | -r] [-b # | -c # | -n #] [file]\n"); 240*50453Sbostic exit(1); 2411214Sroot } 242