xref: /original-bsd/usr.bin/tail/tail.c (revision 1cfaf997)
1 /*-
2  * Copyright (c) 1980 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1980 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)tail.c	5.4 (Berkeley) 04/18/91";
16 #endif /* not lint */
17 
18 /* tail command
19  *
20  *	tail where [file]
21  *	where is +/-n[type]
22  *	- means n lines before end
23  *	+ means nth line from beginning
24  *	type 'b' means tail n blocks, not lines
25  *	type 'c' means tail n characters
26  *	Type 'r' means in lines in reverse order from end
27  *	 (for -r, default is entire buffer )
28  *	option 'f' means loop endlessly trying to read more
29  *		characters after the end of file, on the  assumption
30  *		that the file is growing
31 */
32 
33 #include	<stdio.h>
34 #include	<ctype.h>
35 #include	<sys/types.h>
36 #include	<sys/stat.h>
37 #include	<sys/file.h>
38 #include	<errno.h>
39 
40 #define LBIN 32769
41 #undef	BUFSIZ
42 #define	BUFSIZ	8192
43 struct	stat	statb;
44 int	follow;
45 int	piped;
46 char bin[LBIN];
47 int errno;
48 
49 main(argc,argv)
50 char **argv;
51 {
52 	long n,di;
53 	register i,j,k;
54 	char	*arg;
55 	int partial,bylines,bkwds,fromend,lastnl;
56 	char *p;
57 
58 	arg = argv[1];
59 	if(argc<=1 || *arg!='-'&&*arg!='+') {
60 		arg = "-10l";
61 		argc++;
62 		argv--;
63 	}
64 	fromend = *arg=='-';
65 	arg++;
66 	if (isdigit(*arg)) {
67 		n = 0;
68 		while(isdigit(*arg))
69 			n = n*10 + *arg++ - '0';
70 	} else
71 		n = -1;
72 	if(!fromend&&n>0)
73 		n--;
74 	if(argc>2) {
75 		(void)close(0);
76 		if(open(argv[2],0)!=0) {
77 			perror(argv[2]);
78 			exit(1);
79 		}
80 	}
81 	(void)lseek(0,(off_t)0,L_INCR);
82 	piped = errno==ESPIPE;
83 	bylines = -1; bkwds = 0;
84 	while(*arg)
85 	switch(*arg++) {
86 
87 	case 'b':
88 		if (n == -1) n = 1;
89 		n <<= 9;
90 		if(bylines!=-1) goto errcom;
91 		bylines=0;
92 		break;
93 	case 'c':
94 		if(bylines!=-1) goto errcom;
95 		bylines=0;
96 		break;
97 	case 'f':
98 		follow = 1;
99 		break;
100 	case 'r':
101 		if(n==-1) n = LBIN;
102 		bkwds = 1; fromend = 1; bylines = 1;
103 		break;
104 	case 'l':
105 		if(bylines!=-1) goto errcom;
106 		bylines = 1;
107 		break;
108 	default:
109 		goto errcom;
110 	}
111 	if (n==-1) n = 10;
112 	if(bylines==-1) bylines = 1;
113 	if(bkwds) follow=0;
114 	if(fromend)
115 		goto keep;
116 
117 			/*seek from beginning */
118 
119 	if(bylines) {
120 		j = 0;
121 		while(n-->0) {
122 			do {
123 				if(j--<=0) {
124 					p = bin;
125 					j = read(0,p,BUFSIZ);
126 					if(j--<=0)
127 						fexit();
128 				}
129 			} while(*p++ != '\n');
130 		}
131 		(void)write(1,p,j);
132 	} else  if(n>0) {
133 		if(!piped)
134 			(void)fstat(0,&statb);
135 		if(piped||(statb.st_mode&S_IFMT)==S_IFCHR)
136 			while(n>0) {
137 				i = n>BUFSIZ?BUFSIZ:n;
138 				i = read(0,bin,i);
139 				if(i<=0)
140 					fexit();
141 				n -= i;
142 			}
143 		else
144 			(void)lseek(0,(off_t)n,L_SET);
145 	}
146 copy:
147 	while((i=read(0,bin,BUFSIZ))>0)
148 		(void)write(1,bin,i);
149 	fexit();
150 
151 			/*seek from end*/
152 
153 keep:
154 	if(n <= 0)
155 		fexit();
156 	if(!piped) {
157 		(void)fstat(0,&statb);
158 		/* If by lines, back up 1 buffer: else back up as needed */
159 		di = bylines?LBIN-1:n;
160 		if(statb.st_size > di)
161 			(void)lseek(0,(off_t)-di,L_XTND);
162 		if(!bylines)
163 			goto copy;
164 	}
165 	partial = 1;
166 	for(;;) {
167 		i = 0;
168 		do {
169 			j = read(0,&bin[i],LBIN-i);
170 			if(j<=0)
171 				goto brka;
172 			i += j;
173 		} while(i<LBIN);
174 		partial = 0;
175 	}
176 brka:
177 	if(!bylines) {
178 		k =
179 		    n<=i ? i-n:
180 		    partial ? 0:
181 		    n>=LBIN ? i+1:
182 		    i-n+LBIN;
183 		k--;
184 	} else {
185 		if(bkwds && bin[i==0?LBIN-1:i-1]!='\n'){	/* force trailing newline */
186 			bin[i]='\n';
187 			if(++i>=LBIN) {i = 0; partial = 0;}
188 		}
189 		k = i;
190 		j = 0;
191 		do {
192 			lastnl = k;
193 			do {
194 				if(--k<0) {
195 					if(partial) {
196 						if(bkwds)
197 						    (void)write(1,bin,lastnl+1);
198 						goto brkb;
199 					}
200 					k = LBIN -1;
201 				}
202 			} while(bin[k]!='\n'&&k!=i);
203 			if(bkwds && j>0){
204 				if(k<lastnl) (void)write(1,&bin[k+1],lastnl-k);
205 				else {
206 					(void)write(1,&bin[k+1],LBIN-k-1);
207 					(void)write(1,bin,lastnl+1);
208 				}
209 			}
210 		} while(j++<n&&k!=i);
211 brkb:
212 		if(bkwds) exit(0);
213 		if(k==i) do {
214 			if(++k>=LBIN)
215 				k = 0;
216 		} while(bin[k]!='\n'&&k!=i);
217 	}
218 	if(k<i)
219 		(void)write(1,&bin[k+1],i-k-1);
220 	else {
221 		(void)write(1,&bin[k+1],LBIN-k-1);
222 		(void)write(1,bin,i);
223 	}
224 	fexit();
225 errcom:
226 	fprintf(stderr, "usage: tail [+_[n][lbc][rf]] [file]\n");
227 	exit(2);
228 }
229 
230 fexit()
231 {	register int n;
232 	if (!follow || piped) exit(0);
233 	for (;;)
234 	{	sleep(1);
235 		while ((n = read (0, bin, BUFSIZ)) > 0)
236 			if (write (1, bin, n) < 0)
237 				exit(1);
238 	}
239 }
240