/*
 *	Copyright (C) 1989 Andrew Kemmis
 *
 *	All Rights Reserved
 *
 *	Date:	Mar 1989
 */
#include <ak/stdak.h>

char	Program_name[]	= "Find";
int	Version 	= 2;
int	MinorVer	= 3;
char	Date[]		= "Dec 1993";
char	CRDate[]	= "1989-93";

#include <ak/err.h>
#include <ak/pool.h>
#include <ak/dev.h>
#include <ak/filesystem.h>
#include <intuition/intuition.h>

#define ULONGMAX	4294967295
#define STR_LEN 80

#include "find.qi"

extern	int	strlen();
extern	int	strcmp();
extern	int	sscanf();

int	cli;

struct	IBASE	*IBASE = (struct IBASE *)NULL;

Static	struct	AkDevStuff	Stuff = DEVSTUFFINIT;
Static	uchar	*Buffer;
Static	uchar	*NameBuffer;
Static	long	Dev = -1;

Static	struct	PoolKey BufPoolKey;

Static	ulong	ReadCount = 0;
Static	uchar	*FName;
Static	char	AString2[STR_LEN+1] = "";

Static	int	Match = 0;
Static	int	NameLen;
Static	int	DoAnyDat;
Static	int	DoAnyRangeDat;
Static	int	DoULong;
Static	int	AStringLen;

Static Copy(t, s, l)
REG	char	*t;
REG	char	*s;
REG	int	l;
{
	t += l;
	s += l;
	while (l--)
		*(--t) = *(--s);
}

#define OLD_NAM_LEN	2048

Static LONG CacheRead();

Static char *GetName(num, buf, type)
REG	ulong	num;
REG	ulong	*buf;
char	**type;
{
	static	ulong	old_num = ULONGMAX;
	static	char	old_nam[OLD_NAM_LEN+1] = " ";
	ulong	*Buf;
	REG	char	*ptr = old_nam + 1;
	REG	ulong	new_num = num;
	REG	LONG	ret;
	REG	int	done;
	uchar	*FName;
	REG	int	len = 0;
	int	fnlen;
	ulong	typ;
	ulong	sec_typ;

	Buf = buf;

	typ = Buf[POS_TYP];
	sec_typ = Buf[POS_SEC];
	switch(typ)
	{
	case TYP_CONTROL:
		switch(sec_typ)
		{
		case SEC_ROOT:
			*type = "Root";
			break;
		case SEC_DIR:
			*type = "Directory";
			break;
		case SEC_FIL:
			*type = "File";
			break;
		case SEC_S_LNK:
			*type = "Soft Link";
			break;
		case SEC_H_LNK_DIR:
			*type = "Hard Dir Link";
			break;
		case SEC_H_LNK_FIL:
			*type = "Hard File Link";
			break;
		default:
			*type = "Control???";
			break;
		}
		break;
	case TYP_EXT:
		new_num = Buf[POS_PAR];
		*type = "Extension";
		break;
	case TYP_DAT:
		new_num = Buf[POS_KEY];
		*type = "Data";
		break;
	default:
		*type = "";
		return("(Unknown block type)");
	}

	if (new_num == old_num)
		return(old_nam);

	done = 0;
	while (!done)
	{	if (new_num < Stuff.Info.res || new_num > (Stuff.Info.End - Stuff.Info.Begin))
		{	FName = (uchar *)"\022***InvalidBlock***";
			done = !0;
		}
		else
		{	if (new_num != num)
			{	Buf = (ulong *)NameBuffer;
				ret = AkDevRead(new_num + Stuff.Info.Begin, Buf, &Stuff, BLOCK_SIZE);
			}
			else
			{	Buf = buf;
				ret = 0;
			}

			if (ret)
			{	FName = (uchar *)"\016***BadBlock***";
				done = !0;
			}
			else
			{	FName = (uchar *)(&Buf[POS_NAME]);

				if (*FName == 0 || *FName > BLOCKFILENAMELENGTH)
				{	FName = (uchar *)"\021***InvalidName***";
					done = !0;
				}
				else
				{	FName[*FName+1] = '\0';
					if (*FName != strlen(FName+1))
						FName = (uchar *)"\022*NameContainsNull*";
				}
			}
		}

		fnlen = *FName;

		if ((len + fnlen + 1) > OLD_NAM_LEN)
		{	Copy(ptr+3+1, ptr, len);
			Copy(ptr, "...", 3);
			ptr[3] = '//';
			done = !0;
		}
		else
		{	if (len)
				Copy(ptr+fnlen+1, ptr, len);
			Copy(ptr, FName+1, fnlen);

			if (new_num == Stuff.Info.Root)
			{	ptr[fnlen] = ':';
				if (len == 0)
					ptr[fnlen + 1] = '\0';
				done = !0;
			}
			else
			{	if (len == 0)
					ptr[fnlen] = '\0';
				else
					ptr[fnlen] = '/';
				new_num = Buf[POS_PAR];

				len += (fnlen + 1);
			}
		}
	}
	return(old_nam);
}

