/* Filer.c */
/* Manx 5.0d source */
/* cc -mc -md -sa -sb -sn -sp -sr -ss Filer.c */
/* ln Filer.o RexxInterface.o c.lib */

#include "Filer.h"

/* .... GLOBALS .... */
struct	GfxBase *GfxBase = NULL;
struct	DosLibrary *DosBase = NULL;
struct	IntuitionBase *IntuitionBase = NULL;
struct	RexxBase *RexxBase = NULL; /* rexxapp.library pointer */
struct	RxsLib *RexxSysBase = NULL; /* ARexx system library */
UBYTE	undobuf[11]; /* Undo buffer for all string gadgets */
ULONG	EventSignal; /* wake-up signal for event handler */
struct	Interrupt *myHandler;
struct	IOStdReq *myRequest;
struct	MsgPort *myPort;
struct  MsgPort *ASyncPort;
struct	Task *myTask;	/* pointer to Filer task structure */
struct  myGlobals *Global; /* a structure to hold global variables */
extern  char *RexxErrMsg; /* error message set by rexxapp.library */

/* Comments on some fileds of the myGlobals structure:
 fieldcount      -  a count of the definition structures in a record
 deflist         -  linked list of field definitions that comprise a record
 return_str      -  pointer used to pass return strings back to ARexx macros
 fileptr         -  the active file handle
 sleeping        -  These next three variables describe the current
 window_inactive -  state of the Filer Window throughout the
 all_done        -  execution of the program
 waitsigs        -  Signals that could wake-up the program
*/

struct RexxData myRD = {
			{NULL},
			(char *) "fil",
			(APTR) dispatch,
			NULL,
			(APTR) error_rtn,
			(APTR) process_rtn,
			NULL,
			NULL,
			{ { "new",	    (APTR)&rexxnewfile },
			  { "open",	    (APTR)&rexxopenfile },
			  { "save",	    (APTR)&rexxsavefile },
			  { "quit",	    (APTR)&rexxquitprog },
			  { "activate",	    (APTR)&rexxactivate },
			  { "wakeup",	    (APTR)&rexxwakeup },
			  { "closeit",	    (APTR)&rexxcloseit },
			  { "filename",	    (APTR)&rexxsupplyfilename },
			  { "recordstruct", (APTR)&rexxsupplyrecordstruct },
			  { "recordsize",   (APTR)&rexxsupplyrecordsize },
			  { "view",	    (APTR)&rexxviewrecord },
			  { "add", 	    (APTR)&rexxaddnewrecord },
			  { (char *) 0,	    (APTR) 0 } }
		};

struct TextAttr MyFont = {(STRPTR)"topaz.font",TOPAZ_EIGHTY,\
	FS_NORMAL,FPF_ROMFONT};

/* Include all the Menu, Gadget, Requester and associated definitions */
#include "Filer.pw" /* Modified PowerWindows output file */

void main (void)
{
   LONG myClass;
   WORD myCode;
   ULONG rtnsigs;
   struct RexxMsg *myRexxMsg;
   struct IntuiMessage *myMessage;
   static char PortName[8] = "AFiler"; /* my ARexx port name */

   if (!Open_Libs())
      goto main_exit; /* catastrophic failure, can't continue */

   if (!(myPort = CreatePort("Filer", NULL)))
   {  /* a message port is critical to the program's operation */
      puts("Unable to create a message port.  Exiting!");
      goto main_exit;
   }

   if (!(ASyncPort = CreatePort("ASync", (long) 0)))
   {
      puts("Unable to create an asynchronous message port.");
      goto main_exit;
   }

   if (!(Global = AllocMem((long) sizeof(struct myGlobals), MEMF_CLEAR)))
   {  /* unable to continue without memory for global variables */
      puts("Not enought memory for global variables.");
      goto main_exit;
   }
   Global->sleeping = FALSE;
   Global->window_inactive = FALSE;
   Global->all_done = FALSE; /* execution of the program */

   if (!(Global->myWindow = Open_Window((SHORT) XORIGIN, (SHORT) YORIGIN,\
	(SHORT) XLENGTH, (SHORT) YLENGTH, NULL, FWINDOWFLAGS,\
	(UBYTE *)"Filer", (struct Screen *)0)))
   {
	puts("Insufficient memory for the Filer Window.  Exiting!");
	goto main_exit;
   }

   Global->myWindow->UserPort = myPort; /* Add the message port then modify */
   ModifyIDCMP(Global->myWindow, FWINDOWIDCMP); /* the window's IDCMP flags */

   if (RexxBase) /* if RexxBase is not 0 then the library is available */
   {
	if (!(Global->RexxSignal = SetRexxPort((STRPTR)PortName, &myRD)))
	{
	   Global->error_str = "Can't open Rexx message port.";
	   inform(Global->myWindow);
	   CloseLibrary((struct Library *) RexxBase); /* Can't open a port */
	   RexxBase = (struct RexxBase *)0; /* so close the library */
	}
   }

   myTask = FindTask(0); /* The next two globals are intended solely for */
   EventSignal = (1 << AllocSignal(-1)); /* used by my event handler */
   Global->PortSignal = (1 << myPort->mp_SigBit);
   Global->ASyncSignal = (1 << ASyncPort->mp_SigBit);

   /* This assignment works correctly regardless of RexxSignal's value */
   Global->waitsigs = (Global->PortSignal|EventSignal|Global->RexxSignal|Global->ASyncSignal);

   install_input_handler(myPort); /* start my input handler */

   SetMenuStrip(Global->myWindow, MenuList);

   while (!Global->all_done)
   {
	rtnsigs = Wait(Global->waitsigs);
	if (rtnsigs & EventSignal)
	{ /* if the program is sleeping or the window is inactive and it */
	  /* receives an input event signal, then wake up or activate the */
	  /* window and process the user's request */
	   if (Global->window_inactive)
		activate();
	   if (Global->sleeping)
		wakeup();
	}
	if ((!Global->sleeping) && rtnsigs & Global->PortSignal)
	{ /* Process all IDCMP messages that arrive at my window's message */
	  /* port. */
	   while (myMessage = (struct IntuiMessage *)GetMsg(myPort))
	   {
		myClass = myMessage->Class;
		myCode = myMessage->Code;
		ReplyMsg((struct Message *)myMessage);
		switch(myClass)
		{
		   case CLOSEWINDOW:
			closeit();
			break;
		   case MENUPICK:
			Global->all_done = processmenu(myCode);
			break;
		   case ACTIVEWINDOW:
			Global->sleeping = FALSE;
			Global->window_inactive = FALSE;
			break;
		   case INACTIVEWINDOW:
			Global->sleeping = FALSE;
			Global->window_inactive = TRUE;
			break;
		   default:
			Global->error_str = "Unexpected class of Intuition Message!";
			inform(Global->myWindow);
		}
	   }
	}
	if (RexxBase && (rtnsigs & Global->RexxSignal))
	{ /* If ARexx is available and we receive a signal from our ARexx */
	  /* message port, call the rexxapp.library ReceiveRexx() function */
	  /* to process all the outstanding ARexx messages. So simple!*/
	   ReceiveRexx(&myRD);
	}
	if (rtnsigs & Global->ASyncSignal)
        {
           while (myRexxMsg = (struct RexxMsg *) GetMsg(ASyncPort))
           {
              if (myRexxMsg->rm_Args[0])
                 DeleteArgstring(myRexxMsg->rm_Args[0]);
              DeleteRexxMsg(myRexxMsg);
           }
        }
   }

   close_input_handler(); /* shut down input handler */

main_exit:
   if (Global->myWindow)
   {
      if (Global->myWindow->MenuStrip)
	 ClearMenuStrip(Global->myWindow);
	 Global->myWindow->UserPort = NULL;
	 CloseWindow(Global->myWindow);
   }

   if (ASyncPort)
   {
      while (myRexxMsg = (struct RexxMsg *) GetMsg(ASyncPort))
      {
         if (myRexxMsg->rm_Args[0])
            DeleteArgstring(myRexxMsg->rm_Args[0]);
         DeleteRexxMsg(myRexxMsg);
      }
      DeletePort(ASyncPort);
   }

   if (myPort)
   { /* reply to all outstanding messages and delete the message port */
      while (myMessage = (struct IntuiMessage *)GetMsg(myPort))
	 ReplyMsg((struct Message *)myMessage);
      DeletePort(myPort);
   }

   if (Global->fileptr)
      Close(Global->fileptr);

   if (Global->deflist)
      freedeflist();

   if (Global->RexxSignal)
      FreeRexxPort(&myRD);

   if (Global->return_str)
      FreeMem(Global->return_str, Global->return_str_length);

   Close_Libs();

   return;
}

