/*
          Wc - Text statistics

          Original effort by Fabio Rossetti. Inspired by the wc program
          by Gary Brant found on <>< 179 and the WC utility found
          in the Lattice C 5.0 package.

	  (c) 1989 by Fabio Rossetti

	  To compile under Lattice C v5.0x use:

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

*/

#include <ctype.h>
#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>		


#define NARGS 3

LONG argc;
STRPTR argv[NARGS];

#define FILS 	argv[0]
#define PRINT	argv[1]
#define CHECK 	argv[2]

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

/* file stuff */
#define BFSIZE 	4096
BPTR fh=NULL;
TEXT *Buf;

/* flag to signal if only a file is found, affects output */
BOOL Single = TRUE;

/* extended AnchorPath structure (full path needed) */
struct UAnchor {
	struct AnchorPath AP;
	TEXT Bf[255];
		};

struct UAnchor *Anchor;
struct DirectoryEntry *De,*FileList=NULL;

/* shutdown routine */
VOID Cleanup(r1,r2)
LONG r1,r2;
{
	if (FileList) FreeDAList(FileList);
	if (Anchor) FreeAnchorChain((struct AnchorPath *)Anchor);
	if (fh) Close(fh);
	if (ArpBase) CloseLibrary((struct Library *)ArpBase);
	Pr->pr_Result2 = r2;
	exit(r1);
}

/* Conversion table for checksum */
TEXT *Ascii =
"!@#$%^&*()_+1234567890-=qwertyuiop[]asdfghjkl;'`\\zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:\"~|ZXCVBNM<>?";

/* A Lattice Wc compatible way to assign a unique numeric value to an ASCII char */
Code(c)
TEXT c;
{
	REGISTER BYTE i=0;
	REGISTER TEXT *pos=Ascii;
	while (*pos != NULL) {
		if(*pos++ == c) return(i+1);
		else i++;
		}
   return(0);
}

/* perform statistics on the opened file Name-d */
VOID Wc(filhand,Name)
BPTR filhand;
TEXT *Name;

{


	REGISTER TEXT c;
	BOOL inword=FALSE,inpword=FALSE;
	REGISTER ULONG	i,
			nc=0,	/* number of chars */
			na=0,	/* number of alpha chars */
			asnum=0,/* 1st part of checksum, sum of printable chars */
			sum=0;	/* 2nd part of checksum, sum perf'd by Code() */
	ULONG 		nl=0,	/* number of lines */
			nw=0,	/* number of words of alpha chars */
			np=0,	/* number of words of printable chars */
			count,
			awl;	/* average word lenght */



	while (count = Read(filhand,Buf,BFSIZE)) {

	/* CTRL C ? */
	if (SetSignal(0,0) & SIGBREAKF_CTRL_C) {
		Puts("***Break");
		Cleanup(RETURN_WARN,NULL);
		}

		for (i = 0; i < count; i++) {
		
		c = *(Buf+i);

		/* filter non-printable chars */
		if (PRINT)
			if ( !isprint(c) && !isspace(c) ) continue;

		/* perform chexsum */
		if (CHECK)
			if (isprint(c)) {
				++asnum;
				if(!isspace(c)) sum+=Code(c);
				}	
		++nc;	

		/* a line */
		if (c == '\n') ++nl;

		if (isalpha(c)) ++na;

		/* count words e pwords */
		if (isspace(c))
		{
		inword = FALSE;
		inpword = FALSE;
		}

		else {
		 if (inword == FALSE) 
			{
				inword = TRUE;
				if (isalpha(c)) ++nw;
			}
		 if (inpword == FALSE) 
			{
				inpword = TRUE;
				if ((isprint(c))) ++np;
			}
		}
		}
	}

	/* calculate average word lenght */
	if (!nw) awl = 0;
	else awl = ((na % nw) > (nw / 2)) ? ((na/nw)+1) : (na/nw);

	/* output results, table form if more than one file */
	if (Single) {
	Printf("      Characters : %ld\n",nc);
	Printf("Alpha Characters : %ld\n",na);
	Printf("           Words : %ld\n",nw);
	Printf("Avr. Word Lenght : %ld\n",awl);
	Printf("          PWords : %ld\n",np);
	Printf("           Lines : %ld\n",nl);
	if(CHECK) Printf("\n        Checksum : %ld %ld\n",asnum,sum);	
	}
	else
	{
	if(CHECK) Printf("%7ld %7ld %7ld %7ld %7ld %7ld %7ld %7ld %s\n",
			nc,na,nw,awl,np,nl,asnum,sum,Name);
	else Printf("%7ld %7ld %7ld %7ld %7ld %7ld %s\n",
			nc,na,nw,awl,np,nl,Name);
	}
}