Static	char	one_buf[20+1];

Static GotOne(num, str, Buf)
REG	ulong	num;
REG	char	*str;
REG	ulong	*Buf;
{
	REG	char	*FName;
	char	*Type;

	num -= Stuff.Info.Begin;

	if (CheckChecksum)
		if (DosChecksum(Buf))
			return;

	if (FileNames)
		FName = GetName(num, Buf, &Type);
	else
		FName = "";

	printf("Match: %20.20s Block Offset: 0x%08lx, (%lu)%s%s%s\n",
		str, num, num, *Type ? " Type: " : "", Type, FName);
	Match++;
}

Static LONG CacheRead(num, buf)
REG	ulong	num;
REG	ulong	**buf;
{
	static	ulong	cache0 = -1;
	static	ulong	cache1 = -1;
	REG	LONG	ret = 0;

	*buf = (ulong *)Buffer;

	if (Cache == 1)
		ret = AkDevRead(num, *buf, &Stuff, BLOCK_SIZE);
	else
	{	if (cache0 <= num && num <= cache1)
			*buf = (ulong *)&Buffer[BLOCK_SIZE*(num - cache0)];
		else
		{	cache0 = num;
			cache1 = num + Cache - 1;
			if (cache1 > Stuff.Info.End)
				cache1 = Stuff.Info.End;
			ret = AkDevRead(num, *buf, &Stuff, BLOCK_SIZE*(1 + cache1 - cache0));
			if (ret)
				cache0 = cache1 = -1;
		}
	}
	return(ret);
}

