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

/* This is a simple screen user interface for making WAIS queries
 * It is the strange offspring of waisq and curses..
 *
 * -jcurran 7/91    (dedicated to Suzi, Alex, et. al.)          
 *
 */

#include <curses.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <ui.h>
#include <sockets.h>
#include <sys/types.h>
#include <netinet/in.h>
#if 0
#include <netdb.h>
#endif

#define STRINGSIZE	256

#define MAIN
#include "wais.h"

#if !defined(erasechar) && !defined(_AIX)
#define erasechar()     (_tty.sg_erase)
#define killchar()      (_tty.sg_kill)
#endif

#define	WAIS_SERVICE	"z39_50"

#define WAISSCREEN_NAME     "SWAIS"
#define WAISSCREEN_VERSION  "$Revision: 1.3 $"
#define WAISSCREEN_AUTHOR   "John Curran (jcurran@nnsc.nsf.net)"

/* SWAIS PROGRAM STATES - each decade shares common screen (0-9, 10-19, etc) */
#define	UNKNOWN	        0
#define	GETSOURCES	10
#define	GETKEYWORDS	11
#define	MAKEQUERY	12
#define	SHOWRESULTS	20
#define	LEAVEPROGRAM	90

typedef int programstate;

char* log_file_name = NULL;
FILE* logfile = NULL;
programstate state;
programstate new_state;
jmp_buf main_env;
char keywords[STRINGSIZE];
SList Selected_Sources;
char command[STRINGSIZE];
char *sdir, *cdir;

#define MAXSELECTIONS 500
#define NOSELECTION -999999
char select_line[MAXSELECTIONS][STRINGSIZE];
int  selection; /* 1 to max_selection */
int  max_selection; /* limit on selection */
int  current_page; /* 0 to needed pages; preset to -1 to force refresh */
int  page_length; /* number of selections per page */

/* routine specific selection number */
int source_selection=1;
int result_selection=1;

void screenend() {
	signal(SIGINT,SIG_IGN);
	clear();
	refresh();
	endwin();
}

void startover() {
        longjmp(main_env,1);
}

void screenstart() {
      initscr();
      cbreak();
      noecho();
      leaveok(stdscr,FALSE);
      signal(SIGINT,startover);
}

void
display_prompt(prompt)
char	*prompt;
{
  mvprintw(LINES-1,0,prompt);
  clrtoeol();
  refresh();
}

void
display_highlighted(str)
char	*str;
{
  standout(); 
  mvprintw(LINES-1,0,str);
  standend();
  printw(" ");
  clrtoeol();
  refresh();
}

char *
firstphrase(str)
char *str;
{
  char *res;
  int  n;

  res = str;
  if ((n=strcspn(str," "))!=0)
    if ((n>4) && (str[n-4]=='.')) {

    n = n - 4;
    res = s_malloc(n+1);
    strncpy(res,str,n);
  }
  return res;
}

jmp_buf local_env;

char fpath[STRINGSIZE];
void
start_use(fd)
FILE **fd;
{
  *fd = fopen(fpath,"w+");
}
void
end_use(fd)
FILE **fd;
{
  fclose(*fd);
}

void
UseWaisDocument(q, doc)
Question q;
DocumentID doc;
{
  char message[STRINGSIZE];

 sprintf(message,"Retrieving: %s\n", firstphrase(trim_junk(doc->doc->headline)));
 PrintStatus(message);
 
 sprintf(fpath,"%s%s.src", sdir, firstphrase(trim_junk(doc->doc->headline)));
 RetrieveWaisDocument(start_use, end_use, q, doc);
 
 sprintf(message,"Adding Source: %s\n", firstphrase(trim_junk(doc->doc->headline)));
 PrintStatus(message);
 FreeSources(Sources);
 Sources = NULL;
 ReadSourceDirectory(sdir,TRUE);
 ReadSourceDirectory(cdir,TRUE);
}

jmp_buf local_env;

void screen_catchpipe(sig, code, scp, addr)
          int sig, code;
          struct sigcontext *scp;
          char *addr;
{
   longjmp(local_env,1);
}

char pipe_cmd[STRINGSIZE];

void
start_pipe(pipefd)
FILE **pipefd;
{
  screenend();
  *pipefd = popen(pipe_cmd,"w");
}
void
end_pipe(pipefd)
FILE **pipefd;
{
  pclose(*pipefd);
}

void
PipeWaisDocument(q, doc, cmd)
Question q;
DocumentID doc;
char *cmd;
{
  char message[STRINGSIZE];

 sprintf(message,"Retrieving: %s\n",firstphrase(trim_junk(doc->doc->headline)));
 PrintStatus(message);

  strcpy(pipe_cmd,cmd);
  signal(SIGPIPE,screen_catchpipe);
  if (setjmp(local_env)==0) RetrieveWaisDocument(start_pipe,end_pipe,q,doc); 
  signal(SIGPIPE,SIG_DFL);
}

