9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

commit 7dce804fe658181cb8cbb102a525ca34b4fb0006
parent a7102135eff7e934b6616a59dcb83c09bf188f06
Author: Anselm R Garbe <anselm@garbe.us>
Date:   Tue, 27 Apr 2010 14:48:07 +0000

merge
Diffstat:
.hgtags | 1+
LICENSE | 2+-
Makefile | 2+-
README | 2+-
config.mk | 4++--
fortune/fortune.1 | 23+++++++++++++++++++++++
freq/freq.1 | 40++++++++++++++++++++++++++++++++++++++++
mkdir/mkdir.1 | 43+++++++++++++++++++++++++++++++++++++++++++
tac/Makefile | 10----------
tac/tac.1 | 28----------------------------
tac/tac.c | 60------------------------------------------------------------
tail/Makefile | 11+++++++++++
tail/tail.1 | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tail/tail.c | 363+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
14 files changed, 573 insertions(+), 103 deletions(-)

diff --git a/.hgtags b/.hgtags @@ -2,3 +2,4 @@ 538338114742f2e81f6c52a5a323bdf7ca0d5d86 2 7c1decda5b50405d3b62daa8369aa24c82e7b615 3 25d1757fba6bcef82866bad87dcff6f2333673cc 4 +5f3d19e583ff4504cbc6247b711e728bd602d6f2 5 diff --git a/LICENSE b/LICENSE @@ -2,7 +2,7 @@ The rare bits touched by Anselm R. Garbe are under following LICENSE: MIT/X Consortium License -(C)opyright 2005-2009 Anselm R. Garbe <garbeam at gmail dot com> +© 2005-2010 Anselm R Garbe <anselm@garbe.us> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/Makefile b/Makefile @@ -4,7 +4,7 @@ include config.mk SUBDIRS = lib9 yacc awk basename bc cal cat cleanname date dc du echo \ fortune freq getflags grep hoc ls mk mkdir mtime rc read \ - sed seq sleep sort tac tee test touch tr troff uniq + sed seq sleep sort tail tee test touch tr troff uniq # factor primes diff --git a/README b/README @@ -21,4 +21,4 @@ References ---------- [1] http://swtch.com/plan9port/ ---Anselm R. Garbe +--Anselm R Garbe diff --git a/config.mk b/config.mk @@ -5,9 +5,9 @@ PREFIX = /usr/local/plan9 MANPREFIX = ${PREFIX}/share/man VERSION = 5 -#OBJTYPE = 386 +OBJTYPE = 386 #OBJTYPE = arm -OBJTYPE = x86_64 +#OBJTYPE = x86_64 # Linux/BSD #CFLAGS += -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -c -I. -DPREFIX="\"${PREFIX}\"" diff --git a/fortune/fortune.1 b/fortune/fortune.1 @@ -0,0 +1,23 @@ +.TH FORTUNE 1 +.SH NAME +fortune \- sample lines from a file +.SH SYNOPSIS +.B fortune +[ +.I file +] +.SH DESCRIPTION +.I Fortune +prints a one-line aphorism chosen at random. +If a +.I file +is specified, the saying is taken from that file; +otherwise it is selected from +.BR \*9/lib/fortunes . +.SH FILES +.B \*9/lib/fortunes +.br +.B \*9/lib/fortunes.index +\ \ fast lookup table, maintained automatically +.SH SOURCE +.B \*9/src/cmd/fortune.c diff --git a/freq/freq.1 b/freq/freq.1 @@ -0,0 +1,40 @@ +.TH FREQ 1 +.SH NAME +freq \- print histogram of character frequencies +.SH SYNOPSIS +.B freq +[ +.B -dxocr +] +[ +.I file ... +] +.SH DESCRIPTION +.I Freq +reads the given files (default standard input) +and prints histograms of the character frequencies. +By default, +.I freq +counts each byte as a character; +under the +.B -r +option it instead counts +.SM UTF +sequences, that is, runes. +.PP +Each non-zero entry of the table is printed preceded by the byte value, +in decimal, octal, hex, and +Unicode +character (if printable). +If any options are given, the +.BR -d , +.BR -x , +.BR -o , +.B -c +flags specify a subset of value formats: decimal, hex, octal, and +character, respectively. +.SH SOURCE +.B \*9/src/cmd/freq.c +.SH SEE ALSO +.IR utf (7), +.IR wc (1) diff --git a/mkdir/mkdir.1 b/mkdir/mkdir.1 @@ -0,0 +1,43 @@ +.TH MKDIR 1 +.SH NAME +mkdir \- make a directory +.SH SYNOPSIS +.B mkdir +[ +.B -p +] [ +.B -m +. I mode +] +.I dirname ... +.SH DESCRIPTION +.I Mkdir +creates the specified directories. +It +requires write permission in the parent directory. +.PP +If the +.B -p +flag is given, +.I mkdir +creates any necessary parent directories +and does not complain if the target directory already exists. +.PP +The +.B -m +flag sets the permissions to be used when creating the directory. +The default is 0777. +.SH "SEE ALSO" +.IR rm (1) +.br +.IR cd +in +.IR rc (1) +.SH SOURCE +.B \*9/src/cmd/mkdir.c +.SH DIAGNOSTICS +.I Mkdir +returns null exit status if all directories were successfully made. +Otherwise it prints a diagnostic and returns +.B \&"error" +status. diff --git a/tac/Makefile b/tac/Makefile @@ -1,10 +0,0 @@ -# tac - reverse line order cat -# Depends on ../lib9 - -TARG = tac - -include ../std.mk - -pre-uninstall: - -post-install: diff --git a/tac/tac.1 b/tac/tac.1 @@ -1,28 +0,0 @@ -.TH TAC 1 -.SH NAME -tac \- reverse concatenate files -.SH SYNOPSIS -.B tac -[ -.I file ... -] -.SH DESCRIPTION -.I Tac -reads each -.I file -in sequence and writes it on the standard output in reverse line order. -.IP -.L -tac file -.LP -prints a file in reverse line order -.IP -.L -tac file1 file2 >file3 -.LP -Concatenate reversed file1 and file2 into file3 -.LP -.SH SEE ALSO -.IR cat (1) -.SH BUGS -Same as in cat diff --git a/tac/tac.c b/tac/tac.c @@ -1,60 +0,0 @@ -/* author: pancake<nopcode.org> */ -#include <u.h> -#include <libc.h> - -static vlong bsize = 0; -static char *buf; -#define LINES 4096 - -void -tac() -{ - int i, j; - char *ptr, **nls; - nls = malloc(LINES*sizeof(nls)); - for(i=1, ptr=buf; ptr;) { - assert(nls != NULL); - for(j=0; j<LINES && (ptr=strchr(ptr+1, '\n')); j++) - nls[i++] = ptr+1; - nls = realloc(nls, (i+LINES)*sizeof(nls)); - } - *nls = buf; - while(i--) - write(1, nls[i], nls[i+1]-nls[i]); - free(nls); -} - -void -load(int f) -{ - vlong nsize, size = seek(f, 0, 2); - if (size>0) { - nsize = bsize + size; - buf = realloc(buf, nsize); - seek(f, 0, 0); - read(f, buf+bsize, size); - bsize = nsize; - } else - while ((size = read(f, buf+bsize, LINES))>0) - bsize+=size; -} - -void -main(int argc, char *argv[]) -{ - int i, f; - buf = malloc(1); - assert(buf != NULL); - if (argc == 1) - load(0); - else for(i=1; i<argc; i++){ - f = open(argv[i], OREAD); - if(f >= 0){ - load(f); - close(f); - }else sysfatal("can't open %s: %r", argv[i]); - } - tac(); - free(buf); - exits(0); -} diff --git a/tail/Makefile b/tail/Makefile @@ -0,0 +1,11 @@ +# tail - tail unix port from plan9 +# +# Depends on ../lib9 + +TARG = tail + +include ../std.mk + +pre-uninstall: + +post-install: diff --git a/tail/tail.1 b/tail/tail.1 @@ -0,0 +1,87 @@ +.TH TAIL 1 +.SH NAME +tail \- deliver the last part of a file +.SH SYNOPSIS +.B tail +[ +.BR +- \fInumber\fP[ lbc ][ rf ] +] +[ +.I file +] +.PP +.B tail +[ +.B -fr +] +[ +.B -n +.I nlines +] +[ +.B -c +.I nbytes +] +[ +.I file +] +.SH DESCRIPTION +.I Tail +copies the named file to the standard output beginning +at a designated place. +If no file is named, the standard input is copied. +.PP +Copying begins at position +.BI + number +measured from the beginning, or +.BI - number +from the end of the input. +.I Number +is counted in lines, 1K blocks or bytes, +according to the appended flag +.LR l , +.LR b , +or +.LR c . +Default is +.B -10l +(ten ell). +.PP +The further flag +.L r +causes tail to print lines from the end of the file in reverse order; +.L f +(follow) causes +.IR tail , +after printing to the end, to keep watch and +print further data as it appears. +.PP +The second syntax is that promulgated by POSIX, where +the +.I numbers +rather than the options are signed. +.SH EXAMPLES +.TP +.B tail file +Print the last 10 lines of a file. +.TP +.B tail +0f file +Print a file, and continue to watch +data accumulate as it grows. +.TP +.B sed 10q file +Print the first 10 lines of a file. +.SH SOURCE +.B \*9/src/cmd/tail.c +.SH BUGS +Tails relative to the end of the file +are treasured up in a buffer, and thus +are limited in length. +.PP +According to custom, option +.BI + number +counts lines from 1, and counts +blocks and bytes from 0. +.PP +.I Tail +is ignorant of UTF. diff --git a/tail/tail.c b/tail/tail.c @@ -0,0 +1,363 @@ +#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 + */ + +vlong 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); +#define trunc tailtrunc +extern void trunc(Dir*, Dir**); +extern vlong tseek(vlong, 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? ~0ULL>>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; + vlong 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; + vlong 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); +} + +vlong +tseek(vlong 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((uchar)*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"); +}