/*
 *  Copyright © 1992-94 by S.R. & P.C.
 *
 *  Created:    30 Mar 1992  18:26:36
 *  Modified:   09 Nov 1994  23:11:45
 *
 *  Make>> rx BumpRev For 3 8
 *  Make>> sc <file>.c
 *  Make>> slink LIB:cs.o <file>.o SC SD BATCH NOICONS TO <file> LIB LIB:c.lib
 *  Make>> protect <file> P ADD
 *
 *
 *	30/03/1992: V1.0 (Pierre Carrette)
 *		Simple version with template: "Args/M,ALL/S,DONTFAIL/S,DO/K/A/F"
 *  4/07/1994: V2.2 (Sylvain Rougier)
 *      No longer add the name of the object if no '%%' present
 *  5/07/1994: V3.0
 *      Add the 'Quiet' option not to print hierarchy (but still print FileType if asked)
 *      Now work as it should always work: don't scan all the directory if no pattern
 *      but scan all subdir if 'All' option specified.
 *      Add the 'GivePathName' option to provide a Full Path arguments to the command.
 *      No longuer add '"' around the file name. User can type '"%%"' if he need it.
 *  7/07/1994 V3.1
 *      If a full pathname file entry was specified, For don't find it type. fixed
 *  15/07/1994 V3.2
 *      Guru if a single file was suplied. fixed
 *      Now FullPathArgs realy work
 *      Don't put optimize or it guru ! (see note)
 *  31/07/1994 V3.3
 *      Check the stack size because it need at least 8192 (probably !...)
 *      put optimize again. just to see.
 *  07/08/1994 V3.4
 *      Remove optimize because the sasc bug still here
 *      correct some error int the doc ( the '*' was incorrectly used instead of the '"')
 *  21/08/1994 V3.5
 *      Now use StackSwap() to avoid stack pb
 *  22/09/1994 V3.6	(Pierre Carrette is back :-)
 *		Major code cleanup. No more need StackSwap(). Bug fixes in template. whatis.library now Optional.
 *		Inverted 'Quiet' to 'Verbose'. Changed 'VNFileType' to more comprehensive 'FileTypeVar' keyword.
 *		Changed 'GivePathName' to 'FullPathArgs'.
 *      FileTypeVar was not set if Showbytes was off.
 *      Showbytes/ShowFileType is now off if no Cmd supplied.
 *		'Type' argument syntax simplified. now:  "#text,!source c"
 *		MatchFileType() bug fix. "#text,!source c" was not working!!
 *		SAS Optimisation now works!!!
 *  22/09/1994 V3.7
 *		Recompiled with new version of c.lib which had a bug for 'ENV:' var parsing
 *  09/11/1994 V3.8
 *		Bug fix. File UnLock()ed before processed so that command can open it for writing.
 */

#include <clib.h>
#include <libraries/WhatIsBase.h>
#include <proto/WhatIs.h>
#include "For_rev.h"

// Startup Modules...
CliArgs; ReadEnvArgs("For");

static char Version[] = VERSTAG;

STRPTR Template = "\
Pattern/A/M,Files/K,Dirs/K,Since/K,Before/K,MinSize/K/N,MaxSize/K/N,\
PosProtect/K,NegProtect/K,Type/K,All/S,ASync/S,ReadSize/K/N,ShowBytes/K/N,\
D=Deep/K/N,SFT=ShowFileType/S,FTV=FileTypeVar/K,FPA=FullPathArgs/S,Verbose/S,Do/K/F";

STRPTR CliHelp = VERS" ("DATE") [32m© Sylvain Rougier & Pierre Carrette.[31m\n\
Usage: For <Pattern> [Files <MATCH|YES|NO>] [Dirs <MATCH|YES|NO>]\n\
[Since <Date>] [Before <Date>] [MinSize <Number>] [MaxSize <Number>]\n\
[PosProtect <L|C|H|S|P|A|R|W|E|D>] [NegProtect <L|C|H|S|P|A|R|W|E|D>]\n\
[Type <[#=SUB,~=EXLC]FileType0,,,FileType15>] [All] [ASync] [ReadSize]\n\
[ShowBytes] [Deep] [ShowFileType] [FileTypeVar <var name>] [FullPathArgs]\n\
[DO <Command [args] [,Command [args] [,...]]>]\n";

