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*52827Sbostic static char sccsid[] = "@(#)tail.c 5.8 (Berkeley) 03/03/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; 44*52827Sbostic int ch, first; 4552472Sbostic char *p; 461214Sroot 4752472Sbostic /* 4852472Sbostic * Tail's options are weird. First, -n10 is the same as -n-10, not 4952472Sbostic * -n+10. Second, the number options are 1 based and not offsets, 5052472Sbostic * so -n+1 is the first line, and -c-1 is the last byte. Third, the 5152472Sbostic * number options for the -r option specify the number of things that 5252472Sbostic * get displayed, not the starting point in the file. The one major 5352472Sbostic * incompatibility in this version as compared to historical versions 5452472Sbostic * is that the 'r' option couldn't be modified by the -lbc options, 5552472Sbostic * i.e. it was always done in lines. This version treats -rc as a 5652472Sbostic * number of characters in reverse order. Finally, the default for 5752472Sbostic * -r is the entire file, not 10 lines. 5852472Sbostic */ 5952472Sbostic #define ARG(units, forward, backward) { \ 6052472Sbostic if (style) \ 6152472Sbostic usage(); \ 6252472Sbostic off = strtol(optarg, &p, 10) * (units); \ 6352472Sbostic if (*p) \ 64*52827Sbostic err(1, "illegal offset -- %s", optarg); \ 6552472Sbostic switch(optarg[0]) { \ 6652472Sbostic case '+': \ 6752472Sbostic if (off) \ 6852472Sbostic off -= (units); \ 6952472Sbostic style = (forward); \ 7052472Sbostic break; \ 7152472Sbostic case '-': \ 7252472Sbostic off = -off; \ 7352472Sbostic /* FALLTHROUGH */ \ 7452472Sbostic default: \ 7552472Sbostic style = (backward); \ 7652472Sbostic break; \ 7752472Sbostic } \ 7852472Sbostic } 7952472Sbostic 8050453Sbostic obsolete(argv); 8150453Sbostic style = NOTSET; 8250453Sbostic while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) 8350453Sbostic switch(ch) { 8450453Sbostic case 'b': 8552472Sbostic ARG(512, FBYTES, RBYTES); 8650453Sbostic break; 8750453Sbostic case 'c': 8852472Sbostic ARG(1, FBYTES, RBYTES); 8950453Sbostic break; 9050453Sbostic case 'f': 9150453Sbostic fflag = 1; 9250453Sbostic break; 9350453Sbostic case 'n': 9452472Sbostic 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 /* 10752472Sbostic * If displaying in reverse, don't permit follow option, and convert 10852472Sbostic * 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 11952472Sbostic /* 12052472Sbostic * If style not specified, the default is the whole file for -r, and 12152472Sbostic * the last 10 lines if not -r. 12252472Sbostic */ 12352472Sbostic if (style == NOTSET) 12452472Sbostic if (rflag) { 12552472Sbostic off = 0; 12652472Sbostic style = REVERSE; 12752472Sbostic } else { 12852472Sbostic off = 10; 12952472Sbostic style = RLINES; 13052472Sbostic } 13152472Sbostic 132*52827Sbostic if (*argv) 133*52827Sbostic for (first = 1; fname = *argv++;) { 134*52827Sbostic if ((fp = fopen(fname, "r")) == NULL) { 135*52827Sbostic ierr(); 136*52827Sbostic continue; 137*52827Sbostic } 138*52827Sbostic if (argc > 1) { 139*52827Sbostic (void)printf("%s==> %s <==\n", 140*52827Sbostic first ? "" : "\n", fname); 141*52827Sbostic first = 0; 142*52827Sbostic } 143*52827Sbostic 144*52827Sbostic if (rflag) 145*52827Sbostic reverse(fp, style, off, &sb); 146*52827Sbostic else 147*52827Sbostic forward(fp, style, off, &sb); 148*52827Sbostic } 149*52827Sbostic else { 15050453Sbostic fp = stdin; 15150453Sbostic fname = "stdin"; 1521214Sroot 153*52827Sbostic if (fstat(fileno(fp), &sb)) { 154*52827Sbostic ierr(); 155*52827Sbostic exit(1); 156*52827Sbostic } 1571214Sroot 158*52827Sbostic /* 159*52827Sbostic * Determine if input is a pipe. 4.4BSD will set the SOCKET 160*52827Sbostic * bit in the st_mode field for pipes. Fix this then. 161*52827Sbostic */ 162*52827Sbostic if (lseek(fileno(fp), 0L, SEEK_CUR) == -1 && errno == ESPIPE) { 163*52827Sbostic errno = 0; 164*52827Sbostic fflag = 0; /* POSIX.2 requires this. */ 165*52827Sbostic } 166*52827Sbostic 167*52827Sbostic if (rflag) 168*52827Sbostic reverse(fp, style, off, &sb); 169*52827Sbostic else 170*52827Sbostic forward(fp, style, off, &sb); 17150453Sbostic } 17250453Sbostic exit(rval); 17350453Sbostic } 17450453Sbostic 17550453Sbostic /* 17650453Sbostic * Convert the obsolete argument form into something that getopt can handle. 17750453Sbostic * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 17850453Sbostic * the option argument for a -b, -c or -n option gets converted. 17950453Sbostic */ 18050453Sbostic static void 18150453Sbostic obsolete(argv) 18250453Sbostic char **argv; 18350453Sbostic { 18450453Sbostic register char *ap, *p, *t; 18550453Sbostic int len; 18650453Sbostic char *start; 18750453Sbostic 18850453Sbostic while (ap = *++argv) { 18950453Sbostic /* Return if "--" or not an option of any form. */ 19050453Sbostic if (ap[0] != '-') { 19150453Sbostic if (ap[0] != '+') 19250453Sbostic return; 19350453Sbostic } else if (ap[1] == '-') 19450453Sbostic return; 19550453Sbostic 19650453Sbostic switch(*++ap) { 19750453Sbostic /* Old-style option. */ 19850453Sbostic case '0': case '1': case '2': case '3': case '4': 19950453Sbostic case '5': case '6': case '7': case '8': case '9': 20050453Sbostic 20150453Sbostic /* Malloc space for dash, new option and argument. */ 20250453Sbostic len = strlen(*argv); 20350453Sbostic if ((start = p = malloc(len + 3)) == NULL) 204*52827Sbostic err(1, "%s", strerror(errno)); 20550453Sbostic *p++ = '-'; 20650453Sbostic 20750453Sbostic /* 20850453Sbostic * Go to the end of the option argument. Save off any 20950453Sbostic * trailing options (-3lf) and translate any trailing 21050453Sbostic * output style characters. 21150453Sbostic */ 21250453Sbostic t = *argv + len - 1; 21350896Sbostic if (*t == 'f' || *t == 'r') { 21450896Sbostic *p++ = *t; 21550896Sbostic *t-- = '\0'; 21650896Sbostic } 21750453Sbostic switch(*t) { 21850453Sbostic case 'b': 21950453Sbostic *p++ = 'b'; 22050453Sbostic *t = '\0'; 22150453Sbostic break; 22250453Sbostic case 'c': 22350453Sbostic *p++ = 'c'; 22450453Sbostic *t = '\0'; 22550453Sbostic break; 22650453Sbostic case 'l': 22750453Sbostic *t = '\0'; 22850453Sbostic /* FALLTHROUGH */ 22950453Sbostic case '0': case '1': case '2': case '3': case '4': 23050453Sbostic case '5': case '6': case '7': case '8': case '9': 23150453Sbostic *p++ = 'n'; 23250453Sbostic break; 23350453Sbostic default: 234*52827Sbostic err(1, "illegal option -- %s", *argv); 2351214Sroot } 23650453Sbostic *p++ = *argv[0]; 23750453Sbostic (void)strcpy(p, ap); 23850453Sbostic *argv = start; 23950453Sbostic continue; 2401214Sroot 24150453Sbostic /* 24250453Sbostic * Options w/ arguments, skip the argument and continue 24350453Sbostic * with the next option. 24450453Sbostic */ 24550453Sbostic case 'b': 24650453Sbostic case 'c': 24750453Sbostic case 'n': 24850453Sbostic if (!ap[1]) 24950453Sbostic ++argv; 25050453Sbostic /* FALLTHROUGH */ 25150453Sbostic /* Options w/o arguments, continue with the next option. */ 25250453Sbostic case 'f': 25350453Sbostic case 'r': 25450453Sbostic continue; 2551214Sroot 25650453Sbostic /* Illegal option, return and let getopt handle it. */ 25750453Sbostic default: 25850453Sbostic return; 2591214Sroot } 2601214Sroot } 2611214Sroot } 2621214Sroot 26350453Sbostic static void 26450453Sbostic usage() 26550453Sbostic { 26650453Sbostic (void)fprintf(stderr, 26750453Sbostic "usage: tail [-f | -r] [-b # | -c # | -n #] [file]\n"); 26850453Sbostic exit(1); 2691214Sroot } 270