/*	ADBuildIndex.c
	(c) 1989 by Christopher A. Wichura (u12401@uicvm.uic.edu)

	Created: 12/19/89

	Compile with:  LC -d2 -v -cs -rr ADBuildIndex
*/

#include <exec/types.h>
#include <libraries/ArpBase.h>
#include <proto/dos.h>

/* here we have the structures that will be used to hold index entries */
struct IndexData {
	char *id_Func;
	char *id_Lib;
	char *id_Path;
	char *id_File;
	unsigned long id_Offset;
};

struct NextIndexNode {
	struct NextIndexNode *nin_Next;
	struct IndexData nin_IndexData;
};

struct FullIndexNode {
	struct FullIndexNode *fin_Greater;
	struct FullIndexNode *fin_Lesser;
	struct NextIndexNode *fin_Next;
	struct IndexData fin_IndexData;
};

struct IndexNodeNumbered {
	unsigned long inn_Num;
	struct IndexNodeNumbered *inn_Parent;
	struct IndexNodeNumbered *inn_Greater;
	struct IndexNodeNumbered *inn_Lesser;
	struct NextIndexNode *inn_Next;
	struct IndexData inn_IndexData;
};

/* this structure will be used when building name lists.  Note that the
   text field is not actually 2 bytes long, but rather will be allocated to
   the size of the text to be stored when the node is created */
struct TextNode {
	struct TextNode *tn_Next;
	char tn_Text[2];
};

/* here we have some globals that hold the bases of the various lists we
   will be building */
unsigned long Indexs = 0;	/* total number of index nodes */
struct IndexNodeNumbered *FirstIndex = NULL;
struct TextNode *FirstFunc = NULL;
struct TextNode *FirstLib  = NULL;
struct TextNode *FirstPath = NULL;
struct TextNode *FirstFile = NULL;

/* externs to our three text chunks */
extern char __far LibText1[];
extern char __far LibText2[];
extern char __far LibText3[];

/* we will need to be able to tell what our current position is within a
   given file.  However, we will be doing buffered IO as well so we need an
   easy way to get this info.  result is a global variable that the
   io routine will update for us. */
unsigned long CurrOffset;

/* our main buffer pointer will also be made global */
char *MainBuf;

/* the parsed pattern will be stored in a global buffer as well */
char Pattern[10];

/* we will store the current date and time strings in globals, also */
struct DateTime dat;
char Date[LEN_DATSTRING];
char Time[LEN_DATSTRING];

/* if this flag is set then we give a verbose report as we go along */
BOOL Vflag;

/* if this flag is set then don't expand to full pathnames */
BOOL Eflag;

/* we need to make a ResList global so that when we call AddIndex and
   AddTextNode they don't allocate their memory into the nested ResList
   that is created, thus losing their info right away */
struct ResList *MainRes;

/* we will make our outhandle global as well */
BPTR OutHandle;

/* here we prototype routines used in this file */
#define strcpy __builtin_strcpy
#define strlen __builtin_strlen

void Msg(char *);
void XCEXIT(int);
void NoMem(void);
void ChkAbt(void);
int  GetChar(BPTR);
void GetLine(BPTR);
void DoDir(char *);
int  strlen(char *);
void WriteIndex(void);
void DoFirstDir(char *);
void WriteIndexNodes(void);
void strcpy(char *, char *);
int  stricmp(char *, char *);
void ProcessFile(BPTR, char *, char *);
void WriteTextList(char *, struct TextNode *);
int  Compare(char *, char *, struct TrxtNode *);
int  WriteIndexTree(struct IndexNodeNumbered *);
int  WriteNextNodes(struct IndexNodeNumbered *);
void AddIndex(char *, char *, char *, char *, unsigned long);
unsigned long AddTextNode(struct TextNode *, char *, struct TextNode **);
void FillInIndexData(struct IndexData *, char *, char *, char *, char *, unsigned long);
void AddNextNode(struct IndexNodeNumbered *, char *, char *, char *, char *, unsigned long);

/* library bases */
extern struct ArpBase *ArpBase;

/* here we have some defines */
#define BUF_SIZE 2000
#define FORMFEED 12
#define EOF -1

#define ARP_TEMPLATE "RootDirs/...,VB=VERBOSE/s,NE=NOEXPAND/s"
#define ARG_DIRS 0
#define ARG_VERBOSE 1
#define ARG_NOEXPAND 2
#define ARG_sizeof 3

