typedef char CHAR;
#define LOCAL static
#define AND &&
#define OR ||

#define TEXTLENGTH 50
#define KICK204    37
#define ESC_KEY    27

/**************************************************************************/
/*  LibHeader								  */
/*     NextLib:  Next library in a linked list. 			  */
/*     LibBase:  Pointer to the libraries base (e.g. intuitionbase).      */
/*     Patches:  Pointer to linked list of patches made to this library.  */
/**************************************************************************/
struct LibHeader {
  struct Library    *LibBase;
  struct Patches    *Patches;
  struct LibHeader  *NextLib;
};

/************************************************************************/
/*  Patches								*/
/*     JMP	: Opcode for JMP (used to call the old function).       */
/*     OldFunc	: Address to the previus function (before SetFunction). */
/*     NewFunc	: The new function.					*/
/*     Offset	: Offset in library (Must be negativ).                  */
/*     NextPatch: Pointer to next patch in list.			*/
/************************************************************************/
struct Patches {
  USHORT	  JMP;
  LONG		  OldFunc;
  LONG		  NewFunc;
  SHORT 	  Offset;
  struct Patches *NextPatch;
};

/**************************************************************************/
/*  MyNode	- is used to hold data to show. 			  */
/*     ExecNode : Standard exec node structure				  */
/*     Text	: array to put text inside (ExecNode.ln_Name points here) */
/**************************************************************************/
struct MyNode {
  struct Node ExecNode;
  char	 Text[TEXTLENGTH];
};

/************************/
/* All local functions	*/
/************************/
LOCAL VOID OpenFD(STRPTR LibName); /* Open .fd file corresponding to Libname */
LOCAL BOOL GetLine(CHAR *Buffer);  /* Read one complete line from .fd file   */
LOCAL VOID CloseFD(VOID);          /* Close the .fd file                     */

LOCAL VOID BuildList(BOOL);        /* Build the list of all known patches    */
LOCAL VOID DestroyList(VOID);      /* Remove the list                        */
LOCAL CHAR *AddNewNode(VOID);      /* Create a new node and add it to the list */


LOCAL BOOL FindOffset(CHAR *BUFFER, SHORT OffsetToFind);  /* Finds the function name */
LOCAL VOID OutputToWindow(VOID);   /* Outputs in our own window              */
LOCAL VOID OutputToCLI(VOID);      /* Outputs to CLI (may be redirected      */

/******************************/
/* The only exported function */
/******************************/
VOID MainLoop(BOOL,BOOL);

/******************************/
/* Our assembler functions    */
/******************************/
extern struct LibHeader *FindPatches(VOID);   /* Finds the SaferPatches program */

extern struct Window *OpenWindowWithGad(VOID);          /* Open our window     */
extern VOID CloseWindowWithGad(struct Window *Window);  /* Closes our window   */

extern VOID Bin2Hex(CHAR *, ULONG, UWORD);    /* converts a long to ascii hex  */

extern VOID  Mystrncpy(CHAR *, CHAR *, UWORD);  /* a small and fast strncpy */

/************************************************/
/* I dont have the 2.04 C includes so I have to */
/* declare the new functions here		*/
/************************************************/
struct IntuiMessage *GT_GetIMsg(struct Port *);
VOID GT_ReplyIMsg(struct IntuiMessage *);

VOID GT_BeginRefresh(struct Window *);
VOID GT_EndRefresh(struct Window *, BOOL);

CHAR FGetC(BPTR);
VOID StrToLong(CHAR *, ULONG *);

VOID WriteChars(CHAR *, ULONG);
VOID PutStr(CHAR *);

/************************************/
/* Pragmas for the above functions  */
/************************************/
#pragma libcall GadToolsBase GT_GetIMsg 48 801
#pragma libcall GadToolsBase GT_ReplyIMsg 4e 901
#pragma libcall GadToolsBase GT_BeginRefresh 5a 801
#pragma libcall GadToolsBase GT_EndRefresh 60 802
#pragma libcall DOSBase FGetC 132 101
#pragma libcall DOSBase StrToLong 330 2102
#pragma libcall DOSBase WriteChars 3ae 2102
#pragma libcall DOSBase PutStr 3b4 101

#define IDCMP_CLOSEWINDOW   CLOSEWINDOW
#define IDCMP_GADGETUP	    GADGETUP
#define IDCMP_VANILLAKEY    0x200000L
#define IDCMP_REFRESHWINDOW REFRESHWINDOW

