ftp.nice.ch/pub/next/connectivity/infosystems/WAIStation.1.9.6.N.b.tar.gz#/WAIS/ir/ui.c

This is ui.c in view mode; [Download] [Up]

/* WIDE AREA INFORMATION SERVER SOFTWARE:
   No guarantees or restrictions.  See the readme file for the full standard
   disclaimer.

   Brewster@think.com
*/

#ifndef lint
static char *RCSid = "$Header: /tmp_mnt/net/quake/proj/wais/wais-8-b5/ir/RCS/ui.c,v 1.16 92/03/18 09:01:09 jonathan Exp $";
#endif

/* Change log:
 * $Log:	ui.c,v $
 * Revision 1.16  92/03/18  09:01:09  jonathan
 * don't free DocObjs[0]->Type, it's a copy of the input!
 * 
 * Revision 1.15  92/03/06  12:53:03  jonathan
 * Plug another memory leak.  From ericb@baker.dartmouth.edu (Eric J Bivona).
 * 
 * Revision 1.14  92/02/14  15:26:53  jonathan
 * Conditionalized interpret_message and locally_answer_message so client need
 * not include search engine.
 * 
 * Revision 1.13  92/02/12  13:53:41  jonathan
 * Added "$Log" so RCS will put the log message in the header
 * 
 * 
*/

/* 
 * this is a simple ui toolkit for building other ui's on top.
 * -brewster
 * 
 * top level functions:
 *   generate_search_apdu
 *   generate_retrieval_apdu
 *   interpret_message
 *
 */

/* to do:
 *   generate multiple queries for long documents.
 *     this will crash if the file being retrieved is larger than 100k.
 *   
 */

#include "ui.h"
#include "wutil.h"
#include "ustubs.h"
#include "futil.h"

#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

/* returns a pointer in the buffer of the first free byte.
   if it overflows, then NULL is returned 
 */
char *
generate_search_apdu(buff,
		     buff_len,
		     seed_words,
		     database_name,
		     docobjs,
		     maxDocsRetrieved)
char* buff;     /* buffer to hold the apdu */
long *buff_len;    /* length of the buffer changed to reflect new data written */
char *seed_words;    /* string of the seed words */
char *database_name;
DocObj** docobjs;
long maxDocsRetrieved;
{
  /* local variables */

  SearchAPDU *search3;
  char  *end_ptr;
  static char *database_names[2] = {"", 0};
  any refID;
  WAISSearch *query;
  refID.size = 1;
  refID.bytes = "3";

  database_names[0] = database_name;
  query = makeWAISSearch(seed_words,
                         docobjs, /* DocObjsPtr */
                         0L,
                         1L,     /* DateFactor */
                         0L,     /* BeginDateRange */
                         0L,     /* EndDateRange */
                         maxDocsRetrieved
                         );

  search3 = makeSearchAPDU(30L, 
			   5000L, /* should be large */
			   30L,
                           1L,	/* replace indicator */
                           "",	/* result set name */
                           database_names, /* database name */   
                           QT_RelevanceFeedbackQuery, /* query_type */
                           0L,   /* element name */
                           NULL, /* reference ID */
                           query);

  end_ptr = writeSearchAPDU(search3, buff, buff_len);

  CSTFreeWAISSearch(query);
  freeSearchAPDU(search3);
  return(end_ptr);
}


/* returns a pointer into the buffer of the next free byte.
   if it overflowed, then NULL is returned
 */

char *
 generate_retrieval_apdu(buff,
			buff_len,
			docID,
			chunk_type,
			start,
			end,
			type,
			database_name)
