/*	MAGMAN.C - Magazine Articles Manager for the Commodore Amiga computer.

	Copyright 1990 by Mike Budahn
	
	Searches a comma-delimited, ASCII file for string matches.
	The file must contain the magazine title in the first field,
	the issue date in the next field, and comma/space-delimited
	fields of topic keywords.  A carriage return ends the record.
	
	COMPILE:  lc -v -Lcd
	NOTE: Optimizing adds 12 bytes.
	
	VERS-DATE--NOTES
	---------------
	2.02-03/90-Fixed a terrible bug which caused some of the data file
	buffers to be over-written by junk.  The problem was that the keyword
	gadget allowed for more input than the buffer provided.  As a result,
	after the filling up the buffer, the gadget kept taking input, stuffing
	it into whatever memory it was around.  Gotta watch that.
	
	2.01-02/90-Removed the console window which showed up when running from
	WorkBench. Added a Find menu selection. Moved the window to upper left
	corner.
	
	2.00-01/90-Fully Amigatized with both enter & search functions combined
	into this one module. Menu added.
	
	1.05-12/89-Eliminated lots of variables by printing one item at a time;
	shortened window. Added prompts to the window title. Added mycopy(),
	fixed bug which prevented the program from finding items in the
	database which were followed by COMMAs or EOLs.
	
	1.00-08/89-Original version.
*/
#include <exec/types.h>
#include <intuition/intuition.h>
#include <proto/intuition.h>
#include <proto/exec.h>
#include <stdio.h>
#include "magman.h"

#define FILENAME "MAG:DataBase"
#define m_Rp window->RPort
/* Setup Defaults */
#define MAX_STRING 256
/* Gadget Numbers */
#define WORDSGADGET	3
#define ISSUEGADGET	2
#define TITLEGADGET	1
#define FINDGADGET	0
/* Menus */
#define ADDDATA		0
#define SAVEDATA	1
#define FINDDATA	2
/* Character Codes */
#define EOL 10			/* End of file line character is hex 0A */
#define COMMA 44		/* ASCII comma is decimal 44 */
#define ESCKEY 27		/* The Escape Key */
#define SPACE ' '		/* White Space */

char *msg_Intro			= "Magazine Manager V.2.02 by Mike Budahn" ;
char *msg_Header		= "MagMan» Enter Data." ;
char *msg_SavedOK		= "MagMan» Data Saved Okay." ;
char *msg_Searching		= "MagMan» Searching...Wait..." ;
char *msg_EndSearch		= "MagMan» Search Completed." ;
char *msg_DoSearch		= "MagMan» Enter Text To Search For." ;
char *msg_FileError		= "STATUS» Data Not Saved. Error Opening File." ;
char *msg_FieldError	= "STATUS» Data Not Saved. A Field Is Empty." ;
char *msg_IOError		= "STATUS» Error Opening Database." ;
char *msg_Prompt		= "...ESC TO QUIT...ANY KEY TO CONTINUE..." ;

struct IntuitionBase	*IntuitionBase ;
struct GfxBase			*GfxBase ;
struct Window			*window ;
struct IntuiMessage		*message ;
struct IntuitionBase	*OpenLibrary() ;
struct GfxBase			*OpenLibrary() ;
struct Window			*OpenWindow() ;

void open_all(void) ;
void close_all(int) ;
void clear_gadgets(void) ;
void init(UBYTE *) ;
void find_match(void) ;
int handle_gadget(struct Gadget *) ;
int handle_menu(long) ;
int pause(void) ;
int str_position(char *, char *) ;
char *mycopy(char *, char *) ;

