/* 
  WIDE AREA INFORMATION SERVER SOFTWARE:
   No guarantees or restrictions.  See the readme file for the full standard
   disclaimer.

   This is part of the shell user-interface tools for the WAIS software.
   Do with it as you please.

   Version 0.82
   Sun Jun  2 1991

   jonathan@Think.COM

*/

#define _C_QUESTION

#include "wais.h"

void showDiags(d)
diagnosticRecord **d;
{
  int i;
  char msg[256];

  for (i = 0; d[i] != NULL; i++) {
    if (d[i]->ADDINFO != NULL) {
      sprintf(msg, "Code: %s, %s\n", d[i]->DIAG, d[i] ->ADDINFO);
      PrintStatus(msg);
    }
  }
}
	  
void printDiags(d)
diagnosticRecord **d;
{
  int i;

  for (i = 0; d[i] != NULL; i++)
    if (d[i]->ADDINFO != NULL)
      printf("%s\n", d[i] ->ADDINFO);
}
	  
void
write_text_record_completely(fp, record, quote_string_quotes)
FILE *fp;
WAISDocumentText *record;
boolean quote_string_quotes;
{
  long count;
  /* fprintf(fp," Text\n");
  print_any("     DocumentID:  ", record->DocumentID);
  fprintf(fp,"     VersionNumber:  %d\n", record->VersionNumber);
  */
  for(count = 0; count < record->DocumentText->size; count++){
    int ch = record->DocumentText->bytes[count];
    if(27 == ch){
      /* then we have an escape code */
      /* if the next letter is '(' or ')', then ignore two letters */
      if('(' == record->DocumentText->bytes[count + 1] ||
      ')' == record->DocumentText->bytes[count + 1])
	count += 1;             /* it is a term marker */
      else count += 4;         /* it is a paragraph marker */
    }
    else if (isprint(ch)){
      if(quote_string_quotes && ch == '"')
	putc('\\', fp);
      putc(ch, fp);
    } 
    else if (ch == '\n' || ch == '\r')
      fprintf(fp, "\n");
  }
}


/* for making searches */

DocList
build_response_list(response, source)
SearchResponseAPDU *response;
SourceID source;
{
  long i, k;
  WAISSearchResponse  *info;
  DocList last = NULL, doc, result = NULL;
  DocumentID docID;

  k = response->NumberOfRecordsReturned;

  if ( response->DatabaseDiagnosticRecords != 0 ) {
    info = (WAISSearchResponse*)response->DatabaseDiagnosticRecords;
    if ( info->DocHeaders != NULL ) {
      for(i = 0; i < k; i++) {
	if(result == NULL) {
	  doc = result = makeDocList(NULL, NULL);
	}
	else
	  doc = makeDocList(NULL, NULL);

	if(info->DocHeaders[i] != NULL ) {
	  docID = fillDocumentID(info, source, i);
	  doc->thisDoc = docID;
	}
	if(last != NULL) {
	  last->nextDoc = doc;
	}
	last = doc;
      }
    }
  }
  return result;
}

/* right now this hacks out the ^Q/S too.  I'll do better later. */

void replacecontrolM(buffer, length)
char *buffer;
long *length;
{
  char *here, *there, c;
  long i, newlength;

  here = there = buffer;
  for(newlength = 0, i = 0; i < *length; i++) {
    c = *here;
    switch (c) {
    case 0:
      *there = 0;
      *length = newlength;
      return;
    case '\r':
      *there = '\n';
      newlength++;
      here++; there++;
      break;
    case 19:
    case 17:
      here++;
      break;
    default:
      *there = *here;
      newlength++;
      here++; there++;
    }
  }
  *length = newlength;
}

#define MAXDOCS 40