char *buff;
long *buff_len;    /* length of the buffer changed to reflect new data written */
any *docID;
long chunk_type;
long start;
long end;
char *type;
char *database_name;
{
  SearchAPDU *search;
  char  *end_ptr;

  static char *database_names[2];
  static char *element_names[3];
  any refID;

  DocObj *DocObjs[2];
  any *query;			/* changed from char* by brewster */

  if(NULL == type)
    type = s_strdup("TEXT");

  database_names[0] = database_name;
  database_names[1] = NULL;

  element_names[0] = " ";
  element_names[1] = ES_DocumentText;
  element_names[2] = NULL;

  refID.size = 1;
  refID.bytes = "3";
  
  switch(chunk_type){
  case CT_line: 
    DocObjs[0] = makeDocObjUsingLines(docID, type, start, end);
    break;
  case CT_byte:
    DocObjs[0] = makeDocObjUsingBytes(docID, type, start, end);
    break;
  }
  DocObjs[1] = NULL;

  query = makeWAISTextQuery(DocObjs);   
  search = makeSearchAPDU( 10L, 16L, 15L, 
			  1L,	/* replace indicator */
			  "FOO", /* result set name */
			  database_names, /* database name */   
			  QT_TextRetrievalQuery, /* query_type */
			  element_names, /* element name */
			  &refID, /* reference ID */
			  query);
  end_ptr = writeSearchAPDU(search, buff, buff_len);
  /* s_free(DocObjs[0]->Type); it's a copy of the input, don't free it! */
  CSTFreeDocObj(DocObjs[0]);
  CSTFreeWAISTextQuery(query);
  freeSearchAPDU(search);
  return(end_ptr);
}

/* not currently used 

static boolean isnumber _AP((char* string));

static boolean isnumber(string)
char *string;
{
  long count;
  for(count = 0; count < strlen(string); count++){
    if(!isdigit(string[count])){
      return(false);
    }
  }
  return(true);
}

*/

/* this will negotiate with server, and returs the maximum buffer size 
   the server can handle.

   A connection should be established first using open_socket.

*/

long init_connection(inBuffer, outBuffer, bufferSize, connection, userInfo)
char *inBuffer, *outBuffer;
long bufferSize;
FILE *connection;
char *userInfo;
{ 
  InitAPDU* init = NULL;
  InitResponseAPDU* reply = NULL;
  long result;
  /* construct an init */
  init = makeInitAPDU(true,false,false,false,false,bufferSize,bufferSize,
		      userInfo,defaultImplementationID(),
		      defaultImplementationName(),
		      defaultImplementationVersion(),NULL,userInfo);
  /* write it to the buffer */
  result = writeInitAPDU(init,inBuffer+HEADER_LENGTH,&bufferSize) - inBuffer;

  if(result < 0){
    freeInitAPDU(init);
    return(-1);
  }
  if(0 ==
     interpret_message(inBuffer,
		       result - HEADER_LENGTH,
		       outBuffer,
		       bufferSize,
		       connection,
		       false	/* true verbose */	
		       )) {
    /* error making a connection */
    return (-1);
  }
  if (readInitResponseAPDU(&reply,outBuffer + HEADER_LENGTH) == NULL){
    freeWAISInitResponse((WAISInitResponse*)reply->UserInformationField);
    freeInitResponseAPDU(reply);
    return(-1);
  }
  if (reply->Result == false)
    {				/* the server declined service */
      freeWAISInitResponse((WAISInitResponse*)reply->UserInformationField);
      freeInitResponseAPDU(reply);
      return(-1);
    }
  else				/* we got a response back */
    { result = reply->MaximumRecordSize;
      freeWAISInitResponse((WAISInitResponse*)reply->UserInformationField);
      freeInitResponseAPDU(reply);
      return(result);
    }
}

#ifdef LOCAL_SEARCH
/* returns the length of the response, 0 if an error */
long
locally_answer_message(request_message,
		       request_length,
		       response_message,
		       response_buffer_length)
char *request_message;
long request_length;
char *response_message;
long response_buffer_length;
{
  long request_length_internal = request_length;
  long response_length;
  WAISMessage header;
  long maxBufferSize = response_buffer_length;

  readWAISPacketHeader(request_message, &header);
  {
    char length_array[11];
    strncpy(length_array, header.msg_len, 10);
    length_array[10] = '\0';
    request_length_internal = atol(length_array);
  }
  /*
    printf("request length is %ld (%ld from caller)\n", 
    request_length_internal,
    request_length);
    */
  
  response_length =
    interpret_buffer(request_message + HEADER_LENGTH, 
		     request_length_internal,
		     response_message + HEADER_LENGTH,
		     response_buffer_length,
		     &maxBufferSize,
		     (long)header.hdr_vers,
		     NULL);
  if(0 > response_length)
    return(0);
  writeWAISPacketHeader(response_message,
			response_length,
			(long)'z',	/* Z39.50 */
			"DowQuest  ", /* server name */
			(long)NO_COMPRESSION,	/* no compression */
			(long)NO_ENCODING,(long)header.hdr_vers);
  return(response_length);
}
#endif

