/*
          String - Extract Ascii strings from a file.

          Base on the 'Strings' UNIX port by Egidio Casiraghi published
          in MC-Microcumputer 84, April 1989. Many enhancements by Fabio
          Rossetti: resident, wildcards, BANNER switch.

	  (c) 1989 by Fabio Rossetti

	  To compile under Lattice C v5.0x use:

		lc -O -cus -v string
		blink lib:cres.o string.o to string 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>

#define START 1
#define ALPHA 2
#define BUFSIZE 512

#define FROM	argv[0]
#define MIN	argv[1]
#define BANNER	argv[2]
	STRPTR argv[3];

struct ArpBase *ArpBase;

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

struct	UserAnchor *Anchor;
/* These must be global because of shutdown on ^C */
TEXT *buffer=NULL;
BPTR fh;
TEXT Banner[256];

struct Process *Pr;
			
	ULONG count;
	LONG minlen;

/* Delete Lattice func to keep down code size */
VOID MemCleanup()
{
}

VOID Cleanup(r1,r2,msg)
LONG r1,r2;
STRPTR msg;
{
	if (msg) Puts(msg);
	if (buffer) FreeMem(buffer,BUFSIZE);
	if (Anchor) FreeAnchorChain((struct AnchorPath *)Anchor);
	if (ArpBase) CloseLibrary((struct Library *)ArpBase);
	Pr->pr_Result2 = r2;
	exit(r1);
}

/* This is the real work, a buffer with a segment of the file being
examinated is scanned for strings */

BOOL strings(buf,len,minlen)
LONG len;
LONG minlen;
TEXT *buf;
{

	REGISTER LONG k,n;
	REGISTER TEXT *start,c;
	REGISTER LONG status;

	status=START;
	for(k=0;k<(LONG)len;k++)
	{
		c=buf[k];
		switch (status)
		{
		case START:
			if(c<32 || c >125)
				break;
			status=ALPHA;
			start=buf+k;
			n=1;
			break;
		case ALPHA:
			if(c>31 && c<126)
			{
				n++;
				break;
			}
			if((long)n>=minlen)
			{
				buf[k]='\0';
				Printf("%s\n",start);
				/* Check for ^C */
				if (SetSignal(0,0) & SIGBREAKF_CTRL_C) 
					Cleanup(RETURN_WARN,NULL,"***BREAK");
				if (CheckBreak(SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F,NULL)) {
					Puts("***WARN: file read abort");
					return(TRUE);
					}
			}
			status=START;
			break;
		}
	}
	return (FALSE);
}

VOID Scan(filh)
BPTR filh;
{
	/* print banner, if requested */
	if (BANNER) {	PathName(ArpLock(Anchor->ua_AP.ap_Buf),Banner,255);
			Printf("*** File %s ***\n",
			Banner);}

	for (;;)
	{
		count=Read(filh,buffer,BUFSIZE);
		if ((!count) || (strings(buffer,count,minlen))) break;
	}
}		


VOID _main(Line)

STRPTR Line;

{

	LONG	Result,fl;
	ULONG argc;
			
	
	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 < 3;++argc)
		argv[argc] = (STRPTR) NULL;

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

	 if (GADS(++Line,
			strlen(Line),
			"\nUsage: String FROM pattern [MIN=M minlenght] [BANNER=B]\n",
			argv,
			"FROM,MIN=M/K,BANNER=B/S") <= 0) 
				Cleanup(RETURN_WARN,NULL,argv[0]);
				
				

	/* Set minimum lenght of strings to be displayed */
	if((minlen=Atol(MIN))<=0)
			minlen=4;


	if(!(buffer=(TEXT *)AllocMem(BUFSIZE,MEMF_CLEAR|MEMF_PUBLIC)))
		Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"Error: no memory");

	/* Allocate space for anchorpath */ 	
	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");

	/* Looks for files matching pattern */
	Result = FindFirst(argv[0],(struct AnchorPath*) Anchor);
	fl=-1; 
	while ( Result == 0 )
	{
		fl = 0; 

	/* Get file if it is not a directory block */
		if (Anchor->ua_AP.ap_Info.fib_DirEntryType < 0) {
			fh=ArpOpen(Anchor->ua_AP.ap_Buf,MODE_OLDFILE);

		Scan(fh);
		}

		Result = FindNext((struct AnchorPath*) Anchor );
	

	}
	/* This in case a pattern with no matches is given */
	if ((Result == ERROR_OBJECT_NOT_FOUND) || 
		((Result == ERROR_NO_MORE_ENTRIES)  && fl) )
			Cleanup(RETURN_ERROR,ERROR_OBJECT_NOT_FOUND,"Error:Can't get file");
	
	/* if no FROM , look into stdin */
	

	Cleanup(NULL,NULL,NULL);
	
}