void SearchWais(q)
Question q;
{
  Source source;
  SList asource;
  static long request_buffer_length;
  DocObj **Doc;
  int i;
  char *request_message, *response_message;
  char message[255];
  DocList last;
  diagnosticRecord **diag;
  char *database;
  long numdocs, result;

  request_message = (char*)q->request_message;
  response_message = (char*)q->response_message;

  /* clear the results */

  q->ResultDocuments = NULL;

  /* build DocObjs */

  Doc = (DocObj**)s_malloc((q->numdocs+1) * sizeof(char*));
  
  q->numsources = listlength((List)q->Sources);

  if (q->numsources != 0)
    numdocs = MAXDOCS/q->numsources;
  else numdocs = 0;
  {
    DocList dl;
    for(i=0, dl = q->RelevantDocuments;
	dl != NULL;
	dl = dl->nextDoc, i++)
      if(dl->thisDoc->doc != NULL)
	if(dl->thisDoc->doc->id != NULL)
	    if(dl->thisDoc->start >= 0)
	      Doc[i] =
		makeDocObjUsingLines(anyFromDocID(dl->thisDoc->doc->id),
				     (dl->thisDoc->doc->type) ?
				     dl->thisDoc->doc->type : "TEXT",
				     dl->thisDoc->start, dl->thisDoc->end);
	    else
	      Doc[i] =
		makeDocObjUsingWholeDocument(anyFromDocID(dl->thisDoc->doc->id),
					     (dl->thisDoc->doc->type) ?
					     dl->thisDoc->doc->type : "TEXT");

    Doc[i] = NULL;
  }

  /* check to see if the question has a source */
  source = NULL;

  if(q->Sources != NULL) {
    SourceList slist;

    for(slist = q->Sources;
	slist != NULL;
	slist = slist->nextSource) {
      for(asource = Sources; 
	  asource != NULL;
	  asource = asource->nextSource) {
	if (!strcmp(slist->thisSource->filename,
		    asource->thisSource->name)) {
	  source = asource->thisSource;
	  break;
	}
      }

      if (source == NULL) {
	sprintf(message, "Couldn't find source: %s.\n", slist->thisSource->filename);
	PrintStatus(message);
      }
      else {
        if (strstr(source->name,".src")!=NULL) {
	   sprintf(message, "Searching: ");
           strncat(message,source->name,strlen(source->name)-4);
        }
        else
	   sprintf(message, "Searching: %s", source->name);

	PrintStatus(message);
      
	request_buffer_length = MAX_MESSAGE_LEN;

	if(source->initp != TRUE)
	  init_for_source(source, request_message,
			  request_buffer_length,
			  response_message);

	if(source->database[0] == 0) database = NULL;
	else database = source->database;

	if(source->initp == TRUE) {
	  request_buffer_length = source->buffer_length;
	  result = 0;
	  if(NULL ==
	     generate_search_apdu(request_message + HEADER_LENGTH, 
				  &request_buffer_length, 
				  q->keywords, database, Doc, numdocs)) {
	    PrintStatus("Buffer overflow: request too large");
	  }
	  else if((result =
		   interpret_message(request_message, 
				     MAX_MESSAGE_LEN - request_buffer_length, 
				     response_message,
				     MAX_MESSAGE_LEN,
				     source->connection,
				     false /* true verbose */
				     )) == 0) {
	    PrintStatus("Warning: no information returned.  Possibly a bad connection");
	    close_source(source);
	  }
	  if (result != 0) { /* use the repsonse */
	    readSearchResponseAPDU(&q->query_response,
				   response_message + HEADER_LENGTH);

      
	    if (q->query_response != NULL)
	      if ((WAISSearchResponse *)q->query_response->DatabaseDiagnosticRecords != NULL) 
		if ((diag =
		     ((WAISSearchResponse *)q->query_response->DatabaseDiagnosticRecords)->Diagnostics) != NULL)
		  showDiags(diag);
      
	    if (q->ResultDocuments != NULL) {
	      last = findLast(q->ResultDocuments);
	      last->nextDoc = build_response_list(q->query_response,
						  slist->thisSource);
	    }
	    else
	      q->ResultDocuments =
		build_response_list(q->query_response,
				    slist->thisSource);
	  }
	}
      }
    }
    /* ok, now we've got all the documents, let's sort them */

    sort_document_list(q->ResultDocuments);

    q->numresdocs =  listlength((List)q->ResultDocuments);
    sprintf(message, "Found %d items.\n", q->numresdocs);
    PrintStatus(message);
  }
  else {
    PrintStatus("\nThis Question has no sources to search.  Please add one.");
    q->ResultDocuments = NULL;
    q->numresdocs =  listlength((List)q->ResultDocuments);
  }
}

