// MPIndex - AmigaGuide Indexing program
// Copyright (C) © 1996 Mark John Paddock

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// mark@topic.demon.co.uk
// mpaddock@cix.compulink.co.uk

extern long __stack = 16000;

extern long __oslibversion=39;

#define INTUI_V36_NAMES_ONLY
#define INTUITION_IOBSOLETE_H
#define ASL_V38_NAMES_ONLY
#define __USE_SYSBASE 1

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/utility.h>
#include <proto/locale.h>
#include <proto/amigaguide.h>
struct Library *AmigaGuideBase = NULL;
extern struct Library *LocaleBase = NULL;

#include <intuition/gadgetclass.h>

#include <exec/memory.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dos.h>
#include <ctype.h>

extern struct Catalog *Catalog=NULL;

#define CATCOMP_BLOCK
#define CATCOMP_NUMBERS
#include "Rmessages.h"

#include "MPIndex.h"

AMIGAGUIDECONTEXT 	handle 	= NULL;
struct NewAmigaGuide	nag 		= {NULL};
struct AmigaGuideMsg *agm;			// message from amigaguide

struct RDArgs *rdargs = NULL;

static const char Upper[129] =
"                "
"                "
" !\"#$%&'()*+,-./"
"0123456789:;<=>?"
"@ABCDEFGHIJKLMNO"
"PQRSTUVWXYZ[\\]^_"
"`ABCDEFGHIJKLMNO"
"PQRSTUVWXYZ{|}~ ";

#define mytoupper(x) (Upper[x])

const char Version[]="$VER: RunMPIndex 5.5 (26.2.97)";

struct MyNode *CurrNode = NULL;

struct FileName *FirstName = 0;

struct FileName **MyNamep=NULL;

struct MinList FileList;

int FileCnt=0;

struct MyNode {
	struct MinNode Node;
	struct FileName *FileName;
	char *Name;
	char *Title;
	struct MyNode *MyNode;
	struct MinList List;
	char *UpperTitle;
};

struct MyNode *FirstNode = 0;

struct MyWord {
	struct Node Node;
	struct MinList List;
	struct MinList FileList;
	struct MyNode **MyNode;
	ULONG Count;
	ULONG ActualCount;
	char *UpperWord;
};

struct NWNode {
	struct MinNode Node;
	struct MyWord *MyWord;
};

struct SNode {
	struct Node Node;
	struct MyNode *MyNode;
};

struct FNode {
	struct MinNode Node;
	struct FileName *FileName;
};

struct MyWord AllWord;

struct MyWord **MyWordp;
struct MyWord **MyWordps;

struct SNode *SNodes;

struct MinList Words;
struct MinList EmptyList;
struct MinList Nodes;

struct MyNode **MyNodep = NULL;
struct MyNode **MyNodeps = NULL;
struct MyWord *CurrWord = NULL;

ULONG WordCount = 0;
ULONG NodeCount = 0;

char *command;

int ActualWordCount = 0;

BOOL All = TRUE;
BOOL None = FALSE;

extern void *Chain = NULL;
#define PUDDLE 20000
#define THRESH 4096

#define TEMPLATE "FROM,COMMAND=COMM,PUBSCREEN/K"
#define OPT_FROM	0
#define OPT_COMM	1
#define OPT_PUBS	2

#define OPT_COUNT	3

APTR
Mystrdup(UBYTE *str);

APTR
Mycalloc(ULONG size);

BOOL
Mystrstr(char *a,char *b);

struct Requester RequestC;

char
*GetMessage(UWORD message) {
	LONG   *l;
	UWORD  *w;
	STRPTR  builtIn;

   l = (LONG *)CatCompBlock;

	while (*l != message)  {
		w = (UWORD *)((ULONG)l + 4);
		l = (LONG *)((ULONG)l + (ULONG)*w + 6);
	}
	builtIn = (STRPTR)((ULONG)l + 6);
	return(GetCatalogStr(Catalog,message,builtIn));
}

void DisableWindow(void) {
	InitRequester(&RequestC);
	Request(&RequestC,MPIndexWnd);
	// Wait pointer
	SetWindowPointer(MPIndexWnd, WA_BusyPointer, TRUE,
						  WA_PointerDelay, TRUE,
						  TAG_END);
}

void EnableWindow(void) {
	EndRequest(&RequestC,MPIndexWnd);
	SetWindowPointer(MPIndexWnd,
						  TAG_END);
}

void UnCurrent(void) {
	int i;
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Guide],MPIndexWnd,NULL,
							GTLV_Labels,~0,
							TAG_END);
	for (i=0; i<FileCnt; ++i) {
		MyNamep[i]->Current = FALSE;
	}
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Guide],MPIndexWnd,NULL,
							GTLV_Labels,&FileList,
							TAG_END);
}

void NewCurrent(void) {
	int i;
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Guide],MPIndexWnd,NULL,
							GTLV_Labels,~0,
							TAG_END);
	for (i=0; i<FileCnt; ++i) {
		MyNamep[i]->Current = FALSE;
	}
	CurrNode->FileName->Current=TRUE;
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Guide],MPIndexWnd,NULL,
							GTLV_Labels,&FileList,
							GTLV_MakeVisible, CurrNode->FileName->seq,
							TAG_END);
}