#define ARG_Template		0
#define ARG_Files			1
#define ARG_Dirs			2
#define ARG_Since			3
#define ARG_Before			4
#define ARG_MinSize			5
#define ARG_MaxSize			6
#define ARG_PosProtect		7
#define ARG_NegProtect		8
#define ARG_Type			9
#define ARG_ALL				10
#define ARG_ASYNC			11
#define ARG_READSIZE		12
#define ARG_SHOWBYTES		13
#define ARG_DEEPWHATIS		14
#define ARG_SHOWFILETYPE	15
#define ARG_FileTypeVar		16
#define ARG_FullPathArgs	17
#define ARG_Verbose			18
#define ARG_CMD				19
#define ARG_ENDARG			20


struct Library *WhatIsBase;


#define MAX_PATHLEN		256
#define MAX_CMDLEN 		512

/* char used in the filetype spec string */
#define NOTFileType		'~'
#define WITHSubTypes	'#'

/*******************************************************/
/* not in dos.h !! */
#define	FIBB_HOLD		7
#define	FIBF_HOLD		(1<<FIBB_HOLD)

/* it is my own definition: not standard */
#define	FIBB_COMMENT	8
#define	FIBF_COMMENT	(1<<FIBB_COMMENT)
#define	FIBB_LINK		9
#define	FIBF_LINK		(1<<FIBB_LINK)


#define PATTERN_BUF_SIZE	40
#define MAX_FTS				16	/* Max FileTypeSpec in SelectInfo */

struct FileTypeSpec {
	FileType fs_FileType;
	UWORD fs_Flags;
};

/* FileTypeSpec flags */
#define FTSF_EXCLUDETYPE	0x0001	/* Exclude this type (otherwise include) */
#define FTSF_WITHSUBTYPES	0x0002	/* Affect Include/Exclude to subtypes */


struct SelectInfo {
	UBYTE si_PatTok[PATTERN_BUF_SIZE];		/* PreParsed pattern                    	*/
	LONG si_MinSize;						/* Show files bigger than that  			*/
	LONG si_MaxSize;						/*   and smaller than that              	*/
	struct DateStamp si_SinceDate;			/* Show files newer than that   			*/
	struct DateStamp si_BeforeDate;			/*   and older than that                	*/
	UWORD si_PosProtect;					/* Show files that have these bits set   	*/
	UWORD si_NegProtect;					/* Show files that have these bits clear 	*/
	struct FileTypeSpec si_FileTypes[MAX_FTS];	/* Include and Exclude file types 		*/
	UWORD si_NumFts;						/* Number of FileTypeSpec in previous array */
	UWORD si_Flags;							/* Flags. See below                         */
};

/* SelectInfo flags */

#define SI_ALL_FILES		0x0001
#define SI_MATCH_FILES		0x0002
#define SI_ALL_DIRS			0x0004
#define SI_MATCH_DIRS		0x0008
#define SI_AFFECT_SUBDIRS	0x0010
#define SI_NAME				0x0020
#define SI_SIZE				0x0040
#define SI_SINCEDATE		0x0080
#define SI_BEFOREDATE		0x0100
#define SI_POSPROTECTION	0x0200
#define SI_NEGPROTECTION	0x0400
#define SI_POSFILETYPE		0x0800
#define SI_NEGFILETYPE		0x1000

#define SI_FILETYPE			(SI_POSFILETYPE|SI_NEGFILETYPE)
#define SI_MATCHBITS		(SI_NAME|SI_SIZE|SI_SINCEDATE|SI_BEFOREDATE|SI_POSPROTECTION|SI_NEGPROTECTION|SI_FILETYPE)


struct Opt {
	BOOL Verbose;				/* Print some information */
	BOOL Async;					/* Run commands Asynchronously */
	BOOL FullPathArgs;			/* Give Full PathName in Fmt ? */
	BOOL ShowFileType;			/* Show FileType ? */
	LONG ReadSize;				/* Size to read to find filetype */
	ULONG Deep;					/* deep level to find FileType */
	UWORD Showbytes;			/* number of bytes to show */
	STRPTR FileTypeVar;			/* Name of FileType var. no var if null */
};


/* return pointer to the first non blank char in a string, or to the '\0' if the string is empty */

static char *FirstNonBlank(char *buf)
{
	while (*buf && *buf == ' ')
		buf++;
	return buf;
}


/* check if buffer is empty or not */

static BOOL IsEmpty(char *buf)
{
	return (BOOL) ((*FirstNonBlank(buf)) ? FALSE : TRUE);
}