BOOL Open_Libs(void)
{
   BOOL success;

   success = TRUE;

   /* The first three libraries are critical to the program's operation */

   if (!(GfxBase = (struct GfxBase *)\
	OpenLibrary("graphics.library", GRAPHICS_REV)))
   {
      puts("graphics.library could not be loaded.");
      success = FALSE;
   }

   if (!(DosBase = (struct DosLibrary *)OpenLibrary("dos.library", DOS_REV)))
   {
      puts("dos.library could not be loaded.");
      success = FALSE;
   }

   if (!(IntuitionBase = (struct IntuitionBase *)\
	OpenLibrary("intuition.library", INTUITION_REV)))
   {
      puts("intuition.library could not be loaded.");
      success = FALSE;
   }

   /* If either of the next two libraries don't open correctly, the Open */
   /* Library function is still successfull. We just won't have an ARexx */
   /* interface to work with. */

   if (!(RexxBase = (struct RexxBase *) OpenLibrary(REXXAPPNAME, REXXAPP_REV)))
	puts("rexxapp.library not found, no ARexx port available.");

   if (!(RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME, 0L)))
	puts("rexxsyslib.library not found, no ARexx port available.");


   return(success);
}

void Close_Libs(void)
{
   if (GfxBase)
	CloseLibrary((struct Library *) GfxBase);

   if (DosBase)
	CloseLibrary((struct Library *) DosBase);

   if (IntuitionBase)
	CloseLibrary((struct Library *) IntuitionBase);

   if (RexxBase)
	CloseLibrary((struct Library *) RexxBase);

   if (RexxSysBase)
	CloseLibrary((struct Library *) RexxSysBase);
}

struct Window *Open_Window(SHORT leftedge, SHORT topedge, SHORT width,\
	   SHORT height, ULONG idcmp, ULONG flags, UBYTE *title,\
	   struct Screen *scrn)
{
	struct NewWindow NewWindow;

	NewWindow.LeftEdge = leftedge;
	NewWindow.TopEdge = topedge;
	NewWindow.Width = width;
	NewWindow.Height = height;
	NewWindow.DetailPen = (UBYTE) BCOL;
	NewWindow.BlockPen = (UBYTE) FCOL;
	NewWindow.IDCMPFlags = idcmp;
	NewWindow.Flags = flags;
	NewWindow.FirstGadget = (struct Gadget *) 0;
	NewWindow.CheckMark = (struct Image *) 0;
	NewWindow.Title = title;
	NewWindow.Screen = (struct Screen *) 0;
	NewWindow.BitMap = (struct BitMap *) 0;
	NewWindow.MinWidth = width;
	NewWindow.MinHeight = height;
	NewWindow.MaxWidth = (USHORT) 0;
	NewWindow.MaxHeight = (USHORT) 0;
	NewWindow.Type = WBENCHSCREEN;

	if (scrn) /* The user supplied a screen so make some changes */
	{
		NewWindow.Screen = scrn;
		NewWindow.Type = CUSTOMSCREEN;
	}

	return(OpenWindow(&NewWindow));
}

BOOL activate(void)
{
   if (Global->window_inactive)
   {
      ActivateWindow(Global->myWindow);
      WindowToFront(Global->myWindow);
      Global->window_inactive = FALSE;
   }

   return(TRUE);
}

BOOL rexxactivate(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(activate());
}

