/*
 * tail.c - shows the last n lines of a text file (or pipe)
 *
 * Bruno Costa - 11 Nov 90 - 12 Nov 90
 *
 * (based on some ideas from Lucia Darsa)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef LATTICE
#define SEEK_END 1L	/* this line should be in stdio.h !!! */
#endif


#define TRUE  1
#define FALSE 0

#define MAXLINE 256
#define AVGLINE 90	/* slightly bigger than most lines */


#define strdup(string) strcpy (malloc (strlen (string) + 1), string)


typedef struct {
  int first;
  int last;
  int size;
  int maxsize;
  char **buf;
} queue;


#define qfull(queue) ((queue)->size == (queue)->maxsize)


queue *qalloc (int n)
{
 queue *q;

 q = malloc (sizeof (queue));
 if (q)
 {
   q->first = 0;
   q->last = 0;
   q->size = 0;
   q->maxsize = n;
   q->buf = calloc (n, sizeof (*q->buf));

   if (q->buf == NULL)
   {
     free (q);
     q = NULL;
   }
   else
   {
     int i;
     for (i = 0; i < n; i++)
       q->buf[i] = NULL;
   }
 }
 return (q);
}


void qfree (queue *q)
{
 int i;

 if (q)
 {
   if (q->buf)
   {
     for (i = 0; i < q->maxsize; i++)
       if (q->buf[i])
         free (q->buf[i]);
     free (q->buf);
   }
   free (q);
 }
}


char *dequeue (queue *q)
{
 static char *prev = NULL;
 char *line = q->buf[q->first];

 if (prev)
   free (prev);

 prev = line;

 if (line)
 {
   q->buf[q->first] = NULL;
   q->first = (q->first + 1) % q->maxsize;
   --q->size;
 }

 return (line);
}


void enqueue (queue *q, char *line)
{
 char *copy = strdup (line);

 if (!copy)
 {
   fprintf (stderr, "tail: out of memory!\n");
   exit (20);
 }

 if (qfull (q))
   (void) dequeue (q);

 q->buf[q->last] = copy;
 q->last = (q->last + 1) % q->maxsize;
 ++q->size;
}


int tail (FILE *f, int nlines)
{
 int err = FALSE;
 queue *fifo;
 char *line, lbuf[MAXLINE];

 fifo = qalloc (nlines);
 if (!fifo)
 {
   fprintf (stderr, "tail: out of memory\n");
   return (FALSE);
 }

 fseek (f, - nlines * AVGLINE, SEEK_END);	/* tries to seek to  ..	*/
						/* .. nlines before EOF	*/

 while (fgets (lbuf, MAXLINE, f))
   enqueue (fifo, lbuf);

 err = !feof(f);

 while (!err  &&  (line = dequeue (fifo)) != NULL)
   err = (fputs (line, stdout) == EOF);

 qfree (fifo);
 return (!err);
}


void usage (void)
{
 fprintf (stderr, "usage: tail [-?] [-<nlines>] [files ...]\n");
 exit (10);
}


int main (int argc, char *argv[])
{
 int i;
 int n = 10, filegiven = FALSE;

 for (i = 1; i < argc; i++)
   if (argv[i][0] == '-')
   {
     if (argv[i][1] == '?')
       usage();

     n = atoi (&argv[i][1]);
     if (n == 0)
       n = 10;
   }
   else
   {
     FILE *f;

     filegiven = TRUE;
     f = fopen (argv[i], "r");
     if (!f)
       fprintf (stderr, "tail: could not open '%s'\n", argv[i]);
     else
     {
       if (!tail (f, n))
         fprintf (stderr, "tail: error copying '%s' to stdout\n", argv[i]);
       fclose (f);
     }
   }

 if (!filegiven)
   if (!tail (stdin, n))
     fprintf (stderr, "tail: error copying stdin to stdout\n");

 exit (0);
}
