damnit.
[oweals/busybox.git] / tail.c
1 /* tail -- output the last part of file(s)
2    Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18    Original version by Paul Rubin <phr@ocf.berkeley.edu>.
19    Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
20    tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.  
21
22    Rewrote the option parser, removed locales support,
23     and generally busyboxed, Erik Andersen <andersen@lineo.com>
24  
25  */
26
27 #include "internal.h"
28
29 #include <stdio.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <ctype.h>
37
38
39
40 /* Disable assertions.  Some systems have broken assert macros.  */
41 #define NDEBUG 1
42
43
44 static void error(int i, int errnum, char* fmt, ...)
45 {
46     va_list arguments;
47
48     va_start(arguments, fmt);
49     vfprintf(stderr, fmt, arguments);
50     fprintf(stderr, "\n%s\n", strerror( errnum));
51     va_end(arguments);
52     exit(i);
53 }
54
55
56 #define XWRITE(fd, buffer, n_bytes)                                     \
57   do                                                                    \
58     {                                                                   \
59       assert ((fd) == 1);                                               \
60       assert ((n_bytes) >= 0);                                          \
61       if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0)  \
62         error (EXIT_FAILURE, errno, "write error");                     \
63     }                                                                   \
64   while (0)
65
66 /* Number of items to tail.  */
67 #define DEFAULT_N_LINES 10
68
69 /* Size of atomic reads.  */
70 #ifndef BUFSIZ
71 #define BUFSIZ (512 * 8)
72 #endif
73
74 /* If nonzero, interpret the numeric argument as the number of lines.
75    Otherwise, interpret it as the number of bytes.  */
76 static int count_lines;
77
78 /* If nonzero, read from the end of one file until killed.  */
79 static int forever;
80
81 /* If nonzero, read from the end of multiple files until killed.  */
82 static int forever_multiple;
83
84 /* Array of file descriptors if forever_multiple is 1.  */
85 static int *file_descs;
86
87 /* Array of file sizes if forever_multiple is 1.  */
88 static off_t *file_sizes;
89
90 /* If nonzero, count from start of file instead of end.  */
91 static int from_start;
92
93 /* If nonzero, print filename headers.  */
94 static int print_headers;
95
96 /* When to print the filename banners.  */
97 enum header_mode
98 {
99   multiple_files, always, never
100 };
101
102 char *xmalloc ();
103
104 /* The name this program was run with.  */
105 char *program_name;
106
107 /* Nonzero if we have ever read standard input.  */
108 static int have_read_stdin;
109
110
111 static const char tail_usage[] = 
112 "tail [OPTION]... [FILE]...\n\
113 \n\
114 Print last 10 lines of each FILE to standard output.\n\
115 With more than one FILE, precede each with a header giving the file name.\n\
116 With no FILE, or when FILE is -, read standard input.\n\
117 \n\
118   -c=N[kbm]       output the last N bytes\n\
119   -f              output appended data as the file grows\n\
120   -n=N            output the last N lines, instead of last 10\n\
121   -q              never output headers giving file names\n\
122   -v              always output headers giving file names\n\
123   --help          display this help and exit\n\
124 \n\
125 If the first character of N (bytes or lines) is a `+', output begins with \n\
126 the Nth item from the start of each file, otherwise, print the last N items\n\
127 in the file.  N bytes may be suffixed by k (x1024), b (x512), or m (1024^2).\n\n";
128
129 static void
130 write_header (const char *filename, const char *comment)
131 {
132   static int first_file = 1;
133
134   printf ("%s==> %s%s%s <==\n", (first_file ? "" : "\n"), filename,
135           (comment ? ": " : ""),
136           (comment ? comment : ""));
137   first_file = 0;
138 }
139
140 /* Print the last N_LINES lines from the end of file FD.
141    Go backward through the file, reading `BUFSIZ' bytes at a time (except
142    probably the first), until we hit the start of the file or have
143    read NUMBER newlines.
144    POS starts out as the length of the file (the offset of the last
145    byte of the file + 1).
146    Return 0 if successful, 1 if an error occurred.  */
147
148 static int
149 file_lines (const char *filename, int fd, long int n_lines, off_t pos)
150 {
151   char buffer[BUFSIZ];
152   int bytes_read;
153   int i;                        /* Index into `buffer' for scanning.  */
154
155   if (n_lines == 0)
156     return 0;
157
158   /* Set `bytes_read' to the size of the last, probably partial, buffer;
159      0 < `bytes_read' <= `BUFSIZ'.  */
160   bytes_read = pos % BUFSIZ;
161   if (bytes_read == 0)
162     bytes_read = BUFSIZ;
163   /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
164      reads will be on block boundaries, which might increase efficiency.  */
165   pos -= bytes_read;
166   lseek (fd, pos, SEEK_SET);
167   bytes_read = fullRead (fd, buffer, bytes_read);
168   if (bytes_read == -1)
169     {
170       error (0, errno, "%s", filename);
171       return 1;
172     }
173
174   /* Count the incomplete line on files that don't end with a newline.  */
175   if (bytes_read && buffer[bytes_read - 1] != '\n')
176     --n_lines;
177
178   do
179     {
180       /* Scan backward, counting the newlines in this bufferfull.  */
181       for (i = bytes_read - 1; i >= 0; i--)
182         {
183           /* Have we counted the requested number of newlines yet?  */
184           if (buffer[i] == '\n' && n_lines-- == 0)
185             {
186               /* If this newline wasn't the last character in the buffer,
187                  print the text after it.  */
188               if (i != bytes_read - 1)
189                 XWRITE (STDOUT_FILENO, &buffer[i + 1], bytes_read - (i + 1));
190               return 0;
191             }
192         }
193       /* Not enough newlines in that bufferfull.  */
194       if (pos == 0)
195         {
196           /* Not enough lines in the file; print the entire file.  */
197           lseek (fd, (off_t) 0, SEEK_SET);
198           return 0;
199         }
200       pos -= BUFSIZ;
201       lseek (fd, pos, SEEK_SET);
202     }
203   while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0);
204   if (bytes_read == -1)
205     {
206       error (0, errno, "%s", filename);
207       return 1;
208     }
209   return 0;
210 }
211
212 /* Print the last N_LINES lines from the end of the standard input,
213    open for reading as pipe FD.
214    Buffer the text as a linked list of LBUFFERs, adding them as needed.
215    Return 0 if successful, 1 if an error occured.  */
216
217 static int
218 pipe_lines (const char *filename, int fd, long int n_lines)
219 {
220   struct linebuffer
221   {
222     int nbytes, nlines;
223     char buffer[BUFSIZ];
224     struct linebuffer *next;
225   };
226   typedef struct linebuffer LBUFFER;
227   LBUFFER *first, *last, *tmp;
228   int i;                        /* Index into buffers.  */
229   int total_lines = 0;          /* Total number of newlines in all buffers.  */
230   int errors = 0;
231
232   first = last = (LBUFFER *) xmalloc (sizeof (LBUFFER));
233   first->nbytes = first->nlines = 0;
234   first->next = NULL;
235   tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));
236
237   /* Input is always read into a fresh buffer.  */
238   while ((tmp->nbytes = fullRead (fd, tmp->buffer, BUFSIZ)) > 0)
239     {
240       tmp->nlines = 0;
241       tmp->next = NULL;
242
243       /* Count the number of newlines just read.  */
244       for (i = 0; i < tmp->nbytes; i++)
245         if (tmp->buffer[i] == '\n')
246           ++tmp->nlines;
247       total_lines += tmp->nlines;
248
249       /* If there is enough room in the last buffer read, just append the new
250          one to it.  This is because when reading from a pipe, `nbytes' can
251          often be very small.  */
252       if (tmp->nbytes + last->nbytes < BUFSIZ)
253         {
254           memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
255           last->nbytes += tmp->nbytes;
256           last->nlines += tmp->nlines;
257         }
258       else
259         {
260           /* If there's not enough room, link the new buffer onto the end of
261              the list, then either free up the oldest buffer for the next
262              read if that would leave enough lines, or else malloc a new one.
263              Some compaction mechanism is possible but probably not
264              worthwhile.  */
265           last = last->next = tmp;
266           if (total_lines - first->nlines > n_lines)
267             {
268               tmp = first;
269               total_lines -= first->nlines;
270               first = first->next;
271             }
272           else
273             tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));
274         }
275     }
276   if (tmp->nbytes == -1)
277     {
278       error (0, errno, "%s", filename);
279       errors = 1;
280       free ((char *) tmp);
281       goto free_lbuffers;
282     }
283
284   free ((char *) tmp);
285
286   /* This prevents a core dump when the pipe contains no newlines.  */
287   if (n_lines == 0)
288     goto free_lbuffers;
289
290   /* Count the incomplete line on files that don't end with a newline.  */
291   if (last->buffer[last->nbytes - 1] != '\n')
292     {
293       ++last->nlines;
294       ++total_lines;
295     }
296
297   /* Run through the list, printing lines.  First, skip over unneeded
298      buffers.  */
299   for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
300     total_lines -= tmp->nlines;
301
302   /* Find the correct beginning, then print the rest of the file.  */
303   if (total_lines > n_lines)
304     {
305       char *cp;
306
307       /* Skip `total_lines' - `n_lines' newlines.  We made sure that
308          `total_lines' - `n_lines' <= `tmp->nlines'.  */
309       cp = tmp->buffer;
310       for (i = total_lines - n_lines; i; --i)
311         while (*cp++ != '\n')
312           /* Do nothing.  */ ;
313       i = cp - tmp->buffer;
314     }
315   else
316     i = 0;
317   XWRITE (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
318
319   for (tmp = tmp->next; tmp; tmp = tmp->next)
320     XWRITE (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
321
322 free_lbuffers:
323   while (first)
324     {
325       tmp = first->next;
326       free ((char *) first);
327       first = tmp;
328     }
329   return errors;
330 }
331
332 /* Print the last N_BYTES characters from the end of pipe FD.
333    This is a stripped down version of pipe_lines.
334    Return 0 if successful, 1 if an error occurred.  */
335
336 static int
337 pipe_bytes (const char *filename, int fd, off_t n_bytes)
338 {
339   struct charbuffer
340   {
341     int nbytes;
342     char buffer[BUFSIZ];
343     struct charbuffer *next;
344   };
345   typedef struct charbuffer CBUFFER;
346   CBUFFER *first, *last, *tmp;
347   int i;                        /* Index into buffers.  */
348   int total_bytes = 0;          /* Total characters in all buffers.  */
349   int errors = 0;
350
351   first = last = (CBUFFER *) xmalloc (sizeof (CBUFFER));
352   first->nbytes = 0;
353   first->next = NULL;
354   tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));
355
356   /* Input is always read into a fresh buffer.  */
357   while ((tmp->nbytes = fullRead (fd, tmp->buffer, BUFSIZ)) > 0)
358     {
359       tmp->next = NULL;
360
361       total_bytes += tmp->nbytes;
362       /* If there is enough room in the last buffer read, just append the new
363          one to it.  This is because when reading from a pipe, `nbytes' can
364          often be very small.  */
365       if (tmp->nbytes + last->nbytes < BUFSIZ)
366         {
367           memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
368           last->nbytes += tmp->nbytes;
369         }
370       else
371         {
372           /* If there's not enough room, link the new buffer onto the end of
373              the list, then either free up the oldest buffer for the next
374              read if that would leave enough characters, or else malloc a new
375              one.  Some compaction mechanism is possible but probably not
376              worthwhile.  */
377           last = last->next = tmp;
378           if (total_bytes - first->nbytes > n_bytes)
379             {
380               tmp = first;
381               total_bytes -= first->nbytes;
382               first = first->next;
383             }
384           else
385             {
386               tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));
387             }
388         }
389     }
390   if (tmp->nbytes == -1)
391     {
392       error (0, errno, "%s", filename);
393       errors = 1;
394       free ((char *) tmp);
395       goto free_cbuffers;
396     }
397
398   free ((char *) tmp);
399
400   /* Run through the list, printing characters.  First, skip over unneeded
401      buffers.  */
402   for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
403     total_bytes -= tmp->nbytes;
404
405   /* Find the correct beginning, then print the rest of the file.
406      We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'.  */
407   if (total_bytes > n_bytes)
408     i = total_bytes - n_bytes;
409   else
410     i = 0;
411   XWRITE (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
412
413   for (tmp = tmp->next; tmp; tmp = tmp->next)
414     XWRITE (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
415
416 free_cbuffers:
417   while (first)
418     {
419       tmp = first->next;
420       free ((char *) first);
421       first = tmp;
422     }
423   return errors;
424 }
425
426 /* Skip N_BYTES characters from the start of pipe FD, and print
427    any extra characters that were read beyond that.
428    Return 1 on error, 0 if ok.  */
429
430 static int
431 start_bytes (const char *filename, int fd, off_t n_bytes)
432 {
433   char buffer[BUFSIZ];
434   int bytes_read = 0;
435
436   while (n_bytes > 0 && (bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0)
437     n_bytes -= bytes_read;
438   if (bytes_read == -1)
439     {
440       error (0, errno, "%s", filename);
441       return 1;
442     }
443   else if (n_bytes < 0)
444     XWRITE (STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
445   return 0;
446 }
447
448 /* Skip N_LINES lines at the start of file or pipe FD, and print
449    any extra characters that were read beyond that.
450    Return 1 on error, 0 if ok.  */
451
452 static int
453 start_lines (const char *filename, int fd, long int n_lines)
454 {
455   char buffer[BUFSIZ];
456   int bytes_read = 0;
457   int bytes_to_skip = 0;
458
459   while (n_lines && (bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0)
460     {
461       bytes_to_skip = 0;
462       while (bytes_to_skip < bytes_read)
463         if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
464           break;
465     }
466   if (bytes_read == -1)
467     {
468       error (0, errno, "%s", filename);
469       return 1;
470     }
471   else if (bytes_to_skip < bytes_read)
472     {
473       XWRITE (STDOUT_FILENO, &buffer[bytes_to_skip],
474               bytes_read - bytes_to_skip);
475     }
476   return 0;
477 }
478
479 /* Display file FILENAME from the current position in FD to the end.
480    If `forever' is nonzero, keep reading from the end of the file
481    until killed.  Return the number of bytes read from the file.  */
482
483 static long
484 dump_remainder (const char *filename, int fd)
485 {
486   char buffer[BUFSIZ];
487   int bytes_read;
488   long total;
489
490   total = 0;
491 output:
492   while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0)
493     {
494       XWRITE (STDOUT_FILENO, buffer, bytes_read);
495       total += bytes_read;
496     }
497   if (bytes_read == -1)
498     error (EXIT_FAILURE, errno, "%s", filename);
499   if (forever)
500     {
501       fflush (stdout);
502       sleep (1);
503       goto output;
504     }
505   else
506     {
507       if (forever_multiple)
508         fflush (stdout);
509     }
510
511   return total;
512 }
513
514 /* Tail NFILES (>1) files forever until killed.  The file names are in
515    NAMES.  The open file descriptors are in `file_descs', and the size
516    at which we stopped tailing them is in `file_sizes'.  We loop over
517    each of them, doing an fstat to see if they have changed size.  If
518    none of them have changed size in one iteration, we sleep for a
519    second and try again.  We do this until the user interrupts us.  */
520
521 static void
522 tail_forever (char **names, int nfiles)
523 {
524   int last;
525
526   last = -1;
527
528   while (1)
529     {
530       int i;
531       int changed;
532
533       changed = 0;
534       for (i = 0; i < nfiles; i++)
535         {
536           struct stat stats;
537
538           if (file_descs[i] < 0)
539             continue;
540           if (fstat (file_descs[i], &stats) < 0)
541             {
542               error (0, errno, "%s", names[i]);
543               file_descs[i] = -1;
544               continue;
545             }
546           if (stats.st_size == file_sizes[i])
547             continue;
548
549           /* This file has changed size.  Print out what we can, and
550              then keep looping.  */
551
552           changed = 1;
553
554           if (stats.st_size < file_sizes[i])
555             {
556               write_header (names[i], "file truncated");
557               last = i;
558               lseek (file_descs[i], stats.st_size, SEEK_SET);
559               file_sizes[i] = stats.st_size;
560               continue;
561             }
562
563           if (i != last)
564             {
565               if (print_headers)
566                 write_header (names[i], NULL);
567               last = i;
568             }
569           file_sizes[i] += dump_remainder (names[i], file_descs[i]);
570         }
571
572       /* If none of the files changed size, sleep.  */
573       if (! changed)
574         sleep (1);
575     }
576 }
577
578 /* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
579    Return 0 if successful, 1 if an error occurred.  */
580
581 static int
582 tail_bytes (const char *filename, int fd, off_t n_bytes)
583 {
584   struct stat stats;
585
586   /* FIXME: resolve this like in dd.c.  */
587   /* Use fstat instead of checking for errno == ESPIPE because
588      lseek doesn't work on some special files but doesn't return an
589      error, either.  */
590   if (fstat (fd, &stats))
591     {
592       error (0, errno, "%s", filename);
593       return 1;
594     }
595
596   if (from_start)
597     {
598       if (S_ISREG (stats.st_mode))
599         lseek (fd, n_bytes, SEEK_CUR);
600       else if (start_bytes (filename, fd, n_bytes))
601         return 1;
602       dump_remainder (filename, fd);
603     }
604   else
605     {
606       if (S_ISREG (stats.st_mode))
607         {
608           off_t current_pos, end_pos;
609           size_t bytes_remaining;
610
611           if ((current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
612               && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
613             {
614               off_t diff;
615               /* Be careful here.  The current position may actually be
616                  beyond the end of the file.  */
617               bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff;
618             }
619           else
620             {
621               error (0, errno, "%s", filename);
622               return 1;
623             }
624
625           if (bytes_remaining <= n_bytes)
626             {
627               /* From the current position to end of file, there are no
628                  more bytes than have been requested.  So reposition the
629                  file pointer to the incoming current position and print
630                  everything after that.  */
631               lseek (fd, current_pos, SEEK_SET);
632             }
633           else
634             {
635               /* There are more bytes remaining than were requested.
636                  Back up.  */
637               lseek (fd, -n_bytes, SEEK_END);
638             }
639           dump_remainder (filename, fd);
640         }
641       else
642         return pipe_bytes (filename, fd, n_bytes);
643     }
644   return 0;
645 }
646
647 /* Output the last N_LINES lines of file FILENAME open for reading in FD.
648    Return 0 if successful, 1 if an error occurred.  */
649
650 static int
651 tail_lines (const char *filename, int fd, long int n_lines)
652 {
653   struct stat stats;
654   off_t length;
655
656   if (fstat (fd, &stats))
657     {
658       error (0, errno, "%s", filename);
659       return 1;
660     }
661
662   if (from_start)
663     {
664       if (start_lines (filename, fd, n_lines))
665         return 1;
666       dump_remainder (filename, fd);
667     }
668   else
669     {
670       /* Use file_lines only if FD refers to a regular file with
671          its file pointer positioned at beginning of file.  */
672       /* FIXME: adding the lseek conjunct is a kludge.
673          Once there's a reasonable test suite, fix the true culprit:
674          file_lines.  file_lines shouldn't presume that the input
675          file pointer is initially positioned to beginning of file.  */
676       if (S_ISREG (stats.st_mode)
677           && lseek (fd, (off_t) 0, SEEK_CUR) == (off_t) 0)
678         {
679           length = lseek (fd, (off_t) 0, SEEK_END);
680           if (length != 0 && file_lines (filename, fd, n_lines, length))
681             return 1;
682           dump_remainder (filename, fd);
683         }
684       else
685         return pipe_lines (filename, fd, n_lines);
686     }
687   return 0;
688 }
689
690 /* Display the last N_UNITS units of file FILENAME, open for reading
691    in FD.
692    Return 0 if successful, 1 if an error occurred.  */
693
694 static int
695 tail (const char *filename, int fd, off_t n_units)
696 {
697   if (count_lines)
698     return tail_lines (filename, fd, (long) n_units);
699   else
700     return tail_bytes (filename, fd, n_units);
701 }
702
703 /* Display the last N_UNITS units of file FILENAME.
704    "-" for FILENAME means the standard input.
705    FILENUM is this file's index in the list of files the user gave.
706    Return 0 if successful, 1 if an error occurred.  */
707
708 static int
709 tail_file (const char *filename, off_t n_units, int filenum)
710 {
711   int fd, errors;
712   struct stat stats;
713
714   if (!strcmp (filename, "-"))
715     {
716       have_read_stdin = 1;
717       filename = "standard input";
718       if (print_headers)
719         write_header (filename, NULL);
720       errors = tail (filename, 0, n_units);
721       if (forever_multiple)
722         {
723           if (fstat (0, &stats) < 0)
724             {
725               error (0, errno, "standard input");
726               errors = 1;
727             }
728           else if (!S_ISREG (stats.st_mode))
729             {
730               error (0, 0,
731                      "standard input: cannot follow end of non-regular file");
732               errors = 1;
733             }
734           if (errors)
735             file_descs[filenum] = -1;
736           else
737             {
738               file_descs[filenum] = 0;
739               file_sizes[filenum] = stats.st_size;
740             }
741         }
742     }
743   else
744     {
745       /* Not standard input.  */
746       fd = open (filename, O_RDONLY);
747       if (fd == -1)
748         {
749           if (forever_multiple)
750             file_descs[filenum] = -1;
751           error (0, errno, "%s", filename);
752           errors = 1;
753         }
754       else
755         {
756           if (print_headers)
757             write_header (filename, NULL);
758           errors = tail (filename, fd, n_units);
759           if (forever_multiple)
760             {
761               if (fstat (fd, &stats) < 0)
762                 {
763                   error (0, errno, "%s", filename);
764                   errors = 1;
765                 }
766               else if (!S_ISREG (stats.st_mode))
767                 {
768                   error (0, 0, "%s: cannot follow end of non-regular file",
769                          filename);
770                   errors = 1;
771                 }
772               if (errors)
773                 {
774                   close (fd);
775                   file_descs[filenum] = -1;
776                 }
777               else
778                 {
779                   file_descs[filenum] = fd;
780                   file_sizes[filenum] = stats.st_size;
781                 }
782             }
783           else
784             {
785               if (close (fd))
786                 {
787                   error (0, errno, "%s", filename);
788                   errors = 1;
789                 }
790             }
791         }
792     }
793
794   return errors;
795 }
796
797 extern int
798 tail_main (int argc, char **argv)
799 {
800   int stopit = 0;
801   enum header_mode header_mode = multiple_files;
802   int exit_status = 0;
803   /* If from_start, the number of items to skip before printing; otherwise,
804      the number of items at the end of the file to print.  Initially, -1
805      means the value has not been set.  */
806   off_t n_units = -1;
807   int n_files;
808   char **file;
809
810   program_name = argv[0];
811   have_read_stdin = 0;
812   count_lines = 1;
813   forever = forever_multiple = from_start = print_headers = 0;
814       
815   /* Parse any options */
816   //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv);
817     while (--argc > 0 && ( **(++argv) == '-' || **argv == '+' )) {
818         if (**argv == '+') {
819             from_start = 1;
820         }
821         stopit = 0;
822         while (stopit == 0 && *(++(*argv))) {
823             switch (**argv) {
824                 case 'c':
825                   count_lines = 0;
826
827                   if (--argc < 1) {
828                       usage(tail_usage);
829                   }
830                   n_units = getNum(*(++argv));
831                   stopit = 1;
832                   break;
833
834                 case 'f':
835                   forever = 1;
836                   break;
837
838                 case 'n':
839                   count_lines = 1;
840
841                   if (--argc < 1) {
842                       usage(tail_usage);
843                   }
844                   n_units = atol(*(++argv));
845                   stopit = 1;
846                   break;
847
848                 case 'q':
849                   header_mode = never;
850                   break;
851
852                 case 'v':
853                   header_mode = always;
854                   break;
855
856                 default:
857                   usage (tail_usage);
858             }
859         }
860     }
861
862
863   if (n_units == -1)
864     n_units = DEFAULT_N_LINES;
865
866   /* To start printing with item N_UNITS from the start of the file, skip
867      N_UNITS - 1 items.  `tail +0' is actually meaningless, but for Unix
868      compatibility it's treated the same as `tail +1'.  */
869   if (from_start)
870     {
871       if (n_units)
872         --n_units;
873     }
874
875   n_files = argc;
876   file = argv;
877
878   if (n_files > 1 && forever)
879     {
880       forever_multiple = 1;
881       forever = 0;
882       file_descs = (int *) xmalloc (n_files * sizeof (int));
883       file_sizes = (off_t *) xmalloc (n_files * sizeof (off_t));
884     }
885
886   if (header_mode == always
887       || (header_mode == multiple_files && n_files > 1))
888     print_headers = 1;
889
890   if (n_files == 0)
891     {
892       exit_status |= tail_file ("-", n_units, 0);
893     }
894   else
895     {
896       int i;
897       for (i = 0; i < n_files; i++)
898         exit_status |= tail_file (file[i], n_units, i);
899
900       if (forever_multiple)
901         tail_forever (file, n_files);
902     }
903
904   if (have_read_stdin && close (0) < 0)
905     error (EXIT_FAILURE, errno, "-");
906   if (fclose (stdout) == EOF)
907     error (EXIT_FAILURE, errno, "write error");
908   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
909 }