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: stable/11/usr.bin/tail/tail.c 332482 2018-04-13 17:57:00Z kevans $"); 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> 52332482Skevans#include <getopt.h> 531590Srgrimes#include <stdio.h> 541590Srgrimes#include <stdlib.h> 551590Srgrimes#include <string.h> 5687712Smarkm#include <unistd.h> 5787712Smarkm 581590Srgrimes#include "extern.h" 591590Srgrimes 60160045Sflzint Fflag, fflag, qflag, rflag, rval, no_files; 611590Srgrimes 62227184Sedstatic file_info_t *files; 63137225Spaul 6492922Simpstatic void obsolete(char **); 6592922Simpstatic void usage(void); 661590Srgrimes 67332482Skevansstatic const struct option long_opts[] = 68332482Skevans{ 69332482Skevans {"blocks", required_argument, NULL, 'b'}, 70332482Skevans {"bytes", required_argument, NULL, 'c'}, 71332482Skevans {"lines", required_argument, NULL, 'n'}, 72332482Skevans {NULL, no_argument, NULL, 0} 73332482Skevans}; 74332482Skevans 751590Srgrimesint 7699983Salfredmain(int argc, char *argv[]) 771590Srgrimes{ 781590Srgrimes struct stat sb; 79193488Sbrian const char *fn; 801590Srgrimes FILE *fp; 8182762Sache off_t off; 821590Srgrimes enum STYLE style; 83137225Spaul int i, ch, first; 84137225Spaul file_info_t *file; 851590Srgrimes char *p; 861590Srgrimes 871590Srgrimes /* 881590Srgrimes * Tail's options are weird. First, -n10 is the same as -n-10, not 891590Srgrimes * -n+10. Second, the number options are 1 based and not offsets, 901590Srgrimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 911590Srgrimes * number options for the -r option specify the number of things that 921590Srgrimes * get displayed, not the starting point in the file. The one major 931590Srgrimes * incompatibility in this version as compared to historical versions 941590Srgrimes * is that the 'r' option couldn't be modified by the -lbc options, 951590Srgrimes * i.e. it was always done in lines. This version treats -rc as a 961590Srgrimes * number of characters in reverse order. Finally, the default for 971590Srgrimes * -r is the entire file, not 10 lines. 981590Srgrimes */ 991590Srgrimes#define ARG(units, forward, backward) { \ 1001590Srgrimes if (style) \ 1011590Srgrimes usage(); \ 10282762Sache off = strtoll(optarg, &p, 10) * (units); \ 1031590Srgrimes if (*p) \ 10417825Speter errx(1, "illegal offset -- %s", optarg); \ 1051590Srgrimes switch(optarg[0]) { \ 1061590Srgrimes case '+': \ 1071590Srgrimes if (off) \ 1081590Srgrimes off -= (units); \ 1091590Srgrimes style = (forward); \ 1101590Srgrimes break; \ 1111590Srgrimes case '-': \ 1121590Srgrimes off = -off; \ 1131590Srgrimes /* FALLTHROUGH */ \ 1141590Srgrimes default: \ 1151590Srgrimes style = (backward); \ 1161590Srgrimes break; \ 1171590Srgrimes } \ 1181590Srgrimes} 1191590Srgrimes 1201590Srgrimes obsolete(argv); 1211590Srgrimes style = NOTSET; 122173285Scharnier off = 0; 123332482Skevans while ((ch = getopt_long(argc, argv, "+Fb:c:fn:qr", long_opts, NULL)) != 124332482Skevans -1) 1251590Srgrimes switch(ch) { 12635081Speter case 'F': /* -F is superset of (and implies) -f */ 12735081Speter Fflag = fflag = 1; 12835081Speter break; 1291590Srgrimes case 'b': 1301590Srgrimes ARG(512, FBYTES, RBYTES); 1311590Srgrimes break; 1321590Srgrimes case 'c': 1331590Srgrimes ARG(1, FBYTES, RBYTES); 1341590Srgrimes break; 1351590Srgrimes case 'f': 1361590Srgrimes fflag = 1; 1371590Srgrimes break; 1381590Srgrimes case 'n': 1391590Srgrimes ARG(1, FLINES, RLINES); 1401590Srgrimes break; 141160045Sflz case 'q': 142160045Sflz qflag = 1; 143160045Sflz break; 1441590Srgrimes case 'r': 1451590Srgrimes rflag = 1; 1461590Srgrimes break; 1471590Srgrimes case '?': 1481590Srgrimes default: 1491590Srgrimes usage(); 1501590Srgrimes } 1511590Srgrimes argc -= optind; 1521590Srgrimes argv += optind; 1531590Srgrimes 154137225Spaul no_files = argc ? argc : 1; 1551590Srgrimes 1561590Srgrimes /* 1571590Srgrimes * If displaying in reverse, don't permit follow option, and convert 1581590Srgrimes * style values. 1591590Srgrimes */ 1601590Srgrimes if (rflag) { 1611590Srgrimes if (fflag) 1621590Srgrimes usage(); 1631590Srgrimes if (style == FBYTES) 1641590Srgrimes style = RBYTES; 1651590Srgrimes else if (style == FLINES) 1661590Srgrimes style = RLINES; 1671590Srgrimes } 1681590Srgrimes 1691590Srgrimes /* 1701590Srgrimes * If style not specified, the default is the whole file for -r, and 1711590Srgrimes * the last 10 lines if not -r. 1721590Srgrimes */ 17348566Sbillf if (style == NOTSET) { 1741590Srgrimes if (rflag) { 1751590Srgrimes off = 0; 1761590Srgrimes style = REVERSE; 1771590Srgrimes } else { 1781590Srgrimes off = 10; 1791590Srgrimes style = RLINES; 1801590Srgrimes } 18148566Sbillf } 1821590Srgrimes 183137225Spaul if (*argv && fflag) { 184193488Sbrian files = (struct file_info *) malloc(no_files * 185193488Sbrian sizeof(struct file_info)); 186193488Sbrian if (!files) 187137225Spaul err(1, "Couldn't malloc space for file descriptors."); 188137225Spaul 189193488Sbrian for (file = files; (fn = *argv++); file++) { 190193488Sbrian file->file_name = strdup(fn); 191137225Spaul if (! file->file_name) 192137225Spaul errx(1, "Couldn't malloc space for file name."); 193137225Spaul if ((file->fp = fopen(file->file_name, "r")) == NULL || 194137225Spaul fstat(fileno(file->fp), &file->st)) { 195193488Sbrian if (file->fp != NULL) { 196193488Sbrian fclose(file->fp); 197193488Sbrian file->fp = NULL; 198193488Sbrian } 199193488Sbrian if (!Fflag || errno != ENOENT) 200193488Sbrian ierr(file->file_name); 201137225Spaul } 202137225Spaul } 203137225Spaul follow(files, style, off); 204137225Spaul for (i = 0, file = files; i < no_files; i++, file++) { 205137225Spaul free(file->file_name); 206137225Spaul } 207137225Spaul free(files); 208137225Spaul } else if (*argv) { 209193488Sbrian for (first = 1; (fn = *argv++);) { 210193488Sbrian if ((fp = fopen(fn, "r")) == NULL || 2111590Srgrimes fstat(fileno(fp), &sb)) { 212193488Sbrian ierr(fn); 2131590Srgrimes continue; 2141590Srgrimes } 215160045Sflz if (argc > 1 && !qflag) { 216251565Sjh printfn(fn, !first); 2171590Srgrimes first = 0; 2181590Srgrimes } 2191590Srgrimes 2201590Srgrimes if (rflag) 221193488Sbrian reverse(fp, fn, style, off, &sb); 2221590Srgrimes else 223193488Sbrian forward(fp, fn, style, off, &sb); 2241590Srgrimes } 225137225Spaul } else { 226193488Sbrian fn = "stdin"; 2271590Srgrimes 2281590Srgrimes if (fstat(fileno(stdin), &sb)) { 229193488Sbrian ierr(fn); 2301590Srgrimes exit(1); 2311590Srgrimes } 2321590Srgrimes 233146882Seivind /* 234146882Seivind * Determine if input is a pipe. 4.4BSD will set the SOCKET 235146882Seivind * bit in the st_mode field for pipes. Fix this then. 236146882Seivind */ 237146882Seivind if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 238146882Seivind errno == ESPIPE) { 239146882Seivind errno = 0; 2401590Srgrimes fflag = 0; /* POSIX.2 requires this. */ 2411590Srgrimes } 2421590Srgrimes 2431590Srgrimes if (rflag) 244193488Sbrian reverse(stdin, fn, style, off, &sb); 2451590Srgrimes else 246193488Sbrian forward(stdin, fn, style, off, &sb); 2471590Srgrimes } 2481590Srgrimes exit(rval); 2491590Srgrimes} 2501590Srgrimes 2511590Srgrimes/* 2521590Srgrimes * Convert the obsolete argument form into something that getopt can handle. 25384350Sfenner * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't 2541590Srgrimes * the option argument for a -b, -c or -n option gets converted. 2551590Srgrimes */ 2561590Srgrimesstatic void 25799983Salfredobsolete(char *argv[]) 2581590Srgrimes{ 25969552Sasmodai char *ap, *p, *t; 26069530Sasmodai size_t len; 2611590Srgrimes char *start; 2621590Srgrimes 26387712Smarkm while ((ap = *++argv)) { 2641590Srgrimes /* Return if "--" or not an option of any form. */ 2651590Srgrimes if (ap[0] != '-') { 2661590Srgrimes if (ap[0] != '+') 2671590Srgrimes return; 2681590Srgrimes } else if (ap[1] == '-') 2691590Srgrimes return; 2701590Srgrimes 2711590Srgrimes switch(*++ap) { 2721590Srgrimes /* Old-style option. */ 2731590Srgrimes case '0': case '1': case '2': case '3': case '4': 2741590Srgrimes case '5': case '6': case '7': case '8': case '9': 2751590Srgrimes 2761590Srgrimes /* Malloc space for dash, new option and argument. */ 2771590Srgrimes len = strlen(*argv); 2781590Srgrimes if ((start = p = malloc(len + 3)) == NULL) 27917825Speter err(1, "malloc"); 2801590Srgrimes *p++ = '-'; 2811590Srgrimes 2821590Srgrimes /* 2831590Srgrimes * Go to the end of the option argument. Save off any 2841590Srgrimes * trailing options (-3lf) and translate any trailing 2851590Srgrimes * output style characters. 2861590Srgrimes */ 2871590Srgrimes t = *argv + len - 1; 28884350Sfenner if (*t == 'F' || *t == 'f' || *t == 'r') { 2891590Srgrimes *p++ = *t; 2901590Srgrimes *t-- = '\0'; 2911590Srgrimes } 2921590Srgrimes switch(*t) { 2931590Srgrimes case 'b': 2941590Srgrimes *p++ = 'b'; 2951590Srgrimes *t = '\0'; 2961590Srgrimes break; 2971590Srgrimes case 'c': 2981590Srgrimes *p++ = 'c'; 2991590Srgrimes *t = '\0'; 3001590Srgrimes break; 3011590Srgrimes case 'l': 3021590Srgrimes *t = '\0'; 3031590Srgrimes /* FALLTHROUGH */ 3041590Srgrimes case '0': case '1': case '2': case '3': case '4': 3051590Srgrimes case '5': case '6': case '7': case '8': case '9': 3061590Srgrimes *p++ = 'n'; 3071590Srgrimes break; 3081590Srgrimes default: 30917825Speter errx(1, "illegal option -- %s", *argv); 3101590Srgrimes } 3111590Srgrimes *p++ = *argv[0]; 3121590Srgrimes (void)strcpy(p, ap); 3131590Srgrimes *argv = start; 3141590Srgrimes continue; 3151590Srgrimes 3161590Srgrimes /* 3171590Srgrimes * Options w/ arguments, skip the argument and continue 3181590Srgrimes * with the next option. 3191590Srgrimes */ 3201590Srgrimes case 'b': 3211590Srgrimes case 'c': 3221590Srgrimes case 'n': 3231590Srgrimes if (!ap[1]) 3241590Srgrimes ++argv; 3251590Srgrimes /* FALLTHROUGH */ 3261590Srgrimes /* Options w/o arguments, continue with the next option. */ 32784350Sfenner case 'F': 3281590Srgrimes case 'f': 3291590Srgrimes case 'r': 3301590Srgrimes continue; 3311590Srgrimes 3321590Srgrimes /* Illegal option, return and let getopt handle it. */ 3331590Srgrimes default: 3341590Srgrimes return; 3351590Srgrimes } 3361590Srgrimes } 3371590Srgrimes} 3381590Srgrimes 3391590Srgrimesstatic void 34099983Salfredusage(void) 3411590Srgrimes{ 3421590Srgrimes (void)fprintf(stderr, 343160049Sru "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]" 344160049Sru " [file ...]\n"); 3451590Srgrimes exit(1); 3461590Srgrimes} 347