static BOOL String2Date(char *src, struct DateStamp *ds)
{
	struct DateTime *dt;
	BOOL Ok = TRUE;
	char *s1, *s2;
	char buf[60];

	if (!(dt = AllocMem(sizeof(struct DateTime), MEMF_PUBLIC | MEMF_CLEAR)))
		 return FALSE;

	strcpy(buf, src);
	s1 = s2 = FirstNonBlank(buf);
	if (*s1) {
		dt->dat_StrDate = s1;
		while (*s2 && *s2 != ' ')
			s2++;
		if (*s2) {
			*s2++ = '\0';
			s2 = FirstNonBlank(s2);
		}
		if (*s2) {
			dt->dat_StrTime = s2;
			if (!StrToDate(dt))
				Ok = FALSE;
		}
		else if (!StrToDate(dt)) {
			dt->dat_StrDate = NULL;
			dt->dat_StrTime = s1;
			if (!StrToDate(dt))
				Ok = FALSE;
		}
		if (Ok)
			*ds = dt->dat_Stamp;
	}
	FreeMem(dt, sizeof(struct DateTime));

	return Ok;
}


static void PrintByte(UBYTE Buffer[], LONG BufLen, UBYTE Num)
{
	register short i;

	if (BufLen > 0) {
		for (i = 0; i < Num && i < BufLen; i++)
			Printf("%02lx", Buffer[i]);
	}
}


/*
 *  Parse a line that may contain comas. Backslash ('\') is the override char.
 */

static UWORD ProtectBit(char P[])
{
	UWORD PB = 0;

	while (*P) {
		switch (*P) {
		case 'L':
		case 'l':
			PB |= FIBF_LINK;	/* another magic bit! */
			break;
		case 'C':
		case 'c':
			PB |= FIBF_COMMENT;	/* our magic bit! */
			break;
		case 'H':
		case 'h':
			PB |= FIBF_HOLD;
			break;
		case 'S':
		case 's':
			PB |= FIBF_SCRIPT;
			break;
		case 'P':
		case 'p':
			PB |= FIBF_PURE;
			break;
		case 'A':
		case 'a':
			PB |= FIBF_ARCHIVE;
			break;
		case 'R':
		case 'r':
			PB |= FIBF_READ;
			break;
		case 'W':
		case 'w':
			PB |= FIBF_WRITE;
			break;
		case 'E':
		case 'e':
			PB |= FIBF_EXECUTE;
			break;
		case 'D':
		case 'd':
			PB |= FIBF_DELETE;
			break;
		}
		P++;
	}
	return PB;
}


static UWORD __inline EasyProtect(LONG Protection)
{
	return (UWORD) ((Protection ^ 0x0F) & 0x00FF);	/* Complement 4 lower bits so all work the same way */
}


static BOOL Match(struct SelectInfo * SelectInfo, struct FileInfoBlock *fib)
{
	if (fib->fib_DirEntryType < 0) {	/* this is a file */
		if (SelectInfo->si_Flags & SI_ALL_FILES)
			return TRUE;
		else if (!(SelectInfo->si_Flags & SI_MATCH_FILES))
			return FALSE;
	}
	else {						/* Dir */
		if (SelectInfo->si_Flags & SI_ALL_DIRS)
			return TRUE;
		else if (!(SelectInfo->si_Flags & SI_MATCH_DIRS))
			return FALSE;
	}
	if (SelectInfo->si_Flags & SI_NAME) {
		if (!MatchPatternNoCase(SelectInfo->si_PatTok, fib->fib_FileName))
			return FALSE;
	}
	if (SelectInfo->si_Flags & SI_SINCEDATE) {
		if (CompareDates(&SelectInfo->si_SinceDate, &fib->fib_Date) < 0)
			return FALSE;
	}
	if (SelectInfo->si_Flags & SI_BEFOREDATE) {
		if (CompareDates(&SelectInfo->si_BeforeDate, &fib->fib_Date) > 0)
			return FALSE;
	}
	if (fib->fib_Size < SelectInfo->si_MinSize)
		return FALSE;
	if (SelectInfo->si_MaxSize && (fib->fib_Size > SelectInfo->si_MaxSize))
		return FALSE;
	{
		UWORD Protection = EasyProtect(fib->fib_Protection);

		if (SelectInfo->si_Flags & SI_POSPROTECTION) {
			if (!(Protection & SelectInfo->si_PosProtect))
				return FALSE;
		}
		if (SelectInfo->si_Flags & SI_NEGPROTECTION) {
			if ((Protection & SelectInfo->si_NegProtect))
				return FALSE;
		}
	}
	return TRUE;
}