BOOL wakeup(void)
{
   if (Global->sleeping)
   {
      if (!(Global->myWindow = Open_Window((SHORT) XORIGIN, (SHORT) YORIGIN,\
         (SHORT) XLENGTH, (SHORT) YLENGTH, NULL, FWINDOWFLAGS,\
         (UBYTE *)"Filer", (struct Screen *)0)))
      {
       /* Flash the screen if there is not enough memory to complete*/
       /* the operation. Wait for the operator to free up some memory.*/
         DisplayBeep(NULL);
      }else{
         Global->sleeping = FALSE;
         Global->myWindow->UserPort = myPort;
         ModifyIDCMP(Global->myWindow, FWINDOWIDCMP);
         Global->waitsigs = (Global->PortSignal|EventSignal|Global->RexxSignal|Global->ASyncSignal);
         SetMenuStrip(Global->myWindow, MenuList);
      }
   }

   return(TRUE);
}

BOOL rexxwakeup(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(wakeup());
}

BOOL closeit(void)
{
   if (!Global->sleeping)
   {
      Global->sleeping = TRUE;
      Global->window_inactive = FALSE;
      if (Global->myWindow->MenuStrip)
         ClearMenuStrip(Global->myWindow);
      Global->myWindow->UserPort = NULL;
      CloseWindow(Global->myWindow);
      Global->waitsigs = (EventSignal|Global->RexxSignal|Global->ASyncSignal);
   }

   return(TRUE);
}

BOOL rexxcloseit(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(closeit());
}

struct IntuiText b_txt = {0, 1, JAM2, 5, 5, NULL, NULL, NULL};
struct IntuiText p_txt = {0, 1, JAM2, 5, 3, NULL, (UBYTE *)"OK", NULL};

void inform(struct Window *myWindow)
{
   b_txt.IText = (UBYTE *)Global->error_str;
   AutoRequest(myWindow,&b_txt,0L,&p_txt,0L,0L,\
      (long)(IntuiTextLength(&b_txt)+50L),50L);
}

BOOL install_input_handler(struct MsgPort *myPort)
{
   BOOL success;

   success = FALSE;

   /* allocate the memory for my input handler Interrupt structure */
   if (!(myHandler = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),\
	MEMF_PUBLIC|MEMF_CLEAR)))
   {
	Global->error_str = "Insufficient Memory - can't allocate Handler memory.";
	goto iih_exit;
   }

   /* allocate the memory for my standard IO requester structure */
   if (!(myRequest = (struct IOStdReq *)CreateExtIO(myPort,\
	sizeof(struct IOStdReq))))
   {
	Global->error_str = "Insufficient Memory - can't allocate IORequest memory.";
	goto iih_exit;
   }

   /* open the input device for my use */
   if (OpenDevice("input.device", NULL, (struct IORequest *)myRequest, NULL))
   {
	Global->error_str = "Error - Unable to open Input Device.";
	goto iih_exit;
   }

   success = TRUE;

   /* prepare the Interrupt structure to be used as an input handler */
   myHandler->is_Code = myEventhandler; /* routine entry point */
   myHandler->is_Data = NULL;		/* I don't need a data area */
   myHandler->is_Node.ln_Pri = 51;	/* put handler right before Intuition */
   myHandler->is_Node.ln_Name = "Filer_Handler"; /* my handler name */

   myRequest->io_Data = (APTR)myHandler;  /* address of Interrupt structure */
   myRequest->io_Command = IND_ADDHANDLER;

   /* send the IO request to the input device to add my input event handler */
   DoIO((struct IORequest *)myRequest);

iih_exit:

   if (!success)
   { /* if this routine failed for any reason, I need to release */
     /* the memory I allocated. */
      if (myHandler)
      {
	 FreeMem(myHandler, sizeof(struct Interrupt));
	 myHandler = 0;
      }
      if (myRequest)
      {
	 FreeMem(myRequest, sizeof(struct IOStdReq));
	 myRequest = 0;
      }
   }

   return(success);
}

void close_input_handler(void)
{
   /* prepare to remove my input event handler */
   myRequest->io_Data = (APTR)myHandler; /* address of my Interrupt struct */
   myRequest->io_Command = IND_REMHANDLER;

   /* send the IO request to actually remove my input event handler */
   DoIO((struct IORequest *)myRequest);

   /* close the input device as far as my program is concerned */
   CloseDevice((struct IORequest *)myRequest);

   /* free my standard IO request structure */
   DeleteExtIO((struct IORequest *)myRequest);

   /* release my Interrupt structure memory */
   FreeMem(myHandler, sizeof(struct Interrupt));

   return;
}

BOOL dispatch(struct RexxMsg *myRexxMsg, BOOL (*fpt)(struct RexxMsg *,\
     char *, struct RexxData *), char *argstr, struct RexxData *myRexxData)
{
   BOOL success;

   Global->success_str = NULL; /* initialize success string to NULL */

   /* Call the handler designated by the rexxapp.library */
   success = (*fpt)(myRexxMsg, argstr, myRexxData);

   /* Setup the Result returns depending on error and success returns */
   if (success)
   {
	/* Success, both result values = 0. Return success_str */
	SetupResults( 0L, 0L, (STRPTR)Global->success_str, myRexxMsg,\
                     myRexxData);
   }else{
	/* Error, Return error level 10 and a NULL result string */
	SetupResults(10L, 10L, NULL, myRexxMsg, myRexxData);
   }

   return(success);
}

void error_rtn(ULONG Code, char *Str, struct RexxMsg *rm, struct RexxData *rd)
{
   /* Very simple error processing routine. Simply display the error */
   /* string in the form of an auto requester to inform the user that an */
   /* error has occured */

   Global->error_str = Str;
   inform(Global->myWindow);

   return;
}

void process_rtn(ULONG Code, char *Str, struct RexxMsg *rm,\
	struct RexxData *rd)
{
   /* This is just a dummy routine as required by the rexxapp.library */
   /* I don't care for any return strings so just return immediatly when */
   /* called and let rexxapp.library take care of the message deletion. */

   return;
}