/********************/
/* Global variables */
/********************/
extern BPTR  Fh;	   /* File handle for .fd file		   */
extern LONG  FDOffset;	   /* How far we have read in the .fd file */

extern struct Library *GadToolsBase;	     /* Library base */
extern struct DOSBase *DOSBase; 	     /* Library base */
extern struct IntuitionBase *IntuitionBase;  /* Library base */

extern struct List List;	/* Exec list of ascii strings */

/****************************************************************/
/* MainProg(NoFD,UseWindow)                                     */
/*   NoFD	- TRUE if we shouldn't look for FD files.       */
/*   UseWIndow	- if TRUE open a window else output to console	*/
/****************************************************************/
VOID MainLoop(BOOL NoFD, BOOL UseWindow) {

  BuildList(NoFD);

  if (UseWindow)
    OutputToWindow();
  else
    OutputToCLI();

  DestroyList();
}

/****************************************************************/
/* OutputToWindow()  - create a window with a LISTVIEW gadget   */
/*		     - and present all patches there		*/
/****************************************************************/
LOCAL VOID OutputToWindow() {
  BOOL KeepRunning = TRUE;
  struct Window *Window;

  if (IntuitionBase = (struct IntuitionBase *)
		      OpenLibrary("intuition.library",KICK204)) {

    if (GadToolsBase  = OpenLibrary("gadtools.library",KICK204)) {

      if (Window = OpenWindowWithGad()) {
	do {
	  struct IntuiMessage *Msg;

	  WaitPort(Window->UserPort);

	  while (Msg = GT_GetIMsg(Window->UserPort)) {
	    ULONG Class = Msg->Class;

	    if (Class & (IDCMP_CLOSEWINDOW | IDCMP_GADGETUP))
	      KeepRunning = FALSE;
	    else if (Class & IDCMP_VANILLAKEY) {
	      UWORD Code = Msg->Code;

	      if (Code == ESC_KEY  OR  Code == 'X'  OR  Code == 'x')
		KeepRunning = FALSE;
	      else
		DisplayBeep(Window->WScreen);
	    }
	    else if (Class & IDCMP_REFRESHWINDOW) {
	      GT_BeginRefresh(Window);
	      GT_EndRefresh(Window,TRUE);
	    }
	    GT_ReplyIMsg(Msg);
	  }
	}
	while (KeepRunning);

	CloseWindowWithGad(Window);
      }
      CloseLibrary(GadToolsBase);
    }
    CloseLibrary((struct Library *)IntuitionBase);
  }
}

/******************************************************************/
/* OutputToCLI()   - output all data to the default output stream */
/******************************************************************/
LOCAL VOID OutputToCLI() {
  extern CHAR far LISTTEXT[];

  struct MyNode *Node;

  WriteChars(LISTTEXT,32);
  PutStr("\n");
  for (Node = (struct MyNode *)(List.lh_Head); Node->ExecNode.ln_Succ;
       Node = (struct MyNode *)(Node->ExecNode.ln_Succ)) {
    PutStr(Node->Text);
    PutStr("\n");
  }
}

/************************************************************************/
/* VOID BuildList(NoFD)     - Build up a list of nodes each containing  */
/*			      an ascii string describing one patch made */
/*    NoFD  - TRUE if we shouldn't look for .fd files                   */
/************************************************************************/
LOCAL VOID BuildList(BOOL NoFD) {
  struct LibHeader *LocHeader;

  NewList(&List);

  if ((LocHeader = FindPatches()) != NULL) {
    Forbid();

    while (LocHeader = LocHeader->NextLib) {
      struct Patches *Patch = LocHeader->Patches;
      STRPTR	      LibName = LocHeader->LibBase->lib_Node.ln_Name;
      CHAR	      FuncName[200]; /* hope this is enough */
      CHAR	     *NodeText;

      if (NodeText = AddNewNode()) {
	strcpy(NodeText, "Library: ");
	Mystrncpy(NodeText+9, LibName, TEXTLENGTH-10);

	Fh = FDOffset = 0;

	if (!NoFD)
	  OpenFD(LibName);

	while (Patch = Patch->NextPatch) {
	  if (NodeText = AddNewNode()) {
	    Bin2Hex(NodeText   , (ULONG)(-Patch->Offset), 4);
	    Bin2Hex(NodeText+ 6, Patch->NewFunc, 8);
	    Bin2Hex(NodeText+16, Patch->OldFunc, 8);
	    NodeText[4]  = NodeText[5]	= NodeText[14] = NodeText[15] =
	    NodeText[24] = NodeText[25] = ' ';

	    if (FindOffset(FuncName,-Patch->Offset))
	      Mystrncpy(NodeText+26, FuncName, TEXTLENGTH-27);
	  } /* if */
	} /* while */
	CloseFD();
      } /* if */
    } /* while */
    Permit();
  } /* if */
  else {
    CHAR *NodeText;

    if (NodeText = AddNewNode())
      strcpy(NodeText," SaferPatches not installed !");
  }
}

