home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / freeWAIS-sf-1.1 / ir / irfiles.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-21  |  99.5 KB  |  3,355 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    Brewster@think.com
  6. */
  7.  
  8. /* This file defines the files of an inverted file index.
  9.  *
  10.  * This structure is designed to be flexible rather than particularly
  11.  * optimized for speed or space.
  12.  * Thus this organization can support:
  13.  *   boolean, proximity, weights, and relevance feedback.
  14.  *
  15.  * Ported directly from the Lisp version 1.2 of the search engine.
  16.  *
  17.  * -brewster 6/90
  18.  */
  19.  
  20. #ifndef lint
  21. static char *RCSid = "$Header: /usr/local/ls6/src+data/src/freeWAIS-sf/ir/RCS/irfiles.c,v 1.35 1994/12/22 14:01:36 pfeifer Exp $";
  22. #endif
  23.  
  24. /* ==================== */
  25. /* ===  Change Log  === */
  26. /*Created 12/4/89 Brewster full lisp version
  27.  *split from ir-engine 1/11/90 brewster
  28.  *
  29.  *added memory indexing for efficiency
  30.  *added variable index block sizes
  31.  *5/90 ported to C
  32.  *5/90 split from irbuild.c
  33.  *7/90 declared truename() a static function - HWM 
  34.  *7/90 changed filename table and headline table to be null
  35.  *     terminated in the file rather than \newline.
  36.  *     compatibility problems between systems (sigh).
  37.  *     -brewster
  38.  *7/90 added field to document table for WAIStation
  39.  *     -brewster
  40.  *7/90 fixed: BUG: when adding words to the word disk hashtable, watch out 
  41.  *     for the end of the file and wrap.  If it is full, error out.
  42.  *3/91 took out utilities and created futil.c -brewster
  43.  *3/91 took out the inverted file and created irinv.c -brewster
  44.  *
  45.  * $Log: irfiles.c,v $
  46.  * Revision 1.35  1994/12/22  14:01:36  pfeifer
  47.  * added :description to  :fields
  48.  *
  49.  * Revision 1.34  1994/12/22  10:41:48  pfeifer
  50.  * Added :fields to source files
  51.  *
  52.  * Revision 1.33  1994/12/13  18:52:45  pfeifer
  53.  * chip@chinacat.unicom.com (Chip Rosenthal) patches.
  54.  * Excluding the merge of libinv and libwais
  55.  *
  56.  * Revision 1.32  1994/09/06  16:52:58  pfeifer
  57.  * Removed #include <stdlib.h> which is now done in cdialect.h
  58.  *
  59.  * Revision 1.31  1994/09/02  14:34:21  pfeifer
  60.  * fixed overlapping memory copies
  61.  *
  62.  * Revision 1.30  1994/08/23  13:06:48  pfeifer
  63.  * removed irlex.h. Code now in Defaults.tmpl
  64.  *
  65.  * Revision 1.29  1994/08/22  14:10:47  pfeifer
  66.  * Some fixes to make purify happy (a the memory not wasted)
  67.  * jp made the getenv code safer
  68.  *
  69.  * Revision 1.28  1994/08/15  16:18:18  pfeifer
  70.  * fixed unchecked usage of getenv("USER") in printf
  71.  *
  72.  * Revision 1.27  1994/08/08  10:29:45  pfeifer
  73.  * Fixed the MAX_OCCURANCES - STOP_WORD_FLAG bug
  74.  *
  75.  * Revision 1.26  1994/08/05  08:53:59  pfeifer
  76.  * Fix for total_word_count bug
  77.  *
  78.  * Revision 1.25  1994/08/05  07:12:09  pfeifer
  79.  * Release beta 04
  80.  *
  81.  * Revision 1.24  1994/07/22  12:30:05  huynh1
  82.  * left for holidays
  83.  *
  84.  * Revision 1.23  1994/07/15  17:11:03  pfeifer
  85.  * negative numerics
  86.  *
  87.  * Revision 1.22  1994/07/13  07:52:36  huynh1
  88.  * Uli
  89.  *
  90.  * Revision 1.21  1994/07/05  17:14:21  huynh1
  91.  * bug by numeric searching (>) corrected.
  92.  *
  93.  * Revision 1.20  1994/06/24  11:18:56  huynh1
  94.  * bug by stemming corrected.
  95.  *
  96.  * Revision 1.19  1994/06/09  18:34:01  pfeifer
  97.  * added ftp:
  98.  *
  99.  * Revision 1.18  1994/06/09  17:14:40  pfeifer
  100.  * fixed the url stuff in write_filename_table_entry to make it not
  101.  * prepend the path.
  102.  *
  103.  * Revision 1.17  1994/06/09  16:51:22  pfeifer
  104.  * added a hack in write_filename_table_entry, to avoid testing of date
  105.  * for files starting withh "http:". So URL can be put in the docid!!
  106.  *
  107.  * Revision 1.16  1994/06/03  10:49:11  huynh1
  108.  * bug in (field_)finished_add_word_to_dictionary corrected.
  109.  *
  110.  * Revision 1.15  1994/05/31  14:29:46  pfeifer
  111.  * dict_number_of_blocks: added 4 to make it run with 2999 words ????
  112.  *
  113.  * Revision 1.14  1994/05/26  15:41:18  huynh1
  114.  * look_up_total_word_count, field_look_up_total_word_count upadted.
  115.  * beta.
  116.  *
  117.  * Revision 1.13  1994/05/20  12:57:24  huynh1
  118.  * beta
  119.  *
  120.  * Revision 1.12  1994/04/28  16:07:07  huynh1
  121.  * *** empty log message ***
  122.  *
  123.  * Revision 1.11  1994/03/25  11:11:00  huynh1
  124.  * fix bugs in partial search corrected.
  125.  * faster numeric search. Patchlevel 07.
  126.  *
  127.  * Revision 1.10  1994/03/23  12:55:42  huynh1
  128.  * openDatabase modified.
  129.  * patchlevel 07.
  130.  *
  131.  * Revision 1.9  1994/03/21  12:23:21  pfeifer
  132.  * *** empty log message ***
  133.  *
  134.  * Revision 1.8  1994/03/10  17:33:36  huynh1
  135.  * Patchlevel 05
  136.  *
  137.  * Revision 1.7  1994/03/08  20:42:18  huynh1
  138.  * Patchlevel 04
  139.  *
  140.  * Revision 1.6  1994/02/14  10:30:29  huynh1
  141.  * functions for field concept added,
  142.  * fix bug in parttial search corrected.
  143.  *
  144.  * Revision 1.5  1993/07/01  19:34:50  warnock
  145.  * explicit declaration of gSavepart in savePartMatch
  146.  *
  147.  * Revision 1.4  93/07/01  19:18:54  warnock
  148.  * gethostname -> mygethostname
  149.  * 
  150.  * Revision 1.3  1993/02/16  17:07:49  freewais
  151.  * added AT&T patches for keyword list
  152.  *
  153.  * Revision 1.2  1993/02/16  15:32:56  freewais
  154.  * changed directory of servers registration to cnidr.org
  155.  *
  156.  * Revision 1.1  1993/02/16  15:05:35  freewais
  157.  * Initial revision
  158.  *
  159.  * Revision 1.63  92/04/28  16:54:41  morris
  160.  * added boolean support
  161.  * 
  162.  * Revision 1.62  92/03/20  13:57:04  jonathan
  163.  * New and Improved server registration.
  164.  * 
  165.  * Revision 1.61  92/03/19  10:38:27  shen
  166.  * modified lock to prevent more than one indexing at the same time.
  167.  * modified lock to block query while initilaizing a database
  168.  * 
  169.  * Revision 1.60  92/03/19  09:33:35  morris
  170.  * fixed the dictionary header to accurately indicate the number of blocks
  171.  * 
  172.  * Revision 1.59  92/02/27  12:25:27  shen
  173.  * add in locks
  174.  * 
  175.  * Revision 1.58  92/02/25  16:42:28  jonathan
  176.  * Added find_pointer_in_block using binary search from
  177.  * ses@techunix.technion.ac.il. (part of wais-8-b3-ses).
  178.  * 
  179.  * 
  180.  * Revision 1.57  92/02/25  12:49:16  jonathan
  181.  * removed a bunch of \n's from waislog's.
  182.  * 
  183.  * Revision 1.56  92/02/17  16:23:58  jonathan
  184.  * Modified build_catalog so it passes over the first entry (which seems to be
  185.  * empty).
  186.  * 
  187.  * Revision 1.55  92/02/17  12:37:34  jonathan
  188.  * Added code to build a catalog containing all headlines and DocID's for
  189.  * documents in the database.
  190.  * 
  191.  * Revision 1.54  92/02/16  09:50:49  jonathan
  192.  * plugged some memory leaks.  I bet there are more.
  193.  * 
  194.  * Revision 1.53  92/02/16  09:26:39  jonathan
  195.  * ask harry.
  196.  * 
  197.  * Revision 1.52  92/02/12  13:25:12  jonathan
  198.  * Added "$Log" so RCS will put the log message in the header
  199.  * 
  200.  */
  201. /* ==================== */
  202.  
  203. /* ==================== */
  204. /*     To Do list       
  205.  *
  206.  * Implement a filename hashtable so that we can test quickly when
  207.  *   a file has been indexed.
  208.  * Free up all memory when we can. 
  209.  * Implement logrithmic merging
  210.  * 
  211.  * change DOC_TAB_ENTRY_FILENAME_ID_SIZE to 4 This must be in version 9
  212.  * change DOC_TAB_ENTRY_HEADLINE_ID_SIZE to 4 This must be in version 9
  213.  * change DOC_TAB_ENTRY_NUM_LINES_SIZE to 4 This must be in version 9
  214.  * change MAX_WORD_LENGTH to 15 This must be in version 9
  215.  */
  216.  
  217. /* A specification for this is called ir-engine.text in microsoft word. */
  218.  
  219. /* #include <string.h> /* for memset() */
  220. #include "irhash.h"
  221. #include "cutil.h"
  222. #include "irfiles.h"
  223. #include "panic.h"
  224. #include "ustubs.h" /* for strstr */
  225. #include "futil.h"
  226. #include "sockets.h"
  227. #include "version.h"
  228. #include "irext.h"
  229. #include "lock.h"
  230.  
  231. #ifdef FIELDS /* tung, 1/94 */
  232. #include "../config.h"
  233. #include "field_search.h"
  234. #endif
  235.  
  236.  extern char* keyword[50];
  237.  extern short nKeys;
  238.  char *descript[1000];
  239.  short nDesLines = 0;
  240.  
  241. #define PRINT_AS_INDEXING false /* also defined in irtfiles.c and irhash.c */
  242.  
  243. /*      -------------------------------    */
  244. #define DOC_TAB_HEADER_SIZE 2
  245. #define DOC_TAB_MAXIMUM_ENTRIES 8192
  246. #define DOC_TAB_ENTRY_FILENAME_ID_SIZE 3
  247. #define DOC_TAB_ENTRY_START_CHAR_SIZE 4
  248. #define DOC_TAB_ENTRY_END_CHAR_SIZE 4
  249. #ifdef HUGE_HEADLINES
  250. #define DOC_TAB_ENTRY_HEADLINE_ID_SIZE 4
  251. #else
  252. #define DOC_TAB_ENTRY_HEADLINE_ID_SIZE 3
  253. #endif
  254. #define DOC_TAB_ENTRY_DOC_LENGTH_SIZE 4
  255. #define DOC_TAB_ENTRY_NUM_LINES_SIZE 3
  256. #define DOC_TAB_ENTRY_DATE_SIZE 4
  257. #ifdef HUGE_HEADLINES
  258. #define DOC_TAB_ELEMENT_SIZE 26 /* sum of above sizes */
  259. #else
  260. #define DOC_TAB_ELEMENT_SIZE 25 /* sum of above sizes */
  261. #endif
  262. #define DICTIONARY_ENTRY_SIZE 29 /* sum of MAX_WORD_LENGTH, 1 ('\0'), 
  263.                     NEXT_INDEX_BLOCK_SIZE and
  264.                     NUMBER_OF_OCCURANCES_SIZE */
  265.  
  266.  
  267. #define FILENAME_TABLE_HEADER_SIZE 4
  268. #define HEADLINE_TABLE_HEADER_SIZE 4
  269. #ifdef BIO
  270. #define DELIMITERS_SIZE 4
  271. #endif
  272.  
  273. /*#define FILE_WRITE_DATE_SIZE 4*/
  274. #define NUMBER_OF_OCCURANCES_SIZE 4
  275. #define DOCUMENT_SCORE_LIMIT_SIZE 1
  276. #define DOCUMENT_SCORE_LIMIT 255  /* this is computed from DOCUMENT_SCORE_LIMIT_SIZE */
  277.  
  278. #define TIME_WAIT_QUERY_END 5
  279. #define TIMEOUT_WAIT_QUERY_END 45
  280.  
  281. static char* temp_dictionary_filename _AP((char* destination, database* db));
  282. #ifdef FIELDS /* tung, 12/93 */
  283. static char* field_temp_dictionary_filename _AP((char* destination,
  284.                                                  int field_id, database* db));
  285. #endif
  286. static long current_lock_type = INVALID_LOCK;
  287.  
  288. /*============================
  289.   ===   Database support   ===
  290.   ============================*/
  291.  
  292. #ifdef STEM_WORDS /* tung, 4/94 */
  293. boolean stemming = false;
  294. #endif
  295.  
  296. /* looks up the total word count in an existing dictionary. */
  297.  boolean look_up_total_word_count _AP((database *db));
  298.  boolean look_up_total_word_count(db)
  299. database *db;
  300. {
  301.   long word_count;
  302.   long answer = look_up_word_in_dictionary(DICTIONARY_TOTAL_SIZE_WORD,
  303.                        &word_count, db);
  304. #ifdef STEM_WORDS 
  305.   if(stemming)
  306.     db->stemming = true;
  307. #endif
  308.   if(answer == 0){
  309.     waislog(WLOG_HIGH, WLOG_ERROR,
  310.         "error finding total_word_count in dictionary %s\n", 
  311.         db->database_file);
  312.     return(false);
  313.   }
  314.   else if(answer < 0){
  315.     waislog(WLOG_HIGH, WLOG_ERROR,"total_word_count not found in dictionary\n.This is either an error,or the database is old.");
  316.     db->total_word_count = word_count;
  317.     return(false);
  318.   }
  319.   else{
  320.     db->total_word_count = word_count;
  321.   }
  322.   /* printf("Total Words in DB: %ld\n", db->total_word_count); */
  323.   return(true);
  324. }
  325.  
  326. #ifdef FIELDS /* tung, 1/94 */
  327. boolean field_look_up_total_word_count _AP((char* field_name, long field_id, database *db));
  328. boolean field_look_up_total_word_count(field_name, field_id, db)
  329. char* field_name;
  330. long field_id;
  331. database *db;
  332. {
  333.   long word_count;
  334.   long answer = field_look_up_word_in_dictionary(field_name, 
  335.                          DICTIONARY_TOTAL_SIZE_WORD,
  336.                                                  &word_count, db);
  337.   if(answer == 0){
  338.     waislog(WLOG_HIGH, WLOG_ERROR,
  339.         "error finding total_word_count in dictionary %s of field %s\n", 
  340.         db->database_file, field_name);
  341.     return(false);
  342.   }
  343.   else if(answer < 0){
  344.     waislog(WLOG_HIGH, WLOG_ERROR,"total_word_count not found in dictionary\n.This is either an error,or the database is old.");
  345.     db->fields[field_id].total_word_count = word_count;
  346.     return(false);
  347.   }
  348.   else{
  349.     db->fields[field_id].total_word_count = word_count;
  350.   }
  351.   /* printf("Total Words in DB: %ld\n", db->total_word_count); */
  352.   return(true);
  353. }
  354. #endif
  355.  
  356. #ifdef FIELDS /* tung, 3/94 */
  357. database*     
  358. openDatabase(name,initialize,for_search, type_field)
  359. #else
  360. openDatabase(name,initialize,for_search)
  361. #endif
  362. char* name;
  363. boolean initialize;
  364. boolean for_search;
  365. #ifdef FIELDS /* tung, 3/94 */
  366. boolean type_field;
  367. #endif
  368. {
  369.   /* open a database (open all its files), and return an opaque object.
  370.      return NULL if there is an error
  371.    */
  372.   unsigned long pid;
  373.   long timeout;
  374.   char file[MAX_FILE_NAME_LEN + 1 ];
  375.   char tmpfile[MAX_FILE_NAME_LEN + 1];
  376.   char open_mode[4];
  377.   database* db = (database*)s_malloc((size_t)sizeof(database));    
  378.   if (db == NULL){ 
  379.     waislog(WLOG_HIGH, WLOG_ERROR,
  380.         "can't make a database, out of memory.\n");
  381.     return(NULL);
  382.   }
  383.  
  384.   db->total_word_count = 0;
  385.  
  386.   if (for_search == true)  
  387.     strncpy(open_mode,"rb",3); /* read only for searching */
  388.   else 
  389.     strncpy(open_mode,"r+b",4); /* read/write for building */
  390.  
  391.   /* set the query parameter to the original name */
  392.   {
  393.     query_parameter_type parameters;
  394.     char **list;
  395.     list=(char **)s_malloc(2*sizeof(char*));
  396.     list[0]=s_strdup(name);
  397.     list[1]=NULL;
  398.     parameters.srcs = list;
  399.     set_query_parameter(SET_SELECT_SOURCE,¶meters);
  400.   }
  401.    
  402.   /* ask the backend where the database lives, but put in the 
  403.      directory information that we already have.  This changes
  404.      the 'name' variable. */
  405.   db->database_file = 
  406.     s_strdup(merge_pathnames(database_file(pathname_name(name)),
  407.                  pathname_directory(name, tmpfile)));
  408.   if (for_search == true) {
  409.   
  410.     /* check and set appropriate locks */
  411.  
  412.     if( utlk_using_lock(db->database_file, LOCK_UPDATE) ) {
  413.       waislog(WLOG_HIGH, WLOG_ERROR,
  414.        "can't search the database as an update is currently running");
  415.         return(NULL);
  416.       }
  417.     if ( utlk_set_lock(db->database_file, LOCK_QUERY) )
  418.        current_lock_type = LOCK_QUERY;
  419.     else
  420.       waislog(WLOG_LOW, WLOG_INFO, "query lock can't be set");
  421.  
  422.     }
  423.  
  424.   else {
  425.  
  426.     if( utlk_using_lock_and_get_pid(db->database_file, LOCK_INDEX, &pid) &&
  427.         (pid != getpid()) ) {
  428.       waislog(WLOG_HIGH, WLOG_ERROR,
  429.        "an indexing is currently running on the database. Try again later.");
  430.         return(NULL);
  431.       }
  432.     if (  utlk_set_lock(db->database_file, LOCK_INDEX) )
  433.        current_lock_type = LOCK_INDEX;
  434.     else
  435.       waislog(WLOG_LOW, WLOG_INFO, "index lock can't be set");
  436.     if ( initialize == true ) {
  437.      /* wait for current query finishing off */
  438.      timeout = 0;
  439.      while ( utlk_using_lock(db->database_file, LOCK_QUERY) ) {
  440.       if ( timeout >= TIMEOUT_WAIT_QUERY_END ) {
  441.          waislog(WLOG_HIGH, WLOG_ERROR,
  442.              "timed out in waiting for a query to finish. Try again later.");
  443.          utlk_unset_lock(db->database_file, LOCK_INDEX);
  444.          return(NULL);
  445.          }
  446.       waislog(WLOG_LOW, WLOG_INFO,
  447.              "waiting for a query to finish to initialize the database...");
  448.       sleep(TIME_WAIT_QUERY_END);
  449.       timeout += TIME_WAIT_QUERY_END;
  450.       }
  451.      if (  utlk_set_lock(db->database_file, LOCK_UPDATE) )
  452.        current_lock_type = LOCK_UPDATE;
  453.      else
  454.        waislog(WLOG_LOW, WLOG_INFO, "update lock can't be set");
  455.      }
  456.  
  457.     }
  458.   
  459.   if(initialize == true){
  460.     initialize_index_files(db);
  461.   } 
  462.   else     {
  463.  
  464.     /* if the global database does not exist it is not nessesary to open
  465.      * the dictionary_stream.
  466.      */
  467. #ifdef FIELDS /* tung, 3/94 */ 
  468.     if(type_field == false) {
  469. #endif
  470.       db->dictionary_stream = 
  471.         s_fopen(dictionary_filename(file, db),open_mode);
  472.       if (db->dictionary_stream == NULL){ 
  473.         waislog(WLOG_HIGH,WLOG_ERROR,"can't open the word hash file %s\n",file); 
  474.         disposeDatabase(db);
  475.         return(NULL);
  476.       }
  477. #ifdef FIELDS /* tung, 3/94 */
  478.     }
  479. #endif
  480.     /* find the total_word_count from the dictionary */
  481.     if(for_search){
  482. #ifdef FIELDS /* tung, 1/94 */
  483.       db->number_of_fields = 0;
  484. #endif
  485. #ifdef FIELDS /* tung, 3/94 */ 
  486.       if(type_field == false) {
  487. #endif
  488.         if(false == look_up_total_word_count(db)) { /* side effects db */
  489.           disposeDatabase(db);
  490.           return(NULL);
  491.         }
  492. #ifdef FIELDS /* tung, 3/94 */
  493.       }
  494. #endif
  495.     }
  496.     
  497.     db->filename_table_stream =
  498.       s_fopen(filename_table_filename(file, db),open_mode);
  499.     if (db->filename_table_stream == NULL){ 
  500.       waislog(WLOG_HIGH, WLOG_ERROR,
  501.           "can't open the filename file %s", file); 
  502.       disposeDatabase(db);
  503.       return(NULL);
  504.     }
  505.         
  506.     db->headline_table_stream = 
  507.       s_fopen(headline_table_filename(file, db),open_mode);
  508.     if (db->headline_table_stream == NULL){
  509.       waislog(WLOG_HIGH, WLOG_ERROR,
  510.           "can't open the headline file %s", file); 
  511.       disposeDatabase(db);
  512.       return(NULL);
  513.     }
  514.  
  515. #ifdef BIO
  516.     db->delimiters_stream = 
  517.       s_fopen(delimiters_filename(file, db),open_mode);
  518.     if (db->delimiters_stream == NULL){
  519.       waislog(WLOG_HIGH, WLOG_ERROR,
  520.           "can't open the delimiters file %s, using defaults", file); 
  521.       /* disposeDatabase(db); */
  522.       /* return(NULL); */
  523.     }
  524. #endif
  525.  
  526.     db->document_table_stream = 
  527.       s_fopen(document_table_filename(file, db),open_mode);
  528.     if (db->document_table_stream == NULL){ 
  529.       waislog(WLOG_HIGH, WLOG_ERROR,
  530.           "can't open the document id file %s", file); 
  531.       disposeDatabase(db);
  532.       return(NULL);
  533.     }
  534.       
  535.     /* initialize the allocated entries variable */
  536.     s_fseek(db->document_table_stream, 0L, SEEK_END);
  537.     db->doc_table_allocated_entries = 
  538.       (ftell(db->document_table_stream) - DOC_TAB_HEADER_SIZE) 
  539.     / DOC_TAB_ELEMENT_SIZE;
  540.   }
  541.   db->index_file_number = 0;
  542. #ifdef FIELDS /* tung, 3/94 */
  543.   if(type_field == false)
  544. #endif
  545.     ext_open_database(db,initialize,for_search);
  546.   return(db);
  547. }
  548.  
  549.  
  550. void        
  551. closeDatabase(db)
  552. database* db;
  553. /* close a database and all its files. Do not dispose of the structure. */
  554. {
  555.   
  556. #ifdef FIELDS /* tung, 12/93 */
  557.   int i, j, k;
  558. #endif
  559.   
  560.   if (db == NULL)
  561.     return;
  562.   close_dictionary_file(db);
  563.   if (db->dictionary_stream != NULL)
  564.     s_fclose(db->dictionary_stream);
  565.   if (db->filename_table_stream != NULL)
  566.     s_fclose(db->filename_table_stream);
  567.   if (db->headline_table_stream != NULL)
  568.     s_fclose(db->headline_table_stream);
  569.   if (db->document_table_stream != NULL)
  570.     s_fclose(db->document_table_stream);
  571.   if (db->index_stream != NULL)
  572.     s_fclose(db->index_stream);
  573.  
  574. #ifdef FIELDS /* tung, 12/93 */
  575.   if(db->index_fields != NULL) {
  576.     clear_fields(db);
  577.     if(db->field_dictionary_streams != NULL) {
  578.       if(*(db->field_dictionary_streams) != NULL)
  579.         s_fclose(*(db->field_dictionary_streams));
  580.       s_free(db->field_dictionary_streams);
  581.     }
  582.     if(db->field_index_streams != NULL) {
  583.       if(*(db->field_index_streams) != NULL)
  584.         s_fclose(*(db->field_index_streams));
  585.       s_free(db->field_index_streams);
  586.     }
  587.     for(i=0; i < db->number_of_fields; i++) {
  588.       if(db->fields[i].field_name != NULL)
  589.         s_free(db->fields[i].field_name);
  590.     }
  591.   }
  592.   else {
  593.     for(i=0; i < db->number_of_fields; i++) {
  594.       if (db->field_dictionary_streams != NULL) {
  595.         if (db->field_dictionary_streams[i] != NULL)
  596.           s_fclose(db->field_dictionary_streams[i]);
  597.       }
  598.       if (db->field_index_streams != NULL) {
  599.         if (db->field_index_streams[i] != NULL)
  600.           s_fclose(db->field_index_streams[i]);
  601.       }
  602.       if(db->fields != NULL) {
  603.         if(db->fields[i].field_name != NULL)
  604.           s_free(db->fields[i].field_name);
  605.       }
  606.     }
  607.     if (db->field_dictionary_streams != NULL)
  608.       s_free(db->field_dictionary_streams);
  609.     if (db->field_index_streams != NULL)
  610.       s_free(db->field_index_streams);
  611.   }
  612.   if(db->fields != NULL)
  613.     s_free(db->fields);
  614.   db->number_of_fields = 0;
  615. #endif
  616.   
  617.   ext_close_database(db);
  618.   utlk_unset_lock(db->database_file, current_lock_type);
  619.   if ( current_lock_type == LOCK_UPDATE)
  620.     utlk_unset_lock(db->database_file, LOCK_INDEX);
  621.   current_lock_type = INVALID_LOCK;
  622.   
  623. }
  624.  
  625. void 
  626. disposeDatabase(db)
  627. database* db;
  628. {
  629.     closeDatabase(db);
  630.     s_free(db->database_file);
  631.     s_free(db);
  632. }
  633.  
  634. /* ==================================== */
  635. /* ===  Initialization of the files === */
  636. /* ==================================== */
  637.  
  638. #define BLOCK_SIZE 16384 /* size of blocks of zeros to write to a file */
  639.  
  640. static FILE* initialize_file _AP((long size,char* filename,boolean zero_it));
  641.  
  642. static FILE* initialize_file(size,filename,zero_it)
  643. long size;
  644. char* filename;
  645. boolean zero_it;
  646. /* initializes a file by opening a new stream, making it the right
  647.  * size and returning the stream.
  648.  */
  649. {
  650.   FILE* file = NULL;
  651.   long i;
  652.  
  653. #ifdef ANSI_LIKE
  654.   remove(filename);
  655. #endif
  656.  
  657.   file = s_fopen(filename, "wb");
  658.   if(NULL == file){ 
  659.     panic("The file %s could not be opened\n", filename);
  660.   }
  661.  
  662.   if(zero_it){
  663.     if(size >= BLOCK_SIZE){    /* then write big blocks of zeros */
  664.       char* zeros = NULL;
  665.       zeros = (char*)s_malloc((size_t)BLOCK_SIZE);
  666.       if(NULL == zeros){
  667.     panic("Could not allocate a large block of Zeros\n");
  668.       }
  669.       memset(zeros, 0, BLOCK_SIZE);
  670.       while(size >= BLOCK_SIZE){    
  671.     /* then write big blocks of zeros */
  672.     if(BLOCK_SIZE != fwrite(zeros, 1, BLOCK_SIZE, file))
  673.       panic("Write failed");
  674.     size = size - BLOCK_SIZE;
  675.       }
  676.       s_free(zeros);
  677.     }
  678.     for(i = 0; i < size; i++){    /* clean up the rest */
  679.       putc('\0', file); 
  680.     }
  681.   }    
  682.   else{                /* dont zero it */
  683.     grow_file(file, size);
  684.   }    
  685.  
  686. #ifdef THINK_C
  687.   /* set the mac file type to INDX */
  688.   setFileType(filename, WAIS_INDEX_FILE_TYPE, CREATOR);
  689. #endif                /* THINK_C */
  690.  
  691.   s_fclose(file);
  692.   file = s_fopen(filename, "r+b"); /* open it in read/write */
  693.   if(NULL == file){
  694.     panic("Error in initialization, can not reopen %s.\n", filename);
  695.   }
  696.   return(file);
  697. }
  698.  
  699. void initialize_index_files (db)
  700. database* db;
  701. /* This creates new index files, deleting any old ones. */
  702. {
  703.   char file[MAX_FILENAME_LEN];
  704.  
  705.   /* cprintf(PRINT_AS_INDEXING, "initializing index files: %s\n", db->database_file); */
  706.  
  707.   remove(dictionary_filename(file, db)); /* remove the old one */
  708.  
  709.   db->index_stream = NULL;
  710.  
  711.   db->doc_table_allocated_entries = 1; /* the 0th is the null pointer */
  712.   db->document_table_stream =
  713.     initialize_file((DOC_TAB_HEADER_SIZE + DOC_TAB_ELEMENT_SIZE),
  714.             document_table_filename(file, db), TRUE);
  715.   db->filename_table_stream =
  716.     initialize_file(FILENAME_TABLE_HEADER_SIZE,
  717.             filename_table_filename(file, db), TRUE);
  718.   db->headline_table_stream =
  719.     initialize_file(HEADLINE_TABLE_HEADER_SIZE,
  720.             headline_table_filename(file, db), TRUE);
  721. #ifdef BIO
  722.   db->delimiters_stream =
  723.     initialize_file(DELIMITERS_SIZE,
  724.             delimiters_filename(file, db), TRUE);
  725. #endif
  726. }
  727.  
  728. /* ========================= */
  729. /* ===  Dictionary File  === */
  730. /* ========================= */
  731.  
  732. /* The dictionary file is a 1 deep tree of blocks.  
  733.    The header of the file says how long the header block is.
  734.    The "header block" is a set of pointers to the heads of
  735.    the blocks in the dictionary.
  736.  
  737.    A dictionary block is a list of word and pointer pairs.  The words
  738.    are padded to a fixed length so that it is a fixed length record.
  739.    The pointers are pointers into the inverted file (except in the header
  740.    block where they are pointers into the dictionary file).
  741. */
  742.  
  743. /*  SEARCHING DICTIONARY FILES */
  744.    
  745. /* top level function:
  746.    long look_up_word_in_dictionary(char *word, long *word_id, database* db) 
  747.  */
  748.  
  749. unsigned char *dictionary_header_block = NULL; /* the dictionary header. 
  750.                           loaded once */
  751. long number_of_dictionary_blocks = 0;  /* also the length of the dictionary 
  752.                       header block */
  753.  
  754. unsigned char *dictionary_block = NULL; /* this is one of the dict blocks */
  755.  
  756. int dictionary_last_word_occurances; /* This is a temporary hack so I can 
  757.                     separate out the relevance feedback 
  758.                     changes for posting. DON'T USE THIS 
  759.                     ANYWHERE - IT'LL BE GONE SOON
  760.                       */
  761.  
  762. #ifdef FIELDS /* tung, 12/93 */
  763. unsigned char *field_dictionary_header_block = NULL;
  764. long field_number_of_dictionary_blocks = 0;
  765. unsigned char *field_dictionary_block = NULL;
  766. int field_dictionary_last_word_occurances;
  767. #endif
  768.  
  769. void close_dictionary_file(db)
  770.      database *db;
  771. {
  772.   if(dictionary_header_block) s_free(dictionary_header_block);
  773.   dictionary_header_block = NULL;
  774. #ifdef FIELDS /* tung, 12/93 */
  775.   if(field_dictionary_header_block) s_free(field_dictionary_header_block);
  776.   field_dictionary_header_block = NULL;
  777. #endif
  778. }
  779.   
  780.   
  781. static long fread_from_stream _AP((FILE* stream,unsigned char* buf,
  782.                    long nbytes));
  783.  
  784. static long fread_from_stream(stream,buf,nbytes)
  785. FILE *stream;
  786. unsigned char *buf;
  787. long nbytes;
  788. /* this is a safe version of unix 'fread' it does all the checking
  789.  * and looping necessary
  790.  */
  791. {
  792.   long didRead;
  793.   long toRead = nbytes;
  794.   long totalRead = 0;        /* paranoia */
  795.   /*printf("in Fread_from_stream buffer %ld, nbytes %ld\n", (long)buf, nbytes); */
  796.  
  797.   while (toRead > 0){
  798.     didRead = fread(buf, sizeof(char), toRead, stream);
  799.     if(didRead == -1)        /* error*/
  800.       return(-1);
  801.     if(didRead == 0)        /* eof */
  802.       return(-2);        /* maybe this should return 0? */
  803.     toRead -= didRead;
  804.     buf += didRead;
  805.     totalRead += didRead;
  806.   }
  807.   if(totalRead != nbytes)    /* we overread for some reason */
  808.     return(- totalRead);    /* bad news */    
  809.   return(totalRead);
  810. }
  811.  
  812. #ifdef DICT_FUNC
  813. char *dictionary_block_word(i,block)
  814. long i;
  815. unsigned char *block;
  816. /* returns the word field in the ith dictionary block entry */
  817. {
  818.   return((char *)(block + (i * DICTIONARY_ENTRY_SIZE)));
  819. }
  820.  
  821. long dictionary_block_position(i,block)
  822. long i;
  823. unsigned char *block;
  824. /* returns the position field in the ith dictionary block entry */
  825. {
  826.   /* printf("dictionary_block_position %ld\n",
  827.      read_bytes_from_memory
  828.      (NEXT_INDEX_BLOCK_SIZE,
  829.       block + (i * DICTIONARY_ENTRY_SIZE) + 
  830.       MAX_WORD_LENGTH + 1)); */
  831.   return(read_bytes_from_memory
  832.      (NEXT_INDEX_BLOCK_SIZE,
  833.       block + (i * DICTIONARY_ENTRY_SIZE) + 
  834.       MAX_WORD_LENGTH + 1));
  835. }
  836.  
  837. long dictionary_block_word_occurances(i,block)
  838. long i;
  839. unsigned char *block;
  840. /* returns the occurances field in the ith dictionary block entry */
  841. {
  842.   return(read_bytes_from_memory
  843.      (NEXT_INDEX_BLOCK_SIZE,
  844.       block + (i * DICTIONARY_ENTRY_SIZE) + 
  845.       MAX_WORD_LENGTH + 1 + NEXT_INDEX_BLOCK_SIZE));
  846. }
  847. #endif
  848.  
  849.  
  850. #ifdef PARTIALWORD
  851.  
  852. typedef struct {
  853.     long  blocknum, wordcount;
  854.     } saveparttype;
  855.     
  856. static long gMaxpart = 0;
  857. static long gNpart = 0;
  858. static long gAtpart = 0;
  859. static saveparttype *gSavepart = NULL;
  860.  
  861. void clearPartMatch()
  862. {
  863.   if (gSavepart!=NULL) free(gSavepart);
  864.   gSavepart= NULL;
  865.   gMaxpart= 0;
  866.   gNpart= 0;
  867.   gAtpart= 0;
  868. }
  869.  
  870. void savePartMatch( blocknum, wordcount)
  871. long  blocknum, wordcount;
  872. {
  873.    if (gNpart>=gMaxpart) {
  874.      gMaxpart= gNpart + 100;
  875.      if (gSavepart==NULL) /*(saveparttype*)*/gSavepart= (saveparttype*)malloc(gMaxpart*sizeof(saveparttype));
  876.      else /*(saveparttype*)*/gSavepart= (saveparttype*)realloc(gSavepart, gMaxpart*sizeof(saveparttype));
  877.      }
  878.    gSavepart[gNpart].blocknum= blocknum;
  879.    gSavepart[gNpart].wordcount= wordcount;
  880.    gNpart++;
  881. }
  882. #endif
  883.  
  884. #ifdef FIELDS /* tung, 1/94 */
  885. long* dictionary_block_pos_set = NULL;
  886. long max_number_of_blocks = 0;
  887. long min_number_of_blocks = 0;
  888. char gnumeric_tag = 0;
  889. double current_search_number = 0;
  890. boolean gnumeric = false;
  891.  
  892. static void clearDctBlPsSet _AP((void));
  893. static void clearDctBlPsSet()
  894. {
  895.   if(dictionary_block_pos_set != NULL)
  896.     s_free(dictionary_block_pos_set);
  897.   max_number_of_blocks = 0;
  898.   min_number_of_blocks = 0;
  899.   gnumeric_tag = 0;
  900.   current_search_number = 0;
  901.   gnumeric = false;
  902. }
  903.  
  904. static void saveDctBlPsSet _AP((long blocknum));
  905. static void saveDctBlPsSet(blocknum)
  906.      long  blocknum;
  907. {
  908.   if(min_number_of_blocks >= max_number_of_blocks) {
  909.     max_number_of_blocks = min_number_of_blocks + 100;
  910.     if(dictionary_block_pos_set == NULL)
  911.       dictionary_block_pos_set  = 
  912.         (long*)s_malloc((size_t)(sizeof(long) * max_number_of_blocks));
  913.     else dictionary_block_pos_set  = 
  914.       (long*)s_realloc(dictionary_block_pos_set, 
  915.                        (size_t)(sizeof(long) * max_number_of_blocks));
  916.   }
  917.   dictionary_block_pos_set[min_number_of_blocks] = blocknum;
  918.   min_number_of_blocks++;
  919. }
  920.  
  921. static long handle_numeric _AP((long number_of_elements, long *number_of_occurances, char* field_name, unsigned char* block, database* db));
  922. static long handle_numeric(number_of_elements, number_of_occurances, field_name, block, db)
  923.      long number_of_elements;
  924.      long *number_of_occurances;
  925.      char* field_name;
  926.      unsigned char *block;
  927.      database* db;
  928. {
  929.   long i,j=0;
  930.   long field_id;
  931.   long block_length = DICTIONARY_BLOCK_SIZE;
  932.   FILE* stream = NULL;
  933.   
  934.   field_id = pick_up_field_id(field_name, db);
  935.   stream = db->field_dictionary_streams[field_id];
  936.   
  937.   for(i=0; i < number_of_elements; i++) {
  938.     long dictionary_block_pos = dictionary_block_pos_set[i];
  939.     block = 
  940.       read_dictionary_block(block, ABSOLUTE(dictionary_block_pos),
  941.                             DICTIONARY_BLOCK_SIZE,stream);
  942.     if(NULL == block)
  943.       { waislog(WLOG_HIGH, WLOG_ERROR,
  944.                 "Could not read dictionary block %ld in db %s",
  945.                 ABSOLUTE(dictionary_block_pos),
  946.                 db->database_file);
  947.         return(0);
  948.       }
  949.     j=0;
  950.     while(j<block_length) {
  951.       char* dictionary_word = dictionary_block_word(j, block);
  952.       if(dictionary_word[0] == '\0')
  953.         break;
  954.       else if(isnumchar(dictionary_word[0])) {
  955.         double dictionary_number;
  956.     if(dictionary_word[0] == '-') {
  957.       dictionary_number = atof(dictionary_word + 1) * (-1);
  958.     } else dictionary_number = atof(dictionary_word);
  959.         /* if(dictionary_number == 0.0) break; */
  960.         if(gnumeric_tag == GREATER) {
  961.           /* searching for position where 
  962.            * dictionary_number > current_search_number.
  963.            */
  964.           if(dictionary_number < current_search_number) {
  965.             long low = 0;
  966.             long high = block_length;
  967.             long idx = (low+high)/2;
  968.         boolean greater = false;
  969.             while(low != high) {  /* binary searching */
  970.               dictionary_word = dictionary_block_word(idx, block);
  971.               if(dictionary_word[0] == '\0' || 
  972.                  !strcmp(dictionary_word, DICTIONARY_TOTAL_SIZE_WORD)) {
  973.                 if(high != idx) {
  974.                   high = idx;
  975.                   idx = (low+idx)/2;
  976.                 } else {
  977.                   j = block_length; break;
  978.                 }
  979.               }
  980.               else if(isnumchar(dictionary_word[0])) {
  981.         if(dictionary_word[0] == '-') 
  982.           dictionary_number = atof(dictionary_word + 1) * (-1);
  983.         else dictionary_number = atof(dictionary_word);
  984.                /* if(dictionary_number == 0.0) break; */
  985.                 if(dictionary_number == current_search_number) {
  986.                   j = idx;
  987.                   break;
  988.                 }
  989.                 else if(dictionary_number > current_search_number) {
  990.                   if(high != idx) {
  991.                     high = idx;
  992.                     idx = (low+idx)/2;
  993.             greater = true; 
  994.                   } else {
  995.                     j = idx - 1; break;
  996.                   }
  997.                 }
  998.                 else {
  999.                   if (low != idx) {
  1000.                     low = idx;
  1001.                     idx = (0.5+high+idx)/2;
  1002.                   } else {
  1003.             if(greater) {
  1004.               j = idx;
  1005.               dictionary_word = dictionary_block_word(j+1, block);
  1006.               if(dictionary_word[0] == '-')
  1007.             dictionary_number = atof(dictionary_word + 1) * (-1);
  1008.               else dictionary_number = atof(dictionary_word);
  1009.               if(dictionary_number < current_search_number)
  1010.             j++;
  1011.               while(dictionary_number < current_search_number) {
  1012.             j++;
  1013.             dictionary_word = dictionary_block_word(j, block);
  1014.             if(dictionary_word[0] == '-')
  1015.               dictionary_number = atof(dictionary_word + 1) * (-1);
  1016.             else dictionary_number = atof(dictionary_word);
  1017.             if(dictionary_number > current_search_number)
  1018.               j--;
  1019.               }
  1020.               break;
  1021.             } else {
  1022.               j = block_length; break;
  1023.             }
  1024.                   }
  1025.                 }
  1026.               }
  1027.             }
  1028.           }
  1029.           else if(dictionary_number > current_search_number)
  1030.             savePartMatch(dictionary_block_position(j, block),
  1031.                           dictionary_block_word_occurances(j, block));
  1032.         }
  1033.         else if(gnumeric_tag == LESS) {
  1034.           if(dictionary_number < current_search_number)
  1035.             savePartMatch(dictionary_block_position(j, block),
  1036.                           dictionary_block_word_occurances(j, block));
  1037.           else break;
  1038.         }
  1039.       }
  1040.       ++j;
  1041.     }
  1042.   }
  1043. }
  1044. #endif
  1045.  
  1046. #ifdef FIELDS /* tung, 1/94 */
  1047. static long numeric_find_pointer_in_block _AP((char* word,
  1048.                                                 unsigned char* block,
  1049.                                                 long block_length,
  1050.                                                 long *position,
  1051.                                                 boolean numeric_init,
  1052.                                                 boolean numeric,
  1053.                                                 char* numeric_tag));
  1054.  
  1055. /* Courtesy of Simon Spero <ses@techunix.technion.ac.il> */
  1056.  
  1057. static long numeric_find_pointer_in_block(word,block,block_length, position,
  1058.                                            numeric_init, numeric, numeric_tag)
  1059. char *word;
  1060. unsigned char *block;
  1061. long block_length; /* in entries */ 
  1062. long *position;
  1063. boolean numeric_init;
  1064. boolean numeric;
  1065. char* numeric_tag;
  1066.  
  1067. /* returns 0 if an error or if the word is below the lowest block,
  1068.    (this confusion between error and NULL is bad, but found late in the 
  1069.    design process)
  1070.    it returns the positive position if the word is there exactly,
  1071.    and the negative of the position of the word before it if the
  1072.    word is not there exactly.
  1073.    position is set with the entry postion in the block that the word was 
  1074.    found.  This is used for searching.
  1075. */
  1076. {
  1077.   /* find the entry in the dictionary header for this word.
  1078.      returns 0 if not found. */
  1079.   /* this could be binary search XXX */
  1080.   long i,high,low,tmp, prev_i;
  1081. #ifdef PARTIALWORD
  1082.   long  wordlen= strlen(word);
  1083. #endif
  1084.  
  1085.   low = 0;
  1086.   high = block_length;
  1087.   i = (low+high)/2;
  1088.  
  1089.   if(numeric_init) {
  1090.     if(!strcmp(DICTIONARY_TOTAL_SIZE_WORD, word)) {
  1091.       i = block_length - 1;
  1092.       *position = i;
  1093.       return(- dictionary_block_position(i,block));
  1094.     }
  1095.   }
  1096.  
  1097.   while(low != high) {
  1098.     double compare;
  1099.     char *dictionary_word = dictionary_block_word(i, block);
  1100.     int dct_sign = 1;
  1101.     double dct_number;
  1102.  
  1103.     if(dictionary_word[0] == '\0') {
  1104.       if(high != i) {
  1105.     high = i;
  1106.     i = (low+i)/2;
  1107.       } else {
  1108.     *position = i-1;
  1109.     return(- dictionary_block_position(i-1,block));
  1110.       }
  1111.     } 
  1112.     else {
  1113.       if(!strcmp(DICTIONARY_TOTAL_SIZE_WORD, word)) {
  1114.         compare = -1.0;
  1115.         if(!strcmp(dictionary_word, DICTIONARY_TOTAL_SIZE_WORD)) {
  1116.           *position = i;
  1117.           return(dictionary_block_position(i, block));
  1118.         }
  1119.       }
  1120.       else {
  1121.         if(!strcmp(dictionary_word, DICTIONARY_TOTAL_SIZE_WORD))
  1122.           compare = 1.0;
  1123.         else {
  1124.       if(dictionary_word[0] == '-') {
  1125.         dct_sign = -1;
  1126.         dictionary_word = dictionary_word + 1;
  1127.       }
  1128.       dct_number = atof(dictionary_word) * dct_sign;
  1129.       if(dct_number > current_search_number) compare = 1;
  1130.       else if(dct_number < current_search_number) compare = -1;
  1131.       else compare = 0;
  1132.     }
  1133.       }
  1134.       if(compare == 0.0) {
  1135.         if(*numeric_tag == EQ) {
  1136.           *position = i;
  1137.           return(dictionary_block_position(i, block));
  1138.         }
  1139.         else if(*numeric_tag == GREATER) {
  1140.           prev_i = i;
  1141.           while(i < block_length) {
  1142.             saveDctBlPsSet(dictionary_block_position(i, block));
  1143.             ++i;
  1144.             gnumeric = true;
  1145.           }
  1146.           *position = prev_i;
  1147.           return(-dictionary_block_position(prev_i, block));
  1148.         }
  1149.         else if(*numeric_tag == LESS) {
  1150.           prev_i = --i;
  1151.           while(i >= 0) {
  1152.             saveDctBlPsSet(dictionary_block_position(i, block));
  1153.             --i;
  1154.             gnumeric = true;
  1155.           }
  1156.           *position = prev_i;
  1157.           return(-dictionary_block_position(prev_i, block));
  1158.         }
  1159.       }
  1160.       else if(compare > 0.0) {
  1161.         if(high != i) {
  1162.           high = i;
  1163.           i = (low+i)/2;
  1164.         } 
  1165.         else {
  1166.           if(*numeric_tag == EQ) {
  1167.             *position = i-1;
  1168.             return(- dictionary_block_position(i-1 , block));
  1169.           }
  1170.           else if(*numeric_tag == GREATER) {
  1171.             i = 0;
  1172.             prev_i = i;
  1173.             while(i < block_length) {
  1174.               saveDctBlPsSet(dictionary_block_position(i, block));
  1175.               ++i;
  1176.               gnumeric = true;
  1177.             }
  1178.             *position = prev_i;
  1179.             return(-dictionary_block_position(prev_i, block));
  1180.           }
  1181.           else if(*numeric_tag == LESS) {
  1182.             *numeric_tag = EQ; /* result is empty */
  1183.             *position = i-1;
  1184.             return(-dictionary_block_position(i-1, block));
  1185.           }
  1186.         }
  1187.       } 
  1188.       else {
  1189.         if (low != i) {
  1190.           low = i;
  1191.           i = (0.5+high+i)/2;
  1192.         } 
  1193.         else {
  1194.           if(*numeric_tag == EQ) {
  1195.             *position = i;
  1196.             return(- dictionary_block_position(i , block));
  1197.           }
  1198.           else if(*numeric_tag == GREATER) {
  1199.             prev_i = i;
  1200.             while(i < block_length) {
  1201.               saveDctBlPsSet(dictionary_block_position(i, block));
  1202.               ++i;
  1203.               gnumeric = true;
  1204.             }
  1205.             *position = prev_i;
  1206.             return(-dictionary_block_position(prev_i, block));
  1207.           }
  1208.           else if(*numeric_tag == LESS) {
  1209.             prev_i = i;
  1210.             while(i >= 0) {
  1211.               saveDctBlPsSet(dictionary_block_position(i, block));
  1212.               --i;
  1213.               gnumeric = true;
  1214.             }
  1215.             *position = prev_i;
  1216.             return(-dictionary_block_position(prev_i, block));
  1217.           }
  1218.         }
  1219.       }
  1220.     }
  1221.   }
  1222.   
  1223.   if(i == 0) {
  1224.     if(*numeric_tag == GREATER) {
  1225.       while(i < block_length) {
  1226.         saveDctBlPsSet(dictionary_block_position(i, block));
  1227.         i++;
  1228.         gnumeric = true;
  1229.       }
  1230.       *position = 0;
  1231.       return(- dictionary_block_position(0, block));
  1232.     }
  1233.     else {
  1234.       gnumeric = false;
  1235.       *position = 0;
  1236.       return(0);
  1237.     }
  1238.   }
  1239.   else {
  1240.     gnumeric = false;
  1241.     *position = i-1;
  1242.     return(- dictionary_block_position(i - 1, block));
  1243.   }
  1244. }
  1245. #endif
  1246.  
  1247. static long find_pointer_in_block _AP((char* word,
  1248.                                        unsigned char* block,
  1249.                        long block_length,
  1250.                        long *position,
  1251.                        boolean findpart,
  1252.                                        boolean findpart_init));
  1253.  
  1254. /* Courtesy of Simon Spero <ses@techunix.technion.ac.il> */
  1255.  
  1256. static long find_pointer_in_block(word,block,block_length,position,findpart,findpart_init)
  1257. char *word;
  1258. unsigned char *block;
  1259. long block_length; /* in entries */ 
  1260. long *position;
  1261. boolean findpart;  /* dgg, partial word match */
  1262. boolean findpart_init;
  1263.  
  1264. /* returns 0 if an error or if the word is below the lowest block,
  1265.    (this confusion between error and NULL is bad, but found late in the 
  1266.    design process)
  1267.    it returns the positive position if the word is there exactly,
  1268.    and the negative of the position of the word before it if the
  1269.    word is not there exactly.
  1270.    position is set with the entry postion in the block that the word was 
  1271.    found.  This is used for searching.
  1272. */
  1273. {
  1274.   /* find the entry in the dictionary header for this word.
  1275.      returns 0 if not found. */
  1276.   /* this could be binary search XXX */
  1277.   long i,high,low,tmp;
  1278. #ifdef PARTIALWORD
  1279.   long  wordlen= strlen(word);
  1280. #endif
  1281.   
  1282.   low = 0;
  1283.   high = block_length;
  1284.   i = (low+high)/2;
  1285.   while(low != high) {
  1286.     long compare;
  1287.     char *dictionary_word = dictionary_block_word(i, block);
  1288.     /* 
  1289.      * printf("dw = %s, w = %s, low = %d, i = %d, hi = %d\n",
  1290.      * dictionary_word,word,low,i,high);
  1291.      */
  1292.     if(dictionary_word[0] == '\0') {
  1293.       if(high != i) {
  1294.     high = i;
  1295.     i = (low+i)/2;
  1296.       } else {
  1297.     *position = i-1;
  1298.     return(- dictionary_block_position(i-1,block));
  1299.       }
  1300.     } else {
  1301. #ifdef PARTIALWORD
  1302.       if (findpart) {
  1303.         compare = strncmp(dictionary_word, word, wordlen);
  1304.         if (0 == compare) {
  1305.           int  ati = i;
  1306.           /* save partword matches for later... */
  1307.           savePartMatch( dictionary_block_position(i, block),
  1308.                         dictionary_block_word_occurances(i,block));
  1309.           while (i>low && 0 == compare) {
  1310.             --i;
  1311.             dictionary_word = dictionary_block_word(i, block);
  1312.             compare = strncmp(dictionary_word, word, wordlen);
  1313.             if (0 == compare) savePartMatch(dictionary_block_position(i, block),
  1314.                                             dictionary_block_word_occurances(i,block));
  1315.       }
  1316.           i= ati;
  1317.           compare = 0;
  1318.           while (i<high && 0 == compare) {
  1319.             ++i;
  1320.             dictionary_word = dictionary_block_word(i, block);
  1321.             compare = strncmp(dictionary_word, word, wordlen);
  1322.             if (0 == compare) savePartMatch(dictionary_block_position(i, block),
  1323.                                             dictionary_block_word_occurances(i,block));
  1324.           }
  1325.           *position = ati;
  1326.           return(dictionary_block_position(ati, block));
  1327.         }
  1328.       }
  1329.       else {
  1330.         if(findpart_init)
  1331.           compare = strncmp(dictionary_word, word, wordlen);
  1332.         else {
  1333. #ifdef STEM_WORDS
  1334.       long dctwrd_len = strlen(dictionary_word);
  1335.       if(!strcmp(word, DICTIONARY_TOTAL_SIZE_WORD) &&
  1336.          !strncmp(dictionary_word, DICTIONARY_TOTAL_SIZE_WORD, strlen(DICTIONARY_TOTAL_SIZE_WORD)) &&
  1337.          (dictionary_word[dctwrd_len - 1] == STEMMING_TAG)) {
  1338.         dictionary_word[dctwrd_len - 1] = '\0';
  1339.         stemming = true;
  1340.       } 
  1341. #endif
  1342.       compare = strcmp(dictionary_word, word);
  1343.     }
  1344.       }
  1345. #else
  1346.       compare = strcmp(dictionary_word, word);
  1347. #endif        
  1348.       
  1349.       if(0 == compare) {
  1350.     dictionary_last_word_occurances = 
  1351.       dictionary_block_word_occurances(i,block);
  1352. #ifdef PARTIALWORD
  1353.         if(findpart_init) {
  1354.           long prev_i = i;
  1355.           if(i > 0)
  1356.             saveDctBlPsSet(dictionary_block_position(i-1, block));
  1357.           saveDctBlPsSet(dictionary_block_position(i, block));
  1358.           i++;
  1359.           dictionary_word = dictionary_block_word(i, block);
  1360.           if(strncmp(dictionary_word, word, wordlen)) {
  1361.             *position = prev_i;
  1362.             return(dictionary_block_position(prev_i, block));
  1363.           }
  1364.           while(i <= block_length) {
  1365.             dictionary_word = dictionary_block_word(i, block);
  1366.             if(!strncmp(dictionary_word, word, wordlen)) 
  1367.               saveDctBlPsSet(dictionary_block_position(i, block));
  1368.             ++i;
  1369.           }
  1370.           *position = prev_i;
  1371.           return(dictionary_block_position(prev_i, block));
  1372.         }
  1373.         else {
  1374.           *position = i;
  1375.           return(dictionary_block_position(i, block));
  1376.         }
  1377. #else
  1378.         *position = i;
  1379.         return(dictionary_block_position(i, block));
  1380. #endif
  1381.       }
  1382.       if(compare > 0){
  1383.     if(high != i) {
  1384.       high = i;
  1385.       i = (low+i)/2;
  1386.         } else {
  1387.           *position = i-1;
  1388.           return(- dictionary_block_position(i-1 , block));
  1389.         }
  1390.       } else {
  1391.     if (low != i) {
  1392.       low = i;
  1393.       i = (0.5+high+i)/2;
  1394.     } else {
  1395.       *position = i;
  1396.       return(- dictionary_block_position(i , block));
  1397.     }
  1398.       }
  1399.     }
  1400.   }
  1401.   if(i == 0) {
  1402.     *position = 0;
  1403.     return(0);
  1404.   }
  1405.   else {
  1406.     *position = i-1;
  1407.     return(- dictionary_block_position(i - 1, block));
  1408.   }
  1409. }
  1410.  
  1411.  
  1412. unsigned char *read_dictionary_block(block,position,length,stream)
  1413. unsigned char *block;
  1414. long position;
  1415. long length;
  1416. FILE *stream;
  1417. /* reads the dictionary block from the disk and returns it.
  1418.    block is the place to put it, if it is NULL, then it is malloc'ed.
  1419.    position is the position in the dictionary file to start reading.
  1420.    length is th enumber of entries (not bytes) in the block.
  1421.    stream is the dictionary stream.
  1422.    
  1423.    it returns NULL if it loses.
  1424.  */
  1425.     
  1426. {
  1427.   static long last_position = -1;
  1428.   static unsigned char* last_block = NULL;
  1429.   static FILE* last_dict_file = NULL; /* there may be more than one dict */
  1430.  
  1431.   if (stream != last_dict_file)
  1432.    { /* invalidate the cache */
  1433.      last_position = -1;
  1434.      last_dict_file = stream;
  1435.    }
  1436.   
  1437.   if(NULL == block)
  1438.     block = (unsigned char *)s_malloc((size_t)(length*DICTIONARY_ENTRY_SIZE));
  1439.  
  1440.   if ((block != last_block) || 
  1441.       (position != last_position)) {
  1442.     last_position = position;
  1443.     last_block = block;
  1444.     s_fseek(stream, position, SEEK_SET);
  1445.     if(0 > fread_from_stream(stream, block, (length * DICTIONARY_ENTRY_SIZE))){
  1446.       waislog(WLOG_HIGH, WLOG_ERROR,
  1447.           "Could not read the dictionary block %ld, length %ld",
  1448.           block, length);
  1449.       return(NULL);
  1450.     }
  1451.   }
  1452.   return(block);
  1453. }
  1454.  
  1455.  
  1456. #ifdef PARTIALWORD
  1457. #ifdef FIELDS /* tung, 1/94 */
  1458. long look_up_partialword_in_dictionary(field_name,word,number_of_occurances,db)
  1459. #else
  1460. long look_up_partialword_in_dictionary(word,number_of_occurances,db)
  1461. #endif
  1462. #ifdef FIELDS /* tung, 1/94 */
  1463. char* field_name;
  1464. #endif
  1465. char *word;
  1466. long *number_of_occurances;
  1467. database* db;
  1468. {
  1469.   long answer;
  1470.   boolean findpart = false;
  1471.  
  1472.   if (word != NULL) {
  1473.     clearPartMatch();
  1474. #ifdef FIELDS /* tung, 1/94 */
  1475.     clearDctBlPsSet();
  1476.     
  1477.     if(field_name[0] == '\0')
  1478.       answer= look_up_word_in_dictionary(word, number_of_occurances, db);
  1479.     else answer= field_look_up_word_in_dictionary(field_name, word, 
  1480.                                                   number_of_occurances, db);
  1481. #else
  1482.     answer= look_up_word_in_dictionary( word, number_of_occurances, db);
  1483. #endif
  1484.     if (answer > 0) return (answer); /* got a match */
  1485.     }
  1486.    
  1487. #ifdef FIELDS /* tung, 1/94 */ 
  1488.   if(gnumeric) {
  1489.     if(min_number_of_blocks > 0) {
  1490.       answer = handle_numeric(min_number_of_blocks, number_of_occurances, 
  1491.                                field_name, field_dictionary_block, db);
  1492.       min_number_of_blocks = 0;
  1493.       gnumeric = false;
  1494.     }
  1495.   }
  1496. #endif
  1497.  
  1498.   {
  1499.     if (gAtpart >= gNpart) {
  1500.       clearPartMatch();
  1501.       return(-1);
  1502.     }
  1503.     else {
  1504.       answer= gSavepart[gAtpart].blocknum;
  1505.       if (NULL != number_of_occurances) {
  1506.         if (answer > 0) *number_of_occurances = gSavepart[gAtpart].wordcount;
  1507.         else *number_of_occurances = 0;
  1508.       }
  1509.       gAtpart++;
  1510.       return( answer);
  1511.     }
  1512.   }
  1513. }
  1514. #endif
  1515.  
  1516. long 
  1517. look_up_word_in_dictionary(word, number_of_occurances, db)
  1518. char *word;
  1519. long *number_of_occurances;
  1520. database* db;
  1521. /* looks up the word in the dictionary file. Returns the pointer
  1522.    into the inverted file or negative number if not found, 
  1523.    or 0 if error.
  1524.     It sets number_of_occurances (if it is not NULL) to the number
  1525.       registered in the file.  This is used during searching.  
  1526.       It is set to 0 if error or word not found.
  1527.       If it is NULL, then it is not touched.
  1528.  */
  1529. {
  1530.   long position,prev_position = 0;
  1531.   long answer,prev_answer;
  1532.   FILE *stream = db->dictionary_stream;
  1533.   long dictionary_block_pos;
  1534.   boolean findpart = false; /* dgg, PARTIALWORD flag */
  1535.  
  1536. #ifdef PARTIALWORD
  1537.   {
  1538.     int l = strlen(word) - 1;
  1539.     if (l > 0 && word[l] == PARTWORD_WILDCARD) {
  1540.       findpart= true;
  1541.       word[l]= '\0';
  1542.     }
  1543.   }
  1544. #endif
  1545.   
  1546.   if(NULL == dictionary_header_block)
  1547.    {
  1548.      s_fseek(stream, 0L, SEEK_SET);
  1549.      number_of_dictionary_blocks = read_bytes(DICTIONARY_HEADER_SIZE,stream);
  1550.      dictionary_header_block =
  1551.        read_dictionary_block(dictionary_header_block,DICTIONARY_HEADER_SIZE,
  1552.                  number_of_dictionary_blocks,stream);
  1553.      if(NULL == dictionary_header_block)
  1554.       {    waislog(WLOG_HIGH, WLOG_ERROR,
  1555.         "Could not read dictionary header block in db %s.",
  1556.         db->database_file);
  1557.     return(0);
  1558.       }
  1559.    }
  1560.   
  1561. #ifdef PARTIALWORD  
  1562.   if(findpart)
  1563.     dictionary_block_pos = 
  1564.       find_pointer_in_block(word,
  1565.                             dictionary_header_block,
  1566.                             number_of_dictionary_blocks,
  1567.                             &position, false, true);
  1568.   else
  1569. #endif
  1570.     dictionary_block_pos = 
  1571.       find_pointer_in_block(word,
  1572.                             dictionary_header_block,
  1573.                             number_of_dictionary_blocks,
  1574.                             &position, false, false);
  1575.   
  1576.   if(0 == dictionary_block_pos)
  1577.    { /* waislog(WLOG_HIGH, WLOG_ERROR, "Could not find pointer for word '%s' (location %ld) in block in db %s!",
  1578.          word, word, db->database_file); */
  1579.      return(-1);  /* not an error, necessarily if the word is before the first entry */
  1580.    }
  1581.   
  1582. #ifdef PARTIALWORD
  1583.   if(findpart && min_number_of_blocks) { /* tung, 3/94 */
  1584.     while(min_number_of_blocks > 0) {
  1585.       dictionary_block_pos = dictionary_block_pos_set[min_number_of_blocks-1];
  1586.       dictionary_block = 
  1587.         read_dictionary_block(dictionary_block,ABSOLUTE(dictionary_block_pos),
  1588.                               DICTIONARY_BLOCK_SIZE,stream);
  1589.       if(NULL == dictionary_block)
  1590.         { waislog(WLOG_HIGH, WLOG_ERROR,
  1591.                   "Could not read dictionary block %ld in db %s",
  1592.                   ABSOLUTE(dictionary_block_pos),
  1593.                   db->database_file);
  1594.           return(0);
  1595.         }
  1596.       answer = find_pointer_in_block(word, dictionary_block, 
  1597.                                      DICTIONARY_BLOCK_SIZE, 
  1598.                                      &position, findpart,false);
  1599.       if(prev_position == 0) {
  1600.         prev_position = position;
  1601.         prev_answer = answer;
  1602.       }
  1603.       --min_number_of_blocks;
  1604.     }
  1605.   }
  1606.   else {
  1607. #endif  
  1608.     dictionary_block = 
  1609.       read_dictionary_block(dictionary_block,ABSOLUTE(dictionary_block_pos),
  1610.                             DICTIONARY_BLOCK_SIZE,stream);
  1611.     if(NULL == dictionary_block)
  1612.       { waislog(WLOG_HIGH, WLOG_ERROR,
  1613.                 "Could not read dictionary block %ld in db %s",
  1614.                 ABSOLUTE(dictionary_block_pos),
  1615.                 db->database_file);
  1616.         return(0);
  1617.       }
  1618.     answer = find_pointer_in_block(word, dictionary_block, 
  1619.                                    DICTIONARY_BLOCK_SIZE, 
  1620.                                    &position, findpart,false);
  1621. #ifdef PARTIALWORD 
  1622.   }
  1623. #endif
  1624.   
  1625. #ifdef PARTIALWORD 
  1626.   if(findpart && prev_position) { /* tung, 3/94 */
  1627.     if((NULL != number_of_occurances)) {
  1628.       if (prev_answer > 0)
  1629.         *number_of_occurances = 
  1630.           dictionary_block_word_occurances(prev_position, dictionary_block);
  1631.       else
  1632.         *number_of_occurances = 0;
  1633.     }
  1634.     answer = prev_answer;
  1635.   }
  1636.   else
  1637. #endif
  1638.   if((NULL != number_of_occurances)) {
  1639.     if (answer > 0)
  1640.       *number_of_occurances = 
  1641.         dictionary_block_word_occurances(position, dictionary_block);
  1642.     else
  1643.       *number_of_occurances = 0;
  1644.   }
  1645.   
  1646.   return(answer);
  1647. }
  1648.  
  1649. #ifdef FIELDS /* tung, 12/93 */
  1650. long 
  1651. field_look_up_word_in_dictionary(field_name, word, number_of_occurances, db)
  1652. char* field_name;
  1653. char *word;
  1654. long *number_of_occurances;
  1655. database* db;
  1656. /* looks up the word in the dictionary file. Returns the pointer
  1657.    into the inverted file or negative number if not found, 
  1658.    or 0 if error.
  1659.     It sets number_of_occurances (if it is not NULL) to the number
  1660.       registered in the file.  This is used during searching.  
  1661.       It is set to 0 if error or word not found.
  1662.       If it is NULL, then it is not touched.
  1663.  */
  1664. {
  1665.   long field_id;
  1666.   long position, prev_position = 0;
  1667.   long answer, prev_answer;
  1668.   FILE *stream; /* = db->dictionary_stream; */
  1669.   long dictionary_block_pos;
  1670.   boolean findpart = false; /* dgg, PARTIALWORD flag */
  1671.  
  1672.   boolean numeric = false;
  1673.   char numeric_tag;
  1674.  
  1675.   field_id = pick_up_field_id(field_name, db);
  1676.  
  1677.   if(field_id == -1) {
  1678.     *number_of_occurances = 0;
  1679.     return(-1);
  1680.   }
  1681.   stream = db->field_dictionary_streams[field_id];
  1682.  
  1683. #ifdef PARTIALWORD
  1684.   {
  1685.     int l = strlen(word) - 1;
  1686.     if (l > 0 && word[l] == PARTWORD_WILDCARD) {
  1687.       findpart= true;
  1688.       word[l]= '\0';
  1689.       }
  1690.   }
  1691. #endif
  1692.  
  1693.   {
  1694.     char *tmp;
  1695.     int word_sign = 1;
  1696.     /* word is =1990 or <1990 or >1990 */
  1697.     if(strlen(word) >  0 && ((word[0] == EQ) || (word[0] == GREATER) 
  1698.                   || (word[0] == LESS))) {
  1699.       numeric = true;
  1700.       numeric_tag = word[0];
  1701.       if(word[1] == '-') {
  1702.     tmp = word + 2;
  1703.     word_sign = -1;
  1704.       } else tmp = word + 1; /* ignore first character */
  1705.       word = tmp;
  1706.       gnumeric_tag = numeric_tag;
  1707.       if((tmp = strchr(word, ','))) /* if a number is a real number, */
  1708.         word[tmp - word] = '.';     /* e.g. 12,5 -> 12.5 */
  1709.       current_search_number = atof(word) * word_sign;
  1710.     }
  1711.   }
  1712.   
  1713.   field_dictionary_header_block = NULL;
  1714.   if(NULL == field_dictionary_header_block)
  1715.    {
  1716.      s_fseek(stream, 0L, SEEK_SET);
  1717.      field_number_of_dictionary_blocks = read_bytes(DICTIONARY_HEADER_SIZE,stream);
  1718.      field_dictionary_header_block =
  1719.        read_dictionary_block(field_dictionary_header_block,
  1720.                              DICTIONARY_HEADER_SIZE,
  1721.                  field_number_of_dictionary_blocks,stream);
  1722.      if(NULL == field_dictionary_header_block)
  1723.       {    waislog(WLOG_HIGH, WLOG_ERROR,
  1724.         "Could not read dictionary header block of field %s in db %s.",
  1725.         field_name, db->database_file);
  1726.         s_free(field_dictionary_header_block);
  1727.     return(0);
  1728.       }
  1729.    }
  1730.  
  1731.   if(numeric) { /* word is a number */
  1732.     dictionary_block_pos = 
  1733.       numeric_find_pointer_in_block(word,
  1734.                                     field_dictionary_header_block,
  1735.                                     field_number_of_dictionary_blocks,
  1736.                                     &position, false, numeric, &numeric_tag);
  1737.     if(numeric_tag != EQ) {
  1738.       s_free(field_dictionary_header_block);
  1739.       return(-1);
  1740.     }
  1741.   }
  1742.   else
  1743. #ifdef PARTIALWORD    
  1744.     if(findpart)   /* word is a partial word */
  1745.       dictionary_block_pos = 
  1746.         find_pointer_in_block(word,
  1747.                               field_dictionary_header_block,
  1748.                               field_number_of_dictionary_blocks,
  1749.                               &position, false,true);
  1750.     else
  1751. #endif
  1752.       { /* if a field is a numeric field and the search word is "{}" */
  1753.         if(db->fields[field_id].numeric)
  1754.           dictionary_block_pos = 
  1755.             numeric_find_pointer_in_block(word,
  1756.                                           field_dictionary_header_block,
  1757.                                           field_number_of_dictionary_blocks,
  1758.                                           &position, true, numeric, 
  1759.                                           &numeric_tag);
  1760.         else /* it is not field */
  1761.           dictionary_block_pos = 
  1762.             find_pointer_in_block(word,
  1763.                                   field_dictionary_header_block,
  1764.                                   field_number_of_dictionary_blocks,
  1765.                                   &position, false, false);
  1766.       }
  1767.   s_free(field_dictionary_header_block);
  1768.   if(0 == dictionary_block_pos)
  1769.     { /* waislog(WLOG_HIGH, WLOG_ERROR, "Could not find pointer for word '%s' (location %ld) in block in db %s!",
  1770.          word, word, db->database_file); */
  1771.       return(-1);  /* not an error, necessarily if the word is before the first entry */
  1772.     }
  1773.  
  1774. #ifdef PARTIALWORD
  1775.   if(findpart && min_number_of_blocks) { /* tung, 3/94 */
  1776.     while(min_number_of_blocks > 0) {
  1777.       dictionary_block_pos = dictionary_block_pos_set[min_number_of_blocks-1];
  1778.       dictionary_block = 
  1779.         read_dictionary_block(dictionary_block,ABSOLUTE(dictionary_block_pos),
  1780.                               DICTIONARY_BLOCK_SIZE,stream);
  1781.       if(NULL == dictionary_block)
  1782.         { waislog(WLOG_HIGH, WLOG_ERROR,
  1783.                   "Could not read dictionary block %ld in db %s",
  1784.                   ABSOLUTE(dictionary_block_pos),
  1785.                   db->database_file);
  1786.           return(0);
  1787.         }
  1788.       answer = find_pointer_in_block(word, dictionary_block, 
  1789.                                      DICTIONARY_BLOCK_SIZE, 
  1790.                                      &position, findpart,false);
  1791.       if(prev_position == 0) {
  1792.         prev_position = position;
  1793.         prev_answer = answer;
  1794.       }
  1795.       --min_number_of_blocks;
  1796.     }
  1797.   }
  1798.   else {
  1799. #endif
  1800.     field_dictionary_block = 
  1801.       read_dictionary_block(field_dictionary_block,ABSOLUTE(dictionary_block_pos),
  1802.                             DICTIONARY_BLOCK_SIZE,stream);
  1803.     if(NULL == field_dictionary_block)
  1804.       { waislog(WLOG_HIGH, WLOG_ERROR,
  1805.                 "Could not read dictionary block %ld of field in db %s",
  1806.                 ABSOLUTE(dictionary_block_pos),
  1807.                 field_name, db->database_file);
  1808.         return(0);
  1809.       }
  1810. #ifdef FIELDS /* tung, 1/94 */
  1811.     if(numeric) 
  1812.       answer = numeric_find_pointer_in_block(word,
  1813.                                              field_dictionary_block,
  1814.                                              DICTIONARY_BLOCK_SIZE,
  1815.                                              &position, false, numeric, 
  1816.                                              &numeric_tag);
  1817.     else {
  1818.       /* if a field is a numeric field and the search word is "{}" */
  1819.       if(db->fields[field_id].numeric) 
  1820.         answer = numeric_find_pointer_in_block(word,
  1821.                                                field_dictionary_block,
  1822.                                                DICTIONARY_BLOCK_SIZE,
  1823.                                                &position, false, numeric, 
  1824.                                                &numeric_tag);
  1825.       else
  1826. #endif
  1827.         answer = find_pointer_in_block(word, field_dictionary_block, 
  1828.                                        DICTIONARY_BLOCK_SIZE, &position,
  1829.                                        findpart,false);
  1830. #ifdef PARTIALWORD
  1831.     }
  1832. #endif
  1833. #ifdef FIELDS /* tung, 1/94 */
  1834.   }
  1835. #endif
  1836.  
  1837. #ifdef PARTIALWORD 
  1838.   if(findpart && prev_position) { /* tung, 3/94 */
  1839.     if((NULL != number_of_occurances)) {
  1840.       if (prev_answer > 0)
  1841.         *number_of_occurances = 
  1842.           dictionary_block_word_occurances(prev_position, dictionary_block);
  1843.       else
  1844.         *number_of_occurances = 0;
  1845.     }
  1846.     answer = prev_answer;
  1847.   }
  1848.   else
  1849. #endif
  1850.     if((NULL != number_of_occurances)) {
  1851.       if (answer > 0)
  1852.         *number_of_occurances = 
  1853.           dictionary_block_word_occurances(position, field_dictionary_block);
  1854.       else
  1855.         *number_of_occurances = 0;
  1856.     }
  1857. #ifdef STEM_WORDS
  1858.   if(stemming && db->fields[field_id].stemming == false) {
  1859.     db->fields[field_id].stemming = stemming;
  1860.     stemming = false;
  1861.   }
  1862. #endif
  1863.   return(answer);
  1864. }
  1865. #endif
  1866.  
  1867. /*  BUILDING DICTIONARY FILES */
  1868.  
  1869.  
  1870. long number_of_dictionary_entries; /* number allocated */
  1871.  
  1872. char *block_of_zeros = NULL;
  1873.  
  1874. static void write_zeros_to_stream _AP((long n_bytes,FILE* stream));
  1875.  
  1876. static void write_zeros_to_stream(n_bytes,stream)
  1877. long n_bytes;
  1878. FILE *stream;
  1879. /* writes zeros to a file quickly */
  1880. {    
  1881.   long i;
  1882.   if(n_bytes >= BLOCK_SIZE){    /* then write big blocks of zeros */
  1883.     if(NULL == block_of_zeros){
  1884.       block_of_zeros = (char*)s_malloc((size_t)BLOCK_SIZE);
  1885.       memset(block_of_zeros, 0, BLOCK_SIZE);
  1886.     }
  1887.     while(n_bytes >= BLOCK_SIZE){    
  1888.       /* then write big blocks of zeros */
  1889.       if(BLOCK_SIZE != 
  1890.      fwrite(block_of_zeros, sizeof(char), BLOCK_SIZE, stream))
  1891.     panic("Write failed");
  1892.       n_bytes -= BLOCK_SIZE;
  1893.     }
  1894.   }
  1895.   for(i = 0; i < n_bytes; i++){    /* clean up the rest */
  1896.     putc('\0', stream); 
  1897.   }
  1898. }    
  1899.  
  1900. /* returns 0 if successful */
  1901. long init_dict_file_for_writing(db)
  1902. database *db;
  1903. {
  1904.   char filename[MAX_FILENAME_LEN];
  1905.  
  1906.   if (db->dictionary_stream != NULL)
  1907.     fclose(db->dictionary_stream);
  1908.   db->dictionary_stream = 
  1909.     s_fopen(temp_dictionary_filename(filename, db), "w+b");
  1910.  
  1911.   db->total_word_count = 0;
  1912.   init_dict_file_detailed(db->dictionary_stream,db->number_of_words);
  1913.   return(0);
  1914. }
  1915.  
  1916. #ifdef FIELDS /* tung, 12/93 */
  1917. /* returns 0 if successful */
  1918. long field_init_dict_file_for_writing(field_id, db)
  1919. long field_id;
  1920. database *db;
  1921. {
  1922.   char filename[MAX_FILENAME_LEN];
  1923.  
  1924.   if (*(db->field_dictionary_streams) != NULL)
  1925.     fclose(*(db->field_dictionary_streams));
  1926.   *(db->field_dictionary_streams) = 
  1927.     s_fopen(field_temp_dictionary_filename(filename, field_id, db), "w+b");
  1928.  
  1929.   db->fields[field_id].total_word_count = 0; 
  1930.   init_dict_file_detailed(*(db->field_dictionary_streams),
  1931.               db->fields[field_id].number_of_words);
  1932.   return(0);
  1933. }
  1934. #endif
  1935.  
  1936. static long dict_number_of_blocks _AP((long number_of_words));
  1937.  
  1938. static long
  1939. dict_number_of_blocks(number_of_words)
  1940. long number_of_words;
  1941. {
  1942.   long number_of_blocks;
  1943.   number_of_blocks = (number_of_words / DICTIONARY_BLOCK_SIZE) +
  1944.     ((0 == (number_of_words % DICTIONARY_BLOCK_SIZE)) ? 0 : 1);
  1945.   /*
  1946.    *  fprintf(stderr, "dict_number_of_blocks %ld->%ld\n", number_of_words,
  1947.    *          number_of_blocks);
  1948.    */
  1949.  
  1950.   return(number_of_blocks);
  1951. }
  1952.  
  1953. void
  1954. record_num_blocks_in_dict(dictionary_stream,number_of_words)
  1955. FILE* dictionary_stream;
  1956. long number_of_words;
  1957. { /* write the number of blocks */
  1958.   s_fseek(dictionary_stream, 0L, SEEK_SET);
  1959.   write_bytes(dict_number_of_blocks(number_of_words),
  1960.           DICTIONARY_HEADER_SIZE, dictionary_stream);
  1961.   fseek(dictionary_stream, 0L, SEEK_END);
  1962. }
  1963.  
  1964. void
  1965. init_dict_file_detailed(dictionary_stream,number_of_words)
  1966. FILE* dictionary_stream;
  1967. long number_of_words;
  1968. {
  1969.   /* create space for the table in the front of the file */
  1970.   write_zeros_to_stream(DICTIONARY_HEADER_SIZE + 
  1971.             (DICTIONARY_ENTRY_SIZE * 
  1972.              dict_number_of_blocks(number_of_words)),
  1973.             dictionary_stream);
  1974.   record_num_blocks_in_dict(dictionary_stream,number_of_words);
  1975.   number_of_dictionary_entries = 0;
  1976. }
  1977.  
  1978. /* this must be called in alphabetical order, and writes the word to
  1979.    the dictionary file. */    
  1980. long add_word_to_dictionary(word,position,number_of_occurances,db)
  1981. char *word;
  1982. long position;
  1983. long number_of_occurances;
  1984. database *db;
  1985.      /* Puts a word into the dictionary file. */
  1986. {
  1987.   /* assumes the stream has been initialized, and it is positioned
  1988.      at the end */
  1989.   FILE *stream = db->dictionary_stream;
  1990.   char padded_word[MAX_WORD_LENGTH + 1];
  1991.  
  1992.   memset(padded_word, 0, MAX_WORD_LENGTH + 1); /* clear the word */
  1993.   strcpy(padded_word, word);
  1994. #ifdef STEM_WORDS
  1995.   if(strcmp(word, DICTIONARY_TOTAL_SIZE_WORD) == 0) {
  1996.     if(db->stemming) 
  1997.       padded_word[strlen(padded_word)] = STEMMING_TAG;
  1998.   }
  1999. #endif
  2000.   if(0 == (number_of_dictionary_entries % DICTIONARY_BLOCK_SIZE)){
  2001.     /* then add an entry in the header */
  2002.     long original_position = s_ftell(stream);
  2003.     long header_entry = number_of_dictionary_entries / DICTIONARY_BLOCK_SIZE; 
  2004.     /* printf("Adding header entry %ld %s original pos %ld\n", 
  2005.        header_entry, padded_word, original_position); */
  2006.     fseek(stream, DICTIONARY_HEADER_SIZE + 
  2007.       (header_entry * DICTIONARY_ENTRY_SIZE), SEEK_SET);
  2008.     if((MAX_WORD_LENGTH + 1) != 
  2009.        fwrite(padded_word, sizeof(char), MAX_WORD_LENGTH + 1, stream))
  2010.       panic("Write failed");
  2011.     write_bytes(original_position, NEXT_INDEX_BLOCK_SIZE, stream);
  2012.     write_bytes(0L, NUMBER_OF_OCCURANCES_SIZE, stream);
  2013.     fseek(stream, original_position, SEEK_SET); /* go back to the end */
  2014.     /* zero the next block */
  2015.     write_zeros_to_stream(DICTIONARY_ENTRY_SIZE * DICTIONARY_BLOCK_SIZE,
  2016.               stream); 
  2017.     fseek(stream, original_position, SEEK_SET);      
  2018.   }
  2019.   /* write the word */    
  2020.   if((MAX_WORD_LENGTH + 1) !=
  2021.      fwrite(padded_word, sizeof(char), MAX_WORD_LENGTH + 1, stream))
  2022.     panic("Write failed");
  2023.   write_bytes(position, NEXT_INDEX_BLOCK_SIZE, stream);
  2024.   write_bytes(number_of_occurances, NUMBER_OF_OCCURANCES_SIZE, stream);
  2025.   number_of_dictionary_entries++;    
  2026.   db->total_word_count += number_of_occurances;
  2027.   return(0);
  2028. }
  2029.  
  2030. #ifdef FIELDS /* tung, 12/93 */
  2031. /* this must be called in alphabetical order, and writes the word to
  2032.    the dictionary file. */    
  2033. long field_add_word_to_dictionary(word, position,
  2034.                                   number_of_occurances, field_id, db)
  2035. char *word;
  2036. long position;
  2037. long number_of_occurances;
  2038. long field_id;
  2039. database *db;
  2040.      /* Puts a word into the dictionary file. */
  2041. {
  2042.   /* assumes the stream has been initialized, and it is positioned
  2043.      at the end */
  2044.   FILE *stream = *(db->field_dictionary_streams);
  2045.   char padded_word[MAX_WORD_LENGTH + 1];
  2046.  
  2047.   memset(padded_word, 0, MAX_WORD_LENGTH + 1); /* clear the word */
  2048.   strcpy(padded_word, word);
  2049. #ifdef STEM_WORDS
  2050.   if(strcmp(word, DICTIONARY_TOTAL_SIZE_WORD) == 0) {
  2051.     if(db->fields[field_id].stemming) 
  2052.       padded_word[strlen(padded_word)] = STEMMING_TAG;
  2053.   }
  2054. #endif
  2055.   if(0 == (number_of_dictionary_entries % DICTIONARY_BLOCK_SIZE)){
  2056.     /* then add an entry in the header */
  2057.     long original_position = s_ftell(stream);
  2058.     long header_entry = 
  2059.       number_of_dictionary_entries / DICTIONARY_BLOCK_SIZE; 
  2060.     /* printf("Adding header entry %ld %s original pos %ld\n", 
  2061.        header_entry, padded_word, original_position); */
  2062.     fseek(stream, DICTIONARY_HEADER_SIZE + 
  2063.       (header_entry * DICTIONARY_ENTRY_SIZE), SEEK_SET);
  2064.     if((MAX_WORD_LENGTH + 1) != 
  2065.        fwrite(padded_word, sizeof(char), MAX_WORD_LENGTH + 1, stream))
  2066.       panic("Write failed");
  2067.     write_bytes(original_position, NEXT_INDEX_BLOCK_SIZE, stream);
  2068.     write_bytes(0L, NUMBER_OF_OCCURANCES_SIZE, stream);
  2069.     fseek(stream, original_position, SEEK_SET); /* go back to the end */
  2070.     /* zero the next block */
  2071.     write_zeros_to_stream(DICTIONARY_ENTRY_SIZE * DICTIONARY_BLOCK_SIZE,
  2072.               stream); 
  2073.     fseek(stream, original_position, SEEK_SET);      
  2074.   }
  2075.   /* write the word */    
  2076.   if((MAX_WORD_LENGTH + 1) !=
  2077.      fwrite(padded_word, sizeof(char), MAX_WORD_LENGTH + 1, stream))
  2078.     panic("Write failed");
  2079.   write_bytes(position, NEXT_INDEX_BLOCK_SIZE, stream);
  2080.   write_bytes(number_of_occurances, NUMBER_OF_OCCURANCES_SIZE, stream);
  2081.   number_of_dictionary_entries++; 
  2082.   db->fields[field_id].total_word_count += number_of_occurances; 
  2083.   return(0);
  2084. }
  2085. #endif
  2086.  
  2087. /* this is called after all add_words are done, but before the file 
  2088.    is closed. Returns 0 if successful. */
  2089. long
  2090. finished_add_word_to_dictionary(db)
  2091.      database* db;
  2092. {
  2093.   char temp_filename[MAX_FILENAME_LEN];
  2094.   char filename[MAX_FILENAME_LEN];
  2095.  
  2096.   waislog(WLOG_LOW, WLOG_INFO, "Total word count for dictionary is: %ld",
  2097.       db->total_word_count);
  2098.   if(0 != add_word_to_dictionary(DICTIONARY_TOTAL_SIZE_WORD, 
  2099.                 1, db->total_word_count, db))
  2100.     return(-1);
  2101.  
  2102.   db->number_of_words++;
  2103.   record_num_blocks_in_dict(db->dictionary_stream,db->number_of_words);
  2104.  
  2105.   fflush(db->dictionary_stream); /* so that any new opens will see a 
  2106.                     valid file */
  2107.  
  2108.   /* rename the .dcttmp file to dct */
  2109.   temp_dictionary_filename(temp_filename, db);
  2110.   dictionary_filename(filename, db);
  2111.   /* printf("renaming %s to %s\n", temp_filename, filename); */
  2112.   if(0 != rename(temp_filename, filename))
  2113.     waislog(WLOG_HIGH, WLOG_ERROR,
  2114.         "could not rename file %s to %s",
  2115.         temp_filename, filename);
  2116.   return(0);
  2117. }
  2118.  
  2119. #ifdef FIELDS /* tung, 12/93 */
  2120. long
  2121. field_finished_add_word_to_dictionary(field_id, db)
  2122.      long field_id;
  2123.      database* db;
  2124. {
  2125.   char temp_filename[MAX_FILENAME_LEN];
  2126.   char filename[MAX_FILENAME_LEN];
  2127.  
  2128.   waislog(WLOG_LOW, WLOG_INFO, "Total word count for dictionary of field %s is: %ld",
  2129.       db->fields[field_id].field_name, db->fields[field_id].total_word_count);
  2130.   if(0 != field_add_word_to_dictionary(DICTIONARY_TOTAL_SIZE_WORD, 
  2131.                                        1,db->fields[field_id].total_word_count,
  2132.                                        field_id, db))
  2133.     return(-1);
  2134.   
  2135.   db->fields[field_id].number_of_words++;
  2136.   record_num_blocks_in_dict(*(db->field_dictionary_streams),
  2137.                             db->fields[field_id].number_of_words);
  2138.  
  2139.   fflush(*(db->field_dictionary_streams)); /* so that any new opens 
  2140.                                                      will see a valid file */
  2141.   
  2142.   /* rename the .dcttmp file to dct */
  2143.   field_temp_dictionary_filename(temp_filename, field_id, db);
  2144.   field_dictionary_filename(filename, db->fields[field_id].field_name, db);
  2145.   /* printf("renaming %s to %s\n", temp_filename, filename); */
  2146.   if(0 != rename(temp_filename, filename))
  2147.     waislog(WLOG_HIGH, WLOG_ERROR,
  2148.         "could not rename file %s to %s",
  2149.         temp_filename, filename);
  2150.   return(0);
  2151. }
  2152. #endif  
  2153.  
  2154. void print_dictionary_block(block,size)
  2155. unsigned char *block;
  2156. long size;
  2157. /* this prints the contents of a dictionary block */
  2158. {
  2159.   long i;
  2160.   for(i = 0; i < size; i++){
  2161.     char *word = dictionary_block_word(i, block);
  2162.     if(word[0] == '\0')
  2163.       break;
  2164.     /* I assume this is only for debugging - JG */
  2165.     printf("Entry %3ld: %21s %7ld %7ld\n", i, word,
  2166.         dictionary_block_position(i, block),
  2167.         dictionary_block_word_occurances(i, block));
  2168.   }
  2169. }
  2170.  
  2171. void print_dictionary _AP((database* db));
  2172.   
  2173. void print_dictionary(db)
  2174. database *db;
  2175. {
  2176.   /* prints the contents of a dictionary */
  2177.   FILE *stream = db->dictionary_stream;
  2178.   long i;
  2179.   long new_number_of_dictionary_blocks;
  2180.  
  2181.   if(NULL == stream)
  2182.     panic("dictionary stream is not open");
  2183.   s_fseek(stream, 0L, SEEK_SET);
  2184.   new_number_of_dictionary_blocks = read_bytes(DICTIONARY_HEADER_SIZE, stream);
  2185.   if(new_number_of_dictionary_blocks > number_of_dictionary_blocks)
  2186.     dictionary_header_block = NULL;
  2187.   number_of_dictionary_blocks = new_number_of_dictionary_blocks;
  2188.   printf("Number of dictionary blocks %ld\n", number_of_dictionary_blocks);
  2189.   if(NULL == (dictionary_header_block =
  2190.           read_dictionary_block(dictionary_header_block,
  2191.                     DICTIONARY_HEADER_SIZE,
  2192.                     number_of_dictionary_blocks,
  2193.                     stream)))
  2194.     panic("Could not read dictionary header block");
  2195.   printf("The Dictionary Header Block:\n");
  2196.   print_dictionary_block(dictionary_header_block, number_of_dictionary_blocks);
  2197.   for(i = 0; i < number_of_dictionary_blocks; i++){
  2198.     long pos = dictionary_block_position(i, dictionary_header_block);
  2199.     if(NULL == (dictionary_block =
  2200.         read_dictionary_block(dictionary_block,
  2201.                       pos, DICTIONARY_BLOCK_SIZE, stream)))
  2202.       panic("Could not read dictionary block %ld", pos);
  2203.     printf("\n\nDictionary block %ld (position %ld):\n", i, pos);
  2204.     print_dictionary_block(dictionary_block, DICTIONARY_BLOCK_SIZE);
  2205.   }
  2206.   fseek(stream, 0L, SEEK_END);
  2207. }
  2208.  
  2209. #ifdef testing
  2210. /* dictionary testing code */
  2211.  
  2212. static void check_dictionary_entry _AP((char* word,long expected_position,
  2213.                     database* db));
  2214.  
  2215. static void check_dictionary_entry(word,expected_position,db)
  2216. char *word;
  2217. long expected_position;
  2218. database *db;
  2219. {
  2220.   if(expected_position != look_up_word_in_dictionary(word, NULL, db)) {
  2221.     waislog(WLOG_HIGH, WLOG_ERROR,
  2222.         "%s should be %ld is %ld in db %s", 
  2223.         word, expected_position, 
  2224.         look_up_word_in_dictionary(word, NULL, db),
  2225.         db->database_file);
  2226.   }
  2227. }
  2228.   
  2229. static void test_dictionary _AP((database* db));
  2230.  
  2231. static void test_dictionary(db)
  2232. database *db;
  2233. /* this is just an trivial test */
  2234. {
  2235.  
  2236.   db->number_of_words = 3;
  2237.   init_dict_file_for_writing(db);
  2238.   add_word_to_dictionary("aardvark", 123L, 0l, db);
  2239.   add_word_to_dictionary("house", 234L, 0L, db);
  2240.   add_word_to_dictionary("mary", 345L, 0L, db);
  2241.   fflush(db->dictionary_stream);
  2242.   print_dictionary(db);
  2243.   check_dictionary_entry("aardvark", 123L, db);
  2244.   check_dictionary_entry("house", 234L, db);
  2245.   check_dictionary_entry("mary", 345L, db);
  2246.   check_dictionary_entry("food", -123L, db);
  2247.   check_dictionary_entry("zebra", -345L, db);
  2248.   check_dictionary_entry("aaarf", 0L, db);
  2249. }
  2250. #endif /* def testing */
  2251.  
  2252.  
  2253. /*========================*
  2254.  *===  Document Table  ===*
  2255.  *========================*/
  2256.  
  2257. boolean
  2258. read_document_table_entry(doc_entry,number,db)
  2259. document_table_entry* doc_entry;
  2260. long number;
  2261. database* db;
  2262. /* returns a document_table_entry on the stack */
  2263. {
  2264.   long position;
  2265.   FILE *stream = db->document_table_stream;
  2266.     
  2267.   position = (DOC_TAB_HEADER_SIZE + 
  2268.           ((long)number * (long)DOC_TAB_ELEMENT_SIZE));
  2269.  
  2270.   if (0 != fseek(stream, position, SEEK_SET))
  2271.     { 
  2272.       waislog(WLOG_HIGH, WLOG_ERROR,
  2273.           "fseek failed into the document table to position %ld in db %s", 
  2274.           position,
  2275.           db->database_file);
  2276.       return(false);
  2277.     }
  2278.     
  2279.   doc_entry->filename_id = read_bytes(DOC_TAB_ENTRY_FILENAME_ID_SIZE, 
  2280.                      stream);
  2281.   doc_entry->headline_id = read_bytes(DOC_TAB_ENTRY_HEADLINE_ID_SIZE, 
  2282.                      stream);    
  2283.   doc_entry->start_character = 
  2284.     read_bytes(DOC_TAB_ENTRY_START_CHAR_SIZE, stream);
  2285.   doc_entry->end_character = 
  2286.     read_bytes(DOC_TAB_ENTRY_END_CHAR_SIZE, stream);
  2287.   doc_entry->document_length = 
  2288.     read_bytes(DOC_TAB_ENTRY_DOC_LENGTH_SIZE, stream);
  2289.   doc_entry->number_of_lines = 
  2290.     read_bytes(DOC_TAB_ENTRY_NUM_LINES_SIZE, stream);
  2291.   doc_entry->date = 
  2292.     read_bytes(DOC_TAB_ENTRY_DATE_SIZE, stream);
  2293.   if (doc_entry->date == EOF) { 
  2294.     return(false);
  2295.   }
  2296.  
  2297. /*printf("read_document_table_entry pos %ld val %lx\n",position,doc_entry->date);*/
  2298.  
  2299.   return(true);
  2300. }
  2301.  
  2302.  
  2303. boolean
  2304. writeUserValToDocIDTable(userVal,doc,db)
  2305. unsigned long userVal;
  2306. long doc;
  2307. database* db;
  2308. /* the docIDTable needs to keep a user value for use by other indexing
  2309.    systems.  Currently it is stuffed in the date field. 
  2310.  
  2311.    This routine needs to be updated if read_document_table_entry changes
  2312.  */
  2313. {
  2314.   long position;
  2315.   
  2316.   position = (DOC_TAB_HEADER_SIZE +
  2317.           ((long)doc * (long)DOC_TAB_ELEMENT_SIZE) +
  2318.           DOC_TAB_ENTRY_FILENAME_ID_SIZE +
  2319.           DOC_TAB_ENTRY_HEADLINE_ID_SIZE + 
  2320.           DOC_TAB_ENTRY_START_CHAR_SIZE +
  2321.           DOC_TAB_ENTRY_END_CHAR_SIZE +
  2322.           DOC_TAB_ENTRY_DOC_LENGTH_SIZE +
  2323.           DOC_TAB_ENTRY_NUM_LINES_SIZE);
  2324.  
  2325.   if (0 != fseek(db->document_table_stream,position,SEEK_SET))
  2326.    { waislog(WLOG_HIGH, WLOG_ERROR,
  2327.          "fseek failed into the document table to position %ld in db %s", 
  2328.          position,db->database_file);
  2329.      return(false);
  2330.    }
  2331.  
  2332. /*printf("writeUserValToDocIDTable pos %ld val %lx\n",position,userVal);*/
  2333.  
  2334.   write_bytes(userVal,DOC_TAB_ENTRY_DATE_SIZE,db->document_table_stream);
  2335.   fflush(db->document_table_stream);
  2336.   return(true);
  2337. }
  2338.  
  2339.  
  2340.  
  2341. #ifdef testing
  2342.  
  2343. static  boolean check_document_id _AP((long doc_id,database* db));
  2344.  
  2345. static  boolean
  2346. check_document_id(doc_id,db)
  2347. long doc_id;
  2348. database* db;
  2349. /* returns true if that is a valid doc_id (corresponds to a file
  2350.    that has not been deleted */
  2351. {
  2352.   long position;
  2353.   FILE *stream = db->document_table_stream;
  2354.   long filename_id;
  2355.   char filename[MAX_FILE_NAME_LEN];
  2356.  
  2357.   position = (DOC_TAB_HEADER_SIZE + 
  2358.           ((long)doc_id * (long)DOC_TAB_ELEMENT_SIZE));
  2359.  
  2360.   if (0 != fseek(stream, position, SEEK_SET)) { 
  2361.     waislog(WLOG_HIGH, WLOG_ERROR,
  2362.         "fseek failed into the document table to position %ld in db %s",
  2363.         position,
  2364.         db->database_file);
  2365.     return(false);
  2366.   }
  2367.     
  2368.   filename_id = read_bytes(DOC_TAB_ENTRY_FILENAME_ID_SIZE, stream);
  2369.   /* probe the file.  Is there a faster way? */
  2370.   return(probe_file_possibly_compressed(read_filename_table_entry(filename_id, filename,NULL,db)));
  2371. }
  2372. #endif
  2373.  
  2374. long write_document_table_entry(doc_table_entry, db)
  2375. document_table_entry* doc_table_entry;
  2376. database* db;
  2377. {
  2378.   /* returns the document_id */
  2379.   s_fseek(db->document_table_stream,
  2380.          (DOC_TAB_HEADER_SIZE +
  2381.           (db->doc_table_allocated_entries *
  2382.            DOC_TAB_ELEMENT_SIZE)),
  2383.          SEEK_SET);
  2384.   /* write the pieces */
  2385.   write_bytes(doc_table_entry->filename_id,
  2386.           DOC_TAB_ENTRY_FILENAME_ID_SIZE,
  2387.           db->document_table_stream);
  2388.   write_bytes(doc_table_entry->headline_id,
  2389.           DOC_TAB_ENTRY_HEADLINE_ID_SIZE,
  2390.           db->document_table_stream);
  2391.   write_bytes(doc_table_entry->start_character,
  2392.           DOC_TAB_ENTRY_START_CHAR_SIZE,
  2393.           db->document_table_stream);
  2394.   write_bytes(doc_table_entry->end_character,
  2395.           DOC_TAB_ENTRY_END_CHAR_SIZE,
  2396.           db->document_table_stream);
  2397.   write_bytes(doc_table_entry->document_length,
  2398.           DOC_TAB_ENTRY_DOC_LENGTH_SIZE,
  2399.           db->document_table_stream);
  2400.   /*  printf("Writing %ld lines\n", document_table_entry->number_of_lines); */
  2401.   write_bytes(doc_table_entry->number_of_lines,
  2402.           DOC_TAB_ENTRY_NUM_LINES_SIZE,
  2403.           db->document_table_stream);
  2404.   write_bytes(doc_table_entry->date,
  2405.           DOC_TAB_ENTRY_DATE_SIZE,
  2406.           db->document_table_stream);
  2407.   db->doc_table_allocated_entries++;
  2408.   return(db->doc_table_allocated_entries);
  2409. }
  2410.  
  2411. long next_document_id(db)
  2412. database* db;
  2413. {
  2414.   return(db->doc_table_allocated_entries);
  2415. }
  2416.  
  2417.  
  2418. /*========================*
  2419.  *===  Filename table  ===*
  2420.  *========================*/
  2421.  
  2422. #ifndef MAXPATHLEN /* think_c does not define it for instance */
  2423. #define MAXPATHLEN 1024
  2424. #endif /* MAXPATHLEN */
  2425.  
  2426. static char *read_filename_table_stream _AP((long position, 
  2427.                         char* filename,
  2428.                         char* type, 
  2429.                         time_t* file_write_date,
  2430.                         FILE *stream));
  2431.  
  2432. static char *read_filename_table_stream(position,filename,type,
  2433.                     file_write_date, stream)
  2434. long position;
  2435. char* filename;
  2436. char* type;
  2437. time_t* file_write_date;
  2438. FILE *stream;
  2439. {
  2440.   /* Returns the filename array after side effecting it,
  2441.    *  or NULL if an error.
  2442.    * The type of the file is put in the argument "type".  This will
  2443.    * not be longer than MAX_FILE_NAME_LEN.
  2444.    *
  2445.    * if type is NULL then ignore it,
  2446.    * if file_write_date is NULL then ignore it,
  2447.    * If position is -1, then it does not seek.
  2448.    *
  2449.    * Leave the file positioned at the start of the next entry.
  2450.    */    
  2451.   long file_write_date_internal;
  2452.   char type_internal[MAX_TYPE_LEN];
  2453.  
  2454.   if(NULL == stream)
  2455.     return(NULL);
  2456.  
  2457.   if(NULL == type)  /* this means we do not care, so set up a dummy */
  2458.     type = type_internal;
  2459.  
  2460.   filename[0] = '\0';    /* init to the empty string */
  2461.   if(NULL != type)
  2462.     type[0] = '\0';    /* init to the empty string */
  2463.  
  2464.   if(position != -1){
  2465.     if (0 != fseek(stream, position, SEEK_SET)){
  2466.       waislog(WLOG_HIGH, WLOG_ERROR, "fseek failed into the filename index to position %ld", 
  2467.           position);
  2468.       return(NULL);
  2469.     }
  2470.   }
  2471.   if(false == read_string_from_file(stream, filename, MAX_FILE_NAME_LEN)){
  2472.     return(NULL);
  2473.   }
  2474.   else{
  2475.     file_write_date_internal = read_bytes(FILE_WRITE_DATE_SIZE, stream);
  2476.     if(file_write_date){
  2477.       *file_write_date = (time_t)file_write_date_internal;
  2478.     }
  2479.     if(false == read_string_from_file(stream, type, MAX_TYPE_LEN)){
  2480.       return(NULL);
  2481.     } 
  2482.   }
  2483.   return(filename);
  2484. }
  2485.      
  2486. char *read_filename_table_entry(position,filename,type,file_write_date,db)
  2487. long position;
  2488. char* filename;
  2489. char* type;
  2490. time_t* file_write_date;
  2491. database* db;
  2492. {
  2493.   /* Returns the filename array after side effecting it,
  2494.    *  or NULL if an error.
  2495.    * The type of the file is put in the argument "type".  This will
  2496.    * not be longer than MAX_FILE_NAME_LEN.
  2497.    *
  2498.    * if type is NULL then ignore it,
  2499.    * if file_write_date is NULL then ignore it,
  2500.    * If position is -1, then it does not seek.
  2501.    *
  2502.    * Leave the file positioned at the start of the next entry.
  2503.    */    
  2504.   FILE *stream = db->filename_table_stream;
  2505.   return(read_filename_table_stream(position,filename,type,
  2506.                     file_write_date,stream));
  2507. }
  2508.  
  2509. long write_filename_table_entry(filename,type,db)
  2510. char* filename;
  2511. char *type;
  2512. database* db;
  2513. {
  2514.   /* writes the filename (NULL terminated),
  2515.      followed by 4 bytes of creation date,
  2516.      followed by the file type (NULL terminated),
  2517.      Returns the postion of the filename
  2518.      */
  2519.   long free_position,count,i,j;
  2520.   char full_path[MAXPATHLEN];
  2521.   char savedFileName[MAX_FILENAME_LEN + 1];
  2522.   char* tmp_type = NULL;   /* temporary type */
  2523.   char* tmp_type_pointer = NULL;   /* temporary type pointer */
  2524.  
  2525.   s_fseek(db->filename_table_stream, 0L, SEEK_END);
  2526.   free_position = ftell(db->filename_table_stream);
  2527.   /* add the filename to the hashtable not done yet XXX
  2528.      (setf (gethash filename *filename_table_hashtable*)
  2529.      (file_write_date filename))
  2530.      */
  2531.  
  2532.   if(FILE_WRITE_DATE_SIZE != sizeof(time_t)){ /* check if these are the same */
  2533.     panic("We have a problem with the file_write_date_size\n");
  2534.   }
  2535.  
  2536.   /* this is a hack, but shold not do any harm except for files really
  2537.      starting with "http:" */
  2538.   if (strncmp("http:", filename, 5) && strncmp("ftp:", filename, 4)) {
  2539.     fprintf(db->filename_table_stream, "%s", truename(filename, full_path));
  2540.     fputc(0, db->filename_table_stream);
  2541.     write_bytes((long)file_write_date(filename),
  2542.                 FILE_WRITE_DATE_SIZE, db->filename_table_stream);
  2543.   } else {
  2544.     fprintf(db->filename_table_stream, "%s", filename);
  2545.     fputc(0, db->filename_table_stream);
  2546.     write_bytes((long)0,
  2547.                 FILE_WRITE_DATE_SIZE, db->filename_table_stream);
  2548.   }
  2549. /*  fwrite(type, sizeof(char), strlen(type) + 1, db->filename_table_stream);*/
  2550.  
  2551.  
  2552. /* francois - multitype extensions */
  2553. /* 
  2554.    Here we just add the document types to the file entry, we need to 
  2555.    check to see if each file is there so we probe them.
  2556. */
  2557.  
  2558.    if ( strstr(type,",") == NULL ) {
  2559.       fprintf(db->filename_table_stream, "%s",type);
  2560.       fputc(0,db->filename_table_stream);
  2561.    }
  2562.    else {
  2563.    
  2564.        /* count up the number of document types */
  2565.        count = 1L;
  2566.        for (i = 0L; i < strlen(type); i++){
  2567.           if ( type[i] == ',' )
  2568.              count++;
  2569.        }
  2570.  
  2571.            /* duplicate the type and save the pointer */
  2572.            tmp_type = s_strdup(type);
  2573.            tmp_type_pointer = tmp_type;
  2574.  
  2575.       
  2576.            /* append types - NULL out the pointer so that strtok can grab the subsequent entries */
  2577.        for (i = 0L; i < count; i++ ) {
  2578.           tmp_type_pointer = s_strdup(strtok(tmp_type_pointer,","));
  2579.           
  2580.           strcpy(savedFileName,filename);
  2581.               if ( strcmp(savedFileName+(strlen(savedFileName)-2), ".Z") == 0 ) {
  2582.                /* it's a .Z file.  First, remove the suffix or many things get confused. */
  2583.                  savedFileName[(strlen(savedFileName)-2)] = 0;
  2584.               }
  2585.          
  2586.               /* strip the current extension, but not the period */
  2587.               for ( j = strlen(savedFileName); j >= 0L; j-- ) {
  2588.                  if (savedFileName[j] == '.') {
  2589.                     savedFileName[j+1] = 0;
  2590.                     break;
  2591.                  }
  2592.               }
  2593.          
  2594.               /* append the type to the file name */
  2595.               strcat(savedFileName,tmp_type_pointer);
  2596.  
  2597.       
  2598.               if(probe_file_possibly_compressed(savedFileName)) {
  2599.  
  2600.                  fprintf(db->filename_table_stream, "%s",tmp_type_pointer);
  2601.                  fprintf(db->filename_table_stream, ",");
  2602.               }
  2603.               
  2604.               s_free(tmp_type_pointer);
  2605.           tmp_type_pointer = NULL;
  2606.       
  2607.        }
  2608.        
  2609.        /* release the tmp_type allocations */
  2610.        s_free(tmp_type);
  2611.      
  2612.            /* terminate the string */
  2613.            fputc(0,db->filename_table_stream);
  2614.  
  2615.    }
  2616.  
  2617.  
  2618.  
  2619.  
  2620.   return(free_position);
  2621. }
  2622.  
  2623. /* functions to figure out if the file is in the index already */
  2624.         
  2625. static boolean filename_in_filename_stream _AP((char *filename, char *type, 
  2626.                         time_t *file_write_date, 
  2627.                         FILE *stream));
  2628.              
  2629. static boolean filename_in_filename_stream(filename, type, 
  2630.                        file_write_date, stream)
  2631. char *filename;
  2632. char *type;
  2633. time_t *file_write_date;
  2634. FILE *stream;
  2635.      /* returns true if it is there (and side effects type and 
  2636.       file_write_date). 
  2637.         leaves the stream at the end of the file.
  2638.     If type or file_write_date is NULL, then it is a dont care.
  2639.     type, if it is an array, should be MAX_FILENAME_LEN long at least.
  2640.     */
  2641. {
  2642.   /* this is slow because it loops through the whole file every time.
  2643.      this might want to be optimized by making a hashtable. */
  2644.   char next_filename[MAX_FILENAME_LEN];
  2645.   
  2646.   s_fseek(stream, FILENAME_TABLE_HEADER_SIZE, SEEK_SET);
  2647.   while(!feof(stream)){
  2648.     char new_type[MAX_FILENAME_LEN];
  2649.     if(NULL == 
  2650.        read_filename_table_stream(-1, next_filename, new_type, 
  2651.                   file_write_date, stream))
  2652.       return(false);
  2653.     if(0 == strcmp(next_filename, filename))
  2654.       return(true);
  2655.   }
  2656. }
  2657.  
  2658. boolean filename_in_database(filename,type,file_write_date,db)
  2659. char *filename;
  2660. char *type;
  2661. time_t *file_write_date;
  2662. database *db;
  2663. {
  2664.   return(filename_in_filename_stream(filename, type, file_write_date, 
  2665.                      db->filename_table_stream));
  2666. }
  2667.  
  2668. /* this caches the last filename that was found to be in the filename file,
  2669.    this way repeated attempts to figure out if a file is there will be fast.
  2670.    This is the case when retrieving successive blocks of a file. */   
  2671. char last_filename_found_in_file[MAX_FILE_NAME_LEN];
  2672. char last_filename_file[MAX_FILE_NAME_LEN];
  2673.  
  2674. boolean filename_in_filename_file(filename,type,file_write_date, filename_file)
  2675. char *filename;
  2676. char *type;
  2677. time_t *file_write_date;
  2678. char *filename_file;
  2679. {
  2680.   if(NULL == filename)
  2681.     return(false);
  2682.   
  2683.   if(0 == strcmp(last_filename_found_in_file, filename) &&
  2684.      0 == strcmp(last_filename_file, filename_file))
  2685.     return(true);
  2686.   else
  2687.    { FILE *stream = s_fopen(filename_file, "r");
  2688.      boolean answer;
  2689.  
  2690.      if(NULL == stream)
  2691.       { s_fclose(stream);
  2692.     return(false);
  2693.       }
  2694.      answer = 
  2695.        filename_in_filename_stream(filename,type,file_write_date, stream);
  2696.      if(answer == true)
  2697.       { /* record it in the cache */
  2698.     strncpy(last_filename_file, filename_file, MAX_FILE_NAME_LEN);
  2699.     strncpy(last_filename_found_in_file, filename, MAX_FILE_NAME_LEN);
  2700.       }
  2701.      s_fclose(stream);
  2702.      return(answer);
  2703.    }
  2704. }
  2705.  
  2706.  
  2707. /*========================*
  2708.  *===  Headline Table  ===*
  2709.  *========================*/
  2710.  
  2711. char *read_headline_table_entry(position,db)
  2712. long position;
  2713. database* db;
  2714.   /* returns the headline array after side effecting it.  Beware that 
  2715.    * the next call to this function will overwrite the the headline_array
  2716.    */
  2717. {
  2718.   /* this is the headline that gets returned */
  2719.   static char headline_array[MAX_HEADLINE_LEN]; 
  2720.   FILE *stream = db->headline_table_stream;
  2721.   headline_array[0] = '\0';    /* init to the empty string */
  2722.     
  2723.   if (0 != fseek(stream, position, SEEK_SET)) { 
  2724.     waislog(WLOG_HIGH, WLOG_ERROR, 
  2725.         "fseek failed into the headline index to position %ld in db %s", 
  2726.         position, db->database_file);
  2727.     return(headline_array);
  2728.   }
  2729.   if(false == read_string_from_file(db->headline_table_stream, 
  2730.                     headline_array, MAX_FILE_NAME_LEN)){ 
  2731.    waislog(WLOG_HIGH, WLOG_ERROR, 
  2732.         "headline table is corrupt at %ld in db %s", 
  2733.         position, db->database_file);
  2734.   }
  2735.   return(headline_array);
  2736. }
  2737.  
  2738. /* writes the string to the file followed by a NULL.
  2739.  * The returned number is the position in the file to start reading.
  2740.  */
  2741. long write_headline_table_entry(headline,db)
  2742. char* headline;
  2743. database* db;
  2744. {
  2745.   /* writes the headline followed by a newline.
  2746.      Returns the postion of the headline.
  2747.      */
  2748.   long free_position;
  2749.   s_fseek(db->headline_table_stream, 0L, SEEK_END);
  2750.   free_position = ftell(db->headline_table_stream);
  2751.   /* printf("Headline position: %ld, next headline length: %ld\n",
  2752.      free_position, strlen(headline)); */
  2753.   fprintf(db->headline_table_stream, "%s", headline);
  2754.   fputc(0, db->headline_table_stream);
  2755.   return(free_position);
  2756. }
  2757.  
  2758. #ifdef BIO
  2759. /*========================*
  2760.  *=== delimiters - dgg ===*
  2761.  *========================*/
  2762.  
  2763. char *read_delimiters(db)
  2764. database* db;
  2765.   /* returns the word delimiters for a database.  Beware that 
  2766.    * the next call to this function will overwrite the the headline_array
  2767.    */
  2768. {
  2769.   static char delimiters[MAX_HEADLINE_LEN+1]; 
  2770.   FILE *stream = db->delimiters_stream;
  2771.   delimiters[0] = '\0';    /* init to the empty string */
  2772.     
  2773.   if(false == read_string_from_file(db->delimiters_stream, 
  2774.                     delimiters, MAX_HEADLINE_LEN)){ 
  2775.    waislog(WLOG_HIGH, WLOG_ERROR, 
  2776.         "delimiters are corrupt in db %s",  db->database_file);
  2777.   }
  2778.   /* need to weed out .dlm files that have no symbols... */
  2779.   if (delimiters[0] == '\0') return(NULL);
  2780.   else return(delimiters);
  2781. }
  2782.  
  2783. /* writes the string to the file followed by a NULL.
  2784.  * The returned number is the position in the file to start reading.
  2785.  */
  2786. long write_delimiters(delimiters,db)
  2787. char* delimiters;
  2788. database* db;
  2789. {
  2790.   /* writes the headline followed by a newline.
  2791.      Returns the postion of the headline.
  2792.      */
  2793.   long free_position;
  2794.   s_fseek(db->delimiters_stream, 0L, SEEK_SET); /* _SET, only one set of delims / file ? */
  2795.   free_position = ftell(db->delimiters_stream);
  2796.   fprintf(db->delimiters_stream, "%s", delimiters);
  2797.   fputc(0, db->delimiters_stream);
  2798.   return(free_position);
  2799. }
  2800. #endif
  2801.  
  2802. /* =================== */
  2803. /* === Source file === */
  2804. /* =================== */
  2805.  
  2806. /* the source file is an ascii file for describing a source.
  2807.    it is defined in ../doc/source.txt */
  2808.  
  2809. /* Registers the src structure with the directory of servers.
  2810.    Return true if successful */
  2811. boolean register_src_structure(filename)
  2812. char *filename;
  2813. {
  2814.   char string[200], *editor;
  2815.   long answer;
  2816.   char *env_user;
  2817.  
  2818.   if((editor = (char*)getenv("EDITOR")) == NULL &&
  2819.      (editor = (char*)getenv("VISUAL")) == NULL) {
  2820.     printf("Could not get EDITOR environment variable.\n"); 
  2821.     printf("Please check over the source structure: %s\n", filename);
  2822.     printf("Then mail it to wais-directory-of-servers@cnidr.org\n");
  2823.     return (false);
  2824.   }
  2825.  
  2826.   /* register the server with the directory of servers */
  2827.   printf("Please look over the Source description.  Be sure it contains\n");
  2828.   printf("an IP address and DNS name, as well as the port you intend\n");
  2829.   printf("to use for the server.\n");
  2830.   printf("When you are finished it will be mailed to the directory of servers.\n");
  2831.   fflush(stdout);
  2832.  
  2833.   sprintf(string, "exec %s %s", editor, filename);
  2834.   system(string);
  2835.  
  2836.   printf("Sending source struture to the directory of servers...");
  2837.  
  2838.   if (!(env_user = getenv("USER")))
  2839.     env_user = "nobody";
  2840.   sprintf(string,
  2841.       "cat %s | mail wais-directory-of-servers@cnidr.org %s\n", 
  2842.       filename, env_user);
  2843.  
  2844.   answer = system(string);
  2845.   printf("Done.\n");      
  2846.   return((answer == 0)?true:false);
  2847. }
  2848.  
  2849.  
  2850. /* Writes a source structure to a file.
  2851.    If the export_database arg is set, then the tcp_port is used in the 
  2852.    tcp-port slot.
  2853.    Returns true if successful. */
  2854. #ifdef FIELDS /* tung, 3/94 */
  2855. boolean write_src_structure(filename, database_name, typename, 
  2856.                 filenames, count, export_database, tcp_port, db)
  2857. #else
  2858. boolean write_src_structure(filename, database_name, typename, 
  2859.                 filenames, count, export_database, tcp_port)
  2860. #endif
  2861.      char *filename;
  2862.      char *database_name;
  2863.      char *typename;
  2864.      char **filenames;
  2865.      long count;
  2866.      boolean export_database;
  2867.      long tcp_port;
  2868.      database* db;
  2869. {
  2870.   long i,j;
  2871.   char hostname[120];
  2872.   struct hostent *h;
  2873.   
  2874. #ifndef THINK_C
  2875.  
  2876.   FILE *source_stream = s_fopen(filename, "w");
  2877.   
  2878.   fprintf(source_stream, "\n\n(:source \n");
  2879.   fprintf(source_stream, "   :version  3 \n");
  2880.   if(export_database){
  2881.     mygethostname(hostname, 120);
  2882.     h = gethostbyname(hostname);
  2883.     if (h != NULL && 
  2884.     h->h_addr_list != NULL &&
  2885.     h->h_addr_list[0] != NULL) {
  2886.       fprintf(source_stream,
  2887.           "   :ip-address \"%d.%d.%d.%d\"\n",
  2888.           (unsigned char)h->h_addr_list[0][0], 
  2889.           (unsigned char)h->h_addr_list[0][1],
  2890.           (unsigned char)h->h_addr_list[0][2],
  2891.           (unsigned char)h->h_addr_list[0][3] );
  2892.     }
  2893.     fprintf(source_stream, "   :ip-name \"%s\"\n", hostname );
  2894.     fprintf(source_stream, "   :tcp-port %ld\n", tcp_port);
  2895.   }
  2896.   fprintf(source_stream, "   :database-name \"%s\"\n", database_name);
  2897.   fprintf(source_stream, "   :cost 0.00 \n");
  2898.   fprintf(source_stream, "   :cost-unit :free \n");
  2899.   fprintf(source_stream, "   :maintainer \"%s\"\n", 
  2900.       current_user_name());
  2901.    fprintf(source_stream, "   :keyword-list (\n");
  2902.   for (j=0; j< nKeys; j++) {
  2903.     fprintf(source_stream, "                  %s\n", keyword[j]);
  2904.   }
  2905.   fprintf(source_stream, "                  )\n");
  2906.  
  2907. #ifdef FIELDS /* tung, 3/94 */
  2908. /*
  2909.   :fields           ((:field
  2910.                       :name "database-name"
  2911.                       :description "database name")
  2912.                      (:field
  2913.                       :name "ip-name"
  2914.                       :description "server name"))
  2915.  */
  2916.  
  2917.   if(db->number_of_fields > 0) {
  2918.       fprintf(source_stream, "   :fields (\n");
  2919.       for(i=0; i<db->number_of_fields; i++) {
  2920.           if(db->index_fields[i]->field_name != NULL) {
  2921.               fprintf(source_stream, "                  (:field\n");
  2922.               fprintf(source_stream, "                   :name \"%s\"\n", 
  2923.                       db->index_fields[i]->field_name);
  2924.               fprintf(source_stream, "                   :description \"%s\")\n", 
  2925.                       (db->index_fields[i]->field_description)?
  2926.                       db->index_fields[i]->field_description : "None given");
  2927.           }
  2928.       }
  2929.       fprintf(source_stream, "           )\n");
  2930.   }
  2931. #endif
  2932.           if(!nDesLines){
  2933.     fprintf(source_stream, "   :description \"Server created with %s on %s by %s\n",
  2934.             VERSION, printable_time(), current_user_name());
  2935.     if(count > 0){
  2936.       fprintf(source_stream, "The files of type %s used in the index were:\n",
  2937.               typename);
  2938.       for(i = 0; i < count; i++){
  2939.         char full_path[MAX_FILENAME_LEN + 1];
  2940.         fprintf(source_stream, "   %s\n", truename(filenames[i], full_path));
  2941.       }
  2942.     }
  2943. #ifdef FIELDS /* tung, 3/94 */
  2944.     if(db->number_of_fields > 0) {
  2945.       char fde_filename[MAX_FILENAME_LEN + 1];
  2946.       char line[1001];
  2947.       FILE* fde_stream = NULL;
  2948.       strncpy(fde_filename, db->database_file, 255);
  2949.       strncat(fde_filename, ".fde", 255);
  2950.       fde_stream = s_fopen(fde_filename, "r");
  2951.       fprintf(source_stream, "fields list:\n");
  2952.       fprintf(source_stream, "   ");
  2953.  
  2954.       if(fde_stream == NULL) {
  2955.         for(i=0; i<db->number_of_fields; i++) {
  2956.           if(db->index_fields[i]->field_name != NULL)
  2957.             fprintf(source_stream, "%s ", db->index_fields[i]->field_name);
  2958.         }
  2959.       }
  2960.       else {
  2961.         while(fgets(line, 1000, fde_stream)) {
  2962.           fprintf(source_stream, "%s", line);
  2963.           fprintf(source_stream, "   ");
  2964.         }
  2965.         s_fclose(fde_stream);
  2966.       }
  2967.       fprintf(source_stream, "\n");
  2968.     }
  2969. #endif
  2970.     fprintf(source_stream, "\"\n");
  2971.   } else
  2972.     for (j=0; j<nDesLines; j++)
  2973.       fprintf(source_stream, "%s", descript[j]);
  2974.   fprintf(source_stream, ")\n");      
  2975.   s_fclose(source_stream);
  2976.   
  2977. #endif /* ndef THINK_C */
  2978.  
  2979.   return(true);
  2980. }
  2981.  
  2982. boolean
  2983. build_catalog(db)
  2984. database* db;
  2985. {
  2986.   char catalog_name[MAX_FILENAME_LEN];
  2987.   document_table_entry doc_entry;
  2988.   char filename[MAX_FILE_NAME_LEN], type[100];
  2989.   FILE *catalog;
  2990.   long i;
  2991.  
  2992.   sprintf(catalog_name,"%s%s",db->database_file, catalog_ext);
  2993.   if((catalog = s_fopen(catalog_name, "w")) == NULL) {
  2994.     waislog(WLOG_HIGH, WLOG_ERROR, 
  2995.         "Unable to open catalog file for write: %s.", catalog_name);
  2996.     return(false);
  2997.   }
  2998.  
  2999.   fprintf(catalog, "Catalog for database: %s\n", db->database_file);
  3000.   fprintf(catalog, "Date: %s\n", printable_time());
  3001.  
  3002.   /* the first document is empty - JG */
  3003.  
  3004.   fprintf(catalog, "%ld total document%s\n\n",
  3005.       db->doc_table_allocated_entries-1,
  3006.       (db->doc_table_allocated_entries==2) ? "":"s");
  3007.   
  3008.   for(i=1; i<db->doc_table_allocated_entries; i++) {
  3009.     fprintf(catalog, "Document # %ld\n", i);
  3010.     if (read_document_table_entry(&doc_entry, i, db) 
  3011.     == true){
  3012.       char *hl;
  3013.       long hll;
  3014.       read_filename_table_entry(doc_entry.filename_id, 
  3015.                 filename,
  3016.                 type,
  3017.                 NULL,
  3018.                 db);
  3019.       hl = read_headline_table_entry(doc_entry.headline_id,db);
  3020.       hll = strlen(hl);
  3021.       fprintf(catalog, "Headline: %s", hl);
  3022.       if((hll== 0) || (hl[hll-1] != '\n')) fprintf(catalog,"\n");
  3023.  
  3024.       fprintf(catalog, "DocID: %d %d %s\n\n",
  3025.           doc_entry.start_character, doc_entry.end_character,
  3026.           filename);
  3027.     }
  3028.     else {
  3029.       fprintf(catalog, "Unable to read document table!\n\n");
  3030.     }
  3031.   }
  3032.   s_fclose(catalog);
  3033.   return(true);
  3034. }
  3035.  
  3036. /*****************************/
  3037. /***   Database support    ***/
  3038. /*****************************/
  3039.  
  3040. char* dictionary_filename(destination,db)
  3041. char* destination;
  3042. database* db;
  3043. {
  3044.   strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3045.   s_strncat(destination,dictionary_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3046.   return(destination);
  3047. }
  3048.  
  3049. #ifdef FIELDS /* tung */
  3050. char* field_dictionary_filename(destination, field_name, db)
  3051. char* destination;
  3052. char* field_name;
  3053. database* db;
  3054. {
  3055.   strncpy(destination, db->database_file, MAX_FILE_NAME_LEN);
  3056.   s_strncat(destination, field_ext, MAX_FILE_NAME_LEN, MAX_FILE_NAME_LEN);
  3057.   s_strncat(destination,field_name,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3058.   s_strncat(destination,dictionary_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3059.   return(destination);
  3060. }
  3061. #endif
  3062.  
  3063. /* for use in building so that the real one does not get overstomped */
  3064. static char* temp_dictionary_filename(destination,db)
  3065. char* destination;
  3066. database* db;
  3067. {
  3068.   strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3069.   s_strncat(destination,dictionary_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3070.   s_strncat(destination,"tmp",MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3071.   return(destination);
  3072. }
  3073.  
  3074. #ifdef FIELDS /* tung, 12/93 */
  3075. /* for use in building so that the real one does not get overstomped */
  3076. static char* field_temp_dictionary_filename(destination, field_id, db)
  3077. char* destination;
  3078. int field_id;
  3079. database* db;
  3080. {
  3081.   strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3082.   s_strncat(destination, field_ext, MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3083.   s_strncat(destination, db->fields[field_id].field_name, 
  3084.             MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3085.   s_strncat(destination,dictionary_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3086.   s_strncat(destination,"tmp",MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3087.   return(destination);
  3088. }
  3089. #endif
  3090.  
  3091. char* document_table_filename(destination,db)
  3092. char* destination;
  3093. database* db;
  3094. {
  3095.   strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3096.   s_strncat(destination,document_table_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3097.   return(destination);
  3098. }
  3099.  
  3100. char* filename_table_filename(destination,db)
  3101. char* destination;
  3102. database* db;
  3103. {
  3104.   strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3105.   s_strncat(destination,filename_table_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3106.   return(destination);
  3107. }
  3108.  
  3109. char* headline_table_filename(destination,db)
  3110. char* destination;
  3111. database* db;
  3112. {
  3113.     strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3114.     s_strncat(destination,headline_table_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3115.     return(destination);
  3116. }
  3117.  
  3118. #ifdef BIO
  3119. char* delimiters_filename(destination,db)
  3120. char* destination;
  3121. database* db;
  3122. {
  3123.     strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3124.     s_strncat(destination,delimiters_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3125.     return(destination);
  3126. }
  3127. #endif
  3128.  
  3129. char* index_filename(destination,db)
  3130. char* destination;
  3131. database* db;
  3132. {
  3133.     strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3134.     s_strncat(destination,index_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3135.     return(destination);
  3136. }
  3137.  
  3138. /* this is used during index creation.  if the version is -2, then it means
  3139.    the real index_filename.  This is a kludge */
  3140. char* index_filename_with_version(version,destination,db)
  3141. long version;
  3142. char* destination;
  3143. database* db;
  3144. {
  3145.   if(version == -2L){
  3146.     return(index_filename(destination, db));
  3147.   }
  3148.   else{
  3149.     sprintf(destination, "%s%s%ld", db->database_file,
  3150.         index_ext, version);
  3151.     return(destination);
  3152.   }
  3153. }
  3154.  
  3155. #ifdef FIELDS /* tung, 12/93 */
  3156. char* field_index_filename(destination, field_name, db)
  3157. char* destination;
  3158. char* field_name;
  3159. database* db;
  3160. {
  3161.    strncpy(destination, db->database_file, MAX_FILE_NAME_LEN);
  3162.   s_strncat(destination, field_ext, MAX_FILE_NAME_LEN, MAX_FILE_NAME_LEN);
  3163.   s_strncat(destination,field_name,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3164.   s_strncat(destination,index_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3165.   return(destination);
  3166. }
  3167.  
  3168. char* field_index_filename_with_version(version,destination, field_id, db)
  3169. long version;
  3170. char* destination;
  3171. long field_id;
  3172. database* db;
  3173. {
  3174.   if(version == -2L){
  3175.     return(field_index_filename(destination, 
  3176.                                 db->fields[field_id].field_name, db));
  3177.   }
  3178.   else{
  3179.     sprintf(destination, "%s%s%s%s%ld", db->database_file, field_ext,
  3180.         db->fields[field_id].field_name, index_ext, version);
  3181.     return(destination);
  3182.   }
  3183. }
  3184. #endif
  3185.  
  3186. char* source_filename(destination,db)
  3187. char* destination;
  3188. database* db;
  3189. {
  3190.   strncpy(destination, db->database_file,MAX_FILE_NAME_LEN);
  3191.   s_strncat(destination,source_ext,MAX_FILE_NAME_LEN,MAX_FILE_NAME_LEN);
  3192.   return(destination);
  3193. }
  3194.  
  3195. char*
  3196. get_doc(destination, document_id, db, headline)
  3197. char* destination;
  3198. long document_id;
  3199. database* db;
  3200. boolean headline;
  3201. {
  3202.   document_table_entry doc_entry;
  3203.   char filename[MAX_FILE_NAME_LEN], type[100];
  3204.   char *hl;
  3205.  
  3206.   if (read_document_table_entry(&doc_entry, document_id, db) 
  3207.       == true){
  3208.     read_filename_table_entry(doc_entry.filename_id, 
  3209.                   filename,
  3210.                   type,
  3211.                   NULL,
  3212.                   db);
  3213.  
  3214. /* francois - multitype extension */
  3215.     if ( strstr(type,",") != NULL ) {
  3216.        type[strstr(type,",") - type] = '\0';
  3217.     }
  3218.  
  3219.  
  3220.     if (headline == TRUE) {
  3221.       hl = read_headline_table_entry(doc_entry.headline_id,db);
  3222.       sprintf(destination, "%d %d %s, \"%s\"", 
  3223.           doc_entry.start_character, doc_entry.end_character,
  3224.           filename, hl);
  3225.     }
  3226.     else
  3227.       sprintf(destination, "%d %d %s", 
  3228.           doc_entry.start_character, doc_entry.end_character,
  3229.           filename);
  3230.     return(s_strdup(type));
  3231.   }
  3232.   else return NULL;
  3233. }
  3234.  
  3235. long next_doc(destination, docID, db)
  3236. char* destination;
  3237. char* docID;
  3238. database* db;
  3239. {
  3240.   long i, start, end;
  3241.   char doc[MAX_FILE_NAME_LEN+50], fn[MAX_FILE_NAME_LEN];
  3242.   char *type, *loc;
  3243.  
  3244.   for(i = 0; i < db->doc_table_allocated_entries; i++) {
  3245.     if ((type = get_doc(doc, i, db, FALSE)) != NULL) {
  3246.       s_free(type);
  3247.       if (strcmp(doc, docID) == 0) {
  3248.     type = get_doc(doc, i+1, db, TRUE);
  3249.     sscanf(doc, "%d %d %s", &start, &end, fn);
  3250.     if((loc = strstr(doc, ",")) == NULL) return -1;
  3251.     fn[loc-doc] = 0;
  3252.     sprintf(destination, "%s, %s", doc, type);
  3253.     s_free(type);
  3254.     if( end != 0)
  3255.       return(end-start);
  3256.     else {    
  3257.       /* whole file, find file length from the file */
  3258.       long size;
  3259.       FILE* file = NULL;
  3260.       if (((file = s_fopen(fn, "r")) != NULL) &&
  3261.           (fseek(file, 0L, SEEK_END) == 0)  &&
  3262.           ((size = ftell(file)) != -1)) {
  3263.         s_fclose(file);
  3264.         return(size);    /* we are done, bytes is set */
  3265.       }
  3266.       else {
  3267.         s_fclose(file);
  3268.         return(-1);        /* something went wrong with the file */
  3269.       }
  3270.     }
  3271.       }
  3272.     }
  3273.   }
  3274.   return -1;
  3275. }
  3276.  
  3277. long previous_doc(destination, docID, db)
  3278. char* destination;
  3279. char* docID;
  3280. database* db;
  3281. {
  3282.   long i, start, end;
  3283.   char doc[MAX_FILE_NAME_LEN+50], fn[MAX_FILE_NAME_LEN];
  3284.   char *type, *loc;
  3285.  
  3286.   for(i = 0; i < db->doc_table_allocated_entries; i++) {
  3287.     if ((type = get_doc(doc, i, db, FALSE)) != NULL) {
  3288.       s_free(type);
  3289.       if (strcmp(doc, docID) == 0) {
  3290.     if (i != 0) {
  3291.       type = get_doc(doc, i-1, db, TRUE);
  3292.       sscanf(doc, "%d %d %s", &start, &end, fn);
  3293.       if((loc = strstr(doc, ",")) == NULL) return -1;
  3294.       fn[loc-doc] = 0;
  3295.       sprintf(destination, "%s, %s", doc, type);
  3296.       s_free(type);
  3297.       if( end != 0)
  3298.         return(end-start);
  3299.       else {    
  3300.         /* whole file, find file length from the file */
  3301.         long size;
  3302.         FILE* file = NULL;
  3303.         if (((file = s_fopen(fn, "r")) != NULL) &&
  3304.         (fseek(file, 0L, SEEK_END) == 0)  &&
  3305.         ((size = ftell(file)) != -1)) {
  3306.           s_fclose(file);
  3307.           return(size);    /* we are done, bytes is set */
  3308.         }
  3309.         else {
  3310.           s_fclose(file);
  3311.           return(-1);    /* something went wrong with the file */
  3312.         }
  3313.       }
  3314.     }
  3315.       }
  3316.     }
  3317.   }
  3318.   return(-1);
  3319. }
  3320.  
  3321. long next_docid(docID, db)
  3322. char* docID;
  3323. database* db;
  3324. {
  3325.   long i;
  3326.   char doc[MAX_FILE_NAME_LEN+50];
  3327.  
  3328.   for(i = 0; i < db->doc_table_allocated_entries; i++) {
  3329.     if (get_doc(doc, i, db, FALSE) != NULL) {
  3330.       if (strcmp(doc, docID) == 0) {
  3331.     return (i+1);
  3332.       }
  3333.     }
  3334.   }
  3335.   return -1;
  3336. }
  3337.  
  3338. long previous_docid(docID, db)
  3339. char* docID;
  3340. database* db;
  3341. {
  3342.   long i;
  3343.   char doc[MAX_FILE_NAME_LEN+50];
  3344.  
  3345.   for(i = 0; i < db->doc_table_allocated_entries; i++) {
  3346.     if (get_doc(doc, i, db, FALSE) != NULL) {
  3347.       if (strcmp(doc, docID) == 0) {
  3348.     return (i-1);
  3349.       }
  3350.     }
  3351.   }
  3352.   return -1;
  3353. }
  3354.  
  3355.