/*
 * client.c,v 1.1 1994/01/28 17:04:57 franktor Exp
 *
 * client.c
 *
 * Copyright (c) 1992, 1993 Nordic SR-NETT
 *
 * Geir.Pedersen@usit.uio.no
 *
*/

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <stdlib.h>
#include <sr-api.h>
#include <sr-util.h>
#include <sr-logger.h>
#include <sys/wait.h>
#include <errno.h>

#define TMPFILE		"/tmp/SR.client.%d"
#define STRLEN  	500
#define BUFLEN		5000

typedef enum cmdKind { cmdHELP, cmdSTATUS, cmdQUIT, cmdFIND, cmdSHOW, cmdOPEN, cmdCLOSE, cmdNEXT, cmdRESULTSET, cmdDATABASE, cmdDELRESULTSET, cmdSET, cmdSMALLSETUPPERBOUND, cmdLARGESETLOWERBOUND, cmdMEDIUMSETPRESENTNUMBER, cmdRECORDSYNTAX, cmdILLEGAL } cmdKind;  

char	*Databaser[100];

int	remoteRef	= 0;
char    remoteHost[STRLEN];
Boolean connected	= False;

char   	currentResultSet[1000];
char   	currentDatabase[100][100];
int     noCurrentDatabase       = 0; /* number of databases selected */
int	currentStartPoint	= 0;
int	smallSetUpperBound	= 10;
int 	largeSetLowerBound	= 100;
int	mediumSetPresentNumber	= 10;

Oid	preferredRecordSyntax	= OID_initializer ( NULL );

void doShow ( char *arg );



/* 
 * Basic command handeling
 *
*/

struct commands {
   char		*cmd;
   cmdKind	kind;
} commands[]	=	 {
   "QUIT",	cmdQUIT,
   "FIND",	cmdFIND,
   "SHOW",	cmdSHOW,
   "NEXT",	cmdNEXT,	/* next <n> records */
   "OPEN",	cmdOPEN,	
   "HELP",	cmdHELP,
   "STATUS",    cmdSTATUS,
   "CLOSE",	cmdCLOSE,
   "RESULTSET",	cmdRESULTSET,
   "DATABASE",  cmdDATABASE,
   "DELETERESULTSET", cmdDELRESULTSET,
   "SMALLSETUPPERBOUND", cmdSMALLSETUPPERBOUND,
   "LARGESETLOWERBOUND", cmdLARGESETLOWERBOUND,
   "MEDIUMSETPRESENTNUMBER", cmdMEDIUMSETPRESENTNUMBER,
   "SET", 	cmdSET,
   "RECORDSYNTAX", cmdRECORDSYNTAX,
   NULL,	cmdILLEGAL
};

cmdKind categorizeCmd ( char *str )
{
   int          len = strlen ( str );
   int          i;

   if ( !len )
      return cmdILLEGAL;

   for ( i=0; commands[i].cmd != (char *) NULL; i++ )
      if ( !strncasecmp ( commands[i].cmd, str, len ) )
         break;

   return commands[i].kind;
}


cmdKind get_cmd ( char *prompt, char **args )
{
   static char			cmdbuf[1000];
   char				*cmd, *arg;

   *args = (char *) NULL;
   
   while  ( gnu_gets ( prompt, stdin, cmdbuf, 999 ) )
   {
      /* find start of cmd */
      for ( cmd = cmdbuf; isspace (*cmd) && *cmd; cmd++ );
      if ( !*cmd )
	 continue;

      /* find end of cmd */
      for ( arg = cmd; *arg && !isspace(*arg); arg++ );

      /* find first argument */
      if ( *arg )
      {
         *arg = '\0';
         for ( arg++; isspace (*arg) && *arg; arg++ );
	 *args = arg;
      }

      return categorizeCmd ( cmd );
   }

   return cmdQUIT;
}

char				tmpFile[STRLEN];
FILE				*hf;

FILE *getTextFile ( void )
{
   sprintf ( tmpFile, TMPFILE, getpid() );

   return hf = fopen (tmpFile, "w" );
}


