/*-------------------------------------------------*
 | tail.c - Unix-like utility.                     |
 | Syntax: tail [-N | +N] [file [file [ ... ] ]    |
 | Prints on stdout the last N (default: N_DEFVAL) |
 | lines read from the given files, or from stdin. |
 | If +N is given, the first N lines of input will |
 | be skipped, and the remainder printed.          |
 | v1.00 MLO 911224                                |
 *-------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "mlo.h"

#define N_DEFVAL      10
#define LINE_LENGTH   128

Boolean skip = False;
char *pBuffer, *pOver;

void DoTheStuff(FILE *fp, int n);
void Syntax(void);

void main(
  int argc,
  char **argv
){
  int nLines = N_DEFVAL;
  int bufferLength;

  while (--argc) {
    if ( ((*++argv)[0] == '-') ) {
      if (isdigit((*argv)[1])) {
        if ((nLines = atoi(*argv+1)) < 1) Syntax();
      } else {
        Syntax();
      }
    } else if ((*argv)[0] == '+') {
      skip = True;
      if (isdigit((*argv)[1])) {
        if ((nLines = atoi(*argv+1)) < 1) Syntax();
      } else if ((*argv)[1] == '\0') {
        continue;
      } else {
        Syntax();
      }
    } else if ((*argv)[0] == '?') {
      Syntax();
    } else {
      break;
    }
  }

  if (skip) {
    if ((pBuffer = malloc(LINE_LENGTH)) == NULL) {
      puts("tail: no memory.");
      exit(SYS_ABORT_CODE);
    }
  } else {
    if ((pBuffer = malloc(bufferLength = (nLines+1)*LINE_LENGTH)) == NULL) {
      printf("tail: no memory for %d lines %d characters each.\n",
             nLines, LINE_LENGTH);
      exit(SYS_ABORT_CODE);
    }
    pOver = pBuffer + bufferLength;
  }

  if (argc) {
    while (argc--) {
      FILE *fp;

      if ((fp = fopen(*argv, "r")) == NULL) {
        printf("tail: couldn't open file \"%s\".\n", *argv);
      } else {
        DoTheStuff(fp, nLines);
        fclose(fp);
      }
      ++argv;
    }
  } else {
    DoTheStuff(stdin, nLines);
  }

  free(pBuffer);
  exit(SYS_NORMAL_CODE);
}

void DoTheStuff(
  FILE *fp,
  int n
){
  if (skip) {
    while (n--) {
      if (fgets(pBuffer, LINE_LENGTH, fp) == NULL) {
        puts("END-OF-FILE");
        return;
      }
    }
    while (fgets(pBuffer, LINE_LENGTH, fp)) fputs(pBuffer, stdout);
  } else {
    char *pDest = pBuffer;
    Boolean bufferFull = False;
    char *pc;

    while (fgets(pDest, LINE_LENGTH, fp)) {
      if ((pDest += LINE_LENGTH) == pOver) {
        pDest = pBuffer;
        bufferFull = True;
      }
    }

    if (bufferFull) {
      pc = pDest;
      do {
        if ((pc += LINE_LENGTH) == pOver) pc = pBuffer;
        fputs(pc, stdout);
      } while (pc != pDest);
    } else {
      puts("TOP-OF-FILE");
      for (pc=pBuffer; pc<pDest; pc+=LINE_LENGTH) fputs(pc, stdout);
    }
  }
}

void Syntax(void)
{
  puts("\nUsage:   tail [-N | +N] [file [file [ ... ]]]");
  printf("Purpose: prints on stdout the last N (default: %d) lines read\n",
         N_DEFVAL);
  puts("         from the given file(s), or from stdin. With +N, skips");
  puts("         the first N input lines and prints the remainder.\n");

  exit(SYS_NORMAL_CODE);
}
