home *** CD-ROM | disk | FTP | other *** search
/ Education Sampler 1992 [NeXTSTEP] / Education_1992_Sampler.iso / Programming / Source / WAIS / ir / irtfiles.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-02  |  17.7 KB  |  661 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. /* 
  9.  * Indexes the words in a text file.
  10.  * 
  11.  * Port of irtfiles.lisp.
  12.  *
  13.  * -brewster 6/90
  14.  */
  15.  
  16. /* the main functions are:
  17.  *   index_text_file
  18.  *   index_directory
  19.  *
  20.  * Some of the policy issues coded in this file are
  21.  *   What extra weight should the headline get?
  22.  *
  23.  */
  24.  
  25. #include <ctype.h>
  26. #include <string.h>
  27. #include "panic.h"  
  28. #include "irdirent.h"
  29. #include "irhash.h"
  30. #include "cutil.h"
  31. #include "futil.h"
  32. #include "irfiles.h"
  33. #include "irtfiles.h"
  34.  
  35. #ifndef THINK_C
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #endif /* ndef THINK_C */
  39.  
  40. #define MAX_LINE_LENGTH 1000 /* characters */
  41. #define extra_weight_for_header 10
  42.  
  43. #ifdef UNIX
  44. #define PRINT_AS_INDEXING true /* also defined in irfiles.c */
  45. #else 
  46. #define PRINT_AS_INDEXING false
  47. #endif
  48.  
  49. char* header_flag_1;
  50. char* header_flag_2;
  51. long len_of_files_since_last_delete = 0;
  52. long len_of_files_since_last_flush = 0;
  53. long total_indexed_file_length = 0;
  54.  
  55. boolean indexingForBeta = false;
  56.  
  57. #ifdef NOTUSED
  58. #define WORD_LETTERS  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
  59.  
  60.  
  61. static char *new_word _AP((char* line,char* word));
  62.  
  63. static char *new_word(line,word)
  64. char *line;
  65. char *word;
  66. {
  67.   /* This copies the first word from line into word while downcasing it.
  68.      It returns a pointer into line that is after the word,
  69.      which can be used to call this function again.
  70.      If there are no words left, then NULL is returned,
  71.      and word is length 0.
  72.      There has got to be a better way.
  73.      */
  74.   long i = 0;
  75.   char *beginning_ptr = strpbrk(line, WORD_LETTERS);
  76.   char *next_word;
  77.   long length;
  78.   if(NULL == beginning_ptr){
  79.     word[0] = '\0';
  80.     return(NULL);
  81.   }
  82.   length  = strspn(beginning_ptr, WORD_LETTERS);
  83.   next_word = length + beginning_ptr;
  84.  
  85.   length = MIN(MAX_WORD_LENGTH,length);
  86.   for(i=0; i<length; i++){
  87.     word[i] = char_downcase((unsigned long)*beginning_ptr++);
  88.   }
  89.   word[i] = '\0';
  90.   return(next_word);
  91. }
  92.  
  93. static boolean reasonable_word _AP((char* word));
  94.  
  95. static boolean reasonable_word(word)
  96. char* word;
  97. /* this should be more sophisticated */
  98. {
  99.   if(strlen(word) > 1){
  100.     return(TRUE);
  101.   }
  102.   else{
  103.     return(FALSE);
  104.   }
  105. }
  106.  
  107. #endif /* def NOTUSED */
  108.  
  109. static long add_words_if_appropriate 
  110.   _AP((char* line,long document_id,long weight,long file_position_before_line,
  111.        long* line_length,boolean* newline_terminated,database* db));
  112.  
  113. static long 
  114. add_words_if_appropriate(line,
  115.              document_id,
  116.              weight,
  117.              file_position_before_line,
  118.              line_length,
  119.              newline_terminated,
  120.              db)
  121. char* line;
  122. long document_id;
  123. long weight;
  124. long file_position_before_line;
  125. long *line_length;
  126. boolean *newline_terminated;
  127. database* db;
  128. {
  129.   /* Add words to the index if it should be done. 
  130.    * Returns the number of words added.
  131.    * Should it return the amount of weight added?
  132.    * The line length is side effected with the length of the line.
  133.    * Newline_terminated is set based on whether the last character
  134.    * in the string was a newline.  If it was not, then it fgets probably
  135.    * did not retrieve the whole line.
  136.    */
  137.  
  138.   long position_in_word = 0;
  139.   long word_count = 0;
  140.   char word[MAX_WORD_LENGTH + 1];
  141.   unsigned long ch;
  142.   long char_count = 0;
  143.  
  144.   for(ch = (unsigned char)line[char_count++]; 
  145.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  146.     boolean alnum = isalnum(ch);
  147.     if(alnum){
  148.       /* put the character in the word if not too long */
  149.       if(position_in_word < MAX_WORD_LENGTH){
  150.     word[position_in_word++] = char_downcase((unsigned long)ch);
  151.       }
  152.     }
  153.     else{ /* not an in a word */
  154.       if(position_in_word != 0){
  155.     /* then we have collected a word */
  156.     if(position_in_word > 1){ /* is it reasonable ? */
  157.       word[position_in_word] = '\0';
  158.       add_word(word,
  159.            file_position_before_line + char_count, 
  160.            0L, /* line_pos */
  161.            weight, 
  162.            document_id, 
  163.            (time_t)0L,
  164.            db);
  165.        word_count++;
  166.     }
  167.     position_in_word = 0;
  168.       }
  169.     }
  170.   }
  171.   /* finish last word */
  172.   if(position_in_word > 1){ /* is it reasonable ? */
  173.     word[position_in_word] = '\0';
  174.     add_word(word,
  175.          file_position_before_line + char_count, 
  176.          0L,        /* line_pos */
  177.          weight, 
  178.          document_id, 
  179.          (time_t)0L,
  180.          db);
  181.     word_count++;
  182.   }
  183.  
  184.   /* for debugging */
  185.   if(char_count - 1 != strlen(line)) {
  186.     waislog(WLOG_HIGH, WLOG_ERROR, 
  187.         "char_count: %ld, strlen: %ld", char_count, strlen(line));
  188.   }
  189.   if('\n' != line[char_count-2])
  190.     *newline_terminated = false;
  191.   else
  192.     *newline_terminated = true;
  193.  
  194.   *line_length = char_count - 1;
  195.   return(word_count);
  196. }
  197.  
  198. static int nodecompare _AP((unsigned long* i,unsigned long* j));
  199.  
  200. static int
  201. nodecompare(i,j)
  202. unsigned long *i, *j;
  203. {
  204.   if (i[0] < j[0])
  205.     return(-1);
  206.   else if (i[0] > j[0])
  207.     return(1);
  208.   else
  209.     return(0);
  210. }
  211.  
  212. #define nodeRange 256 /* 2048 sprint nodes on a full sized machine - should
  213.                  be passed in */
  214. #define iterations_to_reorder 50 /* 1 is best but slow */
  215.  
  216. static void finish_document
  217.   _AP((char* header,char* line,long document_id,
  218.        document_table_entry* the_document_table_entry,
  219.        long file_position_before_line,database* db));
  220.  
  221. static void
  222. finish_document(header,line,document_id,the_document_table_entry,file_position_before_line,db)
  223. char* header;
  224. char* line;
  225. long document_id;
  226. document_table_entry* the_document_table_entry;
  227. long file_position_before_line;
  228. database* db;
  229. { long line_length;
  230.   boolean newline_terminated;
  231.   if(0 != strlen(header)){
  232.     /* add weights for the header (if there was one) */
  233.     the_document_table_entry->document_length +=
  234.       add_words_if_appropriate(header, document_id, 
  235.                    extra_weight_for_header, 
  236.                    file_position_before_line,
  237.                    &line_length, 
  238.                    &newline_terminated,
  239.                    db);
  240.   }
  241.  
  242.   /* store out the document header here */
  243.   the_document_table_entry->headline_id = 
  244.     write_headline_table_entry(header, db);
  245.   if(NULL == line)
  246.     { /* EOF */
  247.       /* if it goes to the end of the file, then
  248.        * set the end_character to 0 so that it is clear that
  249.        * it goes to the end of the file.
  250.        */
  251.       the_document_table_entry->end_character = 0;
  252.     }
  253.   else                /* set the end_character */
  254.     the_document_table_entry->end_character = file_position_before_line;
  255.  
  256.  
  257.   /* 
  258.     waislog("start char: %ld, end char: %ld", 
  259.     the_document_table_entry->start_character,
  260.     the_document_table_entry->end_character);
  261.     */
  262.  
  263.   if (indexingForBeta)
  264.     { /* we need to decide which sprint node this doc will go in.
  265.      for now we will store the sn in the date field, but that
  266.      is temporary
  267.      NOTE that we must subract 1 from document_id, since we want
  268.      a 0 based number
  269.        */
  270.       static unsigned long* nodes = NULL; /* size/node# inited to 0 to 2047 */
  271.       static long minPos;
  272.       unsigned long size;
  273.      
  274.       if (nodes == NULL)
  275.        { long i;
  276.      long startPos;
  277.      time_t temp_time;
  278.  
  279.      nodes = (unsigned long*)s_malloc(sizeof(unsigned long)*nodeRange*2);
  280.      srand((int)time(&temp_time)); /* try to distribute the entries */
  281.      startPos = rand() % nodeRange; /* for indexes with < nodeRng docs */
  282.      for (i = 0; i < nodeRange; i++)
  283.       { nodes[(i * 2) + 1] = (i + startPos) % nodeRange; 
  284.         nodes[i * 2] = 0;
  285.       }
  286.      minPos = 0;
  287. /*printf("init: ");
  288. for (i = 0; i < nodeRange; i++)
  289.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  290. nl();*/
  291.        }
  292.  
  293.       /* place the document in the emptiest node (at minPos) */
  294.       the_document_table_entry->date = (time_t)nodes[(minPos * 2) + 1];
  295.  
  296.       /* increment the size to account for document */
  297.       size = nodes[minPos * 2];
  298.       size += (the_document_table_entry->end_character - 
  299.            the_document_table_entry->start_character);
  300.       nodes[minPos * 2] = size;
  301.  
  302. if ((the_document_table_entry->end_character - 
  303.      the_document_table_entry->start_character) > 100000)
  304.   printf("big doc %lu %s\n",the_document_table_entry->end_character - the_document_table_entry->start_character,header);
  305.  
  306.       minPos++;
  307.  
  308.       /* possibly reorder it */
  309.       if (minPos > iterations_to_reorder)
  310.        { 
  311. long i;
  312.      minPos = 0;
  313. /*printf("before: ");
  314. for (i = 0; i < nodeRange; i++)
  315.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  316. nl();*/
  317.      qsort((char*)nodes,nodeRange,sizeof(unsigned long) * 2,nodecompare);
  318. /*printf("after: ");
  319. for (i = 0; i < nodeRange; i++)
  320.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  321. nl();*/
  322. printf("just sorted nodes, min: ");
  323. for (i = 0; i < 10; i++)
  324.  printf("%lu ",nodes[i * 2]);
  325. printf(", max: %lu/%lu\n",nodes[(nodeRange * 2)-2],nodes[(nodeRange * 2)-1]); 
  326.        }
  327.  
  328.  
  329.  
  330. #ifdef old
  331.       sn = (document_id - 1) % 2048; /* 2048 = sn's in a full machine */
  332.  
  333.       /* should also take into account the "fullness" of any particular
  334.      node */
  335.       the_document_table_entry->date = (time_t)sn;
  336. /*      waislog(WLOG_LOW, WLOG_INFO, 
  337.           "put %s in sprint node %ld",header,sn);*/
  338. #endif /* def old */
  339.     }
  340.  
  341.   write_document_table_entry(the_document_table_entry, db);
  342.   cprintf(PRINT_AS_INDEXING, ".");
  343.   total_indexed_file_length =    /* set this so the speed looks right */
  344.     total_indexed_file_length + file_position_before_line;
  345.   total_indexed_file_length =    /* set it back */
  346.     total_indexed_file_length - file_position_before_line;
  347. }
  348.  
  349. #define LENGTH_OF_NEWLINE 1 /* this will be 2 on a PC, I think */  
  350.  
  351. void index_text_file(filename,
  352.              separator_function,
  353.              header_function,
  354.              date_function,
  355.              finish_header_function, 
  356.              type,
  357.              db,
  358.              check_for_text_file,
  359.              check_for_file_already_indexed)
  360. char* filename;
  361. boolfunc *separator_function;
  362. voidfunc *header_function;
  363. longfunc *date_function;
  364. voidfunc *finish_header_function;
  365. char *type;
  366. database* db;
  367. boolean check_for_text_file;
  368. boolean check_for_file_already_indexed;
  369. {
  370.   /* Addes words to the index for a given file.  
  371.    * The function arguments can be NULL which means it would 
  372.    *  always answer NULL.  
  373.    * separator_function is called on every line to see if it 
  374.    *  separates documents.  
  375.    * header_function is called on every line so that a headline 
  376.    *  can be accumulated.  This assumes that it will side effect global 
  377.    *  variables.
  378.    * finish_header_function is called when the document is finished 
  379.    *  (by separator function responding TRUE or EOF) this will return 
  380.    *  the headline string or NULL. 
  381.    *  Presumably finish_header_function will use the
  382.    *  effects of header_function.  finish_header_function 
  383.    *  will only be called once, so it should clear whatever state 
  384.    *  header_function has set.
  385.    * if check_for_text_file then it looks to see if first character
  386.    *  in the file is a printable character.
  387.    * if check_for_file_already_indexed then it looks through the filename 
  388.    *  file to see if the file has not been indexed.  If it has,
  389.    *  then it is checked to see if it is up-to-date. (it does not 
  390.    *  kill the old entry (maybe it should)).
  391.    */
  392.  
  393.   long filename_id;
  394.   document_table_entry the_document_table_entry;
  395.   long document_id = next_document_id(db);
  396.   FILE* input_stream = s_fopen(filename, "r");
  397.   long file_position_before_line = 0;
  398.   long date;
  399.  
  400.   if(NULL == input_stream){
  401.     waislog(WLOG_HIGH, WLOG_ERROR, 
  402.         "File %s does not exist", filename);
  403.     /* then the is not a valid file to be indexed */
  404.     return;
  405.   }
  406.   if(check_for_file_already_indexed){
  407.     time_t time;
  408.     char full_path[MAX_FILENAME_LEN];
  409.     truename(filename, full_path);
  410.     if(true == filename_in_database(full_path, type, &time, db)){
  411.       /* check that it is the same time as this file */
  412.       if(time == file_write_date(filename)){
  413.     waislog(WLOG_HIGH, WLOG_INDEX, 
  414.         "File %s already indexed", filename);
  415.     s_fclose(input_stream);
  416.     return;
  417.       }
  418.     }
  419.   }
  420.     
  421.   if(check_for_text_file){ 
  422.     /* if we need this to be a text file, check the first character
  423.        for a printable character */
  424.     long ch = fgetc(input_stream);
  425.     /* printf("First character is '%c'\n", ch); */
  426.     if(EOF == ch || (!isprint(ch) && !isspace(ch))){
  427.       s_fclose(input_stream);
  428.       return;
  429.     }
  430.     ungetc(ch, input_stream);
  431.   }
  432.   
  433.   /* write out the filename */
  434.   filename_id = write_filename_table_entry(filename, type, db);
  435.   
  436.   /*  (if (not *drop_table*) (make_drop_table)) maybe put in later */
  437.   
  438.   header_flag_1 = NULL;
  439.   the_document_table_entry.filename_id = filename_id;
  440.   the_document_table_entry.start_character = 0;
  441.   the_document_table_entry.document_length = 0;
  442.   the_document_table_entry.number_of_lines = 0;
  443.   the_document_table_entry.date = 0;
  444.  
  445.   while(TRUE){
  446.     long line_length;
  447.     boolean newline_terminated;
  448.     char line[MAX_LINE_LENGTH];
  449.     char header[MAX_LINE_LENGTH];
  450.     char* read_line_result;
  451.     boolean eof;
  452.  
  453.     /* printf("ftell: %ld\n", ftell(input_stream)); */
  454.     /* read a line */
  455.     read_line_result  = fgets(line, MAX_LINE_LENGTH, input_stream);
  456.     beFriendly();
  457.      
  458.     /* eof = feof(input_stream); */ /* zero means not eof */
  459.     eof    = !read_line_result; 
  460.     
  461.     the_document_table_entry.number_of_lines++;
  462.  
  463.     header[0] = '\0';        /* set it to the empty string */
  464.     if(eof ||
  465.        ((NULL != separator_function) &&
  466.     separator_function(line))){
  467.   
  468.       /* we are processing a separator, therefore we should
  469.        * finish off the last document, and start a new one
  470.        */
  471.       if(NULL != finish_header_function){
  472.     finish_header_function(header);
  473.       }
  474.       if(0 == strlen(header)){
  475.     char full_path[1000];
  476.     char directory[1000];
  477.     truename(filename, full_path);
  478.     sprintf(header, "%s   %s", pathname_name(full_path),
  479.         pathname_directory(full_path, directory));
  480.       }
  481.       the_document_table_entry.number_of_lines--; /* dont count separator */
  482.       /* finish off the last */
  483.       finish_document(header, line, document_id,
  484.               &the_document_table_entry,
  485.               eof?    /* if EOF, use file length */
  486.               file_length(input_stream):file_position_before_line, 
  487.               db);
  488.       /* initialize the next one */
  489.       the_document_table_entry.filename_id = filename_id;
  490.       the_document_table_entry.start_character = file_position_before_line;
  491.       the_document_table_entry.number_of_lines = 1; /* count separator */
  492.       the_document_table_entry.date = 0;
  493.  
  494.       document_id = next_document_id(db);    
  495.  
  496.       if(!eof)
  497.     {            /* not EOF */
  498.       if(NULL != header_function){
  499.         header_function(line);
  500.       }
  501.       line_length = strlen(line);
  502.       newline_terminated = true;
  503.     }
  504.       else{            /* EOF */
  505.     /* printf("closing the file\n"); */
  506.     s_fclose(input_stream);
  507.     return;
  508.       }
  509.     }
  510.         
  511.     else{               
  512.       /* not a separator or EOF so process the line */
  513.       long number_of_words;
  514.       if(NULL != header_function) header_function(line);
  515.       if (date_function != NULL && 
  516.       the_document_table_entry.date == 0 &&
  517.            (date = date_function(line)) > 0) 
  518.     the_document_table_entry.date = date;
  519.  
  520.       number_of_words = add_words_if_appropriate(line, document_id, 1L, 
  521.                          file_position_before_line,
  522.                          &line_length, 
  523.                          &newline_terminated,
  524.                          db);
  525.       the_document_table_entry.document_length += number_of_words;
  526.       len_of_files_since_last_delete += number_of_words;
  527.       len_of_files_since_last_flush += number_of_words;
  528.     }
  529.     if(newline_terminated)
  530.       file_position_before_line += (line_length + 
  531.                     LENGTH_OF_NEWLINE /* in case of crlf */
  532.                     - 1 /* fgets gets one newline */
  533.                     );
  534.     else
  535.       file_position_before_line = ftell(input_stream);
  536.  
  537.     
  538.     /* for debugging
  539.     if(file_position_before_line != ftell(input_stream)) {
  540.       waislog(WLOG_LOW, WLOG_INFO, "ftell: %ld, computed ftell: %ld", 
  541.          ftell(input_stream),
  542.          file_position_before_line);
  543.          }
  544.          */    
  545.  
  546.   }
  547. }
  548.  
  549.  
  550.  
  551.  
  552. /* return TRUE if it is a directory, FALSE otherwise */
  553. boolean directoryp(file)
  554. char *file;
  555.  
  556. {
  557. #ifdef THINK_C
  558.   return(false);
  559. #else
  560.   struct stat stbuf;
  561.   if(stat(file, &stbuf) == -1)
  562.     return(FALSE);
  563.   if((stbuf.st_mode & S_IFMT) == S_IFDIR)
  564.     return(true);
  565.   return(FALSE);
  566. #endif
  567. }
  568.  
  569. /* return true if it is a file, FALSE otherwise */
  570. boolean filep(file)
  571. char *file;
  572. {
  573. #ifdef THINK_C
  574.  return(probe_file(file));
  575. #else
  576.   struct stat stbuf;
  577.   if(stat(file, &stbuf) == -1)
  578.     return(FALSE);
  579.   if(!((stbuf.st_mode & S_IFMT) == S_IFDIR))
  580.     return(true);
  581.   return(FALSE);
  582. #endif
  583. }
  584.  
  585. /* recursively indexes the directory specified. 
  586.  * If it is a file, then index it. 
  587.  */
  588. void index_directory(file,
  589.              separator_function,
  590.              header_function,
  591.              date_function,
  592.              finish_header_function, 
  593.              type,
  594.              db,
  595.              check_for_text_file,
  596.              check_for_file_already_indexed)
  597. char *file;
  598. boolfunc *separator_function;
  599. voidfunc *header_function;
  600. longfunc *date_function;
  601. voidfunc *finish_header_function;
  602. char *type;
  603. database* db;
  604. boolean check_for_text_file;
  605. boolean check_for_file_already_indexed;
  606. {
  607.   if(filep(file)){
  608.     waislog(WLOG_MEDIUM, WLOG_INDEX,
  609.         "%s Indexing file: %s", printable_time(), file);
  610.     index_text_file(file, separator_function,
  611.             header_function, 
  612.             date_function,
  613.             finish_header_function,
  614.             type,
  615.             db,
  616.             check_for_text_file,
  617.             check_for_file_already_indexed);
  618.   }
  619.   else if(directoryp(file)){
  620. #ifndef THINK_C
  621. #ifdef SYSV
  622.     FILE *dirp = s_fopen(file, "r");
  623. #else
  624.     DIR *dirp = opendir(file);
  625. #endif
  626.     struct dirent *dp;
  627.     char name[1000];        /* max filename size */
  628.  
  629. #ifdef SYSV
  630.     while (fread((char *)dp,  sizeof( *dp), 1, dirp) == 1) {
  631. #else
  632.     while ((dp = readdir(dirp)) != NULL) {
  633. #endif
  634.       if(strcmp(dp->d_name, ".") == 0
  635.      || strcmp(dp->d_name, "..") == 0
  636.      )
  637.     continue;
  638.       strcpy(name, file);    /* copy the filename into the name variable */
  639.       strcat(name, "/");
  640.       strcat(name, dp->d_name);
  641.       index_directory(name, separator_function,
  642.               header_function, 
  643.               date_function,
  644.               finish_header_function,
  645.               type,
  646.               db,
  647.               check_for_text_file,
  648.               check_for_file_already_indexed);
  649.     }
  650. #ifdef SYSV
  651.     s_fclose(dirp);
  652. #else /* ndef SYSV */
  653.     closedir(dirp);
  654. #endif /* ndef SYSV */
  655. #endif /*ndef THINK_C */
  656.   }
  657. }
  658.  
  659.  
  660.  
  661.