void showTextFile ( void )
{
   int			pid, ch;
   char			**pp;
   static char 		*pager_list[]= { 0, "less", "/local/bin/less",
					    "more", "/bin/more",
					    "cat", "/bin/cat", 0 };
   static char		**pagers = pager_list;
   if (! (pagers[0] || (pagers[0] = getenv ( "PAGER" ))) )
      pagers++;

   hf = fopen (tmpFile, "r" );

   /* fork pager */
   switch ( **pagers == '\0' ? -2 : (pid = fork ()) )
   {
    case 0:				/* CHILD */
      for (pp = pagers;  *pp;  pp++)
	 if ( **pp )
	    execlp ( *pp, *pp, tmpFile, (char *) NULL );

      fputs ( "\n\
No $PAGER, more or cat is available in your search path.\n\
You should probably have a systems person look into this problem.\n", stderr );
      fflush ( stderr );
      _exit ( 1 );
      /* never returns */

    default:				/* MOTHER */
      while ( waitpid ( pid, (int *)NULL, 0 ) < 0 && errno == EINTR );
      break;

    case -1:				/* FORK FAILED */
      perror ( "fork(PAGER)" );
      /* Fall through to 'NO PAGER' */
    case -2:				/* NO PAGER */
      while ( (ch = getc ( hf )) != EOF ) putchar ( ch );
      break;
   }

   unlink ( tmpFile );

   fclose ( hf );
}


char *deleteSetStatus2Str ( DeleteSetStatus s )
{
   switch ( s )
   {
    case deleteSetStatus_success:
      return "success";
      break;
      
    case deleteSetStatus_resultSetDidNotExist:
      return "resultSetDidNotExist";
      break;
      
    case deleteSetStatus_previouslyDeletedByTarget:
      return "previouslyDeletedByTarget";
      break;
      
    case deleteSetStatus_systemProblemAtTarget:
      return "systemProblemAtTarget";
      break;
      
    case deleteSetStatus_accessNotAllowed:
      return "accessNotAllowed";
      break;
      
    case deleteSetStatus_resourceControl:
      return "resourceControl";
      break;
      
    case deleteSetStatus_bulkDeleteNotSupported:
      return "bulkDeleteNotSupported";
      break;
      
    case deleteSetStatus_notAllResultSetDeletedOnBulkDelete:
      return "not all result sets deleted on bulk delete";
      break;
      
    case deleteSetStatus_notAllResultSetsDeleted:
      return "notAllResultSetsDeleted";
      break;
   }

   return "Unknown";
}


void showRecords ( struct Records *records )
{
   NamePlusRecord		*record;
   FILE				*tf;
      
   if ( !records )
   {
      LOG ( facApp, llevExceptions, "No records present" );
      fprintf ( stderr, "No records present\n" );
      return;
   }

   if ( records->recKind != recNamePlusRecord ) 
   {
      LOG ( facApp, llevExceptions, "Diagnostic returned" );
      fprintf ( stderr, "Diagnostic returned: %s\n",
	        diagRec2Str ( records->u.nonSurrogateDiagnostic, False ) );
      return;
   }

   hf = getTextFile ();
   for ( record = records->u.databaseOrSurDiagnostics; record; record = record->next )
   {
      fprintf ( hf, "Database: %s\n", (record->databaseName ? record->databaseName : "(unknown)") );
      if ( record->nprKind == nprDiag )
	 fprintf ( hf, "   Diagnostic record: %s\n\n",
		   diagRec2Str ( record->u.surrogateDiagnostic, False ) );
      else
      {
	 fprintf ( hf, "%s\n\n", os2str ( record->u.databaseRecord->octStr ) );
	 currentStartPoint++;
      }
   }
   fclose ( hf );
   showTextFile();
}


/*
 * Initialise
 *
*/

void initialiseOSI ( void )
{
   (void) OSI_Initialise ( llevAll, stderr );
}   



