|
|
1.1 ! root 1: /* ! 2: * Tail writes from a certain position on a file to EOF onto stdout. ! 3: * The position is specified as a number of characters, blocks or ! 4: * lines from either the start or the end of the file. ! 5: * If possible, tail uses fseeks to position itself, otherwise ! 6: * it reads thru the file. ! 7: */ ! 8: ! 9: ! 10: /* ! 11: * Include files. ! 12: */ ! 13: ! 14: #include <stdio.h> ! 15: #include <ctype.h> ! 16: #include <sys/mdata.h> ! 17: #ifdef COHERENT ! 18: #include <sys/stat.h> ! 19: #endif ! 20: ! 21: ! 22: /* ! 23: * Manifest constants. ! 24: */ ! 25: ! 26: #define BLKSIZE 512 /* bytes per block */ ! 27: #define BUFL 512 /* text buffer quantum */ ! 28: #define PAGE 10 /* default is to list last PAGE lines */ ! 29: #define MAXLINE 512 /* line buffer length */ ! 30: #define TRUE (0 == 0) ! 31: #define FALSE (0 != 0) ! 32: #define START 0 /* fseek from start of file */ ! 33: #define CURRENT 1 /* fseek from current position of file */ ! 34: #define END 2 /* fseek from end of file */ ! 35: ! 36: ! 37: /* ! 38: * Globals. ! 39: */ ! 40: ! 41: FILE *infp = stdin; ! 42: int fflag = FALSE; /* TRUE iff we are skipping forward */ ! 43: int lflag = TRUE; /* TRUE iff we are skipping lines */ ! 44: int cflag = FALSE; /* TRUE iff we want "continuous read" */ ! 45: int skflg = FALSE; /* TRUE iff we can seek on the file */ ! 46: long skipcnt = PAGE; ! 47: long eofp = 0L; /* saved EOF pointer position */ ! 48: char errmsg[]= "Window too large"; ! 49: ! 50: /* ! 51: * Functions returning non-ints. ! 52: */ ! 53: ! 54: char *alloc(), /* allocate space */ ! 55: *index(); ! 56: char *nextline(); ! 57: long ftell(); ! 58: ! 59: ! 60: /* ! 61: * The struct buf is used to save lines of the file when ! 62: * reading thru a file to copy the last few lines. ! 63: */ ! 64: ! 65: struct buf { ! 66: struct buf *next; /* link to next buf */ ! 67: int nlines; /* number of new lines in this buf */ ! 68: char buff[BUFL]; /* buff of characters */ ! 69: } ! 70: ! 71: ! 72: /* ! 73: * Tail. ! 74: */ ! 75: ! 76: main(argc, argv) ! 77: int argc; ! 78: char **argv; ! 79: { ! 80: static char obuf[BUFSIZ]; /* output buffer */ ! 81: ! 82: #ifdef MSDOS ! 83: msdoscvt("tail", &argc, &argv); ! 84: _setbinary(stdin); ! 85: _setbinary(stdout); ! 86: #endif ! 87: setbuf(stdout, obuf); ! 88: options(argc, argv); ! 89: #ifdef COHERENT ! 90: skflg = skbl(); ! 91: #else ! 92: skflg = FALSE; ! 93: #endif ! 94: if (fflag) { ! 95: if (lflag) ! 96: skipl(); ! 97: else ! 98: if (skflg) ! 99: fseek(infp, skipcnt, START); ! 100: else ! 101: skipc(); ! 102: copy(); ! 103: } else ! 104: if (lflag) ! 105: if (skflg) ! 106: backls(); ! 107: else ! 108: backlu(); ! 109: else ! 110: if (skflg) ! 111: backcs(); ! 112: else ! 113: backcu(); ! 114: ! 115: if (cflag) /* continuous output mode selected? */ ! 116: copycont(); /* yes, keep reading at EOF */ ! 117: ! 118: exit(0); ! 119: } ! 120: ! 121: ! 122: /* ! 123: * Options processes all arguments. ! 124: */ ! 125: ! 126: options(argc, argv) ! 127: register int argc; ! 128: register char **argv; ! 129: { ! 130: register char *chp; ! 131: ! 132: ++argv; ! 133: if (--argc > 0 && (**argv=='+' || **argv=='-')) { ! 134: --argc; ! 135: chp = *argv++; ! 136: fflag = *chp++ == '+'; ! 137: if ((*chp != 'f') && (!isascii(*chp) || !isdigit(*chp))) ! 138: usage(); ! 139: if (*chp != 'f') ! 140: { skipcnt = 0; ! 141: do { ! 142: skipcnt = 10 * skipcnt + *chp++ - '0'; ! 143: } while (isascii(*chp) && isdigit(*chp)); ! 144: } ! 145: switch (*chp++) { ! 146: case '\0': ! 147: chp--; ! 148: break; ! 149: case 'l': ! 150: break; ! 151: case 'b': ! 152: skipcnt *= BLKSIZE; ! 153: case 'c': ! 154: lflag = FALSE; ! 155: break; ! 156: /* not documented */ ! 157: case 'f': /* continuous read mode? */ ! 158: cflag = TRUE; ! 159: break; ! 160: default: ! 161: usage(); ! 162: } ! 163: if (*chp == 'f') /* continuous read mode can be here, too! */ ! 164: { if (cflag) /* already selected? */ ! 165: usage(); /* error */ ! 166: cflag = TRUE; /* set mode */ ! 167: } ! 168: } ! 169: if (argc > 1) ! 170: usage(); /* too many args */ ! 171: if (argc > 0) { ! 172: infp = fopen(*argv, "rb"); ! 173: if (infp == NULL) ! 174: die("can't open %s", *argv); ! 175: } ! 176: } ! 177: ! 178: ! 179: /* ! 180: ** Go to next newline plus one char. ! 181: ** The index() call had problems if the buff ! 182: ** contained '\0' bytes, e.g. if the file ! 183: ** was sparse. ! 184: */ ! 185: ! 186: char * ! 187: nextline(cp) ! 188: char *cp; ! 189: { ! 190: for( ;; ) { ! 191: if (*cp == '\n') ! 192: return (++cp); ! 193: cp++; ! 194: } ! 195: } ! 196: ! 197: usage() ! 198: { ! 199: fprintf(stderr, "Usage: tail [+-number[lbc]] [file]\n"); ! 200: exit(1); ! 201: } ! 202: ! 203: ! 204: die(str) ! 205: char *str; ! 206: { ! 207: fprintf(stderr, "tail: %r\n", &str); ! 208: exit(1); ! 209: } ! 210: ! 211: ! 212: /* ! 213: * Skbl returns TRUE iff the stream infp will correctly ! 214: * handle fseeks. ! 215: */ ! 216: ! 217: skbl() ! 218: { ! 219: #ifdef COHERENT ! 220: struct stat stbuf; ! 221: ! 222: fstat(fileno(infp), &stbuf); ! 223: if ((stbuf.st_mode & S_IFMT) == S_IFCHR) ! 224: return (FALSE); ! 225: #endif ! 226: return (fseek(infp, (long)0, CURRENT) != EOF); ! 227: } ! 228: ! 229: ! 230: /* ! 231: * Skipl skips skipcnt lines on the input file. ! 232: * Note that on exit, skipcnt is zero. ! 233: */ ! 234: ! 235: skipl() ! 236: { ! 237: register FILE *fp = infp; ! 238: register int ch; ! 239: ! 240: do { ! 241: while ((ch = getc(fp)) != '\n' && ch != EOF) ! 242: ; ! 243: } while (--skipcnt != 0 && ch != EOF); ! 244: } ! 245: ! 246: ! 247: /* ! 248: * Skipc skips skipcnt characters on the input file ! 249: * without useing seeks on the file. ! 250: * Note that on exit skipcnt is 0. ! 251: */ ! 252: ! 253: skipc() ! 254: { ! 255: register FILE *fp = infp; ! 256: register int ch; ! 257: ! 258: do { ! 259: ch = getc(fp); ! 260: } while (--skipcnt > 0 && ch != EOF); ! 261: } ! 262: ! 263: ! 264: /* ! 265: * Copy copies the rest of the input file to stdout. ! 266: */ ! 267: ! 268: copy() ! 269: { ! 270: register FILE *fp = infp; ! 271: register int ch; ! 272: ! 273: for (;;) ! 274: { if ((ch = getc(fp)) != EOF) ! 275: putchar(ch); /* display the char */ ! 276: else ! 277: return; /* all done */ ! 278: } ! 279: } ! 280: ! 281: ! 282: /* ! 283: * Backcs copyies the last skipcnt characters of the input file ! 284: * to stdout assumeing that seeks will work on the file. ! 285: */ ! 286: ! 287: backcs() ! 288: { ! 289: if (fseek(infp, - skipcnt, END) == EOF) ! 290: rewind(infp); ! 291: copy(); ! 292: } ! 293: ! 294: ! 295: /* ! 296: * Backcu copyies the last skipcnt characters of the input file ! 297: * to stdout without using any fseeks. ! 298: */ ! 299: ! 300: backcu() ! 301: { ! 302: register FILE *fp = infp; ! 303: register char *bp, *buff; ! 304: char *limit; ! 305: int full; ! 306: ! 307: buff = (char *)alloc(check(skipcnt * sizeof *buff)); ! 308: limit = &buff[(int)skipcnt]; ! 309: full = FALSE; ! 310: bp = buff; ! 311: do { ! 312: bp += fread(bp, sizeof *bp, limit - bp, fp); ! 313: if (bp >= limit) { ! 314: bp = buff; ! 315: full = TRUE; ! 316: } ! 317: } while (!feof(fp)); ! 318: if (full) ! 319: fwrite(bp, sizeof *bp, limit - bp, stdout); ! 320: fwrite(buff, sizeof *buff, bp - buff, stdout); ! 321: } ! 322: ! 323: ! 324: /* ! 325: * Alloc returns a pointer to a block of memory of size ! 326: * len. If such a block is un-obtainable, it dies with ! 327: * an appropriate error message. ! 328: */ ! 329: ! 330: char * ! 331: alloc(len) ! 332: int len; ! 333: { ! 334: register char *res; ! 335: char *malloc(); ! 336: ! 337: res = (char *)malloc(len); ! 338: if (res == NULL) ! 339: die(errmsg); ! 340: return (res); ! 341: } ! 342: ! 343: ! 344: /* ! 345: * Check returns lnum converted to an int if possible. ! 346: * If lnum is too big, then it dies with an appropriate ! 347: * error message. ! 348: */ ! 349: ! 350: int ! 351: check(lnum) ! 352: long lnum; ! 353: { ! 354: if (lnum > MAXINT) ! 355: die(errmsg); ! 356: return ((int)lnum); ! 357: } ! 358: ! 359: ! 360: /* ! 361: * Backlu copyies the last skipcnt lines from the input file to ! 362: * stdout without doing any fseeks. ! 363: */ ! 364: ! 365: backlu() ! 366: { ! 367: register struct buf *bp; ! 368: register int cnt; ! 369: struct buf *first, **last; ! 370: int incore; ! 371: int nib; ! 372: char *start; ! 373: struct buf *tmpbp; ! 374: ! 375: check(skipcnt + BUFL); ! 376: cnt = (int)skipcnt; ! 377: incore = 0; ! 378: first = NULL; ! 379: last = &first; ! 380: do { ! 381: bp = (struct buf *)alloc(sizeof *bp); ! 382: *last = bp; ! 383: nib = rdblk(bp->buff, BUFL); ! 384: bp->nlines = lncnt(bp->buff, BUFL - nib); ! 385: incore += bp->nlines; ! 386: last = &(bp->next); ! 387: for (bp = first; incore > cnt + bp->nlines; bp = tmpbp) { ! 388: incore -= bp->nlines; ! 389: tmpbp = bp->next; ! 390: free(bp); ! 391: } ! 392: first = bp; ! 393: } while (nib == 0); ! 394: *last = NULL; ! 395: for (start = bp->buff; incore > cnt; --incore) ! 396: /* start = index(start, '\n') + 1; */ ! 397: start = nextline(start); ! 398: cnt = BUFL - (start - bp->buff); ! 399: for (bp = bp->next; bp != NULL; bp = bp->next) { ! 400: fwrite(start, sizeof *start, cnt, stdout); ! 401: start = bp->buff; ! 402: cnt = BUFL; ! 403: } ! 404: cnt -= nib; ! 405: fwrite(start, sizeof *start, cnt, stdout); ! 406: } ! 407: ! 408: ! 409: /* ! 410: * Rdblk reads in a block from the input file infp into the array ! 411: * blk. The only thing that will prevent rblk from filling the ! 412: * array is EOF, and to detect this, it returns the number of ! 413: * unfilled bytes. ! 414: */ ! 415: ! 416: int ! 417: rdblk(blk, len) ! 418: register char *blk; ! 419: register int len; ! 420: { ! 421: register int got; ! 422: ! 423: do { ! 424: got = fread(blk, sizeof *blk, len, infp); ! 425: len -= got; ! 426: blk += got; ! 427: } while (len != 0 && got != 0); ! 428: return (len); ! 429: } ! 430: ! 431: ! 432: /* ! 433: * Lncnt returns the number of newline characters in a buffer ! 434: * that starts at buff and has length len. ! 435: */ ! 436: ! 437: int ! 438: lncnt(buff, len) ! 439: register char *buff; ! 440: register int len; ! 441: { ! 442: register int cnt; ! 443: ! 444: for (cnt = 0; --len >= 0;) ! 445: if (*buff++ == '\n') ! 446: ++cnt; ! 447: return (cnt); ! 448: } ! 449: ! 450: ! 451: /* ! 452: * Backls copyies the last skipcnt lines of the input file infp to ! 453: * stdout. It assumes that seeks will work on infp. ! 454: */ ! 455: ! 456: backls() ! 457: { ! 458: register struct buf *list; ! 459: register char *start; ! 460: register int extra; ! 461: int cnt; ! 462: struct buf *tmp; ! 463: ! 464: extra = rdback(&tmp, check(skipcnt), &cnt); ! 465: list = tmp; ! 466: start = list->buff; ! 467: while (--extra >= 0) ! 468: /* start = 1 + index(start, '\n'); */ ! 469: start = nextline(start); ! 470: cnt -= start - list->buff; ! 471: fwrite(start, sizeof *start, cnt, stdout); ! 472: for (list = list->next; list != NULL; list = list->next) ! 473: fwrite(list->buff, 1, BUFL, stdout); ! 474: } ! 475: ! 476: ! 477: /* ! 478: * Rdback reads the input file infp backwards into memory. ! 479: * Specifically, it sets bpp to point to the head of a linked ! 480: * list of bufs containing either more than goal lines or ! 481: * all of the file. It sets shrt to the number of chars in the ! 482: * first buf (which may be short due to EOF) and returns the ! 483: * number of extra newlines. ! 484: */ ! 485: ! 486: int ! 487: rdback(bpp, goal, shrt) ! 488: struct buf **bpp; ! 489: int goal, ! 490: *shrt; ! 491: { ! 492: register struct buf *bp; ! 493: register int cnt; ! 494: long pos; ! 495: struct buf *rest; ! 496: ! 497: rest = NULL; ! 498: fseek(infp, (long)0, END); ! 499: eofp = pos = ftell(infp); ! 500: cnt = BUFL; ! 501: while (goal >= 0 && cnt == BUFL) { ! 502: bp = (struct buf *)alloc(sizeof *bp); ! 503: bp->next = rest; ! 504: rest = bp; ! 505: pos -= BUFL; ! 506: if (pos < 0) { ! 507: cnt = pos + BUFL; ! 508: pos = 0; ! 509: } ! 510: fseek(infp, pos, START); ! 511: rdblk(bp->buff, cnt); ! 512: goal -= lncnt(bp->buff, cnt); ! 513: } ! 514: *bpp = bp; ! 515: *shrt = cnt; ! 516: return (-goal); ! 517: } ! 518: ! 519: /* ! 520: * Copycont tries to keep reading the input file, and copying ! 521: * it to stdout, in the hope that the file will grow. ! 522: */ ! 523: ! 524: copycont() ! 525: { ! 526: register int fd, count; ! 527: char tbuff[512]; ! 528: ! 529: fflush(stdout); /* flush any pending output */ ! 530: fd = fileno(infp); /* get input file descriptor */ ! 531: if (eofp == 0L) /* if no saved EOF pointer */ ! 532: eofp = ftell(infp); /* get one */ ! 533: lseek(fd, eofp, 0); /* seek to EOF pointer */ ! 534: for (;;) ! 535: { if ((count = read(fd, tbuff, 512)) > 0) /* try read from file */ ! 536: { write(1, tbuff, count); /* send to stdout */ ! 537: continue; ! 538: } ! 539: #ifdef COHERENT ! 540: sleep(1); /* wait a little while */ ! 541: #endif ! 542: } ! 543: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.