//#define DEBUG   		/* Debug mode issues memory allocation/deallocation messages */
#define USEAUTO 		/* UseAuto mode allows SAS/C autoinit/autoterminate functions */
#define KFAPI_USE_INTUITION	/* Intuition EasyRequest used to prompt for mounting volumes */

/*****************************************************************
 * KingFisher 2
 * Application Programming Interface
 * Copyright © 1994,1995 Udo Schuermann
 * All rights reserved
 *****************************************************************
 * $VER: KingFisher_API 2.26 (11.4.97)
 *****************************************************************
 * This software belongs to Udo Schuermann (author) and may not be
 * redistributed or reproduced in any form without express written
 * permission from the author:
 *    Udo Schuermann
 *    7022 Hanover Parkway, Apt. C2
 *    Greenbelt, MD 20770-2049
 *
 *    walrus@wam.umd.edu
 *****************************************************************
 * By including this file and using the supplied functions, you
 * ensure greater compatibility in future versions and reduce the
 * chance of error through omitted parameters.  PLEASE USE THIS
 * API FOR YOUR APPLICATIONS AS MUCH AS POSSIBLE, RATHER THAN THE
 * THE MESSAGE PORT INTERFACE DIRECTLY!
 *
 * INSTRUCTIONS: In your project, include "kf-api.h" (which also
 * includes "kf.h" for you) to define what is needed to interface
 * with the KingFisher 2 Server.  Compile the file "kf-api.c"
 * into an object file (kf-api.o) and link it with your other
 * objects.
 *****************************************************************
 * See kf-api.h for documentation on individual functions
 *****************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <dos/dostags.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>

#ifdef KFAPI_USE_INTUITION
#include <intuition/intuition.h>   /* EasyRequest */
#include <proto/intuition.h>       /* EasyRequest */
#endif

#include "kf-api.h"


#ifdef DEBUG
#define MALLOC(s)      _MALLOC(s)
#define FREE(p)        _FREE(p)
#define ALLOCMEM(s,t)  _ALLOCMEM(s,t)
#define FREEMEM(p,t)   _FREEMEM(p,t)
#define REALLOC(p,s)   _REALLOC(p,s)
char Scrap[256];
static long fh = 0;
void *_MALLOC(ULONG size) {
  void *p = malloc(size);
  if(fh){sprintf(Scrap,"malloc(%d) = %08x\n",size,p);FPuts(fh,Scrap);Flush(fh);}
  return p;
}
void _FREE(void *p) {
  if(fh){sprintf(Scrap,"free(%08x)\n",p);FPuts(fh,Scrap);Flush(fh);}
  free(p);
}
void *_ALLOCMEM(ULONG size, ULONG mask) {
  char *p = AllocMem(size,mask);
  if(fh){sprintf(Scrap,"AllocMem(%d,%d) = %08x\n",size,mask,p);FPuts(fh,Scrap);Flush(fh);}
  return p;
}
void _FREEMEM(void *p, ULONG size) {
  if(fh){sprintf(Scrap,"FreeMem(%08x,%d)\n",p,size);FPuts(fh,Scrap);Flush(fh);}
  FreeMem(p,size);
}
void *_REALLOC(void *p, ULONG size) {
  void *new;
  if(fh){sprintf(Scrap,"realloc(%08x,%d) = ",p,size);FPuts(fh,Scrap);Flush(fh);}
  new = realloc(p,size);
  if(fh){sprintf(Scrap,"%08x\n",new);FPuts(fh,Scrap);Flush(fh);}
  return new;
}
#else
#define MALLOC(s)      malloc(s)
#define FREE(p)        free(p)
#define ALLOCMEM(s,t)  AllocMem(s,t)
#define FREEMEM(p,t)   FreeMem(p,t)
#define REALLOC(p,s)   realloc(p,s)
#endif