/*
 * openConnection
 *
*/

 
Boolean openConnection ( char *remote )
{
   SRInitialiseRequest 		ireq;
   SRInitialiseResponse		*irsp;
   char				buf[200], *msg;
   presContext			*pc;
   SR_Address			*where;

   if ( (!remote ) || !(*remote) )
   {
      fprintf ( stderr, "You must specify a remote server to connect to\n" );
      return False;
   }

   if ( index ( remote, '.' ) )
      strcpy ( buf, remote );	/* IP address */
   else
      sprintf ( buf, DEFAULT_SERVER_DN_FMT, remote );

/* pc = newPC ( Oid_RECORDSYNTAX_UNIMARC, Oid_ISO2709_TRANSFERSYNTAX_CHARENC, 0 ); */
   pc = (presContext *) NULL;
   
   if ( !str2sr_address ( buf, &where, &msg ) )
   {
      fprintf ( stderr, "Failed to resolve address %s: %s\n", buf, msg );
      return False;
   }
   if ( !SR_OpenAssociation ( where, &pc, &remoteRef ) )
   {
      fprintf ( stderr, "Failed to open connection to the remote server\n" );
      return False;
   }

   ireq.protocolVersion = PROTO_V2;
   ireq.options = OPT_SEARCH + OPT_PRESENT + OPT_DELSET;
   ireq.preferredMessageSize = 1024 * 100;
   ireq.maximumMessageSize = ireq.preferredMessageSize * 10;
   ireq.authentication = (Authentication *) NULL;
   ireq.implementationId = strdup ( "Nordic SR-NET" );
   ireq.implementationName = strdup ( "Nordic SR-NET Client" );
   ireq.implementationVersion = strdup ( "0.2" );
   ireq.userInformationField = (EXTERN *) NULL;

   if ( !SR_SendInitialiseRequest ( remoteRef, &ireq ) )
   {
      LOG ( facApp, llevExceptions, "Failed to send initialise request" );
      return False;
   }

   if ( !( irsp = SR_ReadInitialiseResponse ( remoteRef ) ) )
   {
      LOG ( facApp, llevExceptions,  "Failed to get initialise response" );
      return False;
   }

   fprintf ( stderr, "Connected to %s (SR implementation %s, version %s)\n", remote,
	     irsp->implementationName, irsp->implementationVersion );

   LOG ( facApp, llevTrace, "Connected to %s", remote );

   strcpy ( remoteHost, remote );
   
   return True;
}

void closeConnection ( int ref )
{
   SRCloseRequest		req;
   SRCloseResponse		*rsp;
   
   if ( !connected )
      return;

   if ( SR_SendCloseRequest ( ref, &req ) )
      rsp = SR_ReadCloseResponse ( ref );
      
   connected = False;
}


/*
 * Search 
 * 
*/

