home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / freeWAIS-sf-1.1 / x / qcommands.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-10  |  52.8 KB  |  2,163 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    This is part of the X user-interface for the WAIS software.  Do with it
  6.    as you please.
  7.  
  8.    jonathan@Think.COM
  9. */
  10.  
  11. /* Copyright (c) CNIDR (see ../COPYRIGHT) */
  12.  
  13.  
  14. /* this file contains X specific code - it is an integral part of XWAIS */
  15. /* einet begin MAC */
  16. /* This was previously '#ifndef lint' */
  17. #ifdef RCSID    /* einet */
  18. /* einet end */
  19. static char *RCSid = "$Header: /usr/local/ls6/src+data/src/freeWAIS-sf/x/RCS/qcommands.c,v 1.8 1995/01/10 17:05:55 pfeifer Exp $";
  20. /*
  21.  * was:
  22.  * static char *RCSid = "$Header: /usr/local/ls6/src+data/src/freeWAIS-sf/x/RCS/qcommands.c,v 1.8 1995/01/10 17:05:55 pfeifer Exp $";
  23.  *
  24.  *
  25.  * $Log: qcommands.c,v $
  26.  * Revision 1.8  1995/01/10  17:05:55  pfeifer
  27.  * Bug wich causes coredump when submitting the same query more than once
  28.  * No completely salvaged
  29.  *
  30.  * Revision 1.7  1994/11/24  10:04:45  pfeifer
  31.  * XtMalloc for string was too short
  32.  *
  33.  * Revision 1.6  1994/08/05  07:25:53  pfeifer
  34.  * Release beta 04
  35.  *
  36.  * Revision 1.5  1994/03/25  10:21:02  pfeifer
  37.  * now will try to make 8-bit chars work
  38.  *
  39.  * Revision 1.4  93/07/19  16:32:27  warnock
  40.  * Added catalog patches from Wayne Allen
  41.  * 
  42.  * Revision 1.5  93/07/18  warnock
  43.  * Merged in Wayne Allen's mods for catalog widget, removed call to prep_retrieval
  44.  *
  45.  * Revision 1.4  1993/06/22  20:02:30  hardin
  46.  * Plugged a MAJOR memory leak in SetupSearch.
  47.  *
  48.  * Revision 1.3  1993/01/08  21:31:22  wa
  49.  * added call to prep_retrieval. This checks annoy and crash dates.
  50.  *
  51.  * Revision 1.2  1992/09/29  19:03:20  hardin
  52.  * Changed "#ifndef lint" to "#ifdef RCSID"
  53.  *
  54.  * Revision 1.1  1992/09/23  10:09:22  wa
  55.  * Initial revision
  56.  *
  57.  * Revision 1.28  92/06/22  13:58:29  jonathan
  58.  * Added finish_search for loadSource failure in SetupSearch.
  59.  *
  60.  * Revision 1.27  92/06/22  10:51:44  jonathan
  61.  * Removed references to myAsciiSink and myAsciiSrc.
  62.  * Added function to return seed_words_used.
  63.  *
  64.  * Revision 1.26  92/06/15  13:54:46  jonathan
  65.  * Lots of things.  Cleaned up a lot of the functions, moved things around.
  66.  * Added stuff for Xt handling of IO.  No more alarms.
  67.  *
  68.  * Revision 1.25  92/06/03  17:09:50  jonathan
  69.  * Modified calls to RebuildListWidget to position list widget appropriately.
  70.  *
  71.  * Revision 1.24  92/05/07  14:51:22  jonathan
  72.  * Changed use of setitimer to alarm.  Thanks to
  73.  * steinkel@carlisle-emh2.army.mil.
  74.  *
  75.  * Revision 1.23  92/04/30  12:22:11  jonathan
  76.  * Modified to understand "quit no save" and desensitivize it when necessary.
  77.  *
  78.  * Revision 1.22  92/04/28  15:28:24  jonathan
  79.  * Cleaned up use of scrollists.  Fixed bugs in double clicking.  Added
  80.  * ability to view selected sources and relevant documents.
  81.  *
  82.  * Revision 1.21  92/03/23  16:08:43  jonathan
  83.  * Added file requester, save as button, changed some sensitivity, that kind
  84.  * of stuff.
  85.  *
  86.  * Revision 1.20  92/03/17  14:24:17  jonathan
  87.  * Added Cursor code, timers, etc.  Really a lot of stuff.
  88.  *
  89.  * Revision 1.19  92/03/06  14:47:16  jonathan
  90.  * New and Improved source loading!
  91.  *
  92.  * Revision 1.18  92/03/02  14:34:20  jonathan
  93.  * Added -f to csh call, per blaze@think.com.
  94.  *
  95.  * Revision 1.17  92/03/01  14:02:08  jonathan
  96.  * Modified all calls to PrintStatus to use PrintStatusW for compatibility
  97.  * with ui PrintStatus.
  98.  *
  99.  * Revision 1.16  92/02/17  17:48:27  jonathan
  100.  * Moved last_doc to a global, so it can be reset on a new search.
  101.  *
  102.  *
  103.  * Revision 1.15  92/02/17  12:55:38  jonathan
  104.  * Added WCAT type to text display.  Added RCSid and $Log too.
  105.  *
  106.  *
  107.  */
  108. #endif
  109.  
  110. #define _C_QCOMMANDS
  111.  
  112. #include "xwais.h"
  113. #include "cat.h"
  114. #include <X11/Xaw/TextP.h>
  115. #include <setjmp.h>
  116.  
  117. #define WORLD_INTERVAL 100
  118. #define IN_HEADER 1
  119. #define IN_BODY 2
  120.  
  121. static jmp_buf jbuf;
  122. static XtIntervalId world_timer = NULL;
  123. static SourceList current_sourcelist;
  124. #ifdef ORIGINAL    /* einet */
  125. static DocList resdocs, last_resdocs;
  126. #else            /* einet begin 22jun93 */
  127. /* initialize these values! */
  128. static DocList resdocs = NULL, last_resdocs = NULL;
  129. #endif            /* einet end */
  130. static boolean savep = false;
  131. static Sbuff sb = NULL;
  132.  
  133. static Boolean editting_new_question,
  134.   busy = FALSE, searching = FALSE;
  135.  
  136. static int last_doc = NO_ITEM_SELECTED,
  137.   last_qdoc = NO_ITEM_SELECTED,
  138.   last_source = NO_ITEM_SELECTED,
  139.   last_qsource = NO_ITEM_SELECTED;
  140.  
  141. static XQuestion helpquestion = NULL;
  142. static Textbuff current_text;
  143.  
  144. static int s_dsb = SENSVAL, s_adb = SENSVAL, s_ddb = SENSVAL;
  145.  
  146. /* macros */
  147. #define get_selected_type() \
  148.   get_selected_item(typewindow->ListWidget)
  149.  
  150. #define get_selected_qsource(question) \
  151.   get_selected_item((question)->window->Sources->ListWidget)
  152.  
  153. #define get_selected_qdoc(question) \
  154.   get_selected_item((question)->window->RelevantDocuments->ListWidget)
  155.  
  156. #define get_selected_response(question) \
  157.   get_selected_item((question)->window->ResultDocuments->ListWidget)
  158.  
  159. #define get_question_response(questionwindow) \
  160.   get_selected_item((questionwindow)->ResultDocuments->ListWidget)
  161.  
  162.  
  163. /* forward declarations */
  164.  
  165. static void SetupSearch _AP((Question q));
  166. static void SetupRetrieve _AP((Textbuff textstruct,long page,long size));
  167.  
  168. static void HandleDoc _AP((Sbuff sb));
  169. static void DoTSaveCB _AP((Widget w,
  170.                XtPointer closure,
  171.                XtPointer call_data));
  172.  
  173. /* Private functions */
  174.  
  175. static long
  176. GetLineFromPos(text, p)
  177. char *text;
  178. XawTextPosition p;
  179. {
  180.   long i, lines;
  181.  
  182.   for(lines=0, i=0; (i < p) && (*text != 0); i++, text++)
  183.     if(*text == '\n') lines++;
  184.  
  185.   return lines;
  186. }
  187.  
  188. static XawTextPosition
  189. GetPosFromLine(text, line)
  190. char *text;
  191. long line;
  192. {
  193.   long i;
  194.   XawTextPosition pos;
  195.  
  196.   for(pos=0, i=0; (i < line) && (*text != 0); pos++, text++)
  197.     if(*text == '\n') i++;
  198.  
  199.   return pos;
  200. }
  201.  
  202. #define SetCursor(widget, cursor) \
  203.  XDefineCursor(CurDpy, XtWindow((the_Question->window)->widget), cursor)
  204.  
  205. #define unSetCursor(widget) \
  206.  XUndefineCursor(CurDpy, XtWindow((the_Question->window)->widget))
  207.  
  208. static void
  209. SetCursors(cursor)
  210. Cursor cursor;
  211. {
  212.   static Cursor xterm_cursor = NULL;
  213.  
  214.   if(xterm_cursor == NULL)
  215.     xterm_cursor = XCreateFontCursor(CurDpy, XC_xterm);
  216.  
  217.   if(cursor != NULL) {
  218.     SetCursor(keywordwid, cursor);
  219.     SetCursor(shell, cursor);
  220.     SetCursor(Sources->ListWidget, cursor);
  221.     SetCursor(RelevantDocuments->ListWidget, cursor);
  222.     SetCursor(ResultDocuments->ListWidget, cursor);
  223.     SetCursor(StatusWindow, cursor);
  224.   }
  225.   else {
  226.     SetCursor(keywordwid, xterm_cursor);
  227.     unSetCursor(shell);
  228.     unSetCursor(Sources->ListWidget);
  229.     unSetCursor(RelevantDocuments->ListWidget);
  230.     unSetCursor(ResultDocuments->ListWidget);
  231.     unSetCursor(StatusWindow);
  232.   }
  233. }
  234.  
  235. static void
  236. world(w, closure, call_data)
  237. Widget w;
  238. XtPointer closure, call_data;
  239. {
  240.   static int cursor = 0;
  241.  
  242.   SetCursors(wais_cursors[cursor]);
  243.   cursor = (cursor+1)%NUM_CURSORS;
  244.   world_timer = XtAddTimeOut(WORLD_INTERVAL, world, (Opaque) world);
  245. }
  246.  
  247. #ifdef MOTIF
  248. static void
  249. highlight_words(w)
  250. Widget w;
  251. {
  252.   long b, e, offset;
  253.   char* text = GetString(w);
  254.  
  255.   e = strlen(text);
  256.  
  257.   for (offset = 0, e = strlen(text);
  258.        (b = findKeyword(text, &e)) < 999999;
  259.        offset+=e, text+=e, e = strlen(text)) {
  260.     XmTextSetHighlight(w, b+offset, b+offset+e, XmHIGHLIGHT_SELECTED);
  261.   }
  262. }
  263. #endif
  264.  
  265. static void
  266. cleanup()
  267. {
  268.   Arg args[TWO];
  269. #ifdef MOTIF
  270.   XmString strn = XmStringCreateSimple(" View  ");
  271. #else
  272.   char* strn = " View  ";
  273. #endif
  274.  
  275.   busy = FALSE;
  276.   fuzzButtons(false);
  277.   XtSetArg(args[ZERO], NLABEL, strn);
  278.   XtSetValues(viewbutton, args, ONE);
  279. #ifdef MOTIF
  280.   highlight_words(the_Question->window->keywordwid, GetKeywordsUsed());
  281. #else
  282.   XtSetSensitive(viewbutton, False);
  283.   XtSetSensitive(saveAsButton, False);
  284.   XtCallActionProc(searchButton, "unset", NULL, NULL, 0);
  285.   XawTextDisplay(the_Question->window->keywordwid);
  286. #endif
  287.  
  288.   SetCursors(NULL);
  289.   if(world_timer != NULL) {
  290.     XtRemoveTimeOut(world_timer);
  291.     world_timer = NULL;
  292.   }
  293. }
  294.  
  295. static void
  296. truncate_document_list(dl, n)
  297. DocList dl;
  298. long n;
  299. {
  300.   while(--n > 0)
  301.     if(dl == NULL ||
  302.        (dl = dl->nextDoc) == NULL)
  303.       return;
  304.  
  305.   freeDocList(dl->nextDoc);
  306.   dl->nextDoc = NULL;
  307.   return;
  308. }
  309.  
  310. static void
  311. finish_search(sb)
  312. Sbuff sb;
  313. { /* Done with message, let's interpret it */
  314.   Question q = the_Question->q;
  315.   QuestionWindow qw = the_Question->window;
  316.   char message[255];
  317.   diagnosticRecord **diag;
  318.   WAISSearchResponse *response;
  319.  
  320.   if(sb != NULL &&
  321.      sb->buffer != NULL) {
  322.     readSearchResponseAPDU(&q->query_response,
  323.                sb->buffer + HEADER_LENGTH);
  324.     if (q->query_response != NULL)
  325.       if ((response = (WAISSearchResponse *)
  326.        q->query_response->DatabaseDiagnosticRecords) != NULL) {
  327.     if ((diag = response->Diagnostics)
  328.         != NULL)
  329.       showDiags(diag);
  330.     if (q->ResultDocuments != NULL) {
  331.       char* keys_used = NULL;
  332.       DocList last = findLast(q->ResultDocuments);
  333.  
  334.       last->nextDoc = build_response_list(q->query_response,
  335.                           current_sourcelist->thisSource,
  336.                           &keys_used);
  337.       if (keys_used != NULL) {
  338.         if (keys_used[0] != 0) {
  339.           if(q->keywords_used == NULL)
  340.         q->keywords_used = keys_used;
  341.           else {
  342.         if(strcmp(q->keywords_used, keys_used) != 0) {
  343.           q->keywords_used =
  344.             (char*)s_realloc(q->keywords_used,
  345.                      strlen(q->keywords_used) + strlen(keys_used) +1);
  346.           strcat(q->keywords_used, " ");
  347.           strcat(q->keywords_used, keys_used);
  348.         }
  349.           }
  350.           s_free(keys_used);
  351.         }
  352.       }
  353.     }
  354.     else
  355.       q->ResultDocuments =
  356.         build_response_list(q->query_response,
  357.                 current_sourcelist->thisSource,
  358.                 &q->keywords_used);
  359.     freeWAISSearchResponse(response);
  360.       }
  361.   }
  362.   else q->ResultDocuments = last_resdocs;
  363.  
  364.   sort_document_list(q->ResultDocuments);
  365.  
  366.   truncate_document_list(q->ResultDocuments, q->maxresdocs);
  367.  
  368.   current_sourcelist = current_sourcelist->nextSource;
  369.  
  370.   if(current_sourcelist == NULL) {
  371.     q->numresdocs =  listlength((List)q->ResultDocuments);
  372.     the_Question->Result_Items = buildDocumentItemList(q->ResultDocuments, TRUE);
  373.     RebuildListWidget(qw->ResultDocuments, the_Question->Result_Items, LIST_TOP);
  374.     if(sb != NULL)
  375.       PrintStatus(STATUS_INFO, STATUS_MEDIUM, FOUND_MESSAGE, q->numresdocs);
  376.     cleanup();
  377.   }
  378.   else
  379.     SetupSearch(q);
  380. }
  381.  
  382. static void
  383. finish_retrieval(sb)
  384. Sbuff sb;
  385. {
  386.   WAISDocumentText *text;
  387.   diagnosticRecord **diag;
  388.   Question q = the_Question->q;
  389.   char* viewtext;
  390.   long size, numChars, start_byte, end_byte;
  391.   WAISSearchResponse *response;
  392.  
  393.   if(sb != NULL) {
  394.     numChars = (sb->page+1) * sb->chars_per_page;
  395.  
  396.     if (sb->textstruct->text == NULL)
  397.       sb->textstruct->text = (char*)s_malloc(sb->chars_per_page);
  398.     else
  399.       sb->textstruct->text = s_realloc(sb->textstruct->text, numChars);
  400.  
  401.     if(sb->textstruct->text == NULL) {
  402.       PrintStatus(STATUS_URGENT, STATUS_HIGH, BADALLOC_MESSAGE);
  403.       return;
  404.     }
  405.  
  406.     start_byte = sb->page * sb->chars_per_page;
  407.     end_byte = (sb->doc_size < 0 ?
  408.         (sb->page + 1) * sb->chars_per_page :
  409.         MINIMUM((sb->page + 1) * sb->chars_per_page, sb->doc_size));
  410.  
  411.     readSearchResponseAPDU(&q->retrieval_response,
  412.                sb->buffer + HEADER_LENGTH);
  413.  
  414.     if (q->retrieval_response != NULL)  {
  415.       if ((response = (WAISSearchResponse *)
  416.        q->retrieval_response->DatabaseDiagnosticRecords) != NULL) {
  417.     diag = response->Diagnostics;
  418.  
  419.     if(NULL == response->Text) {
  420.       if (diag != NULL)
  421.         showDiags(diag);
  422.  
  423.       PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
  424.       HandleDoc(sb);
  425.       return;
  426.     }
  427.     else {
  428.       text = response->Text[0];
  429.       if((sb->textstruct->type == NULL) || (strcmp(sb->textstruct->type, "TEXT") == 0)) {
  430.         long length = text->DocumentText->size;;
  431.  
  432.         delete_seeker_codes(text->DocumentText->bytes, &length);
  433.         text->DocumentText->size = length;
  434.         replacecontrolM(text->DocumentText->bytes, &length);
  435.         text->DocumentText->size = length;
  436.       }
  437.  
  438.       if(text->DocumentText->size > (end_byte - start_byte))
  439.         text->DocumentText->size = (end_byte - start_byte);
  440.  
  441.       size = sb->textstruct->size;
  442.       viewtext = sb->textstruct->text+size;
  443.       size+=text->DocumentText->size;
  444.       sb->textstruct->size = size;
  445.       if (size <= numChars) { /* got less than we asked for */
  446.         memcpy(viewtext, text->DocumentText->bytes, text->DocumentText->size);
  447.         PrintStatus(STATUS_CONT, STATUS_HIGH, RECEIVE_MESSAGE,
  448.             size, sb->s->name);
  449.  
  450.         if(diag &&
  451.            diag[0] &&
  452.            diag[0]->ADDINFO != NULL &&
  453.            !strcmp(diag[0]->DIAG, D_PresentRequestOutOfRange)) {
  454.           PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
  455.           sb->textstruct->size = size;
  456.           HandleDoc(sb);
  457.           freeWAISSearchResponse(response);
  458.           return;
  459.         }
  460.         if((sb->textstruct->type != NULL) &&
  461.            (strcmp(sb->textstruct->type, "TEXT") != 0) &&
  462.            (text->DocumentText->size != sb->chars_per_page)) {
  463.           PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
  464.           sb->textstruct->size = size;
  465.           HandleDoc(sb);
  466.           freeWAISSearchResponse(response);
  467.           return;
  468.         }
  469.       }
  470.       else {
  471.         PrintStatus(STATUS_URGENT, STATUS_HIGH, BUFFOVER_MESSAGE);
  472.         sb->textstruct->size = size;
  473.         HandleDoc(sb);
  474.         freeWAISSearchResponse(response);
  475.         return;
  476.       }
  477.     }
  478.  
  479.     if (diag != NULL)
  480.       showDiags(diag);
  481.  
  482.     freeWAISSearchResponse(response);
  483.       }
  484.     }
  485.     sb->textstruct->size = size;
  486.     if (end_byte == sb->doc_size) {
  487.       PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
  488.       HandleDoc(sb);
  489.     }
  490.     else {
  491.       sb->page++;
  492.       SetupRetrieve(sb->textstruct, sb->page, sb->doc_size);
  493.     }
  494.   }
  495.   else HandleDoc(NULL);
  496. }
  497.  
  498.  
  499. static void
  500. GetData(data, fid, id)
  501. XtPointer data;
  502. int *fid;
  503. XtInputId *id;
  504. {
  505.   long nbytes;
  506.   Sbuff sb = (Sbuff)data;
  507.   char* b;
  508.   WAISMessage header;
  509.  
  510.   switch(sb->read_state) {
  511.   case IN_HEADER:
  512.     b = sb->buffer+sb->buffer_offset;
  513.     if((nbytes = read(*fid, b, (HEADER_LENGTH-sb->buffer_offset))) == -1) {
  514.       /* error! unregister XtInput */
  515.       if(sb->xid != NULL)
  516.     XtRemoveInput(sb->xid);
  517.       sb->xid = NULL;
  518.       return;
  519.     }
  520.     sb->buffer_offset+=nbytes;
  521.     if (sb->buffer_offset == HEADER_LENGTH) { /* Done with header, let's interpret it */
  522.       char length_array[11];
  523.       readWAISPacketHeader(sb->buffer, &header);
  524.       strncpy(length_array, header.msg_len, 10);
  525.       length_array[10] = '\0';
  526.       sb->read_state = IN_BODY;
  527.       sb->toRead = atol(length_array);
  528.       sb->buffer_offset = 0;
  529.     }
  530.     break;
  531.   case IN_BODY:
  532.     b = sb->buffer+sb->buffer_offset+HEADER_LENGTH;
  533.     if((nbytes = read(*fid, b, (sb->toRead - sb->buffer_offset))) == -1) {
  534.       /* error! unregister XtInput */
  535.       if(sb->xid != NULL) {
  536.     XtRemoveInput(sb->xid);
  537.     sb->xid = NULL;
  538.       }
  539.       return;
  540.     }
  541.     sb->buffer_offset+=nbytes;
  542.     if (sb->buffer_offset == sb->toRead) {
  543.       sb->read_state = IN_HEADER;
  544.       sb->toRead = HEADER_LENGTH;
  545.       sb->buffer_offset = 0;
  546.       if(sb->xid != NULL) {
  547.     XtRemoveInput(sb->xid);
  548.     sb->xid = NULL;
  549.       }
  550.       if(searching) finish_search(sb);
  551.       else finish_retrieval(sb);
  552.     }
  553.   }
  554.   return;
  555. }
  556.  
  557. static boolean
  558.   SendWaisMessage(request_message, request_length, connection)
  559. char* request_message;
  560. long request_length;
  561. FILE* connection;
  562. {
  563.   writeWAISPacketHeader(request_message,
  564.             request_length,
  565.             (long)'z', /* Z39.50 */
  566.             "wais      ", /* server name */
  567.             (long)NO_COMPRESSION, /* no compression */
  568.             (long)NO_ENCODING,(long)HEADER_VERSION);
  569.  
  570.   if( request_length + HEADER_LENGTH
  571.      != fwrite (request_message, 1L, request_length + HEADER_LENGTH, connection))
  572.     return false;
  573.  
  574.   fflush(connection);
  575.   return true;
  576. }
  577.  
  578. static void
  579.  makeSBuff(source, page)
  580. Source source;
  581. long page;
  582. {
  583.   if(sb == NULL) sb = (Sbuff) s_malloc(sizeof(_Sbuff));
  584.  
  585.   sb->s = source;
  586.   if(page == 0) {
  587.     if (sb->buffer != NULL) s_free(sb->buffer);
  588.     sb->buffer = s_malloc(source->buffer_length);
  589.   }
  590.   sb->page = page;
  591.   sb->read_state = IN_HEADER;
  592.   sb->toRead = HEADER_LENGTH;
  593.   sb->buffer_offset = 0;
  594.   if(source->connection != NULL)
  595.     sb->xid = XtAddInput(fileno(source->connection),
  596.              (XtPointer)XtInputReadMask,
  597.              (XtInputCallbackProc)GetData, (XtPointer)sb);
  598.   else sb->xid = NULL;
  599. }
  600.  
  601. static void SetupSearch(q)
  602. Question q;
  603. {
  604.   Source source;
  605.   long request_buffer_length, numdocs;
  606.   DocList dl;
  607.   DocObj **Doc;
  608.   char message[255];
  609.  
  610.   if(current_sourcelist != NULL) {
  611.     if (last_resdocs) {
  612.       freeDocList(last_resdocs);
  613.       last_resdocs = NULL;
  614.     } else {
  615.       if(q->ResultDocuments != NULL)
  616.         last_resdocs = q->ResultDocuments;
  617.       else last_resdocs = resdocs;
  618.     }
  619.     /* build DocObjs */
  620.     Doc = (DocObj**)s_malloc((q->numdocs+1) * sizeof(char*));
  621.  
  622.     for(numdocs=0, dl = q->RelevantDocuments;
  623.     dl != NULL;
  624.     dl = dl->nextDoc, numdocs++) {
  625.       if(dl->thisDoc->doc != NULL) {
  626.     char* tmptype = s_strdup((dl->thisDoc->doc->type) ?
  627.                  dl->thisDoc->doc->type[0] : "TEXT");
  628.     if(dl->thisDoc->doc->id != NULL)
  629.       if(dl->thisDoc->start >= 0)
  630.         Doc[numdocs] =
  631.           makeDocObjUsingLines(anyFromDocID(dl->thisDoc->doc->id),
  632.                    tmptype, dl->thisDoc->start, dl->thisDoc->end);
  633.       else
  634.         Doc[numdocs] =
  635.           makeDocObjUsingWholeDocument(anyFromDocID(dl->thisDoc->doc->id),
  636.                        tmptype);
  637.  
  638.       }
  639.     }
  640.     Doc[numdocs] = NULL;
  641.  
  642.     if(current_sourcelist == NULL ||
  643.        current_sourcelist->thisSource == NULL) {
  644.       current_sourcelist == NULL;
  645.     }
  646.     else {
  647.       if((source =
  648.       findsource(current_sourcelist->thisSource->filename,
  649.              the_Question->q->sourcepath))
  650.       == NULL) {
  651.     PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
  652.             current_sourcelist->thisSource->filename);
  653.     finish_search(NULL);
  654.       }
  655.       else {
  656.     PrintStatus(STATUS_INFO, STATUS_HIGH, "\n");
  657.  
  658.     if(source->initp != TRUE) {
  659.       PrintStatus(STATUS_INFO, STATUS_HIGH, INITSOURCE_MESSAGE);
  660.       init_for_source(source, q->request_message, MAX_MESSAGE_LEN,
  661.               q->response_message);
  662.     }
  663.     if(source->initp != FALSE) {
  664.       PrintStatus(STATUS_INFO, STATUS_HIGH, SEARCH_MESSAGE, source->name);
  665.       request_buffer_length = source->buffer_length;
  666.       if(NULL ==
  667.          generate_search_apdu(q->request_message + HEADER_LENGTH,
  668.                   &request_buffer_length,
  669.                   q->keywords,
  670.                   (source->database[0]?source->database:NULL),
  671.                   Doc, q->maxresdocs)) {
  672.         PrintStatus(STATUS_URGENT, STATUS_HIGH, BUFFOVER_MESSAGE);
  673.       }
  674.  
  675.       makeSBuff(source, 0);
  676.       if(source->connection != NULL) {
  677.         if(SendWaisMessage(q->request_message,
  678.                    (source->buffer_length -
  679.                 request_buffer_length),
  680.                    source->connection)) {
  681.         }
  682.         else {
  683.           PrintStatus(STATUS_URGENT, STATUS_HIGH, BADWAIS_MESSAGE);
  684.         }
  685.       }
  686.       else {
  687.         if(interpret_message(q->request_message,
  688.                  (source->buffer_length -
  689.                   request_buffer_length),
  690.                  sb->buffer,
  691.                  source->buffer_length,
  692.                  source->connection,
  693.                  false
  694.                  ) == 0) {
  695.           PrintStatus(STATUS_URGENT, STATUS_HIGH, BADCONNECT_MESSAGE);
  696.           close_source(source);
  697.         }
  698.         else {
  699.           finish_search(sb);
  700.         }
  701.       }
  702.     }
  703.     else {
  704.       PrintStatus(STATUS_INFO, STATUS_HIGH, NOSEARCH_MESSAGE, source->name);
  705.       if (sb != NULL && sb->buffer != NULL) {
  706.         s_free(sb->buffer);
  707.         sb->buffer = NULL;
  708.       }
  709.       finish_search(sb);
  710.     }
  711.       }
  712.     }
  713.     if (Doc != NULL) {
  714.       doList((void**)Doc,freeDocObj);
  715.       s_free(Doc);
  716.     }
  717.   }
  718. }
  719.  
  720. static void SetupRetrieve(textstruct, page, size)
  721. Textbuff textstruct;
  722. long page;
  723. long size;
  724. {
  725.   long request_length;
  726.   any* docany;
  727.   Source source = NULL;
  728.   DocumentID doc = textstruct->docid;
  729.   char* type = textstruct->type;
  730.  
  731.   if(doc != NULL &&
  732.      doc->doc != NULL &&
  733.      doc->doc->sourceID != NULL &&
  734.      doc->doc->sourceID->filename != NULL &&
  735.      (source = findsource(doc->doc->sourceID->filename,
  736.               the_Question->q->sourcepath))
  737.      != NULL) {
  738.  
  739.     if(page == 0)
  740.       PrintStatus(STATUS_INFO, STATUS_HIGH, "\n");
  741.  
  742.     if(source->initp == FALSE) {
  743.       PrintStatus(STATUS_INFO, STATUS_HIGH, INITSOURCE_MESSAGE);
  744.       init_for_source(source, the_Question->q->request_message, MAX_MESSAGE_LEN,
  745.               the_Question->q->response_message);
  746.  
  747.     }
  748.     if(source->initp == FALSE) {
  749.       PrintStatus(STATUS_INFO, STATUS_HIGH, NOGETDOC_MESSAGE,
  750.           textstruct->docid->doc->headline, source->name);
  751.       finish_retrieval(NULL);
  752.     }
  753.     else {
  754. /*      prep_retrieval(); */
  755.       makeSBuff(source, page);
  756.       if(page == 0) {
  757.     PrintStatus(STATUS_INFO, STATUS_HIGH, GETDOC_MESSAGE,
  758.             textstruct->docid->doc->headline, source->name);
  759.     sb->chars_per_page = source->buffer_length - HEADER_LENGTH - 1000; /* ? */
  760.     sb->textstruct = textstruct;
  761.     if(size == 0) sb->doc_size = textstruct->docid->doc->numChars;
  762.     else sb->doc_size = size;
  763.       }
  764.       request_length = source->buffer_length;
  765.       docany = anyFromDocID(doc->doc->id);
  766.  
  767.       if(NULL ==
  768.      generate_retrieval_apdu(the_Question->q->request_message + HEADER_LENGTH,
  769.                  &request_length, docany, CT_byte,
  770.                  page * sb->chars_per_page,
  771.                  (sb->doc_size < 0 ?
  772.                   (page + 1) * sb->chars_per_page :
  773.                   MINIMUM((page + 1) * sb->chars_per_page, sb->doc_size)),
  774.                  s_strdup((type ? type : "TEXT")),
  775.                  (source->database[0] ?
  776.                   source->database : NULL))) {
  777.     PrintStatus(STATUS_URGENT, STATUS_HIGH, BUFFOVER_MESSAGE);
  778.       }
  779.       else {
  780.     if(source->connection != NULL) {
  781.       if(SendWaisMessage(the_Question->q->request_message,
  782.                  (source->buffer_length -
  783.                   request_length),
  784.                  source->connection)) {
  785.       }
  786.       else {
  787.         PrintStatus(STATUS_URGENT, STATUS_HIGH, BADWAIS_MESSAGE);
  788.       }
  789.     }
  790.     else {
  791.       if (interpret_message(the_Question->q->request_message,
  792.                 (source->buffer_length -
  793.                  request_length),
  794.                 sb->buffer,
  795.                 source->buffer_length,
  796.                 source->connection,
  797.                 false)
  798.           == 0) {
  799.         PrintStatus(STATUS_URGENT, STATUS_HIGH, BADCONNECT_MESSAGE);
  800.         close_source(source);
  801.       }
  802.       else {
  803.         finish_retrieval(sb);
  804.       }
  805.     }
  806.       }
  807.       freeAny(docany);
  808.     }
  809.   }
  810.   else PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
  811.            doc->doc->sourceID->filename);
  812. }
  813.  
  814. static void
  815. SetPosition(w, shell, centerp)
  816. Widget w;
  817. Widget shell;
  818. boolean centerp;
  819. {
  820.   Arg        args[2];
  821.   Position    x, y;
  822.   Dimension    width, height;
  823.  
  824.   XtSetArg(args[ZERO], XtNwidth, &width);
  825.   XtSetArg(args[ONE], XtNheight, &height);
  826.   XtGetValues(w, args, TWO);
  827.   XtTranslateCoords(w,
  828.             (Position)(centerp?width/2:0),
  829.             (Position)(centerp?height/2:height),
  830.             &x, &y);
  831.  
  832.   XtSetArg(args[ZERO], XtNx, x);
  833.   XtSetArg(args[ONE], XtNy, y);
  834.  
  835.   XtSetValues(shell, args, TWO);
  836. }
  837.  
  838. static char *
  839. findFilter(type)
  840. char *type;
  841. {
  842.   char *p, *i1, *i2, t[MAX_FILE_NAME_LEN+1];
  843.   static char result[MAX_FILE_NAME_LEN+1];
  844.  
  845.   /* filters are of the form TYPE,FILTER;... */
  846.  
  847.   memset(result, 0, MAX_FILE_NAME_LEN+1);
  848.   p = app_resources.filters;
  849.  
  850.   while (*p != 0) {
  851.     if((i1 = (char*)strchr(p, ',')) == NULL) break;
  852.     strncpy(t, p, MINIMUM(i1-p, MAX_FILE_NAME_LEN));
  853.     if(!strncmp(t, type, MINIMUM(i1-p, MAX_FILE_NAME_LEN))) {
  854.       if((i2 = (char*)strchr(i1, ';')) != NULL) {
  855.     strncpy(result, i1+1, MINIMUM(i2-i1-1, MAX_FILE_NAME_LEN));
  856.     result[i2-i1] = 0;
  857.       }
  858.       else strcpy(result, i1+1);
  859.       return result;
  860.     }
  861.     if((p = (char*)strchr(i1, ';')) == NULL) break;
  862.     p++;
  863.   }
  864.   return NULL;
  865. }
  866.  
  867. static Boolean
  868. tryFilter(t, type, filename)
  869. Textbuff t;
  870. char *type;
  871. char *filename;
  872. {
  873.   char fname[STRINGSIZE], command[STRINGSIZE], *text, *viewer;
  874.   FILE *fp;
  875.   long i;
  876.  
  877.   if((viewer = findFilter(type)) == NULL) return FALSE;
  878.  
  879.   sprintf(fname, "%s%s",
  880.       app_resources.documentDirectory,
  881.       get_filename(t->docid->doc->headline));
  882.  
  883.   if((fp = fopen(fname, "w")) == NULL) {
  884.     PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, fname);
  885.     return;
  886.   }
  887.  
  888.   dumptext(fp, t->text, t->size);
  889.   fclose(fp);
  890.  
  891.   KillText(t);
  892.  
  893.   PrintStatus(STATUS_INFO, STATUS_HIGH, FILTER_MESSAGE,
  894.           viewer, fname);
  895.   sprintf(command, "csh -fc '%s %s;/bin/rm %s' &", viewer, fname, fname);
  896.   system(command);
  897.  
  898.   return TRUE;
  899. }
  900.  
  901. static void
  902. DoSource(t)
  903. Textbuff t;
  904. {
  905.   char f[STRINGSIZE], message[STRINGSIZE];
  906.   FILE *fp;
  907.  
  908.   sprintf(f, "/tmp/src%d", getpid());
  909.   if((fp = fopen(f, "w")) == NULL) {
  910.     PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, f);
  911.   }
  912.   else {
  913.     fprintf(fp, t->text);
  914.  
  915.     fclose(fp);
  916.     if((fp = fopen(f, "r")) == NULL) {
  917.       PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, f);
  918.     }
  919.     else {
  920.       memset(the_Source, 0, sizeof(_Source));
  921.  
  922.       ReadSource(the_Source, fp);
  923.       fclose(fp);
  924.  
  925.       if (the_Source->name != NULL) s_free(the_Source->name);
  926.       the_Source->name = s_strdup(get_filename(t->docid->doc->headline));
  927.  
  928.       unlink(f);
  929.  
  930.       KillText(t);
  931.       PopupSource(the_Source);
  932.     }
  933.   }
  934. }
  935.  
  936. static void
  937. do_other_thing(t, type)
  938. Textbuff t;
  939. char *type;
  940. {
  941.   char message[STRINGSIZE];
  942.  
  943.   if (type != NULL && type[0] != 0) {
  944.     PrintStatus(STATUS_URGENT, STATUS_HIGH, UNKNOWNTYPE_MESSAGE, type);
  945.   }
  946.  
  947.   if(savelist == NULL)
  948.     savelist = MakeSaveRequester(top);
  949.  
  950.   SetPosition(the_Question->window->shell, savereq, true);
  951.  
  952.   current_text = t;
  953.  
  954.   ReplaceText(filenamewidget, "");
  955.   ReplaceText(dirnamewidget, app_resources.documentDirectory);
  956.  
  957.   XtPopup(savereq, XtGrabNone);
  958.   SetDir(NULL, NULL, NULL);
  959.   SetReqButtons(false);
  960.  
  961.   XtRemoveAllCallbacks(savebutton, COMMANDCALLBACK);
  962.   XtAddCallback(savebutton, COMMANDCALLBACK, DoTSaveCB, t);
  963. }
  964.  
  965. static boolean
  966. GetDoc(textstruct, size)
  967. Textbuff textstruct;
  968. long size;
  969. {
  970.   busy = TRUE;
  971.   searching = FALSE;
  972.   fuzzButtons(true);
  973.  
  974.   world_timer = XtAddTimeOut(WORLD_INTERVAL, world, (Opaque) world);
  975.   SetupRetrieve(textstruct, 0, size);
  976. }
  977.  
  978. static void
  979. HandleDoc(sb)
  980. Sbuff sb;
  981. {
  982. #ifndef MOTIF
  983.   XtCallActionProc(viewbutton, "unset", NULL, NULL, 0);
  984. #endif
  985.   if(world_timer != NULL) {
  986.     XtRemoveTimeOut(world_timer);
  987.     world_timer = NULL;
  988.   }
  989.   fuzzButtons(false);
  990.   SetCursors(NULL);
  991.   busy = false;
  992.  
  993.   if(sb != NULL) {
  994.     if(sb->textstruct->size == 0) {
  995.       KillText(sb->textstruct);
  996.       PrintStatus(STATUS_URGENT, STATUS_HIGH, NODATA_MESSAGE);
  997.       return;
  998.     }
  999.  
  1000.     if(savep)
  1001.       do_other_thing(sb->textstruct, NULL);
  1002.     else if(tryFilter(sb->textstruct, sb->textstruct->type))
  1003.       return;
  1004.  
  1005.     else if (substrcmp(sb->textstruct->docid->doc->headline, 
  1006.                "Catalog for database:") ||
  1007.          substrcmp(sb->textstruct->docid->doc->headline, ">") || 
  1008.          strcmp(sb->textstruct->type,"WCAT") == 0 ) {
  1009.       Catbuff cat = build_cat(sb->textstruct->text,
  1010.                   sb->textstruct->docid->doc->sourceID);
  1011.  
  1012.       if(cat != NULL) {
  1013.     sb->textstruct->textwindow =
  1014.       MakeCatPopup(top, cat, sb->textstruct->docid->doc->headline);
  1015.     XtPopup(sb->textstruct->textwindow, XtGrabNone);
  1016.       }
  1017.     }
  1018.     else if (sb->textstruct->type == NULL ||
  1019.          substrcmp(sb->textstruct->type, "TEXT") ||
  1020.          !strcmp(sb->textstruct->type, "WCAT")) {
  1021.       Arg args[2];
  1022.  
  1023.       sb->textstruct->textwindow =
  1024.     MakeTextPopup(top, sb->textstruct, sb->textstruct->docid->doc->headline);
  1025.  
  1026.       XtSetArg(args[ZERO], XtNtype, XawAsciiString);
  1027.       XtSetArg(args[ONE], XtNstring, sb->textstruct->text);
  1028.       XtSetValues(sb->textstruct->textwindow, args, TWO);
  1029.  
  1030.       if (sb->textstruct->docid->doc->best > 0) {
  1031.     XawTextPosition pos = GetPosFromLine(sb->textstruct->text,
  1032.                          sb->textstruct->docid->doc->best);
  1033.     XawTextSetInsertionPoint(sb->textstruct->textwindow, pos);
  1034.     XtSetArg(args[ZERO], XtNdisplayPosition, pos);
  1035.     XtSetValues(sb->textstruct->textwindow, args, ONE);
  1036.       }
  1037.     }
  1038.     else if (!strcmp(sb->textstruct->type, "WSRC")) {
  1039.       DoSource(sb->textstruct);
  1040.     }
  1041.     else do_other_thing(sb->textstruct, sb->textstruct->type);
  1042.   }
  1043. }
  1044.  
  1045. static void PopupTypeMenu(w, doc)
  1046. Widget w;
  1047. DocumentID doc;
  1048. {
  1049.   char** types = doc->doc->type;
  1050.   int i;
  1051.  
  1052.   SetPosition(w, typeshell, false);
  1053.  
  1054.   for(i = 0; types[i] != NULL; i++)
  1055.     Type_items[i] = types[i];
  1056.  
  1057.   RebuildListWidget(typewindow, Type_items, LIST_TOP);
  1058.  
  1059.   XtPopup(typeshell, XtGrabExclusive);
  1060. }
  1061.  
  1062. XawTextPosition
  1063. findstring(text, string, casesensitive)
  1064. char *text, *string;
  1065. Boolean casesensitive;
  1066. {
  1067.   char *t, *t2, *t3;
  1068.  
  1069.   if (casesensitive) {
  1070.     for (t = text; *t != 0; t++) {
  1071.       if (*t == *string) {
  1072.     t2 = t;
  1073.     t3 = string;
  1074.     do {
  1075.       t2++;
  1076.       t3++;
  1077.       if(*t3 == 0) return((XawTextPosition)(t-text));
  1078.     }
  1079.     while(*t2 == *t3);
  1080.       }
  1081.     }
  1082.     return -1;
  1083.   }
  1084.   else {
  1085.     for (t = text; *t != 0; t++) {
  1086.       if (tolower(*t) == tolower(*string)) {
  1087.     t2 = t;
  1088.     t3 = string;
  1089.     do {
  1090.       t2++;
  1091.       t3++;
  1092.       if(*t3 == 0) return((XawTextPosition)(t-text));
  1093.     }
  1094.     while(tolower(*t2) == tolower(*t3));
  1095.       }
  1096.     }
  1097.     return -1;
  1098.   }
  1099. }
  1100.  
  1101. /* ARGSUSED */
  1102. void
  1103. showKeyword(w, closure, call_data)
  1104. Widget w;
  1105. XtPointer closure, call_data;
  1106. {
  1107.   Textbuff t;
  1108.   static char msg[STRINGSIZE], str[STRINGSIZE], minstr[STRINGSIZE], *keys;
  1109.   Widget tw;
  1110.   XawTextPosition cpos, minpos;
  1111.  
  1112.   if((t = findText(w)) == NULL) {
  1113.     XwaisPrintf("couldn't find text.\n");
  1114.     return;
  1115.   }
  1116.  
  1117.   tw = t->textwindow;
  1118.  
  1119.   messwidget = t->status;
  1120.   PrintStatus(STATUS_CONT, STATUS_MEDIUM, "\nSearching for next keyword...");
  1121.   PrintStatusW(msg, t->status,false);
  1122.  
  1123.   cpos = XawTextGetInsertionPoint(tw);
  1124.   minpos = findKeyword(t->text+cpos, NULL);
  1125.   if (minpos == 999999) {
  1126.     PrintStatus(STATUS_INFO, STATUS_MEDIUM, "\nCould not find any more keywords.");
  1127.   }
  1128.   else {
  1129.     Arg args[ONE];
  1130.  
  1131.     minpos += cpos+1;
  1132.     XawTextSetInsertionPoint(tw, minpos + 1);
  1133.     /*XtSetArg(args[ZERO], XtNdisplayPosition, minpos);
  1134.     XtSetValues(tw, args, ONE);*/
  1135.     PrintStatus(STATUS_CONT, STATUS_MEDIUM, "\nSearching for next keyword...done");
  1136.   }
  1137.   messwidget = the_Question->window->StatusWindow;
  1138. }
  1139.  
  1140. static void
  1141. DoTSaveCB(w, closure, call_data)
  1142. Widget w;
  1143. XtPointer closure, call_data;
  1144. {
  1145.   FILE *fp;
  1146.   char message[STRINGSIZE], filename[STRINGSIZE];
  1147.  
  1148.   Textbuff t = (Textbuff) closure;
  1149.  
  1150.   strncpy(filename, GetString(filenamewidget), STRINGSIZE);
  1151.  
  1152.   if(filename[0]==0) {
  1153.     PrintStatus(STATUS_INFO, STATUS_HIGH, ENTERFNAME_MESSAGE);
  1154.   }
  1155.   else {
  1156.     XtPopdown(savereq);
  1157.     SetReqButtons(false);
  1158.  
  1159.     if (filename[0] != '/') {
  1160.       if(t->type != NULL &&
  1161.      !strcmp(t->type, "WSRC"))
  1162.     sprintf(message, "%s%s", app_resources.userSourceDirectory, filename);
  1163.       else
  1164.     sprintf(message, "%s%s", GetString(dirnamewidget), filename);
  1165.       strcpy(filename, message);
  1166.     }
  1167.  
  1168.     if((fp = fopen(filename, "w")) == NULL) {
  1169.       PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, filename);
  1170.     }
  1171.     else {
  1172.       dumptext(fp, t->text, t->size);
  1173.       fclose(fp);
  1174.     }
  1175.     if(t->type != NULL && strcmp(t->type, "TEXT") != 0)
  1176.       KillText(t);
  1177.   }
  1178. }
  1179.  
  1180. /* Public Functions */
  1181.  
  1182. void
  1183. fuzzButtons(fuzz)
  1184. boolean fuzz;
  1185. {
  1186.   if(fuzz) {
  1187.     s_dsb = XtIsSensitive(delSourceButton);
  1188.     s_adb = XtIsSensitive(addDocButton);
  1189.     s_ddb = XtIsSensitive(delDocButton);
  1190.   }
  1191.  
  1192. #ifndef MOTIF
  1193.   if(searching) {
  1194.     XtSetSensitive(viewbutton, !fuzz);
  1195.     XtCallActionProc(searchButton, (fuzz?"set":"unset"), NULL, NULL, 0);
  1196.   }
  1197.   else {
  1198.     XtSetSensitive(viewbutton, !fuzz);
  1199.     XtCallActionProc(viewbutton, (fuzz?"set":"unset"), NULL, NULL, 0);
  1200.   }
  1201. #endif
  1202.  
  1203.   XtSetSensitive(viewbutton, !fuzz);
  1204.   XtSetSensitive(saveAsButton, !fuzz);
  1205.   XtSetSensitive(addSourceButton, !fuzz);
  1206.   XtSetSensitive(delSourceButton, (fuzz?False:s_dsb));
  1207.   XtSetSensitive(addDocButton, (fuzz?False:s_adb));
  1208.   XtSetSensitive(delDocButton, (fuzz?False:s_ddb));
  1209.  
  1210.   XtSetSensitive(abortButton, fuzz);
  1211.   XtSetSensitive(helpButton, !fuzz);
  1212.   XtSetSensitive(doneButton, !fuzz);
  1213. }
  1214.  
  1215. void
  1216. Prefs(w, closure, call_data)
  1217. Widget w;
  1218. XtPointer closure, call_data;
  1219. {
  1220.   Arg arglist[TWO];
  1221.   char maxdocstring[STRINGSIZE];
  1222.  
  1223.   if(prefpopup == NULL)
  1224.     prefpopup = MakePrefPopup(top);
  1225.  
  1226.   XtPopup(prefpopup, XtGrabNone);
  1227.   XtSetArg(arglist[ZERO], XtNtitle, "Xwais Preferences");
  1228.   XtSetArg(arglist[ONE], XtNiconName, "Xwais Preferences");
  1229.   XtSetValues(prefpopup, arglist, TWO);
  1230.   sprintf(maxdocstring, "%d", the_Question->q->maxresdocs);
  1231.   ReplaceText(maxdocwid, maxdocstring);
  1232.   ReplaceText(sourcepathwid, the_Question->q->sourcepath);
  1233.   ReplaceText(filterwid, app_resources.filters);
  1234. }
  1235.  
  1236. void
  1237. DoPSave(w, closure, call_data)
  1238. Widget w;
  1239. XtPointer closure, call_data;
  1240. {
  1241.   the_Question->q->maxresdocs = atoi(GetString(maxdocwid));
  1242.   if(the_Question->q->sourcepath != NULL) s_free(the_Question->q->sourcepath);
  1243.   the_Question->q->sourcepath = s_strdup(GetString(sourcepathwid));
  1244.   if(app_resources.filters != NULL) s_free(app_resources.filters);
  1245.   app_resources.filters = s_strdup(GetString(filterwid));
  1246.   XtPopdown(prefpopup);
  1247. }
  1248.  
  1249. void
  1250. DontPSave(w, closure, call_data)
  1251. Widget w;
  1252. XtPointer closure, call_data;
  1253. {
  1254.   XtPopdown(prefpopup);
  1255. }
  1256.  
  1257. void
  1258. Abort(w, closure, call_data)
  1259. Widget w;
  1260. XtPointer closure, call_data;
  1261. {
  1262.   if(busy) {
  1263.     fuzzButtons(false);
  1264.     busy = FALSE;
  1265.     SetCursors(NULL);
  1266.  
  1267.     if (sb != NULL && sb->xid != NULL) {
  1268.       XtRemoveInput(sb->xid);
  1269.       sb->xid = NULL;
  1270.     }
  1271.     if(world_timer != NULL) {
  1272.       XtRemoveTimeOut(world_timer);
  1273.       world_timer = NULL;
  1274.     }
  1275.  
  1276.     PrintStatus(STATUS_URGENT, STATUS_HIGH, ABORT_MESSAGE);
  1277.     if(searching) {
  1278.       if(the_Question->q->ResultDocuments != NULL)
  1279.     freeDocList(the_Question->q->ResultDocuments);
  1280.       the_Question->q->ResultDocuments = resdocs;
  1281.       the_Question->q->numresdocs =
  1282.     listlength((List)the_Question->q->ResultDocuments);
  1283.       the_Question->Result_Items =
  1284.     buildDocumentItemList(the_Question->q->ResultDocuments, TRUE);
  1285.       RebuildListWidget(the_Question->window->ResultDocuments,
  1286.             the_Question->Result_Items, LIST_TOP);
  1287. #ifndef MOTIF
  1288.       XtCallActionProc(searchButton, "unset", NULL, NULL, 0);
  1289. #endif
  1290.     }
  1291.     else {
  1292.       freeDocList(resdocs);
  1293. #ifndef MOTIF
  1294.       XtCallActionProc(viewbutton, "unset", NULL, NULL, 0);
  1295. #endif
  1296.       if(sb != NULL && sb->textstruct != NULL) {
  1297.     KillText(sb->textstruct);
  1298.     sb->textstruct = NULL;
  1299.       }
  1300.       sb->page = 0; sb->chars_per_page = 0;
  1301.     }
  1302.     /* close out the connection to clear buffers */
  1303.  
  1304.     if(the_Question->q->Sources != NULL) {
  1305.       SourceList slist;
  1306.       Source source;
  1307.       for(slist = the_Question->q->Sources;
  1308.       slist != NULL;
  1309.       slist = slist->nextSource) {
  1310.     if((source = findsource(slist->thisSource->filename,
  1311.                 the_Question->q->sourcepath)) != NULL)
  1312.       close_source(source);
  1313.       }
  1314.     }
  1315.   }
  1316. }
  1317.  
  1318. /* these are the commands used in the question widget */
  1319.  
  1320.  
  1321. /* ARGSUSED */
  1322. void
  1323. DoSearch(w, closure, call_data)
  1324. Widget w;
  1325. XtPointer closure, call_data;
  1326. {
  1327.   double_click = FALSE;
  1328.   LastClicked = w;
  1329.   last_doc = -1;
  1330.  
  1331.   if(!busy) {
  1332.     /* update the info */
  1333.  
  1334.     strncpy(the_Question->q->keywords,
  1335.         GetString(the_Question->window->keywordwid), STRINGSIZE);
  1336.  
  1337.     busy = TRUE;
  1338.     searching = TRUE;
  1339.  
  1340.     fuzzButtons(true);
  1341.     if((current_sourcelist = the_Question->q->Sources) == NULL) {
  1342.       SourceID sid;
  1343.  
  1344.       sid = (SourceID)s_malloc(sizeof(_SourceID));
  1345.       sid->filename = s_strdup("directory-of-servers.src");
  1346.       the_Question->q->Sources = makeSourceList(sid, NULL);
  1347.       current_sourcelist = the_Question->q->Sources;
  1348.       the_Question->Source_Items =
  1349.     buildSourceItemList(the_Question->q->Sources);
  1350.       the_Question->q->numsources =
  1351.     charlistlength(the_Question->Source_Items);
  1352.  
  1353.       RebuildListWidget(the_Question->window->Sources,
  1354.             the_Question->Source_Items, LIST_BOTTOM);
  1355.     }
  1356.     world_timer = XtAddTimeOut(WORLD_INTERVAL, world, (Opaque) world);
  1357.     resdocs = the_Question->q->ResultDocuments;
  1358.     the_Question->q->ResultDocuments = NULL;
  1359.     SetupSearch(the_Question->q);
  1360.   }
  1361. }
  1362.  
  1363. /* ARGSUSED */
  1364. void
  1365. DoSave(w, closure, call_data)
  1366. Widget w;
  1367. XtPointer closure, call_data;
  1368. {
  1369. #ifndef MOTIF
  1370.   XtCallActionProc(savebutton, "set", NULL, NULL, 0);
  1371.   XtCallActionProc(savebutton, "notify", NULL, NULL, 0);
  1372.   XtCallActionProc(savebutton, "unset", NULL, NULL, 0);
  1373. #endif
  1374. }
  1375.  
  1376. /* ARGSUSED */
  1377. static void
  1378. DoSaveQ(w, closure, call_data)
  1379. Widget w;
  1380. XtPointer closure, call_data;
  1381. {
  1382.   FILE *fp;
  1383.   char message[STRINGSIZE], filename[STRINGSIZE];
  1384.  
  1385.   XtPopdown(savereq);
  1386.   SetReqButtons(false);
  1387.  
  1388.   strncpy(the_Question->q->name, GetString(filenamewidget), STRINGSIZE);
  1389.   sprintf(filename, "%s/%s",
  1390.       GetString(dirnamewidget), the_Question->q->name);
  1391.  
  1392.   WriteQuestion(filename, the_Question->q);
  1393.   exit(0);
  1394. }
  1395.  
  1396. /* ARGSUSED */
  1397. static void
  1398. DontSave(w, closure, call_data)
  1399. Widget w;
  1400. XtPointer closure, call_data;
  1401. {
  1402.   exit(0);
  1403. }
  1404.  
  1405. static void
  1406. SaveQuestion()
  1407. {
  1408.   Arg args[ONE];
  1409.   if(savelist == NULL)
  1410.     savelist = MakeSaveRequester(top);
  1411.  
  1412.   SetPosition(the_Question->window->shell, savereq, true);
  1413.  
  1414.   XtPopup(savereq, XtGrabNone);
  1415.   ReplaceText(dirnamewidget, app_resources.questionDirectory);
  1416.   ReplaceText(filenamewidget, the_Question->q->name);
  1417.   SetDir(NULL, NULL, NULL);
  1418.  
  1419.   SetReqButtons(true);
  1420.  
  1421.   XtAddCallback(savebutton, COMMANDCALLBACK, DoSaveQ, NULL);
  1422.   XtAddCallback(quitbutton, COMMANDCALLBACK, DontSave, NULL);
  1423.  
  1424. }
  1425.  
  1426. /* ARGSUSED */
  1427. void CloseQuestionEdit(w, closure, call_data)
  1428. Widget w;
  1429. XtPointer closure, call_data;
  1430. {
  1431.   char filename[STRINGSIZE];
  1432.  
  1433.   strncpy(the_Question->q->keywords,
  1434.       GetString(the_Question->window->keywordwid),
  1435.       STRINGSIZE);
  1436.  
  1437.   if(strcmp(the_Question->q->name, "New Question") == 0) {
  1438.     SaveQuestion(NULL, NULL, NULL);
  1439.   }
  1440.   else {
  1441.     sprintf(filename, "%s%s", app_resources.questionDirectory,
  1442.         the_Question->q->name);
  1443.     WriteQuestion(filename, the_Question->q);
  1444.     exit(0);
  1445.   }
  1446. }
  1447.  
  1448. /* ARGSUSED */
  1449. void
  1450. AddResponseToQuestion(w, closure, call_data)
  1451. Widget w;
  1452. XtPointer closure, call_data;
  1453. {
  1454.   long document_number;
  1455.   DocList this, last;
  1456.  
  1457.   double_click = FALSE;
  1458.   LastClicked = w;
  1459.  
  1460.   document_number = get_question_response(the_Question->window);
  1461.  
  1462.   if(document_number != NO_ITEM_SELECTED) {
  1463.     XtSetSensitive(delDocButton, False);
  1464.     last_qdoc = NO_ITEM_SELECTED;
  1465.     /* find and add document to question's relevant documents */
  1466.     last = findLast(the_Question->q->RelevantDocuments);
  1467.  
  1468.     for(this = the_Question->q->ResultDocuments;document_number--;)
  1469.       this = this->nextDoc;
  1470.  
  1471.     if(last != NULL)
  1472.       last->nextDoc = makeDocList(copy_docid(this->thisDoc), NULL);
  1473.     else the_Question->q->RelevantDocuments = makeDocList(copy_docid(this->thisDoc), NULL);
  1474.  
  1475.     if(the_Question->Relevant_Items != NULL)
  1476.       freeItemList(the_Question->Relevant_Items);
  1477.  
  1478.     the_Question->Relevant_Items =
  1479.       buildDocumentItemList(the_Question->q->RelevantDocuments, FALSE);
  1480.  
  1481.     the_Question->q->numdocs = charlistlength(the_Question->Relevant_Items);
  1482.  
  1483.     RebuildListWidget(the_Question->window->RelevantDocuments,
  1484.               the_Question->Relevant_Items, LIST_BOTTOM);
  1485.  
  1486.     the_Question->q->modified = TRUE;
  1487.   }
  1488. }
  1489.  
  1490. /* ARGSUSED */
  1491. void
  1492. DeleteQuestionDoc(w, closure, call_data)
  1493. Widget w;
  1494. XtPointer closure, call_data;
  1495. {
  1496.   int SelectedDoc;
  1497.   DocList doc, last;
  1498.  
  1499.   if((SelectedDoc = get_selected_qdoc(the_Question)) == NO_ITEM_SELECTED) {
  1500.     double_click = FALSE;
  1501.     LastClicked = w;
  1502.     PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSELECT_MESSAGE);
  1503.   }
  1504.   else {
  1505.     /* rip out the bugger */
  1506.  
  1507.     XtSetSensitive(delDocButton, False);
  1508.     last_qdoc = NO_ITEM_SELECTED;
  1509.  
  1510.     the_Question->q->modified = TRUE;
  1511.  
  1512.     double_click = FALSE;
  1513.     LastClicked = NULL;
  1514.     if (SelectedDoc == 0) {
  1515.       last = the_Question->q->RelevantDocuments;
  1516.       the_Question->q->RelevantDocuments = the_Question->q->RelevantDocuments->nextDoc;
  1517.       last->nextDoc = NULL;
  1518.       freeDocList(last);
  1519.     }
  1520.     else {
  1521.       for (doc = the_Question->q->RelevantDocuments;--SelectedDoc;) {
  1522.     doc = doc->nextDoc;
  1523.       }
  1524.       if(doc->nextDoc != NULL) {
  1525.     last = doc->nextDoc;
  1526.     doc->nextDoc = doc->nextDoc->nextDoc;
  1527.     last->nextDoc = NULL;
  1528.     freeDocList(last);
  1529.       }
  1530.     }
  1531.     if(the_Question->Relevant_Items != NULL) freeItemList(the_Question->Relevant_Items);
  1532.     the_Question->Relevant_Items = buildDocumentItemList(the_Question->q->RelevantDocuments, FALSE);
  1533.  
  1534.     the_Question->q->numdocs--;
  1535.     RebuildListWidget(the_Question->window->RelevantDocuments,
  1536.               the_Question->Relevant_Items, LIST_NONE);
  1537.   }
  1538. }
  1539.  
  1540. /* ARGSUSED */
  1541. void
  1542. PopupSourceMenu(w, closure, call_data)
  1543. Widget w;
  1544. XtPointer closure, call_data;
  1545. {
  1546.   SetPosition(w, sshell, false);
  1547. #ifndef MOTIF
  1548.   XawListUnhighlight(sourcewindow->ListWidget);
  1549. #endif
  1550.   XtPopup(sshell, XtGrabExclusive);
  1551. }
  1552.  
  1553. /* ARGSUSED */
  1554. void
  1555. AddSourceToQuestion(w, closure, call_data)
  1556. Widget w;
  1557. XtPointer closure, call_data;
  1558. {
  1559.   long snum = get_selected_source();
  1560.  
  1561.   if(snum == NO_ITEM_SELECTED) XtPopdown(sshell);
  1562.   else {
  1563. #ifdef MOTIF
  1564.     last_source = snum;
  1565. #endif
  1566.     if(last_source != snum) last_source = snum;
  1567.     else {
  1568.       SourceID sid;
  1569.       SourceList slist, source = NULL;
  1570.       char *name = Source_items[snum];
  1571.  
  1572.       XtSetSensitive(delSourceButton, SENSVAL);
  1573.       last_qsource = NO_ITEM_SELECTED;
  1574.       XtPopdown(sshell);
  1575.       /* append it to the current sourcelist */
  1576.  
  1577.       for(source = the_Question->q->Sources;
  1578.       source != NULL && source->nextSource != NULL;
  1579.       source = source->nextSource)
  1580.  
  1581.     if(strcmp(source->thisSource->filename, name) == 0)
  1582.       break;
  1583.  
  1584.       if(source == NULL || source->nextSource == NULL) {
  1585.     sid = (SourceID)s_malloc(sizeof(_SourceID));
  1586.     sid->filename = s_strdup(name);
  1587.     slist = makeSourceList(sid, NULL);
  1588.  
  1589.     if (source != NULL) source->nextSource = slist;
  1590.     else the_Question->q->Sources = slist;
  1591.  
  1592.     the_Question->Source_Items =
  1593.       buildSourceItemList(the_Question->q->Sources);
  1594.     the_Question->q->numsources =
  1595.       charlistlength(the_Question->Source_Items);
  1596.  
  1597.     RebuildListWidget(the_Question->window->Sources,
  1598.               the_Question->Source_Items, LIST_BOTTOM);
  1599.     the_Question->q->modified = TRUE;
  1600.       }
  1601.     }
  1602.   }
  1603. }
  1604.  
  1605. /* ARGSUSED */
  1606. void
  1607. EditQuestionSource(w, closure, call_data)
  1608. Widget w;
  1609. XtPointer closure, call_data;
  1610. {
  1611.   int CurrentSource;
  1612.   Source edit_source = NULL;
  1613.  
  1614.   double_click = FALSE;
  1615.   LastClicked = w;
  1616.  
  1617.   if ((CurrentSource = get_selected_qsource(the_Question))
  1618.       == NO_ITEM_SELECTED) {
  1619.     last_qsource = NO_ITEM_SELECTED;
  1620.   }
  1621.   else {
  1622. #ifdef MOTIF
  1623.     last_qsource = CurrentSource;
  1624. #endif
  1625.     if (last_qsource == CurrentSource) {
  1626.       if((edit_source =
  1627.       findsource(the_Question->Source_Items[CurrentSource],
  1628.              the_Question->q->sourcepath)) == NULL) {
  1629.     PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
  1630.             the_Question->Source_Items[CurrentSource]);
  1631.       }
  1632.       else {
  1633.     PrintStatus(STATUS_INFO, STATUS_MEDIUM, VIEWSOURCE_MESSAGE,
  1634.             edit_source->name);
  1635.     PopupSource(edit_source);
  1636.       }
  1637.     }
  1638.     else last_qsource = CurrentSource;
  1639.   }
  1640. }
  1641.  
  1642. /* ARGSUSED */
  1643. void
  1644. DeleteQuestionSource(w, closure, call_data)
  1645. Widget w;
  1646. XtPointer closure, call_data;
  1647. {
  1648.   int SelectedSource;
  1649.   SourceList source, last;
  1650.  
  1651.   if((SelectedSource = get_selected_qsource(the_Question)) == NO_ITEM_SELECTED) {
  1652.     double_click = FALSE;
  1653.     LastClicked = w;
  1654.     PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSELECT_MESSAGE);
  1655.   }
  1656.   else {
  1657.     /* rip out the bugger */
  1658.     XtSetSensitive(delSourceButton, SENSVAL);
  1659.     last_qsource = NO_ITEM_SELECTED;
  1660.     the_Question->q->modified = TRUE;
  1661.  
  1662.     double_click = FALSE;
  1663.     LastClicked = NULL;
  1664.     if (SelectedSource == 0) {
  1665.       last = the_Question->q->Sources;
  1666.       the_Question->q->Sources = the_Question->q->Sources->nextSource;
  1667.       s_free(last);
  1668.     }
  1669.     else {
  1670.       for (source = the_Question->q->Sources; --SelectedSource;) {
  1671.     source = source->nextSource;
  1672.       }
  1673.       if(source->nextSource != NULL) {
  1674.     last = source->nextSource;
  1675.     source->nextSource = source->nextSource->nextSource;
  1676.     s_free(last);
  1677.       }
  1678.     }
  1679.     the_Question->Source_Items =
  1680.       buildSourceItemList(the_Question->q->Sources);
  1681.  
  1682.     the_Question->q->numsources--;
  1683.     RebuildListWidget(the_Question->window->Sources,
  1684.               the_Question->Source_Items, LIST_NONE);
  1685.   }
  1686. }
  1687.  
  1688. /* ARGSUSED */
  1689. void doType(w, closure, call_data)
  1690. Widget w;
  1691. XtPointer closure, call_data;
  1692. {
  1693.   long tnum, dnum;
  1694.   DocumentID doc;
  1695.  
  1696.   XtPopdown(typeshell);
  1697.   if((tnum = get_selected_type()) != NO_ITEM_SELECTED) {
  1698.     dnum = get_selected_response(the_Question);
  1699.     if((doc = findDoc(the_Question->q->ResultDocuments, dnum)) != NULL) {
  1700.       messwidget = the_Question->window->StatusWindow;
  1701.       ViewDoc(doc, Type_items[tnum], -1, false);
  1702.     }
  1703.   }
  1704. }
  1705.  
  1706. void SensitizeDelSource(w, closure, call_data)
  1707. Widget w;
  1708. XtPointer closure, call_data;
  1709. {
  1710.   XawListReturnStruct *data = (XawListReturnStruct *)call_data;
  1711.  
  1712.   XtSetSensitive(delSourceButton,!!data->string);
  1713. }
  1714.  
  1715. void SensitizeAddDoc(w, closure, call_data)
  1716. Widget w;
  1717. XtPointer closure, call_data;
  1718. {
  1719.   XawListReturnStruct *data = (XawListReturnStruct *)call_data;
  1720.   XtSetSensitive(addDocButton, !!data->string);
  1721.   XtSetSensitive(viewbutton, !!data->string);
  1722.   XtSetSensitive(saveAsButton, !!data->string);
  1723. }
  1724.  
  1725. void SensitizeDelDoc(w, closure, call_data)
  1726. Widget w;
  1727. XtPointer closure, call_data;
  1728. {
  1729.   XawListReturnStruct *data = (XawListReturnStruct *)call_data;
  1730.  
  1731.   XtSetSensitive(delDocButton, !!data->string);
  1732. }
  1733.  
  1734. void
  1735.   ViewDoc(doc, type, size, saveit)
  1736. DocumentID doc;
  1737. char *type;
  1738. long size;
  1739. Boolean saveit;
  1740. {
  1741.   Textbuff textstruct;
  1742.   TextList thisText;
  1743.  
  1744.   savep = saveit;
  1745.  
  1746.   if((textstruct = findTextDoc(doc, type)) != NULL) {
  1747.     if(textstruct->shell != NULL) {
  1748.       XtPopdown(textstruct->shell);
  1749.       XtPopup(textstruct->shell, XtGrabNone);
  1750.     }
  1751.     return;
  1752.   }
  1753.  
  1754.   if(busy) return;
  1755.  
  1756.   if(NULL == (thisText = NewText())) {
  1757.     PrintStatus(STATUS_URGENT, STATUS_HIGH, BADALLOC_MESSAGE);
  1758.     return;
  1759.   }
  1760.  
  1761.   textstruct = thisText->thisText;
  1762.   textstruct->docid = doc;
  1763.  
  1764.   textstruct->size = 0;
  1765.  
  1766.   if(savep)
  1767.     textstruct->type = s_strdup("WaIsOddBall");
  1768.   else
  1769.     textstruct->type = s_strdup(type);
  1770.  
  1771.   GetDoc(textstruct, size);
  1772. }
  1773.  
  1774. /* ARGSUSED */
  1775. void
  1776. ViewResponse(w, closure, call_data)
  1777. Widget w;
  1778. XtPointer closure, call_data;
  1779. {
  1780.   static int document_number;
  1781.  
  1782.   double_click = FALSE;
  1783.   LastClicked = w;
  1784.  
  1785.   if(!busy)
  1786.     if((document_number = get_selected_response(the_Question)) ==
  1787.        NO_ITEM_SELECTED) {
  1788.       last_doc = document_number;
  1789.     }
  1790.     else {
  1791.       DocumentID doc;
  1792.  
  1793.       if((doc = findDoc(the_Question->q->ResultDocuments, document_number)) == NULL)
  1794.     PrintStatus(STATUS_URGENT, STATUS_HIGH, NODOC_MESSAGE);
  1795.       else {
  1796. #ifdef MOTIF
  1797.     last_doc = document_number;
  1798. #endif
  1799.     if (document_number != last_doc) {
  1800.       Arg args[2];
  1801.  
  1802.       XtSetArg(args[0], XtNsensitive, True);
  1803.       if((w != viewbutton) && (doc->doc->type[1] != NULL)) {
  1804.         XtSetArg(args[1], XtNlabel, "View...");
  1805.       }
  1806.       else {
  1807.         XtSetArg(args[1], XtNlabel, " View  ");
  1808.       }
  1809.       XtSetValues(viewbutton, args, TWO);
  1810.       XtSetValues(saveAsButton, args, ONE);
  1811.       last_doc = document_number;
  1812.     }
  1813.     else if((w == viewbutton) && (doc->doc->type[1] != NULL)) {
  1814.       PopupTypeMenu(w, doc);
  1815.     }
  1816.     else {
  1817.       messwidget = the_Question->window->StatusWindow;
  1818.       ViewDoc(doc, doc->doc->type[0], 0, (w == saveAsButton));
  1819.     }
  1820.       }
  1821.     }
  1822. }
  1823.  
  1824. /* ARGSUSED */
  1825. void
  1826. ViewRelevant(w, closure, call_data)
  1827. Widget w;
  1828. XtPointer closure, call_data;
  1829. {
  1830.   double_click = FALSE;
  1831.   LastClicked = w;
  1832.  
  1833.   if(!busy) {
  1834.     long document_number = get_selected_qdoc(the_Question);
  1835.  
  1836.     if(document_number == NO_ITEM_SELECTED) {
  1837.       last_qdoc = document_number;
  1838.     }
  1839.     else {
  1840.       DocumentID doc;
  1841. #ifdef MOTIF
  1842.       last_qdoc = document_number;
  1843. #endif
  1844.       if((doc = findDoc(the_Question->q->RelevantDocuments, document_number))
  1845.      == NULL) {
  1846.     PrintStatus(STATUS_URGENT, STATUS_HIGH, NODOC_MESSAGE);
  1847.       }
  1848.       else if (document_number != last_qdoc) {
  1849.     last_qdoc = document_number;
  1850.       }
  1851.       else {
  1852.     messwidget = the_Question->window->StatusWindow;
  1853.     ViewDoc(doc, doc->doc->type[0], 0, false);
  1854.       }
  1855.     }
  1856.   }
  1857. }
  1858.  
  1859. /* ARGSUSED */
  1860. void
  1861. EndText(w, closure, call_data)
  1862. Widget w;
  1863. XtPointer closure, call_data;
  1864. {
  1865.   Textbuff t = findText(w);
  1866.  
  1867.   if(t != NULL) {
  1868.     XtPopdown(t->shell);
  1869.     KillText(t);
  1870.   }
  1871. }
  1872.  
  1873. /* ARGSUSED */
  1874. void
  1875. SaveText(w, closure, call_data)
  1876. Widget w;
  1877. XtPointer closure, call_data;
  1878. {
  1879.   Arg        args[5];
  1880.   Position    x, y;
  1881.   Dimension    width, height;
  1882.   Cardinal    n;
  1883.   Textbuff t;
  1884.  
  1885.   if((t = findText(w)) == NULL) {
  1886.     XwaisPrintf("couldn't find text.\n");
  1887.     return;
  1888.   }
  1889.  
  1890.   if(savelist == NULL)
  1891.     savelist = MakeSaveRequester(top);
  1892.  
  1893.   SetPosition(t->textwindow, savereq, true);
  1894.  
  1895.   current_text = t;
  1896.  
  1897.   ReplaceText(filenamewidget, "");
  1898.   ReplaceText(dirnamewidget, app_resources.documentDirectory);
  1899.  
  1900.   XtPopup(savereq, XtGrabNone);
  1901.   SetDir(NULL, NULL, NULL);
  1902.   SetReqButtons(false);
  1903.  
  1904.   XtRemoveAllCallbacks(savebutton, COMMANDCALLBACK);
  1905.   XtAddCallback(savebutton, COMMANDCALLBACK, DoTSaveCB, t);
  1906. }
  1907.  
  1908. /* ARGSUSED */
  1909. void
  1910. DoTSave(w, closure, call_data)
  1911. Widget w;
  1912. XtPointer closure, call_data;
  1913. {
  1914.   DoTSaveCB(w, (XtPointer)current_text, NULL);
  1915. }
  1916.  
  1917. /* ARGSUSED */
  1918. void
  1919. DontTSave(w, closure, call_data)
  1920. Widget w;
  1921. XtPointer closure, call_data;
  1922. {
  1923.   XtPopdown(savereq);
  1924.   SetReqButtons(false);
  1925. }
  1926.  
  1927. /* ARGSUSED */
  1928. void
  1929. addSection(w, closure, call_data)
  1930. Widget w;
  1931. XtPointer closure, call_data;
  1932. {
  1933.   int i;
  1934.   Question q = the_Question->q;
  1935.   QuestionWindow qw = the_Question->window;
  1936.   float top, shown;
  1937.   DocList dlist;
  1938.   DocumentID doc;
  1939.   XawTextPosition p1, p2;
  1940.   long l1, l2;
  1941.   Textbuff t;
  1942.  
  1943.   t = findText(w);
  1944.  
  1945.   XawTextGetSelectionPos(t->textwindow, &p1, &p2);
  1946.  
  1947.   if(p1 >= 0 && p2 > 0) {
  1948.     /* find the line positions */
  1949.     l1 = GetLineFromPos(t->text, p1);
  1950.     l2 = GetLineFromPos(t->text, p2);
  1951.  
  1952.     doc = copy_docid(t->docid);
  1953.     doc->start = l1;
  1954.     doc->end = l2;
  1955.     dlist = makeDocList(doc, NULL);
  1956.     /* append it to the current rellist */
  1957.  
  1958.     if(q->RelevantDocuments != NULL) {
  1959.       DocList doc;
  1960.  
  1961.       for(doc = q->RelevantDocuments; doc->nextDoc != NULL; doc = doc->nextDoc);
  1962.       doc->nextDoc = dlist;
  1963.     }
  1964.     else
  1965.       q->RelevantDocuments = dlist;
  1966.  
  1967.     if(the_Question->Relevant_Items != NULL)
  1968.       freeItemList(the_Question->Relevant_Items);
  1969.     the_Question->Relevant_Items =
  1970.       buildDocumentItemList(q->RelevantDocuments, FALSE);
  1971.     q->numdocs = charlistlength(the_Question->Relevant_Items);
  1972.  
  1973.     RebuildListWidget(qw->RelevantDocuments, the_Question->Relevant_Items, LIST_BOTTOM);
  1974.   }
  1975. }
  1976.  
  1977. /* ARGSUSED */
  1978. void
  1979. DoSSave(w, closure, call_data)
  1980. Widget w;
  1981. XtPointer closure, call_data;
  1982. {
  1983.   FILE *fp;
  1984.   char name[STRINGSIZE];
  1985.   Source source;
  1986.   SList current_sources;
  1987.  
  1988.   source = the_Source;
  1989.  
  1990.   XtPopdown(sourcepopup);
  1991.  
  1992.   strcpy(name, GetString(snamewid));
  1993.  
  1994.   if(!((strlen(name) > 4) &&
  1995.        strstr(name, ".src") &&
  1996.        (!strcmp(".src", strstr(name, ".src")))))
  1997.     strcat(name, ".src");
  1998.  
  1999.   if(source->name != NULL) s_free(source->name);
  2000.   source->name = s_strdup(name);
  2001.  
  2002.   if (source->maintainer != NULL) s_free(source->maintainer);
  2003.   source->maintainer = s_strdup(GetString(maintainerwid));
  2004.  
  2005.   if (source->description != NULL) s_free(source->description);
  2006.   source->description = s_strdup(GetString(descwid));
  2007.  
  2008.   strncpy(source->server, GetString(serverwid), STRINGSIZE);
  2009.   strncpy(source->service, GetString(servicewid), STRINGSIZE);
  2010.   strncpy(source->database, GetString(dbwid), STRINGSIZE);
  2011.   strncpy(source->cost, GetString(costwid), STRINGSIZE);
  2012.   strncpy(source->units, GetString(unitwid), STRINGSIZE);
  2013.  
  2014.   WriteSource(app_resources.userSourceDirectory, source, TRUE);
  2015.  
  2016.   NumSources = 0;
  2017.  
  2018.   GetSourceNames(app_resources.userSourceDirectory);
  2019.   if(app_resources.commonSourceDirectory[0] != 0)
  2020.     GetSourceNames(app_resources.commonSourceDirectory);
  2021.  
  2022.   RebuildListWidget(sourcewindow, Source_items, LIST_NONE);
  2023. }
  2024.  
  2025. /* ARGSUSED */
  2026. void
  2027. DontSSave(w, closure, call_data)
  2028. Widget w;
  2029. XtPointer closure, call_data;
  2030. {
  2031.   XtPopdown(sourcepopup);
  2032. }
  2033.  
  2034. /* ARGSUSED */
  2035. void showNext(w, closure, call_data)
  2036. Widget w;
  2037. XtPointer closure, call_data;
  2038. {
  2039.   Textbuff t;
  2040.   DocumentID d;
  2041.   Source source;
  2042.   Question q = the_Question->q;
  2043.  
  2044.   t = findText(w);
  2045.   if(t != NULL) {
  2046.     if(t->docid->doc != NULL &&
  2047.        t->docid->doc->sourceID != NULL &&
  2048.        t->docid->doc->sourceID->filename != NULL)
  2049.       source = findsource(t->docid->doc->sourceID->filename,
  2050.               the_Question->q->sourcepath);
  2051.     messwidget = t->status;
  2052.     if (source == NULL) {
  2053.       PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
  2054.           t->docid->doc->sourceID->filename);
  2055.       return;
  2056.     }
  2057.  
  2058.     if((d = getNextorPrevDoc(q, source, t->docid, TRUE)) != NULL)
  2059.       ViewDoc(d, d->doc->type[0], 0, false);
  2060.     else
  2061.       PrintStatus(STATUS_URGENT, STATUS_HIGH, BADNEXT_MESSAGE);
  2062.   }
  2063. }
  2064.  
  2065. /* ARGSUSED */
  2066. void showPrevious(w, closure, call_data)
  2067. Widget w;
  2068. XtPointer closure, call_data;
  2069. {
  2070.   Textbuff t;
  2071.   DocumentID d;
  2072.   Source source;
  2073.  
  2074.   t = findText(w);
  2075.   if(t != NULL) {
  2076.     if(t->docid->doc != NULL &&
  2077.        t->docid->doc->sourceID != NULL &&
  2078.        t->docid->doc->sourceID->filename != NULL)
  2079.       source = findsource(t->docid->doc->sourceID->filename,
  2080.               the_Question->q->sourcepath);
  2081.     messwidget = t->status;
  2082.     if (source == NULL) {
  2083.       PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
  2084.           t->docid->doc->sourceID->filename);
  2085.       return;
  2086.     }
  2087.  
  2088.     if((d = getNextorPrevDoc(the_Question->q, source, t->docid, FALSE))
  2089.        != NULL)
  2090.       ViewDoc(d, d->doc->type[0], 0, false);
  2091.     else
  2092.       PrintStatus(STATUS_URGENT, STATUS_HIGH, BADPREV_MESSAGE);
  2093.   }
  2094. }
  2095.  
  2096. /* ARGSUSED */
  2097. void setFile(w, closure, call_data)
  2098. Widget w;
  2099. XtPointer closure, call_data;
  2100. {
  2101.   XtPopdown(savereq);
  2102.   SetReqButtons(false);
  2103. }
  2104.  
  2105. /* ARGSUSED */
  2106. void quitFile(w, closure, call_data)
  2107. Widget w;
  2108. XtPointer closure, call_data;
  2109. {
  2110.   XtPopdown(savereq);
  2111.   SetReqButtons(false);
  2112. }
  2113.  
  2114. char* GetKeywordsUsed() {
  2115.   char* result = the_Question->q->keywords_used;
  2116.  
  2117. #ifdef BOOLEANS
  2118.   static char* nobool_question;
  2119.   int i, j;
  2120.   char savec;
  2121. #endif
  2122.     
  2123. #ifndef BOOLEANS
  2124.   if (result == NULL) result = the_Question->q->keywords;
  2125. #else
  2126.   if (result == NULL) {
  2127.     result = the_Question->q->keywords;
  2128.     if (nobool_question != NULL) {
  2129.        XtFree(nobool_question);
  2130.        nobool_question = NULL;
  2131.     }
  2132.   } 
  2133. #endif
  2134. #ifdef BOOLEANS
  2135.   if (nobool_question == NULL) {
  2136.     nobool_question = XtMalloc(sizeof(unsigned char) * (strlen(result)+1));
  2137.     *nobool_question = '\0';
  2138.     for(j = 0, i = 0; i <= strlen(result); i++) {    /* step thru keys */
  2139.       if ((result[i] == 0) || isspace(result[i]) || ispunct(result[i])) {
  2140.         savec = result[i];
  2141.         result[i] = '\0';
  2142.         /* toss booleans, need code here for partial matches as well? */
  2143.         if(! (0 == strcasecmp(&result[j], "and") || 
  2144.               0 == strcasecmp(&result[j], "or" ) || 
  2145.               0 == strcasecmp(&result[j], "not") )) {
  2146.           if(nobool_question[0] != '\0')        /* delimit keys */
  2147.         strcat(nobool_question, " ");
  2148.           strcat(nobool_question, &result[j]);
  2149.   
  2150.         }
  2151.         result[i] = savec;
  2152.         j = i + 1;
  2153.       }
  2154.     }
  2155.   }
  2156.   if(strlen(nobool_question) > 0) /* make sure we didn't strip everything ...*/
  2157.     result = nobool_question;
  2158. #endif
  2159.  
  2160.   return(result);
  2161. }
  2162.  
  2163.