BOOL processmenu(USHORT selection)
{
   BOOL alldone;
   ULONG Menunum, Itemnum, Subnum;

   alldone = FALSE;

   while (selection != MENUNULL)
   {
      Menunum = MENUNUM(selection);
      Itemnum = ITEMNUM(selection);
      Subnum = SUBNUM(selection);
      Global->error_str = NULL;

      switch (Menunum)
      {
         case PROJECTMENU:
            switch (Itemnum)
            {
               case NEWFILE:
                  if (!newfile())
                     if (Global->error_str)
                        inform(Global->myWindow);
                  break;
               case OPENFILE:
                  if (!openfile())
                     if (Global->error_str)
                        inform(Global->myWindow);
                  break;
               case SAVEFILE:
                  if (!savefile())
                     if (Global->error_str)
                        inform(Global->myWindow);
                  break;
               case QUIT:
                  alldone = TRUE;
                  break;
               default:
                  Global->error_str = "Unexpected Item selection from Project menu.";
                  inform(Global->myWindow);
            } /* Itumnum switch */
            break;
         case EDITMENU:
            switch (Itemnum)
            {
               case ADDNEWRECORD:
                  if (!addnewrecord())
                     if (Global->error_str)
                        inform(Global->myWindow);
                  break;
               case VIEWRECORD:
                  if (!viewrecord())
                     if (Global->error_str)
                        inform(Global->myWindow);
                  break;
               default:
                  Global->error_str = "Unexpected Item selection from Edit menu.";
                  inform(Global->myWindow);
            } /* Itumnum switch */
            break;
         default:
            Global->error_str = "Unexpected Menu selection.";
            inform(Global->myWindow);
            break;
         } /* Menunum switch */

      selection = ((struct MenuItem *)ItemAddress(MenuList,\
                   (LONG)selection))->NextSelect;
   } /* while */

   return(alldone);
}

BOOL newfile(void)
{
   BOOL success, definitioncomplete;
   UBYTE definitionstring[21];
   SHORT YPsn;
   const SHORT XPsn = 18;
   struct Window *NFwindow;
   struct Gadget *Selectedgadget;
   struct IntuiMessage *NFintuimsg;
   struct IntuiText Definitiontext;
   struct {
	unsigned int typeselected : 1;  /* bit-fields to tell me when all */
	unsigned int sizeselected : 1;  /* three items have been selected */
	unsigned int nameselected : 1;
	unsigned int is_raw       : 1;  /* is it a raw or key type? */
   } flags;

   success = definitioncomplete = FALSE;
   flags.typeselected = 0;
   flags.sizeselected = 0;
   flags.nameselected = 0;
   Global->fieldcount = 0;
   YPsn = 36;
   Definitiontext.FrontPen = FCOL;
   Definitiontext.BackPen = BCOL;
   Definitiontext.DrawMode = JAM1;
   Definitiontext.LeftEdge = XPsn;
   Definitiontext.ITextFont = &MyFont;
   Definitiontext.IText = definitionstring;
   Definitiontext.NextText = NULL;

   if (Global->fileptr) /* close any open file before creating a new one */
   {
      Close(Global->fileptr);
      Global->fileptr = NULL;
   }

   if (!set_up_deflist())
      goto nf_exit;

   if (!(NFwindow = Open_Window((SHORT)191, (SHORT)17, (SHORT)275,\
	(SHORT)164, NFWINDOWIDCMP, NFWINDOWFLAGS, (UBYTE *)"New File",\
	(struct Screen *)0)))
   {
	Global->error_str = "Can't open New File window.";
	goto nf_exit;
   }

   /* Print the heading text for each gadget */
   PrintIText(NFwindow->RPort, &HeadingTextList, 0L, 0L);
   /* Add the Type, Size and Name gadgets */
   AddGList(NFwindow, &GadgetList, -1, -1, NULL);
   RefreshGList(&GadgetList, NFwindow, NULL, -1);

   while (!definitioncomplete)
   {
	WaitPort(NFwindow->UserPort);

	while (NFintuimsg = (struct IntuiMessage *)GetMsg(NFwindow->UserPort))
	{
	  switch (NFintuimsg->Class)
	  {
	     case GADGETUP:
		Selectedgadget = (struct Gadget *)NFintuimsg->IAddress;
		switch (Selectedgadget->GadgetID)
		{
		   case RAW:
		      flags.typeselected = 1;
		      flags.is_raw = 1;
		      break;
		   case KEY:
		      flags.typeselected = 1;
		      flags.is_raw = 0;
		      break;
		   case SIZE:
		      flags.sizeselected = 1;
		      break;
		   case NAME:
		      flags.nameselected = 1;
		      break;
		   default:
		      Global->error_str = "Unexpected gadget type!";
		      inform(NFwindow);
		} /* gadget ID switch */
		if ((Global->fieldcount < MAXFIELDS) && flags.typeselected &&\
			flags.sizeselected && flags.nameselected)
		{
		   if (!addlistitem((char *)NamegadgetSIBuff, flags.is_raw,\
			(UBYTE) atoi((char *)SizegadgetSIBuff)))
		   { /* failure indicates insufficient memory */
		      success = FALSE;
		      definitioncomplete = TRUE;
		   }else{
		      Global->fieldcount++;
		      if (flags.is_raw)
		      {
			strcpy((char *)definitionstring, "RAW ");
		      }else{
			strcpy((char *)definitionstring, "KEY ");
		      }
		      strcat((char *)definitionstring, (char *)SizegadgetSIBuff);
		      strcat((char *)definitionstring, " ");
		      strcat((char *)definitionstring, (char *)NamegadgetSIBuff);
		      Definitiontext.TopEdge = YPsn;
		      PrintIText(NFwindow->RPort, &Definitiontext, 0L, 0L);
		      YPsn += 10;
		      flags.typeselected = 0; /* reset selection indicators */
		      flags.sizeselected = 0;
		      flags.nameselected = 0;
		      RemoveGList(NFwindow, &GadgetList, -1);
		      SizegadgetSIBuff[0] = 0; /* clear the string gadget */
		      NamegadgetSIBuff[0] = 0; /* buffers */
		      AddGList(NFwindow, &GadgetList, -1, -1, NULL);
		      RefreshGList(&GadgetList, NFwindow, NULL, -1);
		   }
		}
		if (Global->fieldcount == MAXFIELDS)
		{
		   DisplayBeep(NULL);
		}
		break;
	     case CLOSEWINDOW:
		success = definitioncomplete = TRUE;
		break;
	     default:
		Global->error_str = "Unexpected Intuition class message.";
		inform(NFwindow);
		break;
	  } /* class switch */
	  ReplyMsg((struct Message *)NFintuimsg);
	}
   }

nf_exit:

   if (NFwindow)
	CloseWindow(NFwindow);

   return(success);
}