void doSearch ( char *cclCmd )
{
   SRSearchRequest		sreq;
   SRSearchResponse		*srsp;
   char				*resultSetName	= (char *) NULL;
   char				*selector	= (char *) NULL;
   int                          i;

   if ( !noCurrentDatabase )
   {
      fprintf ( stderr, "No database selected, use the DATABASE command to specify a database\n" );
      return;
   }
  
   if ( (selector = index ( cclCmd, '=' )) )
   {
      char		*cp	= selector;

      while ( cp > cclCmd && (isspace(*cp) || (*cp == '=')) )
	 *cp = 0, cp--;
      selector++;
      resultSetName = cclCmd;
   }
   else
      selector = cclCmd;
   
   sreq.smallSetUpperBound		= smallSetUpperBound;
   sreq.largeSetLowerBound		= largeSetLowerBound;
   sreq.mediumSetPresentNumber		= mediumSetPresentNumber;
   if ( resultSetName )
   {
      sreq.replaceIndicator		= False;
      sreq.proposedResultSetId		= safe_strdup ( resultSetName );
   }
   else
   {
      sreq.replaceIndicator		= True;
      sreq.proposedResultSetId		= "SRNETT";
   }
   for ( i = 0; i < noCurrentDatabase; i++ )
     Databaser[i] = currentDatabase[i];
   sreq.databaseId			= Databaser;
   sreq.noDatabaseIds			= noCurrentDatabase;
   sreq.smallSetRecordComposition	= new_RecordComposition ( True, "F" );
   sreq.mediumSetRecordComposition	= new_RecordComposition ( True, "F" );
   sreq.preferredRecordSyntax		= OID_dup (preferredRecordSyntax);
   LOG ( facApp, llevDebug, "Using RecordSyntax %s", OID_Oid2name ( sreq.preferredRecordSyntax ) );
   sreq.query = (Query *) malloc ( sizeof ( Query ) );
   sreq.query->kind			= queryIso8777Query;
   sreq.query->q.iso8777Query		= str2os ( selector );

   LOG ( facApp, llevTrace, "Searching for %s", cclCmd );
   
   if ( !SR_SendSearchRequest ( remoteRef, &sreq ) )
   {
      LOG ( facApp, llevExceptions, "Failed to send search request" );
      fprintf ( stderr, "Failed to send search request\n" );

      return;
   }

   if ( !( srsp = SR_ReadSearchResponse ( remoteRef ) ) )
   {
      LOG ( facApp, llevExceptions, "Failed to receive searchresponse" );
      fprintf ( stderr, "Failed to receive search response\n" );

      return;
   }

   /* process search response */
   if ( !srsp->searchStatus )
   {
      LOG ( facApp, llevExceptions, "Search failed (searchStatus is False )" );
      fprintf ( stderr, "Search failed - remote error" );
      if ( srsp->numberOfRecordsReturned )
	 fprintf ( stderr, ": %s", diagRec2Str ( srsp->records->u.nonSurrogateDiagnostic, False ) );
      fprintf ( stderr, "\n" );
      return;
   }

   fprintf ( stderr, "%d records found, %d records returned\n",
	     srsp->numberOfRecordsFound, srsp->numberOfRecordsReturned );

   if ( srsp->numberOfRecordsReturned )
   {
      struct Records		*records;
      
      if ( !(records = srsp->records) )
      {
	 LOG ( facApp, llevExceptions, "No records present" );
	 fprintf ( stderr, "No records present\n" );
      }

      if ( records->recKind != recNamePlusRecord ) 
      {
	 LOG ( facApp, llevExceptions, "Diagnostic returned" );
	 fprintf ( stderr, "Diagnostic returned: %s\n",
		   diagRec2Str ( records->u.nonSurrogateDiagnostic, False ) );
      }
      else
	 showRecords ( srsp->records );

      currentStartPoint = 1;
   }

   if ( resultSetName && srsp->numberOfRecordsFound )
   {
      strcpy ( currentResultSet, resultSetName );
      fprintf ( stderr, "Current result set: %s\n", currentResultSet );
   }
}


/* doNext()
 *
 * NEXT number of records [ starting record ]
*/

Boolean doNext ( char *arg )
{
   int				noRecords, startPoint;
   char				buf[100];

   if ( !arg )
   {
      sprintf ( buf, "%d 1", currentStartPoint );
      doShow ( buf );
   }
   else
      switch ( sscanf ( arg, "%d %d", &noRecords, &startPoint ) )
      {
       case 0:
	 printf ( "Expected one or two integer arguments: noRecords [ startPoint ]\n" );
	 break;

       case 1:
	 sprintf ( buf, "%d %d", currentStartPoint, noRecords );
	 doShow ( buf );
	 break;

       case 2:
	 sprintf ( buf, "%d %d", currentStartPoint, noRecords );
	 doShow ( buf );
	 break;
      }

   return True;
}

      


/*
 * doShow()
 *
 * SHOW starting record [ number of records ]
 *
*/ 

