tail.c revision 17825
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 381590Srgrimesstatic 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 441590Srgrimesstatic char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; 451590Srgrimes#endif /* not lint */ 461590Srgrimes 471590Srgrimes#include <sys/types.h> 481590Srgrimes#include <sys/stat.h> 491590Srgrimes#include <errno.h> 501590Srgrimes#include <unistd.h> 511590Srgrimes#include <stdio.h> 521590Srgrimes#include <stdlib.h> 531590Srgrimes#include <string.h> 541590Srgrimes#include "extern.h" 551590Srgrimes 561590Srgrimesint fflag, rflag, rval; 571590Srgrimeschar *fname; 581590Srgrimes 591590Srgrimesstatic void obsolete __P((char **)); 601590Srgrimesstatic void usage __P((void)); 611590Srgrimes 621590Srgrimesint 631590Srgrimesmain(argc, argv) 641590Srgrimes int argc; 651590Srgrimes char *argv[]; 661590Srgrimes{ 671590Srgrimes struct stat sb; 681590Srgrimes FILE *fp; 691590Srgrimes long off; 701590Srgrimes enum STYLE style; 711590Srgrimes int ch, first; 721590Srgrimes char *p; 731590Srgrimes 741590Srgrimes /* 751590Srgrimes * Tail's options are weird. First, -n10 is the same as -n-10, not 761590Srgrimes * -n+10. Second, the number options are 1 based and not offsets, 771590Srgrimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 781590Srgrimes * number options for the -r option specify the number of things that 791590Srgrimes * get displayed, not the starting point in the file. The one major 801590Srgrimes * incompatibility in this version as compared to historical versions 811590Srgrimes * is that the 'r' option couldn't be modified by the -lbc options, 821590Srgrimes * i.e. it was always done in lines. This version treats -rc as a 831590Srgrimes * number of characters in reverse order. Finally, the default for 841590Srgrimes * -r is the entire file, not 10 lines. 851590Srgrimes */ 861590Srgrimes#define ARG(units, forward, backward) { \ 871590Srgrimes if (style) \ 881590Srgrimes usage(); \ 891590Srgrimes off = strtol(optarg, &p, 10) * (units); \ 901590Srgrimes if (*p) \ 9117825Speter errx(1, "illegal offset -- %s", optarg); \ 921590Srgrimes switch(optarg[0]) { \ 931590Srgrimes case '+': \ 941590Srgrimes if (off) \ 951590Srgrimes off -= (units); \ 961590Srgrimes style = (forward); \ 971590Srgrimes break; \ 981590Srgrimes case '-': \ 991590Srgrimes off = -off; \ 1001590Srgrimes /* FALLTHROUGH */ \ 1011590Srgrimes default: \ 1021590Srgrimes style = (backward); \ 1031590Srgrimes break; \ 1041590Srgrimes } \ 1051590Srgrimes} 1061590Srgrimes 1071590Srgrimes obsolete(argv); 1081590Srgrimes style = NOTSET; 1091590Srgrimes while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) 1101590Srgrimes switch(ch) { 1111590Srgrimes case 'b': 1121590Srgrimes ARG(512, FBYTES, RBYTES); 1131590Srgrimes break; 1141590Srgrimes case 'c': 1151590Srgrimes ARG(1, FBYTES, RBYTES); 1161590Srgrimes break; 1171590Srgrimes case 'f': 1181590Srgrimes fflag = 1; 1191590Srgrimes break; 1201590Srgrimes case 'n': 1211590Srgrimes ARG(1, FLINES, RLINES); 1221590Srgrimes break; 1231590Srgrimes case 'r': 1241590Srgrimes rflag = 1; 1251590Srgrimes break; 1261590Srgrimes case '?': 1271590Srgrimes default: 1281590Srgrimes usage(); 1291590Srgrimes } 1301590Srgrimes argc -= optind; 1311590Srgrimes argv += optind; 1321590Srgrimes 1331590Srgrimes if (fflag && argc > 1) 13417825Speter errx(1, "-f option only appropriate for a single file"); 1351590Srgrimes 1361590Srgrimes /* 1371590Srgrimes * If displaying in reverse, don't permit follow option, and convert 1381590Srgrimes * style values. 1391590Srgrimes */ 1401590Srgrimes if (rflag) { 1411590Srgrimes if (fflag) 1421590Srgrimes usage(); 1431590Srgrimes if (style == FBYTES) 1441590Srgrimes style = RBYTES; 1451590Srgrimes else if (style == FLINES) 1461590Srgrimes style = RLINES; 1471590Srgrimes } 1481590Srgrimes 1491590Srgrimes /* 1501590Srgrimes * If style not specified, the default is the whole file for -r, and 1511590Srgrimes * the last 10 lines if not -r. 1521590Srgrimes */ 1531590Srgrimes if (style == NOTSET) 1541590Srgrimes if (rflag) { 1551590Srgrimes off = 0; 1561590Srgrimes style = REVERSE; 1571590Srgrimes } else { 1581590Srgrimes off = 10; 1591590Srgrimes style = RLINES; 1601590Srgrimes } 1611590Srgrimes 1621590Srgrimes if (*argv) 1631590Srgrimes for (first = 1; fname = *argv++;) { 1641590Srgrimes if ((fp = fopen(fname, "r")) == NULL || 1651590Srgrimes fstat(fileno(fp), &sb)) { 1661590Srgrimes ierr(); 1671590Srgrimes continue; 1681590Srgrimes } 1691590Srgrimes if (argc > 1) { 1701590Srgrimes (void)printf("%s==> %s <==\n", 1711590Srgrimes first ? "" : "\n", fname); 1721590Srgrimes first = 0; 1731590Srgrimes (void)fflush(stdout); 1741590Srgrimes } 1751590Srgrimes 1761590Srgrimes if (rflag) 1771590Srgrimes reverse(fp, style, off, &sb); 1781590Srgrimes else 1791590Srgrimes forward(fp, style, off, &sb); 1801590Srgrimes (void)fclose(fp); 1811590Srgrimes } 1821590Srgrimes else { 1831590Srgrimes fname = "stdin"; 1841590Srgrimes 1851590Srgrimes if (fstat(fileno(stdin), &sb)) { 1861590Srgrimes ierr(); 1871590Srgrimes exit(1); 1881590Srgrimes } 1891590Srgrimes 1901590Srgrimes /* 1911590Srgrimes * Determine if input is a pipe. 4.4BSD will set the SOCKET 1921590Srgrimes * bit in the st_mode field for pipes. Fix this then. 1931590Srgrimes */ 1941590Srgrimes if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 1951590Srgrimes errno == ESPIPE) { 1961590Srgrimes errno = 0; 1971590Srgrimes fflag = 0; /* POSIX.2 requires this. */ 1981590Srgrimes } 1991590Srgrimes 2001590Srgrimes if (rflag) 2011590Srgrimes reverse(stdin, style, off, &sb); 2021590Srgrimes else 2031590Srgrimes forward(stdin, style, off, &sb); 2041590Srgrimes } 2051590Srgrimes exit(rval); 2061590Srgrimes} 2071590Srgrimes 2081590Srgrimes/* 2091590Srgrimes * Convert the obsolete argument form into something that getopt can handle. 2101590Srgrimes * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 2111590Srgrimes * the option argument for a -b, -c or -n option gets converted. 2121590Srgrimes */ 2131590Srgrimesstatic void 2141590Srgrimesobsolete(argv) 2151590Srgrimes char *argv[]; 2161590Srgrimes{ 2171590Srgrimes register char *ap, *p, *t; 2181590Srgrimes int len; 2191590Srgrimes char *start; 2201590Srgrimes 2211590Srgrimes while (ap = *++argv) { 2221590Srgrimes /* Return if "--" or not an option of any form. */ 2231590Srgrimes if (ap[0] != '-') { 2241590Srgrimes if (ap[0] != '+') 2251590Srgrimes return; 2261590Srgrimes } else if (ap[1] == '-') 2271590Srgrimes return; 2281590Srgrimes 2291590Srgrimes switch(*++ap) { 2301590Srgrimes /* Old-style option. */ 2311590Srgrimes case '0': case '1': case '2': case '3': case '4': 2321590Srgrimes case '5': case '6': case '7': case '8': case '9': 2331590Srgrimes 2341590Srgrimes /* Malloc space for dash, new option and argument. */ 2351590Srgrimes len = strlen(*argv); 2361590Srgrimes if ((start = p = malloc(len + 3)) == NULL) 23717825Speter err(1, "malloc"); 2381590Srgrimes *p++ = '-'; 2391590Srgrimes 2401590Srgrimes /* 2411590Srgrimes * Go to the end of the option argument. Save off any 2421590Srgrimes * trailing options (-3lf) and translate any trailing 2431590Srgrimes * output style characters. 2441590Srgrimes */ 2451590Srgrimes t = *argv + len - 1; 2461590Srgrimes if (*t == 'f' || *t == 'r') { 2471590Srgrimes *p++ = *t; 2481590Srgrimes *t-- = '\0'; 2491590Srgrimes } 2501590Srgrimes switch(*t) { 2511590Srgrimes case 'b': 2521590Srgrimes *p++ = 'b'; 2531590Srgrimes *t = '\0'; 2541590Srgrimes break; 2551590Srgrimes case 'c': 2561590Srgrimes *p++ = 'c'; 2571590Srgrimes *t = '\0'; 2581590Srgrimes break; 2591590Srgrimes case 'l': 2601590Srgrimes *t = '\0'; 2611590Srgrimes /* FALLTHROUGH */ 2621590Srgrimes case '0': case '1': case '2': case '3': case '4': 2631590Srgrimes case '5': case '6': case '7': case '8': case '9': 2641590Srgrimes *p++ = 'n'; 2651590Srgrimes break; 2661590Srgrimes default: 26717825Speter errx(1, "illegal option -- %s", *argv); 2681590Srgrimes } 2691590Srgrimes *p++ = *argv[0]; 2701590Srgrimes (void)strcpy(p, ap); 2711590Srgrimes *argv = start; 2721590Srgrimes continue; 2731590Srgrimes 2741590Srgrimes /* 2751590Srgrimes * Options w/ arguments, skip the argument and continue 2761590Srgrimes * with the next option. 2771590Srgrimes */ 2781590Srgrimes case 'b': 2791590Srgrimes case 'c': 2801590Srgrimes case 'n': 2811590Srgrimes if (!ap[1]) 2821590Srgrimes ++argv; 2831590Srgrimes /* FALLTHROUGH */ 2841590Srgrimes /* Options w/o arguments, continue with the next option. */ 2851590Srgrimes case 'f': 2861590Srgrimes case 'r': 2871590Srgrimes continue; 2881590Srgrimes 2891590Srgrimes /* Illegal option, return and let getopt handle it. */ 2901590Srgrimes default: 2911590Srgrimes return; 2921590Srgrimes } 2931590Srgrimes } 2941590Srgrimes} 2951590Srgrimes 2961590Srgrimesstatic void 2971590Srgrimesusage() 2981590Srgrimes{ 2991590Srgrimes (void)fprintf(stderr, 3001590Srgrimes "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n"); 3011590Srgrimes exit(1); 3021590Srgrimes} 303