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*52472Sbostic static char sccsid[] = "@(#)tail.c 5.7 (Berkeley) 02/12/92"; 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; 45*52472Sbostic char *p; 461214Sroot 47*52472Sbostic /* 48*52472Sbostic * Tail's options are weird. First, -n10 is the same as -n-10, not 49*52472Sbostic * -n+10. Second, the number options are 1 based and not offsets, 50*52472Sbostic * so -n+1 is the first line, and -c-1 is the last byte. Third, the 51*52472Sbostic * number options for the -r option specify the number of things that 52*52472Sbostic * get displayed, not the starting point in the file. The one major 53*52472Sbostic * incompatibility in this version as compared to historical versions 54*52472Sbostic * is that the 'r' option couldn't be modified by the -lbc options, 55*52472Sbostic * i.e. it was always done in lines. This version treats -rc as a 56*52472Sbostic * number of characters in reverse order. Finally, the default for 57*52472Sbostic * -r is the entire file, not 10 lines. 58*52472Sbostic */ 59*52472Sbostic #define ARG(units, forward, backward) { \ 60*52472Sbostic if (style) \ 61*52472Sbostic usage(); \ 62*52472Sbostic off = strtol(optarg, &p, 10) * (units); \ 63*52472Sbostic if (*p) \ 64*52472Sbostic err("illegal offset -- %s", optarg); \ 65*52472Sbostic switch(optarg[0]) { \ 66*52472Sbostic case '+': \ 67*52472Sbostic if (off) \ 68*52472Sbostic off -= (units); \ 69*52472Sbostic style = (forward); \ 70*52472Sbostic break; \ 71*52472Sbostic case '-': \ 72*52472Sbostic off = -off; \ 73*52472Sbostic /* FALLTHROUGH */ \ 74*52472Sbostic default: \ 75*52472Sbostic style = (backward); \ 76*52472Sbostic break; \ 77*52472Sbostic } \ 78*52472Sbostic } 79*52472Sbostic 8050453Sbostic obsolete(argv); 8150453Sbostic style = NOTSET; 8250453Sbostic while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) 8350453Sbostic switch(ch) { 8450453Sbostic case 'b': 85*52472Sbostic ARG(512, FBYTES, RBYTES); 8650453Sbostic break; 8750453Sbostic case 'c': 88*52472Sbostic ARG(1, FBYTES, RBYTES); 8950453Sbostic break; 9050453Sbostic case 'f': 9150453Sbostic fflag = 1; 9250453Sbostic break; 9350453Sbostic case 'n': 94*52472Sbostic ARG(1, FLINES, RLINES); 9550453Sbostic break; 9650453Sbostic case 'r': 9750453Sbostic rflag = 1; 9850453Sbostic break; 9950453Sbostic case '?': 10050453Sbostic default: 10150453Sbostic usage(); 1021214Sroot } 10350453Sbostic argc -= optind; 10450453Sbostic argv += optind; 10550453Sbostic 10650453Sbostic /* 107*52472Sbostic * If displaying in reverse, don't permit follow option, and convert 108*52472Sbostic * style values. 10950453Sbostic */ 11050453Sbostic if (rflag) { 11150453Sbostic if (fflag) 11250453Sbostic usage(); 11350453Sbostic if (style == FBYTES) 11450453Sbostic style = RBYTES; 11550453Sbostic if (style == FLINES) 11650453Sbostic style = RLINES; 1171214Sroot } 1181214Sroot 119*52472Sbostic /* 120*52472Sbostic * If style not specified, the default is the whole file for -r, and 121*52472Sbostic * the last 10 lines if not -r. 122*52472Sbostic */ 123*52472Sbostic if (style == NOTSET) 124*52472Sbostic if (rflag) { 125*52472Sbostic off = 0; 126*52472Sbostic style = REVERSE; 127*52472Sbostic } else { 128*52472Sbostic off = 10; 129*52472Sbostic style = RLINES; 130*52472Sbostic } 131*52472Sbostic 13250453Sbostic if (fname = *argv) { 13350453Sbostic if ((fp = fopen(fname, "r")) == NULL) 13450453Sbostic ierr(); 13550453Sbostic } else { 13650453Sbostic fp = stdin; 13750453Sbostic fname = "stdin"; 1381214Sroot } 1391214Sroot 14050453Sbostic if (fstat(fileno(fp), &sb)) 14150453Sbostic ierr(); 1421214Sroot 14350453Sbostic /* 14450453Sbostic * Determine if input is a pipe. 4.4BSD will set the SOCKET 14550453Sbostic * bit in the st_mode field for pipes. Fix this then. 14650453Sbostic */ 14750453Sbostic if (lseek(fileno(fp), 0L, SEEK_CUR) == -1 && errno == ESPIPE) { 14850453Sbostic errno = 0; 14950453Sbostic fflag = 0; /* POSIX.2 requires this. */ 15050453Sbostic } 15150453Sbostic 15250453Sbostic if (rflag) 15350453Sbostic reverse(fp, style, off, &sb); 15450453Sbostic else 15550453Sbostic forward(fp, style, off, &sb); 15650453Sbostic exit(rval); 15750453Sbostic } 15850453Sbostic 15950453Sbostic /* 16050453Sbostic * Convert the obsolete argument form into something that getopt can handle. 16150453Sbostic * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 16250453Sbostic * the option argument for a -b, -c or -n option gets converted. 16350453Sbostic */ 16450453Sbostic static void 16550453Sbostic obsolete(argv) 16650453Sbostic char **argv; 16750453Sbostic { 16850453Sbostic register char *ap, *p, *t; 16950453Sbostic int len; 17050453Sbostic char *start; 17150453Sbostic 17250453Sbostic while (ap = *++argv) { 17350453Sbostic /* Return if "--" or not an option of any form. */ 17450453Sbostic if (ap[0] != '-') { 17550453Sbostic if (ap[0] != '+') 17650453Sbostic return; 17750453Sbostic } else if (ap[1] == '-') 17850453Sbostic return; 17950453Sbostic 18050453Sbostic switch(*++ap) { 18150453Sbostic /* Old-style option. */ 18250453Sbostic case '0': case '1': case '2': case '3': case '4': 18350453Sbostic case '5': case '6': case '7': case '8': case '9': 18450453Sbostic 18550453Sbostic /* Malloc space for dash, new option and argument. */ 18650453Sbostic len = strlen(*argv); 18750453Sbostic if ((start = p = malloc(len + 3)) == NULL) 18850453Sbostic err("%s", strerror(errno)); 18950453Sbostic *p++ = '-'; 19050453Sbostic 19150453Sbostic /* 19250453Sbostic * Go to the end of the option argument. Save off any 19350453Sbostic * trailing options (-3lf) and translate any trailing 19450453Sbostic * output style characters. 19550453Sbostic */ 19650453Sbostic t = *argv + len - 1; 19750896Sbostic if (*t == 'f' || *t == 'r') { 19850896Sbostic *p++ = *t; 19950896Sbostic *t-- = '\0'; 20050896Sbostic } 20150453Sbostic switch(*t) { 20250453Sbostic case 'b': 20350453Sbostic *p++ = 'b'; 20450453Sbostic *t = '\0'; 20550453Sbostic break; 20650453Sbostic case 'c': 20750453Sbostic *p++ = 'c'; 20850453Sbostic *t = '\0'; 20950453Sbostic break; 21050453Sbostic case 'l': 21150453Sbostic *t = '\0'; 21250453Sbostic /* FALLTHROUGH */ 21350453Sbostic case '0': case '1': case '2': case '3': case '4': 21450453Sbostic case '5': case '6': case '7': case '8': case '9': 21550453Sbostic *p++ = 'n'; 21650453Sbostic break; 21750453Sbostic default: 21850453Sbostic err("illegal option -- %s", *argv); 2191214Sroot } 22050453Sbostic *p++ = *argv[0]; 22150453Sbostic (void)strcpy(p, ap); 22250453Sbostic *argv = start; 22350453Sbostic continue; 2241214Sroot 22550453Sbostic /* 22650453Sbostic * Options w/ arguments, skip the argument and continue 22750453Sbostic * with the next option. 22850453Sbostic */ 22950453Sbostic case 'b': 23050453Sbostic case 'c': 23150453Sbostic case 'n': 23250453Sbostic if (!ap[1]) 23350453Sbostic ++argv; 23450453Sbostic /* FALLTHROUGH */ 23550453Sbostic /* Options w/o arguments, continue with the next option. */ 23650453Sbostic case 'f': 23750453Sbostic case 'r': 23850453Sbostic continue; 2391214Sroot 24050453Sbostic /* Illegal option, return and let getopt handle it. */ 24150453Sbostic default: 24250453Sbostic return; 2431214Sroot } 2441214Sroot } 2451214Sroot } 2461214Sroot 24750453Sbostic static void 24850453Sbostic usage() 24950453Sbostic { 25050453Sbostic (void)fprintf(stderr, 25150453Sbostic "usage: tail [-f | -r] [-b # | -c # | -n #] [file]\n"); 25250453Sbostic exit(1); 2531214Sroot } 254