BOOL CheckNode(struct MyNode *MyNode,char *c[],int max) {
	struct NWNode *NWNode;
	int i;
	int cnt = 0;
	BOOL chk[10]={0};
	for (NWNode = (struct NWNode *)(MyNode->List.mlh_Head);
		  (cnt < max) && NWNode->Node.mln_Succ;
		  NWNode = (struct NWNode *)(NWNode->Node.mln_Succ)) {
		for (i=0; i<max; ++i) {
			if (!chk[i]) {
				if (Mystrstr(NWNode->MyWord->UpperWord,c[i])) {
					chk[i] = TRUE;
					++cnt;
				}
			}
		}
	}
	if (cnt == max) {
		return TRUE;
	}
	return FALSE;
}

char nodebuffer[10];

void UpdateNodes(void) {
	char *find;
	char *findw;
	struct MyNode **MyNodes;
	struct SNode *SNode,*SNodes1;
	char *p,*q;
	char buffer[256];
	char *c[10],*s[10];
	int maxw=0,max,i;
	BOOL multiw = FALSE;
	BOOL found;

	for (i=0; i<FileCnt; ++i) {
		MyNamep[i]->WordSel = FALSE;
	}
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Nodes],MPIndexWnd,NULL,
							GTLV_Labels,~0,
							TAG_END);
	NewList((struct List *)&Nodes);
	if (CurrWord) {
		GT_GetGadgetAttrs(MPIndexGadgets[GDX_Find],MPIndexWnd,NULL,
								GTST_String,&find,
								TAG_END);
		GT_GetGadgetAttrs(MPIndexGadgets[GDX_Search],MPIndexWnd,NULL,
								GTST_String,&findw,
								TAG_END);
		if (Mystrstr(findw," ")) {
			multiw = TRUE;
			strcpy(buffer,findw);
			p = buffer;
			while (*p) {
				if (' ' == *p) {
					*p = 0;
				}
				else {
					*p = mytoupper(*p);
				}
				++p;
			}
			++p;
			*p = 0;
			maxw = strbpl(c,9,buffer);
		}
		MyNodes = MyNodeps;
		SNodes1 = SNodes;
		CurrWord->Count = 0;

		if (*find) {
			if (Mystrstr(find," ")) {
				strcpy(buffer,find);
				p = buffer;
				while (*p) {
					if (' ' == *p) {
						*p = 0;
					}
					else {
						*p = mytoupper(*p);
					}
					++p;
				}
				++p;
				*p = 0;
				max = strbpl(s,9,buffer);
				for (SNode = (struct SNode *)(CurrWord->List.mlh_Head);
					  SNode->Node.ln_Succ;
					  SNode = (struct SNode *)(SNode->Node.ln_Succ)) {
					if (SNode->MyNode->FileName->Selected) {
						found = TRUE;
						for (i=0; found && (i<max); ++i) {
							if (!Mystrstr(SNode->MyNode->UpperTitle,s[i])) {
								found = FALSE;
							}
						}
						if (found) {
							if (multiw) {
								found = CheckNode(SNode->MyNode,c,maxw);
							}
							if (found) {
								*SNodes1 = *SNode;
								*MyNodes++ = SNode->MyNode;
								AddTail((struct List *)&Nodes,&(SNodes1->Node));
								++SNodes1;
								++CurrWord->Count;
							}
						}
					}
				}
			}
			else {
				p = find;
				q = buffer;
				while (*p) {
					*q++ = mytoupper(*p++);
				}
				*q = 0;
				for (SNode = (struct SNode *)(CurrWord->List.mlh_Head);
					  SNode->Node.ln_Succ;
					  SNode = (struct SNode *)(SNode->Node.ln_Succ)) {
					if (SNode->MyNode->FileName->Selected) {
						if (Mystrstr(SNode->MyNode->UpperTitle,buffer)) {
							found = TRUE;
							if (multiw) {
								found = CheckNode(SNode->MyNode,c,maxw);
							}
							if (found) {
								*SNodes1 = *SNode;
								*MyNodes++ = SNode->MyNode;
								AddTail((struct List *)&Nodes,&(SNodes1->Node));
								++SNodes1;
								++CurrWord->Count;
							}
						}
					}
				}
			}
		}
		else {
			for (SNode = (struct SNode *)(CurrWord->List.mlh_Head);
				  SNode->Node.ln_Succ;
				  SNode = (struct SNode *)(SNode->Node.ln_Succ)) {
				if (SNode->MyNode->FileName->Selected) {
					found = TRUE;
					if (multiw) {
						found = CheckNode(SNode->MyNode,c,maxw);
					}
					if (found) {
						*SNodes1 = *SNode;
						*MyNodes++ = SNode->MyNode;
						AddTail((struct List *)&Nodes,&(SNodes1->Node));
						++SNodes1;
						++CurrWord->Count;
					}
				}
			}
		}
	}
	if (CurrWord && CurrWord->Count) {
		for (i = 0; i<CurrWord->Count; ++i) {
			MyNodeps[i]->FileName->WordSel = TRUE;
		}
		sprintf(nodebuffer,"%ld",CurrWord->Count);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_NodeCount],MPIndexWnd,NULL,
								GTTX_Text,nodebuffer,
								TAG_END);
		CurrNode = MyNodeps[0];
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Nodes],MPIndexWnd,NULL,
							GTLV_Labels,&Nodes,
							GTLV_Selected, 0,
							TAG_END);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Show],MPIndexWnd,NULL,
							GA_Disabled, FALSE,
							TAG_END);
		NewCurrent();
	}
	else {
		CurrNode = NULL;
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_NodeCount],MPIndexWnd,NULL,
								GTTX_Text,"0",
								TAG_END);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Nodes],MPIndexWnd,NULL,
							GTLV_Labels,&Nodes,
							GTLV_Selected, ~0,
							TAG_END);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Show],MPIndexWnd,NULL,
							GA_Disabled, TRUE,
							TAG_END);
		UnCurrent();
	}
}