void
FreeSourceList(sources)
SourceList sources;
{
 SourceList s, n;

 for (s = sources; s != NULL; s = n) {
   if (s->thisSource != NULL) {
      if (s->thisSource->filename != NULL)
	s_free(s->thisSource->filename);
      s_free(s->thisSource);
   }

   n = s->nextSource;
   s_free(s);
 }
}

void AddQuestionSource(question, sourcename)
Question question;
char *sourcename;
{
  SourceList tmp;
  SourceList s;
  SourceID sid;

  sid = (SourceID)s_malloc(sizeof(_SourceID));
  sid->filename = s_strdup(sourcename);
  s = (SourceList)s_malloc(sizeof(_SourceList));
  s->thisSource = sid;
  s->nextSource = NULL;
  tmp = question->Sources;
  question->Sources = s;
  if (tmp != NULL) 
     s->nextSource = tmp;
}

void
PrintStatus(str)
char *str;
{
   if (stdscr!=NULL) {
     display_highlighted(str);
     sleep(2);
   }
   else
     fprintf(stderr,"%s\n",str);
}

void
usage(name)
char *name;
{
    fprintf(stderr,"Usage: %s\n",name);
    fprintf(stderr,"  [-s sourcname]          select sourcename for search\n");
    fprintf(stderr,"  [-S sourcedir]          defaults to ~/wais-sources\n");
    fprintf(stderr,"  [-C common_sourcedir]   defaults to /lib/wais/wais-sources\n");
    fprintf(stderr,"  [-h]	              this help message\n");
    fprintf(stderr,"  [keywords]\n");
}

SourceID FindQuestionSource(sourcelist, sourcename) 
SourceList sourcelist;
char  *sourcename;
{
   SourceList tmpsource;
   for(tmpsource = sourcelist; tmpsource != NULL; 
                               tmpsource = tmpsource->nextSource) {
    if (tmpsource->thisSource != NULL)
       if (!strcmp(sourcename, tmpsource->thisSource->filename)) 
	 return(tmpsource->thisSource);
   }
   return(NULL);
}

Source FindSource(sourcelist, sourcename) 
SList sourcelist;
char  *sourcename;
{
   SList tmpsource;
   for(tmpsource = sourcelist; tmpsource != NULL; 
                               tmpsource = tmpsource->nextSource) {
    if (tmpsource->thisSource != NULL)
       if (!strcmp(sourcename, tmpsource->thisSource->name)) 
	 return(tmpsource->thisSource);
   }
   return(NULL);
}

void
DumpSources(sourcelist)
SList sourcelist;
{
   SList tmpsource;
  
   if (sourcelist == NULL) {
      printf("Sourcelist NULL");
      return;
   }

   printf(" Dump of Sourcelist \n");
   for(tmpsource = sourcelist; tmpsource != NULL; tmpsource = tmpsource->nextSource) {
      if (tmpsource->thisSource == NULL) 
         printf("	thisSource NULL\n");
      else
         printf("	thisSource = %s\n",tmpsource->thisSource->name);
      if (tmpsource->nextSource == NULL) 
         printf("	nextSource NULL\n");
      else
         printf("	following nextSource ->\n");
    }
}


SList
AddSource(sourcelist,currentsource)
SList sourcelist;
Source currentsource;
{
   Source s;
   SList tmpsource;
   SList last;
  
   if (sourcelist == NULL)
     sourcelist = makeSList(NULL,NULL);

   last=sourcelist;

   for(tmpsource = sourcelist; tmpsource != NULL; 
       last = tmpsource, tmpsource = tmpsource->nextSource) 
         if (tmpsource->thisSource != NULL)
	    if (!strcmp(currentsource->name, tmpsource->thisSource->name)) 
	      return(sourcelist);

   s = (Source) s_malloc(sizeof(_Source));
   s->initp = currentsource->initp;
   s->name  = s_strdup(currentsource->name);
   s->directory  = s_strdup(currentsource->directory);
     
   if (last->thisSource == NULL) 
     last->thisSource = s;
   else
     last->nextSource = makeSList(s, NULL); 
   return(sourcelist);
}

SList
DeleteSource(sourcelist,currentsource)
SList sourcelist;
Source currentsource;
{
   SList s;
   SList tmpsource;
   SList last;
  
   if (sourcelist == NULL)
     return(NULL);

   last=sourcelist;

   for(tmpsource = sourcelist; tmpsource != NULL; 
       last = tmpsource, tmpsource = tmpsource->nextSource) 
         if (tmpsource->thisSource != NULL)
	    if (!strcmp(currentsource->name, tmpsource->thisSource->name)) {
	       if(tmpsource->thisSource->name != NULL)
		 s_free (tmpsource->thisSource->name);
	       if(tmpsource->thisSource->directory != NULL)
		 s_free (tmpsource->thisSource->directory);
	       if(tmpsource->thisSource->maintainer != NULL)
		 s_free (tmpsource->thisSource->maintainer);
               
               /* save next link ptr, discard current link */
               s = tmpsource->nextSource; 
               s_free (tmpsource);

               /* return next as start if start destroyed */
               if (last==sourcelist)
                 return(s);
          
               /* Splice next into position, return start ptr */
               last->nextSource = s;
               break;
            }
   return(sourcelist);
}

