aboutsummaryrefslogtreecommitdiff
path: root/coreutils/tail.c
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2003-03-19 09:13:01 +0000
committerManuel Novoa III <mjn3@codepoet.org>2003-03-19 09:13:01 +0000
commitcad5364599eb5062d59e0c397ed638ddd61a8d5d (patch)
treea318d0f03aa076c74b576ea45dc543a5669e8e91 /coreutils/tail.c
parente01f9662a5bd5d91be4f6b3941b57fff73cd5af1 (diff)
downloadbusybox-w32-cad5364599eb5062d59e0c397ed638ddd61a8d5d.tar.gz
busybox-w32-cad5364599eb5062d59e0c397ed638ddd61a8d5d.tar.bz2
busybox-w32-cad5364599eb5062d59e0c397ed638ddd61a8d5d.zip
Major coreutils update.
Diffstat (limited to 'coreutils/tail.c')
-rw-r--r--coreutils/tail.c396
1 files changed, 232 insertions, 164 deletions
diff --git a/coreutils/tail.c b/coreutils/tail.c
index 1703eefc6..8e0adf5b5 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -2,7 +2,6 @@
2/* 2/*
3 * Mini tail implementation for busybox 3 * Mini tail implementation for busybox
4 * 4 *
5 *
6 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu> 5 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
7 * 6 *
8 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
@@ -21,14 +20,30 @@
21 * 20 *
22 */ 21 */
23 22
23/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
24/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
24 26
25#include <fcntl.h> 27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
26#include <getopt.h> 28 *
29 * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
30 * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
31 * 1) mixing printf/write without fflush()ing stdout
32 * 2) no check that any open files are present
33 * 3) optstring had -q taking an arg
34 * 4) no error checking on write in some cases, and a warning even then
35 * 5) q and s interaction bug
36 * 6) no check for lseek error
37 * 7) lseek attempted when count==0 even if arg was +0 (from top)
38 */
39
40#include <stdio.h>
41#include <stdlib.h>
27#include <string.h> 42#include <string.h>
28#include <ctype.h> 43#include <ctype.h>
29#include <stdlib.h>
30#include <unistd.h> 44#include <unistd.h>
31#include <sys/types.h> 45#include <fcntl.h>
46#include <sys/stat.h>
32#include "busybox.h" 47#include "busybox.h"
33 48
34static const struct suffix_mult tail_suffixes[] = { 49static const struct suffix_mult tail_suffixes[] = {
@@ -38,234 +53,287 @@ static const struct suffix_mult tail_suffixes[] = {
38 { NULL, 0 } 53 { NULL, 0 }
39}; 54};
40 55
41static const int BYTES = 0; 56static int status
42static const int LINES = 1; 57#if EXIT_SUCCESS != 0
43 58 = EXIT_SUCCESS /* If it is 0 (paranoid check), let bss initialize it. */
44static char *tailbuf; 59#endif
45static int taillen; 60 ;
46static int newline;
47 61
48static void tailbuf_append(char *buf, int len) 62static void tail_xprint_header(const char *fmt, const char *filename)
49{ 63{
50 tailbuf = xrealloc(tailbuf, taillen + len); 64 /* If we get an output error, there is really no sense in continuing. */
51 memcpy(tailbuf + taillen, buf, len); 65 if (dprintf(STDOUT_FILENO, fmt, filename) < 0) {
52 taillen += len; 66 bb_perror_nomsg_and_die();
67 }
53} 68}
54 69
55static void tailbuf_trunc(void) 70/* len should probably be size_t */
71static void tail_xbb_full_write(const char *buf, size_t len)
56{ 72{
57 char *s; 73 /* If we get a write error, there is really no sense in continuing. */
58 s = memchr(tailbuf, '\n', taillen); 74 if (bb_full_write(STDOUT_FILENO, buf, len) < 0) {
59 memmove(tailbuf, s + 1, taillen - ((s + 1) - tailbuf)); 75 bb_perror_nomsg_and_die();
60 taillen -= (s + 1) - tailbuf; 76 }
61 newline = 0;
62} 77}
63 78
64int tail_main(int argc, char **argv) 79static ssize_t tail_read(int fd, char *buf, size_t count)
65{ 80{
66 int from_top = 0, units = LINES, count = 10, sleep_period = 1; 81 ssize_t r;
67 int show_headers = 0, hide_headers = 0, follow = 0; 82
68 int *fds, nfiles = 0, status = EXIT_SUCCESS, nread, nwrite, seen = 0; 83 if ((r = safe_read(fd, buf, count)) < 0) {
69 char *s, *start, *end, buf[BUFSIZ]; 84 bb_perror_msg("read");
70 int i, opt; 85 status = EXIT_FAILURE;
71
72 if (argc >= 2) {
73 int line_num;
74 switch (argv[1][0]) {
75 case '+':
76 from_top = 1;
77 /* FALLS THROUGH */
78 case '-':
79 line_num = atoi(&argv[1][1]);
80 if (line_num != 0) {
81 optind = 2;
82 count = line_num;
83 }
84 break;
85 }
86 } 86 }
87 87
88 return r;
89}
90
91static const char tail_opts[] =
92 "fn:"
93#ifdef CONFIG_FEATURE_FANCY_TAIL
94 "c:qs:v"
95#endif
96 ;
97
98static const char header_fmt[] = "\n==> %s <==\n";
99
100int tail_main(int argc, char **argv)
101{
102 long count = 10;
103 unsigned int sleep_period = 1;
104 int from_top = 0;
105 int follow = 0;
106 int header_threshhold = 1;
88#ifdef CONFIG_FEATURE_FANCY_TAIL 107#ifdef CONFIG_FEATURE_FANCY_TAIL
89 while ((opt = getopt(argc, argv, "c:fn:q:s:v")) > 0) { 108 int count_bytes = 0;
90#else
91 while ((opt = getopt(argc, argv, "fn:")) > 0) {
92#endif 109#endif
110
111 char *tailbuf;
112 size_t tailbufsize;
113 int taillen = 0;
114 int newline = 0;
115
116 int *fds, nfiles, nread, nwrite, seen, i, opt;
117 char *s, *buf;
118 const char *fmt;
119
120 /* Allow legacy syntax of an initial numeric option without -n. */
121 if ((argv[1][0] == '+')
122 || ((argv[1][0] == '-')
123 /* && (isdigit)(argv[1][1]) */
124 && (((unsigned int)(argv[1][1] - '0')) <= 9))
125 ) {
126 optind = 2;
127 optarg = argv[1];
128 goto GET_COUNT;
129 }
130
131 while ((opt = getopt(argc, argv, tail_opts)) > 0) {
93 switch (opt) { 132 switch (opt) {
94 case 'f': 133 case 'f':
95 follow = 1; 134 follow = 1;
96 break; 135 break;
97#ifdef CONFIG_FEATURE_FANCY_TAIL 136#ifdef CONFIG_FEATURE_FANCY_TAIL
98 case 'c': 137 case 'c':
99 units = BYTES; 138 count_bytes = 1;
100 /* FALLS THROUGH */ 139 /* FALLS THROUGH */
101#endif 140#endif
102 case 'n': 141 case 'n':
103 count = parse_number(optarg, tail_suffixes); 142 GET_COUNT:
104 if (count < 0) 143 count = bb_xgetlarg10_sfx(optarg, tail_suffixes);
105 count = -count; 144 /* Note: Leading whitespace is an error trapped above. */
106 if (optarg[0] == '+') 145 if (*optarg == '+') {
107 from_top = 1; 146 from_top = 1;
147 } else {
148 from_top = 0;
149 }
150 if (count < 0) {
151 count = -count;
152 }
108 break; 153 break;
109#ifdef CONFIG_FEATURE_FANCY_TAIL 154#ifdef CONFIG_FEATURE_FANCY_TAIL
110 case 'q': 155 case 'q':
111 hide_headers = 1; 156 header_threshhold = INT_MAX;
112 break; 157 break;
113 case 's': 158 case 's':
114 sleep_period = parse_number(optarg, 0); 159 sleep_period =bb_xgetularg10_bnd(optarg, 0, UINT_MAX);
115 break; 160 break;
116 case 'v': 161 case 'v':
117 show_headers = 1; 162 header_threshhold = 0;
118 break; 163 break;
119#endif 164#endif
120 default: 165 default:
121 show_usage(); 166 bb_show_usage();
122 } 167 }
123 } 168 }
124 169
125 /* open all the files */ 170 /* open all the files */
126 fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1)); 171 fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1));
127 if (argc == optind) { 172
128 fds[nfiles++] = STDIN_FILENO; 173 argv += optind;
129 argv[optind] = "standard input"; 174 nfiles = i = 0;
130 } else { 175
131 for (i = optind; i < argc; i++) { 176 if ((argc -= optind) == 0) {
132 if (strcmp(argv[i], "-") == 0) { 177 struct stat statbuf;
133 fds[nfiles++] = STDIN_FILENO; 178
134 argv[i] = "standard input"; 179 if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) {
135 } else if ((fds[nfiles++] = open(argv[i], O_RDONLY)) < 0) { 180 follow = 0;
136 perror_msg("%s", argv[i]);
137 status = EXIT_FAILURE;
138 }
139 } 181 }
182 /* --argv; */
183 *argv = (char *) bb_msg_standard_input;
184 goto DO_STDIN;
185 }
186
187 do {
188 if ((argv[i][0] == '-') && !argv[i][1]) {
189 DO_STDIN:
190 fds[nfiles] = STDIN_FILENO;
191 } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) {
192 bb_perror_msg("%s", argv[i]);
193 status = EXIT_FAILURE;
194 continue;
195 }
196 argv[nfiles] = argv[i];
197 ++nfiles;
198 } while (++i < argc);
199
200 if (!nfiles) {
201 bb_error_msg_and_die("no files");
140 } 202 }
141 203
204 tailbufsize = BUFSIZ;
142#ifdef CONFIG_FEATURE_FANCY_TAIL 205#ifdef CONFIG_FEATURE_FANCY_TAIL
143 /* tail the files */ 206 /* tail the files */
144 if (!from_top && units == BYTES) 207 if (from_top < count_bytes) { /* Each is 0 or 1, so true iff 0 < 1. */
145 tailbuf = xmalloc(count); 208 /* Hence, !from_top && count_bytes */
209 if (tailbufsize < count) {
210 tailbufsize = count + BUFSIZ;
211 }
212 }
146#endif 213#endif
214 buf = tailbuf = xmalloc(tailbufsize);
147 215
148 for (i = 0; i < nfiles; i++) { 216 fmt = header_fmt + 1; /* Skip header leading newline on first output. */
149 if (fds[i] == -1) 217 i = 0;
150 continue; 218 do {
151 if (!count) { 219 /* Be careful. It would be possible to optimize the count-bytes
152 lseek(fds[i], 0, SEEK_END); 220 * case if the file is seekable. If you do though, remember that
221 * starting file position may not be the beginning of the file.
222 * Beware of backing up too far. See example in wc.c.
223 */
224 if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) {
153 continue; 225 continue;
154 } 226 }
155 seen = 0; 227
156 if (show_headers || (!hide_headers && nfiles > 1)) 228 if (nfiles > header_threshhold) {
157 printf("%s==> %s <==\n", i == 0 ? "" : "\n", argv[optind + i]); 229 tail_xprint_header(fmt, argv[i]);
158 while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) { 230 fmt = header_fmt;
231 }
232
233 buf = tailbuf;
234 taillen = 0;
235 seen = 1;
236
237 while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) {
159 if (from_top) { 238 if (from_top) {
239 nwrite = nread;
240 if (seen < count) {
160#ifdef CONFIG_FEATURE_FANCY_TAIL 241#ifdef CONFIG_FEATURE_FANCY_TAIL
161 if (units == BYTES) { 242 if (count_bytes) {
162 if (count - 1 <= seen) 243 nwrite -= (count - seen);
163 nwrite = nread; 244 seen = count;
164 else if (count - 1 <= seen + nread) 245 } else
165 nwrite = nread + seen - (count - 1);
166 else
167 nwrite = 0;
168 seen += nread;
169 } else {
170#else
171 {
172#endif 246#endif
173 if (count - 1 <= seen) 247 {
174 nwrite = nread; 248 s = buf;
175 else { 249 do {
176 nwrite = 0; 250 --nwrite;
177 for (s = memchr(buf, '\n', nread); s != NULL; 251 if ((*s++ == '\n') && (++seen == count)) {
178 s = memchr(s+1, '\n', nread - (s + 1 - buf))) {
179 if (count - 1 <= ++seen) {
180 nwrite = nread - (s + 1 - buf);
181 break; 252 break;
182 } 253 }
183 } 254 } while (nwrite);
184 } 255 }
185 } 256 }
186 if (full_write(STDOUT_FILENO, buf + nread - nwrite, 257 tail_xbb_full_write(buf + nread - nwrite, nwrite);
187 nwrite) < 0) { 258 } else if (count) {
188 perror_msg("write");
189 status = EXIT_FAILURE;
190 break;
191 }
192 } else {
193#ifdef CONFIG_FEATURE_FANCY_TAIL 259#ifdef CONFIG_FEATURE_FANCY_TAIL
194 if (units == BYTES) { 260 if (count_bytes) {
195 if (nread < count) { 261 taillen += nread;
196 memmove(tailbuf, tailbuf + nread, count - nread); 262 if (taillen > count) {
197 memcpy(tailbuf + count - nread, buf, nread); 263 memmove(tailbuf, tailbuf + taillen - count, count);
198 } else { 264 taillen = count;
199 memcpy(tailbuf, buf + nread - count, count);
200 } 265 }
201 seen += nread; 266 } else
202 } else {
203#else
204 {
205#endif 267#endif
206 for (start = buf, end = memchr(buf, '\n', nread); 268 {
207 end != NULL; start = end+1, 269 int k = nread;
208 end = memchr(start, '\n', nread - (start - buf))) { 270 int nbuf = 0;
209 if (newline && count <= seen) 271
210 tailbuf_trunc(); 272 while (k) {
211 tailbuf_append(start, end - start + 1); 273 --k;
212 seen++; 274 if (buf[k] == '\n') {
213 newline = 1; 275 ++nbuf;
276 }
214 } 277 }
215 if (newline && count <= seen && nread - (start - buf) > 0)
216 tailbuf_trunc();
217 tailbuf_append(start, nread - (start - buf));
218 }
219 }
220 }
221 278
222 if (nread < 0) { 279 if (newline + nbuf < count) {
223 perror_msg("read"); 280 newline += nbuf;
224 status = EXIT_FAILURE; 281 taillen += nread;
225 }
226 282
227#ifdef CONFIG_FEATURE_FANCY_TAIL 283 } else {
228 if (!from_top && units == BYTES) { 284 int extra = 0;
229 if (count < seen) 285 if (buf[nread-1] != '\n') {
230 seen = count; 286 extra = 1;
231 if (full_write(STDOUT_FILENO, tailbuf + count - seen, seen) < 0) { 287 }
232 perror_msg("write"); 288
233 status = EXIT_FAILURE; 289 k = newline + nbuf + extra - count;
290 s = tailbuf;
291 while (k) {
292 if (*s == '\n') {
293 --k;
294 }
295 ++s;
296 }
297
298 taillen += nread - (s - tailbuf);
299 memmove(tailbuf, s, taillen);
300 newline = count - extra;
301 }
302 if (tailbufsize < taillen + BUFSIZ) {
303 tailbufsize = taillen + BUFSIZ;
304 tailbuf = xrealloc(tailbuf, tailbufsize);
305 }
306 }
307 buf = tailbuf + taillen;
234 } 308 }
235 } 309 }
236#endif
237 310
238 if (!from_top && units == LINES) { 311 if (!from_top) {
239 if (full_write(STDOUT_FILENO, tailbuf, taillen) < 0) { 312 tail_xbb_full_write(tailbuf, taillen);
240 perror_msg("write");
241 status = EXIT_FAILURE;
242 }
243 } 313 }
244 314
245 taillen = 0; 315 taillen = 0;
246 } 316 } while (++i < nfiles);
247
248 while (follow) {
249 sleep(sleep_period);
250 317
251 for (i = 0; i < nfiles; i++) { 318 buf = xrealloc(tailbuf, BUFSIZ);
252 if (fds[i] == -1)
253 continue;
254 319
255 if ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) { 320 fmt = NULL;
256 if (show_headers || (!hide_headers && nfiles > 1))
257 printf("\n==> %s <==\n", argv[optind + i]);
258 321
259 do { 322 while (follow) {
260 full_write(STDOUT_FILENO, buf, nread); 323 sleep(sleep_period);
261 } while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0); 324 i = 0;
325 do {
326 if (nfiles > header_threshhold) {
327 fmt = header_fmt;
262 } 328 }
263 329 while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) {
264 if (nread < 0) { 330 if (fmt) {
265 perror_msg("read"); 331 tail_xprint_header(fmt, argv[i]);
266 status = EXIT_FAILURE; 332 fmt = NULL;
333 }
334 tail_xbb_full_write(buf, nread);
267 } 335 }
268 } 336 } while (++i < nfiles);
269 } 337 }
270 338
271 return status; 339 return status;