Introduction
Introduction Statistics Contact Development Disclaimer Help
tail.c - 9base - revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log
Files
Refs
README
LICENSE
---
tail.c (5753B)
---
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <bio.h>
5
6 /*
7 * tail command, posix plus v10 option -r.
8 * the simple command tail -c, legal in v10, is illegal
9 */
10
11 vlong count;
12 int anycount;
13 int follow;
14 int file = 0;
15 char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc]…
16
17 Biobuf bout;
18 enum
19 {
20 BEG,
21 END
22 } origin = END;
23 enum
24 {
25 CHARS,
26 LINES
27 } units = LINES;
28 enum
29 {
30 FWD,
31 REV
32 } dir = FWD;
33
34 extern void copy(void);
35 extern void fatal(char*);
36 extern int getnumber(char*);
37 extern void keep(void);
38 extern void reverse(void);
39 extern void skip(void);
40 extern void suffix(char*);
41 extern long tread(char*, long);
42 #define trunc tailtrunc
43 extern void trunc(Dir*, Dir**);
44 extern vlong tseek(vlong, int);
45 extern void twrite(char*, long);
46 extern void usage(void);
47
48 #define JUMP(o,p) tseek(o,p), copy()
49
50 void
51 main(int argc, char **argv)
52 {
53 int seekable, c;
54
55 Binit(&bout, 1, OWRITE);
56 for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
57 if(getnumber(argv[1])) {
58 suffix(argv[1]);
59 continue;
60 } else
61 if(c == '-')
62 switch(argv[1][1]) {
63 case 'c':
64 units = CHARS;
65 case 'n':
66 if(getnumber(argv[1]+2))
67 continue;
68 else
69 if(argc > 2 && getnumber(argv[2])) {
70 argc--, argv++;
71 continue;
72 } else
73 usage();
74 case 'r':
75 dir = REV;
76 continue;
77 case 'f':
78 follow++;
79 continue;
80 case '-':
81 argc--, argv++;
82 }
83 break;
84 }
85 if(dir==REV && (units==CHARS || follow || origin==BEG))
86 fatal("incompatible options");
87 if(!anycount)
88 count = dir==REV? ~0ULL>>1: 10;
89 if(origin==BEG && units==LINES && count>0)
90 count--;
91 if(argc > 2)
92 usage();
93 if(argc > 1 && (file=open(argv[1],0)) < 0)
94 fatal(argv[1]);
95 seekable = seek(file,0L,0) == 0;
96
97 if(!seekable && origin==END)
98 keep();
99 else
100 if(!seekable && origin==BEG)
101 skip();
102 else
103 if(units==CHARS && origin==END)
104 JUMP(-count, 2);
105 else
106 if(units==CHARS && origin==BEG)
107 JUMP(count, 0);
108 else
109 if(units==LINES && origin==END)
110 reverse();
111 else
112 if(units==LINES && origin==BEG)
113 skip();
114 if(follow && seekable)
115 for(;;) {
116 static Dir *sb0, *sb1;
117 trunc(sb1, &sb0);
118 copy();
119 trunc(sb0, &sb1);
120 sleep(5000);
121 }
122 exits(0);
123 }
124
125 void
126 trunc(Dir *old, Dir **new)
127 {
128 Dir *d;
129 vlong olength;
130
131 d = dirfstat(file);
132 if(d == nil)
133 return;
134 olength = 0;
135 if(old)
136 olength = old->length;
137 if(d->length < olength)
138 d->length = tseek(0L, 0);
139 free(*new);
140 *new = d;
141 }
142
143 void
144 suffix(char *s)
145 {
146 while(*s && strchr("0123456789+-", *s))
147 s++;
148 switch(*s) {
149 case 'b':
150 if((count *= 1024) < 0)
151 fatal("too big");
152 case 'c':
153 units = CHARS;
154 case 'l':
155 s++;
156 }
157 switch(*s) {
158 case 'r':
159 dir = REV;
160 return;
161 case 'f':
162 follow++;
163 return;
164 case 0:
165 return;
166 }
167 usage();
168 }
169
170 /*
171 * read past head of the file to find tail
172 */
173 void
174 skip(void)
175 {
176 int i;
177 long n;
178 char buf[Bsize];
179 if(units == CHARS) {
180 for( ; count>0; count -=n) {
181 n = count<Bsize? count: Bsize;
182 if(!(n = tread(buf, n)))
183 return;
184 }
185 } else /*units == LINES*/ {
186 n = i = 0;
187 while(count > 0) {
188 if(!(n = tread(buf, Bsize)))
189 return;
190 for(i=0; i<n && count>0; i++)
191 if(buf[i]=='\n')
192 count--;
193 }
194 twrite(buf+i, n-i);
195 }
196 copy();
197 }
198
199 void
200 copy(void)
201 {
202 long n;
203 char buf[Bsize];
204 while((n=tread(buf, Bsize)) > 0) {
205 twrite(buf, n);
206 Bflush(&bout); /* for FWD on pipe; else harmless …
207 }
208 }
209
210 /*
211 * read whole file, keeping the tail
212 * complexity is length(file)*length(tail).
213 * could be linear.
214 */
215 void
216 keep(void)
217 {
218 int len = 0;
219 long bufsiz = 0;
220 char *buf = 0;
221 int j, k, n;
222
223 for(n=1; n;) {
224 if(len+Bsize > bufsiz) {
225 bufsiz += 2*Bsize;
226 if(!(buf = realloc(buf, bufsiz+1)))
227 fatal("out of space");
228 }
229 for(; n && len<bufsiz; len+=n)
230 n = tread(buf+len, bufsiz-len);
231 if(count >= len)
232 continue;
233 if(units == CHARS)
234 j = len - count;
235 else {
236 /* units == LINES */
237 j = buf[len-1]=='\n'? len-1: len;
238 for(k=0; j>0; j--)
239 if(buf[j-1] == '\n')
240 if(++k >= count)
241 break;
242 }
243 memmove(buf, buf+j, len-=j);
244 }
245 if(dir == REV) {
246 if(len>0 && buf[len-1]!='\n')
247 buf[len++] = '\n';
248 for(j=len-1 ; j>0; j--)
249 if(buf[j-1] == '\n') {
250 twrite(buf+j, len-j);
251 if(--count <= 0)
252 return;
253 len = j;
254 }
255 }
256 if(count > 0)
257 twrite(buf, len);
258 }
259
260 /*
261 * count backward and print tail of file
262 */
263 void
264 reverse(void)
265 {
266 int first;
267 long len = 0;
268 long n = 0;
269 long bufsiz = 0;
270 char *buf = 0;
271 vlong pos = tseek(0L, 2);
272
273 for(first=1; pos>0 && count>0; first=0) {
274 n = pos>Bsize? Bsize: (int)pos;
275 pos -= n;
276 if(len+n > bufsiz) {
277 bufsiz += 2*Bsize;
278 if(!(buf = realloc(buf, bufsiz+1)))
279 fatal("out of space");
280 }
281 memmove(buf+n, buf, len);
282 len += n;
283 tseek(pos, 0);
284 if(tread(buf, n) != n)
285 fatal("length error");
286 if(first && buf[len-1]!='\n')
287 buf[len++] = '\n';
288 for(n=len-1 ; n>0 && count>0; n--)
289 if(buf[n-1] == '\n') {
290 count--;
291 if(dir == REV)
292 twrite(buf+n, len-n);
293 len = n;
294 }
295 }
296 if(dir == FWD) {
297 tseek(n==0? 0 : pos+n+1, 0);
298 copy();
299 } else
300 if(count > 0)
301 twrite(buf, len);
302 }
303
304 vlong
305 tseek(vlong o, int p)
306 {
307 o = seek(file, o, p);
308 if(o == -1)
309 fatal("");
310 return o;
311 }
312
313 long
314 tread(char *buf, long n)
315 {
316 int r = read(file, buf, n);
317 if(r == -1)
318 fatal("");
319 return r;
320 }
321
322 void
323 twrite(char *s, long n)
324 {
325 if(Bwrite(&bout, s, n) != n)
326 fatal("");
327 }
328
329 int
330 getnumber(char *s)
331 {
332 if(*s=='-' || *s=='+')
333 s++;
334 if(!isdigit((uchar)*s))
335 return 0;
336 if(s[-1] == '+')
337 origin = BEG;
338 if(anycount++)
339 fatal("excess option");
340 count = atol(s);
341
342 /* check range of count */
343 if(count < 0 || (int)count != count)
344 fatal("too big");
345 return 1;
346 }
347
348 void
349 fatal(char *s)
350 {
351 char buf[ERRMAX];
352
353 errstr(buf, sizeof buf);
354 fprint(2, "tail: %s: %s\n", s, buf);
355 exits(s);
356 }
357
358 void
359 usage(void)
360 {
361 fprint(2, "%s\n", umsg);
362 exits("usage");
363 }
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.