/* WhereIs 1.1 ***************************************************************
*
*	A program to find the location of a file on each mounted volume.
*
*	Written by:	Olaf 'Olsen' Barthel, ED Electronic Design Hannover
*			Brabeckstrasse 35
*			3000 Hannover 71
*
*			Federal Republic of Germany
*
*	The code of this program was "brought together" from various
*	sources, that's why I do not claim the copyright for the
*	whole program.
*
*	GetCD():	Was adapted from a similar routine in Matt
*			Dillon's shell ("comm2.c").
*
*	OpenVolList(),
*	ReadVolList():	Were adapted from support routines for the STDFile
*			standard file requester written by Peter da Silva.
*
*	CmplPat(),
*	Match():	Are part of pattern matching routines by Jeff Lydiatt
*			who translated them from BCPL.
*
*****************************************************************************/

#include <libraries/dosextens.h>
#include <exec/memory.h>
#include <functions.h>
#include <stdio.h>

	/* #defines from VolList.c */

#define toAPTR(b) ((b)<<2)
#define toBPTR(a) ((a)>>2)

struct DeviceList *list;

	/* Pattern & auxiliary buffer for pattern matching */

char *FileName;
WORD Aux[128];

	/* Matches encountered */

long MatchFound = 0;

	/* User hit CTRL-C, awaiting doom. */

long HitAndQuit = FALSE;

	/* These little "quickies" disable the CTRL-C abort function.
	 * They work on the Aztec 'C' only and were borrowed from
	 * Matt Dillon's Shell.
	 */

BreakCheck()
{
	return(SetSignal(0,0) & SIGBREAKF_CTRL_C);
}

BreakReset()
{
	SetSignal(0,SIGBREAKF_CTRL_C);
}

Chk_Abort()
{
	return(0);
}

	/* GetCD() :
	 *
	 *	Returns a pointer to a string with the current
	 *	pathname (including ":" and "/").
	 */

char *
GetCD()
{
	static char cwd[256];
	struct FileLock *lock,*newlock;
	struct FileInfoBlock *fib;
	register long i,len;
	register char *name;

	fib = (struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC);

		/* Find the current directory for this process */

	newlock = (struct FileLock *)(((struct Process *)FindTask(NULL)) -> pr_CurrentDir);

	lock = (struct FileLock *)DupLock(newlock);

	for(i = 0 ; i < 256 ; i++)
		cwd[i] = 0;

		/* Follow the thread */
      
	while(lock)
	{
		newlock = (struct FileLock *)ParentDir(lock);
		Examine(lock, fib);

		name = fib -> fib_FileName;

			/* Hack to fix bug in RAM: driver, should
			 * be fixed in 1.3 release.
			 */

		if(*name == 0)
			name = "RAM";

		len = strlen(name);

			/* Add directory path, Aztec 'C' only. */

		if(newlock)
		{
			if(i == 255)
			{
				i -= len;
				movmem(name, cwd + i, len);
			}
			else
			{
				i -= len + 1;
				movmem(name, cwd + i, len);
				cwd[i+len] = '/';
			}
		}
		else
		{
			i -= len + 1;
			movmem(name, cwd + i, len);
			cwd[i+len] = ':';
		}

		UnLock(lock);
		lock = newlock;
	}

		/* Return memory to public pool & append "/" if
		 * necessary.
		 */

	FreeMem(fib,sizeof(struct FileInfoBlock));
	movmem(cwd + i, cwd, 256 - i);

	if(cwd[strlen(cwd) - 1] != ':' && cwd[strlen(cwd) - 1] != '/')
		strcat(cwd,"/");

	return(&cwd[0]);
}

	/* OpenVolList() :
	 *
	 *	Set up the first volume entry in the DeviceList.
	 */

void
OpenVolList()
{
	extern struct DosLibrary *DOSBase;
	struct RootNode *root;
	struct DosInfo *info;

		/* For further information on this technique, consult
		 * the AmigaDOS technical manual, chapter 11.3.
		 */

	root = (struct RootNode *)DOSBase -> dl_Root;
	info = (struct DosInfo *)toAPTR(root->rn_Info);
	list = (struct DeviceList *)toAPTR(info->di_DevInfo);

	return;
}

	/* ReadVolList() :
	 *
	 *	Returns a pointer to the name of the next volume
	 *	entry in the DeviceList.
	 */

