/*
          Sf - Search files in volumes or directories.

          Strictly based on the SF program by Andrea Suatoni published
          in MC-Microcomputer 86, June 1989. Little enhancements by
          Fabio Rossetti: resident support, NOCOL option.

          (c) 1989 by Fabio Rossetti

          To compile under Lattice C v5.0x use:

		lc -O -v -cus sf
		blink lib:cres.o sf.o to sf lib lib:a.lib lib:lc.lib lib:amiga.lib sc sd nd

*/

#include <exec/types.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <arpfunctions.h>
#include <string.h>
#include <libraries/arpbase.h>
#include <libraries/dosextens.h>

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

#define NO_ERROR 0
#define ERROR_NO_MEM -101

typedef
	struct
	{
	struct MinNode	dir_Node;
	LONG		PathLen;
	STRPTR		PathName;
	}
	DIR_ENTRY;

GLOBAL VOID (*_ONBREAK)();

UBYTE DateFormat = FORMAT_DOS;
struct Process *Pr;
struct ArpBase *ArpBase;

struct MinList DirList;

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


STRPTR StrUpper(Str)
REGISTER STRPTR Str;
{
	REGISTER STRPTR r = Str;

	do
	{
	*Str = Toupper(*Str);
	}

	while (*(++Str));
	return(r);
}

LONG AddDirEntry(PathNode,Dir)
REGISTER STRPTR	PathNode,
		Dir;
{
	REGISTER DIR_ENTRY *Entry;

	if ((Entry =
		(DIR_ENTRY*) AllocMem(sizeof(DIR_ENTRY), MEMF_CLEAR)) == NULL)
		return(ERROR_NO_MEM);
	Entry->PathLen = strlen(PathNode) + strlen(Dir) + 2;
	if((Entry->PathName =
		(STRPTR) AllocMem(Entry->PathLen, MEMF_CLEAR)) == NULL)
	{
	FreeMem(Entry, sizeof(DIR_ENTRY));
	return(ERROR_NO_MEM);
	}

	strcpy(Entry->PathName, PathNode);

	TackOn(Entry->PathName, Dir);

	AddTail((struct List *) &DirList, (struct Node *) Entry);
	return(0);
}

VOID RemDirEntry()
{

	REGISTER DIR_ENTRY *Entry;

	Entry = (DIR_ENTRY *) RemHead((struct List *) &DirList);

	FreeMem(Entry->PathName, Entry->PathLen);
	FreeMem((STRPTR) Entry, sizeof(DIR_ENTRY));
}

VOID FreeDirList()
{
	while (DirList.mlh_Head->mln_Succ)
		RemDirEntry();
}

VOID FindFile(ArgV)

STRPTR ArgV[];

#define	PATTERN	ArgV[0]
#define	FILES	ArgV[1]
#define	DIRS	ArgV[2]
#define	QUICK	ArgV[3]
#define	NOROOT	ArgV[4]
#define NOCOL	ArgV[5]