#define ESC(a)	  "\x1B[" a "m"
#define ARGA(a)	  " <" ESC("31") a ESC("32") ">"
#define ARGB(a)   " [" ESC("33") a ESC("32") "]"
#define NAME	  ESC("1;33;42") "ADBuildIndex" ESC("0;32;40")
#define ARP_HELP NAME " v1.0 \xA9 1989 by " \
		 ESC("4;33;42") "Christopher A. Wichura" ESC("0;33;40") \
		 " (" ESC("32") "u12401" ESC("33") "@" ESC("32") \
		 "uicvm.uic.edu" ESC("33") ")\n" ESC("32") \
		 "Usage:       " NAME ARGA("dir1") ARGA("dir2") ARGA("dir3") \
		 ARGA("... dirN") "\n                         " \
		 ARGB("VERBOSE") ARGB("NOEXPAND") \
		 ESC("31") "\n"

/* this is the main routine */
void __stdargs __asm main(register __a0 char *cmdline, register __d0 int cmdlen)
{
	register signed long c;
	register char **ptr;
	char *Args[ARG_sizeof];

	if (!(MainRes = FindTaskResList()))
		if (!(MainRes = CreateTaskResList())) {
			Msg("Fatal tracker error!");
			XCEXIT(10);
		}

	for (c = 0; c < ARG_sizeof; c++)
		Args[c] = 0;

	c = GADS(cmdline, cmdlen, ARP_HELP, Args, ARP_TEMPLATE);
	if (c < 0) {
		Msg(Args[0]);
		XCEXIT(5);
	}

	if (!(MainBuf = ArpAlloc(BUF_SIZE)))
		NoMem();

	PreParse("*.doc", Pattern);

	DateStamp(&dat.dat_Stamp);
	dat.dat_Format = FORMAT_DOS;
	dat.dat_Flags = 0;
	dat.dat_StrDay = NULL;
	dat.dat_StrDate = Date;
	dat.dat_StrTime = Time;
	StamptoStr(&dat);

	Vflag = (BOOL)Args[ARG_VERBOSE];
	Eflag = (BOOL)Args[ARG_NOEXPAND];

	ptr = (char **)Args[ARG_DIRS];
	if (*ptr)
		while (*ptr)
			DoFirstDir(*ptr++);
	else
		DoFirstDir("AutoDocs:");

	if (Indexs)
		WriteIndex();
}

/* this is the top level routine of the scanner.  it's only purpose is
   to expand a directory name into its full path specification. */
void DoFirstDir(char *dir)
{
	register char *Expanded;
	BPTR lock;
	struct DefaultTracker *tracklock;

	if (Eflag) {
		DoDir(dir);
		return;
	}

	if (!(Expanded = ArpAlloc(BUF_SIZE)))
		NoMem();

	SPrintf(MainBuf, "Error expanding \"%s\" pathname.", dir);

	if (!(lock = ArpLock(dir, ACCESS_READ))) {
		Puts(MainBuf);
		return;
	}
	tracklock = LastTracker;

	if (!(PathName(lock, Expanded, (BUF_SIZE / DSIZE) - 1))) {
		Puts(MainBuf);
		return;
	}
	FreeTrackedItem(tracklock);

	DoDir(Expanded);

	FreeTrackedItem((struct DefaultTracker *)Expanded);
}

/* here we have a high level routine of the scanner.  it takes as its
   argument a pointer to the directory to scan for doc files.  if it finds
   any directories in the current directory it will recurse to scan them as
   well */
