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 * 4. Neither the name of the University nor the names of its contributors 171590Srgrimes * may be used to endorse or promote products derived from this software 181590Srgrimes * without specific prior written permission. 191590Srgrimes * 201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301590Srgrimes * SUCH DAMAGE. 311590Srgrimes */ 321590Srgrimes 3387712Smarkm#include <sys/cdefs.h> 3487712Smarkm 3587712Smarkm__FBSDID("$FreeBSD$"); 3687712Smarkm 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"; 4187712Smarkm#endif 421590Srgrimes 431590Srgrimes#ifndef lint 4487712Smarkmstatic const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; 4569528Sasmodai#endif 461590Srgrimes 471590Srgrimes#include <sys/types.h> 481590Srgrimes#include <sys/stat.h> 4987712Smarkm 5087712Smarkm#include <err.h> 511590Srgrimes#include <errno.h> 521590Srgrimes#include <stdio.h> 531590Srgrimes#include <stdlib.h> 541590Srgrimes#include <string.h> 5587712Smarkm#include <unistd.h> 5687712Smarkm 571590Srgrimes#include "extern.h" 581590Srgrimes 59160045Sflzint Fflag, fflag, qflag, rflag, rval, no_files; 601590Srgrimes 61227184Sedstatic file_info_t *files; 62137225Spaul 6392922Simpstatic void obsolete(char **); 6492922Simpstatic void usage(void); 651590Srgrimes 661590Srgrimesint 6799983Salfredmain(int argc, char *argv[]) 681590Srgrimes{ 691590Srgrimes struct stat sb; 70193488Sbrian const char *fn; 711590Srgrimes FILE *fp; 7282762Sache off_t off; 731590Srgrimes enum STYLE style; 74137225Spaul int i, ch, first; 75137225Spaul file_info_t *file; 761590Srgrimes char *p; 771590Srgrimes 781590Srgrimes /* 791590Srgrimes * Tail's options are weird. First, -n10 is the same as -n-10, not 801590Srgrimes * -n+10. Second, the number options are 1 based and not offsets, 811590Srgrimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 821590Srgrimes * number options for the -r option specify the number of things that 831590Srgrimes * get displayed, not the starting point in the file. The one major 841590Srgrimes * incompatibility in this version as compared to historical versions 851590Srgrimes * is that the 'r' option couldn't be modified by the -lbc options, 861590Srgrimes * i.e. it was always done in lines. This version treats -rc as a 871590Srgrimes * number of characters in reverse order. Finally, the default for 881590Srgrimes * -r is the entire file, not 10 lines. 891590Srgrimes */ 901590Srgrimes#define ARG(units, forward, backward) { \ 911590Srgrimes if (style) \ 921590Srgrimes usage(); \ 9382762Sache off = strtoll(optarg, &p, 10) * (units); \ 941590Srgrimes if (*p) \ 9517825Speter errx(1, "illegal offset -- %s", optarg); \ 961590Srgrimes switch(optarg[0]) { \ 971590Srgrimes case '+': \ 981590Srgrimes if (off) \ 991590Srgrimes off -= (units); \ 1001590Srgrimes style = (forward); \ 1011590Srgrimes break; \ 1021590Srgrimes case '-': \ 1031590Srgrimes off = -off; \ 1041590Srgrimes /* FALLTHROUGH */ \ 1051590Srgrimes default: \ 1061590Srgrimes style = (backward); \ 1071590Srgrimes break; \ 1081590Srgrimes } \ 1091590Srgrimes} 1101590Srgrimes 1111590Srgrimes obsolete(argv); 1121590Srgrimes style = NOTSET; 113173285Scharnier off = 0; 114160045Sflz while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -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; 131160045Sflz case 'q': 132160045Sflz qflag = 1; 133160045Sflz break; 1341590Srgrimes case 'r': 1351590Srgrimes rflag = 1; 1361590Srgrimes break; 1371590Srgrimes case '?': 1381590Srgrimes default: 1391590Srgrimes usage(); 1401590Srgrimes } 1411590Srgrimes argc -= optind; 1421590Srgrimes argv += optind; 1431590Srgrimes 144137225Spaul no_files = argc ? argc : 1; 1451590Srgrimes 1461590Srgrimes /* 1471590Srgrimes * If displaying in reverse, don't permit follow option, and convert 1481590Srgrimes * style values. 1491590Srgrimes */ 1501590Srgrimes if (rflag) { 1511590Srgrimes if (fflag) 1521590Srgrimes usage(); 1531590Srgrimes if (style == FBYTES) 1541590Srgrimes style = RBYTES; 1551590Srgrimes else if (style == FLINES) 1561590Srgrimes style = RLINES; 1571590Srgrimes } 1581590Srgrimes 1591590Srgrimes /* 1601590Srgrimes * If style not specified, the default is the whole file for -r, and 1611590Srgrimes * the last 10 lines if not -r. 1621590Srgrimes */ 16348566Sbillf if (style == NOTSET) { 1641590Srgrimes if (rflag) { 1651590Srgrimes off = 0; 1661590Srgrimes style = REVERSE; 1671590Srgrimes } else { 1681590Srgrimes off = 10; 1691590Srgrimes style = RLINES; 1701590Srgrimes } 17148566Sbillf } 1721590Srgrimes 173137225Spaul if (*argv && fflag) { 174193488Sbrian files = (struct file_info *) malloc(no_files * 175193488Sbrian sizeof(struct file_info)); 176193488Sbrian if (!files) 177137225Spaul err(1, "Couldn't malloc space for file descriptors."); 178137225Spaul 179193488Sbrian for (file = files; (fn = *argv++); file++) { 180193488Sbrian file->file_name = strdup(fn); 181137225Spaul if (! file->file_name) 182137225Spaul errx(1, "Couldn't malloc space for file name."); 183137225Spaul if ((file->fp = fopen(file->file_name, "r")) == NULL || 184137225Spaul fstat(fileno(file->fp), &file->st)) { 185193488Sbrian if (file->fp != NULL) { 186193488Sbrian fclose(file->fp); 187193488Sbrian file->fp = NULL; 188193488Sbrian } 189193488Sbrian if (!Fflag || errno != ENOENT) 190193488Sbrian ierr(file->file_name); 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) { 199193488Sbrian for (first = 1; (fn = *argv++);) { 200193488Sbrian if ((fp = fopen(fn, "r")) == NULL || 2011590Srgrimes fstat(fileno(fp), &sb)) { 202193488Sbrian ierr(fn); 2031590Srgrimes continue; 2041590Srgrimes } 205160045Sflz if (argc > 1 && !qflag) { 206251565Sjh printfn(fn, !first); 2071590Srgrimes first = 0; 2081590Srgrimes } 2091590Srgrimes 2101590Srgrimes if (rflag) 211193488Sbrian reverse(fp, fn, style, off, &sb); 2121590Srgrimes else 213193488Sbrian forward(fp, fn, style, off, &sb); 2141590Srgrimes } 215137225Spaul } else { 216193488Sbrian fn = "stdin"; 2171590Srgrimes 2181590Srgrimes if (fstat(fileno(stdin), &sb)) { 219193488Sbrian ierr(fn); 2201590Srgrimes exit(1); 2211590Srgrimes } 2221590Srgrimes 223146882Seivind /* 224146882Seivind * Determine if input is a pipe. 4.4BSD will set the SOCKET 225146882Seivind * bit in the st_mode field for pipes. Fix this then. 226146882Seivind */ 227146882Seivind if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 228146882Seivind errno == ESPIPE) { 229146882Seivind errno = 0; 2301590Srgrimes fflag = 0; /* POSIX.2 requires this. */ 2311590Srgrimes } 2321590Srgrimes 2331590Srgrimes if (rflag) 234193488Sbrian reverse(stdin, fn, style, off, &sb); 2351590Srgrimes else 236193488Sbrian forward(stdin, fn, style, off, &sb); 2371590Srgrimes } 2381590Srgrimes exit(rval); 2391590Srgrimes} 2401590Srgrimes 2411590Srgrimes/* 2421590Srgrimes * Convert the obsolete argument form into something that getopt can handle. 24384350Sfenner * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't 2441590Srgrimes * the option argument for a -b, -c or -n option gets converted. 2451590Srgrimes */ 2461590Srgrimesstatic void 24799983Salfredobsolete(char *argv[]) 2481590Srgrimes{ 24969552Sasmodai char *ap, *p, *t; 25069530Sasmodai size_t len; 2511590Srgrimes char *start; 2521590Srgrimes 25387712Smarkm while ((ap = *++argv)) { 2541590Srgrimes /* Return if "--" or not an option of any form. */ 2551590Srgrimes if (ap[0] != '-') { 2561590Srgrimes if (ap[0] != '+') 2571590Srgrimes return; 2581590Srgrimes } else if (ap[1] == '-') 2591590Srgrimes return; 2601590Srgrimes 2611590Srgrimes switch(*++ap) { 2621590Srgrimes /* Old-style option. */ 2631590Srgrimes case '0': case '1': case '2': case '3': case '4': 2641590Srgrimes case '5': case '6': case '7': case '8': case '9': 2651590Srgrimes 2661590Srgrimes /* Malloc space for dash, new option and argument. */ 2671590Srgrimes len = strlen(*argv); 2681590Srgrimes if ((start = p = malloc(len + 3)) == NULL) 26917825Speter err(1, "malloc"); 2701590Srgrimes *p++ = '-'; 2711590Srgrimes 2721590Srgrimes /* 2731590Srgrimes * Go to the end of the option argument. Save off any 2741590Srgrimes * trailing options (-3lf) and translate any trailing 2751590Srgrimes * output style characters. 2761590Srgrimes */ 2771590Srgrimes t = *argv + len - 1; 27884350Sfenner if (*t == 'F' || *t == 'f' || *t == 'r') { 2791590Srgrimes *p++ = *t; 2801590Srgrimes *t-- = '\0'; 2811590Srgrimes } 2821590Srgrimes switch(*t) { 2831590Srgrimes case 'b': 2841590Srgrimes *p++ = 'b'; 2851590Srgrimes *t = '\0'; 2861590Srgrimes break; 2871590Srgrimes case 'c': 2881590Srgrimes *p++ = 'c'; 2891590Srgrimes *t = '\0'; 2901590Srgrimes break; 2911590Srgrimes case 'l': 2921590Srgrimes *t = '\0'; 2931590Srgrimes /* FALLTHROUGH */ 2941590Srgrimes case '0': case '1': case '2': case '3': case '4': 2951590Srgrimes case '5': case '6': case '7': case '8': case '9': 2961590Srgrimes *p++ = 'n'; 2971590Srgrimes break; 2981590Srgrimes default: 29917825Speter errx(1, "illegal option -- %s", *argv); 3001590Srgrimes } 3011590Srgrimes *p++ = *argv[0]; 3021590Srgrimes (void)strcpy(p, ap); 3031590Srgrimes *argv = start; 3041590Srgrimes continue; 3051590Srgrimes 3061590Srgrimes /* 3071590Srgrimes * Options w/ arguments, skip the argument and continue 3081590Srgrimes * with the next option. 3091590Srgrimes */ 3101590Srgrimes case 'b': 3111590Srgrimes case 'c': 3121590Srgrimes case 'n': 3131590Srgrimes if (!ap[1]) 3141590Srgrimes ++argv; 3151590Srgrimes /* FALLTHROUGH */ 3161590Srgrimes /* Options w/o arguments, continue with the next option. */ 31784350Sfenner case 'F': 3181590Srgrimes case 'f': 3191590Srgrimes case 'r': 3201590Srgrimes continue; 3211590Srgrimes 3221590Srgrimes /* Illegal option, return and let getopt handle it. */ 3231590Srgrimes default: 3241590Srgrimes return; 3251590Srgrimes } 3261590Srgrimes } 3271590Srgrimes} 3281590Srgrimes 3291590Srgrimesstatic void 33099983Salfredusage(void) 3311590Srgrimes{ 3321590Srgrimes (void)fprintf(stderr, 333160049Sru "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]" 334160049Sru " [file ...]\n"); 3351590Srgrimes exit(1); 3361590Srgrimes} 337