/*
          For - Use wildcards with commands not supporting them.

          Original effort by Fabio Rossetti.

          (c) 1989 by Fabio Rossetti

          To compile under Lattice C v5.0x use:

		lc -O -v -cus for
		blink lib:cres.o for.o to for 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 MAXARG 20
#define PAT 0
#define MOD 1
#define DO 2
#define ARGS 3
#define DIRECTORY	0
#define FILE		1
#define DALIST TRUE
#define ANCHOR TRUE
#define BREAKS (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)

struct Process *Pr;
struct ArpBase *ArpBase;
BPTR DoSeg;
struct ResidentProgramNode *DoNode;
struct UserAnchor {
	struct	AnchorPath	ua_AP;
	BYTE	moremem[255];	/* extension */
};

struct	UserAnchor *Anchor = NULL;

struct DirectoryEntry *FileList = NULL;	/* Head of DAList */

struct {
	unsigned Sort : 1;
	unsigned Verbose : 1;
	unsigned Res : 1;
	unsigned Dirs : 1;
	unsigned Files : 1;
	} Mod;

TEXT Arguments[256];

LONG argc,Pat;
STRPTR argv[MAXARG];

/* shutdown */
VOID Cleanup(code,retcode,msg)
LONG code,retcode;
STRPTR msg;
{
	if (FileList) FreeDAList( FileList );
	if (Anchor) {
			FreeAnchorChain( (struct AnchorPath *) Anchor );
			FreeMem(Anchor, sizeof( *Anchor));
		 	}

	CloseLibrary( (struct Library *) ArpBase);

	Pr->pr_Result2 = retcode;
	if (msg) Puts(msg);
	if (Mod.Res) RemResidentPrg(argv[DO]);
	exit(code);
}

/* concatenate strings with interleaved blanks */
VOID sstrcat(to,from)
TEXT *to;
TEXT *from;
{
	strcat(to,from);
	strcat(to," ");
}

VOID SyncIt(Pt)
TEXT *Pt;

{
	LONG Rc;
	REGISTER LONG i;

	if(Pat==DO) for (i=ARGS; i <argc; ++i)
				 sstrcat(&Arguments,argv[i]);
		else {
			for(i=ARGS;i < Pat; ++i)
				 sstrcat(&Arguments,argv[i]);
			strcat(&Arguments,"\"");
			strcat(&Arguments,Pt);
			sstrcat(&Arguments,"\"");
			for(i=Pat+1; i <argc; ++i)
				 sstrcat(&Arguments,argv[i]); 
			}

	if (Mod.Verbose) Printf("*** Executing: %s %s ***\n\n",
					argv[DO],Arguments);

	/* problem with resident: checksum invalid or lowmem */
	if (Mod.Res) 
		if (!(DoNode = ObtainResidentPrg(argv[DO]))) {
		Puts("***Warn: problem with resident, releasing segs");
		/*ReleaseResidentPrg(DoSeg);*/
		RemResidentPrg(argv[DO]);
		/* not to do this anymore */
		Mod.Res = 0;
		}
		else ReleaseResidentPrg(DoSeg);

	if((Rc = SyncRun(argv[DO],Arguments,NULL,NULL)) < 0) 
		Cleanup(RETURN_ERROR,NULL,"Error:can't load/execute program");
		
	
	if (Mod.Verbose) Printf("\n*** Return Code = %ld ; Error Code = %ld ***\n",
							Rc,
							Pr->pr_Result2);

	
					
	*Arguments = '\0';

}

VOID _main(Line)

STRPTR Line;

