merge - 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 <[email protected]> | |
Date: Tue, 27 Apr 2010 14:48:07 +0000 | |
merge | |
Diffstat: | |
M .hgtags | 1 + | |
M LICENSE | 2 +- | |
M Makefile | 2 +- | |
M README | 2 +- | |
M config.mk | 4 ++-- | |
M fortune/fortune.1 | 23 +++++++++++++++++++++++ | |
M freq/freq.1 | 40 +++++++++++++++++++++++++++++… | |
M mkdir/mkdir.1 | 43 ++++++++++++++++++++++++++++++ | |
D tac/Makefile | 10 ---------- | |
D tac/tac.1 | 28 ---------------------------- | |
D tac/tac.c | 60 -----------------------------… | |
A tail/Makefile | 11 +++++++++++ | |
A tail/tail.1 | 87 +++++++++++++++++++++++++++++… | |
A 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 LI… | |
MIT/X Consortium License | |
-(C)opyright 2005-2009 Anselm R. Garbe <garbeam at gmail dot com> | |
+© 2005-2010 Anselm R Garbe <[email protected]> | |
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. … | |
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]] … | |
+ | |
+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"); | |
+} |