summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/lynx/WWW/Library/Implementation/HTVMS_WaisUI.c
diff options
context:
space:
mode:
authorMats O Jansson <maja@cvs.openbsd.org>1998-03-11 17:48:09 +0000
committerMats O Jansson <maja@cvs.openbsd.org>1998-03-11 17:48:09 +0000
commit69de3a9e357327c17caa3b7bb058035e263573bc (patch)
treef6260f228c4d949b174128d017a7e323c7122f32 /gnu/usr.bin/lynx/WWW/Library/Implementation/HTVMS_WaisUI.c
parentb81973f175db7d3f4c763069b191dd57f4bd83d3 (diff)
Lynx 2.8
Diffstat (limited to 'gnu/usr.bin/lynx/WWW/Library/Implementation/HTVMS_WaisUI.c')
-rw-r--r--gnu/usr.bin/lynx/WWW/Library/Implementation/HTVMS_WaisUI.c2448
1 files changed, 2448 insertions, 0 deletions
diff --git a/gnu/usr.bin/lynx/WWW/Library/Implementation/HTVMS_WaisUI.c b/gnu/usr.bin/lynx/WWW/Library/Implementation/HTVMS_WaisUI.c
new file mode 100644
index 00000000000..81c097070c9
--- /dev/null
+++ b/gnu/usr.bin/lynx/WWW/Library/Implementation/HTVMS_WaisUI.c
@@ -0,0 +1,2448 @@
+/* HTVMS_WAISUI.c
+**
+** Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+**
+** 30-May-1994 FM Initial version.
+**
+**----------------------------------------------------------------------*/
+
+/*
+** Routines originally from UI.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ No guarantees or restrictions. See the readme file for the full standard
+ disclaimer.
+
+ Brewster@think.com
+*/
+
+/*
+ * 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.
+ * do log_write()
+ *
+ */
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTVMS_WaisUI.h"
+#include "HTVMS_WaisProt.h"
+#include "HTTCP.h"
+/*#include <stdio> included by HTUtils.h -- FM */
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdarg.h>
+
+#include "LYexit.h"
+#include "LYLeaks.h"
+
+void
+log_write(s)
+char *s;
+{
+ return;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* 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 */
+ 0,
+ 1, /* DateFactor */
+ 0, /* BeginDateRange */
+ 0, /* EndDateRange */
+ maxDocsRetrieved
+ );
+
+ search3 = makeSearchAPDU(30,
+ 5000, /* should be large */
+ 30,
+ 1, /* replace indicator */
+ "", /* result set name */
+ database_names, /* database name */
+ QT_RelevanceFeedbackQuery, /* query_type */
+ 0, /* 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( 10, 16, 15,
+ 1, /* 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);
+ CSTFreeWAISTextQuery(query);
+ freeSearchAPDU(search);
+ return(end_ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* 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)
+int d; /* this is the stream */
+char *buf;
+long nbytes;
+{
+ long didRead;
+ long toRead = nbytes;
+ long totalRead = 0; /* paranoia */
+
+ while (toRead > 0){
+ didRead = NETREAD (d, buf, (int)toRead);
+ if(didRead == HT_INTERRUPTED)
+ return(HT_INTERRUPTED);
+ 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)
+int connection;
+char *request_message;
+long request_length;
+char *response_message;
+long response_buffer_length;
+{
+ WAISMessage header;
+ long response_length;
+ int rv;
+
+
+ /* Write out message. Read back header. Figure out response length. */
+
+ if( request_length + HEADER_LENGTH !=
+ NETWRITE(connection,request_message,
+ (int)( request_length +HEADER_LENGTH)) )
+ return 0;
+
+ /* read for the first '0' */
+
+ while(1){
+ rv = read_from_stream(connection, response_message, 1);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ return 0;
+ if('0' == response_message[0])
+ break;
+ }
+
+ rv = read_from_stream(connection, response_message + 1, HEADER_LENGTH -1);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ 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++){
+ rv = read_from_stream(connection,
+ response_message + HEADER_LENGTH,
+ 1);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ return 0;
+ }
+ return(0);
+ }
+ }
+ rv = read_from_stream(connection,
+ response_message + HEADER_LENGTH,
+ response_length);
+ if (rv == HT_INTERRUPTED)
+ return HT_INTERRUPTED;
+ if (rv < 0)
+ return 0;
+ return(response_length);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns the number of bytes writen. 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;
+int connection;
+boolean verbose;
+{
+ long response_length;
+
+ /* ?
+ if(verbose){
+ printf ("sending");
+ if(hostname_internal && strlen(hostname_internal) > 0)
+ printf(" to host %s", hostname_internal);
+ if(service_name && strlen(service_name) > 0)
+ printf(" for service %s", service_name);
+ printf("\n");
+ twais_dsply_rsp_apdu(request_message + HEADER_LENGTH,
+ request_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(connection != 0) {
+ response_length = transport_message(connection, request_message,
+ request_length,
+ response_message,
+ response_buffer_length);
+ if (response_length == HT_INTERRUPTED)
+ return(HT_INTERRUPTED);
+ }
+ else
+ return(0);
+
+ return(response_length);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* 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);
+}
+
+/*----------------------------------------------------------------------*/
+
+#if defined(VMS) && defined(__GNUC__) /* 10-AUG-1995 [pr] */
+/*
+ Workaround for an obscure bug in gcc's 2.6.[123] and 2.7.0 vax/vms port;
+ sometimes global variables will end up not being defined properly,
+ causing first gas to assume they're routines, then the linker to complain
+ about unresolved symbols, and finally the program to reference the wrong
+ objects (provoking ACCVIO). It's triggered by the specific ordering of
+ variable usage in the source code, hence rarely appears. This bug is
+ fixed in gcc 2.7.1, and was not present in 2.6.0 and earlier.
+
+ Make a reference to VAXCRTL's _ctype_[], and also one to this dummy
+ variable itself to prevent any "defined but not used" warning.
+ */
+static __const void *__const ctype_dummy[] = { &_ctype_, &ctype_dummy };
+#endif /* VMS && __GNUC__ */
+
+/* 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);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+** Routines originally from ZProt.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:`
+ No guarantees or restrictions. See the readme file for the full standard
+ disclaimer.
+
+ 3.26.90 Harry Morris, morris@think.com
+ 3.30.90 Harry Morris - Changed any->bits to any->bytes
+ 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
+*/
+
+#define RESERVE_SPACE_FOR_HEADER(spaceLeft) \
+ *spaceLeft -= HEADER_LEN;
+
+#define RELEASE_HEADER_SPACE(spaceLeft) \
+ if (*spaceLeft > 0) \
+ *spaceLeft += HEADER_LEN;
+
+/*----------------------------------------------------------------------*/
+
+InitResponseAPDU*
+makeInitResponseAPDU(result,
+ search,
+ present,
+ deleteIt,
+ accessControl,
+ resourceControl,
+ prefSize,
+ maxMsgSize,
+ auth,
+ id,
+ name,
+ version,
+ refID,
+ userInfo)
+boolean result;
+boolean search;
+boolean present;
+boolean deleteIt;
+boolean accessControl;
+boolean resourceControl;
+long prefSize;
+long maxMsgSize;
+char* auth;
+char* id;
+char* name;
+char* version;
+any* refID;
+void* userInfo;
+/* build an initResponse APDU with user specified information */
+{
+ InitResponseAPDU* init = (InitResponseAPDU*)s_malloc((size_t)sizeof(InitResponseAPDU));
+
+ init->PDUType = initResponseAPDU;
+ init->Result = result;
+ init->willSearch = search;
+ init->willPresent = present;
+ init->willDelete = deleteIt;
+ init->supportAccessControl = accessControl;
+ init->supportResourceControl = resourceControl;
+ init->PreferredMessageSize = prefSize;
+ init->MaximumRecordSize = maxMsgSize;
+ init->IDAuthentication = s_strdup(auth);
+ init->ImplementationID = s_strdup(id);
+ init->ImplementationName = s_strdup(name);
+ init->ImplementationVersion = s_strdup(version);
+ init->ReferenceID = duplicateAny(refID);
+ init->UserInformationField = userInfo; /* not copied! */
+
+ return(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeInitResponseAPDU(init)
+InitResponseAPDU* init;
+/* free an initAPDU */
+{
+ s_free(init->IDAuthentication);
+ s_free(init->ImplementationID);
+ s_free(init->ImplementationName);
+ s_free(init->ImplementationVersion);
+ freeAny(init->ReferenceID);
+ s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeInitResponseAPDU(init,buffer,len)
+InitResponseAPDU* init;
+char* buffer;
+long* len;
+/* write the initResponse to a buffer, adding system information */
+{
+ char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+ long size;
+ bit_map* optionsBM = NULL;
+
+ RESERVE_SPACE_FOR_HEADER(len);
+
+ buf = writePDUType(init->PDUType,buf,len);
+ buf = writeBoolean(init->Result,buf,len);
+ buf = writeProtocolVersion(buf,len);
+
+ optionsBM = makeBitMap((unsigned long)5,init->willSearch,init->willPresent,
+ init->willDelete,init->supportAccessControl,
+ init->supportResourceControl);
+ buf = writeBitMap(optionsBM,DT_Options,buf,len);
+ freeBitMap(optionsBM);
+
+ buf = writeNum(init->PreferredMessageSize,DT_PreferredMessageSize,buf,len);
+ buf = writeNum(init->MaximumRecordSize,DT_MaximumRecordSize,buf,len);
+ buf = writeString(init->IDAuthentication,DT_IDAuthentication,buf,len);
+ buf = writeString(init->ImplementationID,DT_ImplementationID,buf,len);
+ buf = writeString(init->ImplementationName,DT_ImplementationName,buf,len);
+ buf = writeString(init->ImplementationVersion,DT_ImplementationVersion,buf,len);
+ buf = writeAny(init->ReferenceID,DT_ReferenceID,buf,len);
+
+ /* go back and write the header-length-indicator */
+ RELEASE_HEADER_SPACE(len);
+ size = buf - buffer - HEADER_LEN;
+ writeBinaryInteger(size,HEADER_LEN,buffer,len);
+
+ if (init->UserInformationField != NULL)
+ buf = writeInitResponseInfo(init,buf,len);
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readInitResponseAPDU(init,buffer)
+InitResponseAPDU** init;
+char* buffer;
+{
+ char* buf = buffer;
+ boolean search,present,delete,accessControl,resourceControl;
+ long prefSize,maxMsgSize;
+ char *auth,*id,*name,*version;
+ long size;
+ pdu_type pduType;
+ bit_map* versionBM = NULL;
+ bit_map* optionsBM = NULL;
+ boolean result;
+ any *refID = NULL;
+ void* userInfo = NULL;
+
+ auth = id = name = version = NULL;
+ refID = NULL;
+
+ /* read required part */
+ buf = readBinaryInteger(&size,HEADER_LEN,buf);
+ buf = readPDUType(&pduType,buf);
+ buf = readBoolean(&result,buf);
+ buf = readBitMap(&versionBM,buf);
+ buf = readBitMap(&optionsBM,buf);
+ buf = readNum(&prefSize,buf);
+ buf = readNum(&maxMsgSize,buf);
+
+ /* decode optionsBM */
+ search = bitAtPos(0,optionsBM);
+ present = bitAtPos(1,optionsBM);
+ delete = bitAtPos(2,optionsBM);
+ accessControl = bitAtPos(3,optionsBM);
+ resourceControl = bitAtPos(4,optionsBM);
+
+ /* read optional part */
+ while (buf < (buffer + size + HEADER_LEN))
+ { data_tag tag = peekTag(buf);
+ switch (tag)
+ { case DT_IDAuthentication:
+ buf = readString(&auth,buf);
+ break;
+ case DT_ImplementationID:
+ buf = readString(&id,buf);
+ break;
+ case DT_ImplementationName:
+ buf = readString(&name,buf);
+ break;
+ case DT_ImplementationVersion:
+ buf = readString(&version,buf);
+ break;
+ case DT_ReferenceID:
+ buf = readAny(&refID,buf);
+ break;
+ default:
+ freeBitMap(versionBM);
+ freeBitMap(optionsBM);
+ s_free(auth);
+ s_free(id);
+ s_free(name);
+ s_free(version);
+ freeAny(refID);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ buf = readInitResponseInfo(&userInfo,buf);
+ if (buf == NULL)
+ { freeBitMap(versionBM);
+ freeBitMap(optionsBM);
+ s_free(auth);
+ s_free(id);
+ s_free(name);
+ s_free(version);
+ freeAny(refID);
+ }
+ RETURN_ON_NULL(buf);
+
+ /* construct the basic init object */
+ *init = makeInitResponseAPDU(result,
+ search,present,delete,accessControl,resourceControl,
+ prefSize,maxMsgSize,auth,id,name,version,refID,userInfo);
+
+ freeBitMap(versionBM);
+ freeBitMap(optionsBM);
+ s_free(auth);
+ s_free(id);
+ s_free(name);
+ s_free(version);
+ freeAny(refID);
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+InitResponseAPDU*
+replyToInitAPDU(init,result,userInfo)
+InitAPDU* init;
+boolean result;
+void* userInfo;
+/* respond to an init message in the default way - echoing back
+ the init info
+ */
+{
+ InitResponseAPDU* initResp;
+ initResp = makeInitResponseAPDU(result,
+ init->willSearch,init->willPresent,init->willDelete,
+ init->supportAccessControl,init->supportResourceControl,
+ init->PreferredMessageSize,init->MaximumRecordSize,
+ init->IDAuthentication,defaultImplementationID(),defaultImplementationName(),
+ defaultImplementationVersion(),
+ init->ReferenceID,userInfo);
+ return(initResp);
+}
+
+/*----------------------------------------------------------------------*/
+
+SearchAPDU*
+makeSearchAPDU(small,
+ large,
+ medium,
+ replace,
+ name,
+ databases,
+ type,
+ elements,
+ refID,
+ queryInfo)
+long small;
+long large;
+long medium;
+boolean replace;
+char* name;
+char** databases;
+char* type;
+char** elements;
+any* refID;
+void* queryInfo;
+{
+ char* ptr = NULL;
+ long i;
+ SearchAPDU* query = (SearchAPDU*)s_malloc((size_t)sizeof(SearchAPDU));
+ query->PDUType = searchAPDU;
+ query->SmallSetUpperBound = small;
+ query->LargeSetLowerBound = large;
+ query->MediumSetPresentNumber = medium;
+ query->ReplaceIndicator = replace;
+ query->ResultSetName = s_strdup(name);
+ query->DatabaseNames = NULL;
+ if (databases != NULL)
+ { for (i = 0, ptr = databases[i]; ptr != NULL; ptr = databases[++i])
+ { if (query->DatabaseNames == NULL)
+ query->DatabaseNames = (char**)s_malloc((size_t)(sizeof(char*) * 2));
+ else
+ query->DatabaseNames = (char**)s_realloc((char*)query->DatabaseNames,
+ (size_t)(sizeof(char*) * (i + 2)));
+ query->DatabaseNames[i] = s_strdup(ptr);
+ query->DatabaseNames[i+1] = NULL;
+ }
+ }
+ query->QueryType = s_strdup(type);
+ query->ElementSetNames = NULL;
+ if (elements != NULL)
+ { for (i = 0, ptr = elements[i]; ptr != NULL; ptr = elements[++i])
+ { if (query->ElementSetNames == NULL)
+ query->ElementSetNames = (char**)s_malloc((size_t)(sizeof(char*) * 2));
+ else
+ query->ElementSetNames = (char**)s_realloc((char*)query->ElementSetNames,
+ (size_t)(sizeof(char*) * (i + 2)));
+ query->ElementSetNames[i] = s_strdup(ptr);
+ query->ElementSetNames[i+1] = NULL;
+ }
+ }
+ query->ReferenceID = duplicateAny(refID);
+ query->Query = queryInfo; /* not copied! */
+ return(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeSearchAPDU(query)
+SearchAPDU* query;
+{
+ s_free(query->ResultSetName);
+ s_free(query->QueryType);
+ doList((void**)query->DatabaseNames,fs_free); /* can't use the macro here ! */
+ s_free(query->DatabaseNames);
+ doList((void**)query->ElementSetNames,fs_free); /* can't use the macro here ! */
+ s_free(query->ElementSetNames);
+ freeAny(query->ReferenceID);
+ s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define DB_DELIMITER "\037" /* hex 1F occurs between each database name */
+#define ES_DELIMITER_1 "\037" /* separates database name from element name */
+#define ES_DELIMITER_2 "\036" /* hex 1E separates <db,es> groups from one another */
+
+char*
+writeSearchAPDU(query,buffer,len)
+SearchAPDU* query;
+char* buffer;
+long* len;
+{
+ char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+ long size,i;
+ char* ptr = NULL;
+ char* scratch = NULL;
+
+ RESERVE_SPACE_FOR_HEADER(len);
+
+ buf = writePDUType(query->PDUType,buf,len);
+ buf = writeBinaryInteger(query->SmallSetUpperBound,(size_t)3,buf,len);
+ buf = writeBinaryInteger(query->LargeSetLowerBound,(size_t)3,buf,len);
+ buf = writeBinaryInteger(query->MediumSetPresentNumber,(size_t)3,buf,len);
+ buf = writeBoolean(query->ReplaceIndicator,buf,len);
+ buf = writeString(query->ResultSetName,DT_ResultSetName,buf,len);
+ /* write database names */
+ if (query->DatabaseNames != NULL)
+ { for (i = 0,scratch = NULL, ptr = query->DatabaseNames[i]; ptr != NULL; ptr = query->DatabaseNames[++i])
+ { if (scratch == NULL)
+ scratch = s_strdup(ptr);
+ else
+ { size_t newScratchSize = (size_t)(strlen(scratch) + strlen(ptr) + 2);
+ scratch = (char*)s_realloc(scratch,newScratchSize);
+ s_strncat(scratch,DB_DELIMITER,2,newScratchSize);
+ s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize);
+ }
+ }
+ buf = writeString(scratch,DT_DatabaseNames,buf,len);
+ s_free(scratch);
+ }
+ buf = writeString(query->QueryType,DT_QueryType,buf,len);
+ /* write element set names */
+ if (query->ElementSetNames != NULL)
+ { for (i = 0,scratch = NULL, ptr = query->ElementSetNames[i]; ptr != NULL; ptr = query->ElementSetNames[++i])
+ { if (scratch == NULL)
+ { if (query->ElementSetNames[i+1] == NULL) /* there is a single element set name */
+ { scratch = (char*)s_malloc((size_t)strlen(ptr) + 2);
+ strncpy(scratch,ES_DELIMITER_1,2);
+ s_strncat(scratch,ptr,strlen(ptr) + 1,strlen(ptr) + 2);
+ }
+ else /* this is the first of a series of element set names */
+ { size_t newScratchSize = (size_t)(strlen(ptr) + strlen(query->ElementSetNames[i + 1]) + 2);
+ scratch = s_strdup(ptr); /* the database name */
+ ptr = query->ElementSetNames[++i]; /* the element set name */
+ scratch = (char*)s_realloc(scratch,newScratchSize);
+ s_strncat(scratch,ES_DELIMITER_1,2,newScratchSize);
+ s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize);
+ }
+ }
+ else
+ { char* esPtr = query->ElementSetNames[++i]; /* the element set name */
+ size_t newScratchSize = (size_t)(strlen(scratch) + strlen(ptr) + strlen(esPtr) + 3);
+ scratch = (char*)s_realloc(scratch,newScratchSize);
+ s_strncat(scratch,ES_DELIMITER_2,2,newScratchSize);
+ s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize);
+ s_strncat(scratch,ES_DELIMITER_1,2,newScratchSize);
+ s_strncat(scratch,esPtr,strlen(esPtr) + 1,newScratchSize);
+ }
+ }
+ buf = writeString(scratch,DT_ElementSetNames,buf,len);
+ s_free(scratch);
+ }
+ buf = writeAny(query->ReferenceID,DT_ReferenceID,buf,len);
+
+ /* go back and write the header-length-indicator */
+ RELEASE_HEADER_SPACE(len);
+ size = buf - buffer - HEADER_LEN;
+ writeBinaryInteger(size,HEADER_LEN,buffer,len);
+
+ if (query->Query != NULL)
+ buf = writeSearchInfo(query,buf,len);
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+SearchResponseAPDU*
+makeSearchResponseAPDU(result,count,recordsReturned,nextPos,resultStatus,
+ presentStatus,refID,records)
+long result;
+long count;
+long recordsReturned;
+long nextPos;
+long resultStatus;
+long presentStatus;
+any* refID;
+void* records;
+{
+ SearchResponseAPDU* query = (SearchResponseAPDU*)s_malloc((size_t)sizeof(SearchResponseAPDU));
+ query->PDUType = searchResponseAPDU;
+ query->SearchStatus = result;
+ query->ResultCount = count;
+ query->NumberOfRecordsReturned = recordsReturned;
+ query->NextResultSetPosition = nextPos;
+ query->ResultSetStatus = resultStatus;
+ query->PresentStatus = presentStatus;
+ query->ReferenceID = duplicateAny(refID);
+ query->DatabaseDiagnosticRecords = records;
+ return(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeSearchResponseAPDU(queryResponse)
+SearchResponseAPDU* queryResponse;
+{
+ freeAny(queryResponse->ReferenceID);
+ s_free(queryResponse);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeSearchResponseAPDU(queryResponse,buffer,len)
+SearchResponseAPDU* queryResponse;
+char* buffer;
+long* len;
+{
+ char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+ long size;
+
+ RESERVE_SPACE_FOR_HEADER(len);
+
+ buf = writePDUType(queryResponse->PDUType,buf,len);
+ buf = writeBinaryInteger(queryResponse->SearchStatus,(size_t)1,buf,len);
+ buf = writeBinaryInteger(queryResponse->ResultCount,(size_t)3,buf,len);
+ buf = writeBinaryInteger(queryResponse->NumberOfRecordsReturned,(size_t)3,buf,len);
+ buf = writeBinaryInteger(queryResponse->NextResultSetPosition,(size_t)3,buf,len);
+ buf = writeNum(queryResponse->ResultSetStatus,DT_ResultSetStatus,buf,len);
+ buf = writeNum(queryResponse->PresentStatus,DT_PresentStatus,buf,len);
+ buf = writeAny(queryResponse->ReferenceID,DT_ReferenceID,buf,len);
+
+ /* go back and write the header-length-indicator */
+ RELEASE_HEADER_SPACE(len);
+ size = buf - buffer - HEADER_LEN;
+ writeBinaryInteger(size,HEADER_LEN,buffer,len);
+
+ if (queryResponse->DatabaseDiagnosticRecords != NULL)
+ buf = writeSearchResponseInfo(queryResponse,buf,len);
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readSearchResponseAPDU(queryResponse,buffer)
+SearchResponseAPDU** queryResponse;
+char* buffer;
+{
+ char* buf = buffer;
+ long size;
+ pdu_type pduType;
+ long result,count,recordsReturned,nextPos;
+ long resultStatus,presentStatus;
+ any *refID = NULL;
+ void* userInfo = NULL;
+
+ /* read required part */
+ buf = readBinaryInteger(&size,HEADER_LEN,buf);
+ buf = readPDUType(&pduType,buf);
+ buf = readBinaryInteger(&result,(size_t)1,buf);
+ buf = readBinaryInteger(&count,(size_t)3,buf);
+ buf = readBinaryInteger(&recordsReturned,(size_t)3,buf);
+ buf = readBinaryInteger(&nextPos,(size_t)3,buf);
+
+ resultStatus = presentStatus = UNUSED;
+ refID = NULL;
+
+ /* read optional part */
+ while (buf < (buffer + size + HEADER_LEN))
+ { data_tag tag = peekTag(buf);
+ switch (tag)
+ { case DT_ResultSetStatus:
+ buf = readNum(&resultStatus,buf);
+ break;
+ case DT_PresentStatus:
+ buf = readNum(&presentStatus,buf);
+ break;
+ case DT_ReferenceID:
+ buf = readAny(&refID,buf);
+ break;
+ default:
+ freeAny(refID);
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+ }
+
+ buf = readSearchResponseInfo(&userInfo,buf);
+ if (buf == NULL)
+ freeAny(refID);
+ RETURN_ON_NULL(buf);
+
+ /* construct the search object */
+ *queryResponse = makeSearchResponseAPDU(result,count,recordsReturned,nextPos,
+ (long)resultStatus,(long)presentStatus,refID,userInfo);
+
+ freeAny(refID);
+
+ return(buf);
+}
+
+
+/*
+** Routines originally from ZUtil.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ No guarantees or restrictions. See the readme file for the full standard
+ disclaimer.
+
+ 3.26.90 Harry Morris, morris@think.com
+ 3.30.90 Harry Morris - Changed any->bits to any->bytes
+ 4.11.90 HWM - fixed include file names, changed
+ - writeCompressedIntegerWithPadding() to
+ writeCompressedIntWithPadding()
+ - generalized conditional includes (see c-dialect.h)
+ 3.7.91 Jonny Goldman. Replaced "short" in makeBitMap with "int" line 632.
+*/
+
+char* readErrorPosition = NULL; /* pos where buf stoped making sense */
+
+/*----------------------------------------------------------------------*/
+/* A note on error handling
+ read - these are low level routines, they do not check the type tags
+ which (sometimes) preceed the data (this is done by the higher
+ level functions which call these functions). There is no
+ attempt made to check that the reading does not exceed the read
+ buffer. Such cases should be very rare and usually will be
+ caught by the calling functions. (note - it is unlikely that
+ a series of low level reads will go far off the edge without
+ triggering a type error. However, it is possible for a single
+ bad read in an array function (eg. readAny) to attempt to read a
+ large ammount, possibly causing a segmentation violation or out
+ of memory condition.
+ */
+/*----------------------------------------------------------------------*/
+
+diagnosticRecord*
+makeDiag(surrogate,code,addInfo)
+boolean surrogate;
+char* code;
+char* addInfo;
+{
+ diagnosticRecord* diag =
+ (diagnosticRecord*)s_malloc((size_t)sizeof(diagnosticRecord));
+
+ diag->SURROGATE = surrogate;
+ memcpy(diag->DIAG,code,DIAGNOSTIC_CODE_SIZE);
+ diag->ADDINFO = s_strdup(addInfo);
+
+ return(diag);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeDiag(diag)
+diagnosticRecord* diag;
+{
+ if (diag != NULL)
+ { if (diag->ADDINFO != NULL)
+ s_free(diag->ADDINFO);
+ s_free(diag);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+#define END_OF_RECORD 0x1D
+
+char*
+writeDiag(diag,buffer,len)
+diagnosticRecord* diag;
+char* buffer;
+long* len;
+/* diagnostics (as per Appendix D) have a very weird format - this changes
+ in SR-1
+ */
+{
+ char* buf = buffer;
+ long length;
+
+ if (diag == NULL) /* handle unspecified optional args */
+ return(buf);
+
+ buf = writeTag(DT_DatabaseDiagnosticRecords,buf,len);
+ CHECK_FOR_SPACE_LEFT(0,len);
+
+ length = 3;
+ if (diag->ADDINFO != NULL)
+ length += strlen(diag->ADDINFO);
+
+ if (length >= 0xFFFF ) /* make sure the length is reasonable */
+ { length = 0xFFFF - 1;
+ diag->ADDINFO[0xFFFF - 3 - 1] = '\0';
+ }
+
+ buf = writeBinaryInteger(length,2,buf,len);
+
+ CHECK_FOR_SPACE_LEFT(1,len);
+ buf[0] = diag->DIAG[0];
+ buf++;
+
+ CHECK_FOR_SPACE_LEFT(1,len);
+ buf[0] = diag->DIAG[1];
+ buf++;
+
+ if (length > 3)
+ { CHECK_FOR_SPACE_LEFT(3,len);
+ memcpy(buf,diag->ADDINFO,(size_t)length - 3);
+ buf += length - 3;
+ }
+
+ CHECK_FOR_SPACE_LEFT(1,len);
+ buf[0] = diag->SURROGATE;
+ buf++;
+
+ CHECK_FOR_SPACE_LEFT(1,len);
+ buf[0] = END_OF_RECORD;
+ buf++;
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readDiag(diag,buffer)
+diagnosticRecord** diag;
+char* buffer;
+{
+ char* buf = buffer;
+ diagnosticRecord* d
+ = (diagnosticRecord*)s_malloc((size_t)sizeof(diagnosticRecord));
+ data_tag tag;
+ long len;
+
+ buf = readTag(&tag,buf);
+
+ buf = readBinaryInteger(&len,2,buf);
+
+ d->DIAG[0] = buf[0];
+ d->DIAG[1] = buf[1];
+ d->DIAG[2] = '\0';
+
+ if (len > 3)
+ { d->ADDINFO = (char*)s_malloc((size_t)(len - 3 + 1));
+ memcpy(d->ADDINFO,(char*)(buf + 2),(size_t)(len - 3));
+ d->ADDINFO[len - 3] = '\0';
+ }
+ else
+ d->ADDINFO = NULL;
+
+ d->SURROGATE = buf[len - 1];
+
+ *diag = d;
+
+ return(buf + len + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define continueBit 0x80
+#define dataMask 0x7F
+#define dataBits 7
+
+char*
+writeCompressedInteger(num,buf,len)
+unsigned long num;
+char* buf;
+long* len;
+/* write a binary integer in the format described on p. 40.
+ this might be sped up
+*/
+{
+ char byte;
+ long i;
+ unsigned long size;
+
+ size = writtenCompressedIntSize(num);
+ CHECK_FOR_SPACE_LEFT(size,len);
+
+ for (i = size - 1; i >= 0; i--)
+ { byte = num & dataMask;
+ if (i != (size-1)) /* turn on continue bit */
+ byte = (char)(byte | continueBit);
+ buf[i] = byte;
+ num = num >> dataBits; /* don't and here */
+ }
+
+ return(buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readCompressedInteger(num,buf)
+unsigned long *num;
+char* buf;
+/* read a binary integer in the format described on p. 40.
+ this might be sped up
+*/
+{
+ long i = 0;
+ unsigned char byte;
+
+ *num = 0;
+
+ do
+ { byte = buf[i++];
+ *num = *num << dataBits;
+ *num += (byte & dataMask);
+ }
+ while (byte & continueBit);
+
+ return(buf + i);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define pad 128 /* high bit is set */
+
+char*
+writeCompressedIntWithPadding(num,size,buffer,len)
+unsigned long num;
+unsigned long size;
+char* buffer;
+long* len;
+/* Like writeCompressedInteger, except writes padding (128) to make
+ sure that size bytes are used. This can be read correctly by
+ readCompressedInteger()
+*/
+{
+ char* buf = buffer;
+ unsigned long needed,padding;
+ long i;
+
+ CHECK_FOR_SPACE_LEFT(size,len);
+
+ needed = writtenCompressedIntSize(num);
+ padding = size - needed;
+ i = padding - 1;
+
+ for (i = padding - 1;i >= 0;i--)
+ { buf[i] = pad;
+ }
+
+ buf = writeCompressedInteger(num,buf + padding,len);
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long
+writtenCompressedIntSize(num)
+unsigned long num;
+/* return the number of bytes needed to represnet the value num in
+ compressed format. curently limited to 4 bytes
+ */
+{
+ if (num < CompressedInt1Byte)
+ return(1);
+ else if (num < CompressedInt2Byte)
+ return(2);
+ else if (num < CompressedInt3Byte)
+ return(3);
+ else
+ return(4);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeTag(tag,buf,len)
+data_tag tag;
+char* buf;
+long* len;
+/* write out a data tag */
+{
+ return(writeCompressedInteger(tag,buf,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readTag(tag,buf)
+data_tag* tag;
+char* buf;
+/* read a data tag */
+{
+ return(readCompressedInteger(tag,buf));
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long
+writtenTagSize(tag)
+data_tag tag;
+{
+ return(writtenCompressedIntSize(tag));
+}
+
+/*----------------------------------------------------------------------*/
+
+data_tag
+peekTag(buf)
+char* buf;
+/* read a data tag without advancing the buffer */
+{
+ data_tag tag;
+ readTag(&tag,buf);
+ return(tag);
+}
+
+/*----------------------------------------------------------------------*/
+
+any*
+makeAny(size,data)
+unsigned long size;
+char* data;
+{
+ any* a = (any*)s_malloc((size_t)sizeof(any));
+ a->size = size;
+ a->bytes = data;
+ return(a);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeAny(a)
+any* a;
+/* destroy an any and its associated data. Assumes a->bytes was
+ allocated using the s_malloc family of libraries
+ */
+{
+ if (a != NULL)
+ { if (a->bytes != NULL)
+ s_free(a->bytes);
+ s_free(a);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+any*
+duplicateAny(a)
+any* a;
+{
+ any* copy = NULL;
+
+ if (a == NULL)
+ return(NULL);
+
+ copy = (any*)s_malloc((size_t)sizeof(any));
+ copy->size = a->size;
+ if (a->bytes == NULL)
+ copy->bytes = NULL;
+ else
+ { copy->bytes = (char*)s_malloc((size_t)copy->size);
+ memcpy(copy->bytes,a->bytes,(size_t)copy->size);
+ }
+ return(copy);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeAny(a,tag,buffer,len)
+any* a;
+data_tag tag;
+char* buffer;
+long* len;
+/* write an any + tag and size info */
+{
+ char* buf = buffer;
+
+ if (a == NULL) /* handle unspecified optional args */
+ return(buf);
+
+ /* write the tags */
+ buf = writeTag(tag,buf,len);
+ buf = writeCompressedInteger(a->size,buf,len);
+
+ /* write the bytes */
+ CHECK_FOR_SPACE_LEFT(a->size,len);
+ memcpy(buf,a->bytes,(size_t)a->size);
+
+ return(buf+a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+char *readAny(anAny,buffer)
+any** anAny;
+char* buffer;
+/* read an any + tag and size info */
+{
+ char *buf;
+ any* a;
+ data_tag tag;
+
+
+
+a=(any*)s_malloc((size_t)sizeof(any));
+
+ buf=buffer;
+
+ buf = readTag(&tag,buf);
+
+ buf = readCompressedInteger(&a->size,buf);
+
+ /* now simply copy the bytes */
+ a->bytes = (char*)s_malloc((size_t)a->size);
+ memcpy(a->bytes,buf,(size_t)a->size);
+ *anAny = a;
+
+ return(buf+a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long
+writtenAnySize(tag,a)
+data_tag tag;
+any* a;
+{
+ unsigned long size;
+
+ if (a == NULL)
+ return(0);
+
+ size = writtenTagSize(tag);
+ size += writtenCompressedIntSize(a->size);
+ size += a->size;
+ return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+any*
+stringToAny(s)
+char* s;
+{
+ any* a = NULL;
+
+ if (s == NULL)
+ return(NULL);
+
+ a = (any*)s_malloc((size_t)sizeof(any));
+ a->size = strlen(s);
+ a->bytes = (char*)s_malloc((size_t)a->size);
+ memcpy(a->bytes,s,(size_t)a->size);
+ return(a);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+anyToString(a)
+any* a;
+{
+ char* s = NULL;
+
+ if (a == NULL)
+ return(NULL);
+
+ s = s_malloc((size_t)(a->size + 1));
+ memcpy(s,a->bytes,(size_t)a->size);
+ s[a->size] = '\0';
+ return(s);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeString(s,tag,buffer,len)
+char* s;
+data_tag tag;
+char* buffer;
+long* len;
+/* Write a C style string. The terminating null is not written.
+ This function is not part of the Z39.50 spec. It is provided
+ for the convienience of those wishing to pass C strings in
+ the place of an any.
+ */
+{
+ char* buf = buffer;
+ any* data = NULL;
+ if (s == NULL)
+ return(buffer); /* handle unused optional item before making an any */
+ data = (any*)s_malloc((size_t)sizeof(any));
+ data->size = strlen(s);
+ data->bytes = s; /* save a copy here by not using stringToAny() */
+ buf = writeAny(data,tag,buf,len);
+ s_free(data); /* don't use freeAny() since it will free s too */
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readString(s ,buffer)
+char** s ;
+char* buffer;
+/* Read an any and convert it into a C style string.
+ This function is not part of the Z39.50 spec. It is provided
+ for the convienience of those wishing to pass C strings in
+ the place of an any.
+ */
+{
+ any* data = NULL;
+ char* buf = readAny(&data,buffer);
+ *s = anyToString(data);
+ freeAny(data);
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long
+writtenStringSize(tag,s)
+data_tag tag;
+char* s;
+{
+ unsigned long size;
+
+ if (s == NULL)
+ return(0);
+
+ size = writtenTagSize(tag);
+ size += writtenCompressedIntSize(size);
+ size += strlen(s);
+ return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+any*
+longToAny(num)
+long num;
+/* a convienience function */
+{
+ char s[40];
+
+ sprintf(s,"%ld",num);
+
+ return(stringToAny(s));
+}
+
+/*----------------------------------------------------------------------*/
+
+long
+anyToLong(a)
+any* a;
+/* a convienience function */
+{
+ long num;
+ char* str = NULL;
+ str = anyToString(a);
+ sscanf(str,"%ld",&num); /* could check the result and return
+ an error */
+ s_free(str);
+ return(num);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define bitsPerByte 8
+
+bit_map*
+makeBitMap(unsigned long numBits, ...)
+/* construct and return a bitmap with numBits elements */
+{
+ va_list ap;
+ long i,j;
+ bit_map* bm = NULL;
+
+ va_start(ap,numBits);
+
+ bm = (bit_map*)s_malloc((size_t)sizeof(bit_map));
+ bm->size = (unsigned long)ceil((double)numBits / bitsPerByte);
+ bm->bytes = (char*)s_malloc((size_t)bm->size);
+
+ /* fill up the bits */
+ for (i = 0; i < bm->size; i++) /* iterate over bytes */
+ { char byte = 0;
+ for (j = 0; j < bitsPerByte; j++) /* iterate over bits */
+ { if ((i * bitsPerByte + j) < numBits)
+ { boolean bit = false;
+ bit = (boolean)va_arg(ap,boolean);
+ if (bit)
+ { byte = byte | (1 << (bitsPerByte - j - 1));
+ }
+ }
+ }
+ bm->bytes[i] = byte;
+ }
+
+ va_end(ap);
+ return(bm);
+}
+
+
+/*----------------------------------------------------------------------*/
+
+void
+freeBitMap(bm)
+bit_map* bm;
+/* destroy a bit map created by makeBitMap() */
+{
+ s_free(bm->bytes);
+ s_free(bm);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* use this routine to interpret a bit map. pos specifies the bit
+ number. bit 0 is the Leftmost bit of the first byte.
+ Could do bounds checking.
+ */
+
+boolean
+bitAtPos(pos,bm)
+long pos;
+bit_map* bm;
+{
+ if (pos > bm->size*bitsPerByte)
+ return false;
+ else
+ return((bm->bytes[(pos / bitsPerByte)] &
+ (0x80>>(pos % bitsPerByte))) ?
+ true : false);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeBitMap(bm,tag,buffer,len)
+bit_map* bm;
+data_tag tag;
+char* buffer;
+long* len;
+/* write a bitmap + type and size info */
+{
+ return(writeAny((any*)bm,tag,buffer,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readBitMap(bm,buffer)
+bit_map** bm;
+char* buffer;
+/* read a bitmap + type and size info */
+{
+ char *c;
+
+
+
+c=readAny((any**)bm,buffer);
+
+ return(c);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeByte(byte,buf,len)
+unsigned long byte;
+char* buf;
+long* len;
+{
+ CHECK_FOR_SPACE_LEFT(1,len);
+ buf[0] = byte & 0xFF; /* we really only want the first byte */
+ return(buf + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readByte(byte,buf)
+unsigned char* byte;
+char* buf;
+{
+ *byte = buf[0];
+ return(buf + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeBoolean(flag,buf,len)
+boolean flag;
+char* buf;
+long* len;
+{
+ return(writeByte(flag,buf,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readBoolean(flag,buffer)
+boolean* flag;
+char* buffer;
+{
+ unsigned char byte;
+ char* buf = readByte(&byte,buffer);
+ *flag = (byte == true) ? true : false;
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writePDUType(pduType,buf,len)
+pdu_type pduType;
+char* buf;
+long* len;
+/* PDUType is a single byte */
+{
+ return(writeBinaryInteger((long)pduType,(unsigned long)1,buf,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readPDUType(pduType,buf)
+pdu_type* pduType;
+char* buf;
+/* PDUType is a single byte */
+{
+ return(readBinaryInteger((long*)pduType,(unsigned long)1,buf));
+}
+
+/*----------------------------------------------------------------------*/
+
+pdu_type
+peekPDUType(buf)
+char* buf;
+/* read the next pdu without advancing the buffer, Note that this
+ function is to be used on a buffer that is known to contain an
+ APDU. The pdu_type is written HEADER_LEN bytes into the buffer
+ */
+{
+ pdu_type pdu;
+ readPDUType(&pdu,buf + HEADER_LEN);
+ return(pdu);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define BINARY_INTEGER_BYTES sizeof(long) /* the number of bytes used by
+ a "binary integer" */
+char*
+writeBinaryInteger(num,size,buf,len)
+long num;
+unsigned long size;
+char* buf;
+long* len;
+/* write out first size bytes of num - no type info
+ XXX should this take unsigned longs instead ??? */
+{
+ long i;
+ char byte;
+
+ if (size < 1 || size > BINARY_INTEGER_BYTES)
+ return(NULL); /* error */
+
+ CHECK_FOR_SPACE_LEFT(size,len);
+
+ for (i = size - 1; i >= 0; i--)
+ { byte = (char)(num & 255);
+ buf[i] = byte;
+ num = num >> bitsPerByte; /* don't and here */
+ }
+
+ return(buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readBinaryInteger(num,size,buf)
+long* num;
+unsigned long size;
+char* buf;
+/* read in first size bytes of num - no type info
+ XXX this should take unsigned longs instead !!! */
+{
+ long i;
+ unsigned char byte;
+
+ if (size < 1 || size > BINARY_INTEGER_BYTES)
+ return(buf); /* error */
+ *num = 0;
+
+ for (i = 0; i < size; i++)
+ { byte = buf[i];
+ *num = *num << bitsPerByte;
+ *num += byte;
+ }
+
+ return(buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long
+writtenCompressedBinIntSize(num)
+long num;
+/* return the number of bytes needed to represent the value num.
+ currently limited to max of 4 bytes
+ Only compresses for positive nums - negatives get whole 4 bytes
+ */
+{
+ if (num < 0L)
+ return(4);
+ else if (num < 256L) /* 2**8 */
+ return(1);
+ else if (num < 65536L) /* 2**16 */
+ return(2);
+ else if (num < 16777216L) /* 2**24 */
+ return(3);
+ else
+ return(4);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeNum(num,tag,buffer,len)
+long num;
+data_tag tag;
+char* buffer;
+long* len;
+/* write a binary integer + size and tag info */
+{
+ char* buf = buffer;
+ long size = writtenCompressedBinIntSize(num);
+
+ if (num == UNUSED)
+ return(buffer);
+
+ buf = writeTag(tag,buf,len);
+ buf = writeCompressedInteger(size,buf,len);
+ buf = writeBinaryInteger(num,(unsigned long)size,buf,len);
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readNum(num,buffer)
+long* num;
+char* buffer;
+/* read a binary integer + size and tag info */
+{
+ char* buf = buffer;
+ data_tag tag;
+ unsigned long size;
+ unsigned long val;
+
+ buf = readTag(&tag,buf);
+ buf = readCompressedInteger(&val,buf);
+ size = (unsigned long)val;
+ buf = readBinaryInteger(num,size,buf);
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long
+writtenNumSize(tag,num)
+data_tag tag;
+long num;
+{
+ long dataSize = writtenCompressedBinIntSize(num);
+ long size;
+
+ size = writtenTagSize(tag); /* space for the tag */
+ size += writtenCompressedIntSize(dataSize); /* space for the size */
+ size += dataSize; /* space for the data */
+
+ return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+typedef void (voidfunc)();
+
+void
+doList(list,func)
+void** list;
+voidfunc *func;
+/* call func on each element of the NULL terminated list of pointers */
+{
+ register long i;
+ register void* ptr = NULL;
+ if (list == NULL)
+ return;
+ for (i = 0,ptr = list[i]; ptr != NULL; ptr = list[++i])
+ (*func)(ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeProtocolVersion(buf,len)
+char* buf;
+long* len;
+/* write a bitmap describing the protocols available */
+{
+ static bit_map* version = NULL;
+
+ if (version == NULL)
+ { version = makeBitMap((unsigned long)1,true); /* version 1! */
+ }
+
+ return(writeBitMap(version,DT_ProtocolVersion,buf,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+defaultImplementationID()
+{
+ static char ImplementationID[] = "TMC";
+ return(ImplementationID);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+defaultImplementationName()
+{
+ static char ImplementationName[] = "Thinking Machines Corporation Z39.50";
+ return(ImplementationName);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+defaultImplementationVersion()
+{
+ static char ImplementationVersion[] = "2.0A";
+ return(ImplementationVersion);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+** Routines originally from ZType1.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ No guarantees or restrictions. See the readme file for the full standard
+ disclaimer.
+
+ 3.26.90 Harry Morris, morris@think.com
+ 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
+*/
+/*----------------------------------------------------------------------*/
+
+query_term*
+makeAttributeTerm(use,
+ relation,
+ position,
+ structure,
+ truncation,
+ completeness,
+ term)
+char* use;
+char* relation;
+char* position;
+char* structure;
+char* truncation;
+char* completeness;
+any* term;
+{
+ query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term));
+
+ qt->TermType = TT_Attribute;
+
+ /* copy in the attributes */
+ strncpy(qt->Use,use,ATTRIBUTE_SIZE);
+ strncpy(qt->Relation,relation,ATTRIBUTE_SIZE);
+ strncpy(qt->Position,position,ATTRIBUTE_SIZE);
+ strncpy(qt->Structure,structure,ATTRIBUTE_SIZE);
+ strncpy(qt->Truncation,truncation,ATTRIBUTE_SIZE);
+ strncpy(qt->Completeness,completeness,ATTRIBUTE_SIZE);
+
+ qt->Term = duplicateAny(term);
+
+ qt->ResultSetID = NULL;
+
+ return(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term*
+makeResultSetTerm(resultSet)
+any* resultSet;
+{
+ query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term));
+
+ qt->TermType = TT_ResultSetID;
+
+ qt->ResultSetID = duplicateAny(resultSet);
+
+ qt->Term = NULL;
+
+ return(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term*
+makeOperatorTerm(operatorCode)
+char* operatorCode;
+{
+ query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term));
+
+ qt->TermType = TT_Operator;
+
+ strncpy(qt->Operator,operatorCode,OPERATOR_SIZE);
+
+ qt->Term = NULL;
+ qt->ResultSetID = NULL;
+
+ return(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeTerm(qt)
+query_term* qt;
+{
+ switch (qt->TermType)
+ { case TT_Attribute:
+ freeAny(qt->Term);
+ break;
+ case TT_ResultSetID:
+ freeAny(qt->ResultSetID);
+ break;
+ case TT_Operator:
+ /* do nothing */
+ break;
+ default:
+ panic("Implementation error: Unknown term type %ld",
+ qt->TermType);
+ break;
+ }
+ s_free(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define ATTRIBUTE_LIST_SIZE ATTRIBUTE_SIZE * 6
+#define AT_DELIMITER " "
+
+char*
+writeQueryTerm(qt,buffer,len)
+query_term* qt;
+char* buffer;
+long* len;
+{
+ char* buf = buffer;
+ char attributes[ATTRIBUTE_LIST_SIZE];
+
+ switch (qt->TermType)
+ { case TT_Attribute:
+ strncpy(attributes,qt->Use,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,qt->Relation,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,qt->Position,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,qt->Structure,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,qt->Truncation,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+ s_strncat(attributes,qt->Completeness,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE);
+ buf = writeString(attributes,DT_AttributeList,buf,len);
+ buf = writeAny(qt->Term,DT_Term,buf,len);
+ break;
+ case TT_ResultSetID:
+ buf = writeAny(qt->ResultSetID,DT_ResultSetID,buf,len);
+ break;
+ case TT_Operator:
+ buf = writeString(qt->Operator,DT_Operator,buf,len);
+ break;
+ default:
+ panic("Implementation error: Unknown term type %ld",
+ qt->TermType);
+ break;
+ }
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readQueryTerm(qt,buffer)
+query_term** qt;
+char* buffer;
+{
+ char* buf = buffer;
+ char *attributeList = NULL;
+ char* operator = NULL;
+ any* term;
+ char* use = NULL;
+ char* relation = NULL;
+ char* position = NULL;
+ char* structure = NULL;
+ char* truncation = NULL;
+ char* completeness;
+ any* resultSetID = NULL;
+ data_tag tag;
+
+
+ tag = peekTag(buffer);
+
+ switch(tag)
+ { case DT_AttributeList:
+ buf = readString(&attributeList,buf);
+ buf = readAny(&term,buf);
+ use = strtok(attributeList,AT_DELIMITER);
+ relation = strtok(NULL,AT_DELIMITER);
+ position = strtok(NULL,AT_DELIMITER);
+ structure = strtok(NULL,AT_DELIMITER);
+ truncation = strtok(NULL,AT_DELIMITER);
+ completeness = strtok(NULL,AT_DELIMITER);
+ *qt = makeAttributeTerm(use,relation,position,structure,
+ truncation,completeness,term);
+ s_free(attributeList);
+ freeAny(term);
+ break;
+ case DT_ResultSetID:
+ buf = readAny(&resultSetID,buf);
+ *qt = makeResultSetTerm(resultSetID);
+ freeAny(resultSetID);
+ break;
+ case DT_Operator:
+ buf = readString(&operator,buf);
+ *qt = makeOperatorTerm(operator);
+ s_free(operator);
+ break;
+ default:
+ REPORT_READ_ERROR(buf);
+ break;
+ }
+
+ return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+static unsigned long getQueryTermSize _AP((query_term* qt));
+
+static unsigned long
+getQueryTermSize(qt)
+query_term* qt;
+/* figure out how many bytes it will take to write this query */
+{
+ unsigned long size;
+ static char attributes[] = "11 22 33 44 55 66"; /* we just need this to
+ calculate its written
+ size */
+
+ switch (qt->TermType)
+ { case TT_Attribute:
+ size = writtenStringSize(DT_AttributeList,attributes);
+ size += writtenAnySize(DT_Term,qt->Term);
+ break;
+ case TT_ResultSetID:
+ size = writtenAnySize(DT_ResultSetID,qt->ResultSetID);
+ break;
+ case TT_Operator:
+ size = writtenStringSize(DT_Operator,qt->Operator);
+ break;
+ default:
+ panic("Implementation error: Unknown term type %ld",
+ qt->TermType);
+ break;
+ }
+
+ return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* A query is simply a null terminated list of query terms. For
+ transmission, a query is written into an any which is sent as
+ the user information field. */
+
+any*
+writeQuery(terms)
+query_term** terms;
+{
+ any* info = NULL;
+ char* writePos = NULL;
+ char* data = NULL;
+ unsigned long size = 0;
+ long remaining = 0;
+ long i;
+ query_term* qt = NULL;
+
+ if (terms == NULL)
+ return(NULL);
+
+ /* calculate the size of write buffer */
+ for (i = 0,qt = terms[i]; qt != NULL; qt = terms[++i])
+ size += getQueryTermSize(qt);
+
+ data = (char*)s_malloc((size_t)size);
+
+ /* write the terms */
+ writePos = data;
+ remaining = size;
+ for (i = 0,qt = terms[i]; qt != NULL; qt = terms[++i])
+ writePos = writeQueryTerm(qt,writePos,&remaining);
+
+ info = makeAny(size,data);
+
+ return(info);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term**
+readQuery(info)
+any *info;
+{
+ char* readPos = info->bytes;
+ query_term** terms = NULL;
+ query_term* qt = NULL;
+ long numTerms = 0L;
+char tmp[100];
+
+sprintf(tmp,"readquery: bytes: %ld",info->size);
+log_write(tmp);
+
+ while (readPos < info->bytes + info->size)
+ { readPos = readQueryTerm(&qt,readPos);
+
+ if (terms == NULL)
+ { terms = (query_term**)s_malloc((size_t)(sizeof(query_term*)*2));
+ }
+ else
+ { terms =
+ (query_term**)s_realloc((char*)terms,
+ (size_t)(sizeof(query_term*)*(numTerms+2)));
+ }
+if(qt==NULL)
+ log_write("qt = null");
+ terms[numTerms++] = qt;
+ terms[numTerms] = NULL;
+ }
+
+ return(terms);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+** Routines originally from panic.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+ No guarantees or restrictions. See the readme file for the full standard
+ disclaimer.
+
+ Morris@think.com
+*/
+
+/* panic is an error system interface. On the Mac, it will pop
+ * up a little window to explain the problem.
+ * On a unix box, it will print out the error and call perror()
+ */
+
+/*----------------------------------------------------------------------*/
+
+static void exitAction _AP((long error));
+
+static void
+exitAction(error)
+long error;
+{
+ long i;
+ for (i = 0; i < 100000; i++)
+ ;
+ exit(0);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define PANIC_HEADER "Fatal Error: "
+
+void
+panic(char *format, ...)
+{
+ va_list ap; /* the variable arguments */
+
+ fprintf(stderr,PANIC_HEADER);
+ va_start(ap, format); /* init ap */
+ vfprintf(stderr,format,ap); /* print the contents */
+ va_end(ap); /* free ap */
+ fflush(stderr);
+
+ exitAction(0);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+** Routines originally from cutil.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* Wide AREA INFORMATION SERVER SOFTWARE
+ No guarantees or restrictions. See the readme file for the full standard
+ disclaimer.
+
+ 3.26.90 Harry Morris, morris@think.com
+ 4.11.90 HWM - generalized conditional includes (see c-dialect.h)
+*/
+
+#include <varargs.h>
+
+
+/*----------------------------------------------------------------------*/
+
+void
+fs_checkPtr(ptr)
+void* ptr;
+/* If the ptr is NULL, give an error */
+{
+ if (ptr == NULL)
+ panic("checkPtr found a NULL pointer");
+}
+
+/*----------------------------------------------------------------------*/
+
+void*
+fs_malloc(size)
+size_t size;
+/* does safety checks and optional accounting */
+{
+ register void* ptr = NULL;
+
+ ptr = (void*)calloc((size_t)size,(size_t)1);
+ s_checkPtr(ptr);
+
+ return(ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+void*
+fs_realloc(ptr,size)
+void* ptr;
+size_t size;
+/* does safety checks and optional accounting
+ note - we don't know how big ptr's memory is, so we can't ensure
+ that any new memory allocated is NULLed!
+ */
+{
+ register void* nptr = NULL;
+
+ if (ptr == NULL) /* this is really a malloc */
+ return(s_malloc(size));
+
+ nptr = (void*)realloc(ptr,size);
+ s_checkPtr(ptr);
+
+ return(nptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+fs_free(ptr)
+void* ptr;
+/* does safety checks and optional accounting */
+{
+ if (ptr != NULL) /* some non-ansi compilers/os's cant handle freeing null */
+ { /* if we knew the size of this block of memory, we could clear it - oh well */
+ free(ptr);
+ ptr = NULL;
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+s_strdup(s)
+char* s;
+
+/* return a copy of s. This is identical to the standard library routine
+ strdup(), except that it is safe. If s == NULL or malloc fails,
+ appropriate action is taken.
+ */
+{
+ unsigned long len;
+ char* copy = NULL;
+
+ if (s == NULL) /* saftey check to postpone stupid errors */
+ return(NULL);
+
+ len = strlen(s); /* length of string - terminator */
+ copy = (char*)s_malloc((size_t)(sizeof(char)*(len + 1)));
+ strncpy(copy,s,len + 1);
+ return(copy);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+fs_strncat(dst,src,maxToAdd,maxTotal)
+char* dst;
+ char* src;
+ size_t maxToAdd;
+ size_t maxTotal;
+
+/* like strncat, except the fourth argument limits the maximum total
+ length of the resulting string
+ */
+{
+ size_t dstSize = strlen(dst);
+ size_t srcSize = strlen(src);
+
+ if (dstSize + srcSize < maxTotal) /* use regular old strncat */
+ return(strncat(dst,src,maxToAdd));
+ else
+ { size_t truncateTo = maxTotal - dstSize - 1;
+ char saveChar = src[truncateTo];
+ char* result = NULL;
+ src[truncateTo] = '\0';
+ result = strncat(dst,src,maxToAdd);
+ src[truncateTo] = saveChar;
+ return(result);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+char char_downcase(long_ch)
+unsigned long long_ch;
+{
+ unsigned char ch = long_ch & 0xFF; /* just want one byte */
+ /* when ansi is the way of the world, this can be tolower */
+ return (((ch >= 'A') && (ch <= 'Z')) ? (ch + 'a' -'A') : ch);
+}
+
+char *string_downcase(word)
+char *word;
+{
+ long i = 0;
+ while(word[i] != '\0'){
+ word[i] = char_downcase((unsigned long)word[i]);
+ i++;
+ }
+ return(word);
+}
+
+/*----------------------------------------------------------------------*/
+