void DoDir(char *dir)
{
	register struct FileInfoBlock *fib;
	register BPTR fhl;
	register BPTR fh;
	struct DefaultTracker *track;
	char *NewPath;
	char *ErrMsg;

	if (!CreateTaskResList()) {
		Msg("Fatal tracker error!");
		XCEXIT(10);
	}

	if (!(fib = ArpAlloc(sizeof(struct FileInfoBlock))))
		NoMem();

	if (!(ErrMsg = ArpAlloc(BUF_SIZE)))
		NoMem();

	SPrintf(ErrMsg, "Error scanning \"%s\" directory.", dir);

	if (!(NewPath = ArpAlloc(BUF_SIZE)))
		NoMem();

	if (!(fhl = ArpLock(dir, ACCESS_READ))) {
		Puts(ErrMsg);
		goto EndDoDir;
	}

	if (!Examine(fhl, fib)) {
		Puts(ErrMsg);
		goto EndDoDir;
	}

	if (fib->fib_DirEntryType < 0) {
		Printf("%s is not a directory!\n", dir);
		goto EndDoDir;
	}

	if (Vflag)
		Printf("Scanning directory \"%s\"...\n", dir);

	while (ExNext(fhl, fib)) {
		ChkAbt();

		strcpy(NewPath, dir);
		TackOn(NewPath, fib->fib_FileName);

		if (fib->fib_DirEntryType > 0) {
			DoDir(NewPath);
			continue;
		}

		if (!PatternMatch(Pattern, fib->fib_FileName)) {
			if (Vflag)
				Printf("\"%s\" is not an AutoDoc file.\n", fib->fib_FileName);
			continue;
		}

		if (!(fh = ArpOpen(NewPath, MODE_OLDFILE))) {
			Printf("Error scanning \"%s\" AutoDoc file.\n", NewPath);
			continue;
		}

		track = LastTracker;
		Printf("Scanning \"%s\"...\n", NewPath);
		ProcessFile(fh, dir, fib->fib_FileName);
		FreeTrackedItem(track);
	}

	if (IoErr() != ERROR_NO_MORE_ENTRIES)
		Puts(ErrMsg);

EndDoDir:
	FreeTaskResList();
}

/* this is the next level down in the scan routines.  it takes a full
   pathname/filename and scans the file to see what functions it
   describes as its first argument.  the second arguement points to
   the path name of this file and the third is a pointer to the actual
   filename */
void ProcessFile(BPTR fh, char *path, char *file)
{
	register unsigned long offset;
	register char *ptr1;
	register char *ptr2;
	char func[100];

	CurrOffset = 0;
	for (;;) {
		ChkAbt();

		GetLine(fh);
		if (!*MainBuf)
			return;

		if (*MainBuf == FORMFEED) {
		   if (*(MainBuf + 1) == FORMFEED)
			return;
		   else if (*(MainBuf + 1) == '\n') {
			GetLine(fh);
			offset = CurrOffset;
			if (*MainBuf) {
				ptr1 = MainBuf;
				while (*ptr1 == ' ' || *ptr1 == '\t')
					ptr1++;
				ptr2 = ptr1;
				while ((*ptr2 != ' ') && (*ptr2 != '\n') && (*ptr2 != '\t'))
					ptr2++;
				*ptr2 = 0;
				while((*ptr2 != '/') && (ptr2 > ptr1))
					ptr2--;
				if (*ptr2 == '/')
					strcpy(func, ptr2+1);
				else
					strcpy(func, ptr2);
				*ptr2 = 0;
				AddIndex(func, ptr1, path, file, offset);
			}
		   } else
			Printf("-- Bad function separator found at offset %ld of AutoDoc file.\n", CurrOffset - strlen(MainBuf));
		}
	}
}

/* this is a mighty important routine here.  it adds an entry into the
   index lists. */
void AddIndex(char *func, char *lib, char *path, char *file, unsigned long offset)
{
	register struct IndexNodeNumbered *innptr = FirstIndex;
	register struct IndexNodeNumbered *linnptr = NULL;
	char *splitloc;
	char *NewPath;
	register int comparison;

	if (Vflag)
		Printf("%s; %s; %s; %s; %ld\n", func, lib, path, file, offset);

	if (!(NewPath = ArpAlloc(BUF_SIZE)))
		NoMem();

	strcpy(NewPath, path);
	TackOn(NewPath, "a");
	splitloc = BaseName(NewPath);
	*splitloc = 0;

	while (innptr) {
		linnptr = innptr;
		comparison = Compare(func, innptr->inn_IndexData.id_Func, FirstFunc);

		if (!comparison) {
			AddNextNode(innptr, func, lib, NewPath, file, offset);
			goto EndAddIndex;
		}

		if (comparison > 0)
			innptr = innptr->inn_Greater;
		else
			innptr = innptr->inn_Lesser;
	}

	/* if we make it here then we need to add a new IndexNodeNumb struct */

	if (!(innptr = (struct IndexNodeNumbered *)RListAlloc(MainRes, sizeof(struct IndexNodeNumbered))))
		NoMem();

	if (linnptr) {
		innptr->inn_Parent = linnptr;
		if (comparison > 0)
			linnptr->inn_Greater = innptr;
		else
			linnptr->inn_Lesser = innptr;
	} else
		FirstIndex = innptr;

	innptr->inn_Num = Indexs++;
	FillInIndexData(&innptr->inn_IndexData, func, lib, NewPath, file, offset);

EndAddIndex:
	FreeTrackedItem((struct DefaultTracker *)NewPath);
}

