tail.c revision 160045
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 3787712Smarkm#include <sys/cdefs.h> 3887712Smarkm 3987712Smarkm__FBSDID("$FreeBSD: head/usr.bin/tail/tail.c 160045 2006-06-29 22:07:49Z flz $"); 4087712Smarkm 411590Srgrimes#ifndef lint 4269528Sasmodaistatic const char copyright[] = 431590Srgrimes"@(#) Copyright (c) 1991, 1993\n\ 441590Srgrimes The Regents of the University of California. All rights reserved.\n"; 4587712Smarkm#endif 461590Srgrimes 471590Srgrimes#ifndef lint 4887712Smarkmstatic const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; 4969528Sasmodai#endif 501590Srgrimes 511590Srgrimes#include <sys/types.h> 521590Srgrimes#include <sys/stat.h> 5387712Smarkm 5487712Smarkm#include <err.h> 551590Srgrimes#include <errno.h> 561590Srgrimes#include <stdio.h> 571590Srgrimes#include <stdlib.h> 581590Srgrimes#include <string.h> 5987712Smarkm#include <unistd.h> 6087712Smarkm 611590Srgrimes#include "extern.h" 621590Srgrimes 63160045Sflzint Fflag, fflag, qflag, rflag, rval, no_files; 6487712Smarkmconst char *fname; 651590Srgrimes 66137225Spaulfile_info_t *files; 67137225Spaul 6892922Simpstatic void obsolete(char **); 6992922Simpstatic void usage(void); 701590Srgrimes 711590Srgrimesint 7299983Salfredmain(int argc, char *argv[]) 731590Srgrimes{ 741590Srgrimes struct stat sb; 751590Srgrimes FILE *fp; 7682762Sache off_t off; 771590Srgrimes enum STYLE style; 78137225Spaul int i, ch, first; 79137225Spaul file_info_t *file; 801590Srgrimes char *p; 811590Srgrimes 821590Srgrimes /* 831590Srgrimes * Tail's options are weird. First, -n10 is the same as -n-10, not 841590Srgrimes * -n+10. Second, the number options are 1 based and not offsets, 851590Srgrimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 861590Srgrimes * number options for the -r option specify the number of things that 871590Srgrimes * get displayed, not the starting point in the file. The one major 881590Srgrimes * incompatibility in this version as compared to historical versions 891590Srgrimes * is that the 'r' option couldn't be modified by the -lbc options, 901590Srgrimes * i.e. it was always done in lines. This version treats -rc as a 911590Srgrimes * number of characters in reverse order. Finally, the default for 921590Srgrimes * -r is the entire file, not 10 lines. 931590Srgrimes */ 941590Srgrimes#define ARG(units, forward, backward) { \ 951590Srgrimes if (style) \ 961590Srgrimes usage(); \ 9782762Sache off = strtoll(optarg, &p, 10) * (units); \ 981590Srgrimes if (*p) \ 9917825Speter errx(1, "illegal offset -- %s", optarg); \ 1001590Srgrimes switch(optarg[0]) { \ 1011590Srgrimes case '+': \ 1021590Srgrimes if (off) \ 1031590Srgrimes off -= (units); \ 1041590Srgrimes style = (forward); \ 1051590Srgrimes break; \ 1061590Srgrimes case '-': \ 1071590Srgrimes off = -off; \ 1081590Srgrimes /* FALLTHROUGH */ \ 1091590Srgrimes default: \ 1101590Srgrimes style = (backward); \ 1111590Srgrimes break; \ 1121590Srgrimes } \ 1131590Srgrimes} 1141590Srgrimes 1151590Srgrimes obsolete(argv); 1161590Srgrimes style = NOTSET; 117160045Sflz while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1) 1181590Srgrimes switch(ch) { 11935081Speter case 'F': /* -F is superset of (and implies) -f */ 12035081Speter Fflag = fflag = 1; 12135081Speter break; 1221590Srgrimes case 'b': 1231590Srgrimes ARG(512, FBYTES, RBYTES); 1241590Srgrimes break; 1251590Srgrimes case 'c': 1261590Srgrimes ARG(1, FBYTES, RBYTES); 1271590Srgrimes break; 1281590Srgrimes case 'f': 1291590Srgrimes fflag = 1; 1301590Srgrimes break; 1311590Srgrimes case 'n': 1321590Srgrimes ARG(1, FLINES, RLINES); 1331590Srgrimes break; 134160045Sflz case 'q': 135160045Sflz qflag = 1; 136160045Sflz break; 1371590Srgrimes case 'r': 1381590Srgrimes rflag = 1; 1391590Srgrimes break; 1401590Srgrimes case '?': 1411590Srgrimes default: 1421590Srgrimes usage(); 1431590Srgrimes } 1441590Srgrimes argc -= optind; 1451590Srgrimes argv += optind; 1461590Srgrimes 147137225Spaul no_files = argc ? argc : 1; 1481590Srgrimes 1491590Srgrimes /* 1501590Srgrimes * If displaying in reverse, don't permit follow option, and convert 1511590Srgrimes * style values. 1521590Srgrimes */ 1531590Srgrimes if (rflag) { 1541590Srgrimes if (fflag) 1551590Srgrimes usage(); 1561590Srgrimes if (style == FBYTES) 1571590Srgrimes style = RBYTES; 1581590Srgrimes else if (style == FLINES) 1591590Srgrimes style = RLINES; 1601590Srgrimes } 1611590Srgrimes 1621590Srgrimes /* 1631590Srgrimes * If style not specified, the default is the whole file for -r, and 1641590Srgrimes * the last 10 lines if not -r. 1651590Srgrimes */ 16648566Sbillf if (style == NOTSET) { 1671590Srgrimes if (rflag) { 1681590Srgrimes off = 0; 1691590Srgrimes style = REVERSE; 1701590Srgrimes } else { 1711590Srgrimes off = 10; 1721590Srgrimes style = RLINES; 1731590Srgrimes } 17448566Sbillf } 1751590Srgrimes 176137225Spaul if (*argv && fflag) { 177137225Spaul files = (struct file_info *) malloc(no_files * sizeof(struct file_info)); 178137225Spaul if (! files) 179137225Spaul err(1, "Couldn't malloc space for file descriptors."); 180137225Spaul 181137225Spaul for (file = files; (fname = *argv++); file++) { 182137225Spaul file->file_name = malloc(strlen(fname)+1); 183137225Spaul if (! file->file_name) 184137225Spaul errx(1, "Couldn't malloc space for file name."); 185137225Spaul strncpy(file->file_name, fname, strlen(fname)+1); 186137225Spaul if ((file->fp = fopen(file->file_name, "r")) == NULL || 187137225Spaul fstat(fileno(file->fp), &file->st)) { 188137225Spaul file->fp = NULL; 189137225Spaul ierr(); 190137225Spaul continue; 191137225Spaul } 192137225Spaul } 193137225Spaul follow(files, style, off); 194137225Spaul for (i = 0, file = files; i < no_files; i++, file++) { 195137225Spaul free(file->file_name); 196137225Spaul } 197137225Spaul free(files); 198137225Spaul } else if (*argv) { 19987712Smarkm for (first = 1; (fname = *argv++);) { 2001590Srgrimes if ((fp = fopen(fname, "r")) == NULL || 2011590Srgrimes fstat(fileno(fp), &sb)) { 2021590Srgrimes ierr(); 2031590Srgrimes continue; 2041590Srgrimes } 205160045Sflz if (argc > 1 && !qflag) { 2061590Srgrimes (void)printf("%s==> %s <==\n", 2071590Srgrimes first ? "" : "\n", fname); 2081590Srgrimes first = 0; 2091590Srgrimes (void)fflush(stdout); 2101590Srgrimes } 2111590Srgrimes 2121590Srgrimes if (rflag) 2131590Srgrimes reverse(fp, style, off, &sb); 2141590Srgrimes else 2151590Srgrimes forward(fp, style, off, &sb); 2161590Srgrimes } 217137225Spaul } else { 2181590Srgrimes fname = "stdin"; 2191590Srgrimes 2201590Srgrimes if (fstat(fileno(stdin), &sb)) { 2211590Srgrimes ierr(); 2221590Srgrimes exit(1); 2231590Srgrimes } 2241590Srgrimes 225146882Seivind /* 226146882Seivind * Determine if input is a pipe. 4.4BSD will set the SOCKET 227146882Seivind * bit in the st_mode field for pipes. Fix this then. 228146882Seivind */ 229146882Seivind if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 230146882Seivind errno == ESPIPE) { 231146882Seivind errno = 0; 2321590Srgrimes fflag = 0; /* POSIX.2 requires this. */ 2331590Srgrimes } 2341590Srgrimes 2351590Srgrimes if (rflag) 2361590Srgrimes reverse(stdin, style, off, &sb); 2371590Srgrimes else 2381590Srgrimes forward(stdin, style, off, &sb); 2391590Srgrimes } 2401590Srgrimes exit(rval); 2411590Srgrimes} 2421590Srgrimes 2431590Srgrimes/* 2441590Srgrimes * Convert the obsolete argument form into something that getopt can handle. 24584350Sfenner * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't 2461590Srgrimes * the option argument for a -b, -c or -n option gets converted. 2471590Srgrimes */ 2481590Srgrimesstatic void 24999983Salfredobsolete(char *argv[]) 2501590Srgrimes{ 25169552Sasmodai char *ap, *p, *t; 25269530Sasmodai size_t len; 2531590Srgrimes char *start; 2541590Srgrimes 25587712Smarkm while ((ap = *++argv)) { 2561590Srgrimes /* Return if "--" or not an option of any form. */ 2571590Srgrimes if (ap[0] != '-') { 2581590Srgrimes if (ap[0] != '+') 2591590Srgrimes return; 2601590Srgrimes } else if (ap[1] == '-') 2611590Srgrimes return; 2621590Srgrimes 2631590Srgrimes switch(*++ap) { 2641590Srgrimes /* Old-style option. */ 2651590Srgrimes case '0': case '1': case '2': case '3': case '4': 2661590Srgrimes case '5': case '6': case '7': case '8': case '9': 2671590Srgrimes 2681590Srgrimes /* Malloc space for dash, new option and argument. */ 2691590Srgrimes len = strlen(*argv); 2701590Srgrimes if ((start = p = malloc(len + 3)) == NULL) 27117825Speter err(1, "malloc"); 2721590Srgrimes *p++ = '-'; 2731590Srgrimes 2741590Srgrimes /* 2751590Srgrimes * Go to the end of the option argument. Save off any 2761590Srgrimes * trailing options (-3lf) and translate any trailing 2771590Srgrimes * output style characters. 2781590Srgrimes */ 2791590Srgrimes t = *argv + len - 1; 28084350Sfenner if (*t == 'F' || *t == 'f' || *t == 'r') { 2811590Srgrimes *p++ = *t; 2821590Srgrimes *t-- = '\0'; 2831590Srgrimes } 2841590Srgrimes switch(*t) { 2851590Srgrimes case 'b': 2861590Srgrimes *p++ = 'b'; 2871590Srgrimes *t = '\0'; 2881590Srgrimes break; 2891590Srgrimes case 'c': 2901590Srgrimes *p++ = 'c'; 2911590Srgrimes *t = '\0'; 2921590Srgrimes break; 2931590Srgrimes case 'l': 2941590Srgrimes *t = '\0'; 2951590Srgrimes /* FALLTHROUGH */ 2961590Srgrimes case '0': case '1': case '2': case '3': case '4': 2971590Srgrimes case '5': case '6': case '7': case '8': case '9': 2981590Srgrimes *p++ = 'n'; 2991590Srgrimes break; 3001590Srgrimes default: 30117825Speter errx(1, "illegal option -- %s", *argv); 3021590Srgrimes } 3031590Srgrimes *p++ = *argv[0]; 3041590Srgrimes (void)strcpy(p, ap); 3051590Srgrimes *argv = start; 3061590Srgrimes continue; 3071590Srgrimes 3081590Srgrimes /* 3091590Srgrimes * Options w/ arguments, skip the argument and continue 3101590Srgrimes * with the next option. 3111590Srgrimes */ 3121590Srgrimes case 'b': 3131590Srgrimes case 'c': 3141590Srgrimes case 'n': 3151590Srgrimes if (!ap[1]) 3161590Srgrimes ++argv; 3171590Srgrimes /* FALLTHROUGH */ 3181590Srgrimes /* Options w/o arguments, continue with the next option. */ 31984350Sfenner case 'F': 3201590Srgrimes case 'f': 3211590Srgrimes case 'r': 3221590Srgrimes continue; 3231590Srgrimes 3241590Srgrimes /* Illegal option, return and let getopt handle it. */ 3251590Srgrimes default: 3261590Srgrimes return; 3271590Srgrimes } 3281590Srgrimes } 3291590Srgrimes} 3301590Srgrimes 3311590Srgrimesstatic void 33299983Salfredusage(void) 3331590Srgrimes{ 3341590Srgrimes (void)fprintf(stderr, 33571514Sru "usage: tail [-F | -f | -r] [-b # | -c # | -n #] [file ...]\n"); 3361590Srgrimes exit(1); 3371590Srgrimes} 338