19b50d902SRodney W. Grimes /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 49b50d902SRodney W. Grimes * Copyright (c) 1991, 1993 59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 69b50d902SRodney W. Grimes * 79b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by 89b50d902SRodney W. Grimes * Edward Sze-Tyan Wang. 99b50d902SRodney W. Grimes * 109b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 119b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 129b50d902SRodney W. Grimes * are met: 139b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 149b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 159b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 169b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 179b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 18fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 199b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 209b50d902SRodney W. Grimes * without specific prior written permission. 219b50d902SRodney W. Grimes * 229b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 239b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 249b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 259b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 269b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 279b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 289b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 299b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 309b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 319b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 329b50d902SRodney W. Grimes * SUCH DAMAGE. 339b50d902SRodney W. Grimes */ 349b50d902SRodney W. Grimes 35814e3a92SMark Murray #include <sys/cdefs.h> 36814e3a92SMark Murray 37814e3a92SMark Murray __FBSDID("$FreeBSD$"); 38814e3a92SMark Murray 399b50d902SRodney W. Grimes #ifndef lint 40247e7cb1SJeroen Ruigrok van der Werven static const char copyright[] = 419b50d902SRodney W. Grimes "@(#) Copyright (c) 1991, 1993\n\ 429b50d902SRodney W. Grimes The Regents of the University of California. All rights reserved.\n"; 43814e3a92SMark Murray #endif 449b50d902SRodney W. Grimes 459b50d902SRodney W. Grimes #ifndef lint 46814e3a92SMark Murray static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; 47247e7cb1SJeroen Ruigrok van der Werven #endif 489b50d902SRodney W. Grimes 49b4d2c338SMariusz Zaborski #include <sys/capsicum.h> 509b50d902SRodney W. Grimes #include <sys/types.h> 519b50d902SRodney W. Grimes #include <sys/stat.h> 52814e3a92SMark Murray 53b4d2c338SMariusz Zaborski #include <capsicum_helpers.h> 54814e3a92SMark Murray #include <err.h> 559b50d902SRodney W. Grimes #include <errno.h> 5653128fb4SKyle Evans #include <getopt.h> 579b50d902SRodney W. Grimes #include <stdio.h> 589b50d902SRodney W. Grimes #include <stdlib.h> 599b50d902SRodney W. Grimes #include <string.h> 60814e3a92SMark Murray #include <unistd.h> 61814e3a92SMark Murray 62b4d2c338SMariusz Zaborski #include <libcasper.h> 63b4d2c338SMariusz Zaborski #include <casper/cap_fileargs.h> 64b4d2c338SMariusz Zaborski 659b50d902SRodney W. Grimes #include "extern.h" 669b50d902SRodney W. Grimes 67aa1d7ce7SFlorent Thoumie int Fflag, fflag, qflag, rflag, rval, no_files; 68c851fce6SMariusz Zaborski fileargs_t *fa; 699b50d902SRodney W. Grimes 703f330d7dSWarner Losh static void obsolete(char **); 713f330d7dSWarner Losh static void usage(void); 729b50d902SRodney W. Grimes 7353128fb4SKyle Evans static const struct option long_opts[] = 7453128fb4SKyle Evans { 7553128fb4SKyle Evans {"blocks", required_argument, NULL, 'b'}, 7653128fb4SKyle Evans {"bytes", required_argument, NULL, 'c'}, 7753128fb4SKyle Evans {"lines", required_argument, NULL, 'n'}, 7853128fb4SKyle Evans {NULL, no_argument, NULL, 0} 7953128fb4SKyle Evans }; 8053128fb4SKyle Evans 819b50d902SRodney W. Grimes int 82d183dcb6SAlfred Perlstein main(int argc, char *argv[]) 839b50d902SRodney W. Grimes { 849b50d902SRodney W. Grimes struct stat sb; 8522da50cfSBrian Somers const char *fn; 869b50d902SRodney W. Grimes FILE *fp; 87bd9dc975SAndrey A. Chernov off_t off; 889b50d902SRodney W. Grimes enum STYLE style; 89*7e118899SMark Johnston int ch, first; 90*7e118899SMark Johnston file_info_t file, *filep, *files; 919b50d902SRodney W. Grimes char *p; 92b4d2c338SMariusz Zaborski cap_rights_t rights; 93b4d2c338SMariusz Zaborski 949b50d902SRodney W. Grimes /* 959b50d902SRodney W. Grimes * Tail's options are weird. First, -n10 is the same as -n-10, not 969b50d902SRodney W. Grimes * -n+10. Second, the number options are 1 based and not offsets, 979b50d902SRodney W. Grimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 989b50d902SRodney W. Grimes * number options for the -r option specify the number of things that 999b50d902SRodney W. Grimes * get displayed, not the starting point in the file. The one major 1009b50d902SRodney W. Grimes * incompatibility in this version as compared to historical versions 1019b50d902SRodney W. Grimes * is that the 'r' option couldn't be modified by the -lbc options, 1029b50d902SRodney W. Grimes * i.e. it was always done in lines. This version treats -rc as a 1039b50d902SRodney W. Grimes * number of characters in reverse order. Finally, the default for 1049b50d902SRodney W. Grimes * -r is the entire file, not 10 lines. 1059b50d902SRodney W. Grimes */ 1069b50d902SRodney W. Grimes #define ARG(units, forward, backward) { \ 1079b50d902SRodney W. Grimes if (style) \ 1089b50d902SRodney W. Grimes usage(); \ 109bd9dc975SAndrey A. Chernov off = strtoll(optarg, &p, 10) * (units); \ 1109b50d902SRodney W. Grimes if (*p) \ 111ac551270SPeter Wemm errx(1, "illegal offset -- %s", optarg); \ 1129b50d902SRodney W. Grimes switch(optarg[0]) { \ 1139b50d902SRodney W. Grimes case '+': \ 1149b50d902SRodney W. Grimes if (off) \ 1159b50d902SRodney W. Grimes off -= (units); \ 1169b50d902SRodney W. Grimes style = (forward); \ 1179b50d902SRodney W. Grimes break; \ 1189b50d902SRodney W. Grimes case '-': \ 1199b50d902SRodney W. Grimes off = -off; \ 1209b50d902SRodney W. Grimes /* FALLTHROUGH */ \ 1219b50d902SRodney W. Grimes default: \ 1229b50d902SRodney W. Grimes style = (backward); \ 1239b50d902SRodney W. Grimes break; \ 1249b50d902SRodney W. Grimes } \ 1259b50d902SRodney W. Grimes } 1269b50d902SRodney W. Grimes 1279b50d902SRodney W. Grimes obsolete(argv); 1289b50d902SRodney W. Grimes style = NOTSET; 129f8c2d2bdSPhilippe Charnier off = 0; 13053128fb4SKyle Evans while ((ch = getopt_long(argc, argv, "+Fb:c:fn:qr", long_opts, NULL)) != 13153128fb4SKyle Evans -1) 1329b50d902SRodney W. Grimes switch(ch) { 133eb1c9439SPeter Wemm case 'F': /* -F is superset of (and implies) -f */ 134eb1c9439SPeter Wemm Fflag = fflag = 1; 135eb1c9439SPeter Wemm break; 1369b50d902SRodney W. Grimes case 'b': 1379b50d902SRodney W. Grimes ARG(512, FBYTES, RBYTES); 1389b50d902SRodney W. Grimes break; 1399b50d902SRodney W. Grimes case 'c': 1409b50d902SRodney W. Grimes ARG(1, FBYTES, RBYTES); 1419b50d902SRodney W. Grimes break; 1429b50d902SRodney W. Grimes case 'f': 1439b50d902SRodney W. Grimes fflag = 1; 1449b50d902SRodney W. Grimes break; 1459b50d902SRodney W. Grimes case 'n': 1469b50d902SRodney W. Grimes ARG(1, FLINES, RLINES); 1479b50d902SRodney W. Grimes break; 148aa1d7ce7SFlorent Thoumie case 'q': 149aa1d7ce7SFlorent Thoumie qflag = 1; 150aa1d7ce7SFlorent Thoumie break; 1519b50d902SRodney W. Grimes case 'r': 1529b50d902SRodney W. Grimes rflag = 1; 1539b50d902SRodney W. Grimes break; 1549b50d902SRodney W. Grimes case '?': 1559b50d902SRodney W. Grimes default: 1569b50d902SRodney W. Grimes usage(); 1579b50d902SRodney W. Grimes } 1589b50d902SRodney W. Grimes argc -= optind; 1599b50d902SRodney W. Grimes argv += optind; 1609b50d902SRodney W. Grimes 16115a55f79SPaul Richards no_files = argc ? argc : 1; 1629b50d902SRodney W. Grimes 163eee07d30SMark Johnston cap_rights_init(&rights, CAP_FSTAT, CAP_FSTATFS, CAP_FCNTL, 164eee07d30SMark Johnston CAP_MMAP_R); 165eee07d30SMark Johnston if (fflag) 166eee07d30SMark Johnston cap_rights_set(&rights, CAP_EVENT); 167eee07d30SMark Johnston if (caph_rights_limit(STDIN_FILENO, &rights) < 0 || 168eee07d30SMark Johnston caph_limit_stderr() < 0 || caph_limit_stdout() < 0) 169eee07d30SMark Johnston err(1, "can't limit stdio rights"); 170eee07d30SMark Johnston 171b4d2c338SMariusz Zaborski fa = fileargs_init(argc, argv, O_RDONLY, 0, &rights, FA_OPEN); 172b4d2c338SMariusz Zaborski if (fa == NULL) 1737ef518c0SMark Johnston err(1, "unable to init casper"); 174b4d2c338SMariusz Zaborski 175b4d2c338SMariusz Zaborski caph_cache_catpages(); 176b4d2c338SMariusz Zaborski if (caph_enter_casper() < 0) 177b4d2c338SMariusz Zaborski err(1, "unable to enter capability mode"); 178b4d2c338SMariusz Zaborski 1799b50d902SRodney W. Grimes /* 1809b50d902SRodney W. Grimes * If displaying in reverse, don't permit follow option, and convert 1819b50d902SRodney W. Grimes * style values. 1829b50d902SRodney W. Grimes */ 1839b50d902SRodney W. Grimes if (rflag) { 1849b50d902SRodney W. Grimes if (fflag) 1859b50d902SRodney W. Grimes usage(); 1869b50d902SRodney W. Grimes if (style == FBYTES) 1879b50d902SRodney W. Grimes style = RBYTES; 1889b50d902SRodney W. Grimes else if (style == FLINES) 1899b50d902SRodney W. Grimes style = RLINES; 1909b50d902SRodney W. Grimes } 1919b50d902SRodney W. Grimes 1929b50d902SRodney W. Grimes /* 1939b50d902SRodney W. Grimes * If style not specified, the default is the whole file for -r, and 1949b50d902SRodney W. Grimes * the last 10 lines if not -r. 1959b50d902SRodney W. Grimes */ 1969ef5c48bSBill Fumerola if (style == NOTSET) { 1979b50d902SRodney W. Grimes if (rflag) { 1989b50d902SRodney W. Grimes off = 0; 1999b50d902SRodney W. Grimes style = REVERSE; 2009b50d902SRodney W. Grimes } else { 2019b50d902SRodney W. Grimes off = 10; 2029b50d902SRodney W. Grimes style = RLINES; 2039b50d902SRodney W. Grimes } 2049ef5c48bSBill Fumerola } 2059b50d902SRodney W. Grimes 20615a55f79SPaul Richards if (*argv && fflag) { 207*7e118899SMark Johnston files = malloc(no_files * sizeof(struct file_info)); 208*7e118899SMark Johnston if (files == NULL) 20915a55f79SPaul Richards err(1, "Couldn't malloc space for file descriptors."); 21015a55f79SPaul Richards 211*7e118899SMark Johnston for (filep = files; (fn = *argv++); filep++) { 212*7e118899SMark Johnston filep->file_name = fn; 213*7e118899SMark Johnston filep->fp = fileargs_fopen(fa, filep->file_name, "r"); 214*7e118899SMark Johnston if (filep->fp == NULL || 215*7e118899SMark Johnston fstat(fileno(filep->fp), &filep->st)) { 216*7e118899SMark Johnston if (filep->fp != NULL) { 217*7e118899SMark Johnston fclose(filep->fp); 218*7e118899SMark Johnston filep->fp = NULL; 21922da50cfSBrian Somers } 22022da50cfSBrian Somers if (!Fflag || errno != ENOENT) 221*7e118899SMark Johnston ierr(filep->file_name); 22215a55f79SPaul Richards } 22315a55f79SPaul Richards } 22415a55f79SPaul Richards follow(files, style, off); 22515a55f79SPaul Richards free(files); 22615a55f79SPaul Richards } else if (*argv) { 22722da50cfSBrian Somers for (first = 1; (fn = *argv++);) { 228b4d2c338SMariusz Zaborski if ((fp = fileargs_fopen(fa, fn, "r")) == NULL || 2299b50d902SRodney W. Grimes fstat(fileno(fp), &sb)) { 23022da50cfSBrian Somers ierr(fn); 2319b50d902SRodney W. Grimes continue; 2329b50d902SRodney W. Grimes } 233aa1d7ce7SFlorent Thoumie if (argc > 1 && !qflag) { 234849d265dSJaakko Heinonen printfn(fn, !first); 2359b50d902SRodney W. Grimes first = 0; 2369b50d902SRodney W. Grimes } 2379b50d902SRodney W. Grimes 2389b50d902SRodney W. Grimes if (rflag) 23922da50cfSBrian Somers reverse(fp, fn, style, off, &sb); 2409b50d902SRodney W. Grimes else 24122da50cfSBrian Somers forward(fp, fn, style, off, &sb); 2429b50d902SRodney W. Grimes } 24315a55f79SPaul Richards } else { 24422da50cfSBrian Somers fn = "stdin"; 2459b50d902SRodney W. Grimes 2469b50d902SRodney W. Grimes if (fstat(fileno(stdin), &sb)) { 24722da50cfSBrian Somers ierr(fn); 2489b50d902SRodney W. Grimes exit(1); 2499b50d902SRodney W. Grimes } 2509b50d902SRodney W. Grimes 251e1d27f08SEivind Eklund /* 252e1d27f08SEivind Eklund * Determine if input is a pipe. 4.4BSD will set the SOCKET 253e1d27f08SEivind Eklund * bit in the st_mode field for pipes. Fix this then. 254e1d27f08SEivind Eklund */ 255e1d27f08SEivind Eklund if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 256e1d27f08SEivind Eklund errno == ESPIPE) { 257e1d27f08SEivind Eklund errno = 0; 2589b50d902SRodney W. Grimes fflag = 0; /* POSIX.2 requires this. */ 2599b50d902SRodney W. Grimes } 2609b50d902SRodney W. Grimes 261*7e118899SMark Johnston if (rflag) { 26222da50cfSBrian Somers reverse(stdin, fn, style, off, &sb); 263*7e118899SMark Johnston } else if (fflag) { 264*7e118899SMark Johnston file.file_name = fn; 265*7e118899SMark Johnston file.fp = stdin; 266*7e118899SMark Johnston follow(&file, style, off); 267*7e118899SMark Johnston } else { 26822da50cfSBrian Somers forward(stdin, fn, style, off, &sb); 2699b50d902SRodney W. Grimes } 270*7e118899SMark Johnston } 271b4d2c338SMariusz Zaborski fileargs_free(fa); 2729b50d902SRodney W. Grimes exit(rval); 2739b50d902SRodney W. Grimes } 2749b50d902SRodney W. Grimes 2759b50d902SRodney W. Grimes /* 2769b50d902SRodney W. Grimes * Convert the obsolete argument form into something that getopt can handle. 2778cd67784SBill Fenner * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't 2789b50d902SRodney W. Grimes * the option argument for a -b, -c or -n option gets converted. 2799b50d902SRodney W. Grimes */ 2809b50d902SRodney W. Grimes static void 281d183dcb6SAlfred Perlstein obsolete(char *argv[]) 2829b50d902SRodney W. Grimes { 28348a1ef22SJeroen Ruigrok van der Werven char *ap, *p, *t; 284beed3992SJeroen Ruigrok van der Werven size_t len; 2859b50d902SRodney W. Grimes char *start; 2869b50d902SRodney W. Grimes 287814e3a92SMark Murray while ((ap = *++argv)) { 2889b50d902SRodney W. Grimes /* Return if "--" or not an option of any form. */ 2899b50d902SRodney W. Grimes if (ap[0] != '-') { 2909b50d902SRodney W. Grimes if (ap[0] != '+') 2919b50d902SRodney W. Grimes return; 2929b50d902SRodney W. Grimes } else if (ap[1] == '-') 2939b50d902SRodney W. Grimes return; 2949b50d902SRodney W. Grimes 2959b50d902SRodney W. Grimes switch(*++ap) { 2969b50d902SRodney W. Grimes /* Old-style option. */ 2979b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 2989b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 2999b50d902SRodney W. Grimes 3009b50d902SRodney W. Grimes /* Malloc space for dash, new option and argument. */ 3019b50d902SRodney W. Grimes len = strlen(*argv); 3029b50d902SRodney W. Grimes if ((start = p = malloc(len + 3)) == NULL) 303ac551270SPeter Wemm err(1, "malloc"); 3049b50d902SRodney W. Grimes *p++ = '-'; 3059b50d902SRodney W. Grimes 3069b50d902SRodney W. Grimes /* 3079b50d902SRodney W. Grimes * Go to the end of the option argument. Save off any 3089b50d902SRodney W. Grimes * trailing options (-3lf) and translate any trailing 3099b50d902SRodney W. Grimes * output style characters. 3109b50d902SRodney W. Grimes */ 3119b50d902SRodney W. Grimes t = *argv + len - 1; 3128cd67784SBill Fenner if (*t == 'F' || *t == 'f' || *t == 'r') { 3139b50d902SRodney W. Grimes *p++ = *t; 3149b50d902SRodney W. Grimes *t-- = '\0'; 3159b50d902SRodney W. Grimes } 3169b50d902SRodney W. Grimes switch(*t) { 3179b50d902SRodney W. Grimes case 'b': 3189b50d902SRodney W. Grimes *p++ = 'b'; 3199b50d902SRodney W. Grimes *t = '\0'; 3209b50d902SRodney W. Grimes break; 3219b50d902SRodney W. Grimes case 'c': 3229b50d902SRodney W. Grimes *p++ = 'c'; 3239b50d902SRodney W. Grimes *t = '\0'; 3249b50d902SRodney W. Grimes break; 3259b50d902SRodney W. Grimes case 'l': 3269b50d902SRodney W. Grimes *t = '\0'; 3279b50d902SRodney W. Grimes /* FALLTHROUGH */ 3289b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 3299b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 3309b50d902SRodney W. Grimes *p++ = 'n'; 3319b50d902SRodney W. Grimes break; 3329b50d902SRodney W. Grimes default: 333ac551270SPeter Wemm errx(1, "illegal option -- %s", *argv); 3349b50d902SRodney W. Grimes } 3359b50d902SRodney W. Grimes *p++ = *argv[0]; 3369b50d902SRodney W. Grimes (void)strcpy(p, ap); 3379b50d902SRodney W. Grimes *argv = start; 3389b50d902SRodney W. Grimes continue; 3399b50d902SRodney W. Grimes 3409b50d902SRodney W. Grimes /* 3419b50d902SRodney W. Grimes * Options w/ arguments, skip the argument and continue 3429b50d902SRodney W. Grimes * with the next option. 3439b50d902SRodney W. Grimes */ 3449b50d902SRodney W. Grimes case 'b': 3459b50d902SRodney W. Grimes case 'c': 3469b50d902SRodney W. Grimes case 'n': 3479b50d902SRodney W. Grimes if (!ap[1]) 3489b50d902SRodney W. Grimes ++argv; 3499b50d902SRodney W. Grimes /* FALLTHROUGH */ 3509b50d902SRodney W. Grimes /* Options w/o arguments, continue with the next option. */ 3518cd67784SBill Fenner case 'F': 3529b50d902SRodney W. Grimes case 'f': 3539b50d902SRodney W. Grimes case 'r': 3549b50d902SRodney W. Grimes continue; 3559b50d902SRodney W. Grimes 3569b50d902SRodney W. Grimes /* Illegal option, return and let getopt handle it. */ 3579b50d902SRodney W. Grimes default: 3589b50d902SRodney W. Grimes return; 3599b50d902SRodney W. Grimes } 3609b50d902SRodney W. Grimes } 3619b50d902SRodney W. Grimes } 3629b50d902SRodney W. Grimes 3639b50d902SRodney W. Grimes static void 364d183dcb6SAlfred Perlstein usage(void) 3659b50d902SRodney W. Grimes { 3669b50d902SRodney W. Grimes (void)fprintf(stderr, 3672982527dSRuslan Ermilov "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]" 3682982527dSRuslan Ermilov " [file ...]\n"); 3699b50d902SRodney W. Grimes exit(1); 3709b50d902SRodney W. Grimes } 371