{

	REGISTER struct FileLock	*DirLock;
	REGISTER struct FileInfoBlock	*FileInfo;
	REGISTER STRPTR			CurrentPath;
	REGISTER LONG			Error;
	REGISTER BOOL			Found = FALSE,
					First,
					Dir;
		TEXT			Day	[12],
					Time	[10],
					Date	[10],
					StrToken[120],
					Path	[120];
		WORD			Tabs = 0;
		struct DateTime		DateTime;

	if ((FileInfo = (struct FileInfoBlock *)
		AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) != NULL)
	{
	DateTime.dat_Format = DateFormat;
	DateTime.dat_Flags = DTF_FUTURE;
	DateTime.dat_StrDay = Day;
	DateTime.dat_StrDate = Date;
	DateTime.dat_StrTime = Time;
	NewList((struct List *) &DirList);
	*Path = '\0';

	if (NOROOT == NULL)
		if (strchr(PATTERN, ':') == NULL)
			strcpy(Path, ":");
	strcat(Path, PATTERN);
	PreParse((CurrentPath = StrUpper(BaseName(Path))), StrToken);
	*CurrentPath = '\0';
	if ((DirLock = (struct FileLock *) Lock(Path, ACCESS_READ)) == NULL)
		Error = ERROR_INVALID_LOCK;
		else
		{
		PathName((BPTR) DirLock, Path, 10);
		UnLock((BPTR) DirLock);
		Error  = AddDirEntry(Path,"");
		}
	while (DirList.mlh_Head->mln_Succ)
		{
		CurrentPath = ((DIR_ENTRY *) DirList.mlh_Head)->PathName;
		if ((DirLock = (struct FileLock *) Lock(CurrentPath, ACCESS_READ)) ==
				(struct FileLock *) 0)
		{
		Error = ERROR_INVALID_LOCK;
		break;
		}
		First = TRUE;
		if (Examine((BPTR) DirLock, FileInfo))
			{
			while (ExNext((BPTR) DirLock, FileInfo))
				{
				if (Dir = (FileInfo->fib_DirEntryType >= 0))
					if ((Error = AddDirEntry(CurrentPath,
						FileInfo->fib_FileName)) != NO_ERROR)
					break;
				if (PatternMatch(StrToken,
					StrUpper(strcpy(Path, FileInfo->fib_FileName))) == TRUE)
					if (!(DIRS || FILES) ||
						(DIRS && Dir) ||
						(FILES && !Dir))
					{
					Found = TRUE;
					if (First)
					{
						if(Tabs)
						{
						Puts("");
						Tabs = 0;
						}
					if(NOCOL) Printf("\n\"%s\"\n",CurrentPath);
					else Printf("\n\033[33m\"%s\"\033[31m\n",
						CurrentPath);
					First = FALSE;
					}
				if (QUICK)
					{
					if (Dir)
						if (NOCOL) Printf("%s%-18s",(Tabs) ? "": "   ",
						FileInfo->fib_FileName);
						else Printf("\033[33m%s%-18s\033[31m",(Tabs) ? "": "   ",
						FileInfo->fib_FileName);
					else
					Printf("%s%-18s",(Tabs) ? "": "   ",
					FileInfo->fib_FileName);
					if (++Tabs == 4)
						{
						Tabs = 0;
						Puts("");
						}
					}
				else
				{
				DateTime.dat_Stamp.ds_Days = FileInfo->fib_Date.ds_Days;
				DateTime.dat_Stamp.ds_Minute= FileInfo->fib_Date.ds_Minute;
				DateTime.dat_Stamp.ds_Tick = FileInfo->fib_Date.ds_Tick;
				StamptoStr(&DateTime);
				Printf("    %-30s ", FileInfo->fib_FileName);
				if (Dir)
					Printf("  (dir)");
				else
					Printf("%7ld", FileInfo->fib_Size);
				Printf(" %-9s %9s %8s\n", Day, Date, Time);
				}
			}
		if (CheckBreak(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D,NULL))
			{
			Error = ERROR_BREAK;
			break;
			}
		}
		if (Error == NO_ERROR)
			Error = IoErr();
		}
		RemDirEntry();
		UnLock((BPTR) DirLock);
		if (Error != ERROR_NO_MORE_ENTRIES)
			break;
		}
	if (Tabs)
		Puts("");
	if (Error == ERROR_NO_MORE_ENTRIES && Found == TRUE)
		Error = NO_ERROR;
	FreeDirList();
	FreeMem((STRPTR) FileInfo, sizeof(struct FileInfoBlock));
	}
else
	Error  = ERROR_NO_MEM;
switch(Error)
	{
	case NO_ERROR:
		break;
	case ERROR_INVALID_LOCK:
		Puts("Invalid pattern or path name");
		break;
	case ERROR_NO_MEM:
		Puts("No memory");
		break;
	case ERROR_NO_MORE_ENTRIES:
		Puts("Search failed");
		break;
	case ERROR_OBJECT_NOT_FOUND:
		Puts("Object not found");
		break;
	case ERROR_BREAK:
		Puts("***Break");
		break;
	default:
		Printf("Error %ld\n", Error);
	}
}

VOID MemCleanup()
{
}

VOID Break()
{
	Cleanup(RETURN_WARN,NULL,"***Break");
}

VOID _main(Line)
REGISTER STRPTR Line;

#define MAX_ARG 6

{
	REGISTER LONG 	ArgC;
	STRPTR		Arg[MAX_ARG];
	TEXT		EnvBuf[2];
	_ONBREAK  = Break;

	Pr = (struct Process*)FindTask(NULL);
	
	if(!(ArpBase = (struct ArpBase*)OpenLibrary(ArpName,ArpVersion)))
			Cleanup(RETURN_FAIL,ERROR_INVALID_RESIDENT_LIBRARY,NULL);
	

	for (ArgC = 0; ArgC < MAX_ARG; ++ArgC)
		Arg[ArgC] = (STRPTR)NULL;
	while (*Line > ' ') ++Line;

	if ((ArgC = GADS(++Line,
			strlen(Line),
			"Usage: Sf PAT <PathName|Pattern> [FILES=F] [DIRS=D] [QUICK=Q] [NOROOT=NR] [NOCOL=NC]",
			Arg,
			"PAT/A,FILES=F/S,DIRS=D/S,QUICK=Q/S,NOROOT=NR/S,NOCOL=NC/S"))<= 0)
				Cleanup(RETURN_FAIL,ERROR_LINE_TOO_LONG,Arg[0]);

	if(Getenv("dateformat",EnvBuf,2))
	switch (EnvBuf[0]) {
		case '1':
		DateFormat = FORMAT_INT;
		break;
		case '2':
		DateFormat = FORMAT_USA;
		break;
		case '3':
		DateFormat = FORMAT_CDN;
		break;
	 };

	if (ArgC > 0)
		FindFile(Arg);

	Cleanup(NULL,NULL,NULL);
}