void ViewWaisDocument(q, doc)
Question q;
DocumentID doc;
{
  static long request_length, chars_per_page;
  static long lines, size, count, chars, numChars;
  any* docany;
  WAISDocumentText *text;
  diagnosticRecord **diag;
  char *viewtext, message[255];
  char *viewbuffer;
  Source source;
  SList asource;
  char *database;
  long result;

  sprintf(message, "Getting document from server...\n");
  PrintStatus(message);

  source = NULL;

  if(doc->doc != NULL) {
    if(doc->doc->sourceID != NULL) {
      if(doc->doc->sourceID->filename != NULL) {

	char *sourcename;
	sourcename = doc->doc->sourceID->filename;

	for(asource = Sources;
	    asource != NULL;
	    asource = asource->nextSource) {
	  if (!strcmp(sourcename, asource->thisSource->name)) {
	    source = asource->thisSource;
	    break;
	  }
	}
      }
    }
  }

  if (source == NULL) {

    PrintStatus("\nCould not find Source for this document!");
    return;
  }

  if(source->database[0] == 0) database = NULL;
  else database = source->database;

  size = 0;

  lines = doc->doc->numLines;
  chars = doc->doc->numChars;
  numChars = chars+1000;	/* for slop? */

  if((viewbuffer = (char*)s_malloc(numChars)) == NULL) {
    PrintStatus("\nUnable to allocate message space.  Something is wrong.\n");
    return;
  }

  viewtext = viewbuffer;

  docany = anyFromDocID(doc->doc->id);

  if(source->initp == FALSE)
    init_for_source(source,
		    q->request_message, 
		    MAX_MESSAGE_LEN,
		    q->response_message);

  chars_per_page = source->buffer_length - HEADER_LENGTH - 1000; /* ? */

  if (chars <= 0)
    PrintStatus("\nEmpty document.\ndone.\n");
  else if (source->initp == TRUE) {
    for(count = 0; 
	count * chars_per_page < chars;
	count++) {
      /* show as we go... */
      request_length = source->buffer_length;

      if(NULL ==
	 generate_retrieval_apdu(q->request_message + HEADER_LENGTH,
				 &request_length, 
				 docany,
				 CT_byte,
				 count * chars_per_page,
				 MIN((count + 1) * chars_per_page, chars),
				 (doc->doc->type) ?
				 doc->doc->type : "TEXT",
				 database)) {
	PrintStatus("\nWarning: buffer overflow.\n");
	PrintStatus("\ndone.\n");
	return;
      }
	     
      if((result =
	  interpret_message(q->request_message, 
			    source->buffer_length - request_length, 
			    q->response_message,
			    source->buffer_length,
			    source->connection,
			    false /* true verbose */	
			    )) == 0) {
	PrintStatus("\nWarning: no information returned.  Possibly a bad connection.\n");
	close_source(source);
	PrintStatus("\ndone.\n");
	return;
      }
      if (result != 0) 
	readSearchResponseAPDU(&q->retrieval_response, 
			       q->response_message + HEADER_LENGTH);

      if (q->retrieval_response != NULL)
	if ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords != NULL) 
	  if ((diag =
	       ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Diagnostics) != NULL)
	    showDiags(diag);
    
      if(NULL == ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text) {
	PrintStatus("\ndone.\n");
	return;
      }

      text = ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text[0];
      {
	long length;
    
	length = text->DocumentText->size;
	if((doc->doc->type == NULL) || (strcmp(doc->doc->type, "TEXT") == 0)) {
	  delete_seeker_codes(text->DocumentText->bytes, &length);
	  text->DocumentText->size = length;
	  replacecontrolM(text->DocumentText->bytes, &length);
	  text->DocumentText->size = length;
	}
      }

      size+=text->DocumentText->size;
      if (size <= numChars) {
	memcpy(viewtext, text->DocumentText->bytes, text->DocumentText->size);
	viewtext+=text->DocumentText->size;
	sprintf(message, "\rReceived %d bytes from %s server\r",
		size, source->name);
	PrintStatus(message);
      }
      else {
	PrintStatus("\nBuffer overflow!\n");
	break;
      }

    }

    /* display_search_response(q->retrieval_response); the general thing */
    if(NULL == ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text){
      PrintStatus("\nNo text was returned.\ndone.\n");
    } else {
      PrintStatus("\ndone.\n");
      dumptext(stdout, viewbuffer, size);
      /* fprintf(stdout, "%s", viewbuffer);  */
    }
  }
}

