tail.c revision 69530
11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1991, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * This code is derived from software contributed to Berkeley by 61590Srgrimes * Edward Sze-Tyan Wang. 71590Srgrimes * 81590Srgrimes * Redistribution and use in source and binary forms, with or without 91590Srgrimes * modification, are permitted provided that the following conditions 101590Srgrimes * are met: 111590Srgrimes * 1. Redistributions of source code must retain the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer. 131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141590Srgrimes * notice, this list of conditions and the following disclaimer in the 151590Srgrimes * documentation and/or other materials provided with the distribution. 161590Srgrimes * 3. All advertising materials mentioning features or use of this software 171590Srgrimes * must display the following acknowledgement: 181590Srgrimes * This product includes software developed by the University of 191590Srgrimes * California, Berkeley and its contributors. 201590Srgrimes * 4. Neither the name of the University nor the names of its contributors 211590Srgrimes * may be used to endorse or promote products derived from this software 221590Srgrimes * without specific prior written permission. 231590Srgrimes * 241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341590Srgrimes * SUCH DAMAGE. 351590Srgrimes */ 361590Srgrimes 371590Srgrimes#ifndef lint 3869528Sasmodaistatic const char copyright[] = 391590Srgrimes"@(#) Copyright (c) 1991, 1993\n\ 401590Srgrimes The Regents of the University of California. All rights reserved.\n"; 411590Srgrimes#endif /* not lint */ 421590Srgrimes 431590Srgrimes#ifndef lint 4469528Sasmodai#if 0 451590Srgrimesstatic char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; 4669528Sasmodai#endif 4769528Sasmodaistatic const char rcsid[] = 4869528Sasmodai "$FreeBSD: head/usr.bin/tail/tail.c 69530 2000-12-02 20:21:13Z asmodai $"; 491590Srgrimes#endif /* not lint */ 501590Srgrimes 511590Srgrimes#include <sys/types.h> 521590Srgrimes#include <sys/stat.h> 531590Srgrimes#include <errno.h> 541590Srgrimes#include <unistd.h> 551590Srgrimes#include <stdio.h> 561590Srgrimes#include <stdlib.h> 571590Srgrimes#include <string.h> 5817826Speter#include <err.h> 591590Srgrimes#include "extern.h" 601590Srgrimes 6135081Speterint Fflag, fflag, rflag, rval; 621590Srgrimeschar *fname; 631590Srgrimes 641590Srgrimesstatic void obsolete __P((char **)); 651590Srgrimesstatic void usage __P((void)); 661590Srgrimes 671590Srgrimesint 681590Srgrimesmain(argc, argv) 691590Srgrimes int argc; 701590Srgrimes char *argv[]; 711590Srgrimes{ 721590Srgrimes struct stat sb; 731590Srgrimes FILE *fp; 741590Srgrimes long off; 751590Srgrimes enum STYLE style; 761590Srgrimes int ch, first; 771590Srgrimes char *p; 781590Srgrimes 791590Srgrimes /* 801590Srgrimes * Tail's options are weird. First, -n10 is the same as -n-10, not 811590Srgrimes * -n+10. Second, the number options are 1 based and not offsets, 821590Srgrimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 831590Srgrimes * number options for the -r option specify the number of things that 841590Srgrimes * get displayed, not the starting point in the file. The one major 851590Srgrimes * incompatibility in this version as compared to historical versions 861590Srgrimes * is that the 'r' option couldn't be modified by the -lbc options, 871590Srgrimes * i.e. it was always done in lines. This version treats -rc as a 881590Srgrimes * number of characters in reverse order. Finally, the default for 891590Srgrimes * -r is the entire file, not 10 lines. 901590Srgrimes */ 911590Srgrimes#define ARG(units, forward, backward) { \ 921590Srgrimes if (style) \ 931590Srgrimes usage(); \ 941590Srgrimes off = strtol(optarg, &p, 10) * (units); \ 951590Srgrimes if (*p) \ 9617825Speter errx(1, "illegal offset -- %s", optarg); \ 971590Srgrimes switch(optarg[0]) { \ 981590Srgrimes case '+': \ 991590Srgrimes if (off) \ 1001590Srgrimes off -= (units); \ 1011590Srgrimes style = (forward); \ 1021590Srgrimes break; \ 1031590Srgrimes case '-': \ 1041590Srgrimes off = -off; \ 1051590Srgrimes /* FALLTHROUGH */ \ 1061590Srgrimes default: \ 1071590Srgrimes style = (backward); \ 1081590Srgrimes break; \ 1091590Srgrimes } \ 1101590Srgrimes} 1111590Srgrimes 1121590Srgrimes obsolete(argv); 1131590Srgrimes style = NOTSET; 11435081Speter while ((ch = getopt(argc, argv, "Fb:c:fn:r")) != -1) 1151590Srgrimes switch(ch) { 11635081Speter case 'F': /* -F is superset of (and implies) -f */ 11735081Speter Fflag = fflag = 1; 11835081Speter break; 1191590Srgrimes case 'b': 1201590Srgrimes ARG(512, FBYTES, RBYTES); 1211590Srgrimes break; 1221590Srgrimes case 'c': 1231590Srgrimes ARG(1, FBYTES, RBYTES); 1241590Srgrimes break; 1251590Srgrimes case 'f': 1261590Srgrimes fflag = 1; 1271590Srgrimes break; 1281590Srgrimes case 'n': 1291590Srgrimes ARG(1, FLINES, RLINES); 1301590Srgrimes break; 1311590Srgrimes case 'r': 1321590Srgrimes rflag = 1; 1331590Srgrimes break; 1341590Srgrimes case '?': 1351590Srgrimes default: 1361590Srgrimes usage(); 1371590Srgrimes } 1381590Srgrimes argc -= optind; 1391590Srgrimes argv += optind; 1401590Srgrimes 1411590Srgrimes if (fflag && argc > 1) 14217825Speter errx(1, "-f option only appropriate for a single file"); 1431590Srgrimes 1441590Srgrimes /* 1451590Srgrimes * If displaying in reverse, don't permit follow option, and convert 1461590Srgrimes * style values. 1471590Srgrimes */ 1481590Srgrimes if (rflag) { 1491590Srgrimes if (fflag) 1501590Srgrimes usage(); 1511590Srgrimes if (style == FBYTES) 1521590Srgrimes style = RBYTES; 1531590Srgrimes else if (style == FLINES) 1541590Srgrimes style = RLINES; 1551590Srgrimes } 1561590Srgrimes 1571590Srgrimes /* 1581590Srgrimes * If style not specified, the default is the whole file for -r, and 1591590Srgrimes * the last 10 lines if not -r. 1601590Srgrimes */ 16148566Sbillf if (style == NOTSET) { 1621590Srgrimes if (rflag) { 1631590Srgrimes off = 0; 1641590Srgrimes style = REVERSE; 1651590Srgrimes } else { 1661590Srgrimes off = 10; 1671590Srgrimes style = RLINES; 1681590Srgrimes } 16948566Sbillf } 1701590Srgrimes 1711590Srgrimes if (*argv) 1721590Srgrimes for (first = 1; fname = *argv++;) { 1731590Srgrimes if ((fp = fopen(fname, "r")) == NULL || 1741590Srgrimes fstat(fileno(fp), &sb)) { 1751590Srgrimes ierr(); 1761590Srgrimes continue; 1771590Srgrimes } 1781590Srgrimes if (argc > 1) { 1791590Srgrimes (void)printf("%s==> %s <==\n", 1801590Srgrimes first ? "" : "\n", fname); 1811590Srgrimes first = 0; 1821590Srgrimes (void)fflush(stdout); 1831590Srgrimes } 1841590Srgrimes 1851590Srgrimes if (rflag) 1861590Srgrimes reverse(fp, style, off, &sb); 1871590Srgrimes else 1881590Srgrimes forward(fp, style, off, &sb); 1891590Srgrimes (void)fclose(fp); 1901590Srgrimes } 1911590Srgrimes else { 1921590Srgrimes fname = "stdin"; 1931590Srgrimes 1941590Srgrimes if (fstat(fileno(stdin), &sb)) { 1951590Srgrimes ierr(); 1961590Srgrimes exit(1); 1971590Srgrimes } 1981590Srgrimes 1991590Srgrimes /* 2001590Srgrimes * Determine if input is a pipe. 4.4BSD will set the SOCKET 2011590Srgrimes * bit in the st_mode field for pipes. Fix this then. 2021590Srgrimes */ 2031590Srgrimes if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 2041590Srgrimes errno == ESPIPE) { 2051590Srgrimes errno = 0; 2061590Srgrimes fflag = 0; /* POSIX.2 requires this. */ 2071590Srgrimes } 2081590Srgrimes 2091590Srgrimes if (rflag) 2101590Srgrimes reverse(stdin, style, off, &sb); 2111590Srgrimes else 2121590Srgrimes forward(stdin, style, off, &sb); 2131590Srgrimes } 2141590Srgrimes exit(rval); 2151590Srgrimes} 2161590Srgrimes 2171590Srgrimes/* 2181590Srgrimes * Convert the obsolete argument form into something that getopt can handle. 2191590Srgrimes * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 2201590Srgrimes * the option argument for a -b, -c or -n option gets converted. 2211590Srgrimes */ 2221590Srgrimesstatic void 2231590Srgrimesobsolete(argv) 2241590Srgrimes char *argv[]; 2251590Srgrimes{ 2261590Srgrimes register char *ap, *p, *t; 22769530Sasmodai size_t len; 2281590Srgrimes char *start; 2291590Srgrimes 2301590Srgrimes while (ap = *++argv) { 2311590Srgrimes /* Return if "--" or not an option of any form. */ 2321590Srgrimes if (ap[0] != '-') { 2331590Srgrimes if (ap[0] != '+') 2341590Srgrimes return; 2351590Srgrimes } else if (ap[1] == '-') 2361590Srgrimes return; 2371590Srgrimes 2381590Srgrimes switch(*++ap) { 2391590Srgrimes /* Old-style option. */ 2401590Srgrimes case '0': case '1': case '2': case '3': case '4': 2411590Srgrimes case '5': case '6': case '7': case '8': case '9': 2421590Srgrimes 2431590Srgrimes /* Malloc space for dash, new option and argument. */ 2441590Srgrimes len = strlen(*argv); 2451590Srgrimes if ((start = p = malloc(len + 3)) == NULL) 24617825Speter err(1, "malloc"); 2471590Srgrimes *p++ = '-'; 2481590Srgrimes 2491590Srgrimes /* 2501590Srgrimes * Go to the end of the option argument. Save off any 2511590Srgrimes * trailing options (-3lf) and translate any trailing 2521590Srgrimes * output style characters. 2531590Srgrimes */ 2541590Srgrimes t = *argv + len - 1; 2551590Srgrimes if (*t == 'f' || *t == 'r') { 2561590Srgrimes *p++ = *t; 2571590Srgrimes *t-- = '\0'; 2581590Srgrimes } 2591590Srgrimes switch(*t) { 2601590Srgrimes case 'b': 2611590Srgrimes *p++ = 'b'; 2621590Srgrimes *t = '\0'; 2631590Srgrimes break; 2641590Srgrimes case 'c': 2651590Srgrimes *p++ = 'c'; 2661590Srgrimes *t = '\0'; 2671590Srgrimes break; 2681590Srgrimes case 'l': 2691590Srgrimes *t = '\0'; 2701590Srgrimes /* FALLTHROUGH */ 2711590Srgrimes case '0': case '1': case '2': case '3': case '4': 2721590Srgrimes case '5': case '6': case '7': case '8': case '9': 2731590Srgrimes *p++ = 'n'; 2741590Srgrimes break; 2751590Srgrimes default: 27617825Speter errx(1, "illegal option -- %s", *argv); 2771590Srgrimes } 2781590Srgrimes *p++ = *argv[0]; 2791590Srgrimes (void)strcpy(p, ap); 2801590Srgrimes *argv = start; 2811590Srgrimes continue; 2821590Srgrimes 2831590Srgrimes /* 2841590Srgrimes * Options w/ arguments, skip the argument and continue 2851590Srgrimes * with the next option. 2861590Srgrimes */ 2871590Srgrimes case 'b': 2881590Srgrimes case 'c': 2891590Srgrimes case 'n': 2901590Srgrimes if (!ap[1]) 2911590Srgrimes ++argv; 2921590Srgrimes /* FALLTHROUGH */ 2931590Srgrimes /* Options w/o arguments, continue with the next option. */ 2941590Srgrimes case 'f': 2951590Srgrimes case 'r': 2961590Srgrimes continue; 2971590Srgrimes 2981590Srgrimes /* Illegal option, return and let getopt handle it. */ 2991590Srgrimes default: 3001590Srgrimes return; 3011590Srgrimes } 3021590Srgrimes } 3031590Srgrimes} 3041590Srgrimes 3051590Srgrimesstatic void 3061590Srgrimesusage() 3071590Srgrimes{ 3081590Srgrimes (void)fprintf(stderr, 3091590Srgrimes "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n"); 3101590Srgrimes exit(1); 3111590Srgrimes} 312