|
|
1.1 root 1: #include <ctype.h>
2: #include <errno.h>
3: #include <stdio.h>
4: #include <libc/libc.h>
5:
6: /* tail command, posix plus v10 option -r.
7: the simple command tail -c, legal in v10, is illegal */
8:
9: long count;
10: int anycount;
11: int follow;
12: int file = 0;
13: enum { BEG, END } origin = END;
14: enum { CHARS, LINES } units = LINES;
15: enum { FWD, REV } dir = FWD;
16:
17: extern void copy(void), keep(void), skip(void);
18: extern void usage(void), reverse(void);
19: extern void trunc(struct stat*, struct stat*);
20: extern void suffix(char*), fatal(char*);
21: extern long tseek(long, int);
22: extern long tread(char*, long);
23: extern void twrite(char*, long);
24: extern int getnumber(char*);
25: #define jump(o,p) tseek(o,p), copy()
26:
27: main(int argc, char **argv)
28: {
29: int seekable, c;
30: for(; argc>1&&((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
31: if(getnumber(argv[1])) {
32: suffix(argv[1]);
33: continue;
34: } else if(c == '-')
35: switch(argv[1][1]) {
36: case 'c':
37: units = CHARS;
38: case 'n':
39: if(getnumber(argv[1]+2))
40: continue;
41: else if(argc>2&&getnumber(argv[2])) {
42: argc--, argv++;
43: continue;
44: } else
45: usage();
46: case 'r':
47: dir = REV;
48: continue;
49: case 'f':
50: follow++;
51: continue;
52: case '-':
53: argc--, argv++;
54: }
55: break;
56: }
57: if(dir==REV && (units==CHARS || follow || origin==BEG))
58: fatal("incompatible options");
59: if(!anycount)
60: count = dir==REV? ~0UL>>1: 10;
61: if(origin==BEG && units==LINES && count>0)
62: count--;
63: if(argc > 2)
64: usage();
65: if(argc > 1 && (file=open(argv[1],0)) < 0)
66: fatal(argv[1]);
67: seekable = lseek(file,0L,0) == 0;
68: errno = 0;
69:
70: if(!seekable && origin==END)
71: keep();
72: else if(!seekable && origin==BEG)
73: skip();
74: else if(units==CHARS && origin==END)
75: jump(-count, 2);
76: else if(units==CHARS && origin==BEG)
77: jump(count, 0);
78: else if(units==LINES && origin==END)
79: reverse();
80: else if(units==LINES && origin==BEG)
81: skip();
82: if(follow && seekable)
83: for(;;) {
84: static struct stat sb0, sb1;
85: trunc(&sb1, &sb0);
86: copy();
87: trunc(&sb0, &sb1);
88: sleep(5);
89: }
90: return 0;
91: }
92:
93: void
94: trunc(struct stat *old, struct stat *new)
95: {
96: fstat(file, new);
97: if(new->st_size < old->st_size)
98: new->st_size = tseek(0L, 0);
99: }
100:
101: void
102: suffix(char *s)
103: {
104: while(*s && strchr("0123456789+-", *s))
105: s++;
106: switch(*s) {
107: case 'b':
108: if((count*=1024) < 0)
109: fatal("too big");
110: case 'c':
111: units = CHARS;
112: case 'l':
113: s++;
114: }
115: switch(*s) {
116: case 'r':
117: dir = REV;
118: return;
119: case 'f':
120: follow++;
121: return;
122: case 0:
123: return;
124: }
125: usage();
126: }
127:
128: void
129: skip(void) /* read past head of the file to find tail */
130: {
131: int i;
132: long n;
133: char buf[BUFSIZ];
134: if(units == CHARS) {
135: for( ; count>0; count -=n) {
136: n = count<BUFSIZ? count: BUFSIZ;
137: if(!(n = tread(buf, n)))
138: return;
139: }
140: } else /*units == LINES*/ {
141: n = i = 0;
142: while(count > 0) {
143: if(!(n = tread(buf, BUFSIZ)))
144: return;
145: for(i=0; i<n && count>0; i++)
146: if(buf[i]=='\n')
147: count--;
148: }
149: twrite(buf+i, n-i);
150: }
151: copy();
152: }
153:
154: void
155: copy(void)
156: {
157: long n;
158: char buf[BUFSIZ];
159: while((n=tread(buf, BUFSIZ)) > 0) {
160: twrite(buf, n);
161: fflush(stdout); /* for FWD on pipe; else harmless */
162: }
163: }
164:
165: void
166: keep(void) /* read whole file, keeping the tail */
167: { /* complexity=length(file)*length(tail). could be linear */
168: int len = 0;
169: long bufsiz = 0;
170: char *buf = 0;
171: int j, k, n;
172: for(n=1; n;) {
173: if(len+BUFSIZ > bufsiz) {
174: bufsiz += 2*BUFSIZ;
175: if(!(buf = realloc(buf, bufsiz+1)))
176: fatal("out of space");
177: }
178: for( ; n && len<bufsiz; len+=n)
179: n = tread(buf+len, bufsiz-len);
180: if(count >= len)
181: continue;
182: if(units == CHARS)
183: j = len - count;
184: else /*units == LINES*/ {
185: j = buf[len-1]=='\n'? len-1: len;
186: for(k=0; j>0; j--)
187: if(buf[j-1] == '\n')
188: if(++k >= count)
189: break;
190: }
191: memmove(buf, buf+j, len-=j);
192: }
193: if(dir == REV) {
194: if(len>0 && buf[len-1]!='\n')
195: buf[len++] = '\n';
196: for(j=len-1 ; j>0; j--)
197: if(buf[j-1] == '\n') {
198: twrite(buf+j, len-j);
199: if(--count <= 0)
200: return;
201: len = j;
202: }
203: }
204: if(count > 0)
205: twrite(buf, len);
206: }
207:
208: void
209: reverse(void) /* count backward and print tail of file */
210: {
211: int first;
212: long len = 0;
213: long n = 0;
214: long bufsiz = 0;
215: char *buf = 0;
216: long pos = tseek(0L, 2);
217: for(first=1; pos>0 && count>0; first=0) {
218: n = pos>BUFSIZ? BUFSIZ: (int)pos;
219: pos -= n;
220: if(len+n > bufsiz) {
221: bufsiz += 2*BUFSIZ;
222: if(!(buf = realloc(buf, bufsiz+1)))
223: fatal("out of space");
224: }
225: memmove(buf+n, buf, len);
226: len += n;
227: tseek(pos, 0);
228: if(tread(buf, n) != n)
229: fatal("length error");
230: if(first && buf[len-1]!='\n')
231: buf[len++] = '\n';
232: for(n=len-1 ; n>0 && count>0; n--)
233: if(buf[n-1] == '\n') {
234: count--;
235: if(dir == REV)
236: twrite(buf+n, len-n);
237: len = n;
238: }
239: }
240: if(dir == FWD) {
241: tseek(n==0? 0: pos+n+1, 0);
242: copy();
243: } else if(count > 0)
244: twrite(buf, len);
245: }
246:
247: long
248: tseek(long o, int p)
249: {
250: o = lseek(file, o, p);
251: if(o == -1)
252: fatal("");
253: return o;
254: }
255:
256: long
257: tread(char *buf, long n)
258: {
259: int r = read(file, buf, n);
260: if(r == -1)
261: fatal("");
262: return r;
263: }
264:
265: void
266: twrite(char *s, long n)
267: {
268: if(fwrite(s, 1 , n, stdout) != n)
269: fatal("");
270: }
271:
272: int
273: getnumber(char *s)
274: {
275: if(*s=='-' || *s=='+')
276: s++;
277: if(!isdigit(*s))
278: return 0;
279: if(s[-1] == '+')
280: origin = BEG;
281: if(anycount++)
282: fatal("excess option");
283: count = atol(s);
284: if(count < 0 || /* overflow */
285: (int)count != count) /* protect int args (read, fwrite) */
286: fatal("too big");
287: return 1;
288: }
289:
290: void
291: fatal(char *s)
292: {
293: write(2, "tail: ", 6);
294: if(errno)
295: perror(s);
296: else {
297: write(2, s, strlen(s));
298: write(2, "\n", 1);
299: }
300: exit(1);
301: }
302:
303: char *u = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]\n";
304: void
305: usage(void)
306: {
307: write(2, u, strlen(u));
308: exit(1);
309: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.