/* Trick to keep code down to size */
VOID MemCleanup()
{
}
/* as above */
	
VOID _main(Line)
STRPTR Line;

{


	BOOL Result;
	TEXT **ArV;
	LONG i=0;

	Pr = (struct Process*)FindTask(NULL);
	
	if(!(ArpBase = (struct ArpBase*)OpenLibrary(ArpName,ArpVersion)))
			Cleanup(RETURN_FAIL,ERROR_INVALID_RESIDENT_LIBRARY);
	
	/* parse command line */
	for (argc=0;argc < NARGS ;++argc)
		argv[argc] = (STRPTR) NULL;

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

	if((argc = GADS(++Line,
		strlen(Line),
		"Usage: Wc [Pattern] [PRINT=P] [CHECK=C]",
		argv,
		"Files/...,PRINT=P/S,CHECK=C/S" )) < 0)
			{
			Puts(FILS);
			Cleanup(RETURN_WARN,NULL);
			}

 
	/* get buffer for file */
	if (!(Buf = ArpAllocMem(BFSIZE,MEMF_CLEAR))) {
				Puts("Error: no memory");
				Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE);
				}
	if (!(FILS)) {
			Wc(Input(),NULL);
			Cleanup(NULL,NULL);
			}



	/* Allocate space for anchorpath */ 	
	if ( Anchor = (struct UAnchor *)ArpAlloc( (ULONG)sizeof( *Anchor )) )
	{
		Anchor->AP.ap_Length = 255; 
		Anchor->AP.ap_BreakBits = SIGBREAKF_CTRL_C; /* stop if ^C */
	}
	else
	{
		Puts("Error: no memory");
		Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE);
	}
	ArV = (TEXT **) FILS;

	while (ArV[i] != NULL) {

	/* examine files specified by pattern */ 
	Result = FindFirst(ArV[i],(struct AnchorPath *)Anchor);

	while ( Result == 0 )
	{
		if (Anchor->AP.ap_Info.fib_DirEntryType < 0) {

		/* add filename to ordered DAList */ 
		if ( !(De=AddDANode(Anchor->AP.ap_Buf,
					 &FileList, 0L, 0L)))
			{
			Puts("Error: no memory");
			Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE);
			}
		}

		Result = FindNext((struct AnchorPath*) Anchor );
	}
	if (Result != ERROR_NO_MORE_ENTRIES) {
		switch(Result)
		{
		case ERROR_OBJECT_NOT_FOUND:
		Printf("Error:Can't find %s\n",ArV[i]);
		break;

		case ERROR_BREAK:
		Puts("***Break");
		break;

		default:
		Puts("Error");
		}
		Cleanup(RETURN_ERROR,Result);

	}		

	i++;
        }

		

	De = FileList;

	if ((De->de_Next)) {
	Single = FALSE;
	if(CHECK){
	Puts("  Chars   Alpha   Words  A.W.L.  PWords   Lines     Checksum    FileName");
	Puts("--------------------------------------------------------------------------");
	}
	else {
	Puts("  Chars   Alpha   Words  A.W.L.  PWords   Lines FileName");
	Puts("----------------------------------------------------------");
		}
	}
	while (De) {

	if (!(fh = Open(De->de_Name,MODE_OLDFILE)))
		{
		Puts(De->de_Name);
		Puts("Error:");
		Pr->pr_Result2 = ERROR_OBJECT_NOT_FOUND;
		exit(RETURN_ERROR);
		}
	else
		{
		Wc(fh,BaseName(De->de_Name));
		Close(fh);
		}

	De = De->de_Next;

	}
			
	fh = NULL;
	Cleanup(NULL,NULL);

}