BOOL rexxnewfile(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(newfile());
}


BOOL set_up_deflist(void)
{
   BOOL success = FALSE;

   if (Global->deflist)
   {
	freedeflist();
   }

   if (!(Global->deflist = (struct List *)AllocMem((long)sizeof(struct List),\
      MEMF_CLEAR)))
   {
	Global->error_str = "No memory for record definition list.";
	goto sud_exit;
   }

   NewList(Global->deflist); /* Initialize list header */
   success = TRUE;

sud_exit:
   return(success);
}

BOOL addlistitem(char *name, unsigned int is_raw, UBYTE size)
{
   struct definition *newdefinition;
   BOOL success;

   success = FALSE;

   if (!(newdefinition = (struct definition *)AllocMem((long)\
	sizeof(struct definition), MEMF_CLEAR)))
   {
	Global->error_str = "No memory for additional fields.";
	goto ai_exit;
   }

   strcpy(newdefinition->Name, name);
   newdefinition->defNode.ln_Name = newdefinition->Name;
   newdefinition->type = is_raw;
   newdefinition->size = size;

   AddTail(Global->deflist, (struct Node *)newdefinition);

   success = TRUE;

ai_exit:

   return(success);
}

void freedeflist(void)
{
   struct definition *currentnode, *nextnode;

    /* initialize first entry ; look ahead to next entry ; change entries */
   for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
	 nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
	 currentnode = nextnode )
   {
      FreeMem(currentnode, sizeof(struct definition)); /* free each entry */
   }

   FreeMem(Global->deflist, sizeof(struct List));
   Global->deflist = NULL;
   Global->fieldcount = 0;

   return;
}

BOOL openfile(void)
{
   int loopcount;
   BOOL success = FALSE;
   struct definition tempdef;

   if (!getafilename((UBYTE *)"Load File"))
      goto of_exit;

   if (!set_up_deflist())
      goto of_exit;

   if (Global->fileptr)
   {
      Close(Global->fileptr);
      Global->fileptr = NULL;
   }

   if (!(Global->fileptr = Open(Global->FileName, MODE_OLDFILE)))
   {
      Global->error_str = "Unable to open requested file.";
      goto of_exit;
   }

   /* read in the number of definitions in record make-up */
   if (!Read(Global->fileptr, (char *)&Global->fieldcount, sizeof(int)))
   {
      Global->error_str = "Trouble reading in field count from file.";
      goto of_exit;  /* problems with reading from file */
   }
 
   /* read in all the definitions */
   for (loopcount = 0; loopcount < Global->fieldcount; loopcount++)
   {
      if ((!Read(Global->fileptr, (char *)&tempdef, sizeof(struct definition))) ||
	 (!addlistitem(tempdef.Name, (unsigned int)tempdef.type, tempdef.size)))
      {
	 Global->error_str = "Trouble establishing field definitions.";
	 Close(Global->fileptr);
	 Global->fileptr = NULL;
	 freedeflist();
	 goto of_exit;
      }
   }

   success = TRUE;

of_exit:

   return(success);
}

BOOL rexxopenfile(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(openfile());
}

BOOL savefile(void)
{
   BOOL success = FALSE;
   struct definition *currentnode, *nextnode;

   if (!getafilename((UBYTE *)"Save File"))
      goto sf_exit;

   if (Global->FileName[0])
   {
      if (Global->fileptr)
      {
	 Close(Global->fileptr); /* close any open file first */
	 Global->fileptr = NULL;
      }

      if (!(Global->fileptr = Open(Global->FileName, MODE_NEWFILE)))
      {
	 Global->error_str = "Unable to create a new file.";
	 goto sf_exit;
      }

      /* write the number of definitions in record make-up */
      if (!Write(Global->fileptr, (char *)&Global->fieldcount, sizeof(int)))
      {
	 Global->error_str = "Unable to write field count to file.";
	 goto sf_exit; /* problems with writiing to the file */
      }
 
      /* write field definitions */
      /* initialize first entry ; look ahead to next entry ; change entries */
      for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
	    nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
	    currentnode = nextnode )
      {
	 if (!Write(Global->fileptr, (char *)currentnode, sizeof(struct definition)))
	 {
	    Global->error_str = "Unable to write all field definitions to file.";
	    goto sf_exit;
	 }
      }

      success = TRUE;
   }

sf_exit:
   return(success);
}

BOOL rexxsavefile(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(savefile());
}

BOOL quitprog(void)
{
   /* This is all that is needed to indicate that the program is done */
   Global->all_done = TRUE;
   return(TRUE);
}

BOOL rexxquitprog(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(quitprog());
}

BOOL getafilename(UBYTE *RequesterText)
{
   BOOL success = FALSE;
   USHORT activeflag;

   Global->FileName[0] = (char) 0; /* clear any previous file name */
   FNGSIBuff[0] = (UBYTE) 0; /* clear the requester string buffer */
   myReq.ReqText->IText = RequesterText;

   SizeWindow(Global->myWindow, (long) XDELTA, (long) YDELTA);

   Delay(50); /* allow window to resize befor continuing */

   if (!Request(&myReq, Global->myWindow))
   {
      SizeWindow(Global->myWindow, (long) -XDELTA, (long) -YDELTA);
      Global->error_str = "Unable to open a filename requester.";
      goto gfn_exit;
   }

   /* pause while requester is active */
   /* initialize test variable ; test value ; update test variable */
   for ( activeflag = myReq.Flags & REQACTIVE ;\
	 activeflag ;\
	 activeflag = myReq.Flags & REQACTIVE )
   { /* slow down the loop, don't hog all the microprocessor time */
      Delay(50);
   }

   SizeWindow(Global->myWindow, (long) -XDELTA, (long) -YDELTA);

   if (FNGSIBuff[0] != 0) /* non zero indicates user made a file selection */
   {
      strcpy(Global->FileName, (char *)FNGSIBuff); /* copy the name into our buffer */
      success = TRUE;
   }
   
gfn_exit:
   return(success);
}

