|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 9716 (0x25f4)
Types: TextFile
Notes: UNIX file
Names: »tail.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/tail.c«
/*
* Tail writes from a certain position on a file to EOF onto stdout.
* The position is specified as a number of characters, blocks or
* lines from either the start or the end of the file.
* If possible, tail uses fseeks to position itself, otherwise
* it reads thru the file.
*/
/*
* Include files.
*/
#include <stdio.h>
#include <ctype.h>
#include <mdata.h>
#include <sys/stat.h>
/*
* Manifest constants.
*/
#define BLKSIZE 512 /* bytes per block */
#define BUFL 512 /* text buffer quantum */
#define PAGE 10 /* default is to list last PAGE lines */
#define MAXLINE 512 /* line buffer length */
#define TRUE (0 == 0)
#define FALSE (0 != 0)
#define START 0 /* fseek from start of file */
#define CURRENT 1 /* fseek from current position of file */
#define END 2 /* fseek from end of file */
/*
* Globals.
*/
FILE *infp = stdin;
int fflag = FALSE; /* TRUE iff we are skipping forward */
int lflag = TRUE; /* TRUE iff we are skipping lines */
int cflag = FALSE; /* TRUE iff we want "continuous read" */
int skflg = FALSE; /* TRUE iff we can seek on the file */
long skipcnt = PAGE;
long eofp = 0L; /* saved EOF pointer position */
char errmsg[]= "Window too large";
/*
* Functions returning non-ints.
*/
char *alloc(), /* allocate space */
*index();
char *nextline();
long ftell();
/*
* The struct buf is used to save lines of the file when
* reading thru a file to copy the last few lines.
*/
struct buf {
struct buf *next; /* link to next buf */
int nlines; /* number of new lines in this buf */
char buff[BUFL]; /* buff of characters */
}
/*
* Tail.
*/
main(argc, argv)
int argc;
char **argv;
{
static char obuf[BUFSIZ]; /* output buffer */
setbuf(stdout, obuf);
options(argc, argv);
skflg = skbl();
if (fflag) {
if (lflag)
skipl();
else
if (skflg)
fseek(infp, skipcnt, START);
else
skipc();
copy();
} else
if (lflag)
if (skflg)
backls();
else
backlu();
else
if (skflg)
backcs();
else
backcu();
if (cflag) /* continuous output mode selected? */
copycont(); /* yes, keep reading at EOF */
exit(0);
}
/*
* Options processes all arguments.
*/
options(argc, argv)
register int argc;
register char **argv;
{
register char *chp;
++argv;
if (--argc > 0 && (**argv=='+' || **argv=='-')) {
--argc;
chp = *argv++;
fflag = *chp++ == '+';
if ((*chp != 'f') && (!isascii(*chp) || !isdigit(*chp)))
usage();
if (*chp != 'f')
{ skipcnt = 0;
do {
skipcnt = 10 * skipcnt + *chp++ - '0';
} while (isascii(*chp) && isdigit(*chp));
}
switch (*chp++) {
case '\0':
case 'l':
break;
case 'b':
skipcnt *= BLKSIZE;
case 'c':
lflag = FALSE;
break;
case 'f': /* continuous read mode? */
cflag = TRUE;
break;
default:
usage();
}
if (*chp == 'f') /* continuous read mode can be here, too! */
{ if (cflag) /* already selected? */
usage(); /* error */
cflag = TRUE; /* set mode */
}
}
if (argc > 1)
usage(); /* too many args */
if (argc > 0) {
infp = fopen(*argv, "r");
if (infp == NULL)
die("can't open %s", *argv);
}
}
/*
** Go to next newline plus one char.
** The index() call had problems if the buff
** contained '\0' bytes, e.g. if the file
** was sparse.
*/
char *
nextline(cp)
char *cp;
{
for( ;; ) {
if (*cp == '\n')
return (++cp);
cp++;
}
}
usage()
{
fprintf(stderr, "Usage: tail [+-number[lbc][f]] [file]\n");
exit(1);
}
die(str)
char *str;
{
fprintf(stderr, "tail: %r\n", &str);
exit(1);
}
/*
* Skbl returns TRUE iff the stream infp will correctly
* handle fseeks.
*/
skbl()
{
struct stat stbuf;
fstat(fileno(infp), &stbuf);
if ((stbuf.st_mode & S_IFMT) == S_IFCHR)
return (FALSE);
return (fseek(infp, (long)0, CURRENT) != EOF);
}
/*
* Skipl skips skipcnt lines on the input file.
* Note that on exit, skipcnt is zero.
*/
skipl()
{
register FILE *fp = infp;
register int ch;
do {
while ((ch = getc(fp)) != '\n' && ch != EOF)
;
} while (--skipcnt != 0 && ch != EOF);
}
/*
* Skipc skips skipcnt characters on the input file
* without useing seeks on the file.
* Note that on exit skipcnt is 0.
*/
skipc()
{
register FILE *fp = infp;
register int ch;
do {
ch = getc(fp);
} while (--skipcnt != 0 && ch != EOF);
}
/*
* Copy copies the rest of the input file to stdout.
*/
copy()
{
register FILE *fp = infp;
register int ch;
for (;;)
{ if ((ch = getc(fp)) != EOF)
putchar(ch); /* display the char */
else
return; /* all done */
}
}
/*
* Backcs copyies the last skipcnt characters of the input file
* to stdout assumeing that seeks will work on the file.
*/
backcs()
{
if (fseek(infp, - skipcnt, END) == EOF)
rewind(infp);
copy();
}
/*
* Backcu copyies the last skipcnt characters of the input file
* to stdout without using any fseeks.
*/
backcu()
{
register FILE *fp = infp;
register char *bp, *buff;
char *limit;
int full;
buff = (char *)alloc(check(skipcnt * sizeof *buff));
limit = &buff[(int)skipcnt];
full = FALSE;
bp = buff;
do {
bp += fread(bp, sizeof *bp, limit - bp, fp);
if (bp >= limit) {
bp = buff;
full = TRUE;
}
} while (!feof(fp));
if (full)
fwrite(bp, sizeof *bp, limit - bp, stdout);
fwrite(buff, sizeof *buff, bp - buff, stdout);
}
/*
* Alloc returns a pointer to a block of memory of size
* len. If such a block is un-obtainable, it dies with
* an appropriate error message.
*/
char *
alloc(len)
int len;
{
register char *res;
char *malloc();
res = (char *)malloc(len);
if (res == NULL)
die(errmsg);
return (res);
}
/*
* Check returns lnum converted to an int if possible.
* If lnum is too big, then it dies with an appropriate
* error message.
*/
int
check(lnum)
long lnum;
{
if (lnum > MAXINT)
die(errmsg);
return ((int)lnum);
}
/*
* Backlu copyies the last skipcnt lines from the input file to
* stdout without doing any fseeks.
*/
backlu()
{
register struct buf *bp;
register int cnt;
struct buf *first, **last;
int incore;
int nib;
char *start;
struct buf *tmpbp;
check(skipcnt + BUFL);
cnt = (int)skipcnt;
incore = 0;
first = NULL;
last = &first;
do {
bp = (struct buf *)alloc(sizeof *bp);
*last = bp;
nib = rdblk(bp->buff, BUFL);
bp->nlines = lncnt(bp->buff, BUFL - nib);
incore += bp->nlines;
last = &(bp->next);
for (bp = first; incore > cnt + bp->nlines; bp = tmpbp) {
incore -= bp->nlines;
tmpbp = bp->next;
free(bp);
}
first = bp;
} while (nib == 0);
*last = NULL;
for (start = bp->buff; incore > cnt; --incore)
/* start = index(start, '\n') + 1; */
start = nextline(start);
cnt = BUFL - (start - bp->buff);
for (bp = bp->next; bp != NULL; bp = bp->next) {
fwrite(start, sizeof *start, cnt, stdout);
start = bp->buff;
cnt = BUFL;
}
cnt -= nib;
fwrite(start, sizeof *start, cnt, stdout);
}
/*
* Rdblk reads in a block from the input file infp into the array
* blk. The only thing that will prevent rblk from filling the
* array is EOF, and to detect this, it returns the number of
* unfilled bytes.
*/
int
rdblk(blk, len)
register char *blk;
register int len;
{
register int got;
do {
got = fread(blk, sizeof *blk, len, infp);
len -= got;
blk += got;
} while (len != 0 && got != 0);
return (len);
}
/*
* Lncnt returns the number of newline characters in a buffer
* that starts at buff and has length len.
*/
int
lncnt(buff, len)
register char *buff;
register int len;
{
register int cnt;
for (cnt = 0; --len >= 0;)
if (*buff++ == '\n')
++cnt;
return (cnt);
}
/*
* Backls copyies the last skipcnt lines of the input file infp to
* stdout. It assumes that seeks will work on infp.
*/
backls()
{
register struct buf *list;
register char *start;
register int extra;
int cnt;
struct buf *tmp;
extra = rdback(&tmp, check(skipcnt), &cnt);
list = tmp;
start = list->buff;
while (--extra >= 0)
/* start = 1 + index(start, '\n'); */
start = nextline(start);
cnt -= start - list->buff;
fwrite(start, sizeof *start, cnt, stdout);
for (list = list->next; list != NULL; list = list->next)
fwrite(list->buff, 1, BUFL, stdout);
}
/*
* Rdback reads the input file infp backwards into memory.
* Specifically, it sets bpp to point to the head of a linked
* list of bufs containing either more than goal lines or
* all of the file. It sets shrt to the number of chars in the
* first buf (which may be short due to EOF) and returns the
* number of extra newlines.
*/
int
rdback(bpp, goal, shrt)
struct buf **bpp;
int goal,
*shrt;
{
register struct buf *bp;
register int cnt;
long pos;
struct buf *rest;
rest = NULL;
fseek(infp, (long)0, END);
eofp = pos = ftell(infp);
cnt = BUFL;
while (goal >= 0 && cnt == BUFL) {
bp = (struct buf *)alloc(sizeof *bp);
bp->next = rest;
rest = bp;
pos -= BUFL;
if (pos < 0) {
cnt = pos + BUFL;
pos = 0;
}
fseek(infp, pos, START);
rdblk(bp->buff, cnt);
goal -= lncnt(bp->buff, cnt);
}
*bpp = bp;
*shrt = cnt;
return (-goal);
}
/*
* Copycont tries to keep reading the input file, and copying
* it to stdout, in the hope that the file will grow.
*/
copycont()
{
register int fd, count;
char tbuff[512];
fflush(stdout); /* flush any pending output */
fd = fileno(infp); /* get input file descriptor */
if (eofp == 0L) /* if no saved EOF pointer */
eofp = ftell(infp); /* get one */
lseek(fd, eofp, 0); /* seek to EOF pointer */
for (;;)
{ if ((count = read(fd, tbuff, 512)) > 0) /* try read from file */
{ write(1, tbuff, count); /* send to stdout */
continue;
}
sleep(1); /* wait a little while */
}
}