static BOOL MatchFileType(struct SelectInfo *SelectInfo, struct FileInfoBlock *fib, FileType Type)
{
	if (WhatIsBase && (SelectInfo->si_Flags & SI_FILETYPE)) {
		BOOL Sub = FALSE;
		UWORD Flags, i;

		do {
			for( i=0 ; i<SelectInfo->si_NumFts ; i++ ) {
				Flags = SelectInfo->si_FileTypes[i].fs_Flags;
				if ((!Sub || (Flags & FTSF_WITHSUBTYPES)) && !CmpFileType(SelectInfo->si_FileTypes[i].fs_FileType, Type))
					return (BOOL)((Flags & FTSF_EXCLUDETYPE) ? FALSE : TRUE);
			}
			Sub = TRUE;
		} while (Type = ParentFileType(Type));
		if (SelectInfo->si_Flags & SI_POSFILETYPE)
			return FALSE;	/* no match within include FileTypes, exclude file */
		else
			return TRUE;	/* Only exclude types, and not found in them, include file */
	}
	return TRUE;
}


/*
 *	Parse a line that may contain comas ','. Backslash ('\') is the override
 *	char. This function replace comas by cariage returns so that SystemTags()
 *	takes them as different command lines, just like a script file.
 */

static void ParseCmdLine(char *cmd)
{
	char *s, *d, c;

	s = d = cmd;
	while (c = *d++ = *s++) {
		if (c == '\\')
			*(d - 1) = *s++;
		else if (c == ',')
			*(d - 1) = '\n';
	}
}


/* Replaces %% by %s, suitable for SPrintf() */

static void MakeFmt(char *CmdFmt, char *Cmd)
{
	char *s;

	strcpy(CmdFmt, Cmd);
	ParseCmdLine(CmdFmt);		/* Replace , by \n to separate commands */
	s = CmdFmt;
	while (*s) {
		if (*s == '%' && *(s + 1) == '%')
			*(s + 1) = 's';
		s++;
	}
}


static LONG ExecuteCmd(char *Cmd, STRPTR StartPath, char *Name, struct Opt *Opt)
{
	char *CmdBuf, *CmdFmt, *NameBuf;
	LONG rc = 20;
	BOOL NoMem = TRUE;

	if (CmdBuf = AllocVec(MAX_CMDLEN, MEMF_ANY)) {
		if (NameBuf = AllocVec(MAX_CMDLEN, MEMF_ANY)) {
			if (CmdFmt = AllocVec(MAX_CMDLEN, MEMF_ANY)) {
				MakeFmt(CmdFmt, Cmd);
				if (Opt->FullPathArgs) {
					strcpy(NameBuf, StartPath);
					AddPart(NameBuf, Name, MAX_CMDLEN);
				}
				else
					strcpy(NameBuf, Name);
				/* Allow 5 %% in cmd */
				SPrintf(CmdBuf, CmdFmt, NameBuf, NameBuf, NameBuf, NameBuf, NameBuf);		/* build command line */
				if (Opt->Async) {
					SystemTags(CmdBuf,	SYS_Input, Open("*", MODE_OLDFILE),
										SYS_Output, NULL,		// System() will open it
										SYS_Asynch, TRUE,
										SYS_UserShell, TRUE,
										TAG_DONE);
					rc = 0;
				}
				else
					rc = SystemTags(CmdBuf, SYS_UserShell, TRUE, TAG_DONE);
				NoMem = FALSE;
				FreeVec(CmdFmt);
			}
			FreeVec(NameBuf);
		}
		FreeVec(CmdBuf);
	}
	if (NoMem)
		PrintFault(ERROR_NO_FREE_STORE, "For");
	else if (rc)
		PrintFault(IoErr(), "For");
	return rc;
}