char *
fixdirname(dir)
char *dir;
{
  char *res;

  if(dir[strlen(dir)-1] == '/') res = dir;
  else {
    res = s_malloc(strlen(dir)+1);
    sprintf(res,"%s/", dir);
  }
  return res;
}

int
selection_page(sel)
int sel;
{
  return(((sel-1) / page_length));
}

int
selection_line(sel)
int sel;
{
  return(((sel-1) % page_length)+2);
}
  
selection_display(new_sel)
int new_sel;
{
  int new_page;
  int new_selection;
  int sel_offset;
  int k;

  standend();
  mvprintw(selection_line(selection),0,"%s",select_line[selection]);
  
  if (new_sel==NOSELECTION) 
    new_selection = selection;
  else
    new_selection = new_sel;

  if (new_selection < 1)
      new_selection = max_selection;
  if (new_selection > max_selection) 
      new_selection = 1;

  new_page = selection_page(new_selection);
  if (current_page!=new_page) {
    sel_offset = new_page * page_length;
    for (k = 1; k <= page_length; k++) 
      if ((sel_offset+k)<=max_selection)
	mvprintw(1+k,0,"%s",select_line[sel_offset+k]);
      else
	mvprintw(1+k,0,"\n");
  }

  if (new_sel!=NOSELECTION) {
     standout();
     mvprintw(selection_line(new_selection),0,"%s",select_line[new_selection]);
     standend();
  }
  move(selection_line(new_selection),0);
  current_page = new_page;
  selection = new_selection;
  refresh();
}

void
title_display(title,status,status_count)
char *title;
char *status;
int  status_count;
{
  mvprintw(0,0,WAISSCREEN_NAME);
  standout();
  mvprintw(0,COLS/2-(strlen(title)/2),"%s", title);
  standend();
  mvprintw(0,COLS-(strlen(status)+4),"%s %2ld", status, status_count);
}

void
show_source_entry_help()
{
  int j=2;

  clear();
  title_display("Source Selection Help","Page:",1);
  mvprintw(j++,0,"%s\t\t%s","j      ","Move Down one source");
  mvprintw(j++,0,"%s\t\t%s","k      ","Move Up one source");
  mvprintw(j++,0,"%s\t\t%s","##     ","Position to source number ##");
  mvprintw(j++,0,"%s\t\t%s","<space>","Select current source");
  mvprintw(j++,0,"%s\t\t%s","v      ","View current source info");
  mvprintw(j++,0,"%s\t\t%s","<ret>  ","Perform search");
  mvprintw(j++,0,"%s\t\t%s","s      ","Select new sources");
  mvprintw(j++,0,"%s\t\t%s","w      ","Select new keywords");
  mvprintw(j++,0,"%s\t\t%s","X      ","Remove current source permanently");
  mvprintw(j++,0,"%s\t\t%s","h      ","Show this help display");
  mvprintw(j++,0,"%s\t\t%s","H      ","Display program history");
  mvprintw(j++,0,"%s\t\t%s","q      ","Leave this program");
  PrintStatus("Press any key to continue"); 
  getch();
}

void
view_source_info(sourcelist,current_s)
Source	current_s;
{
  int j=2;
  char tmp_str[STRINGSIZE];

  clear();
  title_display("Source Information","Page:",1);
  if (NULL != current_s->name) 
    mvprintw(j++,0,"%s\t%s","Name:       ",current_s->name);
  if (NULL != current_s->directory) 
    mvprintw(j++,0,"%s\t%s","Directory:  ",current_s->directory);
  if (NULL != current_s->maintainer) 
    mvprintw(j++,0,"%s\t%s","Maintainer: ",current_s->maintainer);
  if (NULL != FindSource(sourcelist,current_s->name)) 
    sprintf(tmp_str,"Yes");
  else
    sprintf(tmp_str,"No");
    mvprintw(j++,0,"%s\t%s","Selected:   ",tmp_str);
  format_source_cost(tmp_str,current_s);
    mvprintw(j++,0,"%s\t%s","Cost:       ",tmp_str);
  if (current_s->initp) 
    sprintf(tmp_str,"(Accessed)");
  else
    tmp_str[0]='\0';
  if (NULL != current_s->server) 
    mvprintw(j++,0,"%s\t%s %s","Server:     ",current_s->server,tmp_str);
  if (NULL != current_s->service)  
    mvprintw(j++,0,"%s\t%s","Service:    ",current_s->service);
  if (NULL != current_s->database) 
    mvprintw(j++,0,"%s\t%s","Database:   ",current_s->database);

  j++;
  if (NULL != current_s->description) {
    mvprintw(j++,0,"%s","Description:");
    mvprintw(j++,0,"%s",current_s->description);
  }

  PrintStatus("Press any key to continue"); 
  getch();
}

