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