char wordbuffer[10];

void UpdateWords(void) {
	char *find;
	int i,j,k;
	struct FNode *FNode;
	char pbuffer[256];
	char *p;
	char *c[10];
	BOOL Found;
	int maxw;

	if (None) {
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_WordCount],MPIndexWnd,NULL,
								GTTX_Text,"0",
								TAG_END);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,~0,
								TAG_END);
		NewList((struct List *)&Words);
		WordCount = 0;
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,&Words,
								GTLV_Selected, ~0,
								TAG_END);
		CurrWord = NULL;
		UpdateNodes();
		return;
	}
	GT_GetGadgetAttrs(MPIndexGadgets[GDX_Search],MPIndexWnd,NULL,
							GTST_String,&find,
							TAG_END);
	if (*find && Mystrstr(find," ")) {
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,~0,
								TAG_END);
		NewList((struct List *)&Words);
		MyWordps[0] = MyWordp[0];
		AddTail((struct List *)&Words,&(MyWordps[0]->Node));
		CurrWord = MyWordp[0];
		strcpy(pbuffer,find);
		p = pbuffer;
		while (*p) {
			if (' ' == *p) {
				*p = 0;
			}
			else {
				*p = mytoupper(*p);
			}
			++p;
		}
		++p;
		*p = 0;
		maxw = strbpl(c,9,pbuffer);
		for (i = 1, j = 1; i<ActualWordCount; ++i) {
			Found = FALSE;
			for (k=0; !Found && (k<maxw); ++k) {
				if (Mystrstr(MyWordp[i]->UpperWord,c[k])) {
					Found = TRUE;
				}
			}
			if (Found) {
				if (!All) {
					Found = FALSE;
					for (FNode = (struct FNode *)(MyWordp[i]->FileList.mlh_Head);
						  !Found && FNode->Node.mln_Succ;
						  FNode = (struct FNode *)(FNode->Node.mln_Succ)) {
						if (FNode->FileName->Selected) {
							Found = TRUE;
						}
					}
				}
				else {
					Found = TRUE;
				}
				if (Found) {
					MyWordps[j] = MyWordp[i];
					AddTail((struct List *)&Words,&(MyWordps[j]->Node));
					++j;
				}
			}
		}
		WordCount = j;
		sprintf(wordbuffer,"%ld",j);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_WordCount],MPIndexWnd,NULL,
								GTTX_Text,wordbuffer,
								TAG_END);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,&Words,
								GTLV_Selected, 0,
								TAG_END);
		UpdateNodes();
	}
	else if (ParsePatternNoCase(find,pbuffer,254)) {
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,~0,
								TAG_END);
		NewList((struct List *)&Words);
		for (i = 0, j = 0; i<ActualWordCount; ++i) {
			if (!All) {
				Found = FALSE;
				for (FNode = (struct FNode *)(MyWordp[i]->FileList.mlh_Head);
					  !Found && FNode->Node.mln_Succ;
					  FNode = (struct FNode *)(FNode->Node.mln_Succ)) {
					if (FNode->FileName->Selected) {
						Found = TRUE;
					}
				}
			}
			else {
				Found = TRUE;
			}
			if (Found) {
				if (MatchPatternNoCase(pbuffer,MyWordp[i]->Node.ln_Name)) {
					MyWordps[j] = MyWordp[i];
					AddTail((struct List *)&Words,&(MyWordps[j]->Node));
					++j;
				}
			}
		}
		WordCount = j;
		sprintf(wordbuffer,"%ld",WordCount);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_WordCount],MPIndexWnd,NULL,
								GTTX_Text,wordbuffer,
								TAG_END);
		if (WordCount) {
			CurrWord = MyWordps[0];
		}
		else {
			CurrWord = NULL;
		}
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,&Words,
								GTLV_Selected, WordCount?0:~0,
								TAG_END);
		UpdateNodes();
	}
	else {
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,~0,
								TAG_END);
		NewList((struct List *)&Words);
		for (i = 0, j = 0; i<ActualWordCount; ++i) {
			if (!All) {
				Found = FALSE;
				for (FNode = (struct FNode *)(MyWordp[i]->FileList.mlh_Head);
					  !Found && FNode->Node.mln_Succ;
					  FNode = (struct FNode *)(FNode->Node.mln_Succ)) {
					if (FNode->FileName->Selected) {
						Found = TRUE;
					}
				}
			}
			else {
				Found = TRUE;
			}
			if (Found) {
				MyWordps[j] = MyWordp[i];
				AddTail((struct List *)&Words,&(MyWordps[j]->Node));
				++j;
			}
		}
		WordCount = j;
		sprintf(wordbuffer,"%ld",WordCount);
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_WordCount],MPIndexWnd,NULL,
								GTTX_Text,wordbuffer,
								TAG_END);
		if (WordCount) {
			CurrWord = MyWordp[0];
		}
		else {
			CurrWord = NULL;
		}
		GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
								GTLV_Labels,&Words,
								GTLV_Selected, WordCount?0:~0,
								TAG_END);
		if (WordCount) {
			if (*find) {
				for (i = 0; (i<WordCount) && (mytoupper(*find) > mytoupper(*(MyWordps[i]->Node.ln_Name))); ++i) {
				}
				if (i < WordCount) {
					for (; (i<WordCount) && (stricmp(find,MyWordps[i]->Node.ln_Name)>0); ++i) {
					}
				}
				if (WordCount == i) {
					--i;
				}
			}
			else {
				i = 0;
			}
			CurrWord = MyWordps[i];
			GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
									GTLV_Selected, 	i,
									GTLV_MakeVisible, i,
									TAG_END);
		}
		UpdateNodes();
	}
}