BOOL rexxsupplyfilename(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   Global->success_str = &Global->FileName[0];

   return(TRUE);
}

BOOL rexxsupplyrecordstruct(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   int     length;
   char    tempbuffer[6];
   struct  definition *currentnode, *nextnode;
   BOOL    success = FALSE;

   if (Global->return_str)
   {
      FreeMem(Global->return_str, Global->return_str_length);
      Global->return_str = (char *) 0;
      Global->return_str_length = (long) 0;
   }

      /* calculate length of the string to send */
      /* initialize first entry ; look ahead to next entry ; change entries */
   for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
         nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
         currentnode = nextnode )
   {
      Global->return_str_length += strlen(currentnode->Name);
      length = sprintf((char *)&tempbuffer[0], "%d", currentnode->type);
      Global->return_str_length += length;
      length = sprintf((char *)&tempbuffer[0], "%d", currentnode->size);
      Global->return_str_length += length;
      Global->return_str_length += 3; /* 3 spaces to go between strings */
   }
 
   Global->return_str_length++; /* add one more for the null character */

   if (!(Global->return_str = AllocMem(Global->return_str_length,\
         MEMF_CLEAR)))
      goto srs_exit;

      /* concatenate string to send */
      /* initialize first entry ; look ahead to next entry ; change entries */
   for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
         nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
         currentnode = nextnode )
   {
      strcat(Global->return_str, (char *)currentnode->Name);
      strcat(Global->return_str, (char *)" ");
      sprintf((char *)&tempbuffer[0], "%d", currentnode->type);
      strcat(Global->return_str, (char *)&tempbuffer[0]);
      strcat(Global->return_str, (char *)" ");
      sprintf((char *)&tempbuffer[0], "%d", currentnode->size);
      strcat(Global->return_str, (char *)&tempbuffer[0]);
      strcat(Global->return_str, (char *)" ");
   }

   /* make the last space become a null termination character */
   *(Global->return_str + (Global->return_str_length - 1)) = (char)0;

   Global->success_str = Global->return_str;
   success = TRUE;

srs_exit:
   return(success);
}

BOOL rexxsupplyrecordsize(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   long recordsize;
   BOOL success;
   struct  definition *currentnode, *nextnode;

   recordsize = 0;
   success = FALSE;

      /* determine the record size */
      /* initialize first entry ; look ahead to next entry ; change entries */
   for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
         nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
         currentnode = nextnode )
   { /* + 1 for null termination character */
      recordsize += currentnode->size + 1;
   }

   if (Global->return_str)
   {
      FreeMem(Global->return_str, Global->return_str_length);
      Global->return_str = (char *) 0;
      Global->return_str_length = (long) 0;
   }

   /* Get enough memory to hold the ASCII string representing the */
   /* largest value that a long can hold. */
   if (!(Global->return_str = AllocMem((long) 16, MEMF_CLEAR)))
      goto srz_exit;

   sprintf(Global->return_str, "%ld", recordsize);
   Global->return_str_length = 16;

   /* make the last space become a null termination character*/
   *(Global->return_str + (Global->return_str_length - 1)) = (char)0;

   Global->success_str = Global->return_str;
   success = TRUE;

srz_exit:
   return(success);
}

/* What a roundabout way to get something done through a function */
/* contained in this program (rexxviewrecord). */
BOOL viewrecord(void)
{
   BOOL   success;
   struct RexxMsg *ASyncMsg;

   success = FALSE;

   if (!(ASyncMsg = CreateRexxMsg((struct RexxMsgPort *)ASyncPort, NULL, (STRPTR) "ASync")))
   {
      Global->error_str = "Insufficient memory to view record.";
      goto vr_exit;
   }

   if (!(ASyncMsg->rm_Args[0] = CreateArgstring((STRPTR) "view", 4)))
   {
      Global->error_str = "Insufficient memory to view record.";
      goto vr_exit;
   }

   PutMsg(FindPort("AFiler"), (struct Message *) ASyncMsg);

   success = TRUE;

vr_exit:
   return(success);
}

BOOL rexxviewrecord(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   BOOL success = TRUE;

   if (!(ASyncRexxCmd(msg->rm_Args[0], data)))
   {
      Global->error_str = RexxErrMsg;
      inform(Global->myWindow);
      success = FALSE;
   }

   return(success);
}

