tail.c revision 48566
1275970Scy/*- 2275970Scy * Copyright (c) 1991, 1993 3275970Scy * The Regents of the University of California. All rights reserved. 4275970Scy * 5275970Scy * This code is derived from software contributed to Berkeley by 6275970Scy * Edward Sze-Tyan Wang. 7275970Scy * 8275970Scy * Redistribution and use in source and binary forms, with or without 9275970Scy * modification, are permitted provided that the following conditions 10275970Scy * are met: 11275970Scy * 1. Redistributions of source code must retain the above copyright 12275970Scy * notice, this list of conditions and the following disclaimer. 13275970Scy * 2. Redistributions in binary form must reproduce the above copyright 14275970Scy * notice, this list of conditions and the following disclaimer in the 15275970Scy * documentation and/or other materials provided with the distribution. 16275970Scy * 3. All advertising materials mentioning features or use of this software 17275970Scy * must display the following acknowledgement: 18275970Scy * This product includes software developed by the University of 19275970Scy * California, Berkeley and its contributors. 20275970Scy * 4. Neither the name of the University nor the names of its contributors 21275970Scy * may be used to endorse or promote products derived from this software 22275970Scy * without specific prior written permission. 23275970Scy * 24275970Scy * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25275970Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26275970Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27275970Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28275970Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29275970Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30275970Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31275970Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32275970Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33275970Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34275970Scy * SUCH DAMAGE. 35275970Scy */ 36275970Scy 37275970Scy#ifndef lint 38275970Scystatic char copyright[] = 39275970Scy"@(#) Copyright (c) 1991, 1993\n\ 40275970Scy The Regents of the University of California. All rights reserved.\n"; 41275970Scy#endif /* not lint */ 42275970Scy 43275970Scy#ifndef lint 44275970Scystatic char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; 45275970Scy#endif /* not lint */ 46275970Scy 47275970Scy#include <sys/types.h> 48275970Scy#include <sys/stat.h> 49275970Scy#include <errno.h> 50275970Scy#include <unistd.h> 51275970Scy#include <stdio.h> 52275970Scy#include <stdlib.h> 53275970Scy#include <string.h> 54275970Scy#include <err.h> 55275970Scy#include "extern.h" 56275970Scy 57275970Scyint Fflag, fflag, rflag, rval; 58275970Scychar *fname; 59275970Scy 60275970Scystatic void obsolete __P((char **)); 61275970Scystatic void usage __P((void)); 62275970Scy 63275970Scyint 64275970Scymain(argc, argv) 65275970Scy int argc; 66275970Scy char *argv[]; 67275970Scy{ 68275970Scy struct stat sb; 69275970Scy FILE *fp; 70275970Scy long off; 71275970Scy enum STYLE style; 72275970Scy int ch, first; 73275970Scy char *p; 74275970Scy 75275970Scy /* 76275970Scy * Tail's options are weird. First, -n10 is the same as -n-10, not 77275970Scy * -n+10. Second, the number options are 1 based and not offsets, 78275970Scy * so -n+1 is the first line, and -c-1 is the last byte. Third, the 79275970Scy * number options for the -r option specify the number of things that 80275970Scy * get displayed, not the starting point in the file. The one major 81275970Scy * incompatibility in this version as compared to historical versions 82275970Scy * is that the 'r' option couldn't be modified by the -lbc options, 83275970Scy * i.e. it was always done in lines. This version treats -rc as a 84275970Scy * number of characters in reverse order. Finally, the default for 85275970Scy * -r is the entire file, not 10 lines. 86275970Scy */ 87275970Scy#define ARG(units, forward, backward) { \ 88275970Scy if (style) \ 89275970Scy usage(); \ 90275970Scy off = strtol(optarg, &p, 10) * (units); \ 91275970Scy if (*p) \ 92275970Scy errx(1, "illegal offset -- %s", optarg); \ 93275970Scy switch(optarg[0]) { \ 94275970Scy case '+': \ 95275970Scy if (off) \ 96275970Scy off -= (units); \ 97275970Scy style = (forward); \ 98275970Scy break; \ 99275970Scy case '-': \ 100275970Scy off = -off; \ 101275970Scy /* FALLTHROUGH */ \ 102275970Scy default: \ 103275970Scy style = (backward); \ 104275970Scy break; \ 105275970Scy } \ 106275970Scy} 107275970Scy 108275970Scy obsolete(argv); 109275970Scy style = NOTSET; 110275970Scy while ((ch = getopt(argc, argv, "Fb:c:fn:r")) != -1) 111275970Scy switch(ch) { 112275970Scy case 'F': /* -F is superset of (and implies) -f */ 113275970Scy Fflag = fflag = 1; 114275970Scy break; 115275970Scy case 'b': 116275970Scy ARG(512, FBYTES, RBYTES); 117275970Scy break; 118275970Scy case 'c': 119275970Scy ARG(1, FBYTES, RBYTES); 120275970Scy break; 121275970Scy case 'f': 122275970Scy fflag = 1; 123275970Scy break; 124275970Scy case 'n': 125275970Scy ARG(1, FLINES, RLINES); 126275970Scy break; 127275970Scy case 'r': 128275970Scy rflag = 1; 129275970Scy break; 130275970Scy case '?': 131275970Scy default: 132275970Scy usage(); 133275970Scy } 134275970Scy argc -= optind; 135275970Scy argv += optind; 136275970Scy 137275970Scy if (fflag && argc > 1) 138275970Scy errx(1, "-f option only appropriate for a single file"); 139275970Scy 140275970Scy /* 141275970Scy * If displaying in reverse, don't permit follow option, and convert 142275970Scy * style values. 143275970Scy */ 144275970Scy if (rflag) { 145275970Scy if (fflag) 146275970Scy usage(); 147275970Scy if (style == FBYTES) 148275970Scy style = RBYTES; 149275970Scy else if (style == FLINES) 150275970Scy style = RLINES; 151275970Scy } 152275970Scy 153275970Scy /* 154275970Scy * If style not specified, the default is the whole file for -r, and 155275970Scy * the last 10 lines if not -r. 156275970Scy */ 157275970Scy if (style == NOTSET) { 158275970Scy if (rflag) { 159275970Scy off = 0; 160275970Scy style = REVERSE; 161275970Scy } else { 162275970Scy off = 10; 163275970Scy style = RLINES; 164275970Scy } 165275970Scy } 166275970Scy 167275970Scy if (*argv) 168275970Scy for (first = 1; fname = *argv++;) { 169275970Scy if ((fp = fopen(fname, "r")) == NULL || 170275970Scy fstat(fileno(fp), &sb)) { 171275970Scy ierr(); 172275970Scy continue; 173275970Scy } 174275970Scy if (argc > 1) { 175275970Scy (void)printf("%s==> %s <==\n", 176275970Scy first ? "" : "\n", fname); 177275970Scy first = 0; 178275970Scy (void)fflush(stdout); 179275970Scy } 180275970Scy 181275970Scy if (rflag) 182275970Scy reverse(fp, style, off, &sb); 183275970Scy else 184275970Scy forward(fp, style, off, &sb); 185275970Scy (void)fclose(fp); 186275970Scy } 187275970Scy else { 188275970Scy fname = "stdin"; 189275970Scy 190275970Scy if (fstat(fileno(stdin), &sb)) { 191275970Scy ierr(); 192275970Scy exit(1); 193275970Scy } 194275970Scy 195275970Scy /* 196275970Scy * Determine if input is a pipe. 4.4BSD will set the SOCKET 197275970Scy * bit in the st_mode field for pipes. Fix this then. 198275970Scy */ 199275970Scy if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 && 200275970Scy errno == ESPIPE) { 201275970Scy errno = 0; 202275970Scy fflag = 0; /* POSIX.2 requires this. */ 203275970Scy } 204275970Scy 205275970Scy if (rflag) 206275970Scy reverse(stdin, style, off, &sb); 207275970Scy else 208275970Scy forward(stdin, style, off, &sb); 209275970Scy } 210275970Scy exit(rval); 211275970Scy} 212275970Scy 213275970Scy/* 214275970Scy * Convert the obsolete argument form into something that getopt can handle. 215275970Scy * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 216275970Scy * the option argument for a -b, -c or -n option gets converted. 217275970Scy */ 218275970Scystatic void 219275970Scyobsolete(argv) 220275970Scy char *argv[]; 221275970Scy{ 222275970Scy register char *ap, *p, *t; 223275970Scy int len; 224275970Scy char *start; 225275970Scy 226275970Scy while (ap = *++argv) { 227275970Scy /* Return if "--" or not an option of any form. */ 228275970Scy if (ap[0] != '-') { 229275970Scy if (ap[0] != '+') 230275970Scy return; 231275970Scy } else if (ap[1] == '-') 232275970Scy return; 233275970Scy 234275970Scy switch(*++ap) { 235275970Scy /* Old-style option. */ 236275970Scy case '0': case '1': case '2': case '3': case '4': 237275970Scy case '5': case '6': case '7': case '8': case '9': 238275970Scy 239275970Scy /* Malloc space for dash, new option and argument. */ 240275970Scy len = strlen(*argv); 241275970Scy if ((start = p = malloc(len + 3)) == NULL) 242275970Scy err(1, "malloc"); 243275970Scy *p++ = '-'; 244275970Scy 245275970Scy /* 246275970Scy * Go to the end of the option argument. Save off any 247275970Scy * trailing options (-3lf) and translate any trailing 248275970Scy * output style characters. 249275970Scy */ 250275970Scy t = *argv + len - 1; 251275970Scy if (*t == 'f' || *t == 'r') { 252275970Scy *p++ = *t; 253275970Scy *t-- = '\0'; 254275970Scy } 255275970Scy switch(*t) { 256275970Scy case 'b': 257275970Scy *p++ = 'b'; 258275970Scy *t = '\0'; 259275970Scy break; 260275970Scy case 'c': 261275970Scy *p++ = 'c'; 262275970Scy *t = '\0'; 263275970Scy break; 264275970Scy case 'l': 265275970Scy *t = '\0'; 266275970Scy /* FALLTHROUGH */ 267275970Scy case '0': case '1': case '2': case '3': case '4': 268275970Scy case '5': case '6': case '7': case '8': case '9': 269275970Scy *p++ = 'n'; 270275970Scy break; 271275970Scy default: 272275970Scy errx(1, "illegal option -- %s", *argv); 273275970Scy } 274275970Scy *p++ = *argv[0]; 275275970Scy (void)strcpy(p, ap); 276275970Scy *argv = start; 277275970Scy continue; 278275970Scy 279275970Scy /* 280275970Scy * Options w/ arguments, skip the argument and continue 281275970Scy * with the next option. 282275970Scy */ 283275970Scy case 'b': 284275970Scy case 'c': 285275970Scy case 'n': 286275970Scy if (!ap[1]) 287275970Scy ++argv; 288275970Scy /* FALLTHROUGH */ 289275970Scy /* Options w/o arguments, continue with the next option. */ 290275970Scy case 'f': 291275970Scy case 'r': 292275970Scy continue; 293275970Scy 294275970Scy /* Illegal option, return and let getopt handle it. */ 295275970Scy default: 296275970Scy return; 297275970Scy } 298275970Scy } 299275970Scy} 300275970Scy 301275970Scystatic void 302275970Scyusage() 303275970Scy{ 304275970Scy (void)fprintf(stderr, 305275970Scy "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n"); 306275970Scy exit(1); 307275970Scy} 308275970Scy