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

	Created:  12/17/89

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

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

/* here we define the IndexData structure so we know what's what */
struct IndexData {
	char *id_Func;
	char *id_Lib;
	char *id_Path;
	char *id_File;
	unsigned long id_Offset;
};

/* here we prototype the four functions found in the index library */
struct IndexData *GetFirstIndex(char *);
struct IndexData *GetNextIndex(struct IndexData *);
void FreeIndex(struct IndexData *);
void GetCreationStamp(struct DateStamp *);
#pragma libcall AutoDocIndexBase GetFirstIndex    1e 801
#pragma libcall AutoDocIndexBase GetNextIndex     24 801
#pragma libcall AutoDocIndexBase FreeIndex        2a 801
#pragma libcall AutoDocIndexBase GetCreationStamp 30 101

/* here we prototype the functions found/used in this file */
void Msg(char *);
int  GetChar(void);
void GetLine(void);
void PrintFunc(char *);
int  ChkAbt(struct IndexData *, BOOL);
void DisplayFunc(struct IndexData *);
void __builtin_strcpy(char *, char *);
void XCEXIT(int);

/* handle the library bases */
struct Library *AutoDocIndexBase;
extern struct ArpBase *ArpBase;

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

#define ARP_TEMPLATE "Functions/...,TO/k,OPT/k"
#define ARG_FILES 0
#define ARG_TO 1
#define ARG_OPT 2
#define ARG_sizeof 3

#define ESC(a)	  "\x1B[" a "m"
#define ARGA(a)	  " <" ESC("31") a ESC("32") ">"
#define ARGB(a,b) " [" ESC("33") a ESC("31") " " b ESC("32") "]"
#define NAME	  ESC("1;33;42") "ADLookUp" 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("func1") ARGA("func2") ARGA("func3") \
		 ARGA("... funcN") ARGB("TO", "Outfile") ARGB("OPT", "B|C") \
		 ESC("31") "\n"

/* make the file handles global so all functions can use them */
BPTR OutHandle;
BPTR InHandle;

/* the pointers to our storage buffers will also be made global */
char *MainBuf;
char *FileErrBuf;

/* here we have a couple of global flags */
BOOL NewSeek;
BOOL Banners = 0;
BOOL SayCreation = 0;

/* here we have the main routine */
void __stdargs __asm main (register __a0 char *cmdline, register __d0 int cmdlen)
{
	register signed long c;
	register char *optp;
	register char **ptr;
	register BPTR fh;
	char *Args[ARG_sizeof];

	OutHandle = Output();

	if (!(AutoDocIndexBase = ArpOpenLibrary("AutoDocIndex.library", 0))) {
		Msg("No index library available!");
		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 (Args[ARG_OPT]) {
		optp = Args[ARG_OPT];

		while ((c = Toupper(*optp++))) {
			switch (c) {
				case 'B':
				Banners = 1;
				break;

				case 'C':
				SayCreation = 1;
				break;
			}
		}
	}

	if (Args[ARG_TO]) {
		if (!(fh = ArpOpen(Args[ARG_TO], MODE_NEWFILE))) {
			Msg("Could not create output file!");
			XCEXIT(5);
		}
		OutHandle = fh;
	}

	if (!(MainBuf = ArpAlloc(BUF_SIZE))) {
		Msg("Out of memory!");
		XCEXIT(5);
	}

	if (SayCreation) {
		struct DateTime dat;
		char Date[LEN_DATSTRING];
		char Time[LEN_DATSTRING];

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

		for (c = 0; c < LEN_DATSTRING; c++)
			Date[c] = Time[c] = 0;

		StamptoStr(&dat);

		SPrintf(MainBuf, "Index created on %s at %s.", Date, Time);
		Msg(MainBuf);
		XCEXIT(0);
	}

	if (!(FileErrBuf = ArpAlloc(BUF_SIZE))) {
		Msg("Out of memory!");
		XCEXIT(5);
	}

	ptr = (char **)Args[ARG_FILES];
	while (*ptr)
		PrintFunc(*ptr++);
}

/* this is our general message output routine. */
void Msg(char *msg)
{
	FPrintf(OutHandle, "- ADLookUp: %s\n", msg);
}

/* this is the routine that looks up a function and its duplicates */
void PrintFunc(char *function)
{
	register struct IndexData *idptr;
	register struct IndexData *lidptr;

	if (!(idptr = GetFirstIndex(function))) {
		SPrintf(FileErrBuf, "\"%s\" not found.", function);
		Msg(FileErrBuf);
		return;
	}

	while (idptr) {
		ChkAbt(idptr, 0);
		DisplayFunc(idptr);
		lidptr = idptr;
		idptr = GetNextIndex(idptr);
		FreeIndex(lidptr);
	}
}

/* this routine actually sends the function to the OutHandle */
void DisplayFunc(struct IndexData *idptr)
{
	register struct DefaultTracker *track;

	__builtin_strcpy(MainBuf, idptr->id_Path);
	TackOn(MainBuf, idptr->id_File);
	SPrintf(FileErrBuf, "Error reading \"%s\" AutoDoc file.", MainBuf);

	if (!(InHandle = ArpOpen(MainBuf, MODE_OLDFILE))) {
		Msg(FileErrBuf);
		return;
	}
	track = LastTracker;

	if (Seek(InHandle, idptr->id_Offset, OFFSET_BEGINNING) == -1) {
		Msg(FileErrBuf);
		goto EndDisplay;
	}

	NewSeek = 1;

	if (Banners)
		FPrintf(OutHandle, "*** AutoDoc: %s in %s ***\n",
			idptr->id_Func, idptr->id_Lib);

	for (;;) {
		if (ChkAbt(idptr, 1)) {
			Msg("Read aborted.");
			break;
		}

		GetLine();
		if (*MainBuf == 0 || *MainBuf == FORMFEED)
			break;
		FPrintf(OutHandle, "%s", MainBuf);
	}

EndDisplay:
	FreeTrackedItem(track);
}

/* this function will input one lines worth from the given file handle */
void GetLine(void)
{
	register char *ptr = MainBuf;
	register int c;
	register int count = 0;

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

	*ptr = 0;
}

/* here we do buffered input for getting a character at a time */
int GetChar(void)
{
	static int bufptr = 0;
	static char buf[81];

	if (NewSeek || (bufptr == 80)) {
		bufptr = Read(InHandle, buf, 80);
		if (bufptr == 0)
			return EOF;
		if (bufptr == -1) {
			Msg(FileErrBuf);
			return EOF;
		}
		buf[bufptr] = EOF;
		bufptr = NewSeek = 0;
	}

	return (int)buf[bufptr++];
}

/* this routine will check if the user hit abort and print a message and
   die if so.  Virtually all resources used by ADLookUp are being tracked
   by arp.library for us so we don't have to worry about freeing them all.
   However, we do need to make sure that we free the current index node (if
   it exists) so that is passed to this routine as its only argument.
   if the second argument is 0 then it will abort regardless of which
   abort key you hit.  if the second arg is 1 it will abort on
   CTRL-C, but return a break flag if it gets CTRL-D */
#define MASK SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D
int ChkAbt(struct IndexData *idptr, BOOL flag)
{
	register BOOL masks;

	if ((masks = CheckBreak(MASK, NULL))) {
		if (flag && (masks & SIGBREAKF_CTRL_D))
			return 1;
		Msg("Execution terminated!");
		if (idptr)
			FreeIndex(idptr);
		XCEXIT(1);
	}

	return 0;
}
