about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation/HTVMS_WaisUI.c
diff options
context:
space:
mode:
Diffstat (limited to 'WWW/Library/Implementation/HTVMS_WaisUI.c')
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisUI.c2280
1 files changed, 2280 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.c b/WWW/Library/Implementation/HTVMS_WaisUI.c
new file mode 100644
index 00000000..d8f73019
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisUI.c
@@ -0,0 +1,2280 @@
+/*
+ * $LynxId: HTVMS_WaisUI.c,v 1.17 2010/10/29 21:10:14 tom Exp $
+ *								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>
+
+#ifdef VMS
+#include <HTVMS_WaisUI.h>
+#include <HTVMS_WaisProt.h>
+#include <HTTCP.h>
+
+#undef MAXINT			/* we don't need it here, and www_tcp.h may conflict */
+#include <math.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+void log_write(char *s GCC_UNUSED)
+{
+    return;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns a pointer in the buffer of the first free byte.
+   if it overflows, then NULL is returned
+ */
+char *generate_search_apdu(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(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!
+ */
+static long read_from_stream(int d, 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 */
+
+static long transport_message(long 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 written.  0 if an error */
+long interpret_message(char *request_message,
+		       long request_length,	/* length of the buffer */
+		       char *response_message,
+		       long response_buffer_length,
+		       long connection,
+		       boolean verbose GCC_UNUSED)
+{
+    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. */
+static char *delete_seeker_codes(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(char *headline)
+{
+    long length = strlen(headline) + 1;		/* include the trailing null */
+    size_t 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(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(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(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(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(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(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(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(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(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(SearchResponseAPDU *queryResponse)
+{
+    freeAny(queryResponse->ReferenceID);
+    s_free(queryResponse);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeSearchResponseAPDU(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(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(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(diagnosticRecord * diag)
+{
+    if (diag != NULL) {
+	if (diag->ADDINFO != NULL)
+	    s_free(diag->ADDINFO);
+	s_free(diag);
+    }
+}
+
+/*----------------------------------------------------------------------*/
+
+#define END_OF_RECORD	0x1D
+
+char *writeDiag(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, 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(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), 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(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;
+    unsigned 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(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(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(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(data_tag tag, char *buf, long *len)
+/* write out a data tag */
+{
+    return (writeCompressedInteger(tag, buf, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readTag(data_tag *tag, char *buf)
+/* read a data tag */
+{
+    return (readCompressedInteger(tag, buf));
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenTagSize(data_tag tag)
+{
+    return (writtenCompressedIntSize(tag));
+}
+
+/*----------------------------------------------------------------------*/
+
+data_tag peekTag(char *buf)
+/* read a data tag without advancing the buffer */
+{
+    data_tag tag;
+
+    readTag(&tag, buf);
+    return (tag);
+}
+
+/*----------------------------------------------------------------------*/
+
+any *makeAny(unsigned long size, char *data)
+{
+    any *a = (any *) s_malloc((size_t) sizeof(any));
+
+    a->size = size;
+    a->bytes = data;
+    return (a);
+}
+
+/*----------------------------------------------------------------------*/
+
+void freeAny(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(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, copy->size);
+    }
+    return (copy);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeAny(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, a->size);
+
+    return (buf + a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readAny(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, a->size);
+    *anAny = a;
+
+    return (buf + a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long writtenAnySize(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(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, a->size);
+    return (a);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *anyToString(any *a)
+{
+    char *s = NULL;
+
+    if (a == NULL)
+	return (NULL);
+
+    s = s_malloc((size_t) (a->size + 1));
+    MemCpy(s, a->bytes, a->size);
+    s[a->size] = '\0';
+    return (s);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeString(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(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(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(long num)
+/* a convienience function */
+{
+    char s[40];
+
+    sprintf(s, "%ld", num);
+
+    return (stringToAny(s));
+}
+
+/*----------------------------------------------------------------------*/
+
+long anyToLong(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;
+    unsigned long i, j;
+    bit_map *bm = NULL;
+
+    LYva_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(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(unsigned 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(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(bit_map **bm, char *buffer)
+/* read a bitmap + type and size info */
+{
+    char *c;
+
+    c = readAny((any **) bm, buffer);
+    return (c);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeByte(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(unsigned char *byte, char *buf)
+{
+    *byte = buf[0];
+    return (buf + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writeBoolean(boolean flag, char *buf, long *len)
+{
+    return (writeByte(flag, buf, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readBoolean(boolean *flag, char *buffer)
+{
+    unsigned char byte;
+    char *buf = readByte(&byte, buffer);
+
+    *flag = (byte == true) ? true : false;
+    return (buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *writePDUType(pdu_type pduType, char *buf, long *len)
+/* PDUType is a single byte */
+{
+    return (writeBinaryInteger((long) pduType, (unsigned long) 1, buf, len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char *readPDUType(pdu_type *pduType, char *buf)
+/* PDUType is a single byte */
+{
+    return (readBinaryInteger((long *) pduType, (unsigned long) 1, buf));
+}
+
+/*----------------------------------------------------------------------*/
+
+pdu_type peekPDUType(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(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(long *num, unsigned long size, char *buf)
+/* read in first size bytes of num - no type info
+  XXX this should take unsigned longs instead !!! */
+{
+    unsigned 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(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(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(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(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 *);
+
+void doList(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(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(void)
+{
+    static char ImplementationID[] = "TMC";
+
+    return (ImplementationID);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *defaultImplementationName(void)
+{
+    static char ImplementationName[] = "Thinking Machines Corporation Z39.50";
+
+    return (ImplementationName);
+}
+
+/*----------------------------------------------------------------------*/
+
+char *defaultImplementationVersion(void)
+{
+    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(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(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(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(void *param)
+{
+    query_term *qt = (query_term *) param;
+
+    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(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(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(query_term *qt);
+
+static unsigned long getQueryTermSize(query_term *qt)
+/* figure out how many bytes it will take to write this query */
+{
+    unsigned long size = 0;
+    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(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(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(long error);
+
+static void exitAction(long error GCC_UNUSED)
+{
+    exit_immediately(EXIT_SUCCESS);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define PANIC_HEADER "Fatal Error:  "
+
+void panic(char *format,...)
+{
+    va_list ap;			/* the variable arguments */
+
+    fprintf(stderr, PANIC_HEADER);
+    LYva_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)
+ */
+
+/*----------------------------------------------------------------------*/
+
+void fs_checkPtr(void *ptr)
+/* If the ptr is NULL, give an error */
+{
+    if (ptr == NULL)
+	panic("checkPtr found a NULL pointer");
+}
+
+/*----------------------------------------------------------------------*/
+
+void *fs_malloc(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(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(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(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(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(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(char *word)
+{
+    long i = 0;
+
+    while (word[i] != '\0') {
+	word[i] = char_downcase((unsigned long) word[i]);
+	i++;
+    }
+    return (word);
+}
+
+/*----------------------------------------------------------------------*/
+#endif /* VMS */