tail.c - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tail.c (4355B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include <sys/stat.h> | |
3 | |
4 #include <fcntl.h> | |
5 #include <unistd.h> | |
6 #include <stdint.h> | |
7 #include <stdio.h> | |
8 #include <stdlib.h> | |
9 #include <string.h> | |
10 #include <unistd.h> | |
11 | |
12 #include "utf.h" | |
13 #include "util.h" | |
14 | |
15 static char mode = 'n'; | |
16 | |
17 static int | |
18 dropinit(int fd, const char *fname, size_t count) | |
19 { | |
20 Rune r; | |
21 char buf[BUFSIZ], *p; | |
22 ssize_t n; | |
23 int nr; | |
24 | |
25 if (count < 2) | |
26 goto copy; | |
27 count--; /* numbering starts at 1 */ | |
28 while (count && (n = read(fd, buf, sizeof(buf))) > 0) { | |
29 switch (mode) { | |
30 case 'n': /* lines */ | |
31 for (p = buf; count && n > 0; p++, n--) { | |
32 if (*p == '\n') | |
33 count--; | |
34 } | |
35 break; | |
36 case 'c': /* bytes */ | |
37 if (count > n) { | |
38 count -= n; | |
39 } else { | |
40 p = buf + count; | |
41 n -= count; | |
42 count = 0; | |
43 } | |
44 break; | |
45 case 'm': /* runes */ | |
46 for (p = buf; count && n > 0; p += nr, n -= nr, … | |
47 nr = charntorune(&r, p, n); | |
48 if (!nr) { | |
49 /* we don't have a full rune, mo… | |
50 * remaining data to beginning a… | |
51 * again */ | |
52 memmove(buf, p, n); | |
53 break; | |
54 } | |
55 } | |
56 break; | |
57 } | |
58 } | |
59 if (count) { | |
60 if (n < 0) | |
61 weprintf("read %s:", fname); | |
62 if (n <= 0) | |
63 return n; | |
64 } | |
65 | |
66 /* write the rest of the buffer */ | |
67 if (writeall(1, p, n) < 0) | |
68 eprintf("write:"); | |
69 copy: | |
70 switch (concat(fd, fname, 1, "<stdout>")) { | |
71 case -1: /* read error */ | |
72 return -1; | |
73 case -2: /* write error */ | |
74 exit(1); | |
75 default: | |
76 return 0; | |
77 } | |
78 } | |
79 | |
80 static int | |
81 taketail(int fd, const char *fname, size_t count) | |
82 { | |
83 static char *buf = NULL; | |
84 static size_t size = 0; | |
85 char *p; | |
86 size_t len = 0, left; | |
87 ssize_t n; | |
88 | |
89 if (!count) | |
90 return 0; | |
91 for (;;) { | |
92 if (len + BUFSIZ > size) { | |
93 /* make sure we have at least BUFSIZ to read */ | |
94 size += 2 * BUFSIZ; | |
95 buf = erealloc(buf, size); | |
96 } | |
97 n = read(fd, buf + len, size - len); | |
98 if (n < 0) { | |
99 weprintf("read %s:", fname); | |
100 return -1; | |
101 } | |
102 if (n == 0) | |
103 break; | |
104 len += n; | |
105 switch (mode) { | |
106 case 'n': /* lines */ | |
107 /* ignore the last character; if it is a newline… | |
108 * ends the last line */ | |
109 for (p = buf + len - 2, left = count; p >= buf; … | |
110 if (*p != '\n') | |
111 continue; | |
112 left--; | |
113 if (!left) { | |
114 p++; | |
115 break; | |
116 } | |
117 } | |
118 break; | |
119 case 'c': /* bytes */ | |
120 p = count < len ? buf + len - count : buf; | |
121 break; | |
122 case 'm': /* runes */ | |
123 for (p = buf + len - 1, left = count; p >= buf; … | |
124 /* skip utf-8 continuation bytes */ | |
125 if (UTF8_POINT(*p)) | |
126 continue; | |
127 left--; | |
128 if (!left) | |
129 break; | |
130 } | |
131 break; | |
132 } | |
133 if (p > buf) { | |
134 len -= p - buf; | |
135 memmove(buf, p, len); | |
136 } | |
137 } | |
138 if (writeall(1, buf, len) < 0) | |
139 eprintf("write:"); | |
140 return 0; | |
141 } | |
142 | |
143 static void | |
144 usage(void) | |
145 { | |
146 eprintf("usage: %s [-f] [-c num | -m num | -n num | -num] [file … | |
147 } | |
148 | |
149 int | |
150 main(int argc, char *argv[]) | |
151 { | |
152 struct stat st1, st2; | |
153 int fd; | |
154 size_t n = 10; | |
155 int fflag = 0, ret = 0, newline = 0, many = 0; | |
156 char *numstr; | |
157 int (*tail)(int, const char *, size_t) = taketail; | |
158 | |
159 ARGBEGIN { | |
160 case 'f': | |
161 fflag = 1; | |
162 break; | |
163 case 'c': | |
164 case 'm': | |
165 case 'n': | |
166 mode = ARGC(); | |
167 numstr = EARGF(usage()); | |
168 n = MIN(llabs(estrtonum(numstr, LLONG_MIN + 1, | |
169 MIN(LLONG_MAX, SIZE_MAX))), SIZE… | |
170 if (strchr(numstr, '+')) | |
171 tail = dropinit; | |
172 break; | |
173 ARGNUM: | |
174 n = ARGNUMF(); | |
175 break; | |
176 default: | |
177 usage(); | |
178 } ARGEND | |
179 | |
180 if (!argc) { | |
181 if (tail(0, "<stdin>", n) < 0) | |
182 ret = 1; | |
183 } else { | |
184 if ((many = argc > 1) && fflag) | |
185 usage(); | |
186 for (newline = 0; *argv; argc--, argv++) { | |
187 if (!strcmp(*argv, "-")) { | |
188 *argv = "<stdin>"; | |
189 fd = 0; | |
190 } else if ((fd = open(*argv, O_RDONLY)) < 0) { | |
191 weprintf("open %s:", *argv); | |
192 ret = 1; | |
193 continue; | |
194 } | |
195 if (many) | |
196 printf("%s==> %s <==\n", newline ? "\n" … | |
197 if (fstat(fd, &st1) < 0) | |
198 eprintf("fstat %s:", *argv); | |
199 if (!(S_ISFIFO(st1.st_mode) || S_ISREG(st1.st_mo… | |
200 fflag = 0; | |
201 newline = 1; | |
202 if (tail(fd, *argv, n) < 0) { | |
203 ret = 1; | |
204 fflag = 0; | |
205 } | |
206 | |
207 if (!fflag) { | |
208 if (fd != 0) | |
209 close(fd); | |
210 continue; | |
211 } | |
212 for (;;) { | |
213 if (concat(fd, *argv, 1, "<stdout>") < 0) | |
214 exit(1); | |
215 if (fstat(fd, &st2) < 0) | |
216 eprintf("fstat %s:", *argv); | |
217 if (st2.st_size < st1.st_size) { | |
218 fprintf(stderr, "%s: file trunca… | |
219 if (lseek(fd, SEEK_SET, 0) < 0) | |
220 eprintf("lseek:"); | |
221 } | |
222 st1 = st2; | |
223 sleep(1); | |
224 } | |
225 } | |
226 } | |
227 | |
228 return ret; | |
229 } |