diff options
Diffstat (limited to 'WWW/Library/Implementation/HTVMS_WaisUI.c')
-rw-r--r-- | WWW/Library/Implementation/HTVMS_WaisUI.c | 2279 |
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 */ |