/************************************************/
/*	TABSIZE 4									*/
/************************************************/
/*	Suspect V1.0								*/
/************************************************/
/*	lc -O Suspect	BLink with Suspect.lnk		*/
/************************************************/
/*	show all suspected files or directories		*/
/*	recursively starting at a given directory	*/
/*	a file is suspected if						*/
/*		[1.f]	it cannot be opened				*/
/*		[2.f]	it cannot be read completely	*/
/*	a directory is suspected if					*/
/*		[1.d]	it is a file					*/
/*		[2.d]	it cannot be locked				*/
/*		[3.d]	it cannot be cd-ed to			*/
/************************************************/
/*		author:									*/
/*	Duponcheel Marc	© 1989						*/
/*	Varenlaan 13								*/
/*	B-2610 Wilrijk								*/
/*	--------------								*/
/*	BELGIUM										*/
/*	tel: (03) 830.48.27							*/
/************************************************/

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

#include	<exec/memory.h>
#include	<exec/libraries.h>
#include	<libraries/dos.h>

#define	VERSION " V1.0"

/*	ANSI console stuff	*/
#define	CSI	155

#define	BLACK_ON;\
PutChar(CSI);\
PutChar('2');\
PutChar('m');

#define	BLACK_OFF;\
PutChar(CSI);\
PutChar('0');\
PutChar('m');

#define	CURSOR_UP;\
PutChar(CSI);\
PutChar('A');

#define	CURSOR_OFF;\
PutChar(CSI);\
PutChar('0');\
PutChar(' ');\
PutChar('p');

#define	CURSOR_ON;\
PutChar(CSI);\
PutChar(' ');\
PutChar('p');

#define	ERASE_IN_LINE;\
PutChar(CSI);\
PutChar('K');

#define	CURSOR_FORWARD(column);\
PutChar(CSI);\
Printf((column));\
PutChar('C');

struct Library	*IntuitionBase;

static char	FileName[512] = "";
static int	BadFiles = 0;
static int	BadDirectories = 0;
static int	GoodFiles = 0;
static int	GoodDirectories = 0;
static int	Bytes = 0;
static ULONG	StartSeconds, StartMicros;
static ULONG	StopSeconds, StopMicros;

#define	BUFSIZE	512
static UBYTE	*buffer;
static int	bufsize = BUFSIZE;

static int	StrLen(
register char	*s)
{
	register int	l = 0;
	while(*s)
	{
		s++;
		l++;
	}
	return(l);
}

/*	for ItoA	*/
static char	s[32];

static void Reverse(
)
{
	register int	c, i, j;
	for(i = 0, j = StrLen(s) - 1; i < j; i++, j--)
	{
		c = s[i];
		s[i] = s[j];
		s[j] = c;
	}
}

/*	only for POSITIVE n	*/
static char	*ItoA(
register int	n)
{
	register int	i = 0;
	do
	{
		s[i++] = n % 10 + '0';
	}	while((n /= 10) > 0);
	s[i] = '\0';
	Reverse();
	return(s);
}
#define	BADINT	-1
/*	does not work for very long A's	*/
static int	AtoI(
register char	*s)
{
	register int	i = 0;
	while((*s >= '0') && (*s <= '9'))
		i = (10 * i) + *s++;
	if(*s || (i <= 0))
		return(BADINT);
	else
		return(i);
}

static int	PutChar(
char	c)
{
	return(Write(Output(), &c, 1));
}
static int	Printf(
register char	*s)
{
	return(Write(Output(), s, StrLen(s)));
}
static int	Puts(
register char	*s)
{
	return(Printf(s) + PutChar('\n'));
}