static int ReqGad = 0;
#define GAD_FIND		1
#define GAD_SEARCH	2

int main(int argc,char **argv) {
	long opts[OPT_COUNT] = {
		(long)"Docs.Index",
	};
	int notdone = 1;
	FILE *fp;
	char *p,*t;
	ULONG seq;
	struct MyWord *MyWord=NULL,**MyWordp1,**MyWordp2;
	struct MyNode *MyNode,*LastNode = 0, **MyNodep1;
	struct SNode *SNode;
	struct FileName *FileName,*LastName=NULL,**MyNamep1;
	struct MyNode **MyNodes;
	struct FNode *FNode;
	struct NWNode *NWNode;
	int cnt = 0;
	int cnt1 = 0;
	int resx;
	ULONG Signals;
	char drive[FNSIZE],path[FMSIZE],node[FNSIZE],ext[FESIZE];
	ULONG ASig = 0;
	char *MergedWords;
	char *UpperMergedWords;
	ULONG StringLength;
	int c;
	USHORT FileNum;
	USHORT NodeNum;

	if (argc) {
		if (!(rdargs = ReadArgs((char *)TEMPLATE, opts, NULL))) {
			PrintFault(IoErr(), NULL);
			return 10;
		}
	}
	if (!(LocaleBase = OpenLibrary("locale.library",38))) {
		printf("Error Opening locale.library(38)\n");
		return 20;
	}
	Catalog = OpenCatalog(NULL,
  								"mp/runmpindex.catalog",
  								TAG_END);
	if (!(AmigaGuideBase = OpenLibrary("amigaguide.library",34))) {
		printf(GetMessage(MSG_AGUIDE));
		CloseCatalog(Catalog);
		CloseLibrary(LocaleBase);
		return 10;
	}
	if (!(Chain = CreatePool(MEMF_CLEAR,PUDDLE,THRESH))) {
		FreeArgs(rdargs);
		CloseLibrary(AmigaGuideBase);
		printf(GetMessage(MSG_NOMEM));
		CloseCatalog(Catalog);
		CloseLibrary(LocaleBase);
		return 10;
	}
	command = (char *)opts[OPT_COMM];
	PubScreenName = (char *)opts[OPT_PUBS];
	if (PubScreenName) {
		if (!*PubScreenName) {
			PubScreenName = NULL;
		}
	}
	if (fp = fopen((char *)opts[OPT_FROM],"r")) {
		NewList((struct List *)&Words);
		NewList((struct List *)&EmptyList);
		NewList((struct List *)&FileList);
		c = getc(fp);
		while (c != EOF) {
			while ((c!=EOF) && (c != 'M')) {
				if ('F' == c) {
					FileName = Mycalloc(sizeof(struct FileName));
					if (!FirstName) {
						FirstName = FileName;
					}
					if (LastName) {
						LastName->FileName = FileName;
					}
					c = getc(fp);
					FileName->Name = Mycalloc(c+1);
					fread(FileName->Name,c,1,fp);
					strsfn(FileName->Name,drive,path,node,ext);
					FileName->Node.ln_Name = Mystrdup(node);
					FileName->Selected = TRUE;
					FileName->seq = FileCnt;
					AddTail((struct List *)&FileList,&(FileName->Node));
					LastName = FileName;
					++FileCnt;
					c = getc(fp);
				}
				else {
					if (!MyNamep) {
						MyNamep = Mycalloc((FileCnt+1)*sizeof(struct FileName *));
						MyNamep1 = MyNamep;
						LastName = FirstName;
						while (LastName) {
							*MyNamep1++ = LastName;
							LastName = LastName->FileName;
						}
					}
					MyNode = Mycalloc(sizeof(struct MyNode));
					NewList((struct List *)&(MyNode->List));
					if (!FirstNode) {
						FirstNode = MyNode;
					}
					if (LastNode) {
						LastNode->MyNode = MyNode;
					}
					fread(&FileNum,2,1,fp);
					MyNode->FileName = MyNamep[FileNum];
					c = getc(fp);
					MyNode->Name = Mycalloc(c+1);
					fread(MyNode->Name,c,1,fp);
					c = getc(fp);
					MyNode->Title = Mycalloc(c+1);
					fread(MyNode->Title,c,1,fp);
					t = MyNode->UpperTitle = Mycalloc(strlen(MyNode->Title)+1);
					p = MyNode->Title;
					while (*p) {
						*t++ = mytoupper(*p++);
					}
					*t = 0;
					LastNode = MyNode;
					++cnt;
					c = getc(fp);
				}
			}
			fread(&StringLength,4,1,fp);
			MergedWords = Mycalloc(StringLength);
			fread(MergedWords,StringLength,1,fp);
			UpperMergedWords = Mycalloc(StringLength);
			p = MergedWords;
			t = UpperMergedWords;
			for (seq=0; seq<StringLength; ++seq) {
				if (*p) {
					*t++ = mytoupper(*p);
				}
				else {
					++t;
				}
				++p;
			}
			c = getc(fp);
			AllWord.Node.ln_Name = "#?";
			AddTail((struct List *)&Words,&(AllWord.Node));
			NewList((struct List *)&(AllWord.List));
			NewList((struct List *)&(AllWord.FileList));
			MyNodes = AllWord.MyNode = Mycalloc((cnt+1)*sizeof(struct MyNode *));
			MyNodep = Mycalloc((cnt+1)*sizeof(struct MyNode *));
			MyNodeps = Mycalloc((cnt+1)*sizeof(struct MyNode *));
			SNodes = Mycalloc((cnt+1)*sizeof(struct SNode));
			NodeCount = cnt;
			MyNodep1 = MyNodep;
			LastNode = FirstNode;
			while (LastNode) {
				*MyNodep1++ = LastNode;
				SNode = Mycalloc(sizeof(struct SNode));
				SNode->MyNode = LastNode;
				SNode->Node.ln_Name = SNode->MyNode->Title;
				AddTail((struct List *)&(AllWord.List),&(SNode->Node));
				*MyNodes++ = SNode->MyNode;
				LastNode = LastNode->MyNode;
			}
			LastName = FirstName;
			while (LastName) {
				FNode = Mycalloc(sizeof(struct FNode));
				FNode->FileName = LastName;
				AddTail((struct List *)&(AllWord.FileList),(struct Node *)&(FNode->Node));
				LastName = LastName->FileName;
			}
			cnt = 0;
			cnt1 = 0;
			while (c != EOF) {
				if ('W' == c) {
					if (cnt1) {
						BOOL found;
						MyNodes = MyWord->MyNode = Mycalloc((cnt1+1)*sizeof(struct MyNode *));
						for (SNode = (struct SNode *)(MyWord->List.mlh_Head);
							  SNode->Node.ln_Succ;
							  SNode = (struct SNode *)(SNode->Node.ln_Succ)) {
							*MyNodes++ = SNode->MyNode;
							found = FALSE;
							for (FNode = (struct FNode *)(MyWord->FileList.mlh_Head);
								  !found && FNode->Node.mln_Succ;
								  FNode = (struct FNode *)(FNode->Node.mln_Succ)) {
								if (SNode->MyNode->FileName == FNode->FileName) {
									found = TRUE;
								}
							}
							if (!found) {
								FNode = Mycalloc(sizeof(struct FNode));
								FNode->FileName = SNode->MyNode->FileName;
								AddTail((struct List *)&(MyWord->FileList),(struct Node *)&(FNode->Node));
							}
						}
						MyWord->ActualCount = cnt1;
						MyWord->Count = cnt1;
					}
					cnt1 = 0;
					MyWord = Mycalloc(sizeof(struct MyWord));
					fread(&seq,4,1,fp);
					MyWord->Node.ln_Name = MergedWords + seq;
					MyWord->UpperWord = UpperMergedWords + seq;
					AddTail((struct List *)&Words,&(MyWord->Node));
					NewList((struct List *)&(MyWord->List));
					NewList((struct List *)&(MyWord->FileList));
					cnt++;
				}
				else {
					fread(&NodeNum,2,1,fp);
					SNode = Mycalloc(sizeof(struct SNode));
					SNode->MyNode = MyNodep[NodeNum];
					SNode->Node.ln_Name = SNode->MyNode->Title;
					AddTail((struct List *)&(MyWord->List),&(SNode->Node));
					NWNode = Mycalloc(sizeof(struct NWNode));
					NWNode->MyWord = MyWord;
					AddTail((struct List *)&(SNode->MyNode->List),(struct Node *)&(NWNode->Node));
					cnt1++;
				}
				c = getc(fp);
			}
		}
		if (cnt1) {
			BOOL found;
			MyNodes = MyWord->MyNode = Mycalloc((cnt1+1)*sizeof(struct MyNode *));
			for (SNode = (struct SNode *)(MyWord->List.mlh_Head);
				  SNode->Node.ln_Succ;
				  SNode = (struct SNode *)(SNode->Node.ln_Succ)) {
				*MyNodes++ = SNode->MyNode;
				found = FALSE;
				for (FNode = (struct FNode *)(MyWord->FileList.mlh_Head);
					  !found && FNode->Node.mln_Succ;
					  FNode = (struct FNode *)(FNode->Node.mln_Succ)) {
					if (SNode->MyNode->FileName == FNode->FileName) {
						found = TRUE;
					}
				}
				if (!found) {
					FNode = Mycalloc(sizeof(struct FNode));
					FNode->FileName = SNode->MyNode->FileName;
					AddTail((struct List *)&(MyWord->FileList),(struct Node *)&(FNode->Node));
				}
			}
			MyWord->ActualCount = cnt1;
			MyWord->Count = cnt1;
		}
		WordCount = cnt+1;
		MyWordp = Mycalloc((cnt+2)*sizeof(struct MyWord *));
		MyWordps = Mycalloc((cnt+2)*sizeof(struct MyWord *));
		MyWordp1 = MyWordp;
		MyWordp2 = MyWordps;
		for (MyWord = (struct MyWord *)(Words.mlh_Head);
				MyWord->Node.ln_Succ;
				MyWord = (struct MyWord *)(MyWord->Node.ln_Succ)) {
			*MyWordp1++ = MyWord;
			*MyWordp2++ = MyWord;
		}
		ActualWordCount = WordCount;
		if (!SetupScreen()) {
			if (!OpenMPIndexWindow()) {
				UpdateWords();
				nag.nag_BaseName		= "RunMPIndex";
				nag.nag_Name			= "RunMPIndex.guide";
				nag.nag_ClientPort	= "RunMPIndex_HELP";
				nag.nag_Flags			= HTF_NOACTIVATE;
				nag.nag_PubScreen 	= NULL;
				handle = OpenAmigaGuideAsync(&nag, TAG_END);
				if (handle) {
				   ASig = AmigaGuideSignal(handle);
					while (agm = GetAmigaGuideMsg(handle)) {
						ReplyAmigaGuideMsg(agm);
					}
				}
				while (notdone) {
					Signals = Wait(ASig | (1L<<MPIndexWnd->UserPort->mp_SigBit) | HookData.Signal | (1L<<SigNode));
					if (Signals & ASig) {
						while (agm = GetAmigaGuideMsg(handle)) {
							ReplyAmigaGuideMsg(agm);
						}
					}
					if (Signals & (1L<<MPIndexWnd->UserPort->mp_SigBit)) {
						notdone = HandleMPIndexIDCMP();
					}
					if (Signals & (1L<<SigNode)) {
						DisableWindow();
						UpdateNodes();
						EnableWindow();
						ReqGad = GAD_FIND;
					}
					if (Signals & HookData.Signal) {
						DisableWindow();
						UpdateWords();
						EnableWindow();
						ReqGad = GAD_SEARCH;
					}
				}
				if (handle) {
					while (agm = GetAmigaGuideMsg(handle)) {
						ReplyAmigaGuideMsg(agm);
					}
					CloseAmigaGuide(handle);
				}
				resx = 0;
			}
			else {
				printf(GetMessage(MSG_WINDOW));
				resx = 10;
			}
			CloseMPIndexWindow();
		}
		else {
			printf(GetMessage(MSG_SCREEN));
			resx = 10;
		}
		CloseDownScreen();
		fclose(fp);
	}
	else {
		printf(GetMessage(MSG_OPEN),(char *)opts[OPT_FROM]);
		resx = 10;
	}
	if (rdargs) {
		FreeArgs(rdargs);
	}
	if (Chain) {
		DeletePool(Chain);
	}
	CloseLibrary(AmigaGuideBase);
	CloseCatalog(Catalog);
	CloseLibrary(LocaleBase);
	return resx;
}