static LONG ProcessEntry(struct SelectInfo *SelectInfo, struct FileInfoBlock *fib, STRPTR StartPath,
						 STRPTR Name, UBYTE *FileData, struct Opt *Opt, STRPTR Cmd)
{
	LONG RC = 0, BytesRead = 0;
	FileType Type = TYPE_UNSCANNED;

	if (WhatIsBase) {
		if (Opt->Deep && fib->fib_DirEntryType <= 0) {
			BPTR FH;

			if (FH = Open(Name, MODE_OLDFILE)) {
				BytesRead = Read(FH, FileData, Opt->ReadSize);
				FileData[BytesRead] = '\0';	/* whatis.library need that */
				Type = WhatIsTags(Name, WI_Deep, Opt->Deep, WI_FIB, fib, WI_Buffer, FileData, WI_BufLen, BytesRead, TAG_DONE);
				Close(FH);
			}
		}
		else
			Type = WhatIsTags(Name, WI_Deep, LIGHTTYPE, WI_FIB, fib, TAG_DONE);
	}
	if (MatchFileType(SelectInfo, fib, Type)) {
		if (WhatIsBase) {
			STRPTR IDString;
			BOOL CR = FALSE;

			IDString = GetIDString(Type);
			if (Opt->ShowFileType) {
				Printf("%-12s %-32s", IDString, (Opt->Verbose) ? fib->fib_FileName : Name);
				CR = TRUE;
			}
			if (Opt->Showbytes && fib->fib_DirEntryType <= 0) {
				PrintByte(FileData, BytesRead, Opt->Showbytes);
				CR = TRUE;
			}
			if (CR)
				PutStr("\n");
			if (Opt->FileTypeVar)
				SetVar(Opt->FileTypeVar, IDString, -1L, GVF_LOCAL_ONLY);
		}
		if (Cmd)
			RC = ExecuteCmd(Cmd, StartPath, Name, Opt);
	}
	return RC;
}


static LONG ProcessEntryPattern(struct AnchorPath *AP, struct SelectInfo *SelectInfo, struct Opt *Opt,
								STRPTR StartPath, STRPTR Pattern, UBYTE *FileData, STRPTR Cmd)
{
	BOOL DoIt = TRUE;
	LONG RC=0, MatchErr;

	for (MatchErr = MatchFirst(Pattern, AP); RC == 0 && MatchErr == 0; MatchErr = MatchNext(AP)) {
		if (AP->ap_Flags & APF_DIDDIR) {
			if (Opt->Verbose)
				Printf("[33m%s[31m\n", AP->ap_Buf);
			DoIt = FALSE;					/* Dir processed before entering it, don't process it twice */
			AP->ap_Flags &= ~APF_DIDDIR;	/* clear the completed directory flag */
		}
		else if (AP->ap_Info.fib_DirEntryType > 0) {
			if (SelectInfo->si_Flags & SI_AFFECT_SUBDIRS) {
				if (Opt->Verbose)
					Printf("[33m%s[31m\n", AP->ap_Buf);
				AP->ap_Flags |= APF_DODIR;	/* make Matchext() enter the directory */
			}
			else
				AP->ap_Flags &= ~APF_DODIR;	/* RESET this bit after MatchFirst/MatchNext to AVOID entering a dir. */
		}

		/* Here is code for handling each particular file */
		if (DoIt && Match(SelectInfo, &AP->ap_Info))
			RC = ProcessEntry(SelectInfo, &AP->ap_Info, StartPath, AP->ap_Buf, FileData, Opt, Cmd);
		DoIt = TRUE;
	}
	MatchEnd(AP);
	if (MatchErr != ERROR_NO_MORE_ENTRIES)
		PrintFault(MatchErr, "For");
	return RC;
}