void _main()
{
	APTR address ;
	ULONG class ;
	USHORT code ;
	USHORT again = TRUE ;
	/*
	Open everything but the database file.
	*/
	open_all() ;
	/*
	Loop until the close window gadget is clicked.
	Note: right now, errors do not abort the loop.
	Close the window yourself after irresolvable STATUS messages.
	*/
	do {
		Wait(1<<window->UserPort->mp_SigBit) ;
		while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) != NULL)
		{
			class = message->Class ;
			code = message->Code ;
			address = message->IAddress ;
			ReplyMsg((struct Message *)message) ;
			switch (class) {
				/**/
				case CLOSEWINDOW:
				again = FALSE ;
				break ;
				/**/
				case GADGETUP:
				again = handle_gadget((struct Gadget *)address) ;
				break ;
				/**/
				case MENUPICK:
				again = handle_menu(code) ;
				break ;
			}
		}
	} while (again == TRUE) ;
	close_all(0) ;
}
int handle_menu(long m_number)
{
	FILE *out_file, *fopen() ;
	struct MenuItem *menu ;
	long item ;
	/*
	Find the last menu item selected,
	although use of command keys is obviously most convenient.
	*/
	while (m_number != MENUNULL) {
		item = m_number ;
		menu = (struct MenuItem *)ItemAddress(&Menu1, m_number) ;
		m_number = menu->NextSelect ;
	}
	/*
	Just in case of an errant item number.
	*/
	if (item == MENUNULL) return(TRUE) ;
	switch (ITEMNUM(item)) {
		/**/
		case SAVEDATA:
		/* Make sure every buffer contains something. */
		if (strlen(TitleSIBuff)<1 || strlen(IssueSIBuff)<1 || strlen(KeywordsSIBuff)<1) {
			SetWindowTitles(window, msg_FieldError, NULL) ;
			break ;
		}
		/* Open the file. */
		if (!(out_file = fopen(FILENAME, "a"))) {
			SetWindowTitles(window, msg_FileError, NULL) ;
			break ;
		}
		/* Write the data. */
		fprintf(out_file, "%s, %s, %s\n", TitleSIBuff, IssueSIBuff, KeywordsSIBuff) ;
		fclose(out_file) ;
		SetWindowTitles(window, msg_SavedOK, NULL) ;
		break ;
		/**/
		case ADDDATA:
		clear_gadgets() ;
		SetWindowTitles(window, msg_Header, NULL) ;
		ActivateGadget(&Title, window, NULL) ;
		break ;
		/**/
		case FINDDATA:
		clear_gadgets() ;
		SetWindowTitles(window, msg_DoSearch, NULL) ;
		ActivateGadget(&Find, window, NULL) ;
		break ;
		/**/
		default:
		break ;
	}
	return(TRUE) ;
}
int handle_gadget(struct Gadget *gadget)
{
	switch(gadget->GadgetID) {
		/**/
		case TITLEGADGET:
		ActivateGadget(&Issue, window, NULL) ;
		break ;
		/**/
		case ISSUEGADGET:
		ActivateGadget(&Keywords, window, NULL) ;
		break ;
		/**/
		case WORDSGADGET:
		break ;
		/**/
		case FINDGADGET:
		/*
		If the Find Gadget is tripped by the Enter key, we automatically
		assume a search is wanted. Double-check the buffer for data.
		*/
		if (FindSIBuff[0] == NULL) break ;
		find_match() ;
		init(&FindSIBuff[0]) ;
		/*
		Only refresh one gadget -- this one.
		*/
		RefreshGList(&Find, window, NULL, 1) ;
		break ;
		/**/
		default:
		break ;
	}
	return(TRUE) ;
}
void clear_gadgets()
{
	init(&KeywordsSIBuff[0]) ;
	init(&IssueSIBuff[0]) ;
	init(&TitleSIBuff[0]) ;
	RefreshGadgets(&Title, window, NULL) ;
}
void init(UBYTE *string)
{
	while (*string != '\0') {
		*string = '\0' ;
		*string++ ;
	}
	return ;
}
/*
	BEGINNING OF CODE TO SEARCH DATABASE  */