/*****************************************************************
 * The client may modify the following variables:
 *
 * KFAPIServerName     The complete name of the KFServer binary,
 *                     used to auto-start the KFServer if its port
 *                     cannot be found.  KingFisher, for example,
 *                     updates this with the value of the SERVERNAME
 *                     tooltype/parameter.
 * KFAPISilentRun      Ordinarily the KF-API explicitly writes some
 *                     status information to a stdout console window
 *                     when the server needs to be started.  This
 *                     can be suppressed by setting this variable to
 *                     a value of TRUE.  KingFisher sets this value
 *                     to TRUE if the NOOUTPUT tooltype/parameter
 *                     is given.
 * KFAPIVolumePrompts  For this value to function, the DEFINE
 *                     KFAPI_USE_INTUITION must also be activated.
 *                     (see top of this file.)  Setting this value
 *                     to FALSE suppresses the API's ability to
 *                     prompt the user to insert unmounted disk
 *                     volumes.  This capability is not made part
 *                     of the server to enhance the server's ability
 *                     to effectively multitask.  Until the server
 *                     becomes a multi-threaded application, the
 *                     prompting must be done by the client process,
 *                     not the server.
 *                     For technophiles: the kfeFILEOPEN error is
 *                     an indication that the server could not open
 *                     the file whose exact name is then provided in
 *                     the .RESERVED2 field.
 * KFAPIApplicationWindow  The window to which a requester is to be
 *                     attached if KFAPIVolumePrompts is TRUE and a
 *                     database access requires user-interaction.
 * KFAPITimeout        Number of seconds for the API to wait for
 *                     the server's message port to appear.  This
 *                     defaults to 10 seconds but may be tuned to
 *                     a lower number for harddisk systems to
 *                     prevent a KFServer problem from "hanging"
 *                     the client for as much as 10 seconds before
 *                     giving up.
 * KFAPIServerDebug    Starts (if needed) the KFServer in debug mode.
 *****************************************************************
 */
char KFAPIServerName[128]             = "KFServer";
BOOL KFAPISilentRun                   = FALSE;
BOOL KFAPIVolumePrompts               = TRUE;
BOOL KFAPIServerDebug                 = FALSE;
LONG KFAPITimeout                     = 10;
struct Window *KFAPIApplicationWindow = NULL;


/*****************************************************************
 * The client may read the following variables
 *****************************************************************
 */

/* When 'ExitRequested' becomes TRUE, the server has requested
 * us to quit as swiftly as possible so it can shutdown.  It is
 * important to comply without much delay!  Do not rely on this
 * alone.
 * NOTE: changes to the server make it now more accurate to listen
 * to a ^C signal and shutdown when that is received.  There exists
 * no guarantee that the server remains available after a ^C is
 * received.
 */
BOOL  ExitRequested = FALSE;
int   CallServerErr = 0;
int   AsyncErr      = kfeOK;
char  AsyncMsg[512] = "";


/* We keep a list of all messages allocated with Login();
 * Either the Logout() or the exit()-chain will free these for us.
 * Each message is the sole provider of a pointer to the allocated
 * reply port.
 * In addition, we keep a list of messages we received that asked
 * us to kfcEXIT (shutdown request.)  These we will reply to when
 * the program exits (via the _STD_KFAPI_Down() function.
 * UPDATE for KF2.4: kfcEXIT messages are obsolete
 */
struct Messages {
  struct Messages  *NextMessage;
  struct KFMsg     *ThisMessage;
} *MessageList=NULL,*ReplyPending=NULL;

char KFAPIScratchBuffer[256]; /* used mostly internally */

#ifdef __SASC_650
#ifdef USEAUTO
/* This is a SAS/C AutoTermination function.
 * By virtue of its name (starts with _STD) it will be called
 * after the program exits.  We use this to free all memory.
 */
void _STD_KFAPI_Down( void ) {

  struct KFMsg *p;

#ifdef DEBUG
  if( fh ) {  /* the file handle is opened in the _STI_ function below */
    FPuts(fh,"Closing down\n");
    Flush(fh);
  }
#endif
  while( ReplyPending ) {
#ifdef DEBUG
    if( fh ) {
      FPuts(fh,"Freeing pending reply\n");
      Flush(fh);
    }
#endif
    ReplyMsg( (struct Message *)ReplyPending->ThisMessage );
    FREE( ReplyPending );
    ReplyPending = ReplyPending->NextMessage;
  } /* while */

  while( MessageList ) {
#ifdef DEBUG
    if( fh ) {
      FPuts(fh,"Freeing login-related stuff\n");
      Flush(fh);
    }
#endif
    p = MessageList->ThisMessage;
    DeletePort( p->AmigaMsg.mn_ReplyPort );
    if( p->Buffer != KFAPIScratchBuffer )
      FREE( p->Buffer );
    FREEMEM( p, sizeof(struct KFMsg) );
    FREE( MessageList );
    MessageList = MessageList->NextMessage;
  } /* while */
#ifdef DEBUG
  if( fh != 0 )
    Close( fh );
#endif
} /* _STD_KFAPI_Down() */



