Introduction
Introduction Statistics Contact Development Disclaimer Help
removed tac, added tail from p9 instead (tac == tail -r) - 9base - revived mini…
git clone git://git.suckless.org/9base
Log
Files
Refs
README
LICENSE
---
commit 45ac8c8bd851ba5081939a63dd8a57421abec09a
parent 942791ab23de64d2580e3143ba3866ad85fa8ab3
Author: Anselm R Garbe <[email protected]>
Date: Sun, 11 Apr 2010 19:13:01 +0100
removed tac, added tail from p9 instead (tac == tail -r)
Diffstat:
M Makefile | 2 +-
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 +++++++++++++++++++++++++++++…
7 files changed, 462 insertions(+), 99 deletions(-)
---
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/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");
+}
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.