diff options
Diffstat (limited to 'WWW/Library/Implementation/HTVMS_WaisProt.c')
-rw-r--r-- | WWW/Library/Implementation/HTVMS_WaisProt.c | 2469 |
1 files changed, 2469 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.c b/WWW/Library/Implementation/HTVMS_WaisProt.c new file mode 100644 index 0000000..30feae6 --- /dev/null +++ b/WWW/Library/Implementation/HTVMS_WaisProt.c @@ -0,0 +1,2469 @@ +/* + * $LynxId: HTVMS_WaisProt.c,v 1.9 2010/09/24 23:51:22 tom Exp $ + * + * HTVMS_WAISProt.c + * + * Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu) + * + * 31-May-1994 FM Initial version. + * + *----------------------------------------------------------------------*/ + +/* + * Routines originally from WProt.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 + * - removed chunk code from WAISSearchAPDU, + * - added makeWAISQueryType1Query() and readWAISType1Query() which replace + * makeWAISQueryTerms() and makeWAISQueryDocs(). + * 4.11.90 HWM - generalized conditional includes (see c-dialect.h) + * - renamed makeWAISType1Query() to makeWAISTextQuery() + * renamed readWAISType1Query() to readWAISTextQuery() + * 5.29.90 TS - fixed bug in makeWAISQueryDocs + * added CSTFreeWAISFoo functions + */ + +#define _C_WAIS_protocol_ + +/* This file implements the Z39.50 extensions required for WAIS +*/ + +#include <HTUtils.h> +#include <HTVMS_WaisUI.h> +#include <HTVMS_WaisProt.h> + +#include <LYLeaks.h> + +/* very rough estimates of the size of an object */ +#define DefWAISInitResponseSize (size_t)200 +#define DefWAISSearchSize (size_t)3000 +#define DefWAISSearchResponseSize (size_t)6000 +#define DefWAISPresentSize (size_t)1000 +#define DefWAISPresentResponseSize (size_t)6000 +#define DefWAISDocHeaderSize (size_t)500 +#define DefWAISShortHeaderSize (size_t)200 +#define DefWAISLongHeaderSize (size_t)800 +#define DefWAISDocTextSize (size_t)6000 +#define DefWAISDocHeadlineSize (size_t)500 +#define DefWAISDocCodeSize (size_t)500 + +#define RESERVE_SPACE_FOR_WAIS_HEADER(len) \ + if (*len > 0) \ + *len -= header_len; + +#define S_MALLOC(type) (type*)s_malloc(sizeof(type)) +#define S_MALLOC2(type) (type*)s_malloc(sizeof(type) * 2) + +#define S_REALLOC2(type, ptr, num) (type*)s_realloc((char*)ptr, (sizeof(type) * (num + 2))) + +/*----------------------------------------------------------------------*/ + +static unsigned long userInfoTagSize(data_tag tag, + unsigned long length) +/* return the number of bytes required to write the user info tag and + length + */ +{ + unsigned long size; + + /* calculate bytes required to represent tag. max tag is 16K */ + size = writtenCompressedIntSize(tag); + size += writtenCompressedIntSize(length); + + return (size); +} + +/*----------------------------------------------------------------------*/ + +static char *writeUserInfoHeader(data_tag tag, + long infoSize, + long estHeaderSize, + char *buffer, + long *len) +/* write the tag and size, making sure the info fits. return the true end + of the info (after adjustment) note that the argument infoSize includes + estHeaderSize. Note that the argument len is the number of bytes remaining + in the buffer. Since we write the tag and size at the begining of the + buffer (in space that we reserved) we don't want to pass len the calls which + do that writing. + */ +{ + long dummyLen = 100; /* plenty of space for a tag and size */ + char *buf = buffer; + long realSize = infoSize - estHeaderSize; + long realHeaderSize = userInfoTagSize(tag, realSize); + + if (buffer == NULL || *len == 0) + return (NULL); + + /* write the tag */ + buf = writeTag(tag, buf, &dummyLen); + + /* see if the if the header size was correct. if not, + we have to shift the info to fit the real header size */ + if (estHeaderSize != realHeaderSize) { /* make sure there is enough space */ + CHECK_FOR_SPACE_LEFT(realHeaderSize - estHeaderSize, len); + memmove(buffer + realHeaderSize, buffer + estHeaderSize, (size_t) (realSize)); + } + + /* write the size */ + writeCompressedInteger(realSize, buf, &dummyLen); + + /* return the true end of buffer */ + return (buffer + realHeaderSize + realSize); +} + +/*----------------------------------------------------------------------*/ + +static char *readUserInfoHeader(data_tag *tag, + unsigned long *num, + char *buffer) +/* read the tag and size */ +{ + char *buf = buffer; + + buf = readTag(tag, buf); + buf = readCompressedInteger(num, buf); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISInitResponse *makeWAISInitResponse(long chunkCode, + long chunkIDLen, + char *chunkMarker, + char *highlightMarker, + char *deHighlightMarker, + char *newLineChars) +/* create a WAIS init response object */ +{ + WAISInitResponse *init = S_MALLOC(WAISInitResponse); + + init->ChunkCode = chunkCode; /* note: none are copied! */ + init->ChunkIDLength = chunkIDLen; + init->ChunkMarker = chunkMarker; + init->HighlightMarker = highlightMarker; + init->DeHighlightMarker = deHighlightMarker; + init->NewlineCharacters = newLineChars; + + return (init); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISInitResponse(WAISInitResponse *init) +/* free an object made with makeWAISInitResponse */ +{ + s_free(init->ChunkMarker); + s_free(init->HighlightMarker); + s_free(init->DeHighlightMarker); + s_free(init->NewlineCharacters); + s_free(init); +} + +/*----------------------------------------------------------------------*/ + +char *writeInitResponseInfo(InitResponseAPDU *init, + char *buffer, + long *len) +/* write an init response object */ +{ + unsigned long header_len = userInfoTagSize(DT_UserInformationLength, + DefWAISInitResponseSize); + char *buf = buffer + header_len; + WAISInitResponse *info = (WAISInitResponse *) init->UserInformationField; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeNum(info->ChunkCode, DT_ChunkCode, buf, len); + buf = writeNum(info->ChunkIDLength, DT_ChunkIDLength, buf, len); + buf = writeString(info->ChunkMarker, DT_ChunkMarker, buf, len); + buf = writeString(info->HighlightMarker, DT_HighlightMarker, buf, len); + buf = writeString(info->DeHighlightMarker, DT_DeHighlightMarker, buf, len); + buf = writeString(info->NewlineCharacters, DT_NewlineCharacters, buf, len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_UserInformationLength, + size, + header_len, + buffer, + len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *readInitResponseInfo(void **info, + char *buffer) +/* read an init response object */ +{ + char *buf = buffer; + unsigned long size; + unsigned long headerSize; + long chunkCode, chunkIDLen; + data_tag tag1; + char *chunkMarker = NULL; + char *highlightMarker = NULL; + char *deHighlightMarker = NULL; + char *newLineChars = NULL; + + chunkCode = chunkIDLen = UNUSED; + + buf = readUserInfoHeader(&tag1, &size, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_ChunkCode: + buf = readNum(&chunkCode, buf); + break; + case DT_ChunkIDLength: + buf = readNum(&chunkIDLen, buf); + break; + case DT_ChunkMarker: + buf = readString(&chunkMarker, buf); + break; + case DT_HighlightMarker: + buf = readString(&highlightMarker, buf); + break; + case DT_DeHighlightMarker: + buf = readString(&deHighlightMarker, buf); + break; + case DT_NewlineCharacters: + buf = readString(&newLineChars, buf); + break; + default: + s_free(highlightMarker); + s_free(deHighlightMarker); + s_free(newLineChars); + REPORT_READ_ERROR(buf); + break; + } + } + + *info = (void *) makeWAISInitResponse(chunkCode, chunkIDLen, chunkMarker, + highlightMarker, deHighlightMarker, + newLineChars); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISSearch *makeWAISSearch(char *seedWords, + DocObj **docs, + char **textList, + long dateFactor, + char *beginDateRange, + char *endDateRange, + long maxDocsRetrieved) + +/* create a type 3 query object */ +{ + WAISSearch *query = S_MALLOC(WAISSearch); + + query->SeedWords = seedWords; /* not copied! */ + query->Docs = docs; /* not copied! */ + query->TextList = textList; /* not copied! */ + query->DateFactor = dateFactor; + query->BeginDateRange = beginDateRange; + query->EndDateRange = endDateRange; + query->MaxDocumentsRetrieved = maxDocsRetrieved; + + return (query); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISSearch(WAISSearch *query) + +/* destroy an object made with makeWAISSearch() */ +{ + void *ptr = NULL; + long i; + + s_free(query->SeedWords); + + if (query->Docs != NULL) + for (i = 0, ptr = (void *) query->Docs[i]; + ptr != NULL; + ptr = (void *) query->Docs[++i]) + freeDocObj((DocObj *) ptr); + s_free(query->Docs); + + if (query->TextList != NULL) /* XXX revisit when textlist is fully defined */ + for (i = 0, ptr = (void *) query->TextList[i]; + ptr != NULL; + ptr = (void *) query->TextList[++i]) + s_free(ptr); + s_free(query->TextList); + + s_free(query->BeginDateRange); + s_free(query->EndDateRange); + s_free(query); +} + +/*----------------------------------------------------------------------*/ + +DocObj *makeDocObjUsingWholeDocument(any *docID, + char *type) + +/* construct a document object using byte chunks - only for use by + servers */ +{ + DocObj *doc = S_MALLOC(DocObj); + + doc->DocumentID = docID; /* not copied! */ + doc->Type = type; /* not copied! */ + doc->ChunkCode = CT_document; + return (doc); +} + +/*----------------------------------------------------------------------*/ + +DocObj *makeDocObjUsingLines(any *docID, + char *type, + long start, + long end) + +/* construct a document object using line chunks - only for use by + servers */ +{ + DocObj *doc = S_MALLOC(DocObj); + + doc->ChunkCode = CT_line; + doc->DocumentID = docID; /* not copied */ + doc->Type = type; /* not copied! */ + doc->ChunkStart.Pos = start; + doc->ChunkEnd.Pos = end; + return (doc); +} + +/*----------------------------------------------------------------------*/ + +DocObj *makeDocObjUsingBytes(any *docID, + char *type, + long start, + long end) + +/* construct a document object using byte chunks - only for use by + servers */ +{ + DocObj *doc = S_MALLOC(DocObj); + + doc->ChunkCode = CT_byte; + doc->DocumentID = docID; /* not copied */ + doc->Type = type; /* not copied! */ + doc->ChunkStart.Pos = start; + doc->ChunkEnd.Pos = end; + return (doc); +} + +/*----------------------------------------------------------------------*/ + +DocObj *makeDocObjUsingParagraphs(any *docID, + char *type, + any *start, + any *end) + +/* construct a document object using byte chunks - only for use by + servers */ +{ + DocObj *doc = S_MALLOC(DocObj); + + doc->ChunkCode = CT_paragraph; + doc->DocumentID = docID; /* not copied */ + doc->Type = type; + doc->ChunkStart.ID = start; + doc->ChunkEnd.ID = end; + return (doc); +} + +/*----------------------------------------------------------------------*/ + +void freeDocObj(DocObj *doc) + +/* free a docObj */ +{ + freeAny(doc->DocumentID); + s_free(doc->Type); + if (doc->ChunkCode == CT_paragraph) { + freeAny(doc->ChunkStart.ID); + freeAny(doc->ChunkEnd.ID); + } + s_free(doc); +} + +/*----------------------------------------------------------------------*/ + +static char *writeDocObj(DocObj *doc, + char *buffer, + long *len) + +/* write as little as we can about the doc obj */ +{ + char *buf = buffer; + + /* we alwasy have to write the id, but its tag depends on if its a chunk */ + if (doc->ChunkCode == CT_document) + buf = writeAny(doc->DocumentID, DT_DocumentID, buf, len); + else + buf = writeAny(doc->DocumentID, DT_DocumentIDChunk, buf, len); + + if (doc->Type != NULL) + buf = writeString(doc->Type, DT_TYPE, buf, len); + + switch (doc->ChunkCode) { + case CT_document: + /* do nothing - there is no chunk data */ + break; + case CT_byte: + case CT_line: + buf = writeNum(doc->ChunkCode, DT_ChunkCode, buf, len); + buf = writeNum(doc->ChunkStart.Pos, DT_ChunkStartID, buf, len); + buf = writeNum(doc->ChunkEnd.Pos, DT_ChunkEndID, buf, len); + break; + case CT_paragraph: + buf = writeNum(doc->ChunkCode, DT_ChunkCode, buf, len); + buf = writeAny(doc->ChunkStart.ID, DT_ChunkStartID, buf, len); + buf = writeAny(doc->ChunkEnd.ID, DT_ChunkEndID, buf, len); + break; + default: + panic("Implementation error: unknown chuck type %ld", + doc->ChunkCode); + break; + } + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +static char *readDocObj(DocObj **doc, + char *buffer) + +/* read whatever we have about the new document */ +{ + char *buf = buffer; + data_tag tag; + + *doc = S_MALLOC(DocObj); + + tag = peekTag(buf); + buf = readAny(&((*doc)->DocumentID), buf); + + if (tag == DT_DocumentID) { + (*doc)->ChunkCode = CT_document; + tag = peekTag(buf); + if (tag == DT_TYPE) /* XXX depends on DT_TYPE != what comes next */ + buf = readString(&((*doc)->Type), buf); + /* ChunkStart and ChunkEnd are undefined */ + } else if (tag == DT_DocumentIDChunk) { + boolean readParagraphs = false; /* for cleanup */ + + tag = peekTag(buf); + if (tag == DT_TYPE) /* XXX depends on DT_TYPE != CT_FOO */ + buf = readString(&((*doc)->Type), buf); + buf = readNum(&((*doc)->ChunkCode), buf); + switch ((*doc)->ChunkCode) { + case CT_byte: + case CT_line: + buf = readNum(&((*doc)->ChunkStart.Pos), buf); + buf = readNum(&((*doc)->ChunkEnd.Pos), buf); + break; + case CT_paragraph: + buf = readAny(&((*doc)->ChunkStart.ID), buf); + buf = readAny(&((*doc)->ChunkEnd.ID), buf); + break; + default: + freeAny((*doc)->DocumentID); + if (readParagraphs) { + freeAny((*doc)->ChunkStart.ID); + freeAny((*doc)->ChunkEnd.ID); + } + s_free(doc); + REPORT_READ_ERROR(buf); + break; + } + } else { + freeAny((*doc)->DocumentID); + s_free(*doc); + REPORT_READ_ERROR(buf); + } + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *writeSearchInfo(SearchAPDU *query, + char *buffer, + long *len) + +/* write out a WAIS query (type 1 or 3) */ +{ + if (strcmp(query->QueryType, QT_TextRetrievalQuery) == 0) { + return (writeAny((any *) query->Query, DT_Query, buffer, len)); + } else { + unsigned long header_len = userInfoTagSize(DT_UserInformationLength, + DefWAISSearchSize); + char *buf = buffer + header_len; + WAISSearch *info = (WAISSearch *) query->Query; + unsigned long size; + long i; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeString(info->SeedWords, DT_SeedWords, buf, len); + + if (info->Docs != NULL) { + for (i = 0; info->Docs[i] != NULL; i++) { + buf = writeDocObj(info->Docs[i], buf, len); + } + } + + /* XXX text list */ + + buf = writeNum(info->DateFactor, + DT_DateFactor, + buf, + len); + buf = writeString(info->BeginDateRange, + DT_BeginDateRange, + buf, + len); + buf = writeString(info->EndDateRange, + DT_EndDateRange, + buf, + len); + buf = writeNum(info->MaxDocumentsRetrieved, + DT_MaxDocumentsRetrieved, + buf, + len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_UserInformationLength, + size, + header_len, + buffer, + len); + + return (buf); + } +} + +/*----------------------------------------------------------------------*/ + +char *readSearchInfo(void **info, + char *buffer) + +/* read a WAIS query (type 1 or 3) */ +{ + data_tag type = peekTag(buffer); + + if (type == DT_Query) /* this is a type 1 query */ + { + char *buf = buffer; + any *query = NULL; + + buf = readAny(&query, buf); + *info = (void *) query; + return (buf); + } else { /* a type 3 query */ + char *buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag1; + char *seedWords = NULL; + char *beginDateRange = NULL; + char *endDateRange = NULL; + long dateFactor, maxDocsRetrieved; + char **textList = NULL; + DocObj **docIDs = NULL; + DocObj *doc = NULL; + long docs = 0; + long i; + void *ptr = NULL; + + dateFactor = maxDocsRetrieved = UNUSED; + + buf = readUserInfoHeader(&tag1, &size, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_SeedWords: + buf = readString(&seedWords, buf); + break; + case DT_DocumentID: + case DT_DocumentIDChunk: + if (docIDs == NULL) /* create a new doc list */ + { + docIDs = S_MALLOC2(DocObj *); + } else { /* grow the doc list */ + docIDs = S_REALLOC2(DocObj *, docIDs, docs); + } + buf = readDocObj(&doc, buf); + if (buf == NULL) { + s_free(seedWords); + s_free(beginDateRange); + s_free(endDateRange); + if (docIDs != NULL) + for (i = 0, ptr = (void *) docIDs[i]; + ptr != NULL; + ptr = (void *) docIDs[++i]) + freeDocObj((DocObj *) ptr); + s_free(docIDs); + /* XXX should also free textlist when it is fully defined */ + } + RETURN_ON_NULL(buf); + docIDs[docs++] = doc; /* put it in the list */ + docIDs[docs] = NULL; + break; + case DT_TextList: + /* XXX */ + break; + case DT_DateFactor: + buf = readNum(&dateFactor, buf); + break; + case DT_BeginDateRange: + buf = readString(&beginDateRange, buf); + break; + case DT_EndDateRange: + buf = readString(&endDateRange, buf); + break; + case DT_MaxDocumentsRetrieved: + buf = readNum(&maxDocsRetrieved, buf); + break; + default: + s_free(seedWords); + s_free(beginDateRange); + s_free(endDateRange); + if (docIDs != NULL) + for (i = 0, ptr = (void *) docIDs[i]; + ptr != NULL; + ptr = (void *) docIDs[++i]) + freeDocObj((DocObj *) ptr); + s_free(docIDs); + /* XXX should also free textlist when it is fully defined */ + REPORT_READ_ERROR(buf); + break; + } + } + + *info = (void *) makeWAISSearch(seedWords, docIDs, textList, + dateFactor, beginDateRange, endDateRange, + maxDocsRetrieved); + return (buf); + } +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentHeader *makeWAISDocumentHeader(any *docID, + long versionNumber, + long score, + long bestMatch, + long docLen, + long lines, + char **types, + char *source, + char *date, + char *headline, + char *originCity) + +/* construct a standard document header, note that no fields are copied! + if the application needs to save these fields, it should copy them, + or set the field in this object to NULL before freeing it. + */ +{ + WAISDocumentHeader *header = S_MALLOC(WAISDocumentHeader); + + header->DocumentID = docID; + header->VersionNumber = versionNumber; + header->Score = score; + header->BestMatch = bestMatch; + header->DocumentLength = docLen; + header->Lines = lines; + header->Types = types; + header->Source = source; + header->Date = date; + header->Headline = headline; + header->OriginCity = originCity; + + return (header); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISDocumentHeader(WAISDocumentHeader *header) +{ + freeAny(header->DocumentID); + doList((void **) header->Types, fs_free); /* can't use the macro here ! */ + s_free(header->Types); + s_free(header->Source); + s_free(header->Date); + s_free(header->Headline); + s_free(header->OriginCity); + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +char *writeWAISDocumentHeader(WAISDocumentHeader *header, char *buffer, + long *len) +{ + unsigned long header_len = userInfoTagSize(DT_DocumentHeaderGroup, + DefWAISDocHeaderSize); + char *buf = buffer + header_len; + unsigned long size1; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(header->DocumentID, DT_DocumentID, buf, len); + buf = writeNum(header->VersionNumber, DT_VersionNumber, buf, len); + buf = writeNum(header->Score, DT_Score, buf, len); + buf = writeNum(header->BestMatch, DT_BestMatch, buf, len); + buf = writeNum(header->DocumentLength, DT_DocumentLength, buf, len); + buf = writeNum(header->Lines, DT_Lines, buf, len); + if (header->Types != NULL) { + long size; + char *ptr = NULL; + long i; + + buf = writeTag(DT_TYPE_BLOCK, buf, len); + for (i = 0, size = 0, ptr = header->Types[i]; + ptr != NULL; + ptr = header->Types[++i]) { + long typeSize = strlen(ptr); + + size += writtenTagSize(DT_TYPE); + size += writtenCompressedIntSize(typeSize); + size += typeSize; + } + buf = writeCompressedInteger((unsigned long) size, buf, len); + for (i = 0, ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i]) + buf = writeString(ptr, DT_TYPE, buf, len); + } + buf = writeString(header->Source, DT_Source, buf, len); + buf = writeString(header->Date, DT_Date, buf, len); + buf = writeString(header->Headline, DT_Headline, buf, len); + buf = writeString(header->OriginCity, DT_OriginCity, buf, len); + + /* now write the header and size */ + size1 = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentHeaderGroup, + size1, + header_len, + buffer, + len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *readWAISDocumentHeader(WAISDocumentHeader **header, char *buffer) +{ + char *buf = buffer; + unsigned long size1; + unsigned long headerSize; + data_tag tag1; + any *docID = NULL; + long versionNumber, score, bestMatch, docLength, lines; + char **types = NULL; + char *source = NULL; + char *date = NULL; + char *headline = NULL; + char *originCity = NULL; + + versionNumber = score = bestMatch = docLength = lines = UNUSED; + + buf = readUserInfoHeader(&tag1, &size1, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size1 + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_DocumentID: + buf = readAny(&docID, buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber, buf); + break; + case DT_Score: + buf = readNum(&score, buf); + break; + case DT_BestMatch: + buf = readNum(&bestMatch, buf); + break; + case DT_DocumentLength: + buf = readNum(&docLength, buf); + break; + case DT_Lines: + buf = readNum(&lines, buf); + break; + case DT_TYPE_BLOCK: + { + unsigned long size = -1; + long numTypes = 0; + + buf = readTag(&tag, buf); + buf = readCompressedInteger(&size, buf); + while (size > 0) { + char *type = NULL; + char *originalBuf = buf; + + buf = readString(&type, buf); + types = S_REALLOC2(char *, types, numTypes); + + types[numTypes++] = type; + types[numTypes] = NULL; + size -= (buf - originalBuf); + } + } + /* FALLTHRU */ + case DT_Source: + buf = readString(&source, buf); + break; + case DT_Date: + buf = readString(&date, buf); + break; + case DT_Headline: + buf = readString(&headline, buf); + break; + case DT_OriginCity: + buf = readString(&originCity, buf); + break; + default: + freeAny(docID); + s_free(source); + s_free(date); + s_free(headline); + s_free(originCity); + REPORT_READ_ERROR(buf); + break; + } + } + + *header = makeWAISDocumentHeader(docID, versionNumber, score, bestMatch, + docLength, lines, types, source, date, headline, + originCity); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentShortHeader *makeWAISDocumentShortHeader(any *docID, + long versionNumber, + long score, + long bestMatch, + long docLen, + long lines) +/* construct a short document header, note that no fields are copied! + if the application needs to save these fields, it should copy them, + or set the field in this object to NULL before freeing it. + */ +{ + WAISDocumentShortHeader *header = S_MALLOC(WAISDocumentShortHeader); + + header->DocumentID = docID; + header->VersionNumber = versionNumber; + header->Score = score; + header->BestMatch = bestMatch; + header->DocumentLength = docLen; + header->Lines = lines; + + return (header); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISDocumentShortHeader(WAISDocumentShortHeader *header) +{ + freeAny(header->DocumentID); + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +char *writeWAISDocumentShortHeader(WAISDocumentShortHeader *header, char *buffer, + long *len) +{ + unsigned long header_len = userInfoTagSize(DT_DocumentShortHeaderGroup, + DefWAISShortHeaderSize); + char *buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(header->DocumentID, DT_DocumentID, buf, len); + buf = writeNum(header->VersionNumber, DT_VersionNumber, buf, len); + buf = writeNum(header->Score, DT_Score, buf, len); + buf = writeNum(header->BestMatch, DT_BestMatch, buf, len); + buf = writeNum(header->DocumentLength, DT_DocumentLength, buf, len); + buf = writeNum(header->Lines, DT_Lines, buf, len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentShortHeaderGroup, + size, + header_len, + buffer, + len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *readWAISDocumentShortHeader(WAISDocumentShortHeader **header, char *buffer) +{ + char *buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag1; + any *docID = NULL; + long versionNumber, score, bestMatch, docLength, lines; + + versionNumber = score = bestMatch = docLength = lines = UNUSED; + + buf = readUserInfoHeader(&tag1, &size, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_DocumentID: + buf = readAny(&docID, buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber, buf); + break; + case DT_Score: + buf = readNum(&score, buf); + break; + case DT_BestMatch: + buf = readNum(&bestMatch, buf); + break; + case DT_DocumentLength: + buf = readNum(&docLength, buf); + break; + case DT_Lines: + buf = readNum(&lines, buf); + break; + default: + freeAny(docID); + REPORT_READ_ERROR(buf); + break; + } + } + + *header = makeWAISDocumentShortHeader(docID, versionNumber, score, bestMatch, + docLength, lines); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentLongHeader *makeWAISDocumentLongHeader(any *docID, + long versionNumber, + long score, + long bestMatch, + long docLen, + long lines, + char **types, + char *source, + char *date, + char *headline, + char *originCity, + char *stockCodes, + char *companyCodes, + char *industryCodes) +/* construct a long document header, note that no fields are copied! + if the application needs to save these fields, it should copy them, + or set the field in this object to NULL before freeing it. + */ +{ + WAISDocumentLongHeader *header = S_MALLOC(WAISDocumentLongHeader); + + header->DocumentID = docID; + header->VersionNumber = versionNumber; + header->Score = score; + header->BestMatch = bestMatch; + header->DocumentLength = docLen; + header->Lines = lines; + header->Types = types; + header->Source = source; + header->Date = date; + header->Headline = headline; + header->OriginCity = originCity; + header->StockCodes = stockCodes; + header->CompanyCodes = companyCodes; + header->IndustryCodes = industryCodes; + + return (header); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISDocumentLongHeader(WAISDocumentLongHeader *header) +{ + freeAny(header->DocumentID); + doList((void **) header->Types, fs_free); /* can't use the macro here! */ + s_free(header->Source); + s_free(header->Date); + s_free(header->Headline); + s_free(header->OriginCity); + s_free(header->StockCodes); + s_free(header->CompanyCodes); + s_free(header->IndustryCodes); + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +char *writeWAISDocumentLongHeader(WAISDocumentLongHeader *header, char *buffer, + long *len) +{ + unsigned long header_len = userInfoTagSize(DT_DocumentLongHeaderGroup, + DefWAISLongHeaderSize); + char *buf = buffer + header_len; + unsigned long size1; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(header->DocumentID, DT_DocumentID, buf, len); + buf = writeNum(header->VersionNumber, DT_VersionNumber, buf, len); + buf = writeNum(header->Score, DT_Score, buf, len); + buf = writeNum(header->BestMatch, DT_BestMatch, buf, len); + buf = writeNum(header->DocumentLength, DT_DocumentLength, buf, len); + buf = writeNum(header->Lines, DT_Lines, buf, len); + if (header->Types != NULL) { + long size; + char *ptr = NULL; + long i; + + buf = writeTag(DT_TYPE_BLOCK, buf, len); + for (i = 0, size = 0, ptr = header->Types[i]; + ptr != NULL; + ptr = header->Types[++i]) { + long typeSize = strlen(ptr); + + size += writtenTagSize(DT_TYPE); + size += writtenCompressedIntSize(typeSize); + size += typeSize; + } + buf = writeCompressedInteger((unsigned long) size, buf, len); + for (i = 0, ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i]) + buf = writeString(ptr, DT_TYPE, buf, len); + } + buf = writeString(header->Source, DT_Source, buf, len); + buf = writeString(header->Date, DT_Date, buf, len); + buf = writeString(header->Headline, DT_Headline, buf, len); + buf = writeString(header->OriginCity, DT_OriginCity, buf, len); + buf = writeString(header->StockCodes, DT_StockCodes, buf, len); + buf = writeString(header->CompanyCodes, DT_CompanyCodes, buf, len); + buf = writeString(header->IndustryCodes, DT_IndustryCodes, buf, len); + + /* now write the header and size */ + size1 = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentLongHeaderGroup, + size1, + header_len, + buffer, + len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *readWAISDocumentLongHeader(WAISDocumentLongHeader **header, char *buffer) +{ + char *buf = buffer; + unsigned long size1; + unsigned long headerSize; + data_tag tag1; + any *docID; + long versionNumber, score, bestMatch, docLength, lines; + char **types; + char *source, *date, *headline, *originCity, *stockCodes, *companyCodes, *industryCodes; + + docID = NULL; + versionNumber = + score = + bestMatch = + docLength = + lines = UNUSED; + types = NULL; + source = + date = + headline = + originCity = + stockCodes = + companyCodes = + industryCodes = NULL; + + buf = readUserInfoHeader(&tag1, &size1, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size1 + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_DocumentID: + buf = readAny(&docID, buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber, buf); + break; + case DT_Score: + buf = readNum(&score, buf); + break; + case DT_BestMatch: + buf = readNum(&bestMatch, buf); + break; + case DT_DocumentLength: + buf = readNum(&docLength, buf); + break; + case DT_Lines: + buf = readNum(&lines, buf); + break; + case DT_TYPE_BLOCK: + { + unsigned long size = -1; + long numTypes = 0; + + buf = readTag(&tag, buf); + readCompressedInteger(&size, buf); + while (size > 0) { + char *type = NULL; + char *originalBuf = buf; + + buf = readString(&type, buf); + types = S_REALLOC2(char *, types, numTypes); + + types[numTypes++] = type; + types[numTypes] = NULL; + size -= (buf - originalBuf); + } + } + /* FALLTHRU */ + case DT_Source: + buf = readString(&source, buf); + break; + case DT_Date: + buf = readString(&date, buf); + break; + case DT_Headline: + buf = readString(&headline, buf); + break; + case DT_OriginCity: + buf = readString(&originCity, buf); + break; + case DT_StockCodes: + buf = readString(&stockCodes, buf); + break; + case DT_CompanyCodes: + buf = readString(&companyCodes, buf); + break; + case DT_IndustryCodes: + buf = readString(&industryCodes, buf); + break; + default: + freeAny(docID); + s_free(source); + s_free(date); + s_free(headline); + s_free(originCity); + s_free(stockCodes); + s_free(companyCodes); + s_free(industryCodes); + REPORT_READ_ERROR(buf); + break; + } + } + + *header = makeWAISDocumentLongHeader(docID, + versionNumber, + score, + bestMatch, + docLength, + lines, + types, + source, + date, + headline, + originCity, + stockCodes, + companyCodes, + industryCodes); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISSearchResponse * + makeWAISSearchResponse( + char *seedWordsUsed, + WAISDocumentHeader **docHeaders, + WAISDocumentShortHeader **shortHeaders, + WAISDocumentLongHeader **longHeaders, + WAISDocumentText **text, + WAISDocumentHeadlines **headlines, + WAISDocumentCodes **codes, + diagnosticRecord ** diagnostics) +{ + WAISSearchResponse *response = S_MALLOC(WAISSearchResponse); + + response->SeedWordsUsed = seedWordsUsed; + response->DocHeaders = docHeaders; + response->ShortHeaders = shortHeaders; + response->LongHeaders = longHeaders; + response->Text = text; + response->Headlines = headlines; + response->Codes = codes; + response->Diagnostics = diagnostics; + + return (response); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISSearchResponse(WAISSearchResponse * response) +{ + void *ptr = NULL; + long i; + + s_free(response->SeedWordsUsed); + + if (response->DocHeaders != NULL) + for (i = 0, ptr = (void *) response->DocHeaders[i]; + ptr != NULL; + ptr = (void *) response->DocHeaders[++i]) + freeWAISDocumentHeader((WAISDocumentHeader *) ptr); + s_free(response->DocHeaders); + + if (response->ShortHeaders != NULL) + for (i = 0, ptr = (void *) response->ShortHeaders[i]; + ptr != NULL; + ptr = (void *) response->ShortHeaders[++i]) + freeWAISDocumentShortHeader((WAISDocumentShortHeader *) ptr); + s_free(response->ShortHeaders); + + if (response->LongHeaders != NULL) + for (i = 0, ptr = (void *) response->LongHeaders[i]; + ptr != NULL; + ptr = (void *) response->LongHeaders[++i]) + freeWAISDocumentLongHeader((WAISDocumentLongHeader *) ptr); + s_free(response->LongHeaders); + + if (response->Text != NULL) + for (i = 0, ptr = (void *) response->Text[i]; + ptr != NULL; + ptr = (void *) response->Text[++i]) + freeWAISDocumentText((WAISDocumentText *) ptr); + s_free(response->Text); + + if (response->Headlines != NULL) + for (i = 0, ptr = (void *) response->Headlines[i]; + ptr != NULL; + ptr = (void *) response->Headlines[++i]) + freeWAISDocumentHeadlines((WAISDocumentHeadlines *) ptr); + s_free(response->Headlines); + + if (response->Codes != NULL) + for (i = 0, ptr = (void *) response->Codes[i]; + ptr != NULL; + ptr = (void *) response->Codes[++i]) + freeWAISDocumentCodes((WAISDocumentCodes *) ptr); + s_free(response->Codes); + + if (response->Diagnostics != NULL) + for (i = 0, ptr = (void *) response->Diagnostics[i]; + ptr != NULL; + ptr = (void *) response->Diagnostics[++i]) + freeDiag((diagnosticRecord *) ptr); + s_free(response->Diagnostics); + + s_free(response); +} + +/*----------------------------------------------------------------------*/ + +char *writeSearchResponseInfo(SearchResponseAPDU *query, + char *buffer, + long *len) +{ + unsigned long header_len = userInfoTagSize(DT_UserInformationLength, + DefWAISSearchResponseSize); + char *buf = buffer + header_len; + WAISSearchResponse *info = (WAISSearchResponse *) query->DatabaseDiagnosticRecords; + unsigned long size; + void *header = NULL; + long i; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeString(info->SeedWordsUsed, DT_SeedWordsUsed, buf, len); + + /* write out all the headers */ + if (info->DocHeaders != NULL) { + for (i = 0, header = (void *) info->DocHeaders[i]; + header != NULL; + header = (void *) info->DocHeaders[++i]) + buf = writeWAISDocumentHeader((WAISDocumentHeader *) header, buf, len); + } + + if (info->ShortHeaders != NULL) { + for (i = 0, header = (void *) info->ShortHeaders[i]; + header != NULL; + header = (void *) info->ShortHeaders[++i]) + buf = writeWAISDocumentShortHeader((WAISDocumentShortHeader *) header, + buf, + len); + } + + if (info->LongHeaders != NULL) { + for (i = 0, header = (void *) info->LongHeaders[i]; + header != NULL; + header = (void *) info->LongHeaders[++i]) + buf = writeWAISDocumentLongHeader((WAISDocumentLongHeader *) header, + buf, + len); + } + + if (info->Text != NULL) { + for (i = 0, header = (void *) info->Text[i]; + header != NULL; + header = (void *) info->Text[++i]) + buf = writeWAISDocumentText((WAISDocumentText *) header, buf, len); + } + + if (info->Headlines != NULL) { + for (i = 0, header = (void *) info->Headlines[i]; + header != NULL; + header = (void *) info->Headlines[++i]) + buf = writeWAISDocumentHeadlines((WAISDocumentHeadlines *) header, + buf, + len); + } + + if (info->Codes != NULL) { + for (i = 0, header = (void *) info->Codes[i]; + header != NULL; + header = (void *) info->Codes[++i]) + buf = writeWAISDocumentCodes((WAISDocumentCodes *) header, buf, len); + } + + if (info->Diagnostics != NULL) { + for (i = 0, header = (void *) info->Diagnostics[i]; + header != NULL; + header = (void *) info->Diagnostics[++i]) + buf = writeDiag((diagnosticRecord *) header, buf, len); + } + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_UserInformationLength, + size, + header_len, + buffer, + len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +static void cleanUpWaisSearchResponse(char *buf, + char *seedWordsUsed, + WAISDocumentHeader **docHeaders, + WAISDocumentShortHeader **shortHeaders, + WAISDocumentLongHeader **longHeaders, + WAISDocumentText **text, + WAISDocumentHeadlines **headlines, + WAISDocumentCodes **codes, + diagnosticRecord ** diags) +/* if buf is NULL, we have just gotten a read error, and need to clean up + any state we have built. If not, then everything is going fine, and + we should just hang loose + */ +{ + void *ptr = NULL; + long i; + + if (buf == NULL) { + s_free(seedWordsUsed); + if (docHeaders != NULL) + for (i = 0, ptr = (void *) docHeaders[i]; ptr != NULL; + ptr = (void *) docHeaders[++i]) + freeWAISDocumentHeader((WAISDocumentHeader *) ptr); + s_free(docHeaders); + if (shortHeaders != NULL) + for (i = 0, ptr = (void *) shortHeaders[i]; ptr != NULL; + ptr = (void *) shortHeaders[++i]) + freeWAISDocumentShortHeader((WAISDocumentShortHeader *) ptr); + s_free(shortHeaders); + if (longHeaders != NULL) + for (i = 0, ptr = (void *) longHeaders[i]; ptr != NULL; + ptr = (void *) longHeaders[++i]) + freeWAISDocumentLongHeader((WAISDocumentLongHeader *) ptr); + s_free(longHeaders); + if (text != NULL) + for (i = 0, ptr = (void *) text[i]; + ptr != NULL; + ptr = (void *) text[++i]) + freeWAISDocumentText((WAISDocumentText *) ptr); + s_free(text); + if (headlines != NULL) + for (i = 0, ptr = (void *) headlines[i]; ptr != NULL; + ptr = (void *) headlines[++i]) + freeWAISDocumentHeadlines((WAISDocumentHeadlines *) ptr); + s_free(headlines); + if (codes != NULL) + for (i = 0, ptr = (void *) codes[i]; ptr != NULL; + ptr = (void *) codes[++i]) + freeWAISDocumentCodes((WAISDocumentCodes *) ptr); + s_free(codes); + if (diags != NULL) + for (i = 0, ptr = (void *) diags[i]; ptr != NULL; + ptr = (void *) diags[++i]) + freeDiag((diagnosticRecord *) ptr); + s_free(diags); + } +} + +/*----------------------------------------------------------------------*/ + +char *readSearchResponseInfo(void **info, + char *buffer) +{ + char *buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag1; + void *header = NULL; + WAISDocumentHeader **docHeaders = NULL; + WAISDocumentShortHeader **shortHeaders = NULL; + WAISDocumentLongHeader **longHeaders = NULL; + WAISDocumentText **text = NULL; + WAISDocumentHeadlines **headlines = NULL; + WAISDocumentCodes **codes = NULL; + long numDocHeaders, numLongHeaders, numShortHeaders, numText, numHeadlines; + long numCodes; + char *seedWordsUsed = NULL; + diagnosticRecord **diags = NULL; + diagnosticRecord *diag = NULL; + long numDiags = 0; + + numDocHeaders = + numLongHeaders = + numShortHeaders = + numText = + numHeadlines = + numCodes = 0; + + buf = readUserInfoHeader(&tag1, &size, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_SeedWordsUsed: + buf = readString(&seedWordsUsed, buf); + break; + case DT_DatabaseDiagnosticRecords: + if (diags == NULL) /* create a new diag list */ + { + diags = S_MALLOC2(diagnosticRecord *); + } else { /* grow the diag list */ + diags = S_REALLOC2(diagnosticRecord *, diags, numDiags); + } + buf = readDiag(&diag, buf); + diags[numDiags++] = diag; /* put it in the list */ + diags[numDiags] = NULL; + break; + case DT_DocumentHeaderGroup: + if (docHeaders == NULL) /* create a new header list */ + { + docHeaders = S_MALLOC2(WAISDocumentHeader *); + } else { /* grow the doc list */ + docHeaders = S_REALLOC2(WAISDocumentHeader *, docHeaders, numDocHeaders); + } + buf = readWAISDocumentHeader((WAISDocumentHeader **) &header, buf); + cleanUpWaisSearchResponse(buf, + seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + RETURN_ON_NULL(buf); + docHeaders[numDocHeaders++] = + (WAISDocumentHeader *) header; /* put it in the list */ + docHeaders[numDocHeaders] = NULL; + break; + case DT_DocumentShortHeaderGroup: + if (shortHeaders == NULL) /* create a new header list */ + { + shortHeaders = S_MALLOC2(WAISDocumentShortHeader *); + } else { /* grow the doc list */ + shortHeaders = S_REALLOC2(WAISDocumentShortHeader *, + shortHeaders, + numShortHeaders); + } + buf = readWAISDocumentShortHeader((WAISDocumentShortHeader **) &header, + buf); + cleanUpWaisSearchResponse(buf, + seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + RETURN_ON_NULL(buf); + shortHeaders[numShortHeaders++] = + (WAISDocumentShortHeader *) header; /* put it in the list */ + shortHeaders[numShortHeaders] = NULL; + break; + case DT_DocumentLongHeaderGroup: + if (longHeaders == NULL) /* create a new header list */ + { + longHeaders = S_MALLOC2(WAISDocumentLongHeader *); + } else { /* grow the doc list */ + longHeaders = S_REALLOC2(WAISDocumentLongHeader *, + longHeaders, + numLongHeaders); + } + buf = readWAISDocumentLongHeader((WAISDocumentLongHeader **) &header, + buf); + cleanUpWaisSearchResponse(buf, + seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + RETURN_ON_NULL(buf); + longHeaders[numLongHeaders++] = + (WAISDocumentLongHeader *) header; /* put it in the list */ + longHeaders[numLongHeaders] = NULL; + break; + case DT_DocumentTextGroup: + if (text == NULL) /* create a new list */ + { + text = S_MALLOC2(WAISDocumentText *); + } else { /* grow the list */ + text = S_REALLOC2(WAISDocumentText *, text, numText); + } + buf = readWAISDocumentText((WAISDocumentText **) &header, buf); + cleanUpWaisSearchResponse(buf, + seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + RETURN_ON_NULL(buf); + text[numText++] = + (WAISDocumentText *) header; /* put it in the list */ + text[numText] = NULL; + break; + case DT_DocumentHeadlineGroup: + if (headlines == NULL) /* create a new list */ + { + headlines = S_MALLOC2(WAISDocumentHeadlines *); + } else { /* grow the list */ + headlines = S_REALLOC2(WAISDocumentHeadlines *, headlines, numHeadlines); + } + buf = readWAISDocumentHeadlines((WAISDocumentHeadlines **) &header, + buf); + cleanUpWaisSearchResponse(buf, + seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + RETURN_ON_NULL(buf); + headlines[numHeadlines++] = + (WAISDocumentHeadlines *) header; /* put it in the list */ + headlines[numHeadlines] = NULL; + break; + case DT_DocumentCodeGroup: + if (codes == NULL) /* create a new list */ + { + codes = S_MALLOC2(WAISDocumentCodes *); + } else { /* grow the list */ + codes = S_REALLOC2(WAISDocumentCodes *, codes, numCodes); + } + buf = readWAISDocumentCodes((WAISDocumentCodes **) &header, buf); + cleanUpWaisSearchResponse(buf, + seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + RETURN_ON_NULL(buf); + codes[numCodes++] = + (WAISDocumentCodes *) header; /* put it in the list */ + codes[numCodes] = NULL; + break; + default: + cleanUpWaisSearchResponse(buf, + seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + REPORT_READ_ERROR(buf); + break; + } + } + + *info = (void *) makeWAISSearchResponse(seedWordsUsed, + docHeaders, + shortHeaders, + longHeaders, + text, + headlines, + codes, + diags); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentText *makeWAISDocumentText(any *docID, + long versionNumber, + any *documentText) +{ + WAISDocumentText *docText = S_MALLOC(WAISDocumentText); + + docText->DocumentID = docID; + docText->VersionNumber = versionNumber; + docText->DocumentText = documentText; + + return (docText); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISDocumentText(WAISDocumentText *docText) +{ + freeAny(docText->DocumentID); + freeAny(docText->DocumentText); + s_free(docText); +} + +/*----------------------------------------------------------------------*/ + +char *writeWAISDocumentText(WAISDocumentText *docText, char *buffer, + long *len) +{ + unsigned long header_len = userInfoTagSize(DT_DocumentTextGroup, + DefWAISDocTextSize); + char *buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(docText->DocumentID, DT_DocumentID, buf, len); + buf = writeNum(docText->VersionNumber, DT_VersionNumber, buf, len); + buf = writeAny(docText->DocumentText, DT_DocumentText, buf, len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentTextGroup, size, header_len, buffer, len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *readWAISDocumentText(WAISDocumentText **docText, char *buffer) +{ + char *buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag1; + any *docID, *documentText; + long versionNumber; + + docID = documentText = NULL; + versionNumber = UNUSED; + + buf = readUserInfoHeader(&tag1, &size, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_DocumentID: + buf = readAny(&docID, buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber, buf); + break; + case DT_DocumentText: + buf = readAny(&documentText, buf); + break; + default: + freeAny(docID); + freeAny(documentText); + REPORT_READ_ERROR(buf); + break; + } + } + + *docText = makeWAISDocumentText(docID, versionNumber, documentText); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentHeadlines *makeWAISDocumentHeadlines(any *docID, + long versionNumber, + char *source, + char *date, + char *headline, + char *originCity) +{ + WAISDocumentHeadlines *docHeadline = S_MALLOC(WAISDocumentHeadlines); + + docHeadline->DocumentID = docID; + docHeadline->VersionNumber = versionNumber; + docHeadline->Source = source; + docHeadline->Date = date; + docHeadline->Headline = headline; + docHeadline->OriginCity = originCity; + + return (docHeadline); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISDocumentHeadlines(WAISDocumentHeadlines *docHeadline) +{ + freeAny(docHeadline->DocumentID); + s_free(docHeadline->Source); + s_free(docHeadline->Date); + s_free(docHeadline->Headline); + s_free(docHeadline->OriginCity); + s_free(docHeadline); +} + +/*----------------------------------------------------------------------*/ + +char *writeWAISDocumentHeadlines(WAISDocumentHeadlines *docHeadline, char *buffer, + long *len) +{ + unsigned long header_len = userInfoTagSize(DT_DocumentHeadlineGroup, + DefWAISDocHeadlineSize); + char *buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(docHeadline->DocumentID, DT_DocumentID, buf, len); + buf = writeNum(docHeadline->VersionNumber, DT_VersionNumber, buf, len); + buf = writeString(docHeadline->Source, DT_Source, buf, len); + buf = writeString(docHeadline->Date, DT_Date, buf, len); + buf = writeString(docHeadline->Headline, DT_Headline, buf, len); + buf = writeString(docHeadline->OriginCity, DT_OriginCity, buf, len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentHeadlineGroup, + size, + header_len, + buffer, + len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *readWAISDocumentHeadlines(WAISDocumentHeadlines **docHeadline, char *buffer) +{ + char *buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag1; + any *docID; + long versionNumber; + char *source, *date, *headline, *originCity; + + docID = NULL; + versionNumber = UNUSED; + source = date = headline = originCity = NULL; + + buf = readUserInfoHeader(&tag1, &size, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_DocumentID: + buf = readAny(&docID, buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber, buf); + break; + case DT_Source: + buf = readString(&source, buf); + break; + case DT_Date: + buf = readString(&date, buf); + break; + case DT_Headline: + buf = readString(&headline, buf); + break; + case DT_OriginCity: + buf = readString(&originCity, buf); + break; + default: + freeAny(docID); + s_free(source); + s_free(date); + s_free(headline); + s_free(originCity); + REPORT_READ_ERROR(buf); + break; + } + } + + *docHeadline = makeWAISDocumentHeadlines(docID, versionNumber, source, date, + headline, originCity); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +WAISDocumentCodes *makeWAISDocumentCodes(any *docID, + long versionNumber, + char *stockCodes, + char *companyCodes, + char *industryCodes) +{ + WAISDocumentCodes *docCodes = S_MALLOC(WAISDocumentCodes); + + docCodes->DocumentID = docID; + docCodes->VersionNumber = versionNumber; + docCodes->StockCodes = stockCodes; + docCodes->CompanyCodes = companyCodes; + docCodes->IndustryCodes = industryCodes; + + return (docCodes); +} + +/*----------------------------------------------------------------------*/ + +void freeWAISDocumentCodes(WAISDocumentCodes *docCodes) +{ + freeAny(docCodes->DocumentID); + s_free(docCodes->StockCodes); + s_free(docCodes->CompanyCodes); + s_free(docCodes->IndustryCodes); + s_free(docCodes); +} + +/*----------------------------------------------------------------------*/ + +char *writeWAISDocumentCodes(WAISDocumentCodes *docCodes, + char *buffer, + long *len) +{ + unsigned long header_len = userInfoTagSize(DT_DocumentCodeGroup, + DefWAISDocCodeSize); + char *buf = buffer + header_len; + unsigned long size; + + RESERVE_SPACE_FOR_WAIS_HEADER(len); + + buf = writeAny(docCodes->DocumentID, DT_DocumentID, buf, len); + buf = writeNum(docCodes->VersionNumber, DT_VersionNumber, buf, len); + buf = writeString(docCodes->StockCodes, DT_StockCodes, buf, len); + buf = writeString(docCodes->CompanyCodes, DT_CompanyCodes, buf, len); + buf = writeString(docCodes->IndustryCodes, DT_IndustryCodes, buf, len); + + /* now write the header and size */ + size = buf - buffer; + buf = writeUserInfoHeader(DT_DocumentCodeGroup, size, header_len, buffer, len); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *readWAISDocumentCodes(WAISDocumentCodes **docCodes, + char *buffer) +{ + char *buf = buffer; + unsigned long size; + unsigned long headerSize; + data_tag tag1; + any *docID; + long versionNumber; + char *stockCodes, *companyCodes, *industryCodes; + + docID = NULL; + versionNumber = UNUSED; + stockCodes = companyCodes = industryCodes = NULL; + + buf = readUserInfoHeader(&tag1, &size, buf); + headerSize = buf - buffer; + + while (buf < (buffer + size + headerSize)) { + data_tag tag = peekTag(buf); + + switch (tag) { + case DT_DocumentID: + buf = readAny(&docID, buf); + break; + case DT_VersionNumber: + buf = readNum(&versionNumber, buf); + break; + case DT_StockCodes: + buf = readString(&stockCodes, buf); + break; + case DT_CompanyCodes: + buf = readString(&companyCodes, buf); + break; + case DT_IndustryCodes: + buf = readString(&industryCodes, buf); + break; + default: + freeAny(docID); + s_free(stockCodes); + s_free(companyCodes); + s_free(industryCodes); + REPORT_READ_ERROR(buf); + break; + } + } + + *docCodes = makeWAISDocumentCodes(docID, versionNumber, stockCodes, + companyCodes, industryCodes); + return (buf); +} + +/*----------------------------------------------------------------------*/ + +char *writePresentInfo(PresentAPDU * present GCC_UNUSED, char *buffer, + long *len GCC_UNUSED) +{ + /* The WAIS protocol doesn't use present info */ + return (buffer); +} + +/*----------------------------------------------------------------------*/ + +char *readPresentInfo(void **info, + char *buffer) +{ + /* The WAIS protocol doesn't use present info */ + *info = NULL; + return (buffer); +} + +/*----------------------------------------------------------------------*/ + +char *writePresentResponseInfo(PresentResponseAPDU * response GCC_UNUSED, char *buffer, + long *len GCC_UNUSED) +{ + /* The WAIS protocol doesn't use presentResponse info */ + return (buffer); +} + +/*----------------------------------------------------------------------*/ + +char *readPresentResponseInfo(void **info, + char *buffer) +{ + /* The WAIS protocol doesn't use presentResponse info */ + *info = NULL; + return (buffer); +} + +/*----------------------------------------------------------------------*/ + +/* support for type 1 queries */ + +/* new use values (for the chunk types) */ +#define BYTE "wb" +#define LINE "wl" +#define PARAGRAPH "wp" +#define DATA_TYPE "wt" + +/* WAIS supports the following semantics for type 1 queries: + + 1. retrieve the header/codes from a document: + + System_Control_Number = docID + Data Type = type (optional) + And + + 2. retrieve a fragment of the text of a document: + + System_Control_Number = docID + Data Type = type (optional) + And + Chunk >= start + And + Chunk < end + And + + Information from multiple documents may be requested by using + groups of the above joined by: + + OR + + ( XXX does an OR come after every group but the first, or do they + all come at the end? ) + + ( XXX return type could be in the element set) +*/ + +static query_term **makeWAISQueryTerms(DocObj **docs) +/* given a null terminated list of docObjs, construct the appropriate + query of the form given above + */ +{ + query_term **terms = NULL; + long numTerms = 0; + DocObj *doc = NULL; + long i; + + if (docs == NULL) + return ((query_term **) NULL); + + terms = (query_term **) s_malloc((size_t) (sizeof(query_term *) * 1)); + + terms[numTerms] = NULL; + + /* loop through the docs making terms for them all */ + for (i = 0, doc = docs[i]; doc != NULL; doc = docs[++i]) { + any *type = NULL; + + if (doc->Type != NULL) + type = stringToAny(doc->Type); + + if (doc->ChunkCode == CT_document) /* a whole document */ + { + terms = S_REALLOC2(query_term *, terms, numTerms + 2); + + terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER, + EQUAL, IGNORE, IGNORE, + IGNORE, IGNORE, doc->DocumentID); + if (type != NULL) { + terms[numTerms++] = makeAttributeTerm(DATA_TYPE, EQUAL, + IGNORE, IGNORE, IGNORE, + IGNORE, type); + terms[numTerms++] = makeOperatorTerm(AND); + } + terms[numTerms] = NULL; + } else { /* a document fragment */ + char chunk_att[ATTRIBUTE_SIZE]; + any *startChunk = NULL; + any *endChunk = NULL; + + terms = S_REALLOC2(query_term *, terms, numTerms + 6); + + switch (doc->ChunkCode) { + case CT_byte: + case CT_line: + { + char start[20], end[20]; + + (doc->ChunkCode == CT_byte) ? + StrNCpy(chunk_att, BYTE, ATTRIBUTE_SIZE) : + StrNCpy(chunk_att, LINE, ATTRIBUTE_SIZE); + sprintf(start, "%ld", doc->ChunkStart.Pos); + startChunk = stringToAny(start); + sprintf(end, "%ld", doc->ChunkEnd.Pos); + endChunk = stringToAny(end); + } + break; + case CT_paragraph: + StrNCpy(chunk_att, PARAGRAPH, ATTRIBUTE_SIZE); + startChunk = doc->ChunkStart.ID; + endChunk = doc->ChunkEnd.ID; + break; + default: + /* error */ + break; + } + + terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER, + EQUAL, IGNORE, IGNORE, + IGNORE, + IGNORE, doc->DocumentID); + if (type != NULL) { + terms[numTerms++] = makeAttributeTerm(DATA_TYPE, EQUAL, IGNORE, + IGNORE, IGNORE, IGNORE, + type); + terms[numTerms++] = makeOperatorTerm(AND); + } + terms[numTerms++] = makeAttributeTerm(chunk_att, + GREATER_THAN_OR_EQUAL, + IGNORE, IGNORE, IGNORE, + IGNORE, + startChunk); + terms[numTerms++] = makeOperatorTerm(AND); + terms[numTerms++] = makeAttributeTerm(chunk_att, LESS_THAN, + IGNORE, IGNORE, IGNORE, + IGNORE, + endChunk); + terms[numTerms++] = makeOperatorTerm(AND); + terms[numTerms] = NULL; + + if (doc->ChunkCode == CT_byte || doc->ChunkCode == CT_line) { + freeAny(startChunk); + freeAny(endChunk); + } + } + + freeAny(type); + + if (i != 0) /* multiple independent queries, need a disjunction */ + { + terms = S_REALLOC2(query_term *, terms, numTerms); + + terms[numTerms++] = makeOperatorTerm(OR); + terms[numTerms] = NULL; + } + } + + return (terms); +} + +/*----------------------------------------------------------------------*/ + +static DocObj **makeWAISQueryDocs(query_term **terms) +/* given a list of terms in the form given above, convert them to + DocObjs. + */ +{ + query_term *docTerm = NULL; + query_term *fragmentTerm = NULL; + DocObj **docs = NULL; + DocObj *doc = NULL; + long docNum, termNum; + + docNum = termNum = 0; + + docs = S_MALLOC(DocObj *); + + docs[docNum] = NULL; + + /* translate the terms into DocObjs */ + while (true) { + query_term *typeTerm = NULL; + char *type = NULL; + long startTermOffset; + + docTerm = terms[termNum]; + + if (docTerm == NULL) + break; /* we're done converting */ + + typeTerm = terms[termNum + 1]; /* get the lead Term if it exists */ + + if (strcmp(typeTerm->Use, DATA_TYPE) == 0) /* we do have a type */ + { + startTermOffset = 3; + type = anyToString(typeTerm->Term); + } else { /* no type */ + startTermOffset = 1; + typeTerm = NULL; + type = NULL; + } + + /* grow the doc list */ + docs = S_REALLOC2(DocObj *, docs, docNum); + + /* figure out what kind of docObj to build - and build it */ + fragmentTerm = terms[termNum + startTermOffset]; + if (fragmentTerm != NULL && fragmentTerm->TermType == TT_Attribute) { /* build a document fragment */ + query_term *startTerm = fragmentTerm; + query_term *endTerm = terms[termNum + startTermOffset + 2]; + + if (strcmp(startTerm->Use, BYTE) == 0) { /* a byte chunk */ + doc = makeDocObjUsingBytes(duplicateAny(docTerm->Term), + type, + anyToLong(startTerm->Term), + anyToLong(endTerm->Term)); + log_write("byte"); + } else if (strcmp(startTerm->Use, LINE) == 0) { /* a line chunk */ + doc = makeDocObjUsingLines(duplicateAny(docTerm->Term), + type, + anyToLong(startTerm->Term), + anyToLong(endTerm->Term)); + log_write("line"); + } else { + log_write("chunk"); /* a paragraph chunk */ + doc = makeDocObjUsingParagraphs(duplicateAny(docTerm->Term), + type, + duplicateAny(startTerm->Term), + duplicateAny(endTerm->Term)); + } + termNum += (startTermOffset + 4); /* point to next term */ + } else { /* build a full document */ + doc = makeDocObjUsingWholeDocument(duplicateAny(docTerm->Term), + type); + log_write("whole doc"); + termNum += startTermOffset; /* point to next term */ + } + + docs[docNum++] = doc; /* insert the new document */ + + docs[docNum] = NULL; /* keep the doc list terminated */ + + if (terms[termNum] != NULL) + termNum++; /* skip the OR operator it necessary */ + else + break; /* we are done */ + } + + return (docs); +} + +/*----------------------------------------------------------------------*/ + +any *makeWAISTextQuery(DocObj **docs) +/* given a list of DocObjs, return an any whose contents is the corresponding + type 1 query + */ +{ + any *buf = NULL; + query_term **terms = NULL; + + terms = makeWAISQueryTerms(docs); + buf = writeQuery(terms); + + doList((void **) terms, freeTerm); + s_free(terms); + + return (buf); +} + +/*----------------------------------------------------------------------*/ + +DocObj **readWAISTextQuery(any *buf) +/* given an any whose contents are type 1 queries of the WAIS sort, + construct a list of the corresponding DocObjs + */ +{ + query_term **terms = NULL; + DocObj **docs = NULL; + + terms = readQuery(buf); + docs = makeWAISQueryDocs(terms); + + doList((void **) terms, freeTerm); + s_free(terms); + + return (docs); +} + +/*----------------------------------------------------------------------*/ +/* Customized free WAIS object routines: */ +/* */ +/* This set of procedures is for applications to free a WAIS object */ +/* which was made with makeWAISFOO. */ +/* Each procedure frees only the memory that was allocated in its */ +/* associated makeWAISFOO routine, thus it's not necessary for the */ +/* caller to assign nulls to the pointer fields of the WAIS object. */ +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISInitResponse(WAISInitResponse *init) +/* free an object made with makeWAISInitResponse */ +{ + s_free(init); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISSearch(WAISSearch *query) +/* destroy an object made with makeWAISSearch() */ +{ + s_free(query); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeDocObj(DocObj *doc) +/* free a docObj */ +{ + s_free(doc); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISDocumentHeader(WAISDocumentHeader *header) +{ + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISDocumentShortHeader(WAISDocumentShortHeader *header) +{ + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISDocumentLongHeader(WAISDocumentLongHeader *header) +{ + s_free(header); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISSearchResponse(WAISSearchResponse * response) +{ + s_free(response); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISDocumentText(WAISDocumentText *docText) +{ + s_free(docText); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISDocHeadlines(WAISDocumentHeadlines *docHeadline) +{ + s_free(docHeadline); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISDocumentCodes(WAISDocumentCodes *docCodes) +{ + s_free(docCodes); +} + +/*----------------------------------------------------------------------*/ + +void CSTFreeWAISTextQuery(any *query) +{ + freeAny(query); +} + +/*----------------------------------------------------------------------*/ + +/* + * Routines originally from WMessage.c -- FM + * + *----------------------------------------------------------------------*/ +/* WIDE AREA INFORMATION SERVER SOFTWARE + * No guarantees or restrictions. See the readme file for the full standard + * disclaimer. + * 3.26.90 + */ + +/* This file is for reading and writing the wais packet header. + * Morris@think.com + */ + +/* to do: + * add check sum + * what do you do when checksum is wrong? + */ + +/*---------------------------------------------------------------------*/ + +void readWAISPacketHeader(char *msgBuffer, + WAISMessage * header_struct) +{ + /* msgBuffer is a string containing at least HEADER_LENGTH bytes. */ + + memmove(header_struct->msg_len, msgBuffer, (size_t) 10); + header_struct->msg_type = char_downcase((unsigned long) msgBuffer[10]); + header_struct->hdr_vers = char_downcase((unsigned long) msgBuffer[11]); + memmove(header_struct->server, (void *) (msgBuffer + 12), (size_t) 10); + header_struct->compression = char_downcase((unsigned long) msgBuffer[22]); + header_struct->encoding = char_downcase((unsigned long) msgBuffer[23]); + header_struct->msg_checksum = char_downcase((unsigned long) msgBuffer[24]); +} + +/*---------------------------------------------------------------------*/ + +/* this modifies the header argument. See wais-message.h for the different + * options for the arguments. + */ + +void writeWAISPacketHeader(char *header, + long dataLen, + long type, + char *server, + long compression, + long encoding, + long version) +/* Puts together the new wais before-the-z39-packet header. */ +{ + char lengthBuf[11]; + char serverBuf[11]; + + long serverLen = strlen(server); + + if (serverLen > 10) + serverLen = 10; + + sprintf(lengthBuf, "%010ld", dataLen); + StrNCpy(header, lengthBuf, 10); + + header[10] = type & 0xFF; + header[11] = version & 0xFF; + + StrNCpy(serverBuf, server, serverLen); + StrNCpy((char *) (header + 12), serverBuf, serverLen); + + header[22] = compression & 0xFF; + header[23] = encoding & 0xFF; + header[24] = '0'; /* checkSum(header + HEADER_LENGTH,dataLen); XXX the result must be ascii */ +} + +/*---------------------------------------------------------------------*/ |