home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #3 / amigamamagazinepolishissue1998.iso / bazy / kingfisher-distribution / developer / kf-api.c < prev    next >
C/C++ Source or Header  |  1997-04-10  |  26KB  |  950 lines

  1. //#define DEBUG           /* Debug mode issues memory allocation/deallocation messages */
  2. #define USEAUTO         /* UseAuto mode allows SAS/C autoinit/autoterminate functions */
  3. #define KFAPI_USE_INTUITION    /* Intuition EasyRequest used to prompt for mounting volumes */
  4.  
  5. /*****************************************************************
  6.  * KingFisher 2
  7.  * Application Programming Interface
  8.  * Copyright © 1994,1995 Udo Schuermann
  9.  * All rights reserved
  10.  *****************************************************************
  11.  * $VER: KingFisher_API 2.26 (11.4.97)
  12.  *****************************************************************
  13.  * This software belongs to Udo Schuermann (author) and may not be
  14.  * redistributed or reproduced in any form without express written
  15.  * permission from the author:
  16.  *    Udo Schuermann
  17.  *    7022 Hanover Parkway, Apt. C2
  18.  *    Greenbelt, MD 20770-2049
  19.  *
  20.  *    walrus@wam.umd.edu
  21.  *****************************************************************
  22.  * By including this file and using the supplied functions, you
  23.  * ensure greater compatibility in future versions and reduce the
  24.  * chance of error through omitted parameters.  PLEASE USE THIS
  25.  * API FOR YOUR APPLICATIONS AS MUCH AS POSSIBLE, RATHER THAN THE
  26.  * THE MESSAGE PORT INTERFACE DIRECTLY!
  27.  *
  28.  * INSTRUCTIONS: In your project, include "kf-api.h" (which also
  29.  * includes "kf.h" for you) to define what is needed to interface
  30.  * with the KingFisher 2 Server.  Compile the file "kf-api.c"
  31.  * into an object file (kf-api.o) and link it with your other
  32.  * objects.
  33.  *****************************************************************
  34.  * See kf-api.h for documentation on individual functions
  35.  *****************************************************************
  36.  */
  37.  
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41.  
  42. #include <dos/dostags.h>
  43. #include <exec/memory.h>
  44. #include <libraries/dos.h>
  45. #include <proto/exec.h>
  46. #include <proto/dos.h>
  47.  
  48. #ifdef KFAPI_USE_INTUITION
  49. #include <intuition/intuition.h>   /* EasyRequest */
  50. #include <proto/intuition.h>       /* EasyRequest */
  51. #endif
  52.  
  53. #include "kf-api.h"
  54.  
  55.  
  56. #ifdef DEBUG
  57. #define MALLOC(s)      _MALLOC(s)
  58. #define FREE(p)        _FREE(p)
  59. #define ALLOCMEM(s,t)  _ALLOCMEM(s,t)
  60. #define FREEMEM(p,t)   _FREEMEM(p,t)
  61. #define REALLOC(p,s)   _REALLOC(p,s)
  62. char Scrap[256];
  63. static long fh = 0;
  64. void *_MALLOC(ULONG size) {
  65.   void *p = malloc(size);
  66.   if(fh){sprintf(Scrap,"malloc(%d) = %08x\n",size,p);FPuts(fh,Scrap);Flush(fh);}
  67.   return p;
  68. }
  69. void _FREE(void *p) {
  70.   if(fh){sprintf(Scrap,"free(%08x)\n",p);FPuts(fh,Scrap);Flush(fh);}
  71.   free(p);
  72. }
  73. void *_ALLOCMEM(ULONG size, ULONG mask) {
  74.   char *p = AllocMem(size,mask);
  75.   if(fh){sprintf(Scrap,"AllocMem(%d,%d) = %08x\n",size,mask,p);FPuts(fh,Scrap);Flush(fh);}
  76.   return p;
  77. }
  78. void _FREEMEM(void *p, ULONG size) {
  79.   if(fh){sprintf(Scrap,"FreeMem(%08x,%d)\n",p,size);FPuts(fh,Scrap);Flush(fh);}
  80.   FreeMem(p,size);
  81. }
  82. void *_REALLOC(void *p, ULONG size) {
  83.   void *new;
  84.   if(fh){sprintf(Scrap,"realloc(%08x,%d) = ",p,size);FPuts(fh,Scrap);Flush(fh);}
  85.   new = realloc(p,size);
  86.   if(fh){sprintf(Scrap,"%08x\n",new);FPuts(fh,Scrap);Flush(fh);}
  87.   return new;
  88. }
  89. #else
  90. #define MALLOC(s)      malloc(s)
  91. #define FREE(p)        free(p)
  92. #define ALLOCMEM(s,t)  AllocMem(s,t)
  93. #define FREEMEM(p,t)   FreeMem(p,t)
  94. #define REALLOC(p,s)   realloc(p,s)
  95. #endif
  96.  
  97.  
  98.  
  99. /*****************************************************************
  100.  * The client may modify the following variables:
  101.  *
  102.  * KFAPIServerName     The complete name of the KFServer binary,
  103.  *                     used to auto-start the KFServer if its port
  104.  *                     cannot be found.  KingFisher, for example,
  105.  *                     updates this with the value of the SERVERNAME
  106.  *                     tooltype/parameter.
  107.  * KFAPISilentRun      Ordinarily the KF-API explicitly writes some
  108.  *                     status information to a stdout console window
  109.  *                     when the server needs to be started.  This
  110.  *                     can be suppressed by setting this variable to
  111.  *                     a value of TRUE.  KingFisher sets this value
  112.  *                     to TRUE if the NOOUTPUT tooltype/parameter
  113.  *                     is given.
  114.  * KFAPIVolumePrompts  For this value to function, the DEFINE
  115.  *                     KFAPI_USE_INTUITION must also be activated.
  116.  *                     (see top of this file.)  Setting this value
  117.  *                     to FALSE suppresses the API's ability to
  118.  *                     prompt the user to insert unmounted disk
  119.  *                     volumes.  This capability is not made part
  120.  *                     of the server to enhance the server's ability
  121.  *                     to effectively multitask.  Until the server
  122.  *                     becomes a multi-threaded application, the
  123.  *                     prompting must be done by the client process,
  124.  *                     not the server.
  125.  *                     For technophiles: the kfeFILEOPEN error is
  126.  *                     an indication that the server could not open
  127.  *                     the file whose exact name is then provided in
  128.  *                     the .RESERVED2 field.
  129.  * KFAPIApplicationWindow  The window to which a requester is to be
  130.  *                     attached if KFAPIVolumePrompts is TRUE and a
  131.  *                     database access requires user-interaction.
  132.  * KFAPITimeout        Number of seconds for the API to wait for
  133.  *                     the server's message port to appear.  This
  134.  *                     defaults to 10 seconds but may be tuned to
  135.  *                     a lower number for harddisk systems to
  136.  *                     prevent a KFServer problem from "hanging"
  137.  *                     the client for as much as 10 seconds before
  138.  *                     giving up.
  139.  * KFAPIServerDebug    Starts (if needed) the KFServer in debug mode.
  140.  *****************************************************************
  141.  */
  142. char KFAPIServerName[128]             = "KFServer";
  143. BOOL KFAPISilentRun                   = FALSE;
  144. BOOL KFAPIVolumePrompts               = TRUE;
  145. BOOL KFAPIServerDebug                 = FALSE;
  146. LONG KFAPITimeout                     = 10;
  147. struct Window *KFAPIApplicationWindow = NULL;
  148.  
  149.  
  150. /*****************************************************************
  151.  * The client may read the following variables
  152.  *****************************************************************
  153.  */
  154.  
  155. /* When 'ExitRequested' becomes TRUE, the server has requested
  156.  * us to quit as swiftly as possible so it can shutdown.  It is
  157.  * important to comply without much delay!  Do not rely on this
  158.  * alone.
  159.  * NOTE: changes to the server make it now more accurate to listen
  160.  * to a ^C signal and shutdown when that is received.  There exists
  161.  * no guarantee that the server remains available after a ^C is
  162.  * received.
  163.  */
  164. BOOL  ExitRequested = FALSE;
  165. int   CallServerErr = 0;
  166. int   AsyncErr      = kfeOK;
  167. char  AsyncMsg[512] = "";
  168.  
  169.  
  170. /* We keep a list of all messages allocated with Login();
  171.  * Either the Logout() or the exit()-chain will free these for us.
  172.  * Each message is the sole provider of a pointer to the allocated
  173.  * reply port.
  174.  * In addition, we keep a list of messages we received that asked
  175.  * us to kfcEXIT (shutdown request.)  These we will reply to when
  176.  * the program exits (via the _STD_KFAPI_Down() function.
  177.  * UPDATE for KF2.4: kfcEXIT messages are obsolete
  178.  */
  179. struct Messages {
  180.   struct Messages  *NextMessage;
  181.   struct KFMsg     *ThisMessage;
  182. } *MessageList=NULL,*ReplyPending=NULL;
  183.  
  184. char KFAPIScratchBuffer[256]; /* used mostly internally */
  185.  
  186. #ifdef __SASC_650
  187. #ifdef USEAUTO
  188. /* This is a SAS/C AutoTermination function.
  189.  * By virtue of its name (starts with _STD) it will be called
  190.  * after the program exits.  We use this to free all memory.
  191.  */
  192. void _STD_KFAPI_Down( void ) {
  193.  
  194.   struct KFMsg *p;
  195.  
  196. #ifdef DEBUG
  197.   if( fh ) {  /* the file handle is opened in the _STI_ function below */
  198.     FPuts(fh,"Closing down\n");
  199.     Flush(fh);
  200.   }
  201. #endif
  202.   while( ReplyPending ) {
  203. #ifdef DEBUG
  204.     if( fh ) {
  205.       FPuts(fh,"Freeing pending reply\n");
  206.       Flush(fh);
  207.     }
  208. #endif
  209.     ReplyMsg( (struct Message *)ReplyPending->ThisMessage );
  210.     FREE( ReplyPending );
  211.     ReplyPending = ReplyPending->NextMessage;
  212.   } /* while */
  213.  
  214.   while( MessageList ) {
  215. #ifdef DEBUG
  216.     if( fh ) {
  217.       FPuts(fh,"Freeing login-related stuff\n");
  218.       Flush(fh);
  219.     }
  220. #endif
  221.     p = MessageList->ThisMessage;
  222.     DeletePort( p->AmigaMsg.mn_ReplyPort );
  223.     if( p->Buffer != KFAPIScratchBuffer )
  224.       FREE( p->Buffer );
  225.     FREEMEM( p, sizeof(struct KFMsg) );
  226.     FREE( MessageList );
  227.     MessageList = MessageList->NextMessage;
  228.   } /* while */
  229. #ifdef DEBUG
  230.   if( fh != 0 )
  231.     Close( fh );
  232. #endif
  233. } /* _STD_KFAPI_Down() */
  234.  
  235.  
  236.  
  237. #ifdef DEBUG
  238. void _STI_KFAPI_Init( void ) {
  239.  
  240.   fh = Open("CON:0/15/640/120/KingFisher API/AUTO/CLOSE/WAIT",MODE_NEWFILE);
  241.   if( fh ) {
  242.     FPuts(fh,"API Initialized\n");
  243.     Flush(fh);
  244.     /* yes, we leave the file handle open; the _STD_ function will close it */
  245.   }
  246. } /* _STI_KFAPI_Init() */
  247. #endif
  248. #endif
  249. #endif
  250.  
  251.  
  252.  
  253. /* it is not encouraged, but certainly permissible, to call this function with
  254.  * a pointer to a properly initialized KFMsg structure.  See the kf.h file for
  255.  * details on what various server functions expect in parameters.
  256.  */
  257. BOOL CallServer( struct KFMsg *Msg ) {
  258.  
  259.   struct MsgPort *PrimaryPort,*myPort=Msg->AmigaMsg.mn_ReplyPort;
  260.   struct KFMsg *reply;
  261.   static NoStart = FALSE;
  262.   BOOL result=TRUE,done=FALSE;
  263.   ULONG sigs;
  264.   short retry;
  265.   char SystemCmd[sizeof(KFAPIServerName)+24];  /* +24 for "run <nil: >nil: DEBUG " and NUL */
  266.  
  267.   CallServerErr = 0;
  268.   AsyncErr = kfeOK;
  269.  
  270.   /* It is possible to change this thing to a more-than-1 retry count
  271.    * for times when the system is HORRIBLY slow and waiting for the
  272.    * message port takes longer than a few seconds.
  273.    */
  274.   for(retry=2;retry;retry--) {
  275.     Forbid();
  276.     if( PrimaryPort = FindPort( KFPortName ) )
  277.       PutMsg( PrimaryPort, (struct Message *)Msg );
  278.     Permit();
  279.     if( PrimaryPort || (retry == 1))
  280.       break;  /* message sent; now go wait for a reply */
  281.  
  282.     /* Can't find the server; let's try to start it up */
  283.  
  284.     if( KFAPIServerName[0] == '\0' )  /* if no name, then don't even try */
  285.       break;
  286.     if( NoStart ) {
  287.       ExitRequested = TRUE;
  288.       break;
  289.     }
  290.  
  291.     if( !KFAPISilentRun )
  292.       printf("\nKFAPI: Attempting to start \"%s\"...\n",KFAPIServerName);
  293.  
  294.     /* create the command string, either
  295.      *      run <nil: >nil: kfserver
  296.      * or
  297.      *      run kfserver
  298.      */
  299.     sprintf(SystemCmd,
  300.         "run %s%s%s",
  301.         (KFAPISilentRun?"<nil: >nil: ":""),
  302.         KFAPIServerName,
  303.         (KFAPIServerDebug?" DEBUG":""));
  304.  
  305.     if( SystemTags(SystemCmd,
  306.            NP_StackSize,   8192,   /* 8K stack */
  307.            TAG_DONE) == 0 ) {
  308.       short xyzzy;  /* no, it's not really magic */
  309.       
  310.       /* now wait up to 10 seconds for the server's message port to
  311.        * materialize before giving up; check once ever half second
  312.        * 10 seconds ought to be enough for the KFServer to startup
  313.        * even with a slow (floppy) systems (remember, it needs to
  314.        * load its .prefs file, too, and initialize a database...)
  315.        */
  316.       for(xyzzy=2*KFAPITimeout;xyzzy;xyzzy--) {
  317.     Delay(25L);   /* ½ second */
  318.     if( PrimaryPort = FindPort( KFPortName ) )
  319.       break;  /* hooray! */
  320.       }
  321.     } else
  322.       break;  /* failure of command; don't even retry ... */
  323.   } /* for */
  324.  
  325.   if( PrimaryPort ) {  
  326.     NoStart = TRUE;
  327.     while( !done ) {
  328.       sigs = Wait( (1L << myPort->mp_SigBit) | SIGBREAKF_CTRL_C );
  329.       if( sigs & (1L << myPort->mp_SigBit) )
  330.     while( reply = (struct KFMsg *)GetMsg( myPort ) ) {
  331.       if( reply == Msg ) {
  332. #ifdef KFAPI_USE_INTUITION
  333.         if( (Msg->Error == kfeFILEOPEN) && (Msg->RESERVED != NULL) ) {
  334.           char tmp[312];
  335.           ULONG easyIDCMP;
  336.           struct EasyStruct APIRequester = {
  337.         sizeof(struct EasyStruct),
  338.         0,
  339.         "KFServer Request",
  340.         NULL,
  341.         "Retry|Cancel",
  342.           };
  343.           sprintf(tmp,
  344.               "Please insert volume for database file\n%s\nin any drive!",
  345.               Msg->RESERVED2);
  346.           APIRequester.es_TextFormat = tmp;
  347.           easyIDCMP = IDCMP_DISKINSERTED;
  348.           if( EasyRequest(KFAPIApplicationWindow,&APIRequester,&easyIDCMP) ) {
  349.         Forbid();
  350.         if( PrimaryPort = FindPort( KFPortName ) )
  351.           PutMsg( PrimaryPort, (struct Message *)Msg );
  352.         Permit();
  353.         if( PrimaryPort == NULL ) {
  354.           done = TRUE;  /* error: must exit loop */
  355.           CallServerErr = kfeSERVERGONE;
  356.         }
  357.           } else {
  358.         done = TRUE;    /* cancel */
  359.         result = FALSE;
  360.           }
  361.         } else
  362.           done = TRUE;
  363. #else
  364.         done = TRUE;
  365. #endif
  366.       } else {
  367.         if( reply->Command == kfcEXIT ) {    /* kfcEXIT is obsolete */
  368.           struct Messages *NewMessage = MALLOC( sizeof(struct Messages) );
  369.           if( NewMessage ) {
  370.         NewMessage->ThisMessage = reply;
  371.         NewMessage->NextMessage = ReplyPending;
  372.         ReplyPending = NewMessage;
  373.           } else
  374.         ReplyMsg( (struct Message *)reply );  /* can't store; logout at once! */
  375.           ExitRequested = TRUE;
  376.         } else {
  377.           strncpy(AsyncMsg,Msg->Buffer,sizeof(AsyncMsg));
  378.           AsyncErr = Msg->Error;
  379.           ReplyMsg( (struct Message *)reply );
  380.         }
  381.       }
  382.     } /* while */
  383.       if( sigs & SIGBREAKF_CTRL_C ) {
  384.     SetSignal(0L,SIGBREAKF_CTRL_C);
  385.     ExitRequested = TRUE;
  386.     done = TRUE;
  387.       }
  388.     } /* while */
  389.   } else {
  390.     ExitRequested = TRUE;
  391.     CallServerErr = kfeNOSERVER;
  392.     result = FALSE;
  393.   }
  394.   return result;
  395. }
  396.  
  397.  
  398. /* Creates a new message node and attaches a new message to it, initializes it,
  399.  * with a reply port, and returns us this message.  If the function returns
  400.  * NULL, then it failed one way or another (out of memory)
  401.  */
  402. struct KFMsg *KFLogin( char *Identifier ) {
  403.  
  404.   struct Messages *NewMessage = MALLOC( sizeof(struct Messages) );
  405.   char *NewBuffer;
  406.  
  407.   if( NewMessage ) {
  408.     if( NewMessage->ThisMessage = ALLOCMEM( sizeof(struct KFMsg), MEMF_PUBLIC|MEMF_CLEAR ) ) {
  409.       NewMessage->ThisMessage->AmigaMsg.mn_Node.ln_Type = NT_MESSAGE;
  410.       NewMessage->ThisMessage->AmigaMsg.mn_Length = sizeof(struct KFMsg);
  411.       if( NewMessage->ThisMessage->AmigaMsg.mn_ReplyPort = CreateMsgPort() ) {
  412.     NewMessage->ThisMessage->Command = kfcHELLO;
  413.     NewMessage->ThisMessage->Buffer  = KFAPIScratchBuffer;
  414.     NewMessage->ThisMessage->BufferSize = sizeof(KFAPIScratchBuffer);
  415.     NewMessage->ThisMessage->BParam = (ULONG)FindTask(NULL);
  416.     NewMessage->ThisMessage->RESERVED2 = AsyncMsg;  /* a 256+ byte buffer; yes, it's getting ugly! */
  417.     if( Identifier )
  418.       strncpy(KFAPIScratchBuffer,Identifier,sizeof(KFAPIScratchBuffer));
  419.     else
  420.       strcpy(KFAPIScratchBuffer,"(Unnamed Client)");
  421.     KFAPIScratchBuffer[sizeof(KFAPIScratchBuffer)-1] = '\0'; /* force termination */
  422.     if( CallServer( NewMessage->ThisMessage ) ) {
  423.       if( NewMessage->ThisMessage->VERSIONID >= KFAPIVER ) {
  424.         if( NewMessage->ThisMessage->Error != kfeOK )
  425.           strncpy(KFAPIScratchBuffer,NewMessage->ThisMessage->Buffer,sizeof(KFAPIScratchBuffer));
  426.         if( NewBuffer = MALLOC( NewMessage->ThisMessage->BParam ) ) {
  427.           NewMessage->ThisMessage->Buffer = NewBuffer;
  428.           NewMessage->ThisMessage->BufferSize = NewMessage->ThisMessage->BParam;
  429.           NewMessage->NextMessage = MessageList;
  430.           MessageList = NewMessage;
  431.           return NewMessage->ThisMessage;
  432.         } else
  433.           strcpy(KFAPIScratchBuffer,"No memory for buffer");
  434.       } else
  435.         sprintf(KFAPIScratchBuffer,
  436.             "Client-Server version mismatch! (S=%ld < C=%ld)",
  437.             NewMessage->ThisMessage->VERSIONID,KFAPIVER);
  438.     } else
  439.       strcpy(KFAPIScratchBuffer,"Unable to attach to the KFServer!\n"
  440.                                     "Does KFServer fail initialization?\n"
  441.                             "(To test, try 'KFServer' from the CLI)");
  442.       } else
  443.     strcpy(KFAPIScratchBuffer,"Cannot create message port");
  444.       FREEMEM( NewMessage->ThisMessage, sizeof(struct KFMsg) );
  445.     } else
  446.       strcpy(KFAPIScratchBuffer,"No memory for login");
  447.     FREE( NewMessage );
  448.   } else
  449.     strcpy(KFAPIScratchBuffer,"No memory for message");
  450.   return NULL;
  451. } /* KFLogin() */
  452.  
  453.  
  454. /* Find the indicated message in our list of messages and remove it.  Clears
  455.  * the message, frees the message port and all dynamic memory associated
  456.  * with it, and then logs us out of the server.  The user must have cleared
  457.  * the Buffer pointer (it is assumed to be free) or else that memory will be
  458.  * lost.  If the function returns FALSE, then the message could not be
  459.  * deallocated and its memory will be lost (along with the message port.)
  460.  */
  461. BOOL KFLogout( struct KFMsg *Msg ) {
  462.  
  463.   struct Messages *CurMsg=MessageList,*PrevMsg=NULL;
  464.  
  465.   if( Msg ) {
  466.     Msg->Command = kfcBYE;
  467.     Msg->Buffer = KFAPIScratchBuffer;
  468.     Msg->BufferSize = sizeof(KFAPIScratchBuffer);
  469.     CallServer( Msg );
  470.     
  471.     while( CurMsg ) {
  472.       if( CurMsg->ThisMessage == Msg )
  473.     break;
  474.       PrevMsg = CurMsg;
  475.       CurMsg = CurMsg->NextMessage;
  476.     }
  477.     if( CurMsg ) {
  478.       if( PrevMsg )
  479.     PrevMsg->NextMessage = CurMsg->NextMessage;
  480.       else
  481.     MessageList = CurMsg->NextMessage;
  482.       DeletePort( Msg->AmigaMsg.mn_ReplyPort );
  483.       if( Msg->Buffer != KFAPIScratchBuffer )
  484.     FREE( Msg->Buffer );
  485.       FREEMEM( Msg, sizeof(struct KFMsg) );
  486.       FREE( CurMsg );
  487.       return TRUE;
  488.     }
  489.   }
  490.   return FALSE;
  491. } /* KFLogout() */
  492.  
  493.  
  494. BOOL KFStatus( struct KFMsg *Msg ) {
  495.  
  496.   if( Msg ) {
  497.     Msg->Command = kfcSTATUS;
  498.     if( CallServer( Msg ) )
  499.       if( (Msg->Error == kfeOK) || (Msg->Error == kfeTRUNC) )
  500.     return TRUE;
  501.   }
  502.   return FALSE;
  503. } /* KFStatus() */
  504.  
  505.  
  506. ULONG KFMaxClients( struct KFMsg *Msg ) {
  507.  
  508.   ULONG value = 0;
  509.  
  510.   if( Msg ) {
  511.     Msg->Command = kfcMAXCLIENTS;
  512.     if( CallServer( Msg ) )
  513.       if( Msg->Error == kfeOK )
  514.     value = Msg->BParam;
  515.   } else {
  516.     /* NULL handle given; create a temporary and sub-functional handle that's just
  517.      * enough to get the job done (i.e. we don't even assign a text buffer!)
  518.      */
  519.     struct KFMsg *TmpHandle = ALLOCMEM( sizeof(struct KFMsg), MEMF_PUBLIC|MEMF_CLEAR );
  520.     if( TmpHandle ) {
  521.       TmpHandle->AmigaMsg.mn_Node.ln_Type = NT_MESSAGE;
  522.       TmpHandle->AmigaMsg.mn_Length = sizeof(struct KFMsg);
  523.       if( TmpHandle->AmigaMsg.mn_ReplyPort = CreateMsgPort() ) {
  524.     value = KFMaxClients( TmpHandle );   /* To iterate is human, to recurse divine! */
  525.     DeletePort( TmpHandle->AmigaMsg.mn_ReplyPort );
  526.       }
  527.       FREEMEM( TmpHandle, sizeof(struct KFMsg) );
  528.     }    
  529.   }
  530.   return value;
  531. } /* KFMaxClients() */
  532.  
  533.  
  534.  
  535. ULONG KFCurFish( struct KFMsg *Msg ) {
  536.  
  537.   if( Msg ) {
  538.     Msg->Command = kfcGETPOS;
  539.     if( CallServer( Msg ) )
  540.       if( Msg->Error == kfeOK )
  541.     return Msg->BParam;
  542.   }
  543.   return 0;
  544. } /* KFCurFish() */
  545.  
  546.  
  547. UWORD KFCurFlags( struct KFMsg *Msg ) {
  548.  
  549.   if( Msg ) {
  550.     Msg->Command = kfcGETPOS;
  551.     if( CallServer( Msg ) )
  552.       if( Msg->Error == kfeOK )
  553.     return Msg->FParam;
  554.   }
  555.   return 0;
  556. } /* KFCurFlags() */
  557.  
  558.  
  559. ULONG KFCurDisk( struct KFMsg *Msg ) {
  560.  
  561.   if( Msg ) {
  562.     Msg->Command = kfcGETDISKPOS;
  563.     if( CallServer( Msg ) )
  564.       if( Msg->Error == kfeOK )
  565.     return Msg->BParam;
  566.   }
  567.   return 0;
  568. } /* KFCurDisk() */
  569.  
  570.  
  571. char *KFCurDatabaseDescription( struct KFMsg *Msg ) {
  572.  
  573.   if( Msg ) {
  574.     Msg->Command = kfcGETDBASEDESCRIPT;
  575.     if( CallServer( Msg ) )
  576.       if( Msg->Error == kfeOK )
  577.     return Msg->Buffer;
  578.   }
  579.   return NULL;
  580. } /* KFCurDatabaseDescription() */
  581.  
  582. char *KFCurDatabaseName( struct KFMsg *Msg ) {
  583.  
  584.   if( Msg ) {
  585.     Msg->Command = kfcGETDBASENAME;
  586.     if( CallServer( Msg ) )
  587.       if( Msg->Error == kfeOK )
  588.     return Msg->Buffer;
  589.   }
  590.   return NULL;
  591. } /* KFCurDatabaseName */
  592.  
  593.  
  594. ULONG KFCurDatabaseSize( struct KFMsg *Msg ) {
  595.  
  596.   if( Msg ) {
  597.     Msg->Command = kfcGETDBASESIZE;
  598.     if( CallServer( Msg ) )
  599.       if( Msg->Error == kfeOK )
  600.     return Msg->BParam;
  601.   }
  602.   return 0;
  603. } /* KFCurDatabaseSize() */
  604.  
  605.  
  606. char *KFQuickIndex( struct KFMsg *Msg ) {
  607.  
  608.   if( Msg ) {
  609.     Msg->Command = kfcGETQINDEX;
  610.     if( CallServer( Msg ) )
  611.       if( Msg->Error == kfeOK )
  612.     return (char *)Msg->BParam;
  613.   }
  614.   return NULL;
  615. } /* KFQuickIndex() */
  616.  
  617.  
  618. BOOL KFSelectFish( struct KFMsg *Msg, ULONG FishNumber ) {
  619.  
  620.   if( Msg ) {
  621.     Msg->Command = kfcSETPOS;
  622.     Msg->BParam = FishNumber;
  623.     if( CallServer( Msg ) )
  624.       return TRUE;
  625.   }
  626.   return FALSE;
  627. } /* KFSelectFish */
  628.  
  629.  
  630. ULONG KFNextVersion( struct KFMsg *Msg ) {
  631.  
  632.   if( Msg ) {
  633.     Msg->Command = kfcGETNEXTLINK;
  634.     if( CallServer( Msg ) )
  635.       return Msg->BParam;
  636.   }
  637.   return KF_NIL_FISH;
  638. }
  639.  
  640.  
  641. ULONG KFPrevVersion( struct KFMsg *Msg ) {
  642.  
  643.   if( Msg ) {
  644.     Msg->Command = kfcGETPREVLINK;
  645.     if( CallServer( Msg ) )
  646.       return Msg->BParam;
  647.   }
  648.   return KF_NIL_FISH;
  649. }
  650.  
  651.  
  652.  
  653. ULONG KFSetNextVersion( struct KFMsg *Msg, ULONG LinkValue ) {
  654.  
  655.   if( Msg ) {
  656.     Msg->Command = kfcSETNEXTLINK;
  657.     Msg->BParam = LinkValue;
  658.     if( CallServer( Msg ) )
  659.       return Msg->BParam;
  660.   }
  661.   return -1L;
  662. }
  663.  
  664.  
  665.  
  666. ULONG KFSetPrevVersion( struct KFMsg *Msg, ULONG LinkValue ) {
  667.  
  668.   if( Msg ) {
  669.     Msg->Command = kfcSETPREVLINK;
  670.     Msg->BParam = LinkValue;
  671.     if( CallServer( Msg ) )
  672.       return Msg->BParam;
  673.   }
  674.   return -1L;
  675. }
  676.  
  677.  
  678.  
  679. BOOL KFListDatabases( struct KFMsg *Msg ) {
  680.  
  681.   if( Msg ) {
  682.     Msg->Command = kfcLISTDBASES;
  683.     if( CallServer( Msg ) )
  684.       if( (Msg->Error == kfeOK) || (Msg->Error == kfeTRUNC) )
  685.     return TRUE;
  686.   }
  687.   return FALSE;
  688. } /* KFListDatabases() */
  689.  
  690.  
  691. BOOL KFSelectDatabase( struct KFMsg *Msg, char *Filename ) {
  692.  
  693.   if( Msg ) {
  694.     Msg->Command = kfcSELECTDBASE;
  695.     Msg->BParam = (ULONG)Filename;
  696.     if( CallServer( Msg ) ) {
  697.       if( Msg->Error == kfeOK ) {
  698.     if( Msg->BParam > Msg->BufferSize ) {
  699.       char *NewBuffer = REALLOC( Msg->Buffer, Msg->BParam );
  700.       if( NewBuffer ) {
  701.         if( Msg->Buffer != KFAPIScratchBuffer )
  702.           FREE( Msg->Buffer );
  703.         Msg->Buffer = NewBuffer;
  704.         Msg->BufferSize = Msg->BParam;
  705.       }
  706.     }
  707.     return TRUE;
  708.       }
  709.     }
  710.   }
  711.   return FALSE;
  712. } /* KFSelectDatabase() */
  713.  
  714.  
  715. BOOL KFGetFish( struct KFMsg *Msg, ULONG FishNumber ) {
  716.  
  717.   if( Msg ) {
  718.     Msg->Command = kfcGETFISH;
  719.     Msg->BParam = FishNumber;
  720.     if( CallServer( Msg ) )
  721.       if( Msg->Error == kfeOK )
  722.     return TRUE;
  723.   }
  724.   return FALSE;
  725. } /* KFGetFish() */
  726.  
  727.  
  728. BOOL KFSetFlag( struct KFMsg *Msg, UWORD Mask ) {
  729.  
  730.   if( Msg ) {
  731.     Msg->Command = kfcSETFLAG;
  732.     Msg->FParam  = Mask;
  733.     if( CallServer( Msg ) )
  734.       if( Msg->Error == kfeOK )
  735.     return TRUE;
  736.   }
  737.   return FALSE;
  738. } /* KFCurFlags() */
  739.  
  740.  
  741. BOOL KFClrFlag( struct KFMsg *Msg, UWORD Mask ) {
  742.  
  743.   if( Msg ) {
  744.     Msg->Command = kfcCLRFLAG;
  745.     Msg->FParam  = Mask;
  746.     if( CallServer( Msg ) )
  747.       if( Msg->Error == kfeOK )
  748.     return TRUE;
  749.   }
  750.   return FALSE;
  751. } /* KFCurFlags() */
  752.  
  753.  
  754. BOOL KFFindFlag( struct KFMsg *Msg, BOOL Forward, UWORD MatchMask, UWORD AvoidMask ) {
  755.  
  756.   if( Msg ) {
  757.     if( Forward )
  758.       Msg->Command = kfcFINDMASKFORWARD;
  759.     else
  760.       Msg->Command = kfcFINDMASKREVERSE;
  761.     Msg->FParam = MatchMask;
  762.     Msg->DParam = AvoidMask;
  763.     if( CallServer( Msg ) )
  764.       if( Msg->Error == kfeOK )
  765.     return TRUE;
  766.   }
  767.   return FALSE;
  768. } /* KFFindFlag() */
  769.  
  770.  
  771. ULONG KFFindNextUsedLink( struct KFMsg *Msg, ULONG FishNum ) {
  772.  
  773.   if( Msg ) {
  774.     Msg->Command = kfcFINDNEXTUSEDLINK;
  775.     Msg->BParam = FishNum;
  776.     if( CallServer( Msg ) )
  777.       if( Msg->Error == kfeOK )
  778.     return Msg->BParam;
  779.   }
  780.   return 0L;
  781. } /* KFFindNextUsedLink() */
  782.  
  783.  
  784. BOOL KFGetDisk( struct KFMsg *Msg, ULONG DiskNumber ) {
  785.  
  786.   if( Msg ) {
  787.     Msg->Command = kfcSETDISKPOS;
  788.     Msg->BParam  = DiskNumber;
  789.     if( CallServer( Msg ) )
  790.       if( Msg->Error == kfeOK )
  791.     return KFGetFish( Msg, KF_CURRENT_FISH );
  792.   }
  793.   return FALSE;
  794. } /* KFGetDisk() */
  795.  
  796.  
  797. BOOL KFNextFish( struct KFMsg *Msg ) {
  798.  
  799.   if( Msg ) {
  800.     Msg->Command = kfcNEXTFISH;
  801.     if( CallServer( Msg ) )
  802.       if( Msg->Error == kfeOK )
  803.     return TRUE;
  804.   }
  805.   return FALSE;
  806. } /* KFNextFish() */
  807.  
  808.  
  809. BOOL KFPrevFish( struct KFMsg *Msg ) {
  810.  
  811.   if( Msg ) {
  812.     Msg->Command = kfcPREVFISH;
  813.     if( CallServer( Msg ) )
  814.       if( Msg->Error == kfeOK )
  815.     return TRUE;
  816.   }
  817.   return FALSE;
  818. } /* KFPrevFish() */
  819.  
  820.  
  821. BOOL KFAddFish( struct KFMsg *Msg, UWORD Disk, UWORD Flags, LONG PrevVersion, LONG NextVersion ) {
  822.  
  823.   if( Msg ) {
  824.     Msg->Command = kfcADDFISH;
  825.     Msg->BParam = Disk;
  826.     Msg->FParam = Flags;
  827.     if( CallServer( Msg ) )
  828.       if( Msg->Error == kfeOK ) {
  829.     BOOL ok = TRUE;
  830.     if( PrevVersion > 0 ) {
  831.       Msg->Command = kfcSETPREVLINK;
  832.       Msg->BParam = PrevVersion;
  833.       if( !CallServer( Msg ) )
  834.         ok = FALSE;
  835.       else
  836.         if( Msg->Error != kfeOK )
  837.           ok = FALSE;
  838.     }
  839.     if( NextVersion > 0 ) {
  840.       Msg->Command = kfcSETNEXTLINK;
  841.       Msg->BParam = NextVersion;
  842.       if( !CallServer( Msg ) )
  843.         ok = FALSE;
  844.       else
  845.         if( Msg->Error != kfeOK )
  846.           ok = FALSE;
  847.     }
  848.     if( ok )
  849.       return TRUE;
  850.       }
  851.   }
  852.   return FALSE;
  853. }/* KFAddFish() */
  854.  
  855.  
  856. BOOL KFEndAdding( struct KFMsg *Msg ) {
  857.  
  858.   if( Msg ) {
  859.     Msg->Command = kfcENDADDING;
  860.     if( CallServer( Msg ) )
  861.       if( Msg->Error == kfeOK )
  862.     return TRUE;
  863.   }
  864.   return FALSE;
  865. } /* KFEndAdding() */
  866.  
  867.  
  868. LONG KFTruncate( struct KFMsg *Msg ) {
  869.  
  870.   if( Msg ) {
  871.     Msg->Command = kfcTRUNCATE;
  872.     if( CallServer( Msg ) )
  873.       if( Msg->Error == kfeOK )
  874.     return (LONG)Msg->BParam;
  875.   }
  876.   return -1;
  877. } /* KFTruncate() */
  878.  
  879.  
  880. BOOL KFParseFile( struct KFMsg *Msg, char *Filename ) {
  881.  
  882.   if( Msg ) {
  883.     Msg->Command = kfcPARSEFILE;
  884.     Msg->BParam = (ULONG)Filename;
  885.     if( CallServer( Msg ) )
  886.       if( Msg->Error == kfeOK )
  887.     return TRUE;
  888.   }
  889.   return FALSE;
  890. } /* KFParseFile() */
  891.  
  892.  
  893. BOOL KFEndParsing( struct KFMsg *Msg ) {
  894.  
  895.   if( Msg ) {
  896.     Msg->Command = kfcSTOPPARSE;
  897.     if( CallServer( Msg ) )
  898.       if( Msg->Error == kfeOK )
  899.     return TRUE;
  900.   }
  901.   return FALSE;
  902. } /* KFEndParsing() */
  903.  
  904.  
  905. /* This function performs one or more calls to the server,
  906.  * because the server will reindex only a portion of the
  907.  * database during any one call so as not to ignore requests
  908.  * by other clients for services.
  909.  */
  910. BOOL KFReindex( struct KFMsg *Msg ) {
  911.  
  912.   if( Msg ) {
  913.     for(;;) {
  914.       Msg->Command = kfcREINDEX;
  915.       Msg->BParam = kfrREBUILDMAIN | kfrREBUILDQUICK;
  916.       if( CallServer( Msg ) )
  917.     if( Msg->Error == kfeOK )
  918.       continue;
  919.       break;
  920.     } /* for */
  921.     if( Msg->Error == kfeNOMORE )
  922.       return TRUE;
  923.   }
  924.   return FALSE;
  925. } /* KFReindex() */
  926.  
  927. BOOL KFLockDB( struct KFMsg *Msg ) {
  928.  
  929.   if( Msg ) {
  930.     Msg->Command = kfcLOCKDB;
  931.     if( CallServer( Msg ) )
  932.       if( Msg->Error == kfeOK )
  933.     return TRUE;
  934.   }
  935.   return FALSE;
  936. } /* KFLockDB() */
  937.  
  938. BOOL KFUnlockDB( struct KFMsg *Msg ) {
  939.  
  940.   if( Msg ) {
  941.     Msg->Command = kfcUNLOCKDB;
  942.     if( CallServer( Msg ) )
  943.       if( Msg->Error == kfeOK )
  944.     return TRUE;
  945.   }
  946.   return FALSE;
  947. } /* KFUnlockDB() */
  948.  
  949. //End of File
  950.