#ifdef DEBUG
void _STI_KFAPI_Init( void ) {

  fh = Open("CON:0/15/640/120/KingFisher API/AUTO/CLOSE/WAIT",MODE_NEWFILE);
  if( fh ) {
    FPuts(fh,"API Initialized\n");
    Flush(fh);
    /* yes, we leave the file handle open; the _STD_ function will close it */
  }
} /* _STI_KFAPI_Init() */
#endif
#endif
#endif



/* it is not encouraged, but certainly permissible, to call this function with
 * a pointer to a properly initialized KFMsg structure.  See the kf.h file for
 * details on what various server functions expect in parameters.
 */
BOOL CallServer( struct KFMsg *Msg ) {

  struct MsgPort *PrimaryPort,*myPort=Msg->AmigaMsg.mn_ReplyPort;
  struct KFMsg *reply;
  static NoStart = FALSE;
  BOOL result=TRUE,done=FALSE;
  ULONG sigs;
  short retry;
  char SystemCmd[sizeof(KFAPIServerName)+24];  /* +24 for "run <nil: >nil: DEBUG " and NUL */

  CallServerErr = 0;
  AsyncErr = kfeOK;

  /* It is possible to change this thing to a more-than-1 retry count
   * for times when the system is HORRIBLY slow and waiting for the
   * message port takes longer than a few seconds.
   */
  for(retry=2;retry;retry--) {
    Forbid();
    if( PrimaryPort = FindPort( KFPortName ) )
      PutMsg( PrimaryPort, (struct Message *)Msg );
    Permit();
    if( PrimaryPort || (retry == 1))
      break;  /* message sent; now go wait for a reply */

    /* Can't find the server; let's try to start it up */

    if( KFAPIServerName[0] == '\0' )  /* if no name, then don't even try */
      break;
    if( NoStart ) {
      ExitRequested = TRUE;
      break;
    }

    if( !KFAPISilentRun )
      printf("\nKFAPI: Attempting to start \"%s\"...\n",KFAPIServerName);

    /* create the command string, either
     *      run <nil: >nil: kfserver
     * or
     *      run kfserver
     */
    sprintf(SystemCmd,
	    "run %s%s%s",
	    (KFAPISilentRun?"<nil: >nil: ":""),
	    KFAPIServerName,
	    (KFAPIServerDebug?" DEBUG":""));

    if( SystemTags(SystemCmd,
		   NP_StackSize,   8192,   /* 8K stack */
		   TAG_DONE) == 0 ) {
      short xyzzy;  /* no, it's not really magic */
      
      /* now wait up to 10 seconds for the server's message port to
       * materialize before giving up; check once ever half second
       * 10 seconds ought to be enough for the KFServer to startup
       * even with a slow (floppy) systems (remember, it needs to
       * load its .prefs file, too, and initialize a database...)
       */
      for(xyzzy=2*KFAPITimeout;xyzzy;xyzzy--) {
	Delay(25L);   /* ½ second */
	if( PrimaryPort = FindPort( KFPortName ) )
	  break;  /* hooray! */
      }
    } else
      break;  /* failure of command; don't even retry ... */
  } /* for */

  if( PrimaryPort ) {  
    NoStart = TRUE;
    while( !done ) {
      sigs = Wait( (1L << myPort->mp_SigBit) | SIGBREAKF_CTRL_C );
      if( sigs & (1L << myPort->mp_SigBit) )
	while( reply = (struct KFMsg *)GetMsg( myPort ) ) {
	  if( reply == Msg ) {
#ifdef KFAPI_USE_INTUITION
	    if( (Msg->Error == kfeFILEOPEN) && (Msg->RESERVED != NULL) ) {
	      char tmp[312];
	      ULONG easyIDCMP;
	      struct EasyStruct APIRequester = {
		sizeof(struct EasyStruct),
		0,
		"KFServer Request",
		NULL,
		"Retry|Cancel",
	      };
	      sprintf(tmp,
		      "Please insert volume for database file\n%s\nin any drive!",
		      Msg->RESERVED2);
	      APIRequester.es_TextFormat = tmp;
	      easyIDCMP = IDCMP_DISKINSERTED;
	      if( EasyRequest(KFAPIApplicationWindow,&APIRequester,&easyIDCMP) ) {
		Forbid();
		if( PrimaryPort = FindPort( KFPortName ) )
		  PutMsg( PrimaryPort, (struct Message *)Msg );
		Permit();
		if( PrimaryPort == NULL ) {
		  done = TRUE;  /* error: must exit loop */
		  CallServerErr = kfeSERVERGONE;
		}
	      } else {
		done = TRUE;    /* cancel */
		result = FALSE;
	      }
	    } else
	      done = TRUE;
#else
	    done = TRUE;
#endif
	  } else {
	    if( reply->Command == kfcEXIT ) {    /* kfcEXIT is obsolete */
	      struct Messages *NewMessage = MALLOC( sizeof(struct Messages) );
	      if( NewMessage ) {
		NewMessage->ThisMessage = reply;
		NewMessage->NextMessage = ReplyPending;
		ReplyPending = NewMessage;
	      } else
		ReplyMsg( (struct Message *)reply );  /* can't store; logout at once! */
	      ExitRequested = TRUE;
	    } else {
	      strncpy(AsyncMsg,Msg->Buffer,sizeof(AsyncMsg));
	      AsyncErr = Msg->Error;
	      ReplyMsg( (struct Message *)reply );
	    }
	  }
	} /* while */
      if( sigs & SIGBREAKF_CTRL_C ) {
	SetSignal(0L,SIGBREAKF_CTRL_C);
	ExitRequested = TRUE;
	done = TRUE;
      }
    } /* while */
  } else {
    ExitRequested = TRUE;
    CallServerErr = kfeNOSERVER;
    result = FALSE;
  }
  return result;
}


