summaryrefslogtreecommitdiffstats
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.c2279
1 files changed, 2279 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.c b/WWW/Library/Implementation/HTVMS_WaisUI.c
new file mode 100644
index 0000000..716bbbb
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisUI.c
@@ -0,0 +1,2279 @@
+/*
+ * $LynxId: HTVMS_WaisUI.c,v 1.21 2020/01/21 22:00:50 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];
+
+ LYStrNCpy(length_array, header.msg_len, 10);
+ 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 stopped making sense */
+
+/*----------------------------------------------------------------------*/
+/* A note on error handling
+ read - these are low level routines, they do not check the type tags
+ which (sometimes) precede 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 amount, 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 represent the value num in
+ compressed format. currently 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 convenience 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 convenience 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 convenience function */
+{
+ char s[40];
+
+ sprintf(s, "%ld", num);
+
+ return (stringToAny(s));
+}
+
+/*----------------------------------------------------------------------*/
+
+long anyToLong(any *a)
+/* a convenience 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 */
+ LYStrNCpy(qt->Use, use, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Relation, relation, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Position, position, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Structure, structure, ATTRIBUTE_SIZE);
+ LYStrNCpy(qt->Truncation, truncation, ATTRIBUTE_SIZE);
+ LYStrNCpy(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;
+
+ LYStrNCpy(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:
+ LYStrNCpy(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 can't 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) /* safety 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 */