void
view_result_info(current_doc)
DocumentID current_doc;
{
  int j=2;
  char tmp_str[STRINGSIZE];

  clear();
  title_display("Result Information","Page:",1);

/*  sure.. someday these might have data. jtc
    GetServer(current_doc->doc->id,tmp_str);
    mvprintw(j++,0,"%s\t%s","Server:     ",tmp_str);
    GetDatabase(current_doc->doc->id,tmp_str);
    mvprintw(j++,0,"%s\t%s","Database:   ",tmp_str);
    GetLocalID(current_doc->doc->id,tmp_str);
    mvprintw(j++,0,"%s\t%s","Local ID:   ",tmp_str);
*/
  if (NULL != current_doc->doc->date) 
    mvprintw(j++,0,"%s\t%s","Date:       ",current_doc->doc->date);
  if (NULL != current_doc->doc->headline) 
    mvprintw(j++,0,"%s\t%s","Source:     ",current_doc->doc->source);
  if (NULL != current_doc->doc->headline) 
    mvprintw(j++,0,"%s\t%s","Headline:   ",current_doc->doc->headline);
  if (NULL != current_doc->doc->city) 
    mvprintw(j++,0,"%s\t%s","City:       ",current_doc->doc->city);
  if (NULL != current_doc->doc->stock) 
    mvprintw(j++,0,"%s\t%s","Stock:      ",current_doc->doc->stock);
  if (NULL != current_doc->doc->company) 
    mvprintw(j++,0,"%s\t%s","Company:    ",current_doc->doc->company);
  if (NULL != current_doc->doc->industry) 
    mvprintw(j++,0,"%s\t%s","Industry:   ",current_doc->doc->industry);
  if (NULL != current_doc->doc->type) 
    mvprintw(j++,0,"%s\t%s","Type:       ",current_doc->doc->type);
    mvprintw(j++,0,"%s\t%d","Score       ",current_doc->rawScore);

    mvprintw(j++,0,"%s\t%ld","# of Lines: ",current_doc->doc->numLines);
    mvprintw(j++,0,"%s\t%ld","# of Chars: ",current_doc->doc->numChars);
 
  if (NULL != current_doc->doc->sourceID->filename) 
     mvprintw(j++,0,"%s\t%s","SourceID:   ",current_doc->doc->sourceID->filename);
  switch (GetCopyrightDisposition(current_doc->doc->id)) {
  
     case (COPY_WITHOUT_RESTRICTION) : sprintf(tmp_str,"Copy Without Restriction");
                                       break;
     case (ALL_RIGHTS_RESERVED) : sprintf(tmp_str,"All Rights Reserved");
                                       break;
     case (DISTRIBUTION_RESTRICTIONS_APPLY) : sprintf(tmp_str,"Distribution Restrictions Apply");
                                       break;
     default : sprintf(tmp_str,"Unknown");
                                       break;
  }
     mvprintw(j++,0,"%s\t%s","Disposition: ",tmp_str);

  PrintStatus("Press any key to continue"); 
  getch();
}

void
show_swais_history()
{
  char versiononly[STRINGSIZE];
  int j=2;

  clear();
  title_display("SWAIS History","Page:",1);
  mvprintw(j++,0,"%s"," ");
  mvprintw(j++,0,"%s",
"The WAIS (Wide Area Information Service) system is a collection of programs");
  mvprintw(j++,0,"%s",
"which provide for convenient information distribution over wide area networks.");
  mvprintw(j++,0,"%s",
"Tools for both \"publishing\" and accessing information sources are provided.");
  mvprintw(j++,0,"%s",
"The Simple WAIS (SWAIS) interface is an basic access tool designed for those");
  mvprintw(j++,0,"%s",
"focused on data retreival and not computer operation. It provides most of the");
  mvprintw(j++,0,"%s",
"functionality of the more complicated interfaces but features a simple and");
  mvprintw(j++,0,"%s",
"potentially more natural interface.  The functionality supported includes ");
  mvprintw(j++,0,"%s",
"source selection, keyword entry, and automatic document retrieval.");
  mvprintw(j++,0,"%s",
"I hope that this tool may be of use.  Enjoy.");
  mvprintw(j++,0,"%s"," ");
  mvprintw(j++,0,"%s",
"The WAIS system is the result of a joint project between Thinking Machines,");
  mvprintw(j++,0,"%s",
"Apple Computer, and Dow Jones.  For more information on WAIS, send mail to");
  mvprintw(j++,0,"%s",
"\"wais-discussion@think.com\".  The current release of the WAIS software");
  mvprintw(j++,0,"%s",
"is available via anonymous ftp from think.com in subdirectory wais.");
  sscanf(WAISSCREEN_VERSION,"$ %*s %s",versiononly);
  mvprintw(LINES-5,0,"Simple Wais %s [built with %s]", versiononly,VERSION);
  mvprintw(LINES-3,0,"%s", WAISSCREEN_AUTHOR);

  PrintStatus("Press any key to continue"); 
  getch();
}

