19b50d902SRodney W. Grimes /*- 29b50d902SRodney W. Grimes * Copyright (c) 1991, 1993 39b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 49b50d902SRodney W. Grimes * 59b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by 69b50d902SRodney W. Grimes * Edward Sze-Tyan Wang. 79b50d902SRodney W. Grimes * 89b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 99b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 109b50d902SRodney W. Grimes * are met: 119b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 129b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 139b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 149b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 159b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 169b50d902SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 179b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 189b50d902SRodney W. Grimes * without specific prior written permission. 199b50d902SRodney W. Grimes * 209b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 219b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 229b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 239b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 249b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 259b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 269b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 279b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 289b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 299b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 309b50d902SRodney W. Grimes * SUCH DAMAGE. 319b50d902SRodney W. Grimes */ 329b50d902SRodney W. Grimes 33814e3a92SMark Murray #include <sys/cdefs.h> 34814e3a92SMark Murray 35814e3a92SMark Murray __FBSDID("$FreeBSD$"); 36814e3a92SMark Murray 379b50d902SRodney W. Grimes #ifndef lint 38247e7cb1SJeroen Ruigrok van der Werven static const char copyright[] = 399b50d902SRodney W. Grimes "@(#) Copyright (c) 1991, 1993\n\ 409b50d902SRodney W. Grimes The Regents of the University of California. All rights reserved.\n"; 41814e3a92SMark Murray #endif 429b50d902SRodney W. Grimes 439b50d902SRodney W. Grimes #ifndef lint 44814e3a92SMark Murray static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; 45247e7cb1SJeroen Ruigrok van der Werven #endif 469b50d902SRodney W. Grimes 479b50d902SRodney W. Grimes #include <sys/types.h> 489b50d902SRodney W. Grimes #include <sys/stat.h> 49814e3a92SMark Murray 50814e3a92SMark Murray #include <err.h> 519b50d902SRodney W. Grimes #include <errno.h> 529b50d902SRodney W. Grimes #include <stdio.h> 539b50d902SRodney W. Grimes #include <stdlib.h> 549b50d902SRodney W. Grimes #include <string.h> 55814e3a92SMark Murray #include <unistd.h> 56814e3a92SMark Murray 579b50d902SRodney W. Grimes #include "extern.h" 589b50d902SRodney W. Grimes 59aa1d7ce7SFlorent Thoumie int Fflag, fflag, qflag, rflag, rval, no_files; 609b50d902SRodney W. Grimes 61c7c497f1SEd Schouten static file_info_t *files; 6215a55f79SPaul Richards 633f330d7dSWarner Losh static void obsolete(char **); 643f330d7dSWarner Losh static void usage(void); 659b50d902SRodney W. Grimes 669b50d902SRodney W. Grimes int 67d183dcb6SAlfred Perlstein main(int argc, char *argv[]) 689b50d902SRodney W. Grimes { 699b50d902SRodney W. Grimes struct stat sb; 7022da50cfSBrian Somers const char *fn; 719b50d902SRodney W. Grimes FILE *fp; 72bd9dc975SAndrey A. Chernov off_t off; 739b50d902SRodney W. Grimes enum STYLE style; 7415a55f79SPaul Richards int i, ch, first; 7515a55f79SPaul Richards file_info_t *file; 769b50d902SRodney W. Grimes char *p; 779b50d902SRodney W. Grimes 789b50d902SRodney W. Grimes /* 799b50d902SRodney W. Grimes * Tail's options are weird. First, -n10 is the same as -n-10, not 809b50d902SRodney W. Grimes * -n+10. Second, the number options are 1 based and not offsets, 819b50d902SRodney W. Grimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 829b50d902SRodney W. Grimes * number options for the -r option specify the number of things that 839b50d902SRodney W. Grimes * get displayed, not the starting point in the file. The one major 849b50d902SRodney W. Grimes * incompatibility in this version as compared to historical versions 859b50d902SRodney W. Grimes * is that the 'r' option couldn't be modified by the -lbc options, 869b50d902SRodney W. Grimes * i.e. it was always done in lines. This version treats -rc as a 879b50d902SRodney W. Grimes * number of characters in reverse order. Finally, the default for 889b50d902SRodney W. Grimes * -r is the entire file, not 10 lines. 899b50d902SRodney W. Grimes */ 909b50d902SRodney W. Grimes #define ARG(units, forward, backward) { \ 919b50d902SRodney W. Grimes if (style) \ 929b50d902SRodney W. Grimes usage(); \ 93bd9dc975SAndrey A. Chernov off = strtoll(optarg, &p, 10) * (units); \ 949b50d902SRodney W. Grimes if (*p) \ 95ac551270SPeter Wemm errx(1, "illegal offset -- %s", optarg); \ 969b50d902SRodney W. Grimes switch(optarg[0]) { \ 979b50d902SRodney W. Grimes case '+': \ 989b50d902SRodney W. Grimes if (off) \ 999b50d902SRodney W. Grimes off -= (units); \ 1009b50d902SRodney W. Grimes style = (forward); \ 1019b50d902SRodney W. Grimes break; \ 1029b50d902SRodney W. Grimes case '-': \ 1039b50d902SRodney W. Grimes off = -off; \ 1049b50d902SRodney W. Grimes /* FALLTHROUGH */ \ 1059b50d902SRodney W. Grimes default: \ 1069b50d902SRodney W. Grimes style = (backward); \ 1079b50d902SRodney W. Grimes break; \ 1089b50d902SRodney W. Grimes } \ 1099b50d902SRodney W. Grimes } 1109b50d902SRodney W. Grimes 1119b50d902SRodney W. Grimes obsolete(argv); 1129b50d902SRodney W. Grimes style = NOTSET; 113f8c2d2bdSPhilippe Charnier off = 0; 114aa1d7ce7SFlorent Thoumie while ((ch = getopt(argc, argv, "Fb:c:fn:qr")) != -1) 1159b50d902SRodney W. Grimes switch(ch) { 116eb1c9439SPeter Wemm case 'F': /* -F is superset of (and implies) -f */ 117eb1c9439SPeter Wemm Fflag = fflag = 1; 118eb1c9439SPeter Wemm break; 1199b50d902SRodney W. Grimes case 'b': 1209b50d902SRodney W. Grimes ARG(512, FBYTES, RBYTES); 1219b50d902SRodney W. Grimes break; 1229b50d902SRodney W. Grimes case 'c': 1239b50d902SRodney W. Grimes ARG(1, FBYTES, RBYTES); 1249b50d902SRodney W. Grimes break; 1259b50d902SRodney W. Grimes case 'f': 1269b50d902SRodney W. Grimes fflag = 1; 1279b50d902SRodney W. Grimes break; 1289b50d902SRodney W. Grimes case 'n': 1299b50d902SRodney W. Grimes ARG(1, FLINES, RLINES); 1309b50d902SRodney W. Grimes break; 131aa1d7ce7SFlorent Thoumie case 'q': 132aa1d7ce7SFlorent Thoumie qflag = 1; 133aa1d7ce7SFlorent Thoumie break; 1349b50d902SRodney W. Grimes case 'r': 1359b50d902SRodney W. Grimes rflag = 1; 1369b50d902SRodney W. Grimes break; 1379b50d902SRodney W. Grimes case '?': 1389b50d902SRodney W. Grimes default: 1399b50d902SRodney W. Grimes usage(); 1409b50d902SRodney W. Grimes } 1419b50d902SRodney W. Grimes argc -= optind; 1429b50d902SRodney W. Grimes argv += optind; 1439b50d902SRodney W. Grimes 14415a55f79SPaul Richards no_files = argc ? argc : 1; 1459b50d902SRodney W. Grimes 1469b50d902SRodney W. Grimes /* 1479b50d902SRodney W. Grimes * If displaying in reverse, don't permit follow option, and convert 1489b50d902SRodney W. Grimes * style values. 1499b50d902SRodney W. Grimes */ 1509b50d902SRodney W. Grimes if (rflag) { 1519b50d902SRodney W. Grimes if (fflag) 1529b50d902SRodney W. Grimes usage(); 1539b50d902SRodney W. Grimes if (style == FBYTES) 1549b50d902SRodney W. Grimes style = RBYTES; 1559b50d902SRodney W. Grimes else if (style == FLINES) 1569b50d902SRodney W. Grimes style = RLINES; 1579b50d902SRodney W. Grimes } 1589b50d902SRodney W. Grimes 1599b50d902SRodney W. Grimes /* 1609b50d902SRodney W. Grimes * If style not specified, the default is the whole file for -r, and 1619b50d902SRodney W. Grimes * the last 10 lines if not -r. 1629b50d902SRodney W. Grimes */ 1639ef5c48bSBill Fumerola if (style == NOTSET) { 1649b50d902SRodney W. Grimes if (rflag) { 1659b50d902SRodney W. Grimes off = 0; 1669b50d902SRodney W. Grimes style = REVERSE; 1679b50d902SRodney W. Grimes } else { 1689b50d902SRodney W. Grimes off = 10; 1699b50d902SRodney W. Grimes style = RLINES; 1709b50d902SRodney W. Grimes } 1719ef5c48bSBill Fumerola } 1729b50d902SRodney W. Grimes 17315a55f79SPaul Richards if (*argv && fflag) { 17422da50cfSBrian Somers files = (struct file_info *) malloc(no_files * 17522da50cfSBrian Somers sizeof(struct file_info)); 17615a55f79SPaul Richards if (!files) 17715a55f79SPaul Richards err(1, "Couldn't malloc space for file descriptors."); 17815a55f79SPaul Richards 17922da50cfSBrian Somers for (file = files; (fn = *argv++); file++) { 18022da50cfSBrian Somers file->file_name = strdup(fn); 18115a55f79SPaul Richards if (! file->file_name) 18215a55f79SPaul Richards errx(1, "Couldn't malloc space for file name."); 18315a55f79SPaul Richards if ((file->fp = fopen(file->file_name, "r")) == NULL || 18415a55f79SPaul Richards fstat(fileno(file->fp), &file->st)) { 18522da50cfSBrian Somers if (file->fp != NULL) { 18622da50cfSBrian Somers fclose(file->fp); 18715a55f79SPaul Richards file->fp = NULL; 18822da50cfSBrian Somers } 18922da50cfSBrian Somers if (!Fflag || errno != ENOENT) 19022da50cfSBrian Somers ierr(file->file_name); 19115a55f79SPaul Richards } 19215a55f79SPaul Richards } 19315a55f79SPaul Richards follow(files, style, off); 19415a55f79SPaul Richards for (i = 0, file = files; i < no_files; i++, file++) { 19515a55f79SPaul Richards free(file->file_name); 19615a55f79SPaul Richards } 19715a55f79SPaul Richards free(files); 19815a55f79SPaul Richards } else if (*argv) { 19922da50cfSBrian Somers for (first = 1; (fn = *argv++);) { 20022da50cfSBrian Somers if ((fp = fopen(fn, "r")) == NULL || 2019b50d902SRodney W. Grimes fstat(fileno(fp), &sb)) { 20222da50cfSBrian Somers ierr(fn); 2039b50d902SRodney W. Grimes continue; 2049b50d902SRodney W. Grimes } 205aa1d7ce7SFlorent Thoumie if (argc > 1 && !qflag) { 206*849d265dSJaakko Heinonen printfn(fn, !first); 2079b50d902SRodney W. Grimes first = 0; 2089b50d902SRodney W. Grimes } 2099b50d902SRodney W. Grimes 2109b50d902SRodney W. Grimes if (rflag) 21122da50cfSBrian Somers reverse(fp, fn, style, off, &sb); 2129b50d902SRodney W. Grimes else 21322da50cfSBrian Somers forward(fp, fn, style, off, &sb); 2149b50d902SRodney W. Grimes } 21515a55f79SPaul Richards } else { 21622da50cfSBrian Somers fn = "stdin"; 2179b50d902SRodney W. Grimes 2189b50d902SRodney W. Grimes if (fstat(fileno(stdin), &sb)) { 21922da50cfSBrian Somers ierr(fn); 2209b50d902SRodney W. Grimes exit(1); 2219b50d902SRodney W. Grimes } 2229b50d902SRodney W. Grimes 223e1d27f08SEivind Eklund /* 224e1d27f08SEivind Eklund * Determine if input is a pipe. 4.4BSD will set the SOCKET 225e1d27f08SEivind Eklund * bit in the st_mode field for pipes. Fix this then. 226e1d27f08SEivind Eklund */ 227e1d27f08SEivind Eklund if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 228e1d27f08SEivind Eklund errno == ESPIPE) { 229e1d27f08SEivind Eklund errno = 0; 2309b50d902SRodney W. Grimes fflag = 0; /* POSIX.2 requires this. */ 2319b50d902SRodney W. Grimes } 2329b50d902SRodney W. Grimes 2339b50d902SRodney W. Grimes if (rflag) 23422da50cfSBrian Somers reverse(stdin, fn, style, off, &sb); 2359b50d902SRodney W. Grimes else 23622da50cfSBrian Somers forward(stdin, fn, style, off, &sb); 2379b50d902SRodney W. Grimes } 2389b50d902SRodney W. Grimes exit(rval); 2399b50d902SRodney W. Grimes } 2409b50d902SRodney W. Grimes 2419b50d902SRodney W. Grimes /* 2429b50d902SRodney W. Grimes * Convert the obsolete argument form into something that getopt can handle. 2438cd67784SBill Fenner * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't 2449b50d902SRodney W. Grimes * the option argument for a -b, -c or -n option gets converted. 2459b50d902SRodney W. Grimes */ 2469b50d902SRodney W. Grimes static void 247d183dcb6SAlfred Perlstein obsolete(char *argv[]) 2489b50d902SRodney W. Grimes { 24948a1ef22SJeroen Ruigrok van der Werven char *ap, *p, *t; 250beed3992SJeroen Ruigrok van der Werven size_t len; 2519b50d902SRodney W. Grimes char *start; 2529b50d902SRodney W. Grimes 253814e3a92SMark Murray while ((ap = *++argv)) { 2549b50d902SRodney W. Grimes /* Return if "--" or not an option of any form. */ 2559b50d902SRodney W. Grimes if (ap[0] != '-') { 2569b50d902SRodney W. Grimes if (ap[0] != '+') 2579b50d902SRodney W. Grimes return; 2589b50d902SRodney W. Grimes } else if (ap[1] == '-') 2599b50d902SRodney W. Grimes return; 2609b50d902SRodney W. Grimes 2619b50d902SRodney W. Grimes switch(*++ap) { 2629b50d902SRodney W. Grimes /* Old-style option. */ 2639b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 2649b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 2659b50d902SRodney W. Grimes 2669b50d902SRodney W. Grimes /* Malloc space for dash, new option and argument. */ 2679b50d902SRodney W. Grimes len = strlen(*argv); 2689b50d902SRodney W. Grimes if ((start = p = malloc(len + 3)) == NULL) 269ac551270SPeter Wemm err(1, "malloc"); 2709b50d902SRodney W. Grimes *p++ = '-'; 2719b50d902SRodney W. Grimes 2729b50d902SRodney W. Grimes /* 2739b50d902SRodney W. Grimes * Go to the end of the option argument. Save off any 2749b50d902SRodney W. Grimes * trailing options (-3lf) and translate any trailing 2759b50d902SRodney W. Grimes * output style characters. 2769b50d902SRodney W. Grimes */ 2779b50d902SRodney W. Grimes t = *argv + len - 1; 2788cd67784SBill Fenner if (*t == 'F' || *t == 'f' || *t == 'r') { 2799b50d902SRodney W. Grimes *p++ = *t; 2809b50d902SRodney W. Grimes *t-- = '\0'; 2819b50d902SRodney W. Grimes } 2829b50d902SRodney W. Grimes switch(*t) { 2839b50d902SRodney W. Grimes case 'b': 2849b50d902SRodney W. Grimes *p++ = 'b'; 2859b50d902SRodney W. Grimes *t = '\0'; 2869b50d902SRodney W. Grimes break; 2879b50d902SRodney W. Grimes case 'c': 2889b50d902SRodney W. Grimes *p++ = 'c'; 2899b50d902SRodney W. Grimes *t = '\0'; 2909b50d902SRodney W. Grimes break; 2919b50d902SRodney W. Grimes case 'l': 2929b50d902SRodney W. Grimes *t = '\0'; 2939b50d902SRodney W. Grimes /* FALLTHROUGH */ 2949b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 2959b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 2969b50d902SRodney W. Grimes *p++ = 'n'; 2979b50d902SRodney W. Grimes break; 2989b50d902SRodney W. Grimes default: 299ac551270SPeter Wemm errx(1, "illegal option -- %s", *argv); 3009b50d902SRodney W. Grimes } 3019b50d902SRodney W. Grimes *p++ = *argv[0]; 3029b50d902SRodney W. Grimes (void)strcpy(p, ap); 3039b50d902SRodney W. Grimes *argv = start; 3049b50d902SRodney W. Grimes continue; 3059b50d902SRodney W. Grimes 3069b50d902SRodney W. Grimes /* 3079b50d902SRodney W. Grimes * Options w/ arguments, skip the argument and continue 3089b50d902SRodney W. Grimes * with the next option. 3099b50d902SRodney W. Grimes */ 3109b50d902SRodney W. Grimes case 'b': 3119b50d902SRodney W. Grimes case 'c': 3129b50d902SRodney W. Grimes case 'n': 3139b50d902SRodney W. Grimes if (!ap[1]) 3149b50d902SRodney W. Grimes ++argv; 3159b50d902SRodney W. Grimes /* FALLTHROUGH */ 3169b50d902SRodney W. Grimes /* Options w/o arguments, continue with the next option. */ 3178cd67784SBill Fenner case 'F': 3189b50d902SRodney W. Grimes case 'f': 3199b50d902SRodney W. Grimes case 'r': 3209b50d902SRodney W. Grimes continue; 3219b50d902SRodney W. Grimes 3229b50d902SRodney W. Grimes /* Illegal option, return and let getopt handle it. */ 3239b50d902SRodney W. Grimes default: 3249b50d902SRodney W. Grimes return; 3259b50d902SRodney W. Grimes } 3269b50d902SRodney W. Grimes } 3279b50d902SRodney W. Grimes } 3289b50d902SRodney W. Grimes 3299b50d902SRodney W. Grimes static void 330d183dcb6SAlfred Perlstein usage(void) 3319b50d902SRodney W. Grimes { 3329b50d902SRodney W. Grimes (void)fprintf(stderr, 3332982527dSRuslan Ermilov "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]" 3342982527dSRuslan Ermilov " [file ...]\n"); 3359b50d902SRodney W. Grimes exit(1); 3369b50d902SRodney W. Grimes } 337