extern void
ReqClear(void) {
	int i;
	BOOL act = FALSE;
	if (GAD_FIND == ReqGad) {
		for (i=0; !act && (i<10); ++i) {
			act = ActivateGadget(MPIndexGadgets[GDX_Find],MPIndexWnd,NULL);
			if (!act) {
				Delay(i+1);
			}
		}
		ReqGad = 0;
		return;
	}
	if (GAD_SEARCH == ReqGad) {
		for (i=0; !act && (i<10); ++i) {
			act = ActivateGadget(MPIndexGadgets[GDX_Search],MPIndexWnd,NULL);
			if (!act) {
				Delay(i+1);
			}
		}
		ReqGad = 0;
		return;
	}
}

void
UpdateGuide(ULONG act) {
	int i;
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Guide],MPIndexWnd,NULL,
							GTLV_Labels,~0,
							TAG_END);
	if (0 == act) {
		All = TRUE;
		None = FALSE;
		for (i=0; i<FileCnt; ++i) {
			MyNamep[i]->Selected = TRUE;
		}
	}
	else {
		if (1 == act) {
			All = FALSE;
			None = TRUE;
			for (i=0; i<FileCnt; ++i) {
				MyNamep[i]->Selected = FALSE;
			}
		}
	}
	UpdateWords();
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Guide],MPIndexWnd,NULL,
							GTLV_Labels,&FileList,
							TAG_END);
}