void
show_search_results_help()
{
  int j=2;

  clear();
  title_display("Search Results Help","Page:",1);
  mvprintw(j++,0,"%s\t\t%s","j, ^N  ","Move Down one item");
  mvprintw(j++,0,"%s\t\t%s","k, ^P  ","Move Up one item");
  mvprintw(j++,0,"%s\t\t%s","##     ","Position to item number ##");
  mvprintw(j++,0,"%s\t\t%s","<space>","Display current item");
  mvprintw(j++,0,"%s\t\t%s","<return>","Display current item");
  mvprintw(j++,0,"%s\t\t%s","|      ","Pipe current item into a unix command");
  mvprintw(j++,0,"%s\t\t%s","v      ","View current item information");
  mvprintw(j++,0,"%s\t\t%s","s      ","Specify new sources to search");
  mvprintw(j++,0,"%s\t\t%s","u      ","Use it; add it to the list of sources");
  mvprintw(j++,0,"%s\t\t%s","w      ","Make another search with new keywords");
  mvprintw(j++,0,"%s\t\t%s","h      ","Show this help display");
  mvprintw(j++,0,"%s\t\t%s","H      ","Display program history");
  mvprintw(j++,0,"%s\t\t%s","q      ","Leave this program");
  PrintStatus("Press any key to continue"); 
  getch();
}

int
get_input_string(str)
char str[STRINGSIZE];
{
      int  startcol, startline;
      int  pos;
      char ch;

      getyx(stdscr, startline, startcol);
      addstr(str);
      clrtoeol();
      refresh();

      pos = strlen(str);
      while(TRUE) {

	ch=getch();
        if (ch==killchar()) {
	    move(startline, startcol);
	    clrtoeol();
	    refresh();
	    str[0] = 0;
	    pos = 0;
        }
        else
           if ((ch==erasechar())||(ch==127)) {
	       if (pos>0) {
		 str[--pos]='\0';
		 mvaddch(startline,startcol+pos,' ');
		 move(startline,startcol+pos);
		 refresh();
	       }
               else {
                 str[0]='\0';
                 break;
               }
	   }
           else
	      if (ch=='\n')
		break;
              else 
                if (isprint(ch)){
		 str[pos++]=ch;
		 str[pos]='\0';
		 addch(ch);
		 refresh();
              }

      }
       
      display_prompt("");
      return(str[0]!='\0');
}

void
display_source_selected(sourcelist,currentsource)
SList sourcelist;
Source currentsource;
{
   if (NULL != FindSource(sourcelist,currentsource->name)) {
	 mvprintw(selection_line(selection),5,"*");
      }
   else {
      mvprintw(selection_line(selection),5," ");
      }
}

void
source_selected(sourcelist,currentsource,sel)
SList sourcelist;
Source currentsource;
{
   if (NULL != FindSource(sourcelist,currentsource->name)) {
         select_line[sel][5] = '*';
      }
   else {
      select_line[sel][5] = ' ';
      }
}

programstate
nextstate(question)
Question question;
{
  if ((question->numsources==0) || !(question->modified)) {
     return(GETSOURCES);
  }
    
  if (question->keywords[0] == 0) { 
     return(GETKEYWORDS);
  }
  return(MAKEQUERY);
}

programstate
makequery_state(question)
Question question;
{
  SList asource;

   /* rebuild question source list from Selected_Sources */
   FreeSourceList(question->Sources);
   question->Sources = NULL;
   /* build new question source list */
   for (asource = Selected_Sources; 
     asource != NULL ; asource = asource->nextSource)
       if (asource->thisSource != NULL) 
	 AddQuestionSource(question,asource->thisSource->name);
   question->numsources = listlength((List)question->Sources);

   question->RelevantDocuments = NULL;
   question->numdocs = listlength((List)question->RelevantDocuments);
   question->ResultDocuments = NULL;
   question->numresdocs = listlength((List)question->ResultDocuments);

   SearchWais(question);
   question->modified = FALSE;

   if(question->numresdocs > 0) {
     result_selection = 1;
     return(SHOWRESULTS);
   } else 
     return(nextstate(question));
}

void
source_screen(question)
Question question;
{
  int k;
  int source_count;
  Source current_s;
  SList asource;
  char fstr[STRINGSIZE];

  source_count = listlength((List)Sources);

  title_display("Source Selection","Sources:",source_count);
  mvprintw(1,2,"#            Server                          Source                      Cost");

  sprintf(fstr," %%02d:   [%%20.20s]  %%-%d.%ds %%16.16s",COLS-49,COLS-49);
  k = 1;
  for (asource = Sources; asource != NULL ; asource = asource->nextSource) {

     char tmpstr[STRINGSIZE];
     char cost_str[STRINGSIZE];

     current_s=asource->thisSource;
     tmpstr[0]='\0';
     if (strstr(current_s->name,".src")!=NULL) 
	strncat(tmpstr, current_s->name, strlen(current_s->name)-4);
     else
        strcat(tmpstr, current_s->name);
     
     format_source_cost(cost_str,current_s);
     sprintf(select_line[k], fstr, k, current_s->server, tmpstr, cost_str);
     source_selected(Selected_Sources,current_s,k);
     k++;
  }
  selection = source_selection;
  current_page = -1;
  max_selection=source_count;
  page_length= MIN(LINES-6, source_count);
  selection_display(NOSELECTION);

  mvprintw(LINES-3,0,"Keywords:");
  mvprintw(LINES-3,10,"%s",question->keywords);
  refresh();
}