/* this is a safe version of unix 'read' it does all the checking
 * and looping necessary
 * to those trying to modify the transport code to use non-UNIX streams:
 *  This is the function to modify!
 */
long read_from_stream(d,buf,nbytes)
long d;				/* this is the stream */
char *buf;
long nbytes;
{
  long didRead;
  long toRead = nbytes;
  long totalRead = 0;		/* paranoia */

  while (toRead > 0){
    didRead = read (d, buf, toRead);
    if(didRead == -1)		/* error*/
      return(-1);
    if(didRead == 0)		/* eof */
      return(-2);		/* maybe this should return 0? */
    toRead -= didRead;
    buf += didRead;
    totalRead += didRead;
  }
  if(totalRead != nbytes)	/* we overread for some reason */
    return(- totalRead);	/* bad news */    
  return(totalRead);
}

/* returns the length of the response, 0 if an error */

long 
transport_message(connection,
		  request_message,
		  request_length,
		  response_message,
		  response_buffer_length)
FILE *connection;
char *request_message;
long request_length;
char *response_message;
long response_buffer_length;
{
  WAISMessage header;
  long response_length;

  
  /* Write out message. Read back header. Figure out response length. */
  
  if( request_length + HEADER_LENGTH
     != fwrite (request_message, 1L, request_length + HEADER_LENGTH, connection))
    return 0;

  fflush(connection);

  /* read for the first '0' */

  while(1){
    if(0 > read_from_stream(fileno(connection), response_message, 1))
      return 0;
    if('0' == response_message[0])
      break;
  }

  if(0 > read_from_stream(fileno(connection), 
				  response_message + 1, 
				  HEADER_LENGTH - 1))
    return 0;

  readWAISPacketHeader(response_message, &header);
  {
    char length_array[11];
    strncpy(length_array, header.msg_len, 10);
    length_array[10] = '\0';
    response_length = atol(length_array);
    /*
      if(verbose){
      printf("WAIS header: '%s' length_array: '%s'\n", 
      response_message, length_array);
      }
      */
    if(response_length > response_buffer_length){
      /* we got a message that is too long, therefore empty the message out,
	 and return 0 */
      long i;
      for(i = 0; i < response_length; i++){
	read_from_stream(fileno(connection), 
			 response_message + HEADER_LENGTH,
			 1);
/*	fread(response_message + HEADER_LENGTH, 1, 1, connection); */
      }
      return(0);
    }
  }
  if(0 > read_from_stream(fileno(connection), 
				  response_message + HEADER_LENGTH,
				  response_length))
/*  if(0 > fread(response_message + HEADER_LENGTH,
	       1, response_length, connection)) */
    return 0;
  return(response_length);
}


/* ------------------------------------------------------------*/

/*  Facility to record messages sent and recieved for testing 
    and timing purposes. */

/* from c:

   putenv(strdup("IR_FILE=/users/menlo-park/brewster/tmp/infile"));

   from csh:

   setenv IR_FILE /users/menlo-park/brewster/tmp/infile

 */

boolean environment_variables_read = false; 
char* ir_file_environment_variable = "IR_FILE";
char* ir_file = NULL;

void read_environment_variables(host, port)
     char* host;
     char* port;
{
  
  if(!environment_variables_read){
    FILE *stream;
    ir_file = (char*)getenv(ir_file_environment_variable);
    if(ir_file){
      printf("IR_file: %s\n", ir_file);
      stream = fopen(ir_file, "w");
      fprintf(stream, "%s %s\n", host, port);
      fclose(stream);
    }
    environment_variables_read = true;
  }
}

/* returns 0 if success */
long write_message_to_file(message, length, filename)
     unsigned char *message;
     long length;
     char*filename;
{
  FILE *stream = fopen(filename, "a");
  if(NULL == stream)
    return(-1);
  
  printf("Writing to file: %s %d characters\n", filename, length);

  fprintf(stream, "---------------------------------\n");
  
  if(length != fwrite(message, sizeof(unsigned char), 
		      length, stream)){
    perror("fwrite error");
    fclose(stream);
    return(-2);
  }

  fprintf(stream, "\n");

  if(0 != fclose(stream))
    return(-3);
  return(0);
}  

/* ------------------------------------------------------------*/

/* returns the number of bytes writeen.  0 if an error */
long
interpret_message(request_message,request_length,
		  response_message,
		  response_buffer_length,
		  connection,
		  verbose)