/* this routine will add a NextIndexNode to a chain off a FullIndexNode.
   it checks to make sure that the node doesn't already exist first
   as well */
void AddNextNode(struct IndexNodeNumbered *innptr, char *func, char *lib, char *path, char *file, unsigned long offset)
{
	register struct NextIndexNode *ninptr = (struct NextIndexNode *)&innptr->inn_Next;
	register struct NextIndexNode *lninptr;
	register int flag;

	while (ninptr) {
		lninptr = ninptr;
		flag = 0;

		if (Compare(func, ninptr->nin_IndexData.id_Func, FirstFunc))
			flag++;
		if (Compare(lib, ninptr->nin_IndexData.id_Lib, FirstLib))
			flag++;
		if (Compare(path, ninptr->nin_IndexData.id_Path, FirstPath))
			flag++;
		if (Compare(file, ninptr->nin_IndexData.id_File, FirstFile))
			flag++;
		if (offset != ninptr->nin_IndexData.id_Offset)
			flag++;

		if (flag == 0)
			return;		/* this node already in list */

		ninptr = ninptr->nin_Next;
	}

	/* if we get down here we are adding a new node */
	if (!(ninptr = (struct NextIndexNode *)RListAlloc(MainRes, sizeof(struct NextIndexNode))))
		NoMem();

	/* we don't need to make sure the last node is there as it always
	   will not matter what because the first node is guarenteed to be
	   present (if it wasn't then this routine wouldn't have been
	   called). */
	lninptr->nin_Next = ninptr;

	FillInIndexData(&ninptr->nin_IndexData, func, lib, path, file, offset);
}

/* this routine is used to compare a text string against a value in an
   IndexData struct.  this is needed because at the time the index is
   being build we are only stuffing the entry number and not an actual
   pointer into this location.  Thus we use this routine to look up the
   actual text based on its number and compare it against that. */
int Compare(char *text, char *num, struct TextNode *tn)
{
	register int number = 0;

	while (number++ < (int)num)
		tn = tn->tn_Next;

	return stricmp(text, tn->tn_Text);
}

/* this routine will write out the index. */
void WriteIndex(void)
{
	if (!(OutHandle = ArpOpen("AutoDocIndex.a", MODE_NEWFILE))) {
		Msg("Could not create output file.");
		XCEXIT(5);
	}
	Puts("Writing output file...");

	FPrintf(OutHandle, "; AutoDocIndex.a built by ADBuildIndex on %s at %s.\n\n", Date, Time);

	FPrintf(OutHandle, "%s%s at %s%s", LibText1, Date, Time, LibText2);

	FPrintf(OutHandle, "\n; this is the datestamp of the index's creation.\n\n"
			   "CreationStamp:\n\tdc.l %ld\n\tdc.l %ld\n\tdc.l %ld\n",
			   dat.dat_Stamp.ds_Days, dat.dat_Stamp.ds_Minute, dat.dat_Stamp.ds_Tick);

	FPrintf(OutHandle, "\n; these are all the index nodes.\n\n");
	WriteIndexNodes();

	FPrintf(OutHandle, "\n; these are all the function names.\n\n");
	WriteTextList("Func", FirstFunc);

	FPrintf(OutHandle, "\n; these are all the library names.\n\n");
	WriteTextList("Lib", FirstLib);

	FPrintf(OutHandle, "\n; these are all the path names.\n\n");
	WriteTextList("Path", FirstPath);

	FPrintf(OutHandle, "\n; these are all the file names.\n\n");
	WriteTextList("File", FirstFile);

	FPrintf(OutHandle, "%s", LibText3);
}