programstate
source_state(question)
Question question;
{
  int k;
  int source_count;
  Source current_s;
  SList asource;
  char user_key[STRINGSIZE];
  char digit_str[STRINGSIZE];
  char message[STRINGSIZE];

  source_count = listlength((List)Sources);
  selection_display(selection);

  digit_str[0]='\0';

   while(TRUE) {

     display_prompt("<space> selects, w for keywords, arrows move, <return> searches, q quits, or ?");
	user_key[0]=getch();
	switch(user_key[0]) {
       
	    case 'H' : show_swais_history();
                       state=UNKNOWN;
		       return(GETSOURCES);

	    case 'h' : ;
	    case '?' : show_source_entry_help();
                       state=UNKNOWN;
		       return(GETSOURCES);
	    
	    /* forgive me: hardwired arrow keys for vt100 since we're
	       not using SysV curses and bsd curses lacks key support  */
	    case '\033' : user_key[1]=getch();
			  user_key[2]=getch();
			  user_key[3]='\0';
			  if (strcmp(user_key, "\033[A")) {
			     selection_display(selection+1);
                             source_selection = selection;
                             break;
                          }

			  if (strcmp(user_key, "\033[B")) {
			     selection_display(selection-1);
                             source_selection = selection;
                             break;
                          }

                          break;

	    case ','  : k = 0;
	    case 'v'  : k = 0;
			for (asource = Sources; 
			  asource != NULL ; asource = asource->nextSource) {
			  k++;
			  if (k==selection) break;
			}
                        view_source_info(Selected_Sources,asource->thisSource); 
                        state=UNKNOWN;
		        return(GETSOURCES);

            case 'X'  : ;
	    case '-'  : k = 0;
			for (asource = Sources; 
			  asource != NULL ; asource = asource->nextSource) {
			  k++;
			  if (k==selection) break;
			}
                        sprintf(fpath,"%s%s.src", sdir, firstphrase(trim_junk(asource->thisSource->name)));
                        sprintf(message,"Removing Source: %s\n", firstphrase(trim_junk(asource->thisSource->name)));
			PrintStatus(message);

                        if (unlink(fpath)==0) {  
			   if (FindSource(Selected_Sources,asource->thisSource->name)) {
		   
			      Selected_Sources = DeleteSource(Selected_Sources,asource->thisSource);
			      question->numsources--;
			      question->modified=TRUE;
			   }
			   FreeSources(Sources);
			   Sources = NULL;
			   ReadSourceDirectory(sdir,TRUE);
			   ReadSourceDirectory(cdir,TRUE);
			   state=UNKNOWN;
			   return(GETSOURCES);
                       }
                       PrintStatus("Unable to remove common sources");
                       break;

            case '.'  : ;
	    case ' '  : k = 0;
			for (asource = Sources; 
			  asource != NULL ; asource = asource->nextSource) {
			  k++;
			  if (k==selection) break;
			}
			if (FindSource(Selected_Sources,asource->thisSource->name)) {
		
			   Selected_Sources = DeleteSource(Selected_Sources,asource->thisSource);
                           question->numsources--;
			   question->modified=TRUE;
                        }
			else {
			   Selected_Sources = AddSource(Selected_Sources,asource->thisSource);
                           question->numsources++;
			   question->modified=TRUE;
                        }
			display_source_selected(Selected_Sources,asource->thisSource);
			source_selected(Selected_Sources,asource->thisSource,selection);
			selection_display(selection);
			break;

            case 14   : ;
	    case 'j'  : selection_display(selection+1);
                        source_selection = selection;
			break;

            case 16   : ;
	    case 'k'  : selection_display(selection-1);
                        source_selection = selection;
			break;
	    case 'q'  : return(LEAVEPROGRAM);
	    case 's'  : return(GETSOURCES);
	    case 10   : k = 0;
			for (asource = Sources; 
			  asource != NULL ; asource = asource->nextSource) {
			  k++;
			  if (k==selection) break;
			}
			if (NULL == FindSource(Selected_Sources,asource->thisSource->name)) {
		
			   Selected_Sources = AddSource(Selected_Sources,asource->thisSource);
                           question->numsources++;
			   question->modified=TRUE;
                        }
			display_source_selected(Selected_Sources,asource->thisSource);
			source_selected(Selected_Sources,asource->thisSource,selection);
			selection_display(selection);
                        return(nextstate(question)); 
	    case 'w'  : return(GETKEYWORDS);
	    case '\f' : wrefresh(curscr);

         default :   if (isdigit(user_key[0])) {
                       if (digit_str[0]=='\0') 
                         digit_str[0] = user_key[0]; 
                       else {
                         digit_str[1] = user_key[0];
                         digit_str[2] = '\0';
                         selection_display(atoi(digit_str));
                         source_selection = selection;
                         digit_str[0] = '\0';
                       }
                }
	 }
   }
}