{

	LONG Result,key;

	REGISTER struct DirectoryEntry	*de;
		
	Pr = (struct Process*)FindTask(NULL);

	if(!(ArpBase = (struct ArpBase*)OpenLibrary(ArpName,ArpVersion))){
			Pr->pr_Result2=ERROR_INVALID_RESIDENT_LIBRARY;
			exit(RETURN_FAIL);
			}

	/* parse command line */
	for (argc=0;argc < MAXARG;++argc)
		argv[argc] = (STRPTR) NULL;

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

	/* I couldn't get /.. (and any other tracked fucntion) to work
	   in conjunction with SyncRun() in a way to prevent ARP not to
	   release resources.. There HAS to be a bug. SDB are you listening ? */
	if((argc = GADS(++Line,
			strlen(Line),
			"\nUsage: For Pattern [MODE S|V|R|F|D] DO Command [Arguments]\n       %% will be replaced with pattern\n",
			argv,
			"PAT/A,MODE/K,DO/A,,,,,,,,,,,,,,,")) <= 0)
				Cleanup(RETURN_ERROR,NULL,argv[0]);
	

	argc += (argv[MOD]) ? 0 : 1;

	Mod.Sort=Mod.Verbose=Mod.Res=0;
	Mod.Dirs=Mod.Files=1;

	/* set options */
	do {
	switch (Toupper((TEXT)*argv[MOD])) {

		case 'S':
			Mod.Sort = 1;
			break;
		case 'V':
			Mod.Verbose = 1;
			break;
		case 'R':
			Mod.Res = 1;
			break;
		case 'F':
			Mod.Dirs = 0;
			break;
		case 'D':
			Mod.Files = 0;
			break;
		}
	}    
	while (*argv[MOD]++ != '\0') ;

	/* Just in case...*/
	if (!(Mod.Files || Mod.Dirs)) Mod.Files=Mod.Dirs=1;

	/* Find argument where pattern is to be expanded, if any.
	Only one allowed...Oh how much I love all the C-ism down here.*/
	for(Pat=argc; (Strcmp(argv[Pat],"%%") && (Pat > DO)); Pat--);

	/* Check if resident mode is req'd and proceed if prog ain't
	   resident either */

	if (Mod.Res)
	{
	if (!(DoNode = ObtainResidentPrg(argv[DO]))) {
		
		if(!(DoSeg = LoadPrg(argv[DO]))) {
			Mod.Res = 0;
			Cleanup(RETURN_ERROR,NULL,"Error: can't load program");
			}
		if(!(DoNode  = AddResidentPrg(DoSeg,argv[DO]))) Mod.Res = 0;
	}
	else {
		ReleaseResidentPrg(DoNode->rpn_Segment);
		Mod.Res = 0;	
		}	
	}

	/* Allocate space for anchorpath */ 	
	if ( Anchor = (struct UserAnchor *)AllocMem( (ULONG)sizeof( *Anchor ),MEMF_PUBLIC|MEMF_CLEAR) )
	{
		Anchor->ua_AP.ap_Length = 255;	/* path */
		
	}
	else Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"Error:No memory");
	

	Result = FindFirst( argv[PAT], (struct AnchorPath *)Anchor);

while ( Result == 0 )
{



	if (Mod.Sort) {


		if (Anchor->ua_AP.ap_Info.fib_DirEntryType >= 0)
		{
			key = DIRECTORY;
		}
		else
		{
			key = FILE;
		}

		if ( !AddDANode(Anchor->ua_AP.ap_Buf, &FileList, 0L, key))
			Cleanup(RETURN_FAIL,ERROR_NO_FREE_STORE,"Error: no memory");
		

	}

	else
	{



		if ((Anchor->ua_AP.ap_Info.fib_DirEntryType >= 0) && Mod.Dirs)
			SyncIt(&Anchor->ua_AP.ap_Buf);

		if ((Anchor->ua_AP.ap_Info.fib_DirEntryType < 0) && Mod.Files)
			SyncIt(&Anchor->ua_AP.ap_Buf);


	}

	/* check for ^D */
	if (SetSignal(0,0) & BREAKS)
		Cleanup(RETURN_WARN,NULL,"***Break");

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


	/* Free the Anchor chain built by the above functions */

	if ((Result == ERROR_OBJECT_NOT_FOUND) ||
		(Result != ERROR_NO_MORE_ENTRIES))
			Cleanup(RETURN_ERROR,Result,"Error");

	if (Mod.Sort) {

	de = FileList;
	if (de->de_Type == DIRECTORY)
	{
		for ( ; de ; de = de->de_Next)
		{
		/* check for ^D */
		if (SetSignal(0,0) & BREAKS)
			Cleanup(RETURN_WARN,NULL,"***Break");
	
		if (de->de_Type != DIRECTORY)
				break;
			if (Mod.Dirs) SyncIt(de->de_Name);
		}
	}

	if ( de && Mod.Files )
	{
		for ( ; de ; de = de->de_Next) {
		/* check for ^D */
		if (SetSignal(0,0) & BREAKS)
			Cleanup(RETURN_WARN,NULL,"***Break");
			
	
		SyncIt(de->de_Name);
		}
	}

}

	Cleanup(RETURN_OK,NULL,NULL);
}