char *
ReadVolList()
{
	register struct DeviceList *next;
	static BOOL GotList = FALSE;
	static char name[64];

		/* (Re-)Initialize DeviceList */

	if(!GotList)
	{
		OpenVolList();
		GotList = TRUE;
	}

		/* Scan the list for the next entry */

	while(list)
	{
		next = (struct DeviceList *)toAPTR(list -> dl_Next);

			/* Is Device a volume? */

		if(list -> dl_Type == DLT_VOLUME)
		{
			register char *ptr;
			register int count;

				/* Get the volume name */

			ptr = (char *)toAPTR((BPTR)list -> dl_Name);

			count = *ptr++;

			if(count > 62)
				count = 62;

				/* Convert 'count' bytes of BSTR to CSTR */

			strncpy(name, ptr, count);

			name[count++] = ':';
			name[count] = 0;
			list = next;

				/* Return pointer to 'C' string */

			return(&name[0]);

		}

		list = next;
	}

		/* Last entry skipped */

	GotList = FALSE;
	return(NULL);
}

	/* Look4File(FileLock) :
	 *
	 *	Accepts a Lock to a directory and examines each file
	 *	and subdirectory to be found in it.
	 */

BOOL
Look4File(FileLock)
struct FileLock *FileLock;
{
	struct FileInfoBlock	*FileInfo;
	struct FileLock		*OldLock,*NewLock;
	register long		Successful,SkipDir = TRUE;
	register char		*CD;

	if(!FileLock)
		return(FALSE);

	if(!(FileInfo = (struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR | MEMF_PUBLIC)))
		return(FALSE);

		/* Set up current directory name */

	CD = GetCD();

		/* Examine the first entry */

	Successful = Examine(FileLock,FileInfo);

	while(Successful)
	{
			/* "Just say where we are..." */

		if(FileInfo -> fib_DirEntryType > 0)
			printf("Examining DIR  ");
		else
			printf("Examining FILE ");

		printf("%s\x9BK\n\x9BA",FileInfo -> fib_FileName);

			/* Since the first entry is a directory (source
			 * directory), we must prevent this routine
			 * from examining the source directory again
			 * and again (stack overflow).
			 */

		if(!SkipDir)
		{
				/* Entry is a directory, try to enter it. */

			if(FileInfo -> fib_DirEntryType > 0)
			{
				if(NewLock = Lock(FileInfo -> fib_FileName,ACCESS_READ))
				{
					OldLock = CurrentDir(NewLock);

					Look4File(NewLock);

					CurrentDir(OldLock);
				}
				else
					return(FALSE);
			}
		}

			/* Allow directories for next step */

		SkipDir = FALSE;

			/* Hit CTRL-C? */

		if(BreakCheck())
			HitAndQuit = TRUE;

		if(HitAndQuit)
			break;

			/* Examine next entry */

		Successful = ExNext(FileLock,FileInfo);

			/* Found an entry? */

		if(Successful)
		{
				/* Entry is a file, examine it. */

			if(FileInfo -> fib_DirEntryType < 0)
			{
				if(Match(FileName,Aux,FileInfo -> fib_FileName))
				{
					printf("Found: \33[33m%s%s\33[31m\x9BK\n",GetCD(),FileInfo -> fib_FileName);
					MatchFound++;
				}
			}
		}
	}

		/* Return Lock to AmigaDOS, return memory to public pool. */

	if(FileLock)
		UnLock(FileLock);

	if(FileInfo)
		FreeMem(FileInfo,sizeof(struct FileInfoBlock));

		/* "... then go back to the car!" */

	return(TRUE);
}