Static Try(num)
REG	ulong	num;
{
	REG	ulong	*Buf;
	REG	ulong	i;
	REG	ulong	*l;
	REG	char	*ch;
	ulong	*buf;
	REG	int	j;
	REG	int	done;

	if (ShowReads && (++ReadCount % Count) == 0)
	{	akdeco(ReadCount, 'l', NULL, NULL, NULL, ' ', ' ');
		akhexo(num, 'l', NULL, NULL, NULL, '\r', '0');
	}
	if (Tracks && (ReadCount++ % Stuff.Info.Cyl) == 0)
		akdeco(ReadCount / Stuff.Info.Cyl, 'l', NULL, NULL, NULL, '\r', ' ');

	CacheRead(num, &buf);
	Buf = buf;

	if (*Name)
	{	FName = (uchar *)(&Buf[POS_NAME]);
		if (ICase)
		{	if (AkStrNCmpCase(FName+1, Name, NameLen) == 0)
				GotOne(num, "Name", Buf);
		}
		else
		{	if (AkStrNCmp(FName+1, Name, NameLen) == 0)
				GotOne(num, "Name", Buf);
		}
	}

	if (Parent != ULONGMAX)
	{	if (Parent == Buf[POS_PAR])
			GotOne(num, "Parent", Buf);
	}

	if (Key != ULONGMAX)
	{	if (Key == Buf[POS_KEY])
			GotOne(num, "Key", Buf);
	}

	if (HashChain != ULONGMAX)
	{	if (HashChain == Buf[POS_HASH_CH])
			GotOne(num, "HashChain", Buf);
	}

	if (Extension != ULONGMAX)
	{	if (Extension == Buf[POS_EXT])
			GotOne(num, "Extension", Buf);
	}

	if (SequenceNum != ULONGMAX)
	{	if (SequenceNum == Buf[POS_D_SEQ_NUM])
			GotOne(num, "SequenceNum", Buf);
	}

	if (ByteSize != ULONGMAX)
	{	if (ByteSize == Buf[POS_F_BYTSIZ])
			GotOne(num, "ByteSize", Buf);
	}

	if (BlockCount != ULONGMAX)
	{	if (BlockCount == Buf[POS_NUM_BLK])
			GotOne(num, "BlockCount", Buf);
	}

	if (FirstDataBlk != ULONGMAX)
	{	if (FirstDataBlk == Buf[POS_FST_DAT])
			GotOne(num, "FirstDataBlk(A)", Buf);

		if (FirstDataBlk == Buf[POS_FST_BLK])
			GotOne(num, "FirstDataBlk(B)", Buf);
	}

	if (DoAnyDat || DoAnyRangeDat)
		if (!ValidOnly || Buf[POS_TYP] == TYP_EXT || Buf[POS_TYP] == TYP_CONTROL)
		{	done = 0;
			l = &(Buf[POS_FST_BLK]);
			for (i = POS_FST_BLK; !done && i <= POS_LST_BLK; l++, i++)
			{	if (DoAnyRangeDat)
					for (j = 0; !done && j < DoAnyRangeDat; j+=2)
						if (AnyRangeData[j] <= *l && *l <= AnyRangeData[j+1])
						{	sprintf(one_buf, "AnyRangeD 0x%08lx", *l);
							GotOne(num, one_buf, Buf);
							done = Once;
						}

				if (!done && DoAnyDat)
					for (j = 0; !done && j < DoAnyDat; j++)
						if (AnyDataBlk[j] == *l)
						{	sprintf(one_buf, "AnyD 0x%08lx", *l);
							GotOne(num, one_buf, Buf);
							done = Once;
						}
			}
		}

	if (NextDataBlk != ULONGMAX)
	{	if (NextDataBlk == Buf[POS_D_NXT_DAT])
			GotOne(num, "NextDataBlk", Buf);
	}

	if (DoULong)
		if (!ValidOnly || Buf[POS_TYP] == TYP_EXT || Buf[POS_TYP] == TYP_CONTROL)
		{	done = 0;
			for (j = 0; !done && j < DoULong; j++)
			{	l = Buf;
				for (i = 0; !done && i < LBlockSize; l++, i++)
					if (ULong[j] == *l)
					{	sprintf(one_buf, "ULong 0x%08lx", *l);
						GotOne(num, one_buf, Buf);
						done = Once;
					}
			}
		}

	if (*AString2)
	{	ch = (char *)Buf;
		for (i = 1 + BLOCK_SIZE - AStringLen; i > 0; i--, ch++)         /* i is unsigned! */
			if (ICase)
			{	if ((toupper(*ch) == toupper(*AString2))
				&&  (AkStrNCmpCase(ch, AString2, AStringLen) == 0))
				{	GotOne(num, AString, Buf);
					break;
				}
			}
			else
			{	if (*ch == *AString2 && AkStrNCmp(ch, AString2, AStringLen) == 0)
				{	GotOne(num, AString, Buf);
					break;
				}
			}
	}
}

