Annotation of coherent/d/bin/tail.c, revision 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.