/* Creates a new message node and attaches a new message to it, initializes it,
 * with a reply port, and returns us this message.  If the function returns
 * NULL, then it failed one way or another (out of memory)
 */
struct KFMsg *KFLogin( char *Identifier ) {

  struct Messages *NewMessage = MALLOC( sizeof(struct Messages) );
  char *NewBuffer;

  if( NewMessage ) {
    if( NewMessage->ThisMessage = ALLOCMEM( sizeof(struct KFMsg), MEMF_PUBLIC|MEMF_CLEAR ) ) {
      NewMessage->ThisMessage->AmigaMsg.mn_Node.ln_Type = NT_MESSAGE;
      NewMessage->ThisMessage->AmigaMsg.mn_Length = sizeof(struct KFMsg);
      if( NewMessage->ThisMessage->AmigaMsg.mn_ReplyPort = CreateMsgPort() ) {
	NewMessage->ThisMessage->Command = kfcHELLO;
	NewMessage->ThisMessage->Buffer  = KFAPIScratchBuffer;
	NewMessage->ThisMessage->BufferSize = sizeof(KFAPIScratchBuffer);
	NewMessage->ThisMessage->BParam = (ULONG)FindTask(NULL);
	NewMessage->ThisMessage->RESERVED2 = AsyncMsg;  /* a 256+ byte buffer; yes, it's getting ugly! */
	if( Identifier )
	  strncpy(KFAPIScratchBuffer,Identifier,sizeof(KFAPIScratchBuffer));
	else
	  strcpy(KFAPIScratchBuffer,"(Unnamed Client)");
	KFAPIScratchBuffer[sizeof(KFAPIScratchBuffer)-1] = '\0'; /* force termination */
	if( CallServer( NewMessage->ThisMessage ) ) {
	  if( NewMessage->ThisMessage->VERSIONID >= KFAPIVER ) {
	    if( NewMessage->ThisMessage->Error != kfeOK )
	      strncpy(KFAPIScratchBuffer,NewMessage->ThisMessage->Buffer,sizeof(KFAPIScratchBuffer));
	    if( NewBuffer = MALLOC( NewMessage->ThisMessage->BParam ) ) {
	      NewMessage->ThisMessage->Buffer = NewBuffer;
	      NewMessage->ThisMessage->BufferSize = NewMessage->ThisMessage->BParam;
	      NewMessage->NextMessage = MessageList;
	      MessageList = NewMessage;
	      return NewMessage->ThisMessage;
	    } else
	      strcpy(KFAPIScratchBuffer,"No memory for buffer");
	  } else
	    sprintf(KFAPIScratchBuffer,
		    "Client-Server version mismatch! (S=%ld < C=%ld)",
		    NewMessage->ThisMessage->VERSIONID,KFAPIVER);
	} else
	  strcpy(KFAPIScratchBuffer,"Unable to attach to the KFServer!\n"
                                    "Does KFServer fail initialization?\n"
		                    "(To test, try 'KFServer' from the CLI)");
      } else
	strcpy(KFAPIScratchBuffer,"Cannot create message port");
      FREEMEM( NewMessage->ThisMessage, sizeof(struct KFMsg) );
    } else
      strcpy(KFAPIScratchBuffer,"No memory for login");
    FREE( NewMessage );
  } else
    strcpy(KFAPIScratchBuffer,"No memory for message");
  return NULL;
} /* KFLogin() */