int SearchClicked( void )
{
	char *find;
	/* routine when gadget "" is clicked. */
	switch (MPIndexMsg.Class) {
	case IDCMP_GADGETUP:
		if (MPIndexMsg.Code == 0x5f) {
			if (handle) {
				while (agm = GetAmigaGuideMsg(handle)) {
					ReplyAmigaGuideMsg(agm);
				}
				SendAmigaGuideCmdA(handle,"LINK g-search",NULL);
				while (agm = GetAmigaGuideMsg(handle)) {
					ReplyAmigaGuideMsg(agm);
				}
			}
			ActivateGadget((struct Gadget *)MPIndexMsg.IAddress,MPIndexWnd,NULL);
		}
		break;
	case IDCMP_GADGETDOWN:
		GT_GetGadgetAttrs(MPIndexGadgets[GDX_Search],MPIndexWnd,NULL,
								GTST_String,&find,
								TAG_END);
		if (*find && !Mystrstr(find," ")) {
			UpdateWords();
		}
		break;
	default:
		break;
	}
	return 1;
}

int WordsClicked( void )
{
	/* routine when gadget "" is clicked. */
	CurrWord = MyWordps[MPIndexMsg.Code];
	DisableWindow();
	UpdateNodes();
	EnableWindow();
	return 1;
}

int NodesClicked( void )
{
	static ULONG secs = 0;
	static ULONG micros = 0;
	static struct MyNode *LastClickedNode = NULL;
	/* routine when gadget "" is clicked. */
	GT_SetGadgetAttrs(MPIndexGadgets[GDX_Show],MPIndexWnd,NULL,
							GA_Disabled, FALSE,
							TAG_END);
	CurrNode = MyNodeps[MPIndexMsg.Code];
	if (CurrNode == LastClickedNode) {
		if (DoubleClick(secs,micros,MPIndexMsg.Seconds,MPIndexMsg.Micros)) {
			ShowClicked();
		}
	}
	LastClickedNode = CurrNode;
	NewCurrent();
	secs = MPIndexMsg.Seconds;
	micros = MPIndexMsg.Micros;
	return 1;
}