void RetrieveWaisDocument(start_output, end_output, q, doc)
void (*start_output)();
void (*end_output)();
Question q;
DocumentID doc;
{
  static long request_length, chars_per_page;
  static long lines, size, count, chars, numChars;
  any* docany;
  WAISDocumentText *text;
  diagnosticRecord **diag;
  char *viewtext;
  int i;
  char *viewbuffer;
  Source source;
  SList asource;
  char *database;
  FILE *fp;
  int output_started;

  output_started = FALSE;
  source = NULL;

  if(doc->doc != NULL) {
    if(doc->doc->sourceID != NULL) {
      if(doc->doc->sourceID->filename != NULL) {

	char *sourcename;
	sourcename = doc->doc->sourceID->filename;

	for(asource = Sources;
	    asource != NULL;
	    asource = asource->nextSource) {
	  if (!strcmp(sourcename, asource->thisSource->name)) {
	    source = asource->thisSource;
	    break;
	  }
	}
      }
    }
  }

  if (source == NULL) {
    PrintStatus("Could not find Source for this document!");
    return;
  }

  if(source->database[0] == 0) database = NULL;
  else database = source->database;

  size = 0;

  lines = doc->doc->numLines;
  chars = doc->doc->numChars;
  numChars = chars + 1000; /* for slop? */

  if((viewbuffer = (char*)s_malloc(numChars)) == NULL) {
    PrintStatus("Unable to allocate message space.  Something is wrong.\n");
    return;
  }

  viewtext = viewbuffer;

  docany = anyFromDocID(doc->doc->id);

  if(source->initp == FALSE)
    init_for_source(source,
		    q->request_message, 
		    MAX_MESSAGE_LEN,
		    q->response_message);

  chars_per_page = source->buffer_length - HEADER_LENGTH - 1000; /* ? */

  if (chars <= 0)
      PrintStatus("Empty document.\n");
  else if (source->initp == TRUE) {
    for(count = 0; 
	count * chars_per_page < chars;
	count++) {
      /* show as we go... */

      request_length = source->buffer_length;

      if(0 ==
	 generate_retrieval_apdu(q->request_message + HEADER_LENGTH,
				 &request_length, 
				 docany,
				 CT_byte,
				 count * chars_per_page,
				 MIN((count + 1) * chars_per_page, chars),
				 (doc->doc->type) ?
				 doc->doc->type : "TEXT",
				 database)) {
	PrintStatus("Warning: no information returned.  Possibly a bad connection.\n");
	break;
      }
	     
      if(0 ==
	 interpret_message(q->request_message, 
			   source->buffer_length - request_length, 
			   q->response_message,
			   source->buffer_length,
			   source->connection,
			   false /* true verbose */	
			   )) {
	PrintStatus("Warning: no information returned.  Possibly a bad connection.\n");
	break;
      }

      readSearchResponseAPDU(&q->retrieval_response, 
			     q->response_message + HEADER_LENGTH);

      if (q->retrieval_response != NULL)
	if ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords != NULL) 
	  if ((diag =
	       ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Diagnostics) != NULL)
	    showDiags(diag);
    
      if(NULL == ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text) {
	break;
      }

      text = ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text[0];
      {
	long length;
    
	length = text->DocumentText->size;
	if((doc->doc->type == NULL) || (strcmp(doc->doc->type, "TEXT") == 0)) {
	  delete_seeker_codes(text->DocumentText->bytes, &length);
	  text->DocumentText->size = length;
	  replacecontrolM(text->DocumentText->bytes, &length);
	  text->DocumentText->size = length;
	}
      }

      size+=text->DocumentText->size;
      if (size <= numChars) {
	memcpy(viewtext, text->DocumentText->bytes, text->DocumentText->size);
	viewtext+=text->DocumentText->size;
      }
      else {
	PrintStatus("Buffer overflow!\n");
	break;
      }

    }

    /* display_search_response(q->retrieval_response); the general thing */
    if(NULL == ((WAISSearchResponse *)q->retrieval_response->DatabaseDiagnosticRecords)->Text){
      PrintStatus("No text was returned.\n");
    } else {
      if (output_started==FALSE) {
         start_output(&fp);
         output_started=TRUE;
         if (&fp==NULL) {
           PrintStatus("Unable to open output.");
	   return;
	}
      }
      dumptext(fp, viewbuffer, size);
    }
  }
  if (output_started==TRUE) 
    end_output(&fp);
}

