about summary refs log blame commit diff stats
path: root/WWW/Library/Implementation/HTVMS_WaisProt.c
blob: 760814072e6a7cc7e73589dbeea908b12f2ef5df (plain) (tree)
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
































                                                                              
                    

                           
 
                    




















                                                                          
                                                          




















                                                                          
                                                                        













































                                                                                 
                                                                         



































































































                                                                                         
                            
                






                                  
                                            





































































































































































































                                                                                                 
                                                                      













































                                                                             
                                                            





























































































































                                                                                     
                    












                                             
                                                















































































































































                                                                                                       
                      





























                                                                                        

                                                                                











                                                                          
                       
                           
                









                                                                 
                                             

                            
                                             

















































































































































                                                                                            
                




                                                                 
                                            
























































































































                                                                              
                      
































                                                                                        

                                                                                    











                                                                          
                       
                           
                









                                                                                           
                                             

                            
                                             

































































































































































































































                                                                                                                  
                                                                












































































                                                                              
                















                                                                                           
                                            



















































































































































































                                                                                                                                                                 
                





                              
                                            











































































































                                                                                                                
                







                                               
                                            


















































































































                                                                                                            
                







                                                   
                                            






































                                                                                                    
                                
             
                     




















                                                                          
                                         
             
                     























































                                                                                 
                                                               





















































































































                                                                                
                                                               













































































































































































































































































































































                                                                                                        
/*							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;

/*----------------------------------------------------------------------*/

static unsigned long userInfoTagSize PARAMS((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 PARAMS((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 PARAMS((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;
  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(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 PARAMS((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 PARAMS((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 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 = (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 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(header,buffer)
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 = (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 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(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 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(header,buffer)
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 = (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 PARAMS((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 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 = (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 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(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 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(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 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(present,buffer,len)
PresentAPDU* present GCC_UNUSED;
char* buffer;
long* len GCC_UNUSED;
{
  /* 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 GCC_UNUSED;
char* buffer;
long* len GCC_UNUSED;
{
  /* 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 PARAMS((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 PARAMS((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 */	
}              
              
/*---------------------------------------------------------------------*/