/* Find the indicated message in our list of messages and remove it.  Clears
 * the message, frees the message port and all dynamic memory associated
 * with it, and then logs us out of the server.  The user must have cleared
 * the Buffer pointer (it is assumed to be free) or else that memory will be
 * lost.  If the function returns FALSE, then the message could not be
 * deallocated and its memory will be lost (along with the message port.)
 */
BOOL KFLogout( struct KFMsg *Msg ) {

  struct Messages *CurMsg=MessageList,*PrevMsg=NULL;

  if( Msg ) {
    Msg->Command = kfcBYE;
    Msg->Buffer = KFAPIScratchBuffer;
    Msg->BufferSize = sizeof(KFAPIScratchBuffer);
    CallServer( Msg );
    
    while( CurMsg ) {
      if( CurMsg->ThisMessage == Msg )
	break;
      PrevMsg = CurMsg;
      CurMsg = CurMsg->NextMessage;
    }
    if( CurMsg ) {
      if( PrevMsg )
	PrevMsg->NextMessage = CurMsg->NextMessage;
      else
	MessageList = CurMsg->NextMessage;
      DeletePort( Msg->AmigaMsg.mn_ReplyPort );
      if( Msg->Buffer != KFAPIScratchBuffer )
	FREE( Msg->Buffer );
      FREEMEM( Msg, sizeof(struct KFMsg) );
      FREE( CurMsg );
      return TRUE;
    }
  }
  return FALSE;
} /* KFLogout() */


BOOL KFStatus( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcSTATUS;
    if( CallServer( Msg ) )
      if( (Msg->Error == kfeOK) || (Msg->Error == kfeTRUNC) )
	return TRUE;
  }
  return FALSE;
} /* KFStatus() */


ULONG KFMaxClients( struct KFMsg *Msg ) {

  ULONG value = 0;

  if( Msg ) {
    Msg->Command = kfcMAXCLIENTS;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	value = Msg->BParam;
  } else {
    /* NULL handle given; create a temporary and sub-functional handle that's just
     * enough to get the job done (i.e. we don't even assign a text buffer!)
     */
    struct KFMsg *TmpHandle = ALLOCMEM( sizeof(struct KFMsg), MEMF_PUBLIC|MEMF_CLEAR );
    if( TmpHandle ) {
      TmpHandle->AmigaMsg.mn_Node.ln_Type = NT_MESSAGE;
      TmpHandle->AmigaMsg.mn_Length = sizeof(struct KFMsg);
      if( TmpHandle->AmigaMsg.mn_ReplyPort = CreateMsgPort() ) {
	value = KFMaxClients( TmpHandle );   /* To iterate is human, to recurse divine! */
	DeletePort( TmpHandle->AmigaMsg.mn_ReplyPort );
      }
      FREEMEM( TmpHandle, sizeof(struct KFMsg) );
    }    
  }
  return value;
} /* KFMaxClients() */