dumptext(fp, buffer, size)
FILE *fp;
char *buffer;
long size;
{
  long i;

  for (i = 0; i < size; i++)
      fputc(*buffer++,fp);
  fflush(fp);
}
  

test_connection(q)
Question q;
{
  Source source;
  SList asource;
  static long request_buffer_length;
  char *request_message, *response_message;
  char message[255];
  diagnosticRecord **diag;
  char *database;

  request_message = (char*)q->request_message;
  response_message = (char*)q->response_message;

  /* check to see if the question has a source */
  source = NULL;

  if(q->Sources != NULL) {
    SourceList slist;

    for(slist = q->Sources;
	slist != NULL;
	slist = slist->nextSource) {
      for(asource = Sources;
	  asource != NULL;
	  asource = asource->nextSource) {
	if (!strcmp(slist->thisSource->filename,
		   asource->thisSource->name)) {
	  source = asource->thisSource;
	  break;
	}
      }

      if (source == NULL) {
	sprintf(message, "Couldn't find source: %s.\n", slist->thisSource->filename);
	PrintStatus(message);
      }
      else {
	printf("Source %s ", slist->thisSource->filename);
	if(source->maintainer) 
	  printf("by %s ", source->maintainer);
	if(source->server)
	  printf("at %s ", source->server);

	request_buffer_length = MAX_MESSAGE_LEN;

	if(source->initp == FALSE) {
	  freopen("/dev/null", "w", stderr);

	  if(source->initp == FALSE)
	    init_for_source(source,
			    q->request_message, 
			    request_buffer_length,
			    q->response_message);

	  request_buffer_length = source->buffer_length;

	  if(source->initp == FALSE) {
	    printf("not responding.\n");
	  }
	  else { /* now lets test the database */
	    if(source->database[0] == 0) database = NULL;
	    else database = source->database;

	    if(NULL ==
	     generate_search_apdu(request_message + HEADER_LENGTH, 
				  &request_buffer_length, 
				  "?", database, NULL, 1)) {
	      PrintStatus("Buffer overflow: request too large");
	    }
	    else if(0 ==
		    interpret_message(request_message, 
				      MAX_MESSAGE_LEN - request_buffer_length, 
				      response_message,
				      MAX_MESSAGE_LEN,
				      source->connection,
				      false /* true verbose */
				      )) {
	      PrintStatus("Warning: no information returned.  Possibly a bad connection");
	    }
	    else {
	      readSearchResponseAPDU(&q->query_response,
				     response_message + HEADER_LENGTH);

      
	      if (q->query_response != NULL)
		if ((WAISSearchResponse *)q->query_response->DatabaseDiagnosticRecords != NULL) 
		  if ((diag =
		       ((WAISSearchResponse *)q->query_response->DatabaseDiagnosticRecords)->Diagnostics) != NULL) {
		    printf("not responding:\n");
		    printDiags(diag);
		  }
		  else 
		    printf("responding.\n");
	    }
	  }
	}
      }
    }
  }
  else {
    printf("\nError: No sources to search.  Please add one.\n");
  }
}

close_source(source)
Source source;
{
  SList t;

  if (source->connection != NULL) {
    fclose(source->connection);
  }
  for(t = Sources; t != NULL; t = t->nextSource) {
    if (strcmp(t->thisSource->server, source->server) == 0 &&
	strcmp(t->thisSource->service, source->service) == 0) {
      t->thisSource->initp = FALSE;
      t->thisSource->connection = NULL;
    }
  }
}