int MPIndexCloseWindow( void )
{
	/* routine for "IDCMP_CLOSEWINDOW". */
	CurrNode = NULL;
	UnCurrent();
	return 0;
}

int MPIndexVanillaKey( void )
{
	/* routine for "IDCMP_VANILLAKEY". */
	int res;
	char c;
	ULONG pos = 0;
	c = toupper(MPIndexMsg.Code);
	if (c == SHOW) {
		ShowClicked();
		res = 1;
	}
	else if (c == CANCEL) {
		res = 0;
	}
	else if (c == 0x1b) {
		res = 0;
	}
	else if (c == SEARCH) {
		ActivateGadget(MPIndexGadgets[GDX_Search],MPIndexWnd,NULL);
		res = 1;
	}
	else if (c == SUBSET) {
		ActivateGadget(MPIndexGadgets[GDX_Find],MPIndexWnd,NULL);
		res = 1;
	}
	else if (MPIndexMsg.Code == tolower(WORDS)) {
		if (WordCount) {
			DisableWindow();
			GT_GetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
									GTLV_Selected, &pos,
									TAG_END);
			if ((ULONG)~0 == pos) {
				pos = 0;
			}
			else {
				++pos;
				if (pos == WordCount) {
					pos = 0;
				}
			}
			GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
									GTLV_Selected, pos,
									GTLV_MakeVisible, pos,
									TAG_END);
			CurrWord = MyWordps[pos];
			UpdateNodes();
			EnableWindow();
		}
		res = 1;
	}
	else if (MPIndexMsg.Code == WORDS) {
		if (WordCount) {
			DisableWindow();
			GT_GetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
									GTLV_Selected, &pos,
									TAG_END);
			if ((ULONG)~0 == pos) {
				pos = WordCount-1;
			}
			else {
				if (0 == pos) {
					pos = WordCount-1;
				}
				else {
					--pos;
				}
			}
			GT_SetGadgetAttrs(MPIndexGadgets[GDX_Words],MPIndexWnd,NULL,
									GTLV_Selected, pos,
									GTLV_MakeVisible, pos,
									TAG_END);
			CurrWord = MyWordps[pos];
			UpdateNodes();
			EnableWindow();
		}
		res = 1;
	}
	else if (MPIndexMsg.Code == tolower(NODES)) {
		if (CurrWord && CurrWord->Count) {
			DisableWindow();
			GT_GetGadgetAttrs(MPIndexGadgets[GDX_Nodes],MPIndexWnd,NULL,
									GTLV_Selected, &pos,
									TAG_END);
			if ((ULONG)~0 == pos) {
				pos = 0;
			}
			else {
				++pos;
				if (pos == CurrWord->Count) {
					pos = 0;
				}
			}
			GT_SetGadgetAttrs(MPIndexGadgets[GDX_Nodes],MPIndexWnd,NULL,
									GTLV_Selected, pos,
									GTLV_MakeVisible, pos,
									TAG_END);
			GT_SetGadgetAttrs(MPIndexGadgets[GDX_Show],MPIndexWnd,NULL,
									GA_Disabled, FALSE,
									TAG_END);
			CurrNode = MyNodeps[pos];
			NewCurrent();
			EnableWindow();
		}
		res = 1;
	}
	else if (MPIndexMsg.Code == NODES) {
		if (CurrWord && CurrWord->Count) {
			DisableWindow();
			GT_GetGadgetAttrs(MPIndexGadgets[GDX_Nodes],MPIndexWnd,NULL,
									GTLV_Selected, &pos,
									TAG_END);
			if ((ULONG)~0 == pos) {
				pos = CurrWord->Count-1;
			}
			else {
				if (0 == pos) {
					pos = CurrWord->Count-1;
				}
				else {
					--pos;
				}
			}
			GT_SetGadgetAttrs(MPIndexGadgets[GDX_Nodes],MPIndexWnd,NULL,
									GTLV_Selected, pos,
									GTLV_MakeVisible, pos,
									TAG_END);
			GT_SetGadgetAttrs(MPIndexGadgets[GDX_Show],MPIndexWnd,NULL,
									GA_Disabled, FALSE,
									TAG_END);
			CurrNode = MyNodeps[pos];
			NewCurrent();
			EnableWindow();
		}
		res = 1;
	}
	else if (c == ALL) {
		DisableWindow();
		UpdateGuide(0);
		EnableWindow();
		res = 1;
	}
	else if (c == NONE) {
		DisableWindow();
		UpdateGuide(1);
		EnableWindow();
		res = 1;
	}
	else {
		res = 1;
	}
	return res;
}

int MPIndexRawKey( void )
{
	/* routine for "IDCMP_RAWKEY". */
	int res;
	char buffer[256];
	switch (MPIndexMsg.Code) {
	case 0x5f:
		if (handle) {
			while (agm = GetAmigaGuideMsg(handle)) {
				ReplyAmigaGuideMsg(agm);
			}
			sprintf(buffer,"LINK %s",GuideNode);
			SendAmigaGuideCmdA(handle,buffer,NULL);
			while (agm = GetAmigaGuideMsg(handle)) {
				ReplyAmigaGuideMsg(agm);
			}
		}
		res = 1;
		break;
	default:
		res = 1;
		break;
	}
	return res;
}

