1 /*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Edward Sze-Tyan Wang. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 char copyright[] = 13 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 14 All rights reserved.\n"; 15 #endif /* not lint */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)tail.c 5.6 (Berkeley) 08/26/91"; 19 #endif /* not lint */ 20 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 #include <errno.h> 24 #include <unistd.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include "extern.h" 29 30 int fflag, rflag, rval; 31 char *fname; 32 33 static void obsolete __P((char **)); 34 static void usage __P((void)); 35 36 main(argc, argv) 37 int argc; 38 char **argv; 39 { 40 struct stat sb; 41 FILE *fp; 42 long off; 43 enum STYLE style; 44 int ch; 45 char *p, *num; 46 47 obsolete(argv); 48 49 style = NOTSET; 50 while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF) 51 switch(ch) { 52 case 'b': 53 if (style) 54 usage(); 55 off = strtol(num = optarg, &p, 10) * 512; 56 if (*p) 57 err("illegal offset -- %s", optarg); 58 style = *num == '+' ? FBYTES : RBYTES; 59 break; 60 case 'c': 61 if (style) 62 usage(); 63 off = strtol(num = optarg, &p, 10); 64 if (*p) 65 err("illegal offset -- %s", optarg); 66 style = *num == '+' ? FBYTES : RBYTES; 67 break; 68 case 'f': 69 fflag = 1; 70 break; 71 case 'n': 72 if (style) 73 usage(); 74 off = strtol(num = optarg, &p, 10); 75 if (*p) 76 err("illegal offset -- %s", optarg); 77 style = *num == '+' ? FLINES : RLINES; 78 break; 79 case 'r': 80 rflag = 1; 81 break; 82 case '?': 83 default: 84 usage(); 85 } 86 argc -= optind; 87 argv += optind; 88 89 /* 90 * Don't permit follow option if displaying in reverse. An offset 91 * with an explicit leading minus is meaningless. 92 */ 93 if (rflag) { 94 if (fflag) 95 usage(); 96 if (style && *num == '-') 97 err("illegal offset for -r option -- %s", num); 98 if (style == FBYTES) 99 style = RBYTES; 100 if (style == FLINES) 101 style = RLINES; 102 } 103 104 if (fname = *argv) { 105 if ((fp = fopen(fname, "r")) == NULL) 106 ierr(); 107 } else { 108 fp = stdin; 109 fname = "stdin"; 110 } 111 112 if (fstat(fileno(fp), &sb)) 113 ierr(); 114 115 /* 116 * Determine if input is a pipe. 4.4BSD will set the SOCKET 117 * bit in the st_mode field for pipes. Fix this then. 118 */ 119 if (lseek(fileno(fp), 0L, SEEK_CUR) == -1 && errno == ESPIPE) { 120 errno = 0; 121 fflag = 0; /* POSIX.2 requires this. */ 122 } 123 124 /* 125 * Tail's options are weird. First, -n10 is the same as -n-10, not 126 * -n+10. Second, the number options for the -r option specify the 127 * number of bytes/chars/lines that get displayed, not the offset from 128 * the beginning/end of the file. Finally, the default for -r is the 129 * entire file, not 10 lines. 130 */ 131 if (!style) 132 if (rflag) { 133 off = 0; 134 style = REVERSE; 135 } else { 136 off = 10; 137 style = RLINES; 138 } 139 else if (off < 0) 140 off = -off; 141 142 if (rflag) 143 reverse(fp, style, off, &sb); 144 else 145 forward(fp, style, off, &sb); 146 exit(rval); 147 } 148 149 /* 150 * Convert the obsolete argument form into something that getopt can handle. 151 * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't 152 * the option argument for a -b, -c or -n option gets converted. 153 */ 154 static void 155 obsolete(argv) 156 char **argv; 157 { 158 register char *ap, *p, *t; 159 int len; 160 char *start; 161 162 while (ap = *++argv) { 163 /* Return if "--" or not an option of any form. */ 164 if (ap[0] != '-') { 165 if (ap[0] != '+') 166 return; 167 } else if (ap[1] == '-') 168 return; 169 170 switch(*++ap) { 171 /* Old-style option. */ 172 case '0': case '1': case '2': case '3': case '4': 173 case '5': case '6': case '7': case '8': case '9': 174 175 /* Malloc space for dash, new option and argument. */ 176 len = strlen(*argv); 177 if ((start = p = malloc(len + 3)) == NULL) 178 err("%s", strerror(errno)); 179 *p++ = '-'; 180 181 /* 182 * Go to the end of the option argument. Save off any 183 * trailing options (-3lf) and translate any trailing 184 * output style characters. 185 */ 186 t = *argv + len - 1; 187 if (*t == 'f' || *t == 'r') { 188 *p++ = *t; 189 *t-- = '\0'; 190 } 191 switch(*t) { 192 case 'b': 193 *p++ = 'b'; 194 *t = '\0'; 195 break; 196 case 'c': 197 *p++ = 'c'; 198 *t = '\0'; 199 break; 200 case 'l': 201 *t = '\0'; 202 /* FALLTHROUGH */ 203 case '0': case '1': case '2': case '3': case '4': 204 case '5': case '6': case '7': case '8': case '9': 205 *p++ = 'n'; 206 break; 207 default: 208 err("illegal option -- %s", *argv); 209 } 210 *p++ = *argv[0]; 211 (void)strcpy(p, ap); 212 *argv = start; 213 continue; 214 215 /* 216 * Options w/ arguments, skip the argument and continue 217 * with the next option. 218 */ 219 case 'b': 220 case 'c': 221 case 'n': 222 if (!ap[1]) 223 ++argv; 224 /* FALLTHROUGH */ 225 /* Options w/o arguments, continue with the next option. */ 226 case 'f': 227 case 'r': 228 continue; 229 230 /* Illegal option, return and let getopt handle it. */ 231 default: 232 return; 233 } 234 } 235 } 236 237 static void 238 usage() 239 { 240 (void)fprintf(stderr, 241 "usage: tail [-f | -r] [-b # | -c # | -n #] [file]\n"); 242 exit(1); 243 } 244