void
pipe_command(question)
Question question;
{
  display_prompt("Enter the command to be executed on this item; ^C to cancel");
  standout();
  mvprintw(LINES-3,0,"Command:");
  standend();
  move(LINES-3,9);
  if (get_input_string(command)) {
     standend();
     mvprintw(LINES-3,0,"Command:");
     refresh();
     PipeWaisDocument(question, findDoc(question->ResultDocuments,selection-1),command);
     cbreak();
     noecho();
     PrintStatus("Press any key to continue"); 
     getch();
     screenstart();
  }
  standend();
  mvprintw(LINES-3,0,"\n");
  display_prompt("");
  refresh();
}

keyword_state(question)
Question question;
{
  int k;
  char user_key[STRINGSIZE];
  char digit_str[STRINGSIZE];

  display_prompt("Enter keywords with spaces between them; <return> to search; ^C to cancel");
  standout();
  mvprintw(LINES-3,0,"Keywords:");
  standend();
  move(LINES-3,10);
  strcpy(keywords,question->keywords);
  if (get_input_string(keywords)) {
     standend();
     mvprintw(LINES-3,0,"Keywords:");
     refresh();
     strcpy(question->keywords,keywords);
     question->modified = TRUE;
     return(nextstate(question));
  }
  standend();
  mvprintw(LINES-3,0,"Keywords:");
  mvprintw(LINES-3,10,"%s",question->keywords);
  refresh();
  return(GETSOURCES);
}

void
result_screen(question)
Question question;
{
  DocList doclist;
  DocumentID current_doc;
  int k;
  int doc_count;
  char fstr[STRINGSIZE];
  char *sourcename;

  doc_count = question->numresdocs;

  title_display("Search Results","Items:", doc_count);
  mvprintw(1,2,"#   Score      Source                       Title                       Lines");
  sprintf(fstr," %%02d: [%%4d] (%%15.15s)  %%-%d.%ds %%5ld\n",COLS-38,COLS-38);
  k = 1;
  for ( doclist = question->ResultDocuments; doclist != NULL ;
	  doclist = doclist->nextDoc) {

     current_doc = doclist->thisDoc;
     if (current_doc != NULL) {
       sourcename = firstphrase(trim_junk(current_doc->doc->source));
       if (NULL != strrchr(sourcename,'/')) {
          sourcename = strrchr(sourcename,'/');
          sourcename++;
       }

       sprintf(select_line[k],fstr, k, current_doc->rawScore, sourcename,
	       firstphrase(trim_junk(current_doc->doc->headline)),
               current_doc->doc->numLines);
      k++;
     }
   }
   selection = result_selection;
   current_page = -1;
   max_selection=doc_count;
   page_length= MIN(LINES-6, doc_count);
   selection_display(NOSELECTION);
}

programstate
result_state(question)
Question question;
{
  DocList doclist;
  DocumentID current_doc;
  int k;
  int doc_count;
  char user_key[STRINGSIZE];
  char digit_str[STRINGSIZE];
  char *sourcename;

  doc_count = question->numresdocs;
  selection_display(selection);

   digit_str[0]='\0';
   while(TRUE) {
       display_prompt("<space> selects, arrows move, w for keywords, s for sources, ? for help");
     user_key[0]=getch();
     switch(user_key[0]) {
    
	 case 'H' : show_swais_history();
                    state=UNKNOWN;
	            return(SHOWRESULTS);

         case 'h' : ;
	 case '?' : show_search_results_help();
                    state=UNKNOWN;
	            return(SHOWRESULTS);
         
         /* forgive me: hardwired arrow keys for vt100 since we're
            not using SysV curses and bsd curses lacks key support  */
         case '\033' : user_key[1]=getch();
                       user_key[2]=getch();
                       user_key[3]='\0';
                       if (strcmp(user_key, "\033[A")) {
                          selection_display(selection+1);
                          result_selection = selection;
                          break;
                       }
                       if (strcmp(user_key, "\033[B")) {
                          selection_display(selection-1);
                          result_selection = selection;
                          break;
                       }

	 case ','  : ;
	 case 'v'  : view_result_info(findDoc(question->ResultDocuments,selection-1));
                     state=UNKNOWN;
                     return(SHOWRESULTS);

	 case 'u'  : UseWaisDocument(question, findDoc(question->ResultDocuments,selection-1));
                     break;

	 case 10   : ;
	 case ' '  : PipeWaisDocument(question, 
findDoc(question->ResultDocuments,selection-1), "${PAGER-more}");
	   	     cbreak();
	   	     noecho();
		     PrintStatus("Press any key to continue"); 
	   	     getch();
                     screenstart();
                     state=UNKNOWN;
		     return(SHOWRESULTS);
         case '|'  : ;
	 case 'c'  : pipe_command(question);
                     state=UNKNOWN;
	             return(SHOWRESULTS);

         case 14   : ;
         case 'j'  : selection_display(selection+1); 
                     result_selection = selection;
                     break;
         case 16   : ;
         case 'k'  : selection_display(selection-1); 
                     result_selection = selection;
                     break;
	 case 'q'  : return(LEAVEPROGRAM);
	 case 's'  : return(GETSOURCES);
	 case 'w'  : return(GETKEYWORDS);
                    
	 case '\f' : wrefresh(curscr);
 
         default :   if (isdigit(user_key[0])) {
                       if (digit_str[0]=='\0') 
                         digit_str[0] = user_key[0]; 
                       else {
                         digit_str[1] = user_key[0];
                         digit_str[2] = NULL;
                         selection_display(atoi(digit_str));
                         result_selection = selection;
                         digit_str[0] = NULL;
                       }
                     }

      }
   } 
}

