/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by Olaf Barthel & MXM
 *
 *	Name .....: Icon.c
 *	Created ..: Friday 10-May-91 16:58
 *	Revision .: 0
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	10-May-91       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/

	/* A quick set of file types we are able to provide icons for. */

enum	{	TYPE_DIR,TYPE_FILE,TYPE_ICON,
		TYPE_TEXT,
		TYPE_C,TYPE_H,
		TYPE_ASM,TYPE_I,
		TYPE_MOD,
		TYPE_REXX,
		TYPE_BASIC,
		TYPE_TEX,TYPE_METAFONT,TYPE_GF,TYPE_TEXFONT,TYPE_TEXDVI,TYPE_FLIB,
		TYPE_OLDMANX,TYPE_NEWMANX,TYPE_OLDMANXLIB,TYPE_NEWMANXLIB,
		TYPE_OBJECT,TYPE_LIB,
		TYPE_EXECUTABLE,
		TYPE_LIBRARY,TYPE_DEVICE,TYPE_FILESYS,TYPE_HANDLER,
		TYPE_GIF,TYPE_ILBM,TYPE_ANIM,TYPE_8SVX,TYPE_SMUS,TYPE_FTXT,TYPE_PREFS,TYPE_TERM,
		TYPE_IMPLODER,TYPE_POWERPACKER,
		TYPE_ARC,TYPE_LHARC,TYPE_ZOO,TYPE_ZIP,TYPE_DMS,TYPE_WARP,TYPE_ZOOM,
	};

	/* Name of the global handshake port. */

#define PORTNAME	"Icon Rendezvous"

	/* Library vector offsets for a bunch of routines. */

#define EXAMINE		-102
#define EXNEXT		-108
#define OPEN		-30
#define CLEARMENUSTRIP	-54

	/* This structure describes the global handshake port. */

struct IconPort
{
	struct MsgPort	 VanillaPort;

	BPTR		 Segment;
	VOID		(*Remove)();
};

	/* A structure containing both a file name suffix and the
	 * approriate file type.
	 */

struct Suffix
{
	UBYTE	*Name;
	UBYTE	 Type;
};

	/* A structure containing an icon file name and a special
	 * purpose flag.
	 */

struct NameInfo
{
	UBYTE	*Name;
	BYTE	 IsPresent;
};

	/* A table of valid ASCII characters (7 bits). */