static void	ShowBad(
)
{
	CURSOR_UP;
	ERASE_IN_LINE;
	BLACK_ON;
	Puts(FileName);
	BLACK_OFF;
	Puts(FileName);
}
static void	Append(
register char	*dst,
register char	*src,
register int	at)
{
	register int	pos;
	register int	len = StrLen(src);

	for(pos = 0; pos < len; pos++)
		dst[at + pos] = src[pos];
/*	separate with a blank character	*/
/*	don't try to compute exact fileformat	*/
	dst[at + pos] = ' ';
	dst[at + pos + 1] = '\0';
}
static void	Suspect(
register BPTR	DirLock,
register int	Position)
{
	register struct FileInfoBlock	*fib;
	if((fib = (struct FileInfoblock *)
		AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC | MEMF_CLEAR)))
	{
		if(Examine(DirLock, fib))
			if(fib->fib_DirEntryType <= 0)	/*	[1.d] this is suspect	*/
			{
				BadDirectories++;
				ShowBad();
			}
			else
				while(ExNext(DirLock, fib))
				{
					register BPTR	OldLock;
					register BPTR	NewLock;
					Append(FileName, fib->fib_FileName, Position);
					CURSOR_UP;
					CURSOR_FORWARD(ItoA(Position));
					ERASE_IN_LINE;
					Puts(fib->fib_FileName);
					if(fib->fib_DirEntryType <= 0)
					{
						register BPTR	fh;
						if(fh = Open(fib->fib_FileName, MODE_OLDFILE))
						{
							register int	r;
							register int	read = 0;
							register int	size = fib->fib_Size;
							while((r = Read(fh, buffer, bufsize)) == bufsize)
							{
								read += r;
								Bytes += r;
							}
							if(r != bufsize)
							{
								read += r;
								Bytes += r;
							}
							if(read != size)	/*	[2.f] this is suspect	*/
							{
								BadFiles++;
								ShowBad();
							}
							else
								GoodFiles++;
							Close(fh);
						}
						else	/*	[1.f] this is suspect	*/
						{
							BadFiles++;
							ShowBad();
						}
					}
					else
						if(
							(NewLock = Lock(fib->fib_FileName, ACCESS_READ))
						&&
							(OldLock = CurrentDir(NewLock))
						)
						{
							Suspect(NewLock, StrLen(FileName));
							CurrentDir(OldLock);
							UnLock(NewLock);
							GoodDirectories++;
						}
						else	/*	[2.d 3.d] this is suspect	*/
						{
							BadDirectories++;
							ShowBad();
						}
				FileName[Position] = '\0';
			}
		FreeMem(fib, sizeof(struct FileInfoBlock));
	}
}

#define	ARGPRG	0
#define	ARGDIR	1
#define	ARGBUF	2

static void	ShowUsage(
int		argc,
char	*argv[])
{
/*	positioning IS important ...	*/
	BLACK_ON;
	Printf(argv[ARGPRG]);
	Puts(VERSION);
	BLACK_OFF;
	Printf("usage: ");
	Printf(argv[ARGPRG]);
	Puts(" dir|vol [bufsiz(512)]");
	Exit(0);
}
main(
int		argc,
char	*argv[])
{
	register char	*Dir = FileName;
	register BPTR	OldLock = NULL;
	register BPTR	NewLock = NULL;
	if((argc == ARGDIR) || (argc > ARGBUF + 1))
		ShowUsage(argc, argv);
	/*	no default (=512) readbuffer	*/
	if(argc == ARGBUF + 1)
		if((bufsize = AtoI(argv[ARGBUF])) == BADINT)
			ShowUsage(argc, argv);
	if(!(buffer = AllocMem(bufsize, MEMF_PUBLIC)))
	{
		Puts("no memory");
		Exit(1);
	}
	if(!(IntuitionBase = OpenLibrary("intuition.library", 0)))
	{
		Puts("no intuition");
		Exit(1);
	}
	Dir = argv[ARGDIR];
	Append(FileName, Dir, 0);
	CURSOR_OFF;
	Puts(Dir);
	if(
		(NewLock = Lock(Dir, ACCESS_READ))
	&&
		(OldLock = CurrentDir(NewLock))
	)
	{
		CurrentTime(&StartSeconds, &StartMicros);
		Suspect(NewLock, StrLen(FileName));
		CurrentTime(&StopSeconds, &StopMicros);
	}
	else	/*	[2.d 3.d] this is suspect	*/
	{
		BadDirectories++;
		ShowBad();
	}
	if(OldLock)
		CurrentDir(OldLock);
	if(NewLock)
		UnLock(NewLock);
	CURSOR_UP;
	ERASE_IN_LINE;
	Printf(ItoA(BadFiles));
	Puts(" bad file(s)");
	Printf(ItoA(BadDirectories));
	Puts(" bad directorie(s)");
	Printf(ItoA(GoodFiles));
	Puts(" good file(s)");
	Printf(ItoA(GoodDirectories));
	Puts(" good directorie(s)");
	Printf("on ");
	Puts(Dir);
	if(!BadFiles && !BadDirectories)
	{
		if(StartMicros > StopMicros)
		{
			StopSeconds -= 1;
			StopMicros += 1000000;
		}
		StopSeconds -= StartSeconds;
		StopMicros -= StartMicros;
		Printf(ItoA(Bytes));
		Puts(" bytes");
		Printf(ItoA(StopSeconds));
		Puts(" seconds");
		Printf(ItoA(StopMicros));
		Puts(" micros");
		if(StopSeconds)
		{
			Printf(ItoA(Bytes/StopSeconds));
			Puts(" bytes/second");
		}
		if(GoodFiles)
		{
			Printf(ItoA(Bytes/GoodFiles));
			Puts(" bytes/file");
		}
		if(StopSeconds)
		{
			Printf(ItoA(GoodFiles/StopSeconds));
			Puts(" files/second");
		}
	}
	CURSOR_ON;
	CloseLibrary(IntuitionBase);
	FreeMem(buffer, bufsize);
}