char *request_message;
long request_length; /* length of the buffer */
char *response_message;
long response_buffer_length;
FILE *connection;
boolean verbose;
{
  long response_length;

  writeWAISPacketHeader(request_message,
			request_length,
			(long)'z',	/* Z39.50 */
			"wais      ", /* server name */
			(long)NO_COMPRESSION,	/* no compression */
			(long)NO_ENCODING,(long)HEADER_VERSION);

  if(ir_file){
    long error_code;
    if(0 != (error_code = 
	     write_message_to_file((unsigned char*)request_message, 
				   request_length + HEADER_LENGTH, 
				   ir_file)))
      printf("Error writing log file Code: %d\n", error_code);
  }
    
  if(connection != NULL) {
    if(0 == 
       (response_length =
	transport_message(connection, request_message,
			  request_length,
			  response_message,
			  response_buffer_length)))
      return(0);
  }
  else{
#ifdef LOCAL_SEARCH    
    if(0 == 
       (response_length =
	locally_answer_message(request_message, request_length, 
			       response_message,
			       response_buffer_length)))
      return(0);
#else
    waislog(WLOG_HIGH, WLOG_ERROR, "Local search not supported in this version");
    return(0);
#endif
  }
  if(verbose){
    printf ("decoded %ld bytes: \n", response_length);
    twais_dsply_rsp_apdu(response_message + HEADER_LENGTH, 
			 request_length);
  }
  if(ir_file){
    long error_code;
    if(0 != (error_code = 
	     write_message_to_file((unsigned char*)response_message, 
				   response_length + HEADER_LENGTH, 
				   ir_file)))
      printf("Error writing log file Code: %d\n", error_code);
  }
  return(response_length);
}

/* this closes the connection to the socket.
 * the mythology is that this is cleaner than exiting
 */

long close_connection(connection)
FILE *connection;
{
  long result = 0;
  
  if(connection != NULL){
    result = fclose(connection);
  }
  return(result);
}
  
void
display_text_record_completely(record,quote_string_quotes)
WAISDocumentText *record;
boolean quote_string_quotes;
{
  long count;
  /* printf(" Text\n");
     print_any("     DocumentID:  ", record->DocumentID);
     printf("     VersionNumber:  %d\n", record->VersionNumber);
     */
  for(count = 0; count < record->DocumentText->size; count++){
    long ch = (unsigned char)record->DocumentText->bytes[count];
    if(27 == ch){
      /* then we have an escape code */
      /* if the next letter is '(' or ')', then ignore two letters */
      if('(' == record->DocumentText->bytes[count + 1] ||
	 ')' == record->DocumentText->bytes[count + 1])
	count += 1;             /* it is a term marker */
      else count += 4;		/* it is a paragraph marker */
    }
    else if (ch == '\t') /* a TAB! */
      putc(ch, stdout);
    else if (isprint(ch)){
      if(quote_string_quotes && ch == '"')
	putc('\\', stdout);
      putc(ch, stdout);
    } 
    else if (ch == '\n' || ch == '\r')
      printf ("\n");
  }
}

/* modifies the string to exclude all seeker codes. sets length to
   the new length. */
char *delete_seeker_codes(string,length)
char *string;
long *length;
{
  long original_count; /* index into the original string */
  long new_count = 0; /* index into the collapsed string */
  for(original_count = 0; original_count < *length; original_count++){
    if(27 == string[original_count]){
      /* then we have an escape code */
      /* if the next letter is '(' or ')', then ignore two letters */
      if('(' == string[original_count + 1] ||
    ')' == string[original_count + 1])
     original_count += 1;    /* it is a term marker */
      else original_count += 4; /* it is a paragraph marker */
    }
    else string[new_count++] = string[original_count];
  }
  *length = new_count;
  return(string);
}
  

/* returns a pointer to a string with good stuff */
char *trim_junk(headline)
char *headline;
{
  long length = strlen(headline) + 1; /* include the trailing null */
  long i;
  headline = delete_seeker_codes(headline, &length);
  /* delete leading spaces */
  for(i=0; i < strlen(headline); i++){
    if(isprint(headline[i])){
      break;
    }
  }
  headline = headline + i;
  /* delete trailing stuff */
  for(i=strlen(headline) - 1 ; i > 0; i--){
    if(isprint(headline[i])){
      break;
    }
    headline[i] = '\0';
  }
  return(headline);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.