ULONG KFCurFish( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETPOS;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return Msg->BParam;
  }
  return 0;
} /* KFCurFish() */


UWORD KFCurFlags( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETPOS;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return Msg->FParam;
  }
  return 0;
} /* KFCurFlags() */


ULONG KFCurDisk( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETDISKPOS;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return Msg->BParam;
  }
  return 0;
} /* KFCurDisk() */


char *KFCurDatabaseDescription( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETDBASEDESCRIPT;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return Msg->Buffer;
  }
  return NULL;
} /* KFCurDatabaseDescription() */

char *KFCurDatabaseName( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETDBASENAME;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return Msg->Buffer;
  }
  return NULL;
} /* KFCurDatabaseName */


ULONG KFCurDatabaseSize( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETDBASESIZE;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return Msg->BParam;
  }
  return 0;
} /* KFCurDatabaseSize() */


char *KFQuickIndex( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETQINDEX;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return (char *)Msg->BParam;
  }
  return NULL;
} /* KFQuickIndex() */


BOOL KFSelectFish( struct KFMsg *Msg, ULONG FishNumber ) {

  if( Msg ) {
    Msg->Command = kfcSETPOS;
    Msg->BParam = FishNumber;
    if( CallServer( Msg ) )
      return TRUE;
  }
  return FALSE;
} /* KFSelectFish */


ULONG KFNextVersion( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETNEXTLINK;
    if( CallServer( Msg ) )
      return Msg->BParam;
  }
  return KF_NIL_FISH;
}


ULONG KFPrevVersion( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcGETPREVLINK;
    if( CallServer( Msg ) )
      return Msg->BParam;
  }
  return KF_NIL_FISH;
}



ULONG KFSetNextVersion( struct KFMsg *Msg, ULONG LinkValue ) {

  if( Msg ) {
    Msg->Command = kfcSETNEXTLINK;
    Msg->BParam = LinkValue;
    if( CallServer( Msg ) )
      return Msg->BParam;
  }
  return -1L;
}



ULONG KFSetPrevVersion( struct KFMsg *Msg, ULONG LinkValue ) {

  if( Msg ) {
    Msg->Command = kfcSETPREVLINK;
    Msg->BParam = LinkValue;
    if( CallServer( Msg ) )
      return Msg->BParam;
  }
  return -1L;
}



BOOL KFListDatabases( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcLISTDBASES;
    if( CallServer( Msg ) )
      if( (Msg->Error == kfeOK) || (Msg->Error == kfeTRUNC) )
	return TRUE;
  }
  return FALSE;
} /* KFListDatabases() */


BOOL KFSelectDatabase( struct KFMsg *Msg, char *Filename ) {

  if( Msg ) {
    Msg->Command = kfcSELECTDBASE;
    Msg->BParam = (ULONG)Filename;
    if( CallServer( Msg ) ) {
      if( Msg->Error == kfeOK ) {
	if( Msg->BParam > Msg->BufferSize ) {
	  char *NewBuffer = REALLOC( Msg->Buffer, Msg->BParam );
	  if( NewBuffer ) {
	    if( Msg->Buffer != KFAPIScratchBuffer )
	      FREE( Msg->Buffer );
	    Msg->Buffer = NewBuffer;
	    Msg->BufferSize = Msg->BParam;
	  }
	}
	return TRUE;
      }
    }
  }
  return FALSE;
} /* KFSelectDatabase() */


