Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
long	count;
12
int	anycount;
13
int	follow;
14
int	file	= 0;
15
char*	umsg	= "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
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
extern	void	trunc(Dir*, Dir**);
43
extern	vlong	tseek(vlong, int);
44
extern	void	twrite(char*, long);
45
extern	void	usage(void);
46
static	int	isseekable(int fd);
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? ~0UL>>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 = isseekable(file);
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(0LL, 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(0LL, 2);
272
 
273
	for(first=1; pos>0 && count>0; first=0) {
274
		n = pos>Bsize? Bsize: (long)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
		if(n)
298
			tseek(pos+n+1, 0);
299
		else
300
			tseek(0, 0);
301
		copy();
302
	} else
303
	if(count > 0)
304
		twrite(buf, len);
305
}
306
 
307
vlong
308
tseek(vlong o, int p)
309
{
310
	o = seek(file, o, p);
311
	if(o == -1)
312
		fatal("");
313
	return o;
314
}
315
 
316
long
317
tread(char *buf, long n)
318
{
319
	int r = read(file, buf, n);
320
	if(r == -1)
321
		fatal("");
322
	return r;
323
}
324
 
325
void
326
twrite(char *s, long n)
327
{
328
	if(Bwrite(&bout, s, n) != n)
329
		fatal("");
330
}
331
 
332
int
333
getnumber(char *s)
334
{
335
	if(*s=='-' || *s=='+')
336
		s++;
337
	if(!isdigit(*s))
338
		return 0;
339
	if(s[-1] == '+')
340
		origin = BEG;
341
	if(anycount++)
342
		fatal("excess option");
343
	count = atol(s);
344
 
345
	/* check range of count */
346
	if(count < 0 ||	(int)count != count)
347
		fatal("too big");
348
	return 1;
349
}	
350
 
351
void		
352
fatal(char *s)
353
{
354
	char buf[ERRMAX];
355
 
356
	errstr(buf, sizeof buf);
357
	fprint(2, "tail: %s: %s\n", s, buf);
358
	exits(s);
359
}
360
 
361
void
362
usage(void)
363
{
364
	fprint(2, "%s\n", umsg);
365
	exits("usage");
366
}
367
 
368
/* return true if seeks work and if the file is > 0 length.
369
 * this will eventually bite me in the ass if seeking a file
370
 * is not conservative. - presotto
371
 */
372
static int
373
isseekable(int fd)
374
{	
375
	vlong m;
376
 
377
	m = seek(fd, 0, 1);
378
	if(m < 0)
379
		return 0;
380
	return 1;
381
}