void
main(argc,argv)
long argc;
char *argv[];
{
	register struct FileLock *VolumeLock,*Current = NULL;
	register char *VolumeName;
	register short i;

		/* Started via Workbench? */

	if(!argc)
		exit(RETURN_FAIL);

		/* Modify program name */

	for(i = 0 ; i < strlen(argv[0]) ; i++)
		argv[0][i] = toupper(argv[0][i]);

		/* Long info */

	if(argv[1][0] == '?')
	{
		printf("\n\33[33m\33[1m%s\33[0m\33[31m version 1.1, 29-Apr-89\n\n",argv[0]);

		printf("\t\33[3mWritten by Olaf 'Olsen' Barthel of ED Electronic Design Hannover\33[0m\n\n");

		printf("\tSimilar to \33[33mMS-DOS\33[31m \"WhereIs\" \33[33mAmiga\33[31m \"%s\" scans\n",argv[0]);
		printf("\tthe list of mounted volumes and checks the\n");
		printf("\tcontents of each root/subdirectory for\n");
		printf("\tfiles matching a file pattern.\n\n");

		printf("\tThis program accepts regular \33[33mAmigaDOS\33[31m wildcard\n");
		printf("\tpatterns.\n\n");

		printf("Usage is: \33[1m%s\33[0m [Directory] <Filename>\n\n",argv[0],argv[0]);

		exit(RETURN_OK);
	}

		/* Short info */

	if(argc < 2)
	{
		printf("Usage is: \33[1m%s\33[0m [Directory] <File>, type \33[33m\"%s ?\"\33[31m for help.\n",argv[0],argv[0]);
		exit(RETURN_OK);
	}

		/* Too many arguments */

	if(argc > 3)
		printf("Input line \33[33mtruncated\33[31m.\n");

		/* Set pattern */

	if(argc == 2)
		FileName = argv[1];
	else
		FileName = argv[2];

		/* Check for illegal patterns */

	if(!CmplPat(FileName,Aux))
	{
		printf("\33[33m%s:\33[31m Pattern \33[33m\"%s\"\33[31m is not a regular expression pattern.\7\n",argv[0],argv[1]);
		exit(RETURN_ERROR);
	}

		/* Start hunting */

	printf("\n\tThis is \33[33m\"%s\"\33[31m version 1.1, please wait...\n\n\x9B0 p",argv[0]);

		/* User wants only one single directory to be scanned */

	if(argc > 2)
	{
		if(VolumeLock = Lock(argv[1],ACCESS_READ))
		{
			Current = CurrentDir(VolumeLock);

			if(!Look4File(VolumeLock))
			{
				if(Current)
					CurrentDir(Current);

				UnLock(VolumeLock);

				printf("\x9BM\33[33m%s:\33[31m Error while reading.\7\n\x9B p",argv[0]);

				exit(RETURN_ERROR);
			}
		}
		else
		{
			printf("\x9BM\33[33m%s:\33[31m Error trying to lock \33[33m\"%s\"\33[31m.\7\n\x9B p",argv[0],argv[1]);
			exit(RETURN_ERROR);
		}
	}
	else
	{
			/* User wants us to scan the whole DeviceList */

		while(VolumeName = ReadVolList())
		{
			if(VolumeLock = Lock(VolumeName,ACCESS_READ))
			{
				if(!Current)
					Current = CurrentDir(VolumeLock);
				else
					CurrentDir(VolumeLock);

				if(!Look4File(VolumeLock))
				{
					if(Current)
						CurrentDir(Current);

					UnLock(VolumeLock);

					printf("\x9BM\33[33m%s:\33[31m Error while reading.\7\n\x9B p",argv[0]);

					exit(RETURN_ERROR);
				}
			}
			else
			{
				if(Current)
					CurrentDir(Current);

				printf("\x9BM\33[33m%s:\33[31m Error trying to lock \33[33m\"%s\"\33[31m.\7\n\x9B p",argv[0],VolumeName);

				exit(RETURN_ERROR);
			}
		}
	}

	if(HitAndQuit)
	{
		printf("\x9BM*** You \33[33maborted\33[31m \33[1m%s\33[0m.\7\n\x9B p",argv[0]);

		BreakReset();

		if(Current)
			CurrentDir(Current);

		exit(RETURN_OK);
	}

		/* Statistics */

	if(!MatchFound)
		printf("\x9BM\tSorry, \33[33mNO\33[31m match found.\x9B p");
	else
		printf("\x9BM\n\t\33[33m%d\33[31m occurence(s) encountered.\x9B p",MatchFound);

	printf("\n\n");

		/* Restore current directory */

	if(Current)
		CurrentDir(Current);

		/* Quit... */

	exit(RETURN_OK);
}