Static CnvStr()
{
	REG	char	*ptr1;
	REG	char	*ptr2;
	uint	n;

	ptr1 = AString;
	ptr2 = AString2;

	while (*ptr1)
	{	if (*ptr1 == '\\')
			switch(*(++ptr1))
			{
			case '\\':
				*(ptr2++) = *(ptr1++);
				break;
			case 'b':
				*(ptr2++) = '\b';
				ptr1++;
				break;
			case 'f':
				*(ptr2++) = '\f';
				ptr1++;
				break;
			case 'n':
				*(ptr2++) = '\n';
				ptr1++;
				break;
			case 'r':
				*(ptr2++) = '\r';
				ptr1++;
				break;
			case 't':
				*(ptr2++) = '\t';
				ptr1++;
				break;
			case 'x':
			case '0':
			case '1':
			case '2':
				n = 0;
				if (*ptr1 == 'x')
					sscanf(++ptr1, "%2x", &n);
				else
					sscanf(ptr1++, "%3o", &n);
				*(ptr2++) = n;
				if (*ptr1)
					ptr1++;
				if (*ptr1)
					ptr1++;
				break;
			case '\0':
				*(ptr2++) = '\\';
				break;
			default:
				*(ptr2++) = '\\';
				*(ptr2++) = *(ptr1++);
				break;
			}
		else
			*(ptr2++) = *(ptr1++);

	}
	*ptr2 = '\0';
}

Static	Search()
{
	REG	ulong	l;

	if (*AString)
	{	CnvStr();
		AStringLen = strlen(AString2);
	}

	if (*Name)
		NameLen = strlen(Name);

	DoAnyDat = QL_IDX(Q_ANYDATABLK);

	DoAnyRangeDat = (QL_IDX(Q_ANYRANGEDATA) & 0xffe); /* Even */

	DoULong = QL_IDX(Q_ULONG);

	FromBlock += Stuff.Info.Begin;
	ToBlock += Stuff.Info.Begin;

	if (FromBlock > Stuff.Info.End)
		FromBlock = Stuff.Info.End;

	if (ToBlock > Stuff.Info.End)
		ToBlock = Stuff.Info.End;

	ReadCount = FromBlock - Stuff.Info.Begin;

	AkMotorOn(&Stuff);
	for (l = FromBlock; l <= ToBlock; l++)
		Try(l);
	AkMotorOff(&Stuff);

	printf("Total %d match%s found on %s from %ld to %ld\n",
		Match,
		(Match == 1) ? "" : "es",
		Device,
		FromBlock - Stuff.Info.Begin,
		ToBlock - Stuff.Info.Begin);
}

Static	Find()
{
	if (Dev = AkDevOpen(Device, &Stuff))
		MyError(Dev, Device);

	MPAlloc(Buffer, (long)(BLOCK_SIZE)*Cache, &BufPoolKey);
	if (FileNames)
		MPAlloc(NameBuffer, (long)(BLOCK_SIZE), &BufPoolKey);

	AkDevMsg(&(Stuff.Info));

	Search();
}

main(argc, argv)
int	argc;
char	*argv[];
{
	cli = argc;

	DO_QUAL();

	if (!Quiet && cli)
		STARTUP_MSG;

	OPEN_LIB(IBASE, "intuition.library", INTUITION_REV);

	PoolInit(&BufPoolKey, 0L, MEMF_CHIP);

	Find();

	Cleanup();
}

MyError(num, why)
REG	long	num;
REG	char	*why;
{
	ERROR_INIT

	if (!Dev)
		AkDevClose(&Stuff);

	ERROR_TEST
		CASE_ERR_AKDEVALL
		CASE_ERR_NOVAL
		CASE_ERR_NOMEM
		CASE_ERR_NOLIB
		CASE_ERR_BADFIL
	ERROR_END(FACILITY_FIND)

	PoolFree(&BufPoolKey);

	if (IBASE)
		CloseLibrary(IBASE);

	CLEANUP_QUAL();

	ERROR_EXIT
}
