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*52836Sbostic static char sccsid[] = "@(#)tail.c 5.9 (Berkeley) 03/04/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 36*52836Sbostic int 3750453Sbostic main(argc, argv) 3850453Sbostic int argc; 39*52836Sbostic char *argv[]; 401214Sroot { 4150453Sbostic struct stat sb; 4250453Sbostic FILE *fp; 4350453Sbostic long off; 4450453Sbostic enum STYLE style; 4552827Sbostic int ch, first; 4652472Sbostic char *p; 471214Sroot 4852472Sbostic /* 4952472Sbostic * Tail's options are weird. First, -n10 is the same as -n-10, not 5052472Sbostic * -n+10. Second, the number options are 1 based and not offsets, 5152472Sbostic * so -n+1 is the first line, and -c-1 is the last byte. Third, the 5252472Sbostic * number options for the -r option specify the number of things that 5352472Sbostic * get displayed, not the starting point in the file. The one major 5452472Sbostic * incompatibility in this version as compared to historical versions 5552472Sbostic * is that the 'r' option couldn't be modified by the -lbc options, 5652472Sbostic * i.e. it was always done in lines. This version treats -rc as a 5752472Sbostic * number of characters in reverse order. Finally, the default for 5852472Sbostic * -r is the entire file, not 10 lines. 5952472Sbostic */ 6052472Sbostic #define ARG(units, forward, backward) { \ 6152472Sbostic if (style) \ 6252472Sbostic usage(); \ 6352472Sbostic off = strtol(optarg, &p, 10) * (units); \ 6452472Sbostic if (*p) \ 6552827Sbostic err(1, "illegal offset -- %s", optarg); \ 6652472Sbostic switch(optarg[0]) { \ 6752472Sbostic case '+': \ 6852472Sbostic if (off) \ 6952472Sbostic off -= (units); \ 7052472Sbostic style = (forward); \ 7152472Sbostic break; \ 7252472Sbostic case '-': \ 7352472Sbostic off = -off; \ 7452472Sbostic /* FALLTHROUGH */ \ 7552472Sbostic default: \ 7652472Sbostic style = (backward); \ 7752472Sbostic break; \ 7852472Sbostic } \ 7952472Sbostic } 8052472Sbostic 8150453Sbostic obsolete(argv); 8250453Sbostic style = NOTSET; 8350453Sbostic while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) 8450453Sbostic switch(ch) { 8550453Sbostic case 'b': 8652472Sbostic ARG(512, FBYTES, RBYTES); 8750453Sbostic break; 8850453Sbostic case 'c': 8952472Sbostic ARG(1, FBYTES, RBYTES); 9050453Sbostic break; 9150453Sbostic case 'f': 9250453Sbostic fflag = 1; 9350453Sbostic break; 9450453Sbostic case 'n': 9552472Sbostic ARG(1, FLINES, RLINES); 9650453Sbostic break; 9750453Sbostic case 'r': 9850453Sbostic rflag = 1; 9950453Sbostic break; 10050453Sbostic case '?': 10150453Sbostic default: 10250453Sbostic usage(); 1031214Sroot } 10450453Sbostic argc -= optind; 10550453Sbostic argv += optind; 10650453Sbostic 107*52836Sbostic if (fflag && argc > 1) 108*52836Sbostic err(1, "-f option only appropriate for a single file"); 109*52836Sbostic 11050453Sbostic /* 11152472Sbostic * If displaying in reverse, don't permit follow option, and convert 11252472Sbostic * style values. 11350453Sbostic */ 11450453Sbostic if (rflag) { 11550453Sbostic if (fflag) 11650453Sbostic usage(); 11750453Sbostic if (style == FBYTES) 11850453Sbostic style = RBYTES; 119*52836Sbostic else if (style == FLINES) 12050453Sbostic style = RLINES; 1211214Sroot } 1221214Sroot 12352472Sbostic /* 12452472Sbostic * If style not specified, the default is the whole file for -r, and 12552472Sbostic * the last 10 lines if not -r. 12652472Sbostic */ 12752472Sbostic if (style == NOTSET) 12852472Sbostic if (rflag) { 12952472Sbostic off = 0; 13052472Sbostic style = REVERSE; 13152472Sbostic } else { 13252472Sbostic off = 10; 13352472Sbostic style = RLINES; 13452472Sbostic } 13552472Sbostic 13652827Sbostic if (*argv) 13752827Sbostic for (first = 1; fname = *argv++;) { 138*52836Sbostic if ((fp = fopen(fname, "r")) == NULL || 139*52836Sbostic fstat(fileno(fp), &sb)) { 14052827Sbostic ierr(); 14152827Sbostic continue; 14252827Sbostic } 14352827Sbostic if (argc > 1) { 14452827Sbostic (void)printf("%s==> %s <==\n", 14552827Sbostic first ? "" : "\n", fname); 14652827Sbostic first = 0; 14752827Sbostic } 14852827Sbostic 14952827Sbostic if (rflag) 15052827Sbostic reverse(fp, style, off, &sb); 15152827Sbostic else 15252827Sbostic forward(fp, style, off, &sb); 153*52836Sbostic (void)fclose(fp); 15452827Sbostic } 15552827Sbostic else { 15650453Sbostic fname = "stdin"; 1571214Sroot 158*52836Sbostic if (fstat(fileno(stdin), &sb)) { 15952827Sbostic ierr(); 16052827Sbostic exit(1); 16152827Sbostic } 1621214Sroot 16352827Sbostic /* 16452827Sbostic * Determine if input is a pipe. 4.4BSD will set the SOCKET 16552827Sbostic * bit in the st_mode field for pipes. Fix this then. 16652827Sbostic */ 167*52836Sbostic if (lseek(fileno(stdin), 0L, SEEK_CUR) == -1 && 168*52836Sbostic errno == ESPIPE) { 16952827Sbostic errno = 0; 17052827Sbostic fflag = 0; /* POSIX.2 requires this. */ 17152827Sbostic } 17252827Sbostic 17352827Sbostic if (rflag) 174*52836Sbostic reverse(stdin, style, off, &sb); 17552827Sbostic else 176*52836Sbostic forward(stdin, style, off, &sb); 17750453Sbostic } 17850453Sbostic exit(rval); 17950453Sbostic } 18050453Sbostic 18150453Sbostic /* 18250453Sbostic * Convert the obsolete argument form into something that getopt can handle. 18350453Sbostic * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 18450453Sbostic * the option argument for a -b, -c or -n option gets converted. 18550453Sbostic */ 18650453Sbostic static void 18750453Sbostic obsolete(argv) 188*52836Sbostic char *argv[]; 18950453Sbostic { 19050453Sbostic register char *ap, *p, *t; 19150453Sbostic int len; 19250453Sbostic char *start; 19350453Sbostic 19450453Sbostic while (ap = *++argv) { 19550453Sbostic /* Return if "--" or not an option of any form. */ 19650453Sbostic if (ap[0] != '-') { 19750453Sbostic if (ap[0] != '+') 19850453Sbostic return; 19950453Sbostic } else if (ap[1] == '-') 20050453Sbostic return; 20150453Sbostic 20250453Sbostic switch(*++ap) { 20350453Sbostic /* Old-style option. */ 20450453Sbostic case '0': case '1': case '2': case '3': case '4': 20550453Sbostic case '5': case '6': case '7': case '8': case '9': 20650453Sbostic 20750453Sbostic /* Malloc space for dash, new option and argument. */ 20850453Sbostic len = strlen(*argv); 20950453Sbostic if ((start = p = malloc(len + 3)) == NULL) 21052827Sbostic err(1, "%s", strerror(errno)); 21150453Sbostic *p++ = '-'; 21250453Sbostic 21350453Sbostic /* 21450453Sbostic * Go to the end of the option argument. Save off any 21550453Sbostic * trailing options (-3lf) and translate any trailing 21650453Sbostic * output style characters. 21750453Sbostic */ 21850453Sbostic t = *argv + len - 1; 21950896Sbostic if (*t == 'f' || *t == 'r') { 22050896Sbostic *p++ = *t; 22150896Sbostic *t-- = '\0'; 22250896Sbostic } 22350453Sbostic switch(*t) { 22450453Sbostic case 'b': 22550453Sbostic *p++ = 'b'; 22650453Sbostic *t = '\0'; 22750453Sbostic break; 22850453Sbostic case 'c': 22950453Sbostic *p++ = 'c'; 23050453Sbostic *t = '\0'; 23150453Sbostic break; 23250453Sbostic case 'l': 23350453Sbostic *t = '\0'; 23450453Sbostic /* FALLTHROUGH */ 23550453Sbostic case '0': case '1': case '2': case '3': case '4': 23650453Sbostic case '5': case '6': case '7': case '8': case '9': 23750453Sbostic *p++ = 'n'; 23850453Sbostic break; 23950453Sbostic default: 24052827Sbostic err(1, "illegal option -- %s", *argv); 2411214Sroot } 24250453Sbostic *p++ = *argv[0]; 24350453Sbostic (void)strcpy(p, ap); 24450453Sbostic *argv = start; 24550453Sbostic continue; 2461214Sroot 24750453Sbostic /* 24850453Sbostic * Options w/ arguments, skip the argument and continue 24950453Sbostic * with the next option. 25050453Sbostic */ 25150453Sbostic case 'b': 25250453Sbostic case 'c': 25350453Sbostic case 'n': 25450453Sbostic if (!ap[1]) 25550453Sbostic ++argv; 25650453Sbostic /* FALLTHROUGH */ 25750453Sbostic /* Options w/o arguments, continue with the next option. */ 25850453Sbostic case 'f': 25950453Sbostic case 'r': 26050453Sbostic continue; 2611214Sroot 26250453Sbostic /* Illegal option, return and let getopt handle it. */ 26350453Sbostic default: 26450453Sbostic return; 2651214Sroot } 2661214Sroot } 2671214Sroot } 2681214Sroot 26950453Sbostic static void 27050453Sbostic usage() 27150453Sbostic { 27250453Sbostic (void)fprintf(stderr, 273*52836Sbostic "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n"); 27450453Sbostic exit(1); 2751214Sroot } 276