/* this routine will spit out all the index nodes */
void WriteIndexNodes(void)
{
	register int Nexts = 0;

	Printf("  Writing index nodes...");

	FPrintf(OutHandle, "FirstIndexNode:\n");
	if (FirstIndex->inn_Greater)
		FPrintf(OutHandle, "\tdc.l Index%ld\n", FirstIndex->inn_Greater->inn_Num);
	else
		FPrintf(OutHandle, "\tdc.l 0\n");
	if (FirstIndex->inn_Lesser)
		FPrintf(OutHandle, "\tdc.l Index%ld\n", FirstIndex->inn_Lesser->inn_Num);
	else
		FPrintf(OutHandle, "\tdc.l 0\n");
	if (FirstIndex->inn_Next)
		FPrintf(OutHandle, "\tdc.l Next0_0\n");
	else
		FPrintf(OutHandle, "\tdc.l 0\n");
	FPrintf(OutHandle, "\tdc.l Func%ld\n", (int)FirstIndex->inn_IndexData.id_Func);
	FPrintf(OutHandle, "\tdc.l Lib%ld\n",  (int)FirstIndex->inn_IndexData.id_Lib );
	FPrintf(OutHandle, "\tdc.l Path%ld\n", (int)FirstIndex->inn_IndexData.id_Path);
	FPrintf(OutHandle, "\tdc.l File%ld\n", (int)FirstIndex->inn_IndexData.id_File);
	FPrintf(OutHandle, "\tdc.l %ld\n", FirstIndex->inn_IndexData.id_Offset);
	Nexts += WriteNextNodes(FirstIndex);

	Nexts += WriteIndexTree(FirstIndex);

	Printf("  %ld nodes with %ld sub-nodes output.\n", Indexs, Nexts);
}

/* this writes out the bulk of the index tree */
int WriteIndexTree(struct IndexNodeNumbered *innptr)
{
	register struct IndexNodeNumbered *dinnptr;
	register int nexts = 0;

	for (;innptr;) {
		ChkAbt();

		if (innptr->inn_Greater && innptr->inn_Greater->inn_Parent) {
			innptr = innptr->inn_Greater;
			continue;
		}

		if (innptr->inn_Lesser && innptr->inn_Lesser->inn_Parent) {
			innptr = innptr->inn_Lesser;
			continue;
		}

		if (innptr->inn_Num) {
			FPrintf(OutHandle, "\nIndex%ld:\n", innptr->inn_Num);
			if (innptr->inn_Greater)
				FPrintf(OutHandle, "\tdc.l Index%ld\n", innptr->inn_Greater->inn_Num);
			else
				FPrintf(OutHandle, "\tdc.l 0\n");
			if (innptr->inn_Lesser)
				FPrintf(OutHandle, "\tdc.l Index%ld\n", innptr->inn_Lesser->inn_Num);
			else
				FPrintf(OutHandle, "\tdc.l 0\n");
			if (innptr->inn_Next)
				FPrintf(OutHandle, "\tdc.l Next%ld_0\n", innptr->inn_Num);
			else
				FPrintf(OutHandle, "\tdc.l 0\n");
			FPrintf(OutHandle, "\tdc.l Func%ld\n", (int)innptr->inn_IndexData.id_Func);
			FPrintf(OutHandle, "\tdc.l Lib%ld\n",  (int)innptr->inn_IndexData.id_Lib );
			FPrintf(OutHandle, "\tdc.l Path%ld\n", (int)innptr->inn_IndexData.id_Path);
			FPrintf(OutHandle, "\tdc.l File%ld\n", (int)innptr->inn_IndexData.id_File);
			FPrintf(OutHandle, "\tdc.l %ld\n", innptr->inn_IndexData.id_Offset);
			nexts += WriteNextNodes(innptr);
		}

		dinnptr = innptr;
		innptr = innptr->inn_Parent;
		dinnptr->inn_Parent = NULL;
	}

	return nexts;
}

/* this will spit out all the NextIndexNodes that might be attached to a
   FullIndex */