void doShow ( char *arg )
{
   SRPresentRequest		req;
   SRPresentResponse		*rsp;
   int				noRecords, startPoint;

   if ( (!arg) || (!*arg) )
   {
      fprintf ( stderr, "Expected two integer arguments: startPoint noRecords\n" );
      return;
   }

   if ( sscanf ( arg, "%d %d", &startPoint, &noRecords ) != 2 )
   {
      fprintf ( stderr, "Expected two integer arguments: startPoint noRecords\n" );
      return;
   }

   bzero ( &req, sizeof ( SRPresentRequest ) );

   req.resultSetId = safe_strdup ( currentResultSet );
   req.resultSetStartPoint = startPoint;
   req.numberOfRecordsRequested = noRecords;
   req.recordComposition = new_RecordComposition ( True, "F" );
   req.preferredRecordSyntax = preferredRecordSyntax;
   LOG(facApp, llevDebug, "Record syntax: %s", OID_Oid2name(req.preferredRecordSyntax));

   if ( !SR_SendPresentRequest ( remoteRef, &req ) )
   {
      LOG ( facApp, llevExceptions, "Failed to send Present request" );
      fprintf ( stderr, "Failed to send present request\n" );

      return;
   }

   if ( !( rsp = SR_ReadPresentResponse ( remoteRef ) ) )
   {
      LOG ( facApp, llevExceptions, "Failed to receive Presentresponse" );
      fprintf ( stderr, "Failed to receive present response\n" );

      return;
   }

   fprintf ( stderr, "%d records returned\n", rsp->numberOfRecordsReturned );
   
   if ( rsp->numberOfRecordsReturned  )
      showRecords ( rsp->records );
}


/*
 * RESULTSET
*/

Boolean doResultset ( char *args )
{
   if ( (!args) || (!strlen(args)) )
      printf ( "Current result set is %s\n", currentResultSet );
   else
   {
      strcpy ( currentResultSet, args );

      currentStartPoint = 1;
   }
   return True;
}




/*
 * DATABASE
 *
*/

Boolean doDatabase ( char *args )   
{
   if ( (!args) || (!strlen(args)) )
   {
     int                        i;

     printf ( "Current database is " );
     for ( i = 0; i < noCurrentDatabase; i++ )
       printf ( "%s%s", i > 0 ? ", " : "", currentDatabase[i] );
   }
   else
   {
     char                      *cp1, *cp2;
   
     for ( cp1 = args, cp2 = index ( cp1, ',' ), noCurrentDatabase = 0; 
           cp1; 
           cp1 = (cp2 ? cp2+1 : cp2), cp2 = ( cp1 ? index ( cp1, ',' ) : cp1 ) )
     {
       char                    c;

       if ( cp2 )
       {
	 c = *cp2;
	 *cp2 = '\0';
       }
       strcpy ( currentDatabase[noCurrentDatabase++], cp1 );
       if ( cp2 )
	 *cp2 = c;
     }
   }   
   return True;
}

void doRecordSyntax ( char *args )
{
   const char	*pref_name	= OID_Oid2name ( preferredRecordSyntax );
   Oid		oid;
   if ( !(args && *args) )
      printf ( "Current RecordSyntax is %s.\n", pref_name );
   else if ( !OID_contents ((oid = OID_str2Oid ( args ))) )
      printf ( "Unrecognized RecordSyntax: %s.\n", args );
   else
   {
      printf ( "Previous RecordSyntax was %s.\n", pref_name );
      preferredRecordSyntax = oid;
      printf ( "New RecordSyntax is %s.\n", OID_Oid2name (oid) );
   }
}


/*
 * DELETE RESULT SET
 *
*/