void
main(argc, argv)
int argc;
char **argv;
{
  Question question;
  SList asource;
  char msg[STRINGSIZE];
  char *getenv();
  char sourcename[STRINGSIZE];
  int i;
  long count;

  command[0]='\0';

  sdir = cdir = NULL;
  keywords[0] = 0;
  sourcename[0] = 0;

  i = 1;
  for(; i < argc; i++) {
    if (*argv[i] == '-') {
      argv[i]++;
      switch (*argv[i]) {
      case 'C':
	i++;
	if(i >= argc) {
	  fprintf(stderr, "Too few arguments: common source directory missing.\n");
	  exit(1);
	  }
	cdir = argv[i];
	break;
      case 'S':
	i++;
	if(i >= argc) {
	  fprintf(stderr, "Too few arguments: user source directory missing.\n");
	  exit(1);
	  }
	sdir = argv[i];
	break;
      case 's':
	i++;
	if(i >= argc) {
	  fprintf(stderr, "Too few arguments: source name missing.\n");
	  exit(1);
	  }
        sprintf(sourcename,"%s.src", argv[i]);
	break;
      case 'h':
	usage(argv[0]);
	exit(0);
	break;
      default:
	fprintf(stderr, "Unknown option: %s.\n", argv[i]);
	exit(1);
      }
    }
    else {
      if((strlen(keywords) + strlen(argv[i]) + 1) < STRINGSIZE) {
	strcat(keywords, argv[i]);
	strcat(keywords, " ");
      }
    }
  }
      
   if(sdir == NULL) {
      if((sdir = getenv("WAISSOURCEDIR")) == NULL) {
	 sprintf(msg, "%s/wais-sources", getenv("HOME"));
	 sdir = s_strdup(msg);
      }
   }
   sdir = fixdirname(sdir);
   ReadSourceDirectory(sdir, TRUE);

   if(cdir == NULL) {
      if((cdir = getenv("WAISCOMMONSOURCEDIR")) == NULL) {
	 strcpy(msg, "/lib/wais/wais-sources");
	 cdir = s_strdup(msg);
      }
   }
   cdir = fixdirname(cdir);
   ReadSourceDirectory(cdir, TRUE);

   if (NumSources == 0) {
      PrintStatus("Error: Unable to find any WAIS information sources.");
      exit(-1);
   }

   question = (Question)s_malloc(sizeof(_Question));
   question->numsources=0;
   question->modified=FALSE;

   screenstart();

   state = UNKNOWN;

   if (sourcename[0] != 0)
     for(asource = Sources; asource != NULL; asource = asource->nextSource)  
       if (!strcmp(sourcename, asource->thisSource->name)) {
	 Selected_Sources = AddSource(Selected_Sources, asource->thisSource);
	 question->numsources++;
         question->modified=TRUE;
       }

   if (keywords[0]!='\0') {
     strcpy(question->keywords,keywords);
     question->modified = TRUE;
   }

   new_state = nextstate(question);

   if (setjmp(main_env)!=0) {
      state=UNKNOWN;
      new_state=GETSOURCES;
   }

   while (TRUE) {
   
      if (new_state!=state) {
        if ((new_state / 10) != (state / 10)) {
          clear();
          switch (new_state) {
	     case GETSOURCES: ;
	     case GETKEYWORDS: ;
	     case MAKEQUERY: source_screen(question); break;
	     case SHOWRESULTS: result_screen(question); break;
	     case LEAVEPROGRAM: break; 
          }
        }
        else {
	   selection_display(NOSELECTION);
	   display_prompt("");
        }
        state = new_state;
      }

      switch (state) {

	case MAKEQUERY: 
	   new_state = makequery_state(question);
	   break;

	case GETKEYWORDS: ;
	   new_state = keyword_state(question);
	   break;

	case GETSOURCES: 
	   new_state = source_state(question);
	   break;

	case SHOWRESULTS:
	   new_state = result_state(question);
	   break;

	case LEAVEPROGRAM:
	   screenend();
	   exit(0);
      }
    }
}