/**********************************************************/
/* AddNewNode()   - Create a new node and add it the list */
/*   return  - pointer to MyMode textfield		  */
/**********************************************************/
LOCAL CHAR *AddNewNode(VOID) {
  struct MyNode *Node = AllocMem(sizeof(struct MyNode),MEMF_CLEAR);

  if (Node) {
    AddTail(&List,&Node->ExecNode);
    return (Node->ExecNode.ln_Name = Node->Text)
  }
  return NULL;
}
/**********************************************************/
/* DestroyList()  - remove all nodes and free memory used */
/**********************************************************/
LOCAL VOID DestroyList() {
  struct Node *Node;
  struct Node *Temp;

  Node = List.lh_Head;

  while (Temp = Node->ln_Succ) {
    FreeMem(Node,sizeof(struct MyNode));
    Node = Temp;
  }
}

/********************************************************************************/
/* BOOL FindOffset(Buffer, OffsetToFind) - Walk through the open .fd file until */
/*				   we find the function corresponding to offset */
/*    Buffer	    - Where to store function name				*/
/*    OffSetToFind  -								*/
/*    return	    - TRUE if function found					*/
/********************************************************************************/
LOCAL BOOL FindOffset(CHAR *Buffer, SHORT OffsetToFind) {
  extern CHAR far StdFunctions[];

  if (OffsetToFind < 30) {
    strcpy(Buffer,StdFunctions+((OffsetToFind-6)<<1));
    return TRUE;
  }
  while (OffsetToFind > (SHORT)FDOffset) {
    FDOffset += 6;

    FOREVER {
      if (GetLine(Buffer))
	return FALSE;
      if (Buffer[0] == '#') { /* command */
	if (*((ULONG *)(Buffer+2)) == 0x62696173L)  /* bias */
	  StrToLong(Buffer+6,&FDOffset);
      }
      else if (Buffer[0] > ' ' AND Buffer[0] != '*')
	break;
    }
  }

  /* remove anything beyond the first '(' */
  if (OffsetToFind == (SHORT)FDOffset) {
    while (*Buffer AND *Buffer != '(')
      Buffer++;
    *Buffer = 0;
    return TRUE;
  }
  return FALSE;
}

/*********************************************************/
/* OpenFD(LibName)  - Open a .fd file                    */
/*   Libname - library (or device) to open .fd file for. */
/*********************************************************/
LOCAL VOID OpenFD(STRPTR LibName) {
  TEXT	 FDFile[TEXTLENGTH];
  CHAR	*Temp = FDFile;

  strcpy(FDFile,"FD:");
  strcpy(FDFile+3,LibName);

  while (*Temp != '.')
    Temp++;
  strcpy(Temp,"_lib.fd");

  Fh = Open(FDFile, MODE_OLDFILE);
}

/********************************************/
/* CloseFD()  - Closes the current .fd file */
/********************************************/
LOCAL VOID CloseFD() {
  if (Fh) {
    Close(Fh);
    Fh = NULL;
  }
}

/*****************************************************************/
/* BOOL GetLine(Buffer)  - Reads one line from the open .fd file */
/*   Buffer - where to store the line				 */
/*   return - TRUE if end of file (EOF)                          */
/*****************************************************************/
LOCAL BOOL GetLine(CHAR *Buffer) {

  if (Fh) {
    CHAR tkn;

    do {
      tkn = FGetC(Fh);
      if (tkn == EOF) {
	CloseFD();
	return TRUE;
      }
    }
    while ((*Buffer++ = tkn) != '\n');
    *Buffer = 0;
    return FALSE;
  }
  return TRUE;
}



