#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/rdargs.h>
#include <dos/dosasl.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <support/types.h>
#include <support/exec.h>
#include <support/dos.h>

#include <string.h>

#include <proto/exec.h>
#include <proto/dos.h>

#include "for.rev.h"

#define DOS_NAME		"dos.library"
#define DOS_VERN 		37L

#define TEMPLATE		"FILE/M,DO/K/F,ALL/S,NOQUOTE=NQ/S"

#define DEF_COMM		"%S"

STATIC CONST TEXT VersionString[]=
	VERSION(PROG_NAME,PROG_VERSION,PROG_REVISION,PROG_DATE);

#define OPT_FILE		0
#define OPT_DO			1
#define OPT_ALL			2
#define OPT_NQ			3
#define OPT_COUNT		4

#define NAME_SIZE		32			/* File's name */
#define COMM_SIZE		1024		/* Unexpanded command */
#define PRSD_SIZE		1024		/* Expanded command */
#define PATH_SIZE		256			/* For file's path */

#define CommString	(Opts[OPT_DO] ? (STRPTR)Opts[OPT_DO] : RawComm)

STATIC BOOL PrepComm(STRPTR format, STRPTR buffer, ULONG buffSize,
											STRPTR name, STRPTR path, BOOL quote);
STATIC BOOL CopyComm(STRPTR *args, STRPTR buffer, ULONG buffSize);

ULONG For(VOID)
{
	struct ExecBase *SysBase=*((struct ExecBase **)4);
	struct DosLibrary *DOSBase;

	STATIC CONST struct TagItem CommandTags[]=
	{
		{SYS_UserShell,TRUE},
		{TAG_DONE,0}
	};
	STATIC CONST STRPTR DefFile[]={"#?",NULL};

	struct RDArgs *Args;
	struct AnchorPath *Anchor;
	LONG Opts[OPT_COUNT];
	ULONG I,TempRC,RC=RETURN_FAIL;
	CHAR FName[NAME_SIZE];
	STRPTR RawComm,ParsedComm,CurArg,*ArgPtr;
	BPTR CurrLock,OldLock;
	BOOL ErrOccured=FALSE,MultiFile=TRUE;

	unless(DOSBase=(struct DosLibrary *)OpenLibrary(DOS_NAME,DOS_VERN))
	{
		SetResult2(ERROR_INVALID_RESIDENT_LIBRARY);
		goto InvalidDOS;
	}

	unless(Anchor=AllocVec(sizeof(struct AnchorPath)+PATH_SIZE,MEMF_CLEAR))
	{
		PrintFault(IoErr(),NULL);								/* Inform user */
		goto NoMemory1;
	}
	Anchor->ap_BreakBits=SIGBREAKF_CTRL_C;		/* Allow break */
	Anchor->ap_Strlen=PATH_SIZE;

	unless(RawComm=AllocVec(COMM_SIZE+PRSD_SIZE,MEMF_CLEAR))
	{
		PrintFault(IoErr(),NULL);								/* Inform user */
		goto NoMemory2;
	}
	ParsedComm=RawComm+COMM_SIZE;

	clear(&Opts);
	unless(Args=ReadArgs(TEMPLATE,Opts,NULL))
	{
		PrintFault(IoErr(),NULL);								/* Inform user */
		goto NoArgs;
	}
	ArgPtr=Opts[OPT_FILE] ? (STRPTR *)Opts[OPT_FILE] : DefFile;

	unless(Opts[OPT_DO])												/* 'DO' not specified, */
	{																						/* This is <file>+<command> */
		if(ArgPtr[1])															/* Only one file patern specified */
		{
			unless(CopyComm(ArgPtr,RawComm,COMM_SIZE))	/* Copy patterns to command */
			{
				CauseIoErr(ERROR_LINE_TOO_LONG,NULL);
				goto Exit;
			}
			MultiFile=FALSE;												/* There is only one file */
		}
		else																			/* Multiple patterns */
			stpcpy(RawComm,DEF_COMM);								/* Set command to "run" */
	}

	for(I=0; (CurArg=*ArgPtr++) && (I==0 || MultiFile); I++)
	{
		clear(Anchor);
		Anchor->ap_BreakBits=SIGBREAKF_CTRL_C;		/* Allow break */
		Anchor->ap_Strlen=PATH_SIZE;							/* We have a path buffer */
		TempRC=MatchFirst(CurArg,Anchor);
		while(TempRC==0)
		{
			stpcpy(FName,Anchor->ap_Info.fib_FileName);
			if(Anchor->ap_Info.fib_DirEntryType>=0)
			{
				if(Opts[OPT_ALL])
				{
					if(!ftst(Anchor->ap_Flags,APF_DIDDIR))
						fset(Anchor->ap_Flags,APF_DODIR);
					fclr(Anchor->ap_Flags,APF_DIDDIR);
				}
				goto Next;	
			}
			CurrLock=DupLock(Anchor->ap_Current->an_Lock);
			OldLock=CurrentDir(CurrLock);
			if(PrepComm(CommString,ParsedComm,PRSD_SIZE,
									FName,Anchor->ap_Buf,!Opts[OPT_NQ]))
			{
				if(System(ParsedComm,CommandTags))	/* An error */
				{
					ErrOccured=TRUE;
					PrintFault(IoErr(),FName);
				}
			}
			else
			{
				ErrOccured=TRUE;
				CauseIoErr(ERROR_LINE_TOO_LONG,NULL);
			}
			CurrentDir(OldLock);
			UnLock(CurrLock);
Next:
			TempRC=MatchNext(Anchor);
		}
		if(TempRC==ERROR_NO_MORE_ENTRIES)
			RC=(ErrOccured ? RETURN_ERROR : RETURN_OK);
		else
		{
			PrintFault(TempRC,NULL);
			RC=(TempRC==ERROR_BREAK ? RETURN_WARN : RETURN_FAIL);
		}
		MatchEnd(Anchor);
		if(TempRC==ERROR_BREAK)
			break;
	}

Exit:
	FreeArgs(Args);
NoArgs:
	FreeVec(RawComm);
NoMemory2:
	FreeVec(Anchor);
NoMemory1:
	CloseLibrary((struct Library *)DOSBase);
InvalidDOS:
	return(RC);
}

