|
|
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.