Annotation of coherent/d/bin/tail.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.