int WriteNextNodes(struct IndexNodeNumbered *innptr)
{
	register struct NextIndexNode *ninptr = innptr->inn_Next;
	register int nexts = 0;

	while (ninptr) {
		ChkAbt();

		FPrintf(OutHandle, "\nNext%ld_%ld:\n", innptr->inn_Num, nexts);
		if (ninptr->nin_Next)
			FPrintf(OutHandle, "\tdc.l Next%ld_%ld\n", innptr->inn_Num, nexts + 1);
		else
			FPrintf(OutHandle, "\tdc.l 0\n");
		FPrintf(OutHandle, "\tdc.l Func%ld\n", (int)ninptr->nin_IndexData.id_Func);
		FPrintf(OutHandle, "\tdc.l Lib%ld\n",  (int)ninptr->nin_IndexData.id_Lib );
		FPrintf(OutHandle, "\tdc.l Path%ld\n", (int)ninptr->nin_IndexData.id_Path);
		FPrintf(OutHandle, "\tdc.l File%ld\n", (int)ninptr->nin_IndexData.id_File);
		FPrintf(OutHandle, "\tdc.l %ld\n", ninptr->nin_IndexData.id_Offset);

		ninptr = ninptr->nin_Next;
		nexts++;
	}

	return nexts;
}

/* this routine will spit a TextNode list out to the output file */
void WriteTextList(char *name, struct TextNode *tn)
{
	register int number = 0;

	Printf("  Writing %ss...", name);

	while (tn) {
		ChkAbt();

		FPrintf(OutHandle, "%s%ld: dc.b \"%s\",0\n", name, number, tn->tn_Text);
		number++;
		tn = tn->tn_Next;
	}

	Printf("  %ld unique entries output.\n", number);
}

/* this routine will store all the fields needed into an IndexData struct */
void FillInIndexData(struct IndexData *idptr, char *func, char *lib, char *path, char *file, unsigned long offset)
{
	idptr->id_Func = (char *)AddTextNode(FirstFunc, func, &FirstFunc);
	idptr->id_Lib  = (char *)AddTextNode(FirstLib,  lib,  &FirstLib );
	idptr->id_Path = (char *)AddTextNode(FirstPath, path, &FirstPath);
	idptr->id_File = (char *)AddTextNode(FirstFile, file, &FirstFile);
	idptr->id_Offset = offset;
}

/* this routine will add a text node to the appropriate list.  it checks
   to make sure that the entry does not exist and will not add it if it
   does. */
unsigned long AddTextNode(struct TextNode *tn, char *entry, struct TextNode **first)
{
	register struct TextNode *ltn = NULL;
	register unsigned long number = 0;

	while (tn) {
		if (!stricmp(tn->tn_Text, entry))
			return number;

		ltn = tn;
		tn = tn->tn_Next;
		number++;
	}

	if (!(tn = (struct TextNode *)RListAlloc(MainRes, strlen(entry) + 5)))
		NoMem();

	strcpy(tn->tn_Text, entry);
	if (ltn)
		ltn->tn_Next = tn;
	else
		*first = tn;

	return number;
}

/* this routine prints out general error messages */
void Msg(char *msg)
{
	Printf("- ADBuildIndex: %s\n", msg);
}

/* the most common error message that causes us to exit is an out of
   memory message so here we have it as a seperate function */
void NoMem(void)
{
	Msg("Out of memory!");
	XCEXIT(5);
}

/* this routine will get a line from the specified input file */
void GetLine(BPTR fh)
{
	register char *ptr = MainBuf;
	register int c;
	register int count = 0;

	while ((c = GetChar(fh)) != EOF) {
		*ptr++ = c;
		count++;
		if (count == BUF_SIZE - 1 || c == '\n')
			break;
	}

	*ptr = 0;
}

/* this is the buffered input routine.  note that it checks the given file
   handle against the last used file handle to know if it has been given a
   new file and must do a new seek. */
int GetChar(BPTR fh)
{
	register int c;
	static int bufptr = 0;
	static char buf[81];

	if (CurrOffset == 0 || bufptr == 80) {
		c = Read(fh, buf, 80);
		if (c == 0)
			return EOF;
		if (c == -1) {
			Printf("-- DOS Error #%ld reading AutoDoc file.", IoErr());
			return EOF;
		}
		buf[c] = EOF;
		bufptr = 0;
	}

	CurrOffset++;

	/* this will make sure we always return EOF if we hit the end */
	c = (int)buf[bufptr++];
	if (c == EOF)
		bufptr = 80;

	return c;
}

/* this function will check for an abort and exit gracefully if so.  Note
   that many of the resources we use are tracked by arp.library and thus
   need not be explicitly freed here. */
void ChkAbt(void)
{
	if (CheckAbort(NULL)) {
		Msg("Execution terminated!");
		XCEXIT(1);
	}
}
