123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- /*
- * tail.c - replaces Unix version which uses a fixed size buffer and
- * therefore can only deliver a limited number of lines.
- * The -r option (print backwards) is unimplemented, because I
- * haven't figured out the right thing to do. Printing the entire
- * file, as the manual says, could run out of memory and probably
- * isn't what you want anyway.
- * --phr, 25-27 June 84.
- */
- #include <stdio.h>
- #include <sys/file.h>
- #include <errno.h>
- #define DEFAULTLINES 20 /* number of lines/chars/blocks to read */
- #define BLOCKSIZE 512
- #define BUFSIZE (BLOCKSIZE*8)
- /* modes */
- #define BACKWARD 02
- #define CHARS 010
- #define FOREVER 020
- #define HARDWAY 040
- extern int errno;
- char *index (); /* why the f* do I need this? */
- char *malloc (), *xmalloc ();
- char *progname; /* "tail" */
- main (argc, argv)
- int argc;
- char **argv;
- {
- int fd;
- int mode = 0;
- char c, bgetc ();
- int number = -DEFAULTLINES;
- progname = argv[0];
-
- if (argv[1][0] == '+' || argv[1][0] == '-')
- {
- number = atoi (argv[1]);
- --argc;
- argv++;
- if (number == 0)
- number = -DEFAULTLINES;
- /* now skip over the number */
- while (index ("+-0123456789 ", argv[0][0]) != NULL)
- if (*++argv[0] == '\0')
- break;
- while (**argv)
- {
- switch (*argv[0]++)
- {
- case 'l':
- mode &= ~CHARS; /* do-nothing */
- break;
- case 'b':
- number *= BLOCKSIZE;
- /* fall through */
- case 'c':
- mode |= CHARS;
- break;
- case 'r':
- mode |= BACKWARD;
- break;
- case 'f':
- mode |= FOREVER;
- break;
- case 'H':
- mode |= HARDWAY;
- break;
- }
- }
- if ((mode & (FOREVER|BACKWARD)) == (FOREVER|BACKWARD))
- {
- fprintf (2, "%s: CAN'T print backwards forever!\n", progname);
- exit (1);
- }
- }
- if (argc > 1 && (fd = open (argv[1], O_RDONLY)) < 0)
- {
- perror (argv[1]);
- exit (1);
- }
- tail (fd, mode, number);
- exit (0);
- }
- tail (f, mode, number)
- int f;
- int mode, number;
- {
- char buf[BUFSIZE];
- int pos;
- if (mode & CHARS)
- {
- if ((mode & HARDWAY) ||
- lseek (f, number, number>0 ? L_SET : L_XTND) < 0)
- {
- hardchars (f, mode, number);
- return 0;
- }
- if (!(mode & BACKWARD))
- {
- dump_remainder (f, mode);
- return 0;
- }
- /* do backward chars here (?) */
- }
- else
- {
- if ((mode & HARDWAY) ||
- number > 0 ||
- (pos = lseek (f, 0, L_XTND)) < 0)
- {
- hardlines (f, mode, number);
- return 0;
- }
- if (!(mode & BACKWARD))
- {
- register int n;
- /*
- * set n to the no. of chars to be read,
- * 0 < n <= BUFSIZE;
- * new pos will be a multiple of BUFSIZE
- */
- n = (pos - 1) % BUFSIZE + 1;
- pos -= n;
- lseek (f, pos, L_SET);
- n = read (f, buf, n);
- if (n >= 0) /* Can be 0 doing tail-f of zero-length file */
- do
- {
- register int j;
- for (j = n-1; j >= 0; j--)
- {
- if (buf[j] == '\n' && number++ >= 0)
- {
- /* flush partial block */
- if (j != n-1)
- write (1, &buf[j+1], n-j-1);
- dump_remainder (f, mode);
- return 0;
- }
- }
- /* didn't find enough newlines in that bufferload */
- if (pos == 0)
- {
- /* not enough lines, print entire file */
- lseek (f, 0, L_SET);
- dump_remainder (f, mode);
- return 0;
- }
- pos -= BUFSIZE;
- lseek (f, pos, L_SET);
- } while ((n = read (f, buf, BUFSIZE)) > 0);
- }
- }
- }
- dump_remainder (f, mode)
- int f, mode;
- {
- int n;
- char buf[BUFSIZE];
- output:
- while ((n = read (f, buf, BUFSIZE)) > 0)
- write (1, buf, n);
- if (mode & FOREVER)
- {
- sleep (1);
- goto output;
- }
- }
- hardlines (f, mode, number)
- int f, mode, number;
- {
- struct buf { struct buf *next;
- int nchars, linect;
- char buf[BUFSIZE];
- };
- typedef struct buf BUF;
- BUF *first;
- register BUF *last, *spare;
- register int i;
- int nlines;
- if (number > 0)
- return (head (f, mode, number));
- /*
- * Input is always read into a fresh buffer.
- * If there is enough room in the last buffer read, then the
- * contents of the new one are just moved into it. This is because
- * when reading from pipes, nread can often be very small.
- *
- * If there's not enough room, link the new buffer onto the end
- * of the list, then either free up the oldest buffer for the
- * next read if that would leave enough lines, or else malloc
- * a new one.
- * Some compaction mechanism is possible but probably not worthwhile.
- */
- first = last = (BUF *) xmalloc (sizeof (BUF));
- first->nchars = first->linect = 0;
- last->nchars = last->linect = 0;
- spare = (BUF *) xmalloc (sizeof (BUF));
- nlines = 0;
- while ((spare->nchars = read (f, spare->buf, BUFSIZE)) > 0)
- {
- spare->linect = 0;
- spare->next = NULL;
- for (i = 0; i < spare->nchars; i++)
- if (spare->buf[i] == '\n')
- ++spare->linect;
- nlines += spare->linect;
- if (spare->nchars + last->nchars < BUFSIZE)
- {
- strncpy (&last->buf[last->nchars], spare->buf, spare->nchars);
- last->nchars += spare->nchars;
- last->linect += spare->linect;
- }
- else
- {
- last = last->next = spare;
- if (nlines - first->linect > -number)
- {
- spare = first;
- nlines -= first->linect;
- first = first->next;
- }
- else
- spare = (BUF *) xmalloc (sizeof (BUF));
- }
- }
- /* now run through the list, printing lines. */
- /* first, skip over unneeded buffers */
- for (spare = first; spare != NULL; spare = spare->next)
- {
- if (nlines - spare->linect <= -number)
- break;
- nlines -= spare->linect;
- }
- if (spare == NULL)
- {
- fprintf (2, "%s: Impossible error in hardlines! Please report bug\n", progname);
- exit (3);
- }
- /* find correct beginning, then print the rest of the file. */
- if (nlines > -number)
- {
- register char *p = spare->buf;
- /* remember: number < 0; nlines+number < spare->linect */
- for (i = 0; i < nlines + number; i++)
- /* Can't use index() -- that loses on lines containing \0 */
- while (*p++ != '\n')
- ;
- i = p - spare->buf;
- }
- else
- i = 0;
- write (1, &spare->buf[i], spare->nchars-i);
- for (spare = spare->next; spare != NULL; spare = spare->next)
- write (1, spare->buf, spare->nchars);
- }
- hardchars (f, mode, number) /* stripped down version of hardlines */
- int f, mode, number;
- {
- struct buf { struct buf *next;
- int nchars;
- char buf[BUFSIZE];
- };
- typedef struct buf BUF;
- BUF *first;
- register BUF *last, *spare;
- register int i;
- int nchars;
- if (number > 0)
- return (head (f, mode, number));
- /*
- * Same comment as last time, more or less.
- */
- first = last = (BUF *) xmalloc (sizeof (BUF));
- first->nchars = last->nchars = 0;
- spare = (BUF *) xmalloc (sizeof (BUF));
- nchars = 0;
- while ((spare->nchars = read (f, spare->buf, BUFSIZE)) > 0)
- {
- spare->next = NULL;
- nchars += spare->nchars;
- if (spare->nchars + last->nchars < BUFSIZE)
- {
- strncpy (&last->buf[last->nchars], spare->buf, spare->nchars);
- last->nchars += spare->nchars;
- }
- else
- {
- last = last->next = spare;
- if (nchars - first->nchars > -number)
- {
- spare = first;
- nchars -= first->nchars;
- first = first->next;
- }
- else
- {
- spare = (BUF *) xmalloc (sizeof (BUF));
- }
- }
- }
- /* now run through the list, printing lines. */
- /* first, skip over unneeded buffers */
- for (spare = first; spare != NULL; spare = spare->next)
- {
- if (nchars - spare->nchars <= -number)
- break;
- nchars -= spare->nchars;
- }
- if (spare == NULL)
- {
- fprintf (2, "%s: Impossible error in hardchars! Please report bug\n",
- progname);
- exit (3);
- }
- /* find correct beginning, then print the rest of the file. */
- if (nchars > -number)
- i = nchars + number;
- else
- i = 0;
- write (1, &spare->buf[i], spare->nchars-i);
- for (spare = spare->next; spare != NULL; spare = spare->next)
- write (1, spare->buf, spare->nchars);
- }
- head (f, mode, number)
- int f, mode, number;
- {
- int n, nread;
- register int i;
- char buf[BUFSIZE];
- if (number <= 0)
- return 0;
- while (nread < number)
- {
- n = read (f, buf, BUFSIZE);
- if (n <= 0)
- break;
- if (mode & CHARS)
- nread += n;
- else
- for (i = 0; i < n; i++)
- if (buf[i] == '\n')
- if (++nread >= number-1)
- goto out;
- }
- out:
- if (mode & CHARS)
- {
- if (nread > number)
- write (1, &buf[n-(nread-number)], nread-number);
- }
- else
- {
- if (++i < n) /* skip over newline */
- write (1, &buf[i], n-i);
- }
- dump_remainder (f, mode);
- }
- char *
- xmalloc (size)
- int size;
- {
- register char *val;
- val = malloc (size);
- if (val) return (val);
- fprintf (2, "%s: memory exhausted.\n", progname);
- exit (3);
- }
|