/*
 * hclient.c,v 1.3 1994/01/30 11:41:53 franktor Exp
 *
 * client.c
 *
 * Copyright (c) 1994 Nordic SR-NETT
 *
 * H.B.Furuseth@usit.uio.no
 *
*/

#include <sr-util.h>
#include <sr-logger.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>

#ifndef VMS
#include <high/client-structdesc.h>
#include <sys/wait.h>
#else
#include <client-structdesc.h>
#endif

#define	PREFERREDRECORDSYNTAX	"RECORDSYNTAX NORMARC"

char *gnu_gets ( const char *prompt, FILE *from, char *buf, int len );

/* Fixes/additions to interface */

char *HlvlDiagRec2Str ( HlvlDiagRec *d, Boolean includeSetName )
{
   char *buf = malloc (safe_strlen (d->diagnosticSetId) +
		       safe_strlen (d->addinfo) + 50 );
   if (!d->addinfo)
      sprintf (buf, "Condition %d of %s", d->condition, d->diagnosticSetId);
   else if ( includeSetName )
      sprintf (buf, "%s (Condition %d of %s)", d->addinfo, d->condition, d->diagnosticSetId);
   else
      strcpy ( buf, d->addinfo );
   return buf;
}




typedef struct Connection {
   char			*name;
   Boolean		connected;
} Connection;


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

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

int		remoteRef	= 0;
Connection	*connections	= NULL;
unsigned	noConnections	= 0;
unsigned	allocConnections= 0;

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;

char	*preferredRecordSyntax	= NULL;

void doShow ( char *arg );


/* 
 * Basic command handeling
 *
*/

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

   "SMALLSETUPPERBOUND", cmdSMALLSETUPPERBOUND,
   "LARGESETLOWERBOUND", cmdLARGESETLOWERBOUND,
   "MEDIUMSETPRESENTNUMBER", cmdMEDIUMSETPRESENTNUMBER,
   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 beginning and end of arguments */
      if ( *arg )
      {
         *arg = '\0';
         for ( arg++; isspace (*arg) && *arg; arg++ );
	 *args = arg;

	 if ( *arg )
	    for ( arg += strlen ( arg ); isspace ( *--arg ); *arg = '\0' );
      }

      return categorizeCmd ( cmd );
   }

   return cmdQUIT;
}

#ifdef VMS

#define	getTextFile()		stdout
#define showTextFile()		((void) 0)
#define closeTextFile(f)	((void) (f))

#else /* !VMS */

char				tmpFilename[STRLEN];

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

   return fopen (tmpFilename, "w" );
}