Boolean doDeleteResultSet ( char *args )   
{
   SRDeleteResultSetRequest 	dreq;
   SRDeleteResultSetResponse	*drsp;

   bzero ( (char *) &dreq, sizeof ( SRDeleteResultSetRequest ) );
   
   if ( (!args) || (!*args) )
   {
      /* delete all result sets */
      fprintf ( stderr, "Deleting all result sets... " );
      dreq.deleteSetFunction = deleteSetFunction_all;
   }
   else
   {
      char			*cp;
      ResultSetList		*rl, *rrl;

      for ( cp = strtok ( args, " \t\n\r," ), rl = rrl = (ResultSetList *) NULL; cp;
	    cp = strtok ( NULL, " \t\n\r," ) )
      {
	 if ( !rl )
	    rl = rrl = (ResultSetList *) smalloc ( sizeof ( ResultSetList ) );
	 else
	 {
	    rrl->next = (ResultSetList *) smalloc ( sizeof ( ResultSetList ) );
	    rrl = rrl->next;
	 }

	 rrl->resultSetId = safe_strdup ( cp );
      }

      dreq.deleteSetFunction = deleteSetFunction_list;
      dreq.resultSetList = rl;

      fprintf ( stderr, "Deleting named result sets... " );
   }

   /* send the request of */
   if ( !SR_SendDeleteResultSetRequest ( remoteRef, &dreq ) )
   {
      LOG ( facApp, llevExceptions, "Failed to send delete result set request" );
      fprintf ( stderr, "Failed to send delete result set request\n" );

      return False;
   }

   if ( !( drsp = SR_ReadDeleteResultSetResponse ( remoteRef ) ) )
   {
      LOG ( facApp, llevExceptions,  "Failed to get delete result set response" );
      return False;
   }

   if ( drsp->deleteOperationStatus != deleteSetStatus_success )
   {
      ListStatuses		*l	= (ListStatuses *) NULL;
      
      fprintf ( stderr, " Deletion of result set(s) failed: %s\n",
	        deleteSetStatus2Str ( drsp->deleteOperationStatus ) );

      if ( drsp->deleteListStatuses )
	 l = drsp->deleteListStatuses;
      else if ( drsp->bulkStatuses )
	 l = drsp->bulkStatuses;

      if ( l )
      {
	 fprintf ( stderr, "  Below are statuses for individual result sets:\n" );
	 while ( l )
	 {
	    fprintf ( stderr, "      %s: %s\n", l->resultSetId,
		      deleteSetStatus2Str ( l->deleteSetStatus ) );
	    l = l->next;
	 }
      }
   }
   else
      fprintf ( stderr, " Done\n" );

   return True;
}


/*
 * SET
 *
*/

Boolean doSet ( char *args )
{
   char				*var;
   char				*val;

   if ( (!args) || (!*args) )
   {
      fprintf ( stderr, "Expected argument, one of SMALLSETUPPERBOUND, LARGESETLOWERBOUND, MEDIUMSETPRESENTNUMBER\n" );
      goto Done;
   }

   var = strtok ( args, " \t\n\r=," );
   if ( !(val = strtok ( NULL, " \t\n\r=," )) )
   {
      fprintf ( stderr, "Excepted value to follow variable name\n" );
      goto Done;
   }
   
   switch ( categorizeCmd ( var ) )
   {
    case cmdSMALLSETUPPERBOUND:
      smallSetUpperBound = atoi ( val );
      break;

    case cmdLARGESETLOWERBOUND:
      largeSetLowerBound = atoi ( val );
      break;

    case cmdMEDIUMSETPRESENTNUMBER:
      mediumSetPresentNumber = atoi ( val );
      break;

    default: break;
   }

 Done:
   fprintf ( stderr, "   Small set upper bound: %d\n", smallSetUpperBound );
   fprintf ( stderr, "   Large set lower bound: %d\n", largeSetLowerBound );
   fprintf ( stderr, "   Medium set present number: %d\n", mediumSetPresentNumber );

   return True;
}
      