/* ************************************** */
void find_match()
{
	FILE *input_file, *fopen() ;
	int success, str_position(), pause() ;
	char *p, *mycopy(), string[MAX_STRING] ;
	/*
	Find the database file.
	*/
	if ((input_file = fopen(FILENAME, "r")) == NULL)
	{
		SetWindowTitles(window, msg_IOError, NULL) ;
		return ;
	}
	/*
	Set up the window.
	*/
	clear_gadgets() ;
	SetWindowTitles(window, msg_Searching, NULL) ;
	/*
	Get a line from the database.
	*/
	while (fgets(string, MAX_STRING, input_file))
	{
		/* copy the title */
		p = mycopy(TitleSIBuff, string) ;
		/* copy the issue */
		p = mycopy(IssueSIBuff, p) ;
		/* until the EOL is reached, copy each item into the buffer */
		do {
			p = mycopy(KeywordsSIBuff, p) ;
			/*
			Success will equal -1 if the keyword is not found.
			*/
			if ((success = str_position(KeywordsSIBuff, FindSIBuff)) != -1) {
				RefreshGadgets(&Title, window, NULL) ;
				SetWindowTitles(window, msg_Prompt, NULL) ;
				success = pause() ;
				switch (success) {
					/*
					Close window gadget clicked.
					*/
					case -2:
					fclose(input_file) ;
					close_all(0) ;
					/*
					Either ESC was pressed or file exhausted.
					*/
					case -1:
					fclose(input_file) ;
					SetWindowTitles(window, msg_EndSearch, NULL) ;
					return ;
					/*
					Continue searching.
					*/
					default:
					init(&KeywordsSIBuff[0]) ;
					break ;
				}
				SetWindowTitles(window, msg_Searching, NULL) ;
			}
		} while (p != NULL) ;
		clear_gadgets() ;
	}
	fclose(input_file) ;
	SetWindowTitles(window, msg_EndSearch, NULL) ;
	return ;
}
/*
	mycopy(to, from) - copy src string to dest.
	Return the address of source.
*/
char *mycopy(dest, src)
 register char *dest, *src ;
{
	/*
	Skip the white space and trap for missing data.
	*/
	while ((*src == SPACE) && (*src != EOL)) *src++ ;
	/*
	Copy to the comma or end of line.
	*/
	while ((*src != COMMA) && (*src != EOL)) *dest++ = *src++ ;
	/*
	Tack on NULL for good measure.
	*/
	*dest = '\0' ;
	/*
	If the source string is at a comma, move past it,
	and return the up-dated string position.
	*/
	if (*src == COMMA) {
		*src++ ;
		return (src) ;
	}
	/*
	Otherwise we're at the end of the line.
	*/
	return (NULL) ;
}
/*
	str_position (s1, s2) - case insensitive search of s1 for s2.
	Return an integer indicating the number of characters preceeding
	the string found, or -1 if the string is not found.
*/
int str_position(friskme, findme)
 char friskme[] ;	/* string to search */
 char findme[] ;	/* string to search for */
{
	int pos, match ;
	
	for (pos=0, match=0; pos<=strlen(friskme)-strlen(findme); pos++)
	{
		match = strnicmp(&friskme[pos], &findme[0], strlen(findme)) ;
		if (match == 0) return(pos) ;
	}
	return(-1) ;
}
/*
	pause() - wait for VANILLAKEY press.
	Return 0 if aok, continue search.
	Return -1 if abort search.
	Return -2 to close window.
*/
int pause()
{
	ULONG class ;
	USHORT code ;
	
	Wait(1 << window->UserPort->mp_SigBit) ;
	while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) != NULL) {
		class = message->Class ;
		code = message->Code ;
		ReplyMsg((struct Message *)message) ;
	}
	switch (class) {
		case CLOSEWINDOW:
		return(-2) ;
		/**/
		case VANILLAKEY:
		if (code == ESCKEY) return(-1) ;
		return(0) ;
		/**/
		default:
		return(-1) ;
	}
}
/*
	END OF CODE FOR SEARCHING DATABASE	*/
/* ************************************ */
void open_all()
{
	if (!(IntuitionBase = OpenLibrary("intuition.library",0))) {
		close_all(10) ;
	}
	if (!(GfxBase = OpenLibrary("graphics.library", 0))) {
		close_all(20) ;
	}
	if (!(window = OpenWindow(&NewWindowStructure1))) {
		close_all(30) ;
	}
	SetMenuStrip(window, &Menu1) ;
	SetWindowTitles(window, msg_Intro, NULL) ;
	return ;
}
void close_all(int status)
{
	if (window) ClearMenuStrip(window) ;
	if (window) CloseWindow(window) ;
	if (GfxBase) CloseLibrary(GfxBase) ;
	if (IntuitionBase) CloseLibrary(IntuitionBase) ;
	_exit(status) ;
}
