aboutsummaryrefslogtreecommitdiff
path: root/tail.c
diff options
context:
space:
mode:
Diffstat (limited to 'tail.c')
-rw-r--r--tail.c1789
1 files changed, 848 insertions, 941 deletions
diff --git a/tail.c b/tail.c
index 0ab8f11b0..31705afa2 100644
--- a/tail.c
+++ b/tail.c
@@ -1,3 +1,4 @@
1/* vi: set sw=4 ts=4: */
1#include "internal.h" 2#include "internal.h"
2/* This file contains _two_ implementations of tail. One is 3/* This file contains _two_ implementations of tail. One is
3 * a bit more full featured, but costs 6k. The other (i.e. the 4 * a bit more full featured, but costs 6k. The other (i.e. the
@@ -68,22 +69,23 @@ static int forever;
68static int print_headers; 69static int print_headers;
69 70
70const char tail_usage[] = 71const char tail_usage[] =
71 "tail [OPTION] [FILE]...\n\n" 72 "tail [OPTION] [FILE]...\n\n"
72 "Print last 10 lines of each FILE to standard output.\n" 73 "Print last 10 lines of each FILE to standard output.\n"
73 "With more than one FILE, precede each with a header giving the\n" 74 "With more than one FILE, precede each with a header giving the\n"
74 "file name. With no FILE, or when FILE is -, read standard input.\n\n" 75 "file name. With no FILE, or when FILE is -, read standard input.\n\n"
75 "Options:\n" 76 "Options:\n"
76 "\t-n NUM\t\tPrint last NUM lines instead of first 10\n" 77 "\t-n NUM\t\tPrint last NUM lines instead of first 10\n"
77 "\t-f\t\tOutput data as the file grows. This version\n" 78
78 "\t\t\tof 'tail -f' supports only one file at a time.\n"; 79 "\t-f\t\tOutput data as the file grows. This version\n"
80 "\t\t\tof 'tail -f' supports only one file at a time.\n";
79 81
80 82
81static void write_header(const char *filename) 83static void write_header(const char *filename)
82{ 84{
83 static int first_file = 1; 85 static int first_file = 1;
84 86
85 printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename); 87 printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
86 first_file = 0; 88 first_file = 0;
87} 89}
88 90
89/* Print the last N_LINES lines from the end of file FD. 91/* Print the last N_LINES lines from the end of file FD.
@@ -97,57 +99,57 @@ static void write_header(const char *filename)
97static int 99static int
98file_lines(const char *filename, int fd, long int n_lines, off_t pos) 100file_lines(const char *filename, int fd, long int n_lines, off_t pos)
99{ 101{
100 char buffer[BUFSIZ]; 102 char buffer[BUFSIZ];
101 int bytes_read; 103 int bytes_read;
102 int i; /* Index into `buffer' for scanning. */ 104 int i; /* Index into `buffer' for scanning. */
103
104 if (n_lines == 0)
105 return 0;
106 105
107 /* Set `bytes_read' to the size of the last, probably partial, buffer; 106 if (n_lines == 0)
108 0 < `bytes_read' <= `BUFSIZ'. */
109 bytes_read = pos % BUFSIZ;
110 if (bytes_read == 0)
111 bytes_read = BUFSIZ;
112 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
113 reads will be on block boundaries, which might increase efficiency. */
114 pos -= bytes_read;
115 lseek(fd, pos, SEEK_SET);
116 bytes_read = fullRead(fd, buffer, bytes_read);
117 if (bytes_read == -1)
118 error("read error");
119
120 /* Count the incomplete line on files that don't end with a newline. */
121 if (bytes_read && buffer[bytes_read - 1] != '\n')
122 --n_lines;
123
124 do {
125 /* Scan backward, counting the newlines in this bufferfull. */
126 for (i = bytes_read - 1; i >= 0; i--) {
127 /* Have we counted the requested number of newlines yet? */
128 if (buffer[i] == '\n' && n_lines-- == 0) {
129 /* If this newline wasn't the last character in the buffer,
130 print the text after it. */
131 if (i != bytes_read - 1)
132 XWRITE(STDOUT_FILENO, &buffer[i + 1],
133 bytes_read - (i + 1));
134 return 0; 107 return 0;
135 } 108
136 } 109 /* Set `bytes_read' to the size of the last, probably partial, buffer;
137 /* Not enough newlines in that bufferfull. */ 110 0 < `bytes_read' <= `BUFSIZ'. */
138 if (pos == 0) { 111 bytes_read = pos % BUFSIZ;
139 /* Not enough lines in the file; print the entire file. */ 112 if (bytes_read == 0)
140 lseek(fd, (off_t) 0, SEEK_SET); 113 bytes_read = BUFSIZ;
141 return 0; 114 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
142 } 115 reads will be on block boundaries, which might increase efficiency. */
143 pos -= BUFSIZ; 116 pos -= bytes_read;
144 lseek(fd, pos, SEEK_SET); 117 lseek(fd, pos, SEEK_SET);
145 } 118 bytes_read = fullRead(fd, buffer, bytes_read);
146 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0); 119 if (bytes_read == -1)
147 if (bytes_read == -1) 120 error("read error");
148 error("read error"); 121
122 /* Count the incomplete line on files that don't end with a newline. */
123 if (bytes_read && buffer[bytes_read - 1] != '\n')
124 --n_lines;
125
126 do {
127 /* Scan backward, counting the newlines in this bufferfull. */
128 for (i = bytes_read - 1; i >= 0; i--) {
129 /* Have we counted the requested number of newlines yet? */
130 if (buffer[i] == '\n' && n_lines-- == 0) {
131 /* If this newline wasn't the last character in the buffer,
132 print the text after it. */
133 if (i != bytes_read - 1)
134 XWRITE(STDOUT_FILENO, &buffer[i + 1],
135 bytes_read - (i + 1));
136 return 0;
137 }
138 }
139 /* Not enough newlines in that bufferfull. */
140 if (pos == 0) {
141 /* Not enough lines in the file; print the entire file. */
142 lseek(fd, (off_t) 0, SEEK_SET);
143 return 0;
144 }
145 pos -= BUFSIZ;
146 lseek(fd, pos, SEEK_SET);
147 }
148 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
149 if (bytes_read == -1)
150 error("read error");
149 151
150 return 0; 152 return 0;
151} 153}
152 154
153/* Print the last N_LINES lines from the end of the standard input, 155/* Print the last N_LINES lines from the end of the standard input,
@@ -157,100 +159,100 @@ file_lines(const char *filename, int fd, long int n_lines, off_t pos)
157 159
158static int pipe_lines(const char *filename, int fd, long int n_lines) 160static int pipe_lines(const char *filename, int fd, long int n_lines)
159{ 161{
160 struct linebuffer { 162 struct linebuffer {
161 int nbytes, nlines; 163 int nbytes, nlines;
162 char buffer[BUFSIZ]; 164 char buffer[BUFSIZ];
163 struct linebuffer *next; 165 struct linebuffer *next;
164 }; 166 };
165 typedef struct linebuffer LBUFFER; 167 typedef struct linebuffer LBUFFER;
166 LBUFFER *first, *last, *tmp; 168 LBUFFER *first, *last, *tmp;
167 int i; /* Index into buffers. */ 169 int i; /* Index into buffers. */
168 int total_lines = 0; /* Total number of newlines in all buffers. */ 170 int total_lines = 0; /* Total number of newlines in all buffers. */
169 int errors = 0; 171 int errors = 0;
170 172
171 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER)); 173 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
172 first->nbytes = first->nlines = 0; 174 first->nbytes = first->nlines = 0;
173 first->next = NULL; 175 first->next = NULL;
174 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER)); 176 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
175 177
176 /* Input is always read into a fresh buffer. */ 178 /* Input is always read into a fresh buffer. */
177 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) { 179 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
178 tmp->nlines = 0; 180 tmp->nlines = 0;
179 tmp->next = NULL; 181 tmp->next = NULL;
180 182
181 /* Count the number of newlines just read. */ 183 /* Count the number of newlines just read. */
182 for (i = 0; i < tmp->nbytes; i++) 184 for (i = 0; i < tmp->nbytes; i++)
183 if (tmp->buffer[i] == '\n') 185 if (tmp->buffer[i] == '\n')
184 ++tmp->nlines; 186 ++tmp->nlines;
185 total_lines += tmp->nlines; 187 total_lines += tmp->nlines;
186 188
187 /* If there is enough room in the last buffer read, just append the new 189 /* If there is enough room in the last buffer read, just append the new
188 one to it. This is because when reading from a pipe, `nbytes' can 190 one to it. This is because when reading from a pipe, `nbytes' can
189 often be very small. */ 191 often be very small. */
190 if (tmp->nbytes + last->nbytes < BUFSIZ) { 192 if (tmp->nbytes + last->nbytes < BUFSIZ) {
191 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes); 193 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
192 last->nbytes += tmp->nbytes; 194 last->nbytes += tmp->nbytes;
193 last->nlines += tmp->nlines; 195 last->nlines += tmp->nlines;
194 } else { 196 } else {
195 /* If there's not enough room, link the new buffer onto the end of 197 /* If there's not enough room, link the new buffer onto the end of
196 the list, then either free up the oldest buffer for the next 198 the list, then either free up the oldest buffer for the next
197 read if that would leave enough lines, or else malloc a new one. 199 read if that would leave enough lines, or else malloc a new one.
198 Some compaction mechanism is possible but probably not 200 Some compaction mechanism is possible but probably not
199 worthwhile. */ 201 worthwhile. */
200 last = last->next = tmp; 202 last = last->next = tmp;
201 if (total_lines - first->nlines > n_lines) { 203 if (total_lines - first->nlines > n_lines) {
202 tmp = first; 204 tmp = first;
203 total_lines -= first->nlines; 205 total_lines -= first->nlines;
204 first = first->next; 206 first = first->next;
205 } else 207 } else
206 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER)); 208 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
209 }
207 } 210 }
208 } 211 if (tmp->nbytes == -1)
209 if (tmp->nbytes == -1) 212 error("read error");
210 error("read error"); 213
211 214 free((char *) tmp);
212 free((char *) tmp); 215
213 216 /* This prevents a core dump when the pipe contains no newlines. */
214 /* This prevents a core dump when the pipe contains no newlines. */ 217 if (n_lines == 0)
215 if (n_lines == 0) 218 goto free_lbuffers;
216 goto free_lbuffers; 219
217 220 /* Count the incomplete line on files that don't end with a newline. */
218 /* Count the incomplete line on files that don't end with a newline. */ 221 if (last->buffer[last->nbytes - 1] != '\n') {
219 if (last->buffer[last->nbytes - 1] != '\n') { 222 ++last->nlines;
220 ++last->nlines; 223 ++total_lines;
221 ++total_lines; 224 }
222 } 225
223 226 /* Run through the list, printing lines. First, skip over unneeded
224 /* Run through the list, printing lines. First, skip over unneeded 227 buffers. */
225 buffers. */ 228 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
226 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next) 229 total_lines -= tmp->nlines;
227 total_lines -= tmp->nlines; 230
228 231 /* Find the correct beginning, then print the rest of the file. */
229 /* Find the correct beginning, then print the rest of the file. */ 232 if (total_lines > n_lines) {
230 if (total_lines > n_lines) { 233 char *cp;
231 char *cp; 234
232 235 /* Skip `total_lines' - `n_lines' newlines. We made sure that
233 /* Skip `total_lines' - `n_lines' newlines. We made sure that 236 `total_lines' - `n_lines' <= `tmp->nlines'. */
234 `total_lines' - `n_lines' <= `tmp->nlines'. */ 237 cp = tmp->buffer;
235 cp = tmp->buffer; 238 for (i = total_lines - n_lines; i; --i)
236 for (i = total_lines - n_lines; i; --i) 239 while (*cp++ != '\n')
237 while (*cp++ != '\n') 240 /* Do nothing. */ ;
238 /* Do nothing. */ ; 241 i = cp - tmp->buffer;
239 i = cp - tmp->buffer; 242 } else
240 } else 243 i = 0;
241 i = 0; 244 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
242 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i); 245
243 246 for (tmp = tmp->next; tmp; tmp = tmp->next)
244 for (tmp = tmp->next; tmp; tmp = tmp->next) 247 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
245 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
246 248
247 free_lbuffers: 249 free_lbuffers:
248 while (first) { 250 while (first) {
249 tmp = first->next; 251 tmp = first->next;
250 free((char *) first); 252 free((char *) first);
251 first = tmp; 253 first = tmp;
252 } 254 }
253 return errors; 255 return errors;
254} 256}
255 257
256/* Display file FILENAME from the current position in FD to the end. 258/* Display file FILENAME from the current position in FD to the end.
@@ -259,25 +261,25 @@ static int pipe_lines(const char *filename, int fd, long int n_lines)
259 261
260static long dump_remainder(const char *filename, int fd) 262static long dump_remainder(const char *filename, int fd)
261{ 263{
262 char buffer[BUFSIZ]; 264 char buffer[BUFSIZ];
263 int bytes_read; 265 int bytes_read;
264 long total; 266 long total;
265 267
266 total = 0; 268 total = 0;
267 output: 269 output:
268 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) { 270 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
269 XWRITE(STDOUT_FILENO, buffer, bytes_read); 271 XWRITE(STDOUT_FILENO, buffer, bytes_read);
270 total += bytes_read; 272 total += bytes_read;
271 } 273 }
272 if (bytes_read == -1) 274 if (bytes_read == -1)
273 error("read error"); 275 error("read error");
274 if (forever) { 276 if (forever) {
275 fflush(stdout); 277 fflush(stdout);
276 sleep(1); 278 sleep(1);
277 goto output; 279 goto output;
278 } 280 }
279 281
280 return total; 282 return total;
281} 283}
282 284
283/* Output the last N_LINES lines of file FILENAME open for reading in FD. 285/* Output the last N_LINES lines of file FILENAME open for reading in FD.
@@ -285,31 +287,31 @@ static long dump_remainder(const char *filename, int fd)
285 287
286static int tail_lines(const char *filename, int fd, long int n_lines) 288static int tail_lines(const char *filename, int fd, long int n_lines)
287{ 289{
288 struct stat stats; 290 struct stat stats;
289 off_t length; 291 off_t length;
290 292
291 if (print_headers) 293 if (print_headers)
292 write_header(filename); 294 write_header(filename);
293 295
294 if (fstat(fd, &stats)) 296 if (fstat(fd, &stats))
295 error("fstat error"); 297 error("fstat error");
296 298
297 /* Use file_lines only if FD refers to a regular file with 299 /* Use file_lines only if FD refers to a regular file with
298 its file pointer positioned at beginning of file. */ 300 its file pointer positioned at beginning of file. */
299 /* FIXME: adding the lseek conjunct is a kludge. 301 /* FIXME: adding the lseek conjunct is a kludge.
300 Once there's a reasonable test suite, fix the true culprit: 302 Once there's a reasonable test suite, fix the true culprit:
301 file_lines. file_lines shouldn't presume that the input 303 file_lines. file_lines shouldn't presume that the input
302 file pointer is initially positioned to beginning of file. */ 304 file pointer is initially positioned to beginning of file. */
303 if (S_ISREG(stats.st_mode) 305 if (S_ISREG(stats.st_mode)
304 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) { 306 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
305 length = lseek(fd, (off_t) 0, SEEK_END); 307 length = lseek(fd, (off_t) 0, SEEK_END);
306 if (length != 0 && file_lines(filename, fd, n_lines, length)) 308 if (length != 0 && file_lines(filename, fd, n_lines, length))
307 return 1; 309 return 1;
308 dump_remainder(filename, fd); 310 dump_remainder(filename, fd);
309 } else 311 } else
310 return pipe_lines(filename, fd, n_lines); 312 return pipe_lines(filename, fd, n_lines);
311 313
312 return 0; 314 return 0;
313} 315}
314 316
315/* Display the last N_UNITS lines of file FILENAME. 317/* Display the last N_UNITS lines of file FILENAME.
@@ -318,78 +320,78 @@ static int tail_lines(const char *filename, int fd, long int n_lines)
318 320
319static int tail_file(const char *filename, off_t n_units) 321static int tail_file(const char *filename, off_t n_units)
320{ 322{
321 int fd, errors; 323 int fd, errors;
322 324
323 if (!strcmp(filename, "-")) { 325 if (!strcmp(filename, "-")) {
324 filename = "standard input"; 326 filename = "standard input";
325 errors = tail_lines(filename, 0, (long) n_units); 327 errors = tail_lines(filename, 0, (long) n_units);
326 } else { 328 } else {
327 /* Not standard input. */ 329 /* Not standard input. */
328 fd = open(filename, O_RDONLY); 330 fd = open(filename, O_RDONLY);
329 if (fd == -1) 331 if (fd == -1)
330 error("open error"); 332 error("open error");
331 333
332 errors = tail_lines(filename, fd, (long) n_units); 334 errors = tail_lines(filename, fd, (long) n_units);
333 close(fd); 335 close(fd);
334 } 336 }
335 337
336 return errors; 338 return errors;
337} 339}
338 340
339extern int tail_main(int argc, char **argv) 341extern int tail_main(int argc, char **argv)
340{ 342{
341 int exit_status = 0; 343 int exit_status = 0;
342 int n_units = DEFAULT_N_LINES; 344 int n_units = DEFAULT_N_LINES;
343 int n_tmp, i; 345 int n_tmp, i;
344 char opt; 346 char opt;
345 347
346 forever = print_headers = 0; 348 forever = print_headers = 0;
347 349
348 /* parse argv[] */ 350 /* parse argv[] */
349 for (i = 1; i < argc; i++) { 351 for (i = 1; i < argc; i++) {
350 if (argv[i][0] == '-') { 352 if (argv[i][0] == '-') {
351 opt = argv[i][1]; 353 opt = argv[i][1];
352 switch (opt) { 354 switch (opt) {
353 case 'f': 355 case 'f':
354 forever = 1; 356 forever = 1;
355 break; 357 break;
356 case 'n': 358 case 'n':
357 n_tmp = 0; 359 n_tmp = 0;
358 if (++i < argc) 360 if (++i < argc)
359 n_tmp = atoi(argv[i]); 361 n_tmp = atoi(argv[i]);
360 if (n_tmp < 1) 362 if (n_tmp < 1)
361 usage(tail_usage); 363 usage(tail_usage);
362 n_units = n_tmp; 364 n_units = n_tmp;
363 break; 365 break;
364 case '-': 366 case '-':
365 case 'h': 367 case 'h':
366 usage(tail_usage); 368 usage(tail_usage);
367 default: 369 default:
368 fprintf(stderr, "tail: invalid option -- %c\n", opt); 370 fprintf(stderr, "tail: invalid option -- %c\n", opt);
369 usage(tail_usage); 371 usage(tail_usage);
370 } 372 }
371 } else { 373 } else {
372 break; 374 break;
375 }
373 } 376 }
374 }
375 377
376 if (i + 1 < argc) { 378 if (i + 1 < argc) {
377 if (forever) { 379 if (forever) {
378 fprintf(stderr, 380 fprintf(stderr,
379 "tail: option -f is invalid with multiple files\n"); 381 "tail: option -f is invalid with multiple files\n");
380 usage(tail_usage); 382 usage(tail_usage);
383 }
384 print_headers = 1;
381 } 385 }
382 print_headers = 1;
383 }
384 386
385 if (i >= argc) { 387 if (i >= argc) {
386 exit_status |= tail_file("-", n_units); 388 exit_status |= tail_file("-", n_units);
387 } else { 389 } else {
388 for (; i < argc; i++) 390 for (; i < argc; i++)
389 exit_status |= tail_file(argv[i], n_units); 391 exit_status |= tail_file(argv[i], n_units);
390 } 392 }
391 393
392 exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 394 exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
393} 395}
394 396
395 397
@@ -441,15 +443,15 @@ extern int tail_main(int argc, char **argv)
441#define NDEBUG 1 443#define NDEBUG 1
442 444
443 445
444static void detailed_error(int i, int errnum, char* fmt, ...) 446static void detailed_error(int i, int errnum, char *fmt, ...)
445{ 447{
446 va_list arguments; 448 va_list arguments;
447 449
448 va_start(arguments, fmt); 450 va_start(arguments, fmt);
449 vfprintf(stderr, fmt, arguments); 451 vfprintf(stderr, fmt, arguments);
450 fprintf(stderr, "\n%s\n", strerror( errnum)); 452 fprintf(stderr, "\n%s\n", strerror(errnum));
451 va_end(arguments); 453 va_end(arguments);
452 exit(i); 454 exit(i);
453} 455}
454 456
455 457
@@ -494,9 +496,8 @@ static int from_start;
494static int print_headers; 496static int print_headers;
495 497
496/* When to print the filename banners. */ 498/* When to print the filename banners. */
497enum header_mode 499enum header_mode {
498{ 500 multiple_files, always, never
499 multiple_files, always, never
500}; 501};
501 502
502/* The name this program was run with. */ 503/* The name this program was run with. */
@@ -506,8 +507,7 @@ char *program_name;
506static int have_read_stdin; 507static int have_read_stdin;
507 508
508 509
509static const char tail_usage[] = 510static const char tail_usage[] = "tail [OPTION]... [FILE]...\n\
510"tail [OPTION]... [FILE]...\n\
511\n\ 511\n\
512Print last 10 lines of each FILE to standard output.\n\ 512Print last 10 lines of each FILE to standard output.\n\
513With more than one FILE, precede each with a header giving the file name.\n\ 513With more than one FILE, precede each with a header giving the file name.\n\
@@ -524,15 +524,13 @@ If the first character of N (bytes or lines) is a `+', output begins with \n\
524the Nth item from the start of each file, otherwise, print the last N items\n\ 524the Nth item from the start of each file, otherwise, print the last N items\n\
525in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2).\n\n"; 525in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2).\n\n";
526 526
527static void 527static void write_header(const char *filename, const char *comment)
528write_header (const char *filename, const char *comment)
529{ 528{
530 static int first_file = 1; 529 static int first_file = 1;
531 530
532 printf ("%s==> %s%s%s <==\n", (first_file ? "" : "\n"), filename, 531 printf("%s==> %s%s%s <==\n", (first_file ? "" : "\n"), filename,
533 (comment ? ": " : ""), 532 (comment ? ": " : ""), (comment ? comment : ""));
534 (comment ? comment : "")); 533 first_file = 0;
535 first_file = 0;
536} 534}
537 535
538/* Print the last N_LINES lines from the end of file FD. 536/* Print the last N_LINES lines from the end of file FD.
@@ -544,67 +542,62 @@ write_header (const char *filename, const char *comment)
544 Return 0 if successful, 1 if an error occurred. */ 542 Return 0 if successful, 1 if an error occurred. */
545 543
546static int 544static int
547file_lines (const char *filename, int fd, long int n_lines, off_t pos) 545file_lines(const char *filename, int fd, long int n_lines, off_t pos)
548{ 546{
549 char buffer[BUFSIZ]; 547 char buffer[BUFSIZ];
550 int bytes_read; 548 int bytes_read;
551 int i; /* Index into `buffer' for scanning. */ 549 int i; /* Index into `buffer' for scanning. */
552 550
553 if (n_lines == 0) 551 if (n_lines == 0)
554 return 0; 552 return 0;
555 553
556 /* Set `bytes_read' to the size of the last, probably partial, buffer; 554 /* Set `bytes_read' to the size of the last, probably partial, buffer;
557 0 < `bytes_read' <= `BUFSIZ'. */ 555 0 < `bytes_read' <= `BUFSIZ'. */
558 bytes_read = pos % BUFSIZ; 556 bytes_read = pos % BUFSIZ;
559 if (bytes_read == 0) 557 if (bytes_read == 0)
560 bytes_read = BUFSIZ; 558 bytes_read = BUFSIZ;
561 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all 559 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
562 reads will be on block boundaries, which might increase efficiency. */ 560 reads will be on block boundaries, which might increase efficiency. */
563 pos -= bytes_read; 561 pos -= bytes_read;
564 lseek (fd, pos, SEEK_SET); 562 lseek(fd, pos, SEEK_SET);
565 bytes_read = fullRead (fd, buffer, bytes_read); 563 bytes_read = fullRead(fd, buffer, bytes_read);
566 if (bytes_read == -1) 564 if (bytes_read == -1) {
567 { 565 detailed_error(0, errno, "%s", filename);
568 detailed_error (0, errno, "%s", filename); 566 return 1;
569 return 1; 567 }
570 } 568
571 569 /* Count the incomplete line on files that don't end with a newline. */
572 /* Count the incomplete line on files that don't end with a newline. */ 570 if (bytes_read && buffer[bytes_read - 1] != '\n')
573 if (bytes_read && buffer[bytes_read - 1] != '\n') 571 --n_lines;
574 --n_lines; 572
575 573 do {
576 do 574 /* Scan backward, counting the newlines in this bufferfull. */
577 { 575 for (i = bytes_read - 1; i >= 0; i--) {
578 /* Scan backward, counting the newlines in this bufferfull. */ 576 /* Have we counted the requested number of newlines yet? */
579 for (i = bytes_read - 1; i >= 0; i--) 577 if (buffer[i] == '\n' && n_lines-- == 0) {
580 { 578 /* If this newline wasn't the last character in the buffer,
581 /* Have we counted the requested number of newlines yet? */ 579 print the text after it. */
582 if (buffer[i] == '\n' && n_lines-- == 0) 580 if (i != bytes_read - 1)
583 { 581 XWRITE(STDOUT_FILENO, &buffer[i + 1],
584 /* If this newline wasn't the last character in the buffer, 582 bytes_read - (i + 1));
585 print the text after it. */ 583 return 0;
586 if (i != bytes_read - 1) 584 }
587 XWRITE (STDOUT_FILENO, &buffer[i + 1], bytes_read - (i + 1)); 585 }
588 return 0; 586 /* Not enough newlines in that bufferfull. */
589 } 587 if (pos == 0) {
588 /* Not enough lines in the file; print the entire file. */
589 lseek(fd, (off_t) 0, SEEK_SET);
590 return 0;
591 }
592 pos -= BUFSIZ;
593 lseek(fd, pos, SEEK_SET);
590 } 594 }
591 /* Not enough newlines in that bufferfull. */ 595 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
592 if (pos == 0) 596 if (bytes_read == -1) {
593 { 597 detailed_error(0, errno, "%s", filename);
594 /* Not enough lines in the file; print the entire file. */ 598 return 1;
595 lseek (fd, (off_t) 0, SEEK_SET);
596 return 0;
597 } 599 }
598 pos -= BUFSIZ; 600 return 0;
599 lseek (fd, pos, SEEK_SET);
600 }
601 while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0);
602 if (bytes_read == -1)
603 {
604 detailed_error (0, errno, "%s", filename);
605 return 1;
606 }
607 return 0;
608} 601}
609 602
610/* Print the last N_LINES lines from the end of the standard input, 603/* Print the last N_LINES lines from the end of the standard input,
@@ -612,301 +605,264 @@ file_lines (const char *filename, int fd, long int n_lines, off_t pos)
612 Buffer the text as a linked list of LBUFFERs, adding them as needed. 605 Buffer the text as a linked list of LBUFFERs, adding them as needed.
613 Return 0 if successful, 1 if an error occured. */ 606 Return 0 if successful, 1 if an error occured. */
614 607
615static int 608static int pipe_lines(const char *filename, int fd, long int n_lines)
616pipe_lines (const char *filename, int fd, long int n_lines)
617{ 609{
618 struct linebuffer 610 struct linebuffer {
619 { 611 int nbytes, nlines;
620 int nbytes, nlines; 612 char buffer[BUFSIZ];
621 char buffer[BUFSIZ]; 613 struct linebuffer *next;
622 struct linebuffer *next; 614 };
623 }; 615 typedef struct linebuffer LBUFFER;
624 typedef struct linebuffer LBUFFER; 616 LBUFFER *first, *last, *tmp;
625 LBUFFER *first, *last, *tmp; 617 int i; /* Index into buffers. */
626 int i; /* Index into buffers. */ 618 int total_lines = 0; /* Total number of newlines in all buffers. */
627 int total_lines = 0; /* Total number of newlines in all buffers. */ 619 int errors = 0;
628 int errors = 0; 620
629 621 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
630 first = last = (LBUFFER *) xmalloc (sizeof (LBUFFER)); 622 first->nbytes = first->nlines = 0;
631 first->nbytes = first->nlines = 0; 623 first->next = NULL;
632 first->next = NULL; 624 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
633 tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER)); 625
634 626 /* Input is always read into a fresh buffer. */
635 /* Input is always read into a fresh buffer. */ 627 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
636 while ((tmp->nbytes = fullRead (fd, tmp->buffer, BUFSIZ)) > 0) 628 tmp->nlines = 0;
637 { 629 tmp->next = NULL;
638 tmp->nlines = 0; 630
639 tmp->next = NULL; 631 /* Count the number of newlines just read. */
640 632 for (i = 0; i < tmp->nbytes; i++)
641 /* Count the number of newlines just read. */ 633 if (tmp->buffer[i] == '\n')
642 for (i = 0; i < tmp->nbytes; i++) 634 ++tmp->nlines;
643 if (tmp->buffer[i] == '\n') 635 total_lines += tmp->nlines;
644 ++tmp->nlines; 636
645 total_lines += tmp->nlines; 637 /* If there is enough room in the last buffer read, just append the new
646 638 one to it. This is because when reading from a pipe, `nbytes' can
647 /* If there is enough room in the last buffer read, just append the new 639 often be very small. */
648 one to it. This is because when reading from a pipe, `nbytes' can 640 if (tmp->nbytes + last->nbytes < BUFSIZ) {
649 often be very small. */ 641 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
650 if (tmp->nbytes + last->nbytes < BUFSIZ) 642 last->nbytes += tmp->nbytes;
651 { 643 last->nlines += tmp->nlines;
652 memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes); 644 } else {
653 last->nbytes += tmp->nbytes; 645 /* If there's not enough room, link the new buffer onto the end of
654 last->nlines += tmp->nlines; 646 the list, then either free up the oldest buffer for the next
647 read if that would leave enough lines, or else malloc a new one.
648 Some compaction mechanism is possible but probably not
649 worthwhile. */
650 last = last->next = tmp;
651 if (total_lines - first->nlines > n_lines) {
652 tmp = first;
653 total_lines -= first->nlines;
654 first = first->next;
655 } else
656 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
657 }
658 }
659 if (tmp->nbytes == -1) {
660 detailed_error(0, errno, "%s", filename);
661 errors = 1;
662 free((char *) tmp);
663 goto free_lbuffers;
655 } 664 }
656 else 665
657 { 666 free((char *) tmp);
658 /* If there's not enough room, link the new buffer onto the end of 667
659 the list, then either free up the oldest buffer for the next 668 /* This prevents a core dump when the pipe contains no newlines. */
660 read if that would leave enough lines, or else malloc a new one. 669 if (n_lines == 0)
661 Some compaction mechanism is possible but probably not 670 goto free_lbuffers;
662 worthwhile. */ 671
663 last = last->next = tmp; 672 /* Count the incomplete line on files that don't end with a newline. */
664 if (total_lines - first->nlines > n_lines) 673 if (last->buffer[last->nbytes - 1] != '\n') {
665 { 674 ++last->nlines;
666 tmp = first; 675 ++total_lines;
667 total_lines -= first->nlines; 676 }
668 first = first->next; 677
669 } 678 /* Run through the list, printing lines. First, skip over unneeded
670 else 679 buffers. */
671 tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER)); 680 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
681 total_lines -= tmp->nlines;
682
683 /* Find the correct beginning, then print the rest of the file. */
684 if (total_lines > n_lines) {
685 char *cp;
686
687 /* Skip `total_lines' - `n_lines' newlines. We made sure that
688 `total_lines' - `n_lines' <= `tmp->nlines'. */
689 cp = tmp->buffer;
690 for (i = total_lines - n_lines; i; --i)
691 while (*cp++ != '\n')
692 /* Do nothing. */ ;
693 i = cp - tmp->buffer;
694 } else
695 i = 0;
696 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
697
698 for (tmp = tmp->next; tmp; tmp = tmp->next)
699 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
700
701 free_lbuffers:
702 while (first) {
703 tmp = first->next;
704 free((char *) first);
705 first = tmp;
672 } 706 }
673 } 707 return errors;
674 if (tmp->nbytes == -1)
675 {
676 detailed_error (0, errno, "%s", filename);
677 errors = 1;
678 free ((char *) tmp);
679 goto free_lbuffers;
680 }
681
682 free ((char *) tmp);
683
684 /* This prevents a core dump when the pipe contains no newlines. */
685 if (n_lines == 0)
686 goto free_lbuffers;
687
688 /* Count the incomplete line on files that don't end with a newline. */
689 if (last->buffer[last->nbytes - 1] != '\n')
690 {
691 ++last->nlines;
692 ++total_lines;
693 }
694
695 /* Run through the list, printing lines. First, skip over unneeded
696 buffers. */
697 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
698 total_lines -= tmp->nlines;
699
700 /* Find the correct beginning, then print the rest of the file. */
701 if (total_lines > n_lines)
702 {
703 char *cp;
704
705 /* Skip `total_lines' - `n_lines' newlines. We made sure that
706 `total_lines' - `n_lines' <= `tmp->nlines'. */
707 cp = tmp->buffer;
708 for (i = total_lines - n_lines; i; --i)
709 while (*cp++ != '\n')
710 /* Do nothing. */ ;
711 i = cp - tmp->buffer;
712 }
713 else
714 i = 0;
715 XWRITE (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
716
717 for (tmp = tmp->next; tmp; tmp = tmp->next)
718 XWRITE (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
719
720free_lbuffers:
721 while (first)
722 {
723 tmp = first->next;
724 free ((char *) first);
725 first = tmp;
726 }
727 return errors;
728} 708}
729 709
730/* Print the last N_BYTES characters from the end of pipe FD. 710/* Print the last N_BYTES characters from the end of pipe FD.
731 This is a stripped down version of pipe_lines. 711 This is a stripped down version of pipe_lines.
732 Return 0 if successful, 1 if an error occurred. */ 712 Return 0 if successful, 1 if an error occurred. */
733 713
734static int 714static int pipe_bytes(const char *filename, int fd, off_t n_bytes)
735pipe_bytes (const char *filename, int fd, off_t n_bytes)
736{ 715{
737 struct charbuffer 716 struct charbuffer {
738 { 717 int nbytes;
739 int nbytes; 718 char buffer[BUFSIZ];
740 char buffer[BUFSIZ]; 719 struct charbuffer *next;
741 struct charbuffer *next; 720 };
742 }; 721 typedef struct charbuffer CBUFFER;
743 typedef struct charbuffer CBUFFER; 722 CBUFFER *first, *last, *tmp;
744 CBUFFER *first, *last, *tmp; 723 int i; /* Index into buffers. */
745 int i; /* Index into buffers. */ 724 int total_bytes = 0; /* Total characters in all buffers. */
746 int total_bytes = 0; /* Total characters in all buffers. */ 725 int errors = 0;
747 int errors = 0; 726
748 727 first = last = (CBUFFER *) xmalloc(sizeof(CBUFFER));
749 first = last = (CBUFFER *) xmalloc (sizeof (CBUFFER)); 728 first->nbytes = 0;
750 first->nbytes = 0; 729 first->next = NULL;
751 first->next = NULL; 730 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
752 tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER)); 731
753 732 /* Input is always read into a fresh buffer. */
754 /* Input is always read into a fresh buffer. */ 733 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
755 while ((tmp->nbytes = fullRead (fd, tmp->buffer, BUFSIZ)) > 0) 734 tmp->next = NULL;
756 { 735
757 tmp->next = NULL; 736 total_bytes += tmp->nbytes;
758 737 /* If there is enough room in the last buffer read, just append the new
759 total_bytes += tmp->nbytes; 738 one to it. This is because when reading from a pipe, `nbytes' can
760 /* If there is enough room in the last buffer read, just append the new 739 often be very small. */
761 one to it. This is because when reading from a pipe, `nbytes' can 740 if (tmp->nbytes + last->nbytes < BUFSIZ) {
762 often be very small. */ 741 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
763 if (tmp->nbytes + last->nbytes < BUFSIZ) 742 last->nbytes += tmp->nbytes;
764 { 743 } else {
765 memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes); 744 /* If there's not enough room, link the new buffer onto the end of
766 last->nbytes += tmp->nbytes; 745 the list, then either free up the oldest buffer for the next
746 read if that would leave enough characters, or else malloc a new
747 one. Some compaction mechanism is possible but probably not
748 worthwhile. */
749 last = last->next = tmp;
750 if (total_bytes - first->nbytes > n_bytes) {
751 tmp = first;
752 total_bytes -= first->nbytes;
753 first = first->next;
754 } else {
755 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
756 }
757 }
767 } 758 }
768 else 759 if (tmp->nbytes == -1) {
769 { 760 detailed_error(0, errno, "%s", filename);
770 /* If there's not enough room, link the new buffer onto the end of 761 errors = 1;
771 the list, then either free up the oldest buffer for the next 762 free((char *) tmp);
772 read if that would leave enough characters, or else malloc a new 763 goto free_cbuffers;
773 one. Some compaction mechanism is possible but probably not 764 }
774 worthwhile. */ 765
775 last = last->next = tmp; 766 free((char *) tmp);
776 if (total_bytes - first->nbytes > n_bytes) 767
777 { 768 /* Run through the list, printing characters. First, skip over unneeded
778 tmp = first; 769 buffers. */
779 total_bytes -= first->nbytes; 770 for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
780 first = first->next; 771 total_bytes -= tmp->nbytes;
781 } 772
782 else 773 /* Find the correct beginning, then print the rest of the file.
783 { 774 We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
784 tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER)); 775 if (total_bytes > n_bytes)
785 } 776 i = total_bytes - n_bytes;
777 else
778 i = 0;
779 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
780
781 for (tmp = tmp->next; tmp; tmp = tmp->next)
782 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
783
784 free_cbuffers:
785 while (first) {
786 tmp = first->next;
787 free((char *) first);
788 first = tmp;
786 } 789 }
787 } 790 return errors;
788 if (tmp->nbytes == -1)
789 {
790 detailed_error (0, errno, "%s", filename);
791 errors = 1;
792 free ((char *) tmp);
793 goto free_cbuffers;
794 }
795
796 free ((char *) tmp);
797
798 /* Run through the list, printing characters. First, skip over unneeded
799 buffers. */
800 for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
801 total_bytes -= tmp->nbytes;
802
803 /* Find the correct beginning, then print the rest of the file.
804 We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
805 if (total_bytes > n_bytes)
806 i = total_bytes - n_bytes;
807 else
808 i = 0;
809 XWRITE (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
810
811 for (tmp = tmp->next; tmp; tmp = tmp->next)
812 XWRITE (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
813
814free_cbuffers:
815 while (first)
816 {
817 tmp = first->next;
818 free ((char *) first);
819 first = tmp;
820 }
821 return errors;
822} 791}
823 792
824/* Skip N_BYTES characters from the start of pipe FD, and print 793/* Skip N_BYTES characters from the start of pipe FD, and print
825 any extra characters that were read beyond that. 794 any extra characters that were read beyond that.
826 Return 1 on error, 0 if ok. */ 795 Return 1 on error, 0 if ok. */
827 796
828static int 797static int start_bytes(const char *filename, int fd, off_t n_bytes)
829start_bytes (const char *filename, int fd, off_t n_bytes)
830{ 798{
831 char buffer[BUFSIZ]; 799 char buffer[BUFSIZ];
832 int bytes_read = 0; 800 int bytes_read = 0;
833 801
834 while (n_bytes > 0 && (bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0) 802 while (n_bytes > 0 && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0)
835 n_bytes -= bytes_read; 803 n_bytes -= bytes_read;
836 if (bytes_read == -1) 804 if (bytes_read == -1) {
837 { 805 detailed_error(0, errno, "%s", filename);
838 detailed_error (0, errno, "%s", filename); 806 return 1;
839 return 1; 807 } else if (n_bytes < 0)
840 } 808 XWRITE(STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
841 else if (n_bytes < 0) 809 return 0;
842 XWRITE (STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
843 return 0;
844} 810}
845 811
846/* Skip N_LINES lines at the start of file or pipe FD, and print 812/* Skip N_LINES lines at the start of file or pipe FD, and print
847 any extra characters that were read beyond that. 813 any extra characters that were read beyond that.
848 Return 1 on error, 0 if ok. */ 814 Return 1 on error, 0 if ok. */
849 815
850static int 816static int start_lines(const char *filename, int fd, long int n_lines)
851start_lines (const char *filename, int fd, long int n_lines)
852{ 817{
853 char buffer[BUFSIZ]; 818 char buffer[BUFSIZ];
854 int bytes_read = 0; 819 int bytes_read = 0;
855 int bytes_to_skip = 0; 820 int bytes_to_skip = 0;
856 821
857 while (n_lines && (bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0) 822 while (n_lines && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
858 { 823 bytes_to_skip = 0;
859 bytes_to_skip = 0; 824 while (bytes_to_skip < bytes_read)
860 while (bytes_to_skip < bytes_read) 825 if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
861 if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0) 826 break;
862 break; 827 }
863 } 828 if (bytes_read == -1) {
864 if (bytes_read == -1) 829 detailed_error(0, errno, "%s", filename);
865 { 830 return 1;
866 detailed_error (0, errno, "%s", filename); 831 } else if (bytes_to_skip < bytes_read) {
867 return 1; 832 XWRITE(STDOUT_FILENO, &buffer[bytes_to_skip],
868 } 833 bytes_read - bytes_to_skip);
869 else if (bytes_to_skip < bytes_read) 834 }
870 { 835 return 0;
871 XWRITE (STDOUT_FILENO, &buffer[bytes_to_skip],
872 bytes_read - bytes_to_skip);
873 }
874 return 0;
875} 836}
876 837
877/* Display file FILENAME from the current position in FD to the end. 838/* Display file FILENAME from the current position in FD to the end.
878 If `forever' is nonzero, keep reading from the end of the file 839 If `forever' is nonzero, keep reading from the end of the file
879 until killed. Return the number of bytes read from the file. */ 840 until killed. Return the number of bytes read from the file. */
880 841
881static long 842static long dump_remainder(const char *filename, int fd)
882dump_remainder (const char *filename, int fd)
883{ 843{
884 char buffer[BUFSIZ]; 844 char buffer[BUFSIZ];
885 int bytes_read; 845 int bytes_read;
886 long total; 846 long total;
887 847
888 total = 0; 848 total = 0;
889output: 849 output:
890 while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0) 850 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
891 { 851 XWRITE(STDOUT_FILENO, buffer, bytes_read);
892 XWRITE (STDOUT_FILENO, buffer, bytes_read); 852 total += bytes_read;
893 total += bytes_read; 853 }
894 } 854 if (bytes_read == -1)
895 if (bytes_read == -1) 855 detailed_error(EXIT_FAILURE, errno, "%s", filename);
896 detailed_error (EXIT_FAILURE, errno, "%s", filename); 856 if (forever) {
897 if (forever) 857 fflush(stdout);
898 { 858 sleep(1);
899 fflush (stdout); 859 goto output;
900 sleep (1); 860 } else {
901 goto output; 861 if (forever_multiple)
902 } 862 fflush(stdout);
903 else 863 }
904 { 864
905 if (forever_multiple) 865 return total;
906 fflush (stdout);
907 }
908
909 return total;
910} 866}
911 867
912/* Tail NFILES (>1) files forever until killed. The file names are in 868/* Tail NFILES (>1) files forever until killed. The file names are in
@@ -916,186 +872,161 @@ output:
916 none of them have changed size in one iteration, we sleep for a 872 none of them have changed size in one iteration, we sleep for a
917 second and try again. We do this until the user interrupts us. */ 873 second and try again. We do this until the user interrupts us. */
918 874
919static void 875static void tail_forever(char **names, int nfiles)
920tail_forever (char **names, int nfiles)
921{ 876{
922 int last; 877 int last;
923 878
924 last = -1; 879 last = -1;
925 880
926 while (1) 881 while (1) {
927 { 882 int i;
928 int i; 883 int changed;
929 int changed; 884
930 885 changed = 0;
931 changed = 0; 886 for (i = 0; i < nfiles; i++) {
932 for (i = 0; i < nfiles; i++) 887 struct stat stats;
933 { 888
934 struct stat stats; 889 if (file_descs[i] < 0)
935 890 continue;
936 if (file_descs[i] < 0) 891 if (fstat(file_descs[i], &stats) < 0) {
937 continue; 892 detailed_error(0, errno, "%s", names[i]);
938 if (fstat (file_descs[i], &stats) < 0) 893 file_descs[i] = -1;
939 { 894 continue;
940 detailed_error (0, errno, "%s", names[i]); 895 }
941 file_descs[i] = -1; 896 if (stats.st_size == file_sizes[i])
942 continue; 897 continue;
943 } 898
944 if (stats.st_size == file_sizes[i]) 899 /* This file has changed size. Print out what we can, and
945 continue; 900 then keep looping. */
946 901
947 /* This file has changed size. Print out what we can, and 902 changed = 1;
948 then keep looping. */ 903
949 904 if (stats.st_size < file_sizes[i]) {
950 changed = 1; 905 write_header(names[i], "file truncated");
951 906 last = i;
952 if (stats.st_size < file_sizes[i]) 907 lseek(file_descs[i], stats.st_size, SEEK_SET);
953 { 908 file_sizes[i] = stats.st_size;
954 write_header (names[i], "file truncated"); 909 continue;
955 last = i; 910 }
956 lseek (file_descs[i], stats.st_size, SEEK_SET); 911
957 file_sizes[i] = stats.st_size; 912 if (i != last) {
958 continue; 913 if (print_headers)
959 } 914 write_header(names[i], NULL);
960 915 last = i;
961 if (i != last) 916 }
962 { 917 file_sizes[i] += dump_remainder(names[i], file_descs[i]);
963 if (print_headers) 918 }
964 write_header (names[i], NULL);
965 last = i;
966 }
967 file_sizes[i] += dump_remainder (names[i], file_descs[i]);
968 }
969 919
970 /* If none of the files changed size, sleep. */ 920 /* If none of the files changed size, sleep. */
971 if (! changed) 921 if (!changed)
972 sleep (1); 922 sleep(1);
973 } 923 }
974} 924}
975 925
976/* Output the last N_BYTES bytes of file FILENAME open for reading in FD. 926/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
977 Return 0 if successful, 1 if an error occurred. */ 927 Return 0 if successful, 1 if an error occurred. */
978 928
979static int 929static int tail_bytes(const char *filename, int fd, off_t n_bytes)
980tail_bytes (const char *filename, int fd, off_t n_bytes)
981{ 930{
982 struct stat stats; 931 struct stat stats;
983 932
984 /* FIXME: resolve this like in dd.c. */ 933 /* FIXME: resolve this like in dd.c. */
985 /* Use fstat instead of checking for errno == ESPIPE because 934 /* Use fstat instead of checking for errno == ESPIPE because
986 lseek doesn't work on some special files but doesn't return an 935 lseek doesn't work on some special files but doesn't return an
987 error, either. */ 936 error, either. */
988 if (fstat (fd, &stats)) 937 if (fstat(fd, &stats)) {
989 { 938 detailed_error(0, errno, "%s", filename);
990 detailed_error (0, errno, "%s", filename); 939 return 1;
991 return 1; 940 }
992 } 941
993 942 if (from_start) {
994 if (from_start) 943 if (S_ISREG(stats.st_mode))
995 { 944 lseek(fd, n_bytes, SEEK_CUR);
996 if (S_ISREG (stats.st_mode)) 945 else if (start_bytes(filename, fd, n_bytes))
997 lseek (fd, n_bytes, SEEK_CUR); 946 return 1;
998 else if (start_bytes (filename, fd, n_bytes)) 947 dump_remainder(filename, fd);
999 return 1; 948 } else {
1000 dump_remainder (filename, fd); 949 if (S_ISREG(stats.st_mode)) {
1001 } 950 off_t current_pos, end_pos;
1002 else 951 size_t bytes_remaining;
1003 { 952
1004 if (S_ISREG (stats.st_mode)) 953 if ((current_pos = lseek(fd, (off_t) 0, SEEK_CUR)) != -1
1005 { 954 && (end_pos = lseek(fd, (off_t) 0, SEEK_END)) != -1) {
1006 off_t current_pos, end_pos; 955 off_t diff;
1007 size_t bytes_remaining; 956
1008 957 /* Be careful here. The current position may actually be
1009 if ((current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1 958 beyond the end of the file. */
1010 && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1) 959 bytes_remaining = (diff =
1011 { 960 end_pos - current_pos) < 0 ? 0 : diff;
1012 off_t diff; 961 } else {
1013 /* Be careful here. The current position may actually be 962 detailed_error(0, errno, "%s", filename);
1014 beyond the end of the file. */ 963 return 1;
1015 bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff; 964 }
1016 } 965
1017 else 966 if (bytes_remaining <= n_bytes) {
1018 { 967 /* From the current position to end of file, there are no
1019 detailed_error (0, errno, "%s", filename); 968 more bytes than have been requested. So reposition the
1020 return 1; 969 file pointer to the incoming current position and print
1021 } 970 everything after that. */
1022 971 lseek(fd, current_pos, SEEK_SET);
1023 if (bytes_remaining <= n_bytes) 972 } else {
1024 { 973 /* There are more bytes remaining than were requested.
1025 /* From the current position to end of file, there are no 974 Back up. */
1026 more bytes than have been requested. So reposition the 975 lseek(fd, -n_bytes, SEEK_END);
1027 file pointer to the incoming current position and print 976 }
1028 everything after that. */ 977 dump_remainder(filename, fd);
1029 lseek (fd, current_pos, SEEK_SET); 978 } else
1030 } 979 return pipe_bytes(filename, fd, n_bytes);
1031 else
1032 {
1033 /* There are more bytes remaining than were requested.
1034 Back up. */
1035 lseek (fd, -n_bytes, SEEK_END);
1036 }
1037 dump_remainder (filename, fd);
1038 } 980 }
1039 else 981 return 0;
1040 return pipe_bytes (filename, fd, n_bytes);
1041 }
1042 return 0;
1043} 982}
1044 983
1045/* Output the last N_LINES lines of file FILENAME open for reading in FD. 984/* Output the last N_LINES lines of file FILENAME open for reading in FD.
1046 Return 0 if successful, 1 if an error occurred. */ 985 Return 0 if successful, 1 if an error occurred. */
1047 986
1048static int 987static int tail_lines(const char *filename, int fd, long int n_lines)
1049tail_lines (const char *filename, int fd, long int n_lines)
1050{ 988{
1051 struct stat stats; 989 struct stat stats;
1052 off_t length; 990 off_t length;
1053 991
1054 if (fstat (fd, &stats)) 992 if (fstat(fd, &stats)) {
1055 { 993 detailed_error(0, errno, "%s", filename);
1056 detailed_error (0, errno, "%s", filename); 994 return 1;
1057 return 1; 995 }
1058 } 996
1059 997 if (from_start) {
1060 if (from_start) 998 if (start_lines(filename, fd, n_lines))
1061 { 999 return 1;
1062 if (start_lines (filename, fd, n_lines)) 1000 dump_remainder(filename, fd);
1063 return 1; 1001 } else {
1064 dump_remainder (filename, fd); 1002 /* Use file_lines only if FD refers to a regular file with
1065 } 1003 its file pointer positioned at beginning of file. */
1066 else 1004 /* FIXME: adding the lseek conjunct is a kludge.
1067 { 1005 Once there's a reasonable test suite, fix the true culprit:
1068 /* Use file_lines only if FD refers to a regular file with 1006 file_lines. file_lines shouldn't presume that the input
1069 its file pointer positioned at beginning of file. */ 1007 file pointer is initially positioned to beginning of file. */
1070 /* FIXME: adding the lseek conjunct is a kludge. 1008 if (S_ISREG(stats.st_mode)
1071 Once there's a reasonable test suite, fix the true culprit: 1009 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
1072 file_lines. file_lines shouldn't presume that the input 1010 length = lseek(fd, (off_t) 0, SEEK_END);
1073 file pointer is initially positioned to beginning of file. */ 1011 if (length != 0 && file_lines(filename, fd, n_lines, length))
1074 if (S_ISREG (stats.st_mode) 1012 return 1;
1075 && lseek (fd, (off_t) 0, SEEK_CUR) == (off_t) 0) 1013 dump_remainder(filename, fd);
1076 { 1014 } else
1077 length = lseek (fd, (off_t) 0, SEEK_END); 1015 return pipe_lines(filename, fd, n_lines);
1078 if (length != 0 && file_lines (filename, fd, n_lines, length))
1079 return 1;
1080 dump_remainder (filename, fd);
1081 } 1016 }
1082 else 1017 return 0;
1083 return pipe_lines (filename, fd, n_lines);
1084 }
1085 return 0;
1086} 1018}
1087 1019
1088/* Display the last N_UNITS units of file FILENAME, open for reading 1020/* Display the last N_UNITS units of file FILENAME, open for reading
1089 in FD. 1021 in FD.
1090 Return 0 if successful, 1 if an error occurred. */ 1022 Return 0 if successful, 1 if an error occurred. */
1091 1023
1092static int 1024static int tail(const char *filename, int fd, off_t n_units)
1093tail (const char *filename, int fd, off_t n_units)
1094{ 1025{
1095 if (count_lines) 1026 if (count_lines)
1096 return tail_lines (filename, fd, (long) n_units); 1027 return tail_lines(filename, fd, (long) n_units);
1097 else 1028 else
1098 return tail_bytes (filename, fd, n_units); 1029 return tail_bytes(filename, fd, n_units);
1099} 1030}
1100 1031
1101/* Display the last N_UNITS units of file FILENAME. 1032/* Display the last N_UNITS units of file FILENAME.
@@ -1103,207 +1034,183 @@ tail (const char *filename, int fd, off_t n_units)
1103 FILENUM is this file's index in the list of files the user gave. 1034 FILENUM is this file's index in the list of files the user gave.
1104 Return 0 if successful, 1 if an error occurred. */ 1035 Return 0 if successful, 1 if an error occurred. */
1105 1036
1106static int 1037static int tail_file(const char *filename, off_t n_units, int filenum)
1107tail_file (const char *filename, off_t n_units, int filenum)
1108{ 1038{
1109 int fd, errors; 1039 int fd, errors;
1110 struct stat stats; 1040 struct stat stats;
1111 1041
1112 if (!strcmp (filename, "-")) 1042 if (!strcmp(filename, "-")) {
1113 { 1043 have_read_stdin = 1;
1114 have_read_stdin = 1; 1044 filename = "standard input";
1115 filename = "standard input"; 1045 if (print_headers)
1116 if (print_headers) 1046 write_header(filename, NULL);
1117 write_header (filename, NULL); 1047 errors = tail(filename, 0, n_units);
1118 errors = tail (filename, 0, n_units); 1048 if (forever_multiple) {
1119 if (forever_multiple) 1049 if (fstat(0, &stats) < 0) {
1120 { 1050 detailed_error(0, errno, "standard input");
1121 if (fstat (0, &stats) < 0) 1051 errors = 1;
1122 { 1052 } else if (!S_ISREG(stats.st_mode)) {
1123 detailed_error (0, errno, "standard input"); 1053 detailed_error(0, 0,
1124 errors = 1; 1054 "standard input: cannot follow end of non-regular file");
1125 } 1055 errors = 1;
1126 else if (!S_ISREG (stats.st_mode)) 1056 }
1127 { 1057 if (errors)
1128 detailed_error (0, 0, 1058 file_descs[filenum] = -1;
1129 "standard input: cannot follow end of non-regular file"); 1059 else {
1130 errors = 1; 1060 file_descs[filenum] = 0;
1131 } 1061 file_sizes[filenum] = stats.st_size;
1132 if (errors) 1062 }
1133 file_descs[filenum] = -1;
1134 else
1135 {
1136 file_descs[filenum] = 0;
1137 file_sizes[filenum] = stats.st_size;
1138 }
1139 }
1140 }
1141 else
1142 {
1143 /* Not standard input. */
1144 fd = open (filename, O_RDONLY);
1145 if (fd == -1)
1146 {
1147 if (forever_multiple)
1148 file_descs[filenum] = -1;
1149 detailed_error (0, errno, "%s", filename);
1150 errors = 1;
1151 }
1152 else
1153 {
1154 if (print_headers)
1155 write_header (filename, NULL);
1156 errors = tail (filename, fd, n_units);
1157 if (forever_multiple)
1158 {
1159 if (fstat (fd, &stats) < 0)
1160 {
1161 detailed_error (0, errno, "%s", filename);
1162 errors = 1;
1163 }
1164 else if (!S_ISREG (stats.st_mode))
1165 {
1166 detailed_error (0, 0, "%s: cannot follow end of non-regular file",
1167 filename);
1168 errors = 1;
1169 }
1170 if (errors)
1171 {
1172 close (fd);
1173 file_descs[filenum] = -1;
1174 }
1175 else
1176 {
1177 file_descs[filenum] = fd;
1178 file_sizes[filenum] = stats.st_size;
1179 } 1063 }
1180 } 1064 } else {
1181 else 1065 /* Not standard input. */
1182 { 1066 fd = open(filename, O_RDONLY);
1183 if (close (fd)) 1067 if (fd == -1) {
1184 { 1068 if (forever_multiple)
1185 detailed_error (0, errno, "%s", filename); 1069 file_descs[filenum] = -1;
1186 errors = 1; 1070 detailed_error(0, errno, "%s", filename);
1071 errors = 1;
1072 } else {
1073 if (print_headers)
1074 write_header(filename, NULL);
1075 errors = tail(filename, fd, n_units);
1076 if (forever_multiple) {
1077 if (fstat(fd, &stats) < 0) {
1078 detailed_error(0, errno, "%s", filename);
1079 errors = 1;
1080 } else if (!S_ISREG(stats.st_mode)) {
1081 detailed_error(0, 0,
1082 "%s: cannot follow end of non-regular file",
1083 filename);
1084 errors = 1;
1085 }
1086 if (errors) {
1087 close(fd);
1088 file_descs[filenum] = -1;
1089 } else {
1090 file_descs[filenum] = fd;
1091 file_sizes[filenum] = stats.st_size;
1092 }
1093 } else {
1094 if (close(fd)) {
1095 detailed_error(0, errno, "%s", filename);
1096 errors = 1;
1097 }
1098 }
1187 } 1099 }
1188 }
1189 } 1100 }
1190 }
1191 1101
1192 return errors; 1102 return errors;
1193} 1103}
1194 1104
1195extern int 1105extern int tail_main(int argc, char **argv)
1196tail_main (int argc, char **argv)
1197{ 1106{
1198 int stopit = 0; 1107 int stopit = 0;
1199 enum header_mode header_mode = multiple_files; 1108 enum header_mode header_mode = multiple_files;
1200 int exit_status = 0; 1109 int exit_status = 0;
1201 /* If from_start, the number of items to skip before printing; otherwise, 1110
1202 the number of items at the end of the file to print. Initially, -1 1111 /* If from_start, the number of items to skip before printing; otherwise,
1203 means the value has not been set. */ 1112 the number of items at the end of the file to print. Initially, -1
1204 off_t n_units = -1; 1113 means the value has not been set. */
1205 int n_files; 1114 off_t n_units = -1;
1206 char **file; 1115 int n_files;
1207 1116 char **file;
1208 program_name = argv[0]; 1117
1209 have_read_stdin = 0; 1118 program_name = argv[0];
1210 count_lines = 1; 1119 have_read_stdin = 0;
1211 forever = forever_multiple = from_start = print_headers = 0; 1120 count_lines = 1;
1212 1121 forever = forever_multiple = from_start = print_headers = 0;
1213 /* Parse any options */ 1122
1214 //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv); 1123 /* Parse any options */
1215 while (--argc > 0 && ( **(++argv) == '-' || **argv == '+' )) { 1124 //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv);
1216 if (**argv == '+') { 1125 while (--argc > 0 && (**(++argv) == '-' || **argv == '+')) {
1217 from_start = 1; 1126 if (**argv == '+') {
1127 from_start = 1;
1128 }
1129 stopit = 0;
1130 while (stopit == 0 && *(++(*argv))) {
1131 switch (**argv) {
1132 case 'c':
1133 count_lines = 0;
1134
1135 if (--argc < 1) {
1136 usage(tail_usage);
1137 }
1138 n_units = getNum(*(++argv));
1139 stopit = 1;
1140 break;
1141
1142 case 'f':
1143 forever = 1;
1144 break;
1145
1146 case 'n':
1147 count_lines = 1;
1148
1149 if (--argc < 1) {
1150 usage(tail_usage);
1151 }
1152 n_units = atol(*(++argv));
1153 stopit = 1;
1154 break;
1155
1156 case 'q':
1157 header_mode = never;
1158 break;
1159
1160 case 'v':
1161 header_mode = always;
1162 break;
1163
1164 default:
1165 usage(tail_usage);
1166 }
1167 }
1168 }
1169
1170
1171 if (n_units == -1)
1172 n_units = DEFAULT_N_LINES;
1173
1174 /* To start printing with item N_UNITS from the start of the file, skip
1175 N_UNITS - 1 items. `tail +0' is actually meaningless, but for Unix
1176 compatibility it's treated the same as `tail +1'. */
1177 if (from_start) {
1178 if (n_units)
1179 --n_units;
1218 } 1180 }
1219 stopit = 0; 1181
1220 while (stopit == 0 && *(++(*argv))) { 1182 n_files = argc;
1221 switch (**argv) { 1183 file = argv;
1222 case 'c': 1184
1223 count_lines = 0; 1185 if (n_files > 1 && forever) {
1224 1186 forever_multiple = 1;
1225 if (--argc < 1) { 1187 forever = 0;
1226 usage(tail_usage); 1188 file_descs = (int *) xmalloc(n_files * sizeof(int));
1227 } 1189
1228 n_units = getNum(*(++argv)); 1190 file_sizes = (off_t *) xmalloc(n_files * sizeof(off_t));
1229 stopit = 1;
1230 break;
1231
1232 case 'f':
1233 forever = 1;
1234 break;
1235
1236 case 'n':
1237 count_lines = 1;
1238
1239 if (--argc < 1) {
1240 usage(tail_usage);
1241 }
1242 n_units = atol(*(++argv));
1243 stopit = 1;
1244 break;
1245
1246 case 'q':
1247 header_mode = never;
1248 break;
1249
1250 case 'v':
1251 header_mode = always;
1252 break;
1253
1254 default:
1255 usage (tail_usage);
1256 }
1257 } 1191 }
1258 } 1192
1259 1193 if (header_mode == always
1260 1194 || (header_mode == multiple_files && n_files > 1))
1261 if (n_units == -1) 1195 print_headers = 1;
1262 n_units = DEFAULT_N_LINES; 1196
1263 1197 if (n_files == 0) {
1264 /* To start printing with item N_UNITS from the start of the file, skip 1198 exit_status |= tail_file("-", n_units, 0);
1265 N_UNITS - 1 items. `tail +0' is actually meaningless, but for Unix 1199 } else {
1266 compatibility it's treated the same as `tail +1'. */ 1200 int i;
1267 if (from_start) 1201
1268 { 1202 for (i = 0; i < n_files; i++)
1269 if (n_units) 1203 exit_status |= tail_file(file[i], n_units, i);
1270 --n_units; 1204
1271 } 1205 if (forever_multiple)
1272 1206 tail_forever(file, n_files);
1273 n_files = argc; 1207 }
1274 file = argv; 1208
1275 1209 if (have_read_stdin && close(0) < 0)
1276 if (n_files > 1 && forever) 1210 detailed_error(EXIT_FAILURE, errno, "-");
1277 { 1211 if (fclose(stdout) == EOF)
1278 forever_multiple = 1; 1212 detailed_error(EXIT_FAILURE, errno, "write error");
1279 forever = 0; 1213 exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
1280 file_descs = (int *) xmalloc (n_files * sizeof (int));
1281 file_sizes = (off_t *) xmalloc (n_files * sizeof (off_t));
1282 }
1283
1284 if (header_mode == always
1285 || (header_mode == multiple_files && n_files > 1))
1286 print_headers = 1;
1287
1288 if (n_files == 0)
1289 {
1290 exit_status |= tail_file ("-", n_units, 0);
1291 }
1292 else
1293 {
1294 int i;
1295 for (i = 0; i < n_files; i++)
1296 exit_status |= tail_file (file[i], n_units, i);
1297
1298 if (forever_multiple)
1299 tail_forever (file, n_files);
1300 }
1301
1302 if (have_read_stdin && close (0) < 0)
1303 detailed_error (EXIT_FAILURE, errno, "-");
1304 if (fclose (stdout) == EOF)
1305 detailed_error (EXIT_FAILURE, errno, "write error");
1306 exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
1307} 1214}
1308 1215
1309 1216