BOOL KFGetFish( struct KFMsg *Msg, ULONG FishNumber ) {

  if( Msg ) {
    Msg->Command = kfcGETFISH;
    Msg->BParam = FishNumber;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFGetFish() */


BOOL KFSetFlag( struct KFMsg *Msg, UWORD Mask ) {

  if( Msg ) {
    Msg->Command = kfcSETFLAG;
    Msg->FParam  = Mask;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFCurFlags() */


BOOL KFClrFlag( struct KFMsg *Msg, UWORD Mask ) {

  if( Msg ) {
    Msg->Command = kfcCLRFLAG;
    Msg->FParam  = Mask;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFCurFlags() */


BOOL KFFindFlag( struct KFMsg *Msg, BOOL Forward, UWORD MatchMask, UWORD AvoidMask ) {

  if( Msg ) {
    if( Forward )
      Msg->Command = kfcFINDMASKFORWARD;
    else
      Msg->Command = kfcFINDMASKREVERSE;
    Msg->FParam = MatchMask;
    Msg->DParam = AvoidMask;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFFindFlag() */


ULONG KFFindNextUsedLink( struct KFMsg *Msg, ULONG FishNum ) {

  if( Msg ) {
    Msg->Command = kfcFINDNEXTUSEDLINK;
    Msg->BParam = FishNum;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return Msg->BParam;
  }
  return 0L;
} /* KFFindNextUsedLink() */


BOOL KFGetDisk( struct KFMsg *Msg, ULONG DiskNumber ) {

  if( Msg ) {
    Msg->Command = kfcSETDISKPOS;
    Msg->BParam  = DiskNumber;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return KFGetFish( Msg, KF_CURRENT_FISH );
  }
  return FALSE;
} /* KFGetDisk() */


BOOL KFNextFish( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcNEXTFISH;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFNextFish() */


BOOL KFPrevFish( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcPREVFISH;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFPrevFish() */


BOOL KFAddFish( struct KFMsg *Msg, UWORD Disk, UWORD Flags, LONG PrevVersion, LONG NextVersion ) {

  if( Msg ) {
    Msg->Command = kfcADDFISH;
    Msg->BParam = Disk;
    Msg->FParam = Flags;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK ) {
	BOOL ok = TRUE;
	if( PrevVersion > 0 ) {
	  Msg->Command = kfcSETPREVLINK;
	  Msg->BParam = PrevVersion;
	  if( !CallServer( Msg ) )
	    ok = FALSE;
	  else
	    if( Msg->Error != kfeOK )
	      ok = FALSE;
	}
	if( NextVersion > 0 ) {
	  Msg->Command = kfcSETNEXTLINK;
	  Msg->BParam = NextVersion;
	  if( !CallServer( Msg ) )
	    ok = FALSE;
	  else
	    if( Msg->Error != kfeOK )
	      ok = FALSE;
	}
	if( ok )
	  return TRUE;
      }
  }
  return FALSE;
}/* KFAddFish() */


BOOL KFEndAdding( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcENDADDING;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFEndAdding() */


LONG KFTruncate( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcTRUNCATE;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return (LONG)Msg->BParam;
  }
  return -1;
} /* KFTruncate() */


BOOL KFParseFile( struct KFMsg *Msg, char *Filename ) {

  if( Msg ) {
    Msg->Command = kfcPARSEFILE;
    Msg->BParam = (ULONG)Filename;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFParseFile() */


BOOL KFEndParsing( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcSTOPPARSE;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFEndParsing() */


/* This function performs one or more calls to the server,
 * because the server will reindex only a portion of the
 * database during any one call so as not to ignore requests
 * by other clients for services.
 */
BOOL KFReindex( struct KFMsg *Msg ) {

  if( Msg ) {
    for(;;) {
      Msg->Command = kfcREINDEX;
      Msg->BParam = kfrREBUILDMAIN | kfrREBUILDQUICK;
      if( CallServer( Msg ) )
	if( Msg->Error == kfeOK )
	  continue;
      break;
    } /* for */
    if( Msg->Error == kfeNOMORE )
      return TRUE;
  }
  return FALSE;
} /* KFReindex() */

BOOL KFLockDB( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcLOCKDB;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFLockDB() */

BOOL KFUnlockDB( struct KFMsg *Msg ) {

  if( Msg ) {
    Msg->Command = kfcUNLOCKDB;
    if( CallServer( Msg ) )
      if( Msg->Error == kfeOK )
	return TRUE;
  }
  return FALSE;
} /* KFUnlockDB() */

//End of File