STATIC BOOL PrepComm(STRPTR format, STRPTR buffer, ULONG buffSize,
											STRPTR name, STRPTR path, BOOL quote)
{
	ULONG NameLen=strlen(name),PathLen=strlen(path),Len;
	CHAR CurrChar;
	STRPTR Arg,HighBuffer=buffer+buffSize-1;

	memset(buffer,0,buffSize);		/* Clear buffer */
	while(CurrChar=*format++)
	{
		if(buffer<HighBuffer)				/* The char will fit */
			*buffer=CurrChar;
		else
			return(FALSE);

		if(CurrChar=='%')
		{
			switch(*format)
			{
				case 's':
				case 'S':
					Len=NameLen;
					Arg=name;
					break;
				case 'p':
				case 'P':
					Len=PathLen;
					Arg=path;
					break;
				case '%':
					format++;
				default:
					buffer++;
					continue;
			}
			if(buffer+Len+(quote ? 2 : 0)<HighBuffer)
			{
				if(quote)
					*buffer++='"';
				buffer=stpcpy(buffer,Arg);	/* Insert arg */
				if(quote)
					*buffer++='"';
				format++;										/* Skip control char */
			}
			else
				return(FALSE);
		}
		else
			buffer++;
	}
	return(TRUE);
}

STATIC BOOL CopyComm(STRPTR *args, STRPTR buffer, ULONG buffSize)
{
	ULONG I;
	STRPTR Arg,HighBuffer=buffer+buffSize-1;
	
	memset(buffer,0,buffSize);						/* Clear buffer */
	for(I=1; Arg=args[I]; I++)						/* While there is an arg to add */
		if(buffer+strlen(Arg)+1<HighBuffer)	/* This arg will fit */
		{
			if(I>1)														/* Not first arg */
				*buffer++=' ';									/* Add one separating space */
			buffer=stpcpy(buffer,Arg);				/* Copy this arg */
		}
		else
			return(FALSE);
	return(TRUE);
}