int main ( int argc, char **argv )
{
   char				*args;
   cmdKind			cmd;

   initLogger ( "./client.log" );
   initialiseOSI ();

   **currentDatabase = '\0';
   currentResultSet[0] = '\0';
   noCurrentDatabase = 0;
   
   preferredRecordSyntax = Oid_RECORDSYNTAX_NORMARC; /* Default is NORMARC */
   LOG(facApp, llevDebug, "Default recordSyntax: %s", OID_Oid2name(preferredRecordSyntax));

   printf ( "Nordic SR-NET - Simple SR Client v0.1d\n" );
   printf ( "Type HELP for information on commands.\n" );

/*   if ( !loadAttributeSet ( "../bib-1/attrset.bib-1" ) )
   {
      LOG ( facApp, llevExceptions, "Failed to parse attribute set bib-1" );
      exit ( 1 );
   }
*/
   if ( !loadDiagnosticSet ( "../bib-1/diagset.bib-1" ) )
   {
      LOG ( facApp, llevExceptions, "Failed to parse diagnostic set bib-1" );
      fprintf ( stderr, "Failed to parse diagnostic set bib-1\n" );
      exit ( 1 );
   }

   LOG(facApp, llevDebug, "Default recordSyntax: %s", OID_Oid2name(preferredRecordSyntax));
   currentResultSet[0] = 0;

#ifdef DEBUG
   malloc_debug ( 2 );
#endif
   
   while ( (cmd = get_cmd ( "SR> ", &args )) != cmdQUIT )
   {
      if ( cmd == cmdHELP || cmd == cmdILLEGAL )
      {
	 if ( cmd == cmdHELP )
	    printf ( "Commands are:\n" );
	 else
	    printf ( "Illegal command - legal commands are:\n" );
         printf ( "   STATUS\n" );
	 printf ( "   OPEN database-server\n" );
	 printf ( "   FIND [resultset ID =] CCL command\n" );
	 printf ( "   SHOW record number [ number of records to show]\n" );
	 printf ( "   NEXT number of records to show [ starting record number]\n" );
	 printf ( "   RESULTSET result set name\n" );
	 printf ( "   DATABASE database [, database]\n" );
	 printf ( "   DELETERESULTSET [result set, result set, ]\n" );
	 printf ( "   SET SMALLSETUPPERBOUND | LARGESETLOWERBOUND | MEDIUMSETPRESENTNUMBER <value>\n" );
         printf ( "   RECORDSYNTAX <record syntax>\n");
	 printf ( "   CLOSE\n" );
	 printf ( "   QUIT\n" );
      }
      else if ( (!connected) && cmd != cmdOPEN )
	 fprintf ( stderr,
		   "Not connected to any remote server - use OPEN to establish a connection\n" );
      else
	 switch ( cmd )
	 {
	  case cmdOPEN:
	    if ( connected )
	       fprintf ( stderr,
			 "You are already connected to a server, please disconnect before connecting to a new server.\n" );
	    else if ( ! (connected = openConnection ( args )) )
	       fprintf ( stderr, "Failed to open connection.\n" );
	    break;

	  case cmdFIND:
	    doSearch ( args );
	    break;

	  case cmdNEXT:
	    doNext ( args );
	    break;

	  case cmdRESULTSET:
	    doResultset ( args );
	    break;

	  case cmdSHOW:
	    doShow ( args );
	    break;

	  case cmdDATABASE:
	    doDatabase ( args );
	    break;

	  case cmdDELRESULTSET:
	    doDeleteResultSet ( args );
	    break;

	  case cmdSET:
	    doSet ( args );
	    break;

          case cmdRECORDSYNTAX:
            doRecordSyntax ( args );
            break;

          case cmdSTATUS:
	    if ( connected )
	    {
	       printf ( "Connected to %s", remoteHost );
	       if ( noCurrentDatabase )
	       {
		 int                         i;
		 
		 printf ( ", current database " );
		 for ( i = 0; i < noCurrentDatabase; i++ )
		   printf ( "%s%s", i > 0 ? ", " : "", currentDatabase[i] );
               }
	       if ( *currentResultSet )
		  printf ( ", current result set \"%s\"", currentResultSet );
	       printf ( ".\n" );
	    }
	    else
	       printf ( "Not connected.\n" );
	    break;

	  case cmdCLOSE:
	    if ( connected )
	       closeConnection ( remoteRef );
	    break;
	    
	  default:
	    fprintf ( stderr, "This command has not yet been implemented\n" );
	    break;
	 }
   }

   if ( connected )
      closeConnection ( remoteRef );
   return 0;
}