BYTE ValidTab[256] =
{
	0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,
	0,1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

	/* A table of clearly invalid ASCII characters (8 bits). */

BYTE InvalidTab[256] =
{
	1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,
	1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

	/* Some file name suffixes for text files and the approriate
	 * file types.
	 */

struct Suffix TextSuffix[] =
{
	".C",	TYPE_C,
	".CP",	TYPE_C,
	".CC",	TYPE_C,
	".H",	TYPE_H,
	".ASM",	TYPE_ASM,
	".A",	TYPE_ASM,
	".S",	TYPE_ASM,
	".I",	TYPE_I,
	".BAS",	TYPE_BASIC,
	".GFA",	TYPE_BASIC,
	".REXX",TYPE_REXX,
	".CED",	TYPE_REXX,
	".VLT",	TYPE_REXX,
	".CPR",	TYPE_REXX,
	".TxEd",TYPE_REXX,
	".TEX",	TYPE_TEX,
	".STY",	TYPE_TEX,
	".MF",	TYPE_METAFONT,
	".MOD",	TYPE_MOD,
	".DEF",	TYPE_MOD
};

	/* Some more file name suffixes for executable files and the
	 * approriate file types.
	 */

struct Suffix ExecutableSuffix[] =
{
	".device",	TYPE_DEVICE,
	".library",	TYPE_LIBRARY,
	"FileSystem",	TYPE_FILESYS,
	"Handler",	TYPE_HANDLER
};

	/* This list contains the names of all icon files which can be
	 * attached to the files to be identified. The flag byte indicates
	 * whether an icon is available or not.
	 */

struct NameInfo NameInfo[] =
{
	NULL,		FALSE,
	NULL,		FALSE,
	NULL,		FALSE,
	"text",		FALSE,
	"c",		FALSE,
	"h",		FALSE,
	"asm",		FALSE,
	"i",		FALSE,
	"mod",		FALSE,
	"rexx",		FALSE,
	"basic",	FALSE,
	"tex",		FALSE,
	"metafont",	FALSE,
	"gf",		FALSE,
	"pk",		FALSE,
	"dvi",		FALSE,
	"flib",		FALSE,
	"manx3object",	FALSE,
	"manx5object",	FALSE,
	"manx3lib",	FALSE,
	"manx5lib",	FALSE,
	"object",	FALSE,
	"lib",		FALSE,
	NULL,		FALSE,
	"library",	FALSE,
	"device",	FALSE,
	"filesys",	FALSE,
	"handler",	FALSE,
	"gif",		FALSE,
	"pic",		FALSE,
	"anim",		FALSE,
	"sound",	FALSE,
	"score",	FALSE,
	"ftxt",		FALSE,
	"pref",		FALSE,
	"term",		FALSE,
	"imploder",	FALSE,
	"powerpacker",	FALSE,
	"arc",		FALSE,
	"lharc",	FALSE,
	"zoo",		FALSE,
	"zip",		FALSE,
	"dms",		FALSE,
	"warp",		FALSE,
	"zoom",		FALSE
};

	/* Prototypes for functions in this module and Bypass.asm */

extern LONG __asm	MyExamine(register __d1 BPTR FileLock,register __d2 struct FileInfoBlock *FileInfo);
LONG __saveds __asm	NewExamine(register __d1 BPTR FileLock,register __d2 struct FileInfoBlock *FileInfo);

extern LONG __asm	MyExNext(register __d1 BPTR FileLock,register __d2 struct FileInfoBlock *FileInfo);
LONG __saveds __asm	NewExNext(register __d1 BPTR FileLock,register __d2 struct FileInfoBlock *FileInfo);

extern BPTR __asm	MyOpen(register __d1 UBYTE *Name,register __d2 LONG Mode);
BPTR __saveds __asm	NewOpen(register __d1 UBYTE *Name,register __d2 LONG Mode);

extern VOID __asm	MyClearMenuStrip(register __a0 struct Window *Window);
VOID __saveds __asm	NewClearMenuStrip(register __a0 struct Window *Window);

VOID __saveds		RemoveIcon(VOID);
BYTE			InstallIcon(VOID);

struct Node *		MyFindName(struct List *List,UBYTE *Name);
UBYTE			Local2Upper(UBYTE c);
UBYTE			StrCmp(UBYTE *a,UBYTE *b);
VOID			DeleteNode(struct Node *Node);
struct Node *		CreateNode(UBYTE *Name);
VOID			AddName(UBYTE *FileName,UBYTE Type);

ULONG __saveds		Main(VOID);

	/* Global and shared library identifiers. */

struct ExecBase		*SysBase;
struct DosLibrary	*DOSBase;
struct IntuitionBase	*IntuitionBase;

	/* Pointers to the old library routines. */

APTR			 OldExamine;
APTR			 OldExNext;
APTR			 OldOpen;
APTR			 OldClearMenuStrip;

	/* The list containing the files which do not have an icon and the
	 * access semaphore.
	 */

struct SignalSemaphore	 NameSemaphore;
struct List		 NameList;

	/* The node being scanned. */

struct Node		*ScanNode;

	/* True if ExNext is to perform fake scanning, else false. */

BYTE			 FakeScan;

	/* Global handshake port. */

struct IconPort		*IconPort;

	/* Main():
	 *
	 *	Program entry point.
	 */

ULONG __saveds
Main()
{
	struct Process *ThisProcess;

		/* Set up SysBase. */

	SysBase = *(struct ExecBase **)4;

		/* Look who we are. */

	ThisProcess = (struct Process *)SysBase -> ThisTask;

		/* Started from Shell? */

	if(ThisProcess -> pr_CLI)
	{
			/* Open the DOS library. */

		if(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",36))
		{
			Printf("\33[1m\33[33mIcon\33[31m © \33[0mCopyright 1991 by Olaf Barthel, ");

				/* If the port is available remove an
				 * already running `Icon'.
				 */

			if(IconPort = (struct IconPort *)FindPort(PORTNAME))
			{
				Flush(ThisProcess -> pr_COS);

				Printf("Removing patch... ");

				Flush(ThisProcess -> pr_COS);

				Forbid();

				IconPort -> Remove();

				Permit();

				Printf("done.\n");

				CloseLibrary(DOSBase);
			}
			else
			{
					/* Install `Icon'. */

				Flush(ThisProcess -> pr_COS);

				Printf("Installing patch... ");

				Flush(ThisProcess -> pr_COS);

				if(InstallIcon())
					Printf("done.\n");
				else
				{
					Printf("failed!\a\n");

					CloseLibrary(DOSBase);
				}
			}
		}
		else
			return(RETURN_FAIL);
	}
	else
	{
		struct Message *Message;

			/* Wait for Workbench startup message. */

		Message = (struct Message *)WaitPort(&ThisProcess -> pr_MsgPort);

		Forbid();

			/* And reply it. */

		ReplyMsg(Message);
	}

	return(RETURN_OK);
}

	/* MyFindName(struct List *List,UBYTE *Name):
	 *
	 *	Find a node in a list, scan for the name ignoring
	 *	case.
	 */

struct Node *
MyFindName(struct List *List,UBYTE *Name)
{
	struct Node *Node;

	Node = List -> lh_Head;

	while(Node -> ln_Succ)
	{
		if(!StrCmp(Node -> ln_Name,Name))
			return(Node);

		Node = Node -> ln_Succ;
	}

	return(NULL);
}

	/* RemoveIcon():
	 *
	 *	Remove a previously installed `Icon'.
	 */

VOID __saveds
RemoveIcon()
{
	struct Node	*Node,*NextNode;
	BPTR		 Segment;

		/* Look for the port. */

	IconPort = (struct IconPort *)FindPort(PORTNAME);

	Segment = IconPort -> Segment;

		/* Remove it. */

	RemPort(&IconPort -> VanillaPort);

	FreeVec(IconPort);

	Node = NameList . lh_Head;

		/* Delete all the nodes in the list if necessary. */

	while(Node -> ln_Succ)
	{
		NextNode = Node -> ln_Succ;

		DeleteNode(Node);

		Node = NextNode;
	}

		/* Restore the system library vectors if possible. */

	SetFunction(DOSBase,		EXAMINE,	OldExamine);
	SetFunction(DOSBase,		EXNEXT,		OldExNext);
	SetFunction(DOSBase,		OPEN,		OldOpen);
	SetFunction(IntuitionBase,	CLEARMENUSTRIP,	OldClearMenuStrip);

		/* Remove the `Icon' program. */

	UnLoadSeg(Segment);

		/* Close the libraries. */

	CloseLibrary(IntuitionBase);
	CloseLibrary(DOSBase);
}

	/* InstallIcon():
	 *
	 *	Install `Icon' and get it running.
	 */

BYTE
InstallIcon()
{
		/* Create the global port. */

	if(IconPort = (struct IconPort *)AllocVec(sizeof(struct IconPort) + sizeof(PORTNAME),MEMF_PUBLIC|MEMF_CLEAR))
	{
		IconPort -> VanillaPort . mp_Node . ln_Name = (UBYTE *)(IconPort + 1);

		strcpy(IconPort -> VanillaPort . mp_Node . ln_Name,PORTNAME);

		IconPort -> VanillaPort . mp_Flags = PA_IGNORE;

			/* Open Intuition. */

		if(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",36))
		{
				/* Set up the name list. */

			InitSemaphore(&NameSemaphore);

			NewList(&NameList);

				/* Modify system functions. */

			OldExamine		= (APTR)SetFunction(DOSBase,		EXAMINE,	NewExamine);
			OldExNext		= (APTR)SetFunction(DOSBase,		EXNEXT,		NewExNext);
			OldOpen			= (APTR)SetFunction(DOSBase,		OPEN,		NewOpen);
			OldClearMenuStrip	= (APTR)SetFunction(IntuitionBase,	CLEARMENUSTRIP,	NewClearMenuStrip);

				/* Remember setup data. */

			IconPort -> Remove	= RemoveIcon;
			IconPort -> Segment	= ((struct CommandLineInterface *)BADDR(((struct Process *)SysBase -> ThisTask) -> pr_CLI)) -> cli_Module;

				/* Make the port public. */

			AddPort(&IconPort -> VanillaPort);

				/* Make the Shell believe that `Icon' has
				 * already terminated.
				 */

			((struct CommandLineInterface *)BADDR(((struct Process *)SysBase -> ThisTask) -> pr_CLI)) -> cli_Module = NULL;

			return(TRUE);
		}

			/* Clear the port. */

		FreeVec(IconPort);
	}

	return(FALSE);
}

	/* Local2Upper(UBYTE c):
	 *
	 *	Custom version of toupper, will also handle international
	 *	characters.
	 */

UBYTE
Local2Upper(UBYTE c)
{
	return((UBYTE)((((c) >= 224 && (c) <= 254) || ((c) >= 'a' && (c) <= 'z')) ? (c) - 32 : c));
}

	/* StrCmp(UBYTE *a,UBYTE *b):
	 *
	 *	Custom version of strcmp, compares ignoring case.
	 */

UBYTE
StrCmp(UBYTE *a,UBYTE *b)
{
	for( ; Local2Upper(*a) == Local2Upper(*b) ; a++, b++)
	{
		if(!(*a))
			return(0);
	}

	return((UBYTE)(Local2Upper(*a) - Local2Upper(*b)));
}

	/* DeleteNode(struct Node *Node):
	 *
	 *	Delete a node from a list and free the memory it
	 *	occupies.
	 */

VOID
DeleteNode(struct Node *Node)
{
	Remove(Node);

	FreeVec(Node);
}

	/* CreateNode(UBYTE *Name):
	 *
	 *	Create a node, allocate some extra space for the name
	 *	and initialize it.
	 */

struct Node *
CreateNode(UBYTE *Name)
{
	struct Node *Node;

	if(Node = (struct Node *)AllocVec(sizeof(struct Node) + strlen(Name) + 1,MEMF_PUBLIC|MEMF_CLEAR))
	{
		Node -> ln_Name = (UBYTE *)(Node + 1);

		strcpy(Node -> ln_Name,Name);
	}

	return(Node);
}

	/* Identify(UBYTE *Name):
	 *
	 *	Heuristically identify the type of a file.
	 */

UBYTE
Identify(UBYTE *Name)
{
	ULONG	*Buffer;
	UBYTE	 Type = TYPE_FILE;
	SHORT	 i,Len = strlen(Name);

		/* Allocate a buffer for the first 400 bytes of the
		 * file.
		 */

	if(Buffer = (ULONG *)AllocVec(400,MEMF_PUBLIC))
	{
		BPTR File,Size;

			/* Open the file. */

		if(File = Open(Name,MODE_OLDFILE))
		{
				/* Read the first 400 bytes. */

			if((Size = Read(File,Buffer,400)) >= sizeof(ULONG))
			{
					/* Examine the first longword. */

				switch(Buffer[0])
				{
					case 0x03E7:	Type = TYPE_OBJECT;
							break;

					case 0x03F3:	Type = TYPE_EXECUTABLE;
							break;

					case 0x03FA:	Type = TYPE_LIB;
							break;

					case 0xF7593647:Type = TYPE_TEXFONT;
							break;

					case 0xF7020183:Type = TYPE_TEXDVI;
							break;

					case 0xF7832020:Type = TYPE_GF;
							break;

					case 'FLIB':	Type = TYPE_FLIB;
							break;

					case 'FORM':	switch(Buffer[2])
							{
								case 'ILBM':	Type = TYPE_ILBM;
										break;

								case 'ANIM':	Type = TYPE_ANIM;
										break;

								case '8SVX':	Type = TYPE_8SVX;
										break;

								case 'SMUS':	Type = TYPE_SMUS;
										break;

								case 'FTXT':	Type = TYPE_FTXT;
										break;

								case 'PREF':	Type = TYPE_PREFS;
										break;

								case 'TERM':	Type = TYPE_TERM;
										break;
							}

							break;

					case 'IMP!':	Type = TYPE_IMPLODER;
							break;

					case 'PP20':	Type = TYPE_POWERPACKER;
							break;

					case 'DMS!':	Type = TYPE_DMS;
							break;

					case 'Warp':	Type = TYPE_WARP;
							break;

					case 'ZOOM':	Type = TYPE_ZOOM;
							break;

					case 0x504B0304:Type = TYPE_ZIP;
							break;

					case 'ZOO ':	Type = TYPE_ZOO;
							break;

					case 'GIF8':	Type = TYPE_GIF;
							break;

					default:	break;
				}

					/* No match yet, see if it's an
					 * ASCII file.
					 */

				if(Type == TYPE_FILE)
				{
					UBYTE	*CharBuffer = (UBYTE *)Buffer;
					SHORT	 Count = 0;

					for(i = 0 ; i < Size ; i++)
					{
						if(ValidTab[CharBuffer[i]])
							Count++;
						else
						{
							if(InvalidTab[CharBuffer[i]])
							{
								Count = 0;

								break;
							}
						}
					}

						/* If more than 75% of the
						 * characters in the first
						 * 400 bytes are legal
						 * ASCII characters this
						 * file is supposed to be
						 * a text file.
						 */

					if(Count > 3 * (Size / 4))
						Type = TYPE_TEXT;
				}

					/* Still no match, have another try... */

				if(Type == TYPE_FILE)
				{
					if((Buffer[0] & 0xFFFF0000) == 0x1A080000)
						Type = TYPE_ARC;
					else
					{
						if((Buffer[0] & 0x0000FFFF) == 0x00002D6C && (Buffer[1] & 0xFF00FF00) == 0x68002D00)
							Type = TYPE_LHARC;
						else
						{
							switch(Buffer[0] & 0xFFFF0000)
							{
								case 0x434A0000:	Type = TYPE_NEWMANX;
											break;

								case 0x414A0000:	Type = TYPE_OLDMANX;
											break;

								case 0x636A0000:	Type = TYPE_NEWMANXLIB;
											break;

								case 0x616A0000:	Type = TYPE_OLDMANXLIB;
											break;

								case 0xF5000000:	Type = TYPE_BASIC;
											break;

								default:		break;
							}
						}
					}
				}

					/* Take a look at the file name
					 * suffixes.
					 */

				switch(Type)
				{
					case TYPE_TEXT:
							for(i = 0 ; i < sizeof(TextSuffix) / sizeof(struct Suffix) ; i++)
							{
								Size = strlen(TextSuffix[i] . Name);

								if(Len >= Size)
								{
									if(!StrCmp(&Name[Len - Size],TextSuffix[i] . Name))
									{
										Type = TextSuffix[i] . Type;
										break;
									}
								}
							}

							break;

					case TYPE_EXECUTABLE:
							for(i = 0 ; i < sizeof(ExecutableSuffix) / sizeof(struct Suffix) ; i++)
							{
								Size = strlen(ExecutableSuffix[i] . Name);

								if(Len >= Size)
								{
									if(!StrCmp(&Name[Len - Size],ExecutableSuffix[i] . Name))
									{
										Type = ExecutableSuffix[i] . Type;
										break;
									}
								}
							}

							break;

					case TYPE_OBJECT:
							if(Len >= 4)
							{
								if(!StrCmp(&Name[Len - 4],".LIB"))
									Type = TYPE_LIB;
							}

							break;

					default:	break;
				}
			}

			Close(File);
		}

		FreeVec(Buffer);
	}

	return(Type);
}

	/* AddName(UBYTE *FileName,UBYTE Type):
	 *
	 *	Add another name to the list of file names.
	 */

VOID
AddName(UBYTE *FileName,UBYTE Type)
{
	struct Node	*Node;
	UBYTE		 Name[108];
	SHORT		 i;
	BYTE		 HasInfo = FALSE;

		/* Make sure that we will be able to handle the file type. */

	if(StrCmp(FileName,".info") && StrCmp(FileName,"Disk.info") && FileName[strlen(FileName) - 1] != ':')
	{
		strcpy(Name,FileName);

			/* Check if the file name has an `.info' suffix. */

		for(i = strlen(Name) - 1; i >= 0; i--)
		{
			if(Name[i] == '.')
			{
				if(!StrCmp(&Name[i],".info"))
				{
					Name[i] = 0;

					HasInfo = TRUE;

					break;
				}
			}
		}

		ObtainSemaphore(&NameSemaphore);

		if(HasInfo)
		{
				/* This is probably an icon, see if there's
				 * a file with this name. If there is,
				 * remove the node from the list.
				 */

			if(Node = (struct Node *)MyFindName(&NameList,Name))
				DeleteNode(Node);
			else
			{
					/* Add another icon to the list. */

				if(Node = CreateNode(FileName))
				{
					Node -> ln_Type = TYPE_ICON;

					AddTail(&NameList,Node);
				}
			}
		}
		else
		{
			strcat(Name,".info");

				/* Look if there's already an icon in the
				 * list. If there is one, remove it.
				 */

			if(Node = (struct Node *)MyFindName(&NameList,Name))
				DeleteNode(Node);
			else
			{
					/* Add the file to the list. */

				if(Node = CreateNode(FileName))
				{
					Node -> ln_Type = Type;

					AddTail(&NameList,Node);
				}
			}
		}

		ReleaseSemaphore(&NameSemaphore);
	}
}

	/* NewExamine():
	 *
	 *	Patched dos.library routine, stuffs all files encountered
	 *	by Workbench scan into a list.
	 */

LONG __saveds __asm
NewExamine(register __d1 BPTR FileLock,register __d2 struct FileInfoBlock *FileInfo)
{
		/* Is it Workbench who's calling? */

	if(!strcmp(SysBase -> ThisTask -> tc_Node . ln_Name,"Workbench"))
	{
			/* Are there still nodes in the scan list?
			 * If so, clear the list.
			 */

		if(NameList . lh_Head -> ln_Succ)
		{
			struct Node *Node,*NextNode;

			ObtainSemaphore(&NameSemaphore);

			Node = NameList . lh_Head;

			while(Node -> ln_Succ)
			{
				NextNode = Node -> ln_Succ;

				DeleteNode(Node);

				Node = NextNode;
			}

			ReleaseSemaphore(&NameSemaphore);

			ScanNode = NULL;
		}

			/* Jump back into original dos.library routine. */

		if(MyExamine(FileLock,FileInfo))
		{
				/* If enabled identify the file type
				 * and add the file to the list.
				 */

			if(FakeScan)
			{
				if(StrCmp(FileInfo -> fib_FileName,".info") && StrCmp(FileInfo -> fib_FileName,"Disk.info") && FileInfo -> fib_FileName[strlen(FileInfo -> fib_FileName) - 1] != ':')
				{
					UBYTE Type = FileInfo -> fib_DirEntryType < 0 ? TYPE_FILE : TYPE_DIR;

					if(Type == TYPE_FILE)
						Type = Identify(FileInfo -> fib_FileName);

					AddName(FileInfo -> fib_FileName,Type);

					if(Type != TYPE_EXECUTABLE)
						FileInfo -> fib_Protection |= FIBF_EXECUTE;
				}
			}

			return(DOSTRUE);
		}
		else
			return(DOSFALSE);
	}

		/* Jump into original routine. */

	return(MyExamine(FileLock,FileInfo));
}

	/* NewExNext():
	 *
	 *	Patched dos.library routine, stuffs all files encountered
	 *	by Workbench scan into a list.
	 */

LONG __saveds __asm
NewExNext(register __d1 BPTR FileLock,register __d2 struct FileInfoBlock *FileInfo)
{
		/* Is it Workbench who's calling? */

	if(!strcmp(SysBase -> ThisTask -> tc_Node . ln_Name,"Workbench"))
	{
			/* If enabled identify the file type
			 * and add the file to the list.
			 */

		if(FakeScan)
		{
				/* If ScanNode is nonzero we are actually
				 * faking icons.
				 */

			if(ScanNode)
			{
					/* Scan the list for approriate files. */

				while(ScanNode -> ln_Succ)
				{
						/* Are we able to produce and
						 * icon for this file?
						 */

					if(ScanNode -> ln_Type != TYPE_ICON && ScanNode -> ln_Type != TYPE_DIR && NameInfo[ScanNode -> ln_Type] . IsPresent)
					{
							/* Clear the contents of the FileInfoBlock. */

						memset(FileInfo,0,sizeof(struct FileInfoBlock));

							/* Set the entries to default values. */

						FileInfo -> fib_DirEntryType	= -1;
						FileInfo -> fib_Size		= 42;
						FileInfo -> fib_NumBlocks	= 1;
						FileInfo -> fib_Protection	= FIBF_EXECUTE;

							/* Provide a simple date. */

						DateStamp(&FileInfo -> fib_Date);

							/* Produce and icon. */

						SPrintf(FileInfo -> fib_FileName,"%s.info",ScanNode -> ln_Name);

						ScanNode = ScanNode -> ln_Succ;

						return(DOSTRUE);
					}

					ScanNode = ScanNode -> ln_Succ;
				}

					/* End of list reached. */

				SetIoErr(ERROR_NO_MORE_ENTRIES);

				return(DOSFALSE);
			}

				/* Jump into original dos.library routine. */

			if(MyExNext(FileLock,FileInfo))
			{
					/* Identify file type and add it to the list. */

				if(StrCmp(FileInfo -> fib_FileName,".info") && StrCmp(FileInfo -> fib_FileName,"Disk.info") && FileInfo -> fib_FileName[strlen(FileInfo -> fib_FileName) - 1] != ':')
				{
					UBYTE Type = FileInfo -> fib_DirEntryType < 0 ? TYPE_FILE : TYPE_DIR;

					if(Type == TYPE_FILE)
						Type = Identify(FileInfo -> fib_FileName);

					AddName(FileInfo -> fib_FileName,Type);

					if(Type != TYPE_EXECUTABLE)
						FileInfo -> fib_Protection |= FIBF_EXECUTE;
				}

				return(DOSTRUE);
			}
			else
			{
				struct Node	*Node;
				UBYTE		 Buffer[40];
				BPTR		 FileLock;
				SHORT		 i;

					/* We have just reached the end of list, there are no more
					 * directory entries. Now it's time to see if all icons
					 * are available and to determine the files to produce
					 * icons for.
					 */

				ObtainSemaphore(&NameSemaphore);

					/* Clear the list entry flags. */

				for(i = 0 ; i < sizeof(NameInfo) / sizeof(struct NameInfo) ; i++)
					NameInfo[i] . IsPresent = -1;

				Node = NameList . lh_Head;

					/* Scan the name list. At this point there are only those
					 * files in the list which are not accompanied by an icon.
					 */

				while(Node -> ln_Succ)
				{
					if(NameInfo[Node -> ln_Type] . IsPresent == -1 && NameInfo[Node -> ln_Type] . Name)
					{
						SPrintf(Buffer,"ENV:sys/def_%s.info",NameInfo[Node -> ln_Type] . Name);

							/* Is the default icon available? */

						if(FileLock = Lock(Buffer,ACCESS_READ))
						{
							NameInfo[Node -> ln_Type] . IsPresent = TRUE;

							ScanNode = NameList . lh_Head;

							UnLock(FileLock);
						}
						else
							NameInfo[Node -> ln_Type] . IsPresent = FALSE;
					}

					Node = Node -> ln_Succ;
				}

					/* If there is an icon available produce the first
					 * fake icon.
					 */

				if(ScanNode)
				{
					while(ScanNode -> ln_Succ)
					{
						if(ScanNode -> ln_Type != TYPE_ICON && ScanNode -> ln_Type != TYPE_DIR && NameInfo[ScanNode -> ln_Type] . IsPresent)
						{
							memset(FileInfo,0,sizeof(struct FileInfoBlock));

							FileInfo -> fib_DirEntryType	= -1;
							FileInfo -> fib_Size		= 42;
							FileInfo -> fib_NumBlocks	= 1;
							FileInfo -> fib_Protection	= FIBF_EXECUTE;

							DateStamp(&FileInfo -> fib_Date);

							SPrintf(FileInfo -> fib_FileName,"%s.info",ScanNode -> ln_Name);

							ScanNode = ScanNode -> ln_Succ;

							ReleaseSemaphore(&NameSemaphore);

							return(DOSTRUE);
						}

						ScanNode = ScanNode -> ln_Succ;
					}
				}

				ReleaseSemaphore(&NameSemaphore);

				SetIoErr(ERROR_NO_MORE_ENTRIES);

				return(DOSFALSE);
			}
		}
	}

		/* Jump into original routine. */

	return(MyExNext(FileLock,FileInfo));
}

	/* NewOpen():
	 *
	 *	Patched dos.library routine, opens the fake icon files
	 *	produced by the patched Examine/ExNext routines.
	 */

BPTR __saveds __asm
NewOpen(register __d1 UBYTE *Name,register __d2 LONG Mode)
{
	UBYTE Buffer[108];

		/* Is it Workbench who's calling? */

	if(!strcmp(SysBase -> ThisTask -> tc_Node . ln_Name,"Workbench"))
	{
			/* Is there a list to scan? */

		if(ScanNode)
		{
			struct Node	*Node;
			SHORT		 i;

				/* Extract the base file name. */

			strcpy(Buffer,Name);

			for(i = strlen(Buffer) ; i >= 0; i--)
			{
				if(!StrCmp(&Buffer[i],".info"))
				{
					Buffer[i] = 0;
					break;
				}
			}

				/* Try to find the approriate icon file. */

			if(Node = (struct Node *)MyFindName(&NameList,Buffer))
			{
					/* Change the name of the icon file
					 * into the approriate default icon
					 * type.
					 */

				SPrintf(Buffer,"ENV:sys/def_%s.info",NameInfo[Node -> ln_Type] . Name);

				Name = Buffer;
			}
		}
	}

		/* Jump into original routine. */

	return(MyOpen(Name,Mode));
}

	/* NewClearMenuStrip():
	 *
	 *	Patched intuition.libary routine, checks the Workbench menu
	 *	to determine whether we are to create fake icons or not.
	 */

VOID __saveds __asm
NewClearMenuStrip(register __a0 struct Window *Window)
{
	struct Menu	*Menu;
	struct MenuItem	*MenuItem,*SubItem;

	FakeScan = FALSE;

		/* Is this a Workbench window? */

	if(Window -> Flags & WFLG_WBENCHWINDOW)
	{
		Menu = Window -> MenuStrip;

			/* Walk through the menus... */

		while(Menu && !FakeScan)
		{
			MenuItem = Menu -> FirstItem;

				/* Walk through the menu items... */

			while(MenuItem && !FakeScan)
			{
				SubItem = MenuItem -> SubItem;

					/* Walk through the sub items... */

				while(SubItem && !FakeScan)
				{
						/* Sub item with item text? */

					if(SubItem -> Flags & ITEMTEXT)
					{
							/* Is it the `All Files'
							 * subitem?
							 */

						if(!strcmp(((struct IntuiText *)SubItem -> ItemFill) -> IText,"All Files"))
						{
								/* See if it's
								 * checked.
								 */

							if(SubItem -> Flags & CHECKED)
								FakeScan = TRUE;
							else
								FakeScan = FALSE;
						}
					}

					SubItem = SubItem -> NextItem;
				}

				MenuItem = MenuItem -> NextItem;
			}

			Menu = Menu -> NextMenu;
		}
	}

		/* Jump into original routine. */

	MyClearMenuStrip(Window);
}
