about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation/HTVMS_WaisProt.c
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>1996-09-02 19:39:24 -0400
committerThomas E. Dickey <dickey@invisible-island.net>1996-09-02 19:39:24 -0400
commite087f6d44e87f489fcb3056e86319ebba4218156 (patch)
treed045b58011bfbbf5186d34c4fed9e0dedb363275 /WWW/Library/Implementation/HTVMS_WaisProt.c
downloadlynx-snapshots-e087f6d44e87f489fcb3056e86319ebba4218156.tar.gz
snapshot of project "lynx", label v2_6
Diffstat (limited to 'WWW/Library/Implementation/HTVMS_WaisProt.c')
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisProt.c2501
1 files changed, 2501 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.c b/WWW/Library/Implementation/HTVMS_WaisProt.c
new file mode 100644
index 00000000..ee3a51c0
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisProt.c
@@ -0,0 +1,2501 @@
+/*							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 "tcp.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;
+
+/*----------------------------------------------------------------------*/
+
+static unsigned long userInfoTagSize _AP((data_tag tag,
+					  unsigned long length));
+
+static unsigned long
+userInfoTagSize(tag,length)
+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 _AP((data_tag tag,long infoSize,	
+				      long estHeaderSize,char* buffer,
+				      long* len));
+
+static char*
+writeUserInfoHeader(tag,infoSize,estHeaderSize,buffer,len)
+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 _AP((data_tag* tag,unsigned long* num,
+				     char* buffer));
+
+static char*
+readUserInfoHeader(tag,num,buffer)
+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(chunkCode,
+		     chunkIDLen,
+		     chunkMarker,
+		     highlightMarker,
+		     deHighlightMarker,
+		     newLineChars)
+long chunkCode;
+long chunkIDLen;
+char* chunkMarker;
+char* highlightMarker;
+char* deHighlightMarker;
+char* newLineChars;
+/* create a WAIS init response object */
+{
+  WAISInitResponse* init = (WAISInitResponse*)s_malloc((size_t)sizeof(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(init)
+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(init,buffer,len)
+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(info,buffer)
+void** info;
+char* buffer;
+/* read an init response object */
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  long chunkCode,chunkIDLen;
+  char* chunkMarker = NULL;
+  char* highlightMarker = NULL;
+  char* deHighlightMarker = NULL;
+  char* newLineChars = NULL;
+  
+  chunkCode = chunkIDLen = UNUSED;
+  
+  buf = readUserInfoHeader(&tag,&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(seedWords,
+	       docs,
+	       textList,
+	       dateFactor,
+	       beginDateRange,
+	       endDateRange,
+	       maxDocsRetrieved)
+char* seedWords;
+DocObj** docs;
+char** textList;
+long dateFactor;
+char* beginDateRange;
+char* endDateRange;
+long maxDocsRetrieved;
+
+/* create a type 3 query object */
+{ 
+  WAISSearch* query = (WAISSearch*)s_malloc((size_t)sizeof(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(query)
+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(docID,type)
+any* docID;
+char* type;
+
+/* construct a document object using byte chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj));
+  doc->DocumentID = docID;		/* not copied! */
+  doc->Type = type;		/* not copied! */
+  doc->ChunkCode = CT_document;
+  return(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj* 
+makeDocObjUsingLines(docID,type,start,end)
+any* docID;
+char* type;
+long start;
+long end;
+
+/* construct a document object using line chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(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(docID,type,start,end)
+any* docID;
+char* type;
+long start;
+long end;
+
+/* construct a document object using byte chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(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(docID,type,start,end)
+any* docID;
+char* type;
+any* start;
+any* end;
+
+/* construct a document object using byte chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(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(doc)
+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 _AP((DocObj* doc,char* buffer,long* len));
+
+static char*
+writeDocObj(doc,buffer,len)
+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 _AP((DocObj** doc,char* buffer));
+
+static char*
+readDocObj(doc,buffer)
+DocObj** doc;
+char* buffer;
+
+/* read whatever we have about the new document */
+{
+  char* buf = buffer;
+  data_tag tag;
+  
+  *doc = (DocObj*)s_malloc((size_t)sizeof(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:
+	    readParagraphs = true;
+	    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(query,buffer,len)
+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(info,buffer)
+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 tag;
+      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(&tag,&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 = (DocObj**)s_malloc((size_t)sizeof(DocObj*) * 2);
+		  }
+		else		/* grow the doc list */
+		  { docIDs = (DocObj**)s_realloc((char*)docIDs,(size_t)(sizeof(DocObj*) * (docs + 2)));
+		  }
+		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(docID,
+		       versionNumber,
+		       score,
+		       bestMatch,
+		       docLen,
+		       lines,
+		       types,
+		       source,
+		       date,
+		       headline,
+		       originCity)
+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 = 
+    (WAISDocumentHeader*)s_malloc((size_t)sizeof(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(header)
+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(header,buffer,len)
+WAISDocumentHeader* header;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentHeaderGroup ,
+					     DefWAISDocHeaderSize);
+  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);
+  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 */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentHeaderGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readWAISDocumentHeader(header,buffer)
+WAISDocumentHeader** header;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  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(&tag,&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;
+	  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 = (char**)s_realloc(types,(size_t)(sizeof(char*) * (numTypes + 2)));
+		  types[numTypes++] = type;
+		  types[numTypes] = NULL;
+		  size -= (buf - originalBuf);
+		}
+	    }
+	  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(docID,
+			    versionNumber,
+			    score,
+			    bestMatch,
+			    docLen,
+			    lines)
+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 = 
+    (WAISDocumentShortHeader*)s_malloc((size_t)sizeof(WAISDocumentShortHeader));
+
+  header->DocumentID = docID;
+  header->VersionNumber = versionNumber;
+  header->Score = score;
+  header->BestMatch = bestMatch;
+  header->DocumentLength = docLen;
+  header->Lines = lines;
+  
+  return(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeWAISDocumentShortHeader(header)
+WAISDocumentShortHeader* header;
+{
+  freeAny(header->DocumentID);
+  s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeWAISDocumentShortHeader(header,buffer,len)
+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(header,buffer)
+WAISDocumentShortHeader** header;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID = NULL;
+  long versionNumber,score,bestMatch,docLength,lines;
+  
+  versionNumber = score = bestMatch = docLength = lines = UNUSED;
+  
+  buf = readUserInfoHeader(&tag,&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(docID,
+			   versionNumber,
+			   score,
+			   bestMatch,
+			   docLen,
+			   lines,
+			   types,
+			   source,
+			   date,
+			   headline,
+			   originCity,
+			   stockCodes,
+			   companyCodes,
+			   industryCodes)
+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 = 
+    (WAISDocumentLongHeader*)s_malloc((size_t)sizeof(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(header)
+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(header,buffer,len)
+WAISDocumentLongHeader* header;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentLongHeaderGroup ,
+					     DefWAISLongHeaderSize);
+  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);
+  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 */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentLongHeaderGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readWAISDocumentLongHeader(header,buffer)
+WAISDocumentLongHeader** header;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  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(&tag,&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;
+	  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 = (char**)s_realloc(types,(size_t)(sizeof(char*) * (numTypes + 2)));
+		  types[numTypes++] = type;
+		  types[numTypes] = NULL;
+		  size -= (buf - originalBuf);
+		}
+	    }
+	  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(seedWordsUsed,
+		       docHeaders,
+		       shortHeaders,
+		       longHeaders,
+		       text,
+		       headlines,
+		       codes,
+		       diagnostics)
+char* seedWordsUsed;
+WAISDocumentHeader** docHeaders;
+WAISDocumentShortHeader** shortHeaders;
+WAISDocumentLongHeader** longHeaders;
+WAISDocumentText** text;
+WAISDocumentHeadlines** headlines;
+WAISDocumentCodes** codes;
+diagnosticRecord** diagnostics;
+{
+  WAISSearchResponse* response = (WAISSearchResponse*)s_malloc((size_t)sizeof(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(response)
+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(query,buffer,len)
+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 _AP((char* buf,char* seedWordsUsed,
+			       WAISDocumentHeader** docHeaders,
+			       WAISDocumentShortHeader** shortHeaders,
+			       WAISDocumentLongHeader** longHeaders,
+			       WAISDocumentText** text,
+			       WAISDocumentHeadlines** headlines,
+			       WAISDocumentCodes** codes,
+			       diagnosticRecord**diags));
+
+static void
+cleanUpWaisSearchResponse (buf,seedWordsUsed,docHeaders,shortHeaders,
+			   longHeaders,text,headlines,codes,diags)
+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(info,buffer)
+void** info;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  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(&tag,&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 = (diagnosticRecord**)s_malloc((size_t)sizeof(diagnosticRecord*) * 2);
+      	   }
+      	  else /* grow the diag list */
+      	   { diags = (diagnosticRecord**)s_realloc((char*)diags,(size_t)(sizeof(diagnosticRecord*) * (numDiags + 2)));
+      	   }
+      	  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 = (WAISDocumentHeader**)s_malloc((size_t)sizeof(WAISDocumentHeader*) * 2);
+  		   }
+  		  else /* grow the doc list */
+  		   { docHeaders = (WAISDocumentHeader**)s_realloc((char*)docHeaders,(size_t)(sizeof(WAISDocumentHeader*) * (numDocHeaders + 2)));
+  		   }
+  		  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 = (WAISDocumentShortHeader**)s_malloc((size_t)sizeof(WAISDocumentShortHeader*) * 2);
+  		   }
+  		  else /* grow the doc list */
+  		   { shortHeaders = (WAISDocumentShortHeader**)s_realloc((char*)shortHeaders,(size_t)(sizeof(WAISDocumentShortHeader*) * (numShortHeaders + 2)));
+  		   }
+  		  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 = (WAISDocumentLongHeader**)s_malloc((size_t)sizeof(WAISDocumentLongHeader*) * 2);
+  		   }
+  		  else /* grow the doc list */
+  		   { longHeaders = (WAISDocumentLongHeader**)s_realloc((char*)longHeaders,(size_t)(sizeof(WAISDocumentLongHeader*) * (numLongHeaders + 2)));
+  		   }
+  		  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 = (WAISDocumentText**)s_malloc((size_t)sizeof(WAISDocumentText*) * 2);
+  		   }
+  		  else /* grow the list */
+  		   { text = (WAISDocumentText**)s_realloc((char*)text,(size_t)(sizeof(WAISDocumentText*) * (numText + 2)));
+  		   }
+  		  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 = (WAISDocumentHeadlines**)s_malloc((size_t)sizeof(WAISDocumentHeadlines*) * 2);
+  		   }
+  		  else /* grow the list */
+  		   { headlines = (WAISDocumentHeadlines**)s_realloc((char*)headlines,(size_t)(sizeof(WAISDocumentHeadlines*) * (numHeadlines + 2)));
+  		   }
+  		  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 = (WAISDocumentCodes**)s_malloc((size_t)sizeof(WAISDocumentCodes*) * 2);
+  		   }
+  		  else /* grow the list */
+  		   { codes = (WAISDocumentCodes**)s_realloc((char*)codes,(size_t)(sizeof(WAISDocumentCodes*) * (numCodes + 2)));
+  		   }
+  		  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(docID,versionNumber,documentText)
+any* docID;
+long versionNumber;
+any* documentText;
+{
+  WAISDocumentText* docText = (WAISDocumentText*)s_malloc((size_t)sizeof(WAISDocumentText));
+
+  docText->DocumentID = docID;
+  docText->VersionNumber = versionNumber;
+  docText->DocumentText = documentText;
+  
+  return(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISDocumentText(docText)
+WAISDocumentText* docText;
+{
+  freeAny(docText->DocumentID);
+  freeAny(docText->DocumentText);
+  s_free(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeWAISDocumentText(docText,buffer,len)
+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(docText,buffer)
+WAISDocumentText** docText;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any *docID,*documentText;
+  long versionNumber;
+  
+  docID = documentText = NULL;
+  versionNumber = UNUSED;
+  
+  buf = readUserInfoHeader(&tag,&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(docID,
+			  versionNumber,
+			  source,
+			  date,
+			  headline,
+			  originCity)
+any* docID;
+long versionNumber;
+char* source;
+char* date;
+char* headline;
+char* originCity;
+{
+  WAISDocumentHeadlines* docHeadline =
+    (WAISDocumentHeadlines*)s_malloc((size_t)sizeof(WAISDocumentHeadlines));
+
+  docHeadline->DocumentID = docID;
+  docHeadline->VersionNumber = versionNumber;
+  docHeadline->Source = source;
+  docHeadline->Date = date;
+  docHeadline->Headline = headline;
+  docHeadline->OriginCity = originCity;
+  
+  return(docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISDocumentHeadlines(docHeadline)
+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(docHeadline,buffer,len)
+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(docHeadline,buffer)
+WAISDocumentHeadlines** docHeadline;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID;
+  long versionNumber;
+  char *source,*date,*headline,*originCity;
+  
+  docID = NULL;
+  versionNumber = UNUSED;
+  source = date = headline = originCity = NULL;
+  
+  buf = readUserInfoHeader(&tag,&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(docID,
+		      versionNumber,
+		      stockCodes,
+		      companyCodes,
+		      industryCodes)
+any* docID;
+long versionNumber;
+char* stockCodes;
+char* companyCodes;
+char* industryCodes;
+{
+  WAISDocumentCodes* docCodes = (WAISDocumentCodes*)s_malloc((size_t)sizeof(WAISDocumentCodes));
+
+  docCodes->DocumentID = docID;
+  docCodes->VersionNumber = versionNumber;
+  docCodes->StockCodes = stockCodes;
+  docCodes->CompanyCodes = companyCodes;
+  docCodes->IndustryCodes = industryCodes;
+  
+  return(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISDocumentCodes(docCodes)
+WAISDocumentCodes* docCodes;
+{
+  freeAny(docCodes->DocumentID);
+  s_free(docCodes->StockCodes);
+  s_free(docCodes->CompanyCodes);
+  s_free(docCodes->IndustryCodes);
+  s_free(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeWAISDocumentCodes(docCodes,buffer,len)
+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(docCodes,buffer)
+WAISDocumentCodes** docCodes;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID;
+  long versionNumber;
+  char *stockCodes,*companyCodes,*industryCodes;
+  
+  docID = NULL;
+  versionNumber = UNUSED;
+  stockCodes = companyCodes = industryCodes = NULL;
+  
+  buf = readUserInfoHeader(&tag,&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(present,buffer,len)
+PresentAPDU* present;
+char* buffer;
+long* len;
+{
+  /* The WAIS protocol doesn't use present info */
+  return(buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readPresentInfo(info,buffer)
+void** info;
+char* buffer;
+{
+  /* The WAIS protocol doesn't use present info */
+  *info = NULL;
+  return(buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writePresentResponseInfo(response,buffer,len)
+PresentResponseAPDU* response;
+char* buffer;
+long* len;
+{
+  /* The WAIS protocol doesn't use presentResponse info */
+  return(buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readPresentResponseInfo(info,buffer)
+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 _AP((DocObj** docs));
+   
+static query_term**
+makeWAISQueryTerms(docs)
+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 = (query_term**)s_realloc((char*)terms,
+					  (size_t)(sizeof(query_term*) * 
+						   (numTerms + 3 + 1)));
+	  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 = (query_term**)s_realloc((char*)terms,
+						(size_t)(sizeof(query_term*) * 
+							 (numTerms + 7 + 1)));
+
+		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 = (query_term**)s_realloc((char*)terms,
+					  (size_t)(sizeof(query_term*) * 
+						   (numTerms + 1 + 1)));
+	  terms[numTerms++] = makeOperatorTerm(OR);
+	  terms[numTerms] = NULL;
+	}
+    }
+
+  return(terms);
+}
+
+/*----------------------------------------------------------------------*/
+
+static DocObj** makeWAISQueryDocs _AP((query_term** terms));
+
+static DocObj** 
+makeWAISQueryDocs(terms)
+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 = (DocObj**)s_malloc((size_t)(sizeof(DocObj*) * 1));
+  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 = (DocObj**)s_realloc((char*)docs,(size_t)(sizeof(DocObj*) * 
+						      (docNum + 1 + 1)));
+
+      /* 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(docs)
+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(buf)
+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(init)
+WAISInitResponse* init;
+/* free an object made with makeWAISInitResponse */
+{
+  s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISSearch(query)
+WAISSearch* query;
+/* destroy an object made with makeWAISSearch() */
+{ 
+  s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeDocObj(doc)
+DocObj* doc;
+/* free a docObj */
+{ 
+    s_free(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISDocumentHeader(header)
+WAISDocumentHeader* header;
+{ 
+    s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISDocumentShortHeader(header)
+WAISDocumentShortHeader* header;
+{ 
+  s_free(header);
+}
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISDocumentLongHeader(header)
+WAISDocumentLongHeader* header;
+{
+  s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISSearchResponse(response)
+WAISSearchResponse* response;
+{ 
+  s_free(response);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISDocumentText(docText)
+WAISDocumentText* docText;
+{ 
+  s_free(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISDocHeadlines(docHeadline)
+WAISDocumentHeadlines* docHeadline;
+{ 
+  s_free(docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISDocumentCodes(docCodes)
+WAISDocumentCodes* docCodes;
+{
+  s_free(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISTextQuery(query)
+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(msgBuffer,header_struct)
+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(header,
+		      dataLen,
+		      type,
+		      server,
+		      compression,
+		      encoding,
+		      version)
+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 */	
+}              
+              
+/*---------------------------------------------------------------------*/
+