int ShowClicked( void )
{
	char buffer[256];
	char buffer1[128];
	/* routine when gadget "S_how" is clicked. */
	if (CurrNode) {
		DisableWindow();
		sprintf(buffer1,"\"%s/%s\"",CurrNode->FileName->Name,CurrNode->Name);
		if (command) {
			sprintf(buffer,command,buffer1);
			system(buffer);
		}
		else {
			if (handle) {
				while (agm = GetAmigaGuideMsg(handle)) {
					ReplyAmigaGuideMsg(agm);
				}
				sprintf(buffer,"LINK %s",buffer1);
				SendAmigaGuideCmdA(handle,buffer,NULL);
				while (agm = GetAmigaGuideMsg(handle)) {
					ReplyAmigaGuideMsg(agm);
				}
			}
		}
		EnableWindow();
	}
	return 1;
}

int CancelClicked( void )
{
	/* routine when gadget "_Cancel" is clicked. */
	CurrNode = NULL;
	UnCurrent();
	return 0;
}

int FindClicked( void )
{
	if (MPIndexMsg.Code == 0x5f) {
		if (handle) {
			while (agm = GetAmigaGuideMsg(handle)) {
				ReplyAmigaGuideMsg(agm);
			}
			SendAmigaGuideCmdA(handle,"LINK g-subset",NULL);
			while (agm = GetAmigaGuideMsg(handle)) {
				ReplyAmigaGuideMsg(agm);
			}
		}
		ActivateGadget((struct Gadget *)MPIndexMsg.IAddress,MPIndexWnd,NULL);
	}
	return 1;
}

int GuideClicked(void) {
	int i;
	DisableWindow();
	if (MyNamep[MPIndexMsg.Code]->Selected) {
		MyNamep[MPIndexMsg.Code]->Selected = FALSE;
	}
	else {
		MyNamep[MPIndexMsg.Code]->Selected = TRUE;
	}
	None = TRUE;
	All = TRUE;
	for (i=0; i<FileCnt; ++i) {
		if (MyNamep[i]->Selected) {
			None = FALSE;
		}
		else {
			All = FALSE;
		}
	}
	UpdateGuide(2);
	EnableWindow();
	return 1;
}

int AllClicked(void) {
	UpdateGuide(0);
	return 1;
}

int NoneClicked(void) {
	UpdateGuide(1);
	return 1;
}

APTR
Mystrdup(UBYTE *str) {
	return strcpy(Mycalloc(strlen(str)+1),str);
}

#define ALLOC 4080
#define LIMIT 512

APTR
Mycalloc(ULONG size) {
	static char *Current = NULL;
	static int Remaining = 0;
	APTR res;
	if (size > LIMIT) {
		res = AllocPooled(Chain,size);
	}
	else {
		if (size > Remaining) {
			res = AllocPooled(Chain,ALLOC);
			Current = (char *)res + size;
			Remaining = ALLOC - size;
		}
		else {
			res = Current;
			Current += size;
			Remaining -= size;
		}
	}
	if (res) {
		return res;
	}
	if (rdargs) {
		FreeArgs(rdargs);
	}
	if (Chain) {
		DeletePool(Chain);
	}
	if (AmigaGuideBase) {
		CloseLibrary(AmigaGuideBase);
	}
	printf(GetMessage(MSG_NOMEM));
	CloseCatalog(Catalog);
	CloseLibrary(LocaleBase);
	exit(10);
}

// Note both strings are in upper case
BOOL
Mystrstr(char *a,char *b) {
	char *p;
	int al,bl,t;
	if (!*b) {
		return TRUE;
	}
	if (!*a) {
		return FALSE;
	}
	if (!b[1]) {
		for (p=a; *p; ++p) {
			if (*p == *b) {
				return TRUE;
			}
		}
		return FALSE;
	}
	if (!a[1]) {
		return FALSE;
	}
	if (!b[2]) {
		for (p=a; p[1]; ++p) {
			if ((*p == *b) && (p[1] == b[1])) {
				return TRUE;
			}
		}
		return FALSE;
	}
	if (!a[2]) {
		return FALSE;
	}
	al = strlen(a);
	bl = strlen(b);
	if (al < bl) {
		return FALSE;
	}
	if (al == bl) {
		if (*a == *b) {
			if (a[1] == b[1]) {
				if (!strcmp(&a[2],&b[2])) {
					return TRUE;
				}
			}
		}
		return FALSE;
	}
	t = bl-2;
	if (al == (bl+1)) {
		if (*a == *b) {
			if (a[1] == b[1]) {
				if (!strncmp(&a[2],&b[2],t)) {
					return TRUE;
				}
			}
		}
		if (a[1] == *b) {
			if (a[2] == b[1]) {
				if (!strcmp(&a[3],&b[2])) {
					return TRUE;
				}
			}
		}
		return FALSE;
	}
	if (al == (bl+2)) {
		if (*a == *b) {
			if (a[1] == b[1]) {
				if (!strncmp(&a[2],&b[2],t)) {
					return TRUE;
				}
			}
		}
		if (a[1] == *b) {
			if (a[2] == b[1]) {
				if (!strncmp(&a[3],&b[2],t)) {
					return TRUE;
				}
			}
		}
		if (a[2] == *b) {
			if (a[3] == b[1]) {
				if (!strcmp(&a[4],&b[2])) {
					return TRUE;
				}
			}
		}
		return FALSE;
	}
	for (p = a; p[2]; ++p) {
		if (*b == *p) {
			if (b[1] == p[1]) {
				if (!strncmp(&p[2],&b[2],t)) {
					return TRUE;
				}
			}
		}
	}
	return FALSE;
}