LONG Main(char *ArgV[], struct WBStartup *WBenchMsg)
{
	LONG rc = RETURN_OK;
	char *StartPath = NULL, *Buffer = NULL;
	struct FileInfoBlock *fib = NULL;
	struct SelectInfo SelectInfo;
	struct Opt Opt;
	UBYTE *FileData;

	WhatIsBase = OpenLibrary("whatis.library", 3L);
	memset(&SelectInfo, 0, sizeof(SelectInfo));
	memset(&Opt, 0, sizeof(struct Opt));

	if (ArgV[ARG_Files]) {
		if (!Stricmp(ArgV[ARG_Files], "MATCH"))
			SelectInfo.si_Flags |= SI_MATCH_FILES;
		else if (!Stricmp(ArgV[ARG_Files], "YES"))
			SelectInfo.si_Flags |= SI_ALL_FILES;
	}
	else						/* set default */
		SelectInfo.si_Flags |= SI_MATCH_FILES;
	if (ArgV[ARG_Dirs]) {
		if (!Stricmp(ArgV[ARG_Dirs], "MATCH"))
			SelectInfo.si_Flags |= SI_MATCH_DIRS;
		else if (!Stricmp(ArgV[ARG_Dirs], "YES"))
			SelectInfo.si_Flags |= SI_ALL_DIRS;
	}
	else						/* set default */
		SelectInfo.si_Flags |= SI_MATCH_DIRS;
	if (ArgV[ARG_Since]) {
		struct DateStamp DateStamp;

		String2Date(ArgV[ARG_Since], &DateStamp);
		SelectInfo.si_SinceDate = DateStamp;
		SelectInfo.si_Flags |= SI_SINCEDATE;
	}
	if (ArgV[ARG_Before]) {
		struct DateStamp DateStamp;

		String2Date(ArgV[ARG_Before], &DateStamp);
		SelectInfo.si_BeforeDate = DateStamp;
		SelectInfo.si_Flags |= SI_BEFOREDATE;
	}
	if (ArgV[ARG_MinSize])
		SelectInfo.si_MinSize = *(ULONG *) ArgV[ARG_MinSize];
	if (ArgV[ARG_MaxSize])
		SelectInfo.si_MaxSize = *(ULONG *) ArgV[ARG_MaxSize];
	if (ArgV[ARG_PosProtect]) {
		SelectInfo.si_Flags |= SI_POSPROTECTION;
		SelectInfo.si_PosProtect = ProtectBit(ArgV[ARG_PosProtect]);
	}
	if (ArgV[ARG_NegProtect]) {
		SelectInfo.si_Flags |= SI_NEGPROTECTION;
		SelectInfo.si_NegProtect = ProtectBit(ArgV[ARG_NegProtect]);
	}
	if (ArgV[ARG_Type] && WhatIsBase) {
		char IDType[100];
		UBYTE NumFT = 0;
		char *c, *i;

		for (c = ArgV[ARG_Type]; *c && NumFT < MAX_FTS && rc == 0;) {
			switch (*c) {
			case NOTFileType:	// ~
				SelectInfo.si_FileTypes[NumFT].fs_Flags |= FTSF_EXCLUDETYPE;
				break;
			case WITHSubTypes:	// #
				SelectInfo.si_FileTypes[NumFT].fs_Flags |= FTSF_WITHSUBTYPES;
				break;
			default:
				i = IDType;
				while (*c && *c != ',' && i < IDType+100-1)
					*i++ = *c++;	// copy until ',' or end of string
				*i = '\0';			// nul terminate the string
				if (SelectInfo.si_FileTypes[NumFT].fs_Flags & FTSF_EXCLUDETYPE)
					SelectInfo.si_Flags |= SI_NEGFILETYPE;
				else
					SelectInfo.si_Flags |= SI_POSFILETYPE;
				SelectInfo.si_FileTypes[NumFT].fs_FileType = GetIDType(IDType);
				if (TYPE_UNKNOWNIDSTRING == SelectInfo.si_FileTypes[NumFT].fs_FileType) {
					Printf("Unknown FileType: %s\n", IDType);
					rc = 21;	// Hack to prevent PrintFault('no memory')
				}
				NumFT++;
			}
			if (*c)
				c++;
		}
		SelectInfo.si_NumFts = NumFT;
	}
	if (ArgV[ARG_ALL])
		SelectInfo.si_Flags |= SI_AFFECT_SUBDIRS;

	Opt.Async = (BOOL) ArgV[ARG_ASYNC];
	Opt.ReadSize = (ArgV[ARG_READSIZE]) ? *(LONG *) ArgV[ARG_READSIZE] : 488;
	Opt.Deep = (ArgV[ARG_DEEPWHATIS]) ? *(LONG *) ArgV[ARG_DEEPWHATIS] : LIGHTTYPE;
	if (ArgV[ARG_SHOWBYTES]) {
		Opt.Showbytes = (UBYTE) (*(LONG *) ArgV[ARG_SHOWBYTES]);
		Opt.Deep = DEEPTYPE;
	}
	Opt.ShowFileType = (BOOL) ArgV[ARG_SHOWFILETYPE];
	Opt.FileTypeVar = (STRPTR) ArgV[ARG_FileTypeVar];
	Opt.FullPathArgs = (BOOL) ArgV[ARG_FullPathArgs];
	Opt.Verbose = (BOOL) ArgV[ARG_Verbose];

	if (rc == 0 && !(Buffer = AllocVec(MAX_PATHLEN, MEMF_ANY)))
		rc = RETURN_FAIL;

	if (rc == 0 && !(StartPath = AllocVec(MAX_PATHLEN, MEMF_ANY)))
		rc = RETURN_FAIL;

	if (rc == 0 && !(fib = AllocVec(sizeof(struct FileInfoBlock), MEMF_ANY)))
		rc = RETURN_FAIL;

	if (rc == 0 && (FileData = AllocVec(Opt.ReadSize + 1, MEMF_ANY))) {
		struct AnchorPath *AP;

		if (AP = AllocVec(sizeof(struct AnchorPath) + MAX_PATHLEN, MEMF_ANY)) {
			LONG IsWild;
			STRPTR Elmt;
			UBYTE i;

			if (Opt.FullPathArgs)
				NameFromLock(((struct Process *)SysBase->ThisTask)->pr_CurrentDir, StartPath, MAX_PATHLEN);
			else
				StartPath[0] = '\0';

			for (i = 0; rc == 0 && (Elmt = ((char **) ArgV[ARG_Template])[i]); i++) {
				memset(AP, 0, sizeof(struct AnchorPath) + MAX_PATHLEN);
				AP->ap_BreakBits = SIGBREAKF_CTRL_C;	/* Break on these bits  */
				AP->ap_Strlen = MAX_PATHLEN;
				/*
				 *  For wildcard matching with ALL flag set, two situations are handled differently
				 *	1.	foo#?/bar
				 *	2.	foobar#?
				 *	In the first case, a standard MatchFirst()/MatchNext() is used.
				 *	For the second one, if 'ALL' option is on, the pattern is replaced with "#?"
				 *	so that all directories are parsed, and then files filtered out by the more
				 *	powerfull function Match()
				 */
				IsWild = ParsePatternNoCase(Elmt, Buffer, MAX_PATHLEN);
				if (ArgV[ARG_ALL] && IsWild == 1 && FilePart(Elmt) == Elmt) {
					SelectInfo.si_Flags |= SI_NAME;
					strcpy(SelectInfo.si_PatTok, Buffer);
					rc = ProcessEntryPattern(AP, &SelectInfo, &Opt, StartPath, "#?", FileData, ArgV[ARG_CMD]);
				}
				else {
					BPTR L;

					switch (IsWild) {
					case 1:
						/*
						 *	Pattern
						 *	Dir1/Dir2/Pattern
						 *	Vol:Dir1/Dir2/Pattern
						 */
						rc = ProcessEntryPattern(AP, &SelectInfo, &Opt, StartPath, Elmt, FileData, ArgV[ARG_CMD]);
						break;
					case 0:
						if (L = Lock(Elmt, SHARED_LOCK)) {
							long ok;

							ok = Examine(L, fib);
							UnLock(L);	// UnLock so that command can open file for writing
							if (ok) {
								if (ArgV[ARG_ALL] && fib->fib_DirEntryType >= 0) {	// This is a dir
									strcpy(Buffer, Elmt);
									AddPart(Buffer, "#?", MAX_PATHLEN);
									rc = ProcessEntryPattern(AP, &SelectInfo, &Opt, StartPath, Buffer, FileData, ArgV[ARG_CMD]);
								}
								else
									rc = ProcessEntry(&SelectInfo, fib, StartPath, Elmt, FileData, &Opt, ArgV[ARG_CMD]);
							}
							else {
								PrintFault(IoErr(), Elmt);
								rc = RETURN_FAIL;
							}
						}
						else if (ArgV[ARG_CMD])		// Arg does not exists, allow:  For 1 2 3 DO Echo %%
							rc = ExecuteCmd(ArgV[ARG_CMD], StartPath, Elmt, &Opt);
						else							
							rc = RETURN_WARN;
						break;
					default:
						PrintFault(IoErr(), Elmt);
						rc = RETURN_FAIL;
					}
				}
			}
			FreeVec(AP);
		}
		FreeVec(FileData);
	}
	else if (rc == 21)
		rc = RETURN_FAIL;
	else
		PrintFault(ERROR_NO_FREE_STORE, "For");
	if (fib)
		FreeVec(fib);
	if (Buffer)
		FreeVec(Buffer);
	if (StartPath)
		FreeVec(StartPath);
	if (WhatIsBase)
		CloseLibrary(WhatIsBase);
	return rc;
}