void showTextFile ( void )
{
   FILE			*hf;
   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 (tmpFilename, "r" );

   /* fork pager */
   switch ( **pagers == '\0' ? -2 : (pid = fork ()) )
   {
    case 0:				/* CHILD */
      for (pp = pagers;  *pp;  pp++)
	 if ( **pp )
	    execlp ( *pp, *pp, tmpFilename, (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 ( ! EX_OK );
      /* 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 ( tmpFilename );

   fclose ( hf );
}

#define closeTextFile(f)	((void) fclose ((f)))

#endif /* !VMS */

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

   if ( records->recKind != recHlvlNamePlusRecord ) 
   {
      if ( records->recKind != recHlvlNonSurrogateDiagRec )
      {
	 LOG ( facApp, llevExceptions, "Unknown record kind received: %d", records->recKind );
	 fprintf ( stderr, "Unknown record kind received: %d\n", records->recKind );
      }
      else
      {
	 LOG ( facApp, llevExceptions, "Diagnostic returned" );
	 fprintf ( stderr, "Diagnostic returned: %s\n",
		  HlvlDiagRec2Str ( records->u.nonSurrogateDiagnosticRecord, False ) );
      }
      return;
   }

   tf = getTextFile ();
   for ( record = records->u.databaseOrSurDiagnostics; record; record = record->next )
   {
      if ( record->recKind == recHlvlNamePlusRecord )
      {
	 fprintf ( tf, "Database: %s\n", (record->u.record.databaseName ? record->u.record.databaseName : "(unknown)") );
	 fprintf ( tf, "%s\n\n", os2str ( &record->u.record.databaseRecord ) );
	 currentStartPoint++;
      }
      else if ( record->recKind == recHlvlNonSurrogateDiagRec )
	 fprintf ( tf, "Diagnostic record: %s\n\n",
		   HlvlDiagRec2Str ( record->u.surrogateDiagnostic, False ) );
      else
	 fprintf ( tf, "   Unknown record kind received: %d\n\n", record->recKind );
   }
   closeTextFile ( tf );
   showTextFile ();
}


/*
 * SELECT
 *
*/

Connection	*getConnection ( int ref )
{
   return ((unsigned)ref < noConnections ? &connections[ref] : NULL);
}

void		new_Connection ( int ref, char *name, Boolean connected )
{
   name			= strdup ( name );
   if ( noConnections <= (unsigned)ref )
   {
      connections	= (Connection *)
	 realloc ( (char *)connections, (ref+10) * sizeof(Connection) );
      bzero ( &connections[noConnections],
	      (ref+10-noConnections) * sizeof(Connection) );
      noConnections	= ref + 10;
   }
   else if ( connections[ref].name )
      free ( connections[ref].name );
   connections[ref].name	= name;
   connections[ref].connected	= connected;
}

Boolean		print_Connection ( int ref, const char *prefix, Boolean force )
{
   Connection		*conn = &connections[ref];
   if ( !(force
	  ? (conn->name != NULL)
	  : (conn->connected || ref == remoteRef)) )
      return False;

   printf ( "%s%s connection %d: %s%s\n", prefix,
	    (ref == remoteRef ? "Selected" : "        "),
	    ref, conn->name,
	    (conn->connected ? "" : " (not connected)") );
   return True;
}


Boolean doSelect ( const char *args )
{
   unsigned			i, ref;

   if ( args && *args )
      if ( !isdigit ( *args ) )
      {
	 fprintf ( stderr, "Expected connection number\n" );
	 return False;
      }
      else if ( !((ref = atoi ( args )) < noConnections
		  && connections[ref].name ) )
	 fprintf ( stderr, "Connection %d does not exist.\n", ref );
      else
      {
	 remoteRef = ref;
	 print_Connection ( ref, "", True );
	 return True;
      }

   for ( i = ref = 0;  ref < noConnections;  ref++ )
      i |= print_Connection ( ref, (i ? "" : "Connections:\n"), False );
   if ( !i )
      puts ( "No connections." );
   return False;
}

/*
 * Send a packet, wait for proper response, and return it if successful.
 * At failure, the error message is returned in ERR, or printed on stder
 * if ERR is 0.
 */

HC_Packet *
HC_Send_and_wait ( HC_Packet *req, Boolean wait, char **err )
{
  static char		msgBuf[100], *msgPtr = msgBuf;
  static int		msgLen = sizeof(msgBuf);
  static HC_Packet	none_packet = { hcNone, 0 };
  HC_Packet		*resp;
  HC_PacketTypes	resp_type;

  if ( err )
    *err = NULL;

  if ( !HC_Send ( req ) )
  {
    sprintf ( msgPtr, "Failed to send %s.", HC_Packet_name[req->type] );
    goto failed;
  }

  if ( !wait )
    return &none_packet;

  resp_type = (HC_PacketTypes) ((int)req->type + 1);

  if ( !HC_Receive ( &resp ) )
  {
    sprintf ( msgPtr, "Failed to receive %s.", HC_Packet_name[resp_type] );
    goto failed;
  }

  if ( resp->type == resp_type )
  {
    if ( resp->ref != req->ref )
      LOG(facApp, llevExceptions, "Expected ref %d, got %d", req->ref, resp->ref);
    return resp;
  }

  if ( resp->type == hcErrorMsg )
  {
    HC_ErrorMsg	*msg = resp->u.error_msg;
    const char	*fmt = (msg->message ? "%s failed: %s" : "%s failed.");
    int		len;

    if (msg->message &&
	msgLen <= (len = strlen (fmt) + strlen (msg->message)))
      msgLen = len,
    msgPtr = (msgPtr == msgBuf ? malloc ( len ) : realloc ( msgPtr, len ));
    sprintf ( msgPtr, fmt, HC_Packet_name[req->type], msg->message );
    LOG ( facApp, llevExceptions, "%s", msgPtr );

    if ( msg->abort && req->type != hcCloseRequest )
      HC_Close ();		/* Maybe only close that connection???? */
  }
  else
  {
    strcpy (msgPtr, "Protocol error: received unexpected package type ");
    if ( (unsigned)resp->type < (unsigned)hcErrorMsg )
      strcat ( msgPtr, HC_Packet_name[resp->type] );
    else
      sprintf ( msgPtr + strlen ( msgPtr ), "%d", resp->type );
    HC_Close ();
  }

  HC_FreePacket ( resp );

 failed:
  LOG ( facApp, llevExceptions, "%s", msgPtr );
  if ( err )
    *err = msgPtr;
  else
    fprintf ( stderr, "%s\n", msgPtr );
  return NULL;
}


/*
 * openConnection
 *
*/

static
Boolean openConnection ( char *remote )
{
   HC_Packet			req_p, *rsp_p;
   HC_OpenRequest		req;
   HC_OpenResponse		*rsp;
   char				buf[200];

   req_p.type			= hcOpenRequest;
/* req_p.ref			= ++remoteRef; */
   req_p.u.open_request		= &req;

   if ( (!remote || !*remote)
	&& (connections[remoteRef].connected
	    || !(remote = connections[remoteRef].name)) )
   {
      fputs ( "You must specify a remote server to connect to.\n", stderr );
      return False;
   }

   if ( !HC_Initialise () )
   {
      unsigned i;
      fputs ( "Lost contact with high-level client-server.\n", stderr );
      for ( i = 0; i < noConnections; connections[i++].connected = False );
   }

   if ( connections[remoteRef].connected )
      puts ( "Opening another connection." );

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

   req.address			= buf;
   req.preferredMessageSize	= 1024 * 100;
   req.maximumMessageSize	= req.preferredMessageSize * 10;
   req.authentification		= NULL;
   req.userInformationField	= NULL;

   if ( !(rsp_p = HC_Send_and_wait ( &req_p, True, (char **)NULL )) )
   {
      puts ( "Failed to open connection." );
      return False;
   }

   rsp = rsp_p->u.open_response;

   if ( !rsp->openAccepted )
      fputs ( "Failed to open connection", stdout );
   if ( rsp->message && *rsp->message )
      printf ( "%s%s\n", (rsp->openAccepted ? "" : ": "), rsp->message );
   else if ( !rsp->openAccepted )
      puts ( "." );

   if ( rsp->openAccepted )
   {
      new_Connection ( remoteRef = req_p.ref, remote, True );
      printf ( "Connection %d to %s (SR implementation %s, version %s)\n",
	       remoteRef, remote,
	       rsp->implementationName, rsp->implementationVersion );

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

   HC_FreePacket ( rsp_p );
   return connections[remoteRef].connected;
}


static
void closeConnection ( int ref )
{
   HC_Packet			req_p, *rsp_p;
   static HC_CloseRequest	req;
   HC_CloseResponse		*rsp;
   
   if ( !((unsigned)ref < noConnections && connections[ref].connected) )
      return;

   req_p.type			= hcCloseRequest;
   req_p.ref			= remoteRef;
   req_p.u.close_request	= &req;

   printf ( "Closing connection to %s...\n", connections[ref].name );
   if ( (rsp_p = HC_Send_and_wait ( &req_p, True, (char **)NULL )) )
   {
      rsp = rsp_p->u.close_response;

      if ( rsp->message )
	 puts ( rsp->message );

      if ( !rsp->closeAccepted )
	 fprintf ( stderr, "Close failed.  %s\n", (rsp->message ? rsp->message : "") );
      HC_FreePacket ( rsp_p );
   }

   connections[ref].connected = False;
}

void doClose ( char *args )
{
   int		ref = remoteRef;
   if ( args && *args && !((ref = atoi ( args )) || isdigit ( *args )) )
      fputs ( "Expected optional connection number\n", stderr );
   else
      closeConnection ( ref );
}

/*
 * Search 
 * 
*/

void doSearch ( char *cclCmd )
{
   HC_Packet			req_p, *rsp_p;
   HC_SearchRequest		req;
   HC_SearchResponse		*rsp;
   RecordComposition		r_comp;
   Query			query;

   char				*resultSetName	= (char *) NULL;
   char				*selector	= (char *) NULL;
   int                          i;

   if ( !connections[remoteRef].connected )
      return;

   if ( !noCurrentDatabase )
   {
      fprintf ( stderr, "No database selected, use the DATABASE command to specify a database\n" );
      return;
   }
  
   req_p.type			= hcSearchRequest;
   req_p.ref			= remoteRef;
   req_p.u.search_request	= &req;

   if ( (selector = strchr ( cclCmd, '=' )) )
   {
      char		*cp	= selector;

      while ( cp > cclCmd && (isspace(*cp) || (*cp == '=')) )
	 *cp = 0, cp--;
      selector++;
      resultSetName = cclCmd;
   }
   else
      selector = cclCmd;
   
   req.smallSetUpperBound	= smallSetUpperBound;
   req.largeSetLowerBound	= largeSetLowerBound;
   req.mediumSetPresentNumber	= mediumSetPresentNumber;
   if ( resultSetName )
   {
      req.replaceIndicator	= False;
      req.proposedResultSetId	= resultSetName;
   }
   else
   {
      req.replaceIndicator	= True;
      req.proposedResultSetId	= "SRNETT";
   }

   req.databaseNames = NULL;
   for ( i = noCurrentDatabase;  --i >= 0;  )
   {
      DatabaseName *curDB = (DatabaseName *) malloc ( sizeof (DatabaseName) );
      curDB->database		= safe_strdup ( currentDatabase[i] );
      curDB->next		= req.databaseNames;
      req.databaseNames		= curDB;
   }

   r_comp.genericRecordComposition	= True;
   r_comp.u.generic			= "F";
   req.smallSetRecordComposition	=
      req.mediumSetRecordComposition	= &r_comp;
   req.preferredRecordSyntax		= preferredRecordSyntax;
   LOG ( facApp, llevDebug, "Using RecordSyntax %s", req.preferredRecordSyntax );
   req.query				= &query;
   query.kind				= queryIso8777Query;
   query.q.iso8777Query			= str2os ( selector );

   LOG ( facApp, llevTrace, "Searching for %s", cclCmd );
   
   rsp_p = HC_Send_and_wait ( &req_p, True, (char **)NULL );
   free_struct ((char *)req.databaseNames, desc_DatabaseName);
   osfree ( query.q.iso8777Query );
   if ( !rsp_p )
      return;

   rsp = rsp_p->u.search_response;

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

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

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

      if ( records->recKind != recHlvlNamePlusRecord ) 
      {
	 LOG ( facApp, llevExceptions, "Diagnostic returned" );
	 fprintf ( stderr, "Diagnostic returned: %s\n",
		   HlvlDiagRec2Str ( records->u.nonSurrogateDiagnosticRecord, False ) );
      }
      else
	 showHlvlRecords ( rsp->records );

      currentStartPoint = 1;
   }

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

   HC_FreePacket ( rsp_p );
}


/* 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 )
{
   HC_Packet			req_p, *rsp_p;
   HC_PresentRequest		req;
   HC_PresentResponse		*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;
   }

   req_p.type			= hcPresentRequest;
   req_p.ref			= remoteRef;
   req_p.u.present_request	= &req;
   bzero ( &req, sizeof(req) );
   req.resultSetId		= currentResultSet;
   req.resultSetStartPoint	= startPoint;
   req.numberOfRecordsRequested	= noRecords;
   req.recordComposition	= new_RecordComposition ( True, "F" );
   req.preferredRecordSyntax	= preferredRecordSyntax;
   LOG(facApp, llevDebug, "Record syntax: %s", preferredRecordSyntax);

   rsp_p = HC_Send_and_wait ( &req_p, True, (char **)NULL );
   free_struct ( (char *)req.recordComposition, desc_RecordComposition );
   if ( !rsp_p )
      return;

   rsp = rsp_p->u.present_response;

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

   HC_FreePacket ( rsp_p );
}


/*
 * 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 = strchr ( cp1, ',' ), noCurrentDatabase = 0; 
           cp1; 
           cp1 = (cp2 ? cp2+1 : cp2), cp2 = ( cp1 ? strchr ( cp1, ',' ) : cp1 ) )
     {
       char                    c;

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

void doRecordSyntax ( const char *args )
{
   if ( !(args && *args) )
      printf ( "Current RecordSyntax is %s.\n", preferredRecordSyntax );
   else
   {
      printf ( "Previous RecordSyntax was %s.\n", preferredRecordSyntax );
      printf ( "New RecordSyntax is %s.\n", args );
      preferredRecordSyntax = strdup ( args );
   }
}


/*
 * DELETE RESULT SET
 *
*/

void doDeleteResultSet ( char *args )   
{
   HC_Packet			req_p, *rsp_p;
   HC_DeleteResultSetRequest	req;
   HC_DeleteResultSetResponse	*rsp;
   
   req_p.type			= hcDeleteResultSetRequest;
   req_p.ref			= remoteRef;
   req_p.u.delete_result_set_request = &req;
   
   if ( (!args) || (!*args) )
   {
      /* delete all result sets */
      req.all			= True;
      req.resultSetId		= NULL;
      puts ( "Deleting all result sets." );
   }
   else
   {
      req.all			= False;
      req.resultSetId		= args;
      printf ( "Deleting result set %s.", req.resultSetId );
   }

   if ( !(rsp_p = HC_Send_and_wait ( &req_p, True, (char **)NULL )) )
      return;

   rsp = rsp_p->u.delete_result_set_response;

   if ( !rsp->status )
      fputs ( "Result set deletion failed.\n", stderr );

   HC_FreePacket ( rsp_p );
}


/*
 * 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;
   char				*progname = argv[0];
   int				debug = 0;

   if ( (debug = (argv[1] &&
		  strncasecmp ( argv[1], "-debug", strlen ( argv[1] ) ) == 0 &&
		  strlen ( argv[1]) >= 2 )) )
      argv++, argv--;
   if ( argc > 1 && *argv[1] )
   {
      hclient_address = argv[1];
      if ( argc > 2 && *argv[2] )
	 hclient_port = atoi ( argv[2] );
      if ( argc > 3 || *hclient_address == '-' || hclient_port <= 0 )
      {
	 fprintf ( stderr, "Usage: %s [-debug] [host [port]]\n", progname );
	 return ! EX_OK;
      }
   }

   facLogLevel ( -1, llevAll );		/* Log everything */
   initLogger ( debug ? 0 : "./testclient.log" );

   if ( !HC_Initialise () )
   {
      fputs ( "Failed to connect to high-level client-server.\n", stderr );
      return ! EX_OK;
   }

   **currentDatabase	= '\0';
   currentResultSet[0]	= '\0';
   noCurrentDatabase	= 0;

   noConnections	= 10;
   connections		= (Connection *) calloc ( 10, sizeof (Connection) );
   
   preferredRecordSyntax = PREFERREDRECORDSYNTAX;
   LOG(facApp, llevDebug, "Default recordSyntax: %s", preferredRecordSyntax);

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

   currentResultSet[0] = 0;

#if defined(DEBUG) && !defined(VMS)
   malloc_debug ( 2 );
#endif
   
   while ( (cmd = get_cmd ( "SR> ", &args )) != cmdQUIT )
   {
      switch ( cmd )
      {
       case cmdHELP: case 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 ( "   SELECT [connection-number]\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" );
	 continue;

       case cmdOPEN:
	 openConnection ( args );
	 continue;

       case cmdSELECT:
	 doSelect ( args );
	 continue;

       case cmdFIND:
	 if ( !connections[remoteRef].connected )
	    break;
	 doSearch ( args );
	 continue;

       case cmdNEXT:
	 if ( !connections[remoteRef].connected )
	    break;
	 doNext ( args );
	 continue;

       case cmdRESULTSET:
	 if ( !connections[remoteRef].connected )
	    break;
	 doResultset ( args );
	 continue;

       case cmdSHOW:
	 if ( !connections[remoteRef].connected )
	    break;
	 doShow ( args );
	 continue;

       case cmdDATABASE:
	 doDatabase ( args );
	 continue;

       case cmdDELRESULTSET:
	 if ( !connections[remoteRef].connected )
	    break;
	 doDeleteResultSet ( args );
	 continue;

       case cmdSET:
	 doSet ( args );
	 continue;

       case cmdRECORDSYNTAX:
	 doRecordSyntax ( args );
	 continue;

       case cmdSTATUS:
	 doSelect ( NULL );
	 if ( connections[remoteRef].connected )
	 {
	    if ( noCurrentDatabase )
	    {
	       int                         i;
		 
	       printf ( "Current database " );
	       for ( i = 0; i < noCurrentDatabase; i++ )
		  printf ( "%s%s", i > 0 ? ", " : "", currentDatabase[i] );
	    }
	    if ( *currentResultSet )
	       printf ( "%s result set \"%s\"",
			(noCurrentDatabase ? ", current" : "Current"),
			currentResultSet );
	    printf ( ".\n" );
	 }
	 continue;

       case cmdCLOSE:
	 doClose ( args );
	 continue;
	    
       default:
	 fprintf ( stderr, "This command has not yet been implemented\n" );
	 continue;
      }

      fprintf ( stderr,
		"Not connected to any remote server - use OPEN to establish a connection\n" );
   }

#if 1
   HC_Close ();
#else
   for ( i = 0;  i < noConnections;  i++ )
      if ( connections[i].connected )
	 closeConnection ( i );
#endif

   return EX_OK;
}
