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 62643ac419SXin LI #include <libutil.h> 63643ac419SXin LI 64b4d2c338SMariusz Zaborski #include <libcasper.h> 65b4d2c338SMariusz Zaborski #include <casper/cap_fileargs.h> 66b4d2c338SMariusz Zaborski 679b50d902SRodney W. Grimes #include "extern.h" 689b50d902SRodney W. Grimes 69643ac419SXin LI int Fflag, fflag, qflag, rflag, rval, no_files, vflag; 70c851fce6SMariusz Zaborski fileargs_t *fa; 719b50d902SRodney W. Grimes 723f330d7dSWarner Losh static void obsolete(char **); 733f330d7dSWarner Losh static void usage(void); 749b50d902SRodney W. Grimes 7553128fb4SKyle Evans static const struct option long_opts[] = 7653128fb4SKyle Evans { 7753128fb4SKyle Evans {"blocks", required_argument, NULL, 'b'}, 7853128fb4SKyle Evans {"bytes", required_argument, NULL, 'c'}, 7953128fb4SKyle Evans {"lines", required_argument, NULL, 'n'}, 80643ac419SXin LI {"quiet", no_argument, NULL, 'q'}, 81643ac419SXin LI {"silent", no_argument, NULL, 'q'}, 82643ac419SXin LI {"verbose", no_argument, NULL, 'v'}, 8353128fb4SKyle Evans {NULL, no_argument, NULL, 0} 8453128fb4SKyle Evans }; 8553128fb4SKyle Evans 869b50d902SRodney W. Grimes int 87d183dcb6SAlfred Perlstein main(int argc, char *argv[]) 889b50d902SRodney W. Grimes { 899b50d902SRodney W. Grimes struct stat sb; 9022da50cfSBrian Somers const char *fn; 919b50d902SRodney W. Grimes FILE *fp; 92bd9dc975SAndrey A. Chernov off_t off; 939b50d902SRodney W. Grimes enum STYLE style; 947e118899SMark Johnston int ch, first; 957e118899SMark Johnston file_info_t file, *filep, *files; 96b4d2c338SMariusz Zaborski cap_rights_t rights; 97b4d2c338SMariusz Zaborski 989b50d902SRodney W. Grimes /* 999b50d902SRodney W. Grimes * Tail's options are weird. First, -n10 is the same as -n-10, not 1009b50d902SRodney W. Grimes * -n+10. Second, the number options are 1 based and not offsets, 1019b50d902SRodney W. Grimes * so -n+1 is the first line, and -c-1 is the last byte. Third, the 1029b50d902SRodney W. Grimes * number options for the -r option specify the number of things that 1039b50d902SRodney W. Grimes * get displayed, not the starting point in the file. The one major 1049b50d902SRodney W. Grimes * incompatibility in this version as compared to historical versions 1059b50d902SRodney W. Grimes * is that the 'r' option couldn't be modified by the -lbc options, 1069b50d902SRodney W. Grimes * i.e. it was always done in lines. This version treats -rc as a 1079b50d902SRodney W. Grimes * number of characters in reverse order. Finally, the default for 1089b50d902SRodney W. Grimes * -r is the entire file, not 10 lines. 1099b50d902SRodney W. Grimes */ 1109b50d902SRodney W. Grimes #define ARG(units, forward, backward) { \ 1119b50d902SRodney W. Grimes if (style) \ 1129b50d902SRodney W. Grimes usage(); \ 113643ac419SXin LI if (expand_number(optarg, &off)) \ 114643ac419SXin LI err(1, "illegal offset -- %s", optarg); \ 115643ac419SXin LI if (off > INT64_MAX / units || off < INT64_MIN / units ) \ 116ac551270SPeter Wemm errx(1, "illegal offset -- %s", optarg); \ 1179b50d902SRodney W. Grimes switch(optarg[0]) { \ 1189b50d902SRodney W. Grimes case '+': \ 1199b50d902SRodney W. Grimes if (off) \ 1209b50d902SRodney W. Grimes off -= (units); \ 1219b50d902SRodney W. Grimes style = (forward); \ 1229b50d902SRodney W. Grimes break; \ 1239b50d902SRodney W. Grimes case '-': \ 1249b50d902SRodney W. Grimes off = -off; \ 1259b50d902SRodney W. Grimes /* FALLTHROUGH */ \ 1269b50d902SRodney W. Grimes default: \ 1279b50d902SRodney W. Grimes style = (backward); \ 1289b50d902SRodney W. Grimes break; \ 1299b50d902SRodney W. Grimes } \ 1309b50d902SRodney W. Grimes } 1319b50d902SRodney W. Grimes 1329b50d902SRodney W. Grimes obsolete(argv); 1339b50d902SRodney W. Grimes style = NOTSET; 134f8c2d2bdSPhilippe Charnier off = 0; 135643ac419SXin LI while ((ch = getopt_long(argc, argv, "+Fb:c:fn:qrv", long_opts, NULL)) != 13653128fb4SKyle Evans -1) 1379b50d902SRodney W. Grimes switch(ch) { 138eb1c9439SPeter Wemm case 'F': /* -F is superset of (and implies) -f */ 139eb1c9439SPeter Wemm Fflag = fflag = 1; 140eb1c9439SPeter Wemm break; 1419b50d902SRodney W. Grimes case 'b': 1429b50d902SRodney W. Grimes ARG(512, FBYTES, RBYTES); 1439b50d902SRodney W. Grimes break; 1449b50d902SRodney W. Grimes case 'c': 1459b50d902SRodney W. Grimes ARG(1, FBYTES, RBYTES); 1469b50d902SRodney W. Grimes break; 1479b50d902SRodney W. Grimes case 'f': 1489b50d902SRodney W. Grimes fflag = 1; 1499b50d902SRodney W. Grimes break; 1509b50d902SRodney W. Grimes case 'n': 1519b50d902SRodney W. Grimes ARG(1, FLINES, RLINES); 1529b50d902SRodney W. Grimes break; 153aa1d7ce7SFlorent Thoumie case 'q': 154aa1d7ce7SFlorent Thoumie qflag = 1; 155643ac419SXin LI vflag = 0; 156aa1d7ce7SFlorent Thoumie break; 1579b50d902SRodney W. Grimes case 'r': 1589b50d902SRodney W. Grimes rflag = 1; 1599b50d902SRodney W. Grimes break; 160643ac419SXin LI case 'v': 161643ac419SXin LI vflag = 1; 162643ac419SXin LI qflag = 0; 163643ac419SXin LI break; 1649b50d902SRodney W. Grimes case '?': 1659b50d902SRodney W. Grimes default: 1669b50d902SRodney W. Grimes usage(); 1679b50d902SRodney W. Grimes } 1689b50d902SRodney W. Grimes argc -= optind; 1699b50d902SRodney W. Grimes argv += optind; 1709b50d902SRodney W. Grimes 17115a55f79SPaul Richards no_files = argc ? argc : 1; 1729b50d902SRodney W. Grimes 173eee07d30SMark Johnston cap_rights_init(&rights, CAP_FSTAT, CAP_FSTATFS, CAP_FCNTL, 174eee07d30SMark Johnston CAP_MMAP_R); 175eee07d30SMark Johnston if (fflag) 176eee07d30SMark Johnston cap_rights_set(&rights, CAP_EVENT); 177eee07d30SMark Johnston if (caph_rights_limit(STDIN_FILENO, &rights) < 0 || 178eee07d30SMark Johnston caph_limit_stderr() < 0 || caph_limit_stdout() < 0) 179eee07d30SMark Johnston err(1, "can't limit stdio rights"); 180eee07d30SMark Johnston 181b4d2c338SMariusz Zaborski fa = fileargs_init(argc, argv, O_RDONLY, 0, &rights, FA_OPEN); 182b4d2c338SMariusz Zaborski if (fa == NULL) 1837ef518c0SMark Johnston err(1, "unable to init casper"); 184b4d2c338SMariusz Zaborski 185b4d2c338SMariusz Zaborski caph_cache_catpages(); 186b4d2c338SMariusz Zaborski if (caph_enter_casper() < 0) 187b4d2c338SMariusz Zaborski err(1, "unable to enter capability mode"); 188b4d2c338SMariusz Zaborski 1899b50d902SRodney W. Grimes /* 1909b50d902SRodney W. Grimes * If displaying in reverse, don't permit follow option, and convert 1919b50d902SRodney W. Grimes * style values. 1929b50d902SRodney W. Grimes */ 1939b50d902SRodney W. Grimes if (rflag) { 1949b50d902SRodney W. Grimes if (fflag) 1959b50d902SRodney W. Grimes usage(); 1969b50d902SRodney W. Grimes if (style == FBYTES) 1979b50d902SRodney W. Grimes style = RBYTES; 1989b50d902SRodney W. Grimes else if (style == FLINES) 1999b50d902SRodney W. Grimes style = RLINES; 2009b50d902SRodney W. Grimes } 2019b50d902SRodney W. Grimes 2029b50d902SRodney W. Grimes /* 2039b50d902SRodney W. Grimes * If style not specified, the default is the whole file for -r, and 2049b50d902SRodney W. Grimes * the last 10 lines if not -r. 2059b50d902SRodney W. Grimes */ 2069ef5c48bSBill Fumerola if (style == NOTSET) { 2079b50d902SRodney W. Grimes if (rflag) { 2089b50d902SRodney W. Grimes off = 0; 2099b50d902SRodney W. Grimes style = REVERSE; 2109b50d902SRodney W. Grimes } else { 2119b50d902SRodney W. Grimes off = 10; 2129b50d902SRodney W. Grimes style = RLINES; 2139b50d902SRodney W. Grimes } 2149ef5c48bSBill Fumerola } 2159b50d902SRodney W. Grimes 21615a55f79SPaul Richards if (*argv && fflag) { 2177e118899SMark Johnston files = malloc(no_files * sizeof(struct file_info)); 2187e118899SMark Johnston if (files == NULL) 21915a55f79SPaul Richards err(1, "Couldn't malloc space for file descriptors."); 22015a55f79SPaul Richards 2217e118899SMark Johnston for (filep = files; (fn = *argv++); filep++) { 2227e118899SMark Johnston filep->file_name = fn; 2237e118899SMark Johnston filep->fp = fileargs_fopen(fa, filep->file_name, "r"); 2247e118899SMark Johnston if (filep->fp == NULL || 2257e118899SMark Johnston fstat(fileno(filep->fp), &filep->st)) { 2267e118899SMark Johnston if (filep->fp != NULL) { 2277e118899SMark Johnston fclose(filep->fp); 2287e118899SMark Johnston filep->fp = NULL; 22922da50cfSBrian Somers } 23022da50cfSBrian Somers if (!Fflag || errno != ENOENT) 2317e118899SMark Johnston ierr(filep->file_name); 23215a55f79SPaul Richards } 23315a55f79SPaul Richards } 23415a55f79SPaul Richards follow(files, style, off); 23515a55f79SPaul Richards free(files); 23615a55f79SPaul Richards } else if (*argv) { 23722da50cfSBrian Somers for (first = 1; (fn = *argv++);) { 238b4d2c338SMariusz Zaborski if ((fp = fileargs_fopen(fa, fn, "r")) == NULL || 2399b50d902SRodney W. Grimes fstat(fileno(fp), &sb)) { 24022da50cfSBrian Somers ierr(fn); 2419b50d902SRodney W. Grimes continue; 2429b50d902SRodney W. Grimes } 243643ac419SXin LI if (vflag || (qflag == 0 && argc > 1)) { 244849d265dSJaakko Heinonen printfn(fn, !first); 2459b50d902SRodney W. Grimes first = 0; 2469b50d902SRodney W. Grimes } 2479b50d902SRodney W. Grimes 2489b50d902SRodney W. Grimes if (rflag) 24922da50cfSBrian Somers reverse(fp, fn, style, off, &sb); 2509b50d902SRodney W. Grimes else 25122da50cfSBrian Somers forward(fp, fn, style, off, &sb); 2529b50d902SRodney W. Grimes } 25315a55f79SPaul Richards } else { 25422da50cfSBrian Somers fn = "stdin"; 2559b50d902SRodney W. Grimes 2569b50d902SRodney W. Grimes if (fstat(fileno(stdin), &sb)) { 25722da50cfSBrian Somers ierr(fn); 2589b50d902SRodney W. Grimes exit(1); 2599b50d902SRodney W. Grimes } 2609b50d902SRodney W. Grimes 261e1d27f08SEivind Eklund /* 262e1d27f08SEivind Eklund * Determine if input is a pipe. 4.4BSD will set the SOCKET 263e1d27f08SEivind Eklund * bit in the st_mode field for pipes. Fix this then. 264e1d27f08SEivind Eklund */ 265e1d27f08SEivind Eklund if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 266e1d27f08SEivind Eklund errno == ESPIPE) { 267e1d27f08SEivind Eklund errno = 0; 2689b50d902SRodney W. Grimes fflag = 0; /* POSIX.2 requires this. */ 2699b50d902SRodney W. Grimes } 2709b50d902SRodney W. Grimes 2717e118899SMark Johnston if (rflag) { 27222da50cfSBrian Somers reverse(stdin, fn, style, off, &sb); 2737e118899SMark Johnston } else if (fflag) { 2747e118899SMark Johnston file.file_name = fn; 2757e118899SMark Johnston file.fp = stdin; 276*e599810dSMark Johnston file.st = sb; 2777e118899SMark Johnston follow(&file, style, off); 2787e118899SMark Johnston } else { 27922da50cfSBrian Somers forward(stdin, fn, style, off, &sb); 2809b50d902SRodney W. Grimes } 2817e118899SMark Johnston } 282b4d2c338SMariusz Zaborski fileargs_free(fa); 2839b50d902SRodney W. Grimes exit(rval); 2849b50d902SRodney W. Grimes } 2859b50d902SRodney W. Grimes 2869b50d902SRodney W. Grimes /* 2879b50d902SRodney W. Grimes * Convert the obsolete argument form into something that getopt can handle. 2888cd67784SBill Fenner * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't 2899b50d902SRodney W. Grimes * the option argument for a -b, -c or -n option gets converted. 2909b50d902SRodney W. Grimes */ 2919b50d902SRodney W. Grimes static void 292d183dcb6SAlfred Perlstein obsolete(char *argv[]) 2939b50d902SRodney W. Grimes { 29448a1ef22SJeroen Ruigrok van der Werven char *ap, *p, *t; 295beed3992SJeroen Ruigrok van der Werven size_t len; 2969b50d902SRodney W. Grimes char *start; 2979b50d902SRodney W. Grimes 298814e3a92SMark Murray while ((ap = *++argv)) { 2999b50d902SRodney W. Grimes /* Return if "--" or not an option of any form. */ 3009b50d902SRodney W. Grimes if (ap[0] != '-') { 3019b50d902SRodney W. Grimes if (ap[0] != '+') 3029b50d902SRodney W. Grimes return; 3039b50d902SRodney W. Grimes } else if (ap[1] == '-') 3049b50d902SRodney W. Grimes return; 3059b50d902SRodney W. Grimes 3069b50d902SRodney W. Grimes switch(*++ap) { 3079b50d902SRodney W. Grimes /* Old-style option. */ 3089b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 3099b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 3109b50d902SRodney W. Grimes 3119b50d902SRodney W. Grimes /* Malloc space for dash, new option and argument. */ 3129b50d902SRodney W. Grimes len = strlen(*argv); 3139b50d902SRodney W. Grimes if ((start = p = malloc(len + 3)) == NULL) 314ac551270SPeter Wemm err(1, "malloc"); 3159b50d902SRodney W. Grimes *p++ = '-'; 3169b50d902SRodney W. Grimes 3179b50d902SRodney W. Grimes /* 3189b50d902SRodney W. Grimes * Go to the end of the option argument. Save off any 3199b50d902SRodney W. Grimes * trailing options (-3lf) and translate any trailing 3209b50d902SRodney W. Grimes * output style characters. 3219b50d902SRodney W. Grimes */ 3229b50d902SRodney W. Grimes t = *argv + len - 1; 3238cd67784SBill Fenner if (*t == 'F' || *t == 'f' || *t == 'r') { 3249b50d902SRodney W. Grimes *p++ = *t; 3259b50d902SRodney W. Grimes *t-- = '\0'; 3269b50d902SRodney W. Grimes } 3279b50d902SRodney W. Grimes switch(*t) { 3289b50d902SRodney W. Grimes case 'b': 3299b50d902SRodney W. Grimes *p++ = 'b'; 3309b50d902SRodney W. Grimes *t = '\0'; 3319b50d902SRodney W. Grimes break; 3329b50d902SRodney W. Grimes case 'c': 3339b50d902SRodney W. Grimes *p++ = 'c'; 3349b50d902SRodney W. Grimes *t = '\0'; 3359b50d902SRodney W. Grimes break; 3369b50d902SRodney W. Grimes case 'l': 3379b50d902SRodney W. Grimes *t = '\0'; 3389b50d902SRodney W. Grimes /* FALLTHROUGH */ 3399b50d902SRodney W. Grimes case '0': case '1': case '2': case '3': case '4': 3409b50d902SRodney W. Grimes case '5': case '6': case '7': case '8': case '9': 3419b50d902SRodney W. Grimes *p++ = 'n'; 3429b50d902SRodney W. Grimes break; 3439b50d902SRodney W. Grimes default: 344ac551270SPeter Wemm errx(1, "illegal option -- %s", *argv); 3459b50d902SRodney W. Grimes } 3469b50d902SRodney W. Grimes *p++ = *argv[0]; 3479b50d902SRodney W. Grimes (void)strcpy(p, ap); 3489b50d902SRodney W. Grimes *argv = start; 3499b50d902SRodney W. Grimes continue; 3509b50d902SRodney W. Grimes 3519b50d902SRodney W. Grimes /* 3529b50d902SRodney W. Grimes * Options w/ arguments, skip the argument and continue 3539b50d902SRodney W. Grimes * with the next option. 3549b50d902SRodney W. Grimes */ 3559b50d902SRodney W. Grimes case 'b': 3569b50d902SRodney W. Grimes case 'c': 3579b50d902SRodney W. Grimes case 'n': 3589b50d902SRodney W. Grimes if (!ap[1]) 3599b50d902SRodney W. Grimes ++argv; 3609b50d902SRodney W. Grimes /* FALLTHROUGH */ 3619b50d902SRodney W. Grimes /* Options w/o arguments, continue with the next option. */ 3628cd67784SBill Fenner case 'F': 3639b50d902SRodney W. Grimes case 'f': 3649b50d902SRodney W. Grimes case 'r': 3659b50d902SRodney W. Grimes continue; 3669b50d902SRodney W. Grimes 3679b50d902SRodney W. Grimes /* Illegal option, return and let getopt handle it. */ 3689b50d902SRodney W. Grimes default: 3699b50d902SRodney W. Grimes return; 3709b50d902SRodney W. Grimes } 3719b50d902SRodney W. Grimes } 3729b50d902SRodney W. Grimes } 3739b50d902SRodney W. Grimes 3749b50d902SRodney W. Grimes static void 375d183dcb6SAlfred Perlstein usage(void) 3769b50d902SRodney W. Grimes { 3779b50d902SRodney W. Grimes (void)fprintf(stderr, 3782982527dSRuslan Ermilov "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]" 3792982527dSRuslan Ermilov " [file ...]\n"); 3809b50d902SRodney W. Grimes exit(1); 3819b50d902SRodney W. Grimes } 382