new utilities.
the .C files compile but are renamed to avoid building automatically.
This commit is contained in:
parent
f08fdedcee
commit
bc7cb1a15a
45 changed files with 16585 additions and 0 deletions
362
src/cmd/tail.c
Normal file
362
src/cmd/tail.c
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
#include <bio.h>
|
||||
|
||||
/*
|
||||
* tail command, posix plus v10 option -r.
|
||||
* the simple command tail -c, legal in v10, is illegal
|
||||
*/
|
||||
|
||||
long count;
|
||||
int anycount;
|
||||
int follow;
|
||||
int file = 0;
|
||||
char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
|
||||
|
||||
Biobuf bout;
|
||||
enum
|
||||
{
|
||||
BEG,
|
||||
END
|
||||
} origin = END;
|
||||
enum
|
||||
{
|
||||
CHARS,
|
||||
LINES
|
||||
} units = LINES;
|
||||
enum
|
||||
{
|
||||
FWD,
|
||||
REV
|
||||
} dir = FWD;
|
||||
|
||||
extern void copy(void);
|
||||
extern void fatal(char*);
|
||||
extern int getnumber(char*);
|
||||
extern void keep(void);
|
||||
extern void reverse(void);
|
||||
extern void skip(void);
|
||||
extern void suffix(char*);
|
||||
extern long tread(char*, long);
|
||||
extern void trunc(Dir*, Dir**);
|
||||
extern long tseek(long, int);
|
||||
extern void twrite(char*, long);
|
||||
extern void usage(void);
|
||||
|
||||
#define JUMP(o,p) tseek(o,p), copy()
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int seekable, c;
|
||||
|
||||
Binit(&bout, 1, OWRITE);
|
||||
for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
|
||||
if(getnumber(argv[1])) {
|
||||
suffix(argv[1]);
|
||||
continue;
|
||||
} else
|
||||
if(c == '-')
|
||||
switch(argv[1][1]) {
|
||||
case 'c':
|
||||
units = CHARS;
|
||||
case 'n':
|
||||
if(getnumber(argv[1]+2))
|
||||
continue;
|
||||
else
|
||||
if(argc > 2 && getnumber(argv[2])) {
|
||||
argc--, argv++;
|
||||
continue;
|
||||
} else
|
||||
usage();
|
||||
case 'r':
|
||||
dir = REV;
|
||||
continue;
|
||||
case 'f':
|
||||
follow++;
|
||||
continue;
|
||||
case '-':
|
||||
argc--, argv++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(dir==REV && (units==CHARS || follow || origin==BEG))
|
||||
fatal("incompatible options");
|
||||
if(!anycount)
|
||||
count = dir==REV? ~0UL>>1: 10;
|
||||
if(origin==BEG && units==LINES && count>0)
|
||||
count--;
|
||||
if(argc > 2)
|
||||
usage();
|
||||
if(argc > 1 && (file=open(argv[1],0)) < 0)
|
||||
fatal(argv[1]);
|
||||
seekable = seek(file,0L,0) == 0;
|
||||
|
||||
if(!seekable && origin==END)
|
||||
keep();
|
||||
else
|
||||
if(!seekable && origin==BEG)
|
||||
skip();
|
||||
else
|
||||
if(units==CHARS && origin==END)
|
||||
JUMP(-count, 2);
|
||||
else
|
||||
if(units==CHARS && origin==BEG)
|
||||
JUMP(count, 0);
|
||||
else
|
||||
if(units==LINES && origin==END)
|
||||
reverse();
|
||||
else
|
||||
if(units==LINES && origin==BEG)
|
||||
skip();
|
||||
if(follow && seekable)
|
||||
for(;;) {
|
||||
static Dir *sb0, *sb1;
|
||||
trunc(sb1, &sb0);
|
||||
copy();
|
||||
trunc(sb0, &sb1);
|
||||
sleep(5000);
|
||||
}
|
||||
exits(0);
|
||||
}
|
||||
|
||||
void
|
||||
trunc(Dir *old, Dir **new)
|
||||
{
|
||||
Dir *d;
|
||||
ulong olength;
|
||||
|
||||
d = dirfstat(file);
|
||||
if(d == nil)
|
||||
return;
|
||||
olength = 0;
|
||||
if(old)
|
||||
olength = old->length;
|
||||
if(d->length < olength)
|
||||
d->length = tseek(0L, 0);
|
||||
free(*new);
|
||||
*new = d;
|
||||
}
|
||||
|
||||
void
|
||||
suffix(char *s)
|
||||
{
|
||||
while(*s && strchr("0123456789+-", *s))
|
||||
s++;
|
||||
switch(*s) {
|
||||
case 'b':
|
||||
if((count *= 1024) < 0)
|
||||
fatal("too big");
|
||||
case 'c':
|
||||
units = CHARS;
|
||||
case 'l':
|
||||
s++;
|
||||
}
|
||||
switch(*s) {
|
||||
case 'r':
|
||||
dir = REV;
|
||||
return;
|
||||
case 'f':
|
||||
follow++;
|
||||
return;
|
||||
case 0:
|
||||
return;
|
||||
}
|
||||
usage();
|
||||
}
|
||||
|
||||
/*
|
||||
* read past head of the file to find tail
|
||||
*/
|
||||
void
|
||||
skip(void)
|
||||
{
|
||||
int i;
|
||||
long n;
|
||||
char buf[Bsize];
|
||||
if(units == CHARS) {
|
||||
for( ; count>0; count -=n) {
|
||||
n = count<Bsize? count: Bsize;
|
||||
if(!(n = tread(buf, n)))
|
||||
return;
|
||||
}
|
||||
} else /*units == LINES*/ {
|
||||
n = i = 0;
|
||||
while(count > 0) {
|
||||
if(!(n = tread(buf, Bsize)))
|
||||
return;
|
||||
for(i=0; i<n && count>0; i++)
|
||||
if(buf[i]=='\n')
|
||||
count--;
|
||||
}
|
||||
twrite(buf+i, n-i);
|
||||
}
|
||||
copy();
|
||||
}
|
||||
|
||||
void
|
||||
copy(void)
|
||||
{
|
||||
long n;
|
||||
char buf[Bsize];
|
||||
while((n=tread(buf, Bsize)) > 0) {
|
||||
twrite(buf, n);
|
||||
Bflush(&bout); /* for FWD on pipe; else harmless */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* read whole file, keeping the tail
|
||||
* complexity is length(file)*length(tail).
|
||||
* could be linear.
|
||||
*/
|
||||
void
|
||||
keep(void)
|
||||
{
|
||||
int len = 0;
|
||||
long bufsiz = 0;
|
||||
char *buf = 0;
|
||||
int j, k, n;
|
||||
|
||||
for(n=1; n;) {
|
||||
if(len+Bsize > bufsiz) {
|
||||
bufsiz += 2*Bsize;
|
||||
if(!(buf = realloc(buf, bufsiz+1)))
|
||||
fatal("out of space");
|
||||
}
|
||||
for(; n && len<bufsiz; len+=n)
|
||||
n = tread(buf+len, bufsiz-len);
|
||||
if(count >= len)
|
||||
continue;
|
||||
if(units == CHARS)
|
||||
j = len - count;
|
||||
else {
|
||||
/* units == LINES */
|
||||
j = buf[len-1]=='\n'? len-1: len;
|
||||
for(k=0; j>0; j--)
|
||||
if(buf[j-1] == '\n')
|
||||
if(++k >= count)
|
||||
break;
|
||||
}
|
||||
memmove(buf, buf+j, len-=j);
|
||||
}
|
||||
if(dir == REV) {
|
||||
if(len>0 && buf[len-1]!='\n')
|
||||
buf[len++] = '\n';
|
||||
for(j=len-1 ; j>0; j--)
|
||||
if(buf[j-1] == '\n') {
|
||||
twrite(buf+j, len-j);
|
||||
if(--count <= 0)
|
||||
return;
|
||||
len = j;
|
||||
}
|
||||
}
|
||||
if(count > 0)
|
||||
twrite(buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* count backward and print tail of file
|
||||
*/
|
||||
void
|
||||
reverse(void)
|
||||
{
|
||||
int first;
|
||||
long len = 0;
|
||||
long n = 0;
|
||||
long bufsiz = 0;
|
||||
char *buf = 0;
|
||||
long pos = tseek(0L, 2);
|
||||
|
||||
for(first=1; pos>0 && count>0; first=0) {
|
||||
n = pos>Bsize? Bsize: (int)pos;
|
||||
pos -= n;
|
||||
if(len+n > bufsiz) {
|
||||
bufsiz += 2*Bsize;
|
||||
if(!(buf = realloc(buf, bufsiz+1)))
|
||||
fatal("out of space");
|
||||
}
|
||||
memmove(buf+n, buf, len);
|
||||
len += n;
|
||||
tseek(pos, 0);
|
||||
if(tread(buf, n) != n)
|
||||
fatal("length error");
|
||||
if(first && buf[len-1]!='\n')
|
||||
buf[len++] = '\n';
|
||||
for(n=len-1 ; n>0 && count>0; n--)
|
||||
if(buf[n-1] == '\n') {
|
||||
count--;
|
||||
if(dir == REV)
|
||||
twrite(buf+n, len-n);
|
||||
len = n;
|
||||
}
|
||||
}
|
||||
if(dir == FWD) {
|
||||
tseek(n==0? 0 : pos+n+1, 0);
|
||||
copy();
|
||||
} else
|
||||
if(count > 0)
|
||||
twrite(buf, len);
|
||||
}
|
||||
|
||||
long
|
||||
tseek(long o, int p)
|
||||
{
|
||||
o = seek(file, o, p);
|
||||
if(o == -1)
|
||||
fatal("");
|
||||
return o;
|
||||
}
|
||||
|
||||
long
|
||||
tread(char *buf, long n)
|
||||
{
|
||||
int r = read(file, buf, n);
|
||||
if(r == -1)
|
||||
fatal("");
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
twrite(char *s, long n)
|
||||
{
|
||||
if(Bwrite(&bout, s, n) != n)
|
||||
fatal("");
|
||||
}
|
||||
|
||||
int
|
||||
getnumber(char *s)
|
||||
{
|
||||
if(*s=='-' || *s=='+')
|
||||
s++;
|
||||
if(!isdigit(*s))
|
||||
return 0;
|
||||
if(s[-1] == '+')
|
||||
origin = BEG;
|
||||
if(anycount++)
|
||||
fatal("excess option");
|
||||
count = atol(s);
|
||||
|
||||
/* check range of count */
|
||||
if(count < 0 || (int)count != count)
|
||||
fatal("too big");
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
fatal(char *s)
|
||||
{
|
||||
char buf[ERRMAX];
|
||||
|
||||
errstr(buf, sizeof buf);
|
||||
fprint(2, "tail: %s: %s\n", s, buf);
|
||||
exits(s);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "%s\n", umsg);
|
||||
exits("usage");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue