148313Sbostic /*- 250453Sbostic * Copyright (c) 1991 The Regents of the University of California. 348313Sbostic * All rights reserved. 448313Sbostic * 550453Sbostic * This code is derived from software contributed to Berkeley by 650453Sbostic * Edward Sze-Tyan Wang. 750453Sbostic * 850453Sbostic * %sccs.include.redist.c% 921579Sdist */ 1021579Sdist 1117708Sbloom #ifndef lint 1221579Sdist char copyright[] = 1350453Sbostic "@(#) 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*50896Sbostic static char sccsid[] = "@(#)tail.c 5.6 (Berkeley) 08/26/91"; 1948313Sbostic #endif /* not lint */ 2021579Sdist 2150453Sbostic #include <sys/types.h> 2250453Sbostic #include <sys/stat.h> 2350453Sbostic #include <errno.h> 2450453Sbostic #include <unistd.h> 2550453Sbostic #include <stdio.h> 2650453Sbostic #include <stdlib.h> 2750453Sbostic #include <string.h> 2850453Sbostic #include "extern.h" 291214Sroot 3050453Sbostic int fflag, rflag, rval; 3150453Sbostic char *fname; 321214Sroot 3350453Sbostic static void obsolete __P((char **)); 3450453Sbostic static void usage __P((void)); 351214Sroot 3650453Sbostic main(argc, argv) 3750453Sbostic int argc; 3850453Sbostic char **argv; 391214Sroot { 4050453Sbostic struct stat sb; 4150453Sbostic FILE *fp; 4250453Sbostic long off; 4350453Sbostic enum STYLE style; 4450453Sbostic int ch; 4550453Sbostic char *p, *num; 461214Sroot 4750453Sbostic obsolete(argv); 4850453Sbostic 4950453Sbostic style = NOTSET; 5050453Sbostic while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) 5150453Sbostic switch(ch) { 5250453Sbostic case 'b': 5350453Sbostic if (style) 5450453Sbostic usage(); 5550453Sbostic off = strtol(num = optarg, &p, 10) * 512; 5650453Sbostic if (*p) 5750453Sbostic err("illegal offset -- %s", optarg); 5850453Sbostic style = *num == '+' ? FBYTES : RBYTES; 5950453Sbostic break; 6050453Sbostic case 'c': 6150453Sbostic if (style) 6250453Sbostic usage(); 6350453Sbostic off = strtol(num = optarg, &p, 10); 6450453Sbostic if (*p) 6550453Sbostic err("illegal offset -- %s", optarg); 6650453Sbostic style = *num == '+' ? FBYTES : RBYTES; 6750453Sbostic break; 6850453Sbostic case 'f': 6950453Sbostic fflag = 1; 7050453Sbostic break; 7150453Sbostic case 'n': 7250453Sbostic if (style) 7350453Sbostic usage(); 7450453Sbostic off = strtol(num = optarg, &p, 10); 7550453Sbostic if (*p) 7650453Sbostic err("illegal offset -- %s", optarg); 7750453Sbostic style = *num == '+' ? FLINES : RLINES; 7850453Sbostic break; 7950453Sbostic case 'r': 8050453Sbostic rflag = 1; 8150453Sbostic break; 8250453Sbostic case '?': 8350453Sbostic default: 8450453Sbostic usage(); 851214Sroot } 8650453Sbostic argc -= optind; 8750453Sbostic argv += optind; 8850453Sbostic 8950453Sbostic /* 9050453Sbostic * Don't permit follow option if displaying in reverse. An offset 9150453Sbostic * with an explicit leading minus is meaningless. 9250453Sbostic */ 9350453Sbostic if (rflag) { 9450453Sbostic if (fflag) 9550453Sbostic usage(); 9650453Sbostic if (style && *num == '-') 9750453Sbostic err("illegal offset for -r option -- %s", num); 9850453Sbostic if (style == FBYTES) 9950453Sbostic style = RBYTES; 10050453Sbostic if (style == FLINES) 10150453Sbostic style = RLINES; 1021214Sroot } 1031214Sroot 10450453Sbostic if (fname = *argv) { 10550453Sbostic if ((fp = fopen(fname, "r")) == NULL) 10650453Sbostic ierr(); 10750453Sbostic } else { 10850453Sbostic fp = stdin; 10950453Sbostic fname = "stdin"; 1101214Sroot } 1111214Sroot 11250453Sbostic if (fstat(fileno(fp), &sb)) 11350453Sbostic ierr(); 1141214Sroot 11550453Sbostic /* 11650453Sbostic * Determine if input is a pipe. 4.4BSD will set the SOCKET 11750453Sbostic * bit in the st_mode field for pipes. Fix this then. 11850453Sbostic */ 11950453Sbostic if (lseek(fileno(fp), 0L, SEEK_CUR) == -1 && errno == ESPIPE) { 12050453Sbostic errno = 0; 12150453Sbostic fflag = 0; /* POSIX.2 requires this. */ 12250453Sbostic } 12350453Sbostic 12450453Sbostic /* 12550453Sbostic * Tail's options are weird. First, -n10 is the same as -n-10, not 12650453Sbostic * -n+10. Second, the number options for the -r option specify the 12750453Sbostic * number of bytes/chars/lines that get displayed, not the offset from 12850453Sbostic * the beginning/end of the file. Finally, the default for -r is the 12950453Sbostic * entire file, not 10 lines. 13050453Sbostic */ 13150453Sbostic if (!style) 13250453Sbostic if (rflag) { 13350453Sbostic off = 0; 13450453Sbostic style = REVERSE; 13550453Sbostic } else { 13650453Sbostic off = 10; 13750453Sbostic style = RLINES; 1381214Sroot } 13950453Sbostic else if (off < 0) 14050453Sbostic off = -off; 14150453Sbostic 14250453Sbostic if (rflag) 14350453Sbostic reverse(fp, style, off, &sb); 14450453Sbostic else 14550453Sbostic forward(fp, style, off, &sb); 14650453Sbostic exit(rval); 14750453Sbostic } 14850453Sbostic 14950453Sbostic /* 15050453Sbostic * Convert the obsolete argument form into something that getopt can handle. 15150453Sbostic * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 15250453Sbostic * the option argument for a -b, -c or -n option gets converted. 15350453Sbostic */ 15450453Sbostic static void 15550453Sbostic obsolete(argv) 15650453Sbostic char **argv; 15750453Sbostic { 15850453Sbostic register char *ap, *p, *t; 15950453Sbostic int len; 16050453Sbostic char *start; 16150453Sbostic 16250453Sbostic while (ap = *++argv) { 16350453Sbostic /* Return if "--" or not an option of any form. */ 16450453Sbostic if (ap[0] != '-') { 16550453Sbostic if (ap[0] != '+') 16650453Sbostic return; 16750453Sbostic } else if (ap[1] == '-') 16850453Sbostic return; 16950453Sbostic 17050453Sbostic switch(*++ap) { 17150453Sbostic /* Old-style option. */ 17250453Sbostic case '0': case '1': case '2': case '3': case '4': 17350453Sbostic case '5': case '6': case '7': case '8': case '9': 17450453Sbostic 17550453Sbostic /* Malloc space for dash, new option and argument. */ 17650453Sbostic len = strlen(*argv); 17750453Sbostic if ((start = p = malloc(len + 3)) == NULL) 17850453Sbostic err("%s", strerror(errno)); 17950453Sbostic *p++ = '-'; 18050453Sbostic 18150453Sbostic /* 18250453Sbostic * Go to the end of the option argument. Save off any 18350453Sbostic * trailing options (-3lf) and translate any trailing 18450453Sbostic * output style characters. 18550453Sbostic */ 18650453Sbostic t = *argv + len - 1; 187*50896Sbostic if (*t == 'f' || *t == 'r') { 188*50896Sbostic *p++ = *t; 189*50896Sbostic *t-- = '\0'; 190*50896Sbostic } 19150453Sbostic switch(*t) { 19250453Sbostic case 'b': 19350453Sbostic *p++ = 'b'; 19450453Sbostic *t = '\0'; 19550453Sbostic break; 19650453Sbostic case 'c': 19750453Sbostic *p++ = 'c'; 19850453Sbostic *t = '\0'; 19950453Sbostic break; 20050453Sbostic case 'l': 20150453Sbostic *t = '\0'; 20250453Sbostic /* FALLTHROUGH */ 20350453Sbostic case '0': case '1': case '2': case '3': case '4': 20450453Sbostic case '5': case '6': case '7': case '8': case '9': 20550453Sbostic *p++ = 'n'; 20650453Sbostic break; 20750453Sbostic default: 20850453Sbostic err("illegal option -- %s", *argv); 2091214Sroot } 21050453Sbostic *p++ = *argv[0]; 21150453Sbostic (void)strcpy(p, ap); 21250453Sbostic *argv = start; 21350453Sbostic continue; 2141214Sroot 21550453Sbostic /* 21650453Sbostic * Options w/ arguments, skip the argument and continue 21750453Sbostic * with the next option. 21850453Sbostic */ 21950453Sbostic case 'b': 22050453Sbostic case 'c': 22150453Sbostic case 'n': 22250453Sbostic if (!ap[1]) 22350453Sbostic ++argv; 22450453Sbostic /* FALLTHROUGH */ 22550453Sbostic /* Options w/o arguments, continue with the next option. */ 22650453Sbostic case 'f': 22750453Sbostic case 'r': 22850453Sbostic continue; 2291214Sroot 23050453Sbostic /* Illegal option, return and let getopt handle it. */ 23150453Sbostic default: 23250453Sbostic return; 2331214Sroot } 2341214Sroot } 2351214Sroot } 2361214Sroot 23750453Sbostic static void 23850453Sbostic usage() 23950453Sbostic { 24050453Sbostic (void)fprintf(stderr, 24150453Sbostic "usage: tail [-f | -r] [-b # | -c # | -n #] [file]\n"); 24250453Sbostic exit(1); 2431214Sroot } 244