BOOL addnewrecord(void)
{
   BOOL   success, recordcomplete;
   long   longest, width, height;
   SHORT  YOrigin, XOrigin, YPsn, XPsn, *tempXY;
   struct Window *NRwindow;
   struct Border *tempborder;
   struct StringInfo *tempstringinfo;
   struct Gadget AnotherGadget;
   struct Gadget *Selectedgadget, *FieldStringGadget, *LastStringGadget;
   struct IntuiMessage *NRintuimsg;
   struct definition *currentnode, *nextnode;
   struct IntuiText FieldText, AnotherText;

   success = recordcomplete = FALSE;
   longest = 0;
   XPsn = 8;
   YPsn = 12;
   FieldText.FrontPen = AnotherText.FrontPen = FCOL;
   FieldText.BackPen = AnotherText.BackPen = BCOL;
   FieldText.LeftEdge = XPsn;
   FieldText.ITextFont = AnotherText.ITextFont = &MyFont;
   FieldText.NextText = AnotherText.NextText = NULL;
   LastStringGadget = FieldStringGadget = NULL;

      /* determine the longest row */
      /* initialize first entry ; look ahead to next entry ; change entries */
   for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
         nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
         currentnode = nextnode )
   {  /* The 14 added indicates the max length of the field name. */
      if ( (currentnode->size + 14) > longest)
         longest = currentnode->size + 14;
   }

   /* Approximate the size of my Add Record window. + 3 = leading space, */
   /* trailing space, and a space between name field and string gadget */
   /* + 3 to height for window title bar and "add another" gadget */
   width = (longest + 3) * 8;
   height = (Global->fieldcount + 3) * 10;

   /* Center the window on the screen. */
   YOrigin = (200 - height)/2;
   XOrigin = (640 - width)/2;

   if (!(NRwindow = Open_Window((SHORT)XOrigin, (SHORT)YOrigin, (SHORT)width,\
	(SHORT)height, NRWINDOWIDCMP, NRWINDOWFLAGS, (UBYTE *)"Add Record",\
	(struct Screen *)0)))
   {
	Global->error_str = "Can't open Add Record window.";
	goto nr_exit;
   }

      /* display the field names and initialize string gadget structures */
      /* initialize first entry ; look ahead to next entry ; change entries */
   for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
         nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
         currentnode = nextnode )
   {
      if (!(FieldStringGadget = allocategadget(currentnode->size)))
      {
         FieldStringGadget = LastStringGadget;
         Global->error_str = "Not enough memory for gadgets.";
         goto nr_exit;
      }
      FieldText.IText = (UBYTE *) currentnode->Name;
      if (currentnode->type)
      {  /* JAM1 drawing mode for RAW data fields */
         FieldText.DrawMode = JAM1;
      }else{
         /* Inverse video for KEY data fields */
         FieldText.DrawMode = INVERSVID;
      }
      FieldText.TopEdge = YPsn;
      PrintIText(NRwindow->RPort, &FieldText, 0L, 0L);
      /* set up the border vectors */
      tempborder = (struct Border *)FieldStringGadget->GadgetRender;
      tempXY = tempborder->XY;
      tempXY += 2;
      *tempXY = (currentnode->size * 8) + 1;
      tempXY += 2;
      *tempXY = (currentnode->size * 8) + 1;
      tempXY++;
      *tempXY = 9;
      tempXY += 2;
      *tempXY = 9;
      /* set up the border structure */
      tempborder->LeftEdge = -1;
      tempborder->TopEdge = -1;
      tempborder->FrontPen = 3;
      tempborder->DrawMode = JAM1;
      tempborder->Count = 5;
      /* set up the gadget structure */
      FieldStringGadget->NextGadget = LastStringGadget;
      FieldStringGadget->LeftEdge = (14 + 2) * 8;
      FieldStringGadget->TopEdge = YPsn;
      FieldStringGadget->Width = (currentnode->size + 1) * 8;
      FieldStringGadget->Height = 8;
      FieldStringGadget->Activation = RELVERIFY;
      FieldStringGadget->GadgetType = STRGADGET;
      /* set up string info structure */
      tempstringinfo = (struct StringInfo *) FieldStringGadget->SpecialInfo;
      tempstringinfo->MaxChars = currentnode->size + 1;
      YPsn += 10;
      LastStringGadget = FieldStringGadget;
   }

   /* Add a gadget to request if they want to add another record. */
   AnotherText.LeftEdge = 0;
   AnotherText.TopEdge = 0;
   AnotherText.DrawMode = INVERSVID;
   AnotherText.IText = (UBYTE *)"Add Another";
   AnotherGadget.NextGadget = FieldStringGadget;
   AnotherGadget.LeftEdge = width - 100;
   AnotherGadget.TopEdge = YPsn + 4;
   AnotherGadget.Width = 88;
   AnotherGadget.Height = 8;
   AnotherGadget.Flags = GADGHCOMP;
   AnotherGadget.Activation = RELVERIFY|TOPBORDER;
   AnotherGadget.GadgetType = BOOLGADGET;
   AnotherGadget.GadgetText = &AnotherText;
   AnotherGadget.GadgetID = ANOTHER;

   AddGList(NRwindow, &AnotherGadget, -1, -1, NULL);
   RefreshGList(&AnotherGadget, NRwindow, NULL, -1);

   while (!recordcomplete)
   {
      WaitPort(NRwindow->UserPort);

      while (NRintuimsg = (struct IntuiMessage *)GetMsg(NRwindow->UserPort))
      {
         switch (NRintuimsg->Class)
         {
            case GADGETUP:
               Selectedgadget = (struct Gadget *)NRintuimsg->IAddress;
               switch (Selectedgadget->GadgetID)
               {
                  case ANOTHER:
                     if (!writerecord(&AnotherGadget, NRwindow))
                        goto nr_exit;
                     break;
                  default:
                     break; /* ignore all others */
               }
               break;
            case CLOSEWINDOW:
               success = recordcomplete = TRUE;
               if (!writerecord(&AnotherGadget, NRwindow))
                  goto nr_exit;
               break;
            default:
               Global->error_str = "Unexpected Intuition class message.";
               inform(NRwindow);
               break;
         } /* class switch */
         ReplyMsg((struct Message *)NRintuimsg);
      }
   }

nr_exit:
   RemoveGList(NRwindow, &AnotherGadget, -1);
   CloseWindow(NRwindow);

   if (FieldStringGadget)
   {
      for (LastStringGadget = FieldStringGadget->NextGadget ; \
           FieldStringGadget ; \
           FieldStringGadget = LastStringGadget, \
           LastStringGadget = FieldStringGadget->NextGadget)
      {
         releasegadget(FieldStringGadget, (FieldStringGadget->Width/8) - 1);
      }
   }

   return(success);
}

struct Gadget *allocategadget(UBYTE size)
{
   struct Gadget *tempgadget;
   struct Border *tempborder;
   struct StringInfo *tempstringinfo;

   /* allocate Gadget, Border, vectors, SpecialInfo and buffer memory */
      /* gadget structure memory */
   if (!(tempgadget = AllocMem(sizeof(struct Gadget), MEMF_CLEAR)))
      goto ag_exit;
      /* border structure memory */
   if (!(tempgadget->GadgetRender = (APTR) AllocMem(sizeof(struct Border),\
         MEMF_CLEAR)))
   {
      releasegadget(tempgadget, size);
      tempgadget = NULL;
      goto ag_exit;
   }
      /* memory for border vectors */
   tempborder = (struct Border *)tempgadget->GadgetRender;
   if (!(tempborder->XY = (SHORT *) AllocMem(sizeof(SHORT) * 10,\
          MEMF_CLEAR)))
   {
      releasegadget(tempgadget, size);
      tempgadget = NULL;
      goto ag_exit;
   }
      /* SpecialInfo memory (StringInfo structure) */
   if (!(tempgadget->SpecialInfo = (APTR) AllocMem(sizeof(struct StringInfo),\
         MEMF_CLEAR)))
   {
      releasegadget(tempgadget, size);
      tempgadget = NULL;
      goto ag_exit;
   }
      /* Buffer memory ( + 1 is for the null character) */
   tempstringinfo = (struct StringInfo *)tempgadget->SpecialInfo;
   if (!(tempstringinfo->Buffer = (UBYTE *) AllocMem((long)size + 1,\
         MEMF_CLEAR)))
   {
      releasegadget(tempgadget, size);
      tempgadget = NULL;
   }

ag_exit:
   return(tempgadget);
}

void releasegadget(struct Gadget *tempgadget, UBYTE size)
{
   struct Border *tempborder;
   struct StringInfo *tempstringinfo;

   tempborder = (struct Border *) tempgadget->GadgetRender;
   tempstringinfo = (struct StringInfo *)tempgadget->SpecialInfo;

   if (tempstringinfo->Buffer)
      FreeMem(tempstringinfo->Buffer, size + 1);
   if (tempgadget->SpecialInfo)
      FreeMem(tempgadget->SpecialInfo, sizeof(struct StringInfo));
   if (tempborder->XY)
      FreeMem(tempborder->XY, sizeof(SHORT) * 10);
   if (tempgadget->GadgetRender)
      FreeMem(tempgadget->GadgetRender, sizeof(struct Border));
   if (tempgadget)
      FreeMem(tempgadget, sizeof(struct Gadget));
}

BOOL writerecord(struct Gadget *FirstGadget, struct Window *NRwindow)
{
   BOOL   success;
   char  *fieldstring;
   long   longest, index, length;
   struct Gadget *FieldGadget;
   struct Border *tempborder;
   struct StringInfo *tempstringinfo;
   struct definition *currentnode, *nextnode;

   success = FALSE;
   longest = 0;
   FieldGadget = FirstGadget->NextGadget;

      /* determine the longest field */
      /* initialize first entry ; look ahead to next entry ; change entries */
   for ( currentnode = (struct definition *)(Global->deflist->lh_Head) ;\
         nextnode = (struct definition *)(currentnode->defNode.ln_Succ) ;\
         currentnode = nextnode )
   {
      if ( currentnode->size > longest)
         longest = currentnode->size;
   }

   /* + 1 for the terminating null character */
   if (!(fieldstring = AllocMem(longest + 1, MEMF_CLEAR)))
   {
      Global->error_str = "Can't write record.";
      goto wr_exit;
   }

   RemoveGList(NRwindow, FirstGadget, -1);

   Seek(Global->fileptr, 0, 1); /* go to end of file */

   /* write fields one at a time */
   /* no initialization; test if there is a gadget; next gadget */
   for ( ; FieldGadget ;
           FieldGadget = FieldGadget->NextGadget )
   {
      tempstringinfo = (struct StringInfo *) FieldGadget->SpecialInfo;
      strcpy(fieldstring, (char *) tempstringinfo->Buffer);
      length = tempstringinfo->MaxChars;
      if (!Write(Global->fileptr, fieldstring, length))
      {
         Global->error_str = "Error writing record to file.";
         goto wr_exit;
      }
      *tempstringinfo->Buffer = NULL;
      for ( index = 0 ; index < longest ; index++ )
         *(fieldstring + index) = NULL;
   }

   success = TRUE;

wr_exit:
   AddGList(NRwindow, FirstGadget, -1, -1, NULL);
   RefreshGList(FirstGadget, NRwindow, NULL, -1);

   if (fieldstring)
      FreeMem(fieldstring, longest + 1);

   return(success);
}

BOOL rexxaddnewrecord(struct RexxMsg *msg, char *str, struct RexxData *data)
{
   return(addnewrecord());
}

#asm
	; .... ENTRY VALUES ....
	; a0 = pointer to the event linked list
	; a1 = pointer to my data segment (NULL)
	; .... RETURNS ....
	; d0 = return the new event linked list here

	xdef _myEventhandler	;make entry point available to C code
	xref _myTask		;pointer to Filer Task structure
	xref _EventSignal	;pointer to Filer Wake-up event signal

	; some equates that may event handler needs
ie_NextEvent	equ 0
ie_Class	equ 4
ie_Code		equ 6
ie_Qualifier	equ 8
IECLASS_NULL	equ $00
IECLASS_RAWKEY	equ $01
IEQUALIFIERB_CONTROL	equ $03
IEQUALIFIERB_LALT	equ $04
IEQUALIFIERB_RALT	equ $05

_myEventhandler:
	move.l a0,-(sp) ;save the pointer to the first event structure
check:	cmp.b  #IECLASS_RAWKEY,ie_Class(a0) ;rawkey event?
	bne.s  next	;no, so go to the next event
	cmp.w  #$23,ie_Code(a0)	;'f' key depressed?
	bne.s  next	;no, don't need this event
	move.w ie_Qualifier(a0),d0 ;move qualifier into d0
	btst   #IEQUALIFIERB_CONTROL,d0 ;is CONTROL down?
	beq.s  next	;bit not set, don't need this event
	btst   #IEQUALIFIERB_LALT,d0 ;is left ALT down?
	bne.s  ok	;bit set, I want this event
	btst   #IEQUALIFIERB_RALT,d0 ;is right ALT down?
	beq.s  next	;bit not set, don't need this event
ok:	move.l _SysBase#,a6
	move.l a0,-(sp)		;save event list pointer
	move.l _myTask,a1	;put Task structure pointer in a1
	move.l _EventSignal,d0	;put wake-up signal in d0
	jsr    -324(a6)	;Signal(myTask, EventSignal)
	move.l (sp)+,a0		;restore event list pointer
	move.b #IECLASS_NULL,ie_Class(a0)	;remove event
next:	move.l ie_NextEvent(a0),d0	;get next event
	move.l d0,a0	;put event structure address in a0
	bne.s  check	;go back if there is another event
	move.l (sp)+,d0 ;restore the event list
	rts		;return from handler

#endasm
