/*

          Tail - Display the last lines of a file.

          Original effort by Fabio Rossetti. Inspired by the tail program
          by Gary Brant found on <>< 179.

	  (c) 1989 by Fabio Rossetti

	  To compile under Lattice C v5.0x use:

		lc  -O -v -cus tail
		blink lib:cres.o tail.o to tail lib lib:a.lib lib:lc.lib sd nd

*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/arpbase.h>
#include <arpfunctions.h>
#include <proto/exec.h>
#include <proto/dos.h>		

struct ArpBase *ArpBase=NULL;
struct Process *Pr;
LONG argc;

#define NARGS 4
#define BFSIZE 256	
#define FROM	argv[0]
#define TO	argv[1]
#define LIN	argv[2]
#define BAN	argv[3]
STRPTR argv[NARGS];
#define MAXLIN 256
TEXT *Lin[256];

	
TEXT *Buf,*OBuf;
TEXT Banner[256];

struct UserAnchor {
	struct	AnchorPath	ua_AP;
	BYTE	moremem[255];	/* extension */
};
struct	UserAnchor *Anchor=NULL;

/* Trick to keep code down to size */
VOID MemCleanup()
{
}

/* shutdown routine */
VOID Cleanup(r1,r2,msg)
LONG r1,r2;
STRPTR msg;
{
	if (msg) Puts(msg);
	if (ArpBase) CloseLibrary((struct Library *)ArpBase);
	Pr->pr_Result2 = r2;
	exit(r1);
}

VOID Tail(Inp,Out,lines)
BPTR Inp,Out;
ULONG lines;

{

	REGISTER TEXT *il = Lin[0];
	REGISTER LONG count, obp=0,p=0,i,fl=0;

	/* enqueue the last lines read */
	for(;;) {
	/* read until eof */
	if (!obp) if (!(count = Read(Inp,Buf,BFSIZE))) {		
			/* the last line */	
			if(fl) {
			*il = '\0';
			p++;}
			break;
			}
	fl = 1;
	if (*(Buf + obp) == '\n') {
		fl = 0; 
		++obp;
		*il = '\0';
		p++;
		il = Lin[p%lines];

		if (SetSignal(0,0) & SIGBREAKF_CTRL_C) 
			Cleanup(RETURN_WARN,NULL,"***Break");
  		 }
	else 
	{
	if (((TEXT *)il - (TEXT *)Lin[p%lines]) > MAXLIN)
			Cleanup(RETURN_FAIL,NULL,"Buffer overflow");
	*il++ = *(Buf + obp++);
	}

	if (obp == count) obp = 0;

}

	/* display queue */
	if (p <= lines) for(i=0; i < p; i++) FPrintf(Out,"%s\n",Lin[i]);
	else {
	for(i=(p%lines); i < lines; i++) {
		if (SetSignal(0,0) & SIGBREAKF_CTRL_C)
			Cleanup(RETURN_WARN,NULL,"***Break");
		FPrintf(Out,"%s\n",Lin[i]);
		}

	if ((lines != 1) && ((p%lines) != 0))
		 for(i=0; i <= (p%lines)-1 ; i++) {
			FPrintf(Out,"%s\n",Lin[i]);
			if (SetSignal(0,0) & SIGBREAKF_CTRL_C) 
				Cleanup(RETURN_WARN,NULL,"***Break");
				}
	}
}

VOID _main(Line)
STRPTR Line;
{



	BPTR infh,outfh;
	LONG Lines,Result,i=0,j;
	char **ArV;
	Pr = (struct Process*)FindTask(NULL);
	
	
	if(!(ArpBase = (struct ArpBase*)OpenLibrary(ArpName,ArpVersion)))
			Cleanup(RETURN_FAIL,ERROR_INVALID_RESIDENT_LIBRARY,NULL);
	
	/* parse command line */
	for (argc=0;argc < NARGS ;++argc)
		argv[argc] = (STRPTR) NULL;

	while(*Line > ' ')
		++Line;

	if((argc = GADS(++Line,
		strlen(Line),
		"Usage: Tail [Files file1 file2 ...] [TO filename] [LIN linenumber] [BAN]",
		argv,
		"Files/...,TO/K,LIN/K,BAN/S" )) < 0)
			Cleanup(RETURN_WARN,NULL,FROM);

		if (!(Buf = ArpAllocMem(BFSIZE,MEMF_CLEAR))) 
			Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"Error: no memory");

	if ( Anchor = (struct UserAnchor *)ArpAlloc( (ULONG)sizeof( *Anchor )) )
	{
		Anchor->ua_AP.ap_Length = 255;	/* Want full path built */
		
	}
	else Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"Error:No memory");
	
	
	if (LIN) {
	Lines = Atol(LIN);
	if((Errno == ERRBADINT) || (Lines <=0)) Cleanup(RETURN_ERROR,NULL,"Bad args");
	if(Lines > 255) Cleanup(RETURN_WARN,NULL,"Too many lines");
	}
	else Lines = 10;

	/* set up queue */
	for (j = 0; j < Lines; j++) {
		if (!(Lin[j]=ArpAllocMem(MAXLIN,MEMF_CLEAR)))
		Cleanup(RETURN_ERROR,ERROR_NO_FREE_STORE,"Error: no memory!");
                  }
	if(TO) {
			if(!(outfh=ArpOpen(TO,MODE_NEWFILE))) {
				Printf("Can't open %s\n",TO);
				Cleanup(RETURN_ERROR,ERROR_OBJECT_NOT_FOUND,NULL);
				}
			}
	else outfh = Output();


	ArV = (char **)FROM;
	if (ArV[0]) {

	if ( Anchor = (struct UserAnchor *)ArpAlloc( (ULONG)sizeof( *Anchor )) )
	{
		Anchor->ua_AP.ap_Length = 255;	/* Want full path built */
		
	}
	else
		Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"Error: no memory");
	
	while (ArV[i] != NULL) {

	Result = FindFirst(ArV[i],(struct AnchorPath*) Anchor);

	while (Result == 0) {

		if (Anchor->ua_AP.ap_Info.fib_DirEntryType < 0) {
			if(!(infh=ArpOpen(Anchor->ua_AP.ap_Buf,MODE_OLDFILE))) {
				Printf("Can't open %s\n",FROM);
				Cleanup(RETURN_ERROR,ERROR_OBJECT_NOT_FOUND,NULL);
					}
			else {
				if (BAN) {
					PathName(ArpLock(Anchor->ua_AP.ap_Buf,
					ACCESS_READ),Banner,255);
					FPrintf(outfh,"*** File %s ***\n",
					Banner);
					}
			Tail(infh,outfh,Lines);
			}
			}
		else {
			Printf("%s is a directory !\n",ArV[i]);
			Cleanup(RETURN_ERROR,ERROR_OBJECT_WRONG_TYPE,NULL);
			}

		Result = FindNext((struct AnchorPath*) Anchor );	
		}
		i++;
		}
	}
	
	else Tail(Input(),outfh,Lines);

	Cleanup(NULL,NULL,NULL);


}
