LCOV - code coverage report
Current view: top level - libs/libbib - index.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 7 413 1.7 %
Date: 2026-01-16 17:51:41 Functions: 1 25 4.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1989-2020 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include <config.h>
      21             : #endif
      22             : 
      23             : #include <errno.h>
      24             : #include <stdlib.h> // free(), malloc()
      25             : 
      26             : #include "lib.h"
      27             : 
      28             : #include "posix.h"
      29             : #include "cset.h"
      30             : #include "cmap.h"
      31             : #include "errarg.h"
      32             : #include "error.h"
      33             : 
      34             : #include "refid.h"
      35             : #include "search.h"
      36             : #include "index.h"
      37             : #include "defs.h"
      38             : 
      39             : #include "nonposix.h"
      40             : 
      41             : // Interface to mmap.
      42             : extern "C" {
      43             :   void *mapread(int fd, int len);
      44             :   int unmap(void *, int len);
      45             : }
      46             : 
      47             : #if 0
      48             : const 
      49             : #endif
      50             : int minus_one = -1;
      51             : 
      52             : bool do_verify = false;
      53             : 
      54             : struct word_list;
      55             : 
      56             : class index_search_item : public search_item {
      57             :   search_item *out_of_date_files;
      58             :   index_header header;
      59             :   char *buffer;
      60             :   void *map_addr;
      61             :   int map_len;
      62             :   tag *tags;
      63             :   int *table;
      64             :   int *lists;
      65             :   char *pool;
      66             :   char *key_buffer;
      67             :   char *filename_buffer;
      68             :   size_t filename_buflen;
      69             :   char **common_words_table;
      70             :   int common_words_table_size;
      71             :   const char *ignore_fields;
      72             :   time_t mtime;
      73             : 
      74             :   const char *get_invalidity_reason();
      75             :   const int *search1(const char **pp, const char *end);
      76             :   const int *search(const char *ptr, int length, int **temp_listp);
      77             :   const char *munge_filename(const char *);
      78             :   void read_common_words_file();
      79             :   void add_out_of_date_file(int fd, const char *filename, int fid);
      80             : public:
      81             :   index_search_item(const char *, int);
      82             :   ~index_search_item();
      83             :   const char *check_header(index_header *, unsigned);
      84             :   bool load(int fd);
      85             :   search_item_iterator *make_search_item_iterator(const char *);
      86             :   bool is_valid();
      87             :   void check_files();
      88             :   int next_filename_id() const;
      89             :   friend class index_search_item_iterator;
      90             : };
      91             : 
      92             : class index_search_item_iterator : public search_item_iterator {
      93             :   index_search_item *indx;
      94             :   search_item_iterator *out_of_date_files_iter;
      95             :   search_item *next_out_of_date_file;
      96             :   const int *found_list;
      97             :   int *temp_list;
      98             :   char *buf;
      99             :   int buflen;
     100             :   linear_searcher searcher;
     101             :   char *query;
     102             :   int get_tag(int tagno, const linear_searcher &, const char **, int *,
     103             :               reference_id *);
     104             : public:
     105             :   index_search_item_iterator(index_search_item *, const char *);
     106             :   ~index_search_item_iterator();
     107             :   int next(const linear_searcher &, const char **, int *, reference_id *);
     108             : };
     109             : 
     110             : 
     111           0 : index_search_item::index_search_item(const char *filename, int fid)
     112             : : search_item(filename, fid), out_of_date_files(0), buffer(0), map_addr(0),
     113             :   map_len(0), key_buffer(0), filename_buffer(0), filename_buflen(0),
     114           0 :   common_words_table(0)
     115             : {
     116           0 : }
     117             : 
     118           0 : index_search_item::~index_search_item()
     119             : {
     120           0 :   if (buffer)
     121           0 :     free(buffer);
     122           0 :   if (map_addr) {
     123           0 :     if (unmap(map_addr, map_len) < 0)
     124           0 :       error("unmap: %1", strerror(errno));
     125             :   }
     126           0 :   while (out_of_date_files) {
     127           0 :     search_item *tem = out_of_date_files;
     128           0 :     out_of_date_files = out_of_date_files->next;
     129           0 :     delete tem;
     130             :   }
     131           0 :   delete[] filename_buffer;
     132           0 :   delete[] key_buffer;
     133           0 :   if (common_words_table) {
     134           0 :     for (int i = 0; i < common_words_table_size; i++)
     135           0 :       delete[] common_words_table[i];
     136           0 :     delete[] common_words_table;
     137             :   }
     138           0 : }
     139             : 
     140             : class file_closer {
     141             :   int *fdp;
     142             : public:
     143           0 :   file_closer(int &fd) : fdp(&fd) { }
     144           0 :   ~file_closer() { close(*fdp); }
     145             : };
     146             : 
     147             : // Tell the compiler that a variable is intentionally unused.
     148           0 : inline void unused(void *) { }
     149             : 
     150             : // Validate the data reported in the header so that we don't overread on
     151             : // the heap in the load() member function.  Return null pointer if no
     152             : // problems are detected.
     153           0 : const char *index_search_item::check_header(index_header *file_header,
     154             :                                             unsigned file_size)
     155             : {
     156           0 :   if (file_header->tags_size < 0)
     157           0 :     return "tag list length negative";
     158           0 :   if (file_header->lists_size < 0)
     159           0 :     return "reference list length negative";
     160             :   // The table and string pool sizes will not be zero, even in an empty
     161             :   // index.
     162           0 :   if (file_header->table_size < 1)
     163           0 :     return "table size nonpositive";
     164           0 :   if (file_header->strings_size < 1)
     165           0 :     return "string pool size nonpositive";
     166           0 :   size_t sz = (file_header->tags_size * sizeof(tag)
     167           0 :                + file_header->lists_size * sizeof(int)
     168           0 :                + file_header->table_size * sizeof(int)
     169           0 :                + file_header->strings_size
     170             :                + sizeof *file_header);
     171           0 :   if (sz != file_size)
     172           0 :     return("size mismatch between header and data");
     173           0 :   unsigned size_remaining = file_size;
     174           0 :   unsigned chunk_size = file_header->tags_size * sizeof(tag);
     175           0 :   if (chunk_size > size_remaining)
     176           0 :     return "claimed tag list length exceeds file size";
     177           0 :   size_remaining -= chunk_size;
     178           0 :   chunk_size = file_header->lists_size * sizeof(int);
     179           0 :   if (chunk_size > size_remaining)
     180           0 :     return "claimed reference list length exceeds file size";
     181           0 :   size_remaining -= chunk_size;
     182           0 :   chunk_size = file_header->table_size * sizeof(int);
     183           0 :   if (chunk_size > size_remaining)
     184           0 :     return "claimed table size exceeds file size";
     185           0 :   size_remaining -= chunk_size;
     186           0 :   chunk_size = file_header->strings_size;
     187           0 :   if (chunk_size > size_remaining)
     188           0 :     return "claimed string pool size exceeds file size";
     189           0 :   return 0;
     190             : }
     191             : 
     192           0 : bool index_search_item::load(int fd)
     193             : {
     194           0 :   file_closer fd_closer(fd);    // close fd on return
     195           0 :   unused(&fd_closer);
     196             :   struct stat sb;
     197           0 :   if (fstat(fd, &sb) < 0) {
     198           0 :     error("can't fstat index '%1': %2", name, strerror(errno));
     199           0 :     return false;
     200             :   }
     201           0 :   if (!S_ISREG(sb.st_mode)) {
     202           0 :     error("index '%1' is not a regular file", name);
     203           0 :     return false;
     204             :   }
     205           0 :   mtime = sb.st_mtime;
     206           0 :   unsigned size = unsigned(sb.st_size); // widening conversion
     207           0 :   if (size == 0) {
     208           0 :     error("index '%1' is an empty file", name);
     209           0 :     return false;
     210             :   }
     211             :   char *addr;
     212           0 :   map_addr = mapread(fd, size);
     213           0 :   if (map_addr) {
     214           0 :     addr = (char *)map_addr;
     215           0 :     map_len = size;
     216             :   }
     217             :   else {
     218           0 :     addr = buffer = (char *)malloc(size);
     219           0 :     if (buffer == 0) {
     220           0 :       error("can't allocate memory to process index '%1'", name);
     221           0 :       return false;
     222             :     }
     223           0 :     char *ptr = buffer;
     224           0 :     int bytes_to_read = size;
     225           0 :     while (bytes_to_read > 0) {
     226           0 :       int nread = read(fd, ptr, bytes_to_read);
     227           0 :       if (nread == 0) {
     228           0 :         error("unexpected end-of-file while reading index '%1'", name);
     229           0 :         return false;
     230             :       }
     231           0 :       if (nread < 0) {
     232           0 :         error("read error on index '%1': %2", name, strerror(errno));
     233           0 :         return false;
     234             :       }
     235           0 :       bytes_to_read -= nread;
     236           0 :       ptr += nread;
     237             :     }
     238             :   }
     239           0 :   header = *(index_header *)addr;
     240           0 :   if (header.magic != INDEX_MAGIC) {
     241           0 :     error("'%1' is not an index file: wrong magic number", name);
     242           0 :     return false;
     243             :   }
     244           0 :   if (header.version != INDEX_VERSION) {
     245           0 :     error("version number in index '%1' is wrong: was %2, should be %3",
     246           0 :           name, header.version, INDEX_VERSION);
     247           0 :     return false;
     248             :   }
     249           0 :   const char *problem = check_header(&header, size);
     250           0 :   if (problem != 0) {
     251           0 :     if (do_verify)
     252           0 :       error("corrupt header in index file '%1': %2", name, problem);
     253             :     else
     254           0 :       error("corrupt header in index file '%1'", name);
     255           0 :     return false;
     256             :   }
     257           0 :   tags = (tag *)(addr + sizeof(header));
     258           0 :   lists = (int *)(tags + header.tags_size);
     259           0 :   table = (int *)(lists + header.lists_size);
     260           0 :   pool = (char *)(table + header.table_size);
     261           0 :   ignore_fields = strchr(strchr(pool, '\0') + 1, '\0') + 1;
     262           0 :   key_buffer = new char[header.truncate];
     263           0 :   read_common_words_file();
     264           0 :   return true;
     265             : }
     266             : 
     267           0 : const char *index_search_item::get_invalidity_reason()
     268             : {
     269           0 :   if (tags == 0)
     270           0 :     return "not loaded";
     271           0 :   if ((header.lists_size > 0) && (lists[header.lists_size - 1] >= 0))
     272           0 :     return "last list element not negative";
     273             :   int i;
     274           0 :   for (i = 0; i < header.table_size; i++) {
     275           0 :     int li = table[i];
     276           0 :     if (li >= header.lists_size)
     277           0 :       return "bad list index";
     278           0 :     if (li >= 0) {
     279           0 :       for (int *ptr = lists + li; *ptr >= 0; ptr++) {
     280           0 :         if (*ptr >= header.tags_size)
     281           0 :           return "bad tag index";
     282           0 :         if (*ptr >= ptr[1] && ptr[1] >= 0)
     283           0 :           return "list not ordered";
     284             :       }
     285             :     }
     286             :   }
     287           0 :   for (i = 0; i < header.tags_size; i++) {
     288           0 :     if (tags[i].filename_index >= header.strings_size)
     289           0 :       return "bad index in tags";
     290           0 :     if (tags[i].length < 0)
     291           0 :       return "bad length in tags";
     292           0 :     if (tags[i].start < 0)
     293           0 :       return "bad start in tags";
     294             :   }
     295           0 :   if (pool[header.strings_size - 1] != '\0')
     296           0 :     return "last character in string pool is not null";
     297           0 :   return 0;
     298             : }
     299             : 
     300           0 : bool index_search_item::is_valid()
     301             : {
     302           0 :   const char *reason = get_invalidity_reason();
     303           0 :   if (!reason)
     304           0 :     return true;
     305           0 :   error("'%1' is bad: %2", name, reason);
     306           0 :   return false;
     307             : }
     308             : 
     309           0 : int index_search_item::next_filename_id() const
     310             : {
     311           0 :   return filename_id + header.strings_size + 1;
     312             : }
     313             : 
     314           0 : search_item_iterator *index_search_item::make_search_item_iterator(
     315             :   const char *query)
     316             : {
     317           0 :   return new index_search_item_iterator(this, query);
     318             : }
     319             : 
     320           2 : search_item *make_index_search_item(const char *filename, int fid)
     321             : {
     322           2 :   char *index_filename = new char[strlen(filename) + sizeof(INDEX_SUFFIX)];
     323           2 :   strcpy(index_filename, filename);
     324           2 :   strcat(index_filename, INDEX_SUFFIX);
     325           2 :   int fd = open(index_filename, O_RDONLY | O_BINARY);
     326           2 :   if (fd < 0)
     327           2 :     return 0;
     328           0 :   index_search_item *item = new index_search_item(index_filename, fid);
     329           0 :   delete[] index_filename;
     330           0 :   if (!item->load(fd)) {
     331           0 :     close(fd);
     332           0 :     delete item;
     333           0 :     return 0;
     334             :   }
     335           0 :   else if (do_verify && !item->is_valid()) {
     336           0 :     delete item;
     337           0 :     return 0;
     338             :   }
     339             :   else {
     340           0 :     item->check_files();
     341           0 :     return item;
     342             :   }
     343             : }
     344             : 
     345             : 
     346           0 : index_search_item_iterator::index_search_item_iterator(index_search_item *ind,
     347           0 :                                                        const char *q)
     348             : : indx(ind), out_of_date_files_iter(0), next_out_of_date_file(0), temp_list(0),
     349             :   buf(0), buflen(0),
     350           0 :   searcher(q, strlen(q), ind->ignore_fields, ind->header.truncate),
     351           0 :   query(strsave(q))
     352             : {
     353           0 :   found_list = indx->search(q, strlen(q), &temp_list);
     354           0 :   if (!found_list) {
     355           0 :     found_list = &minus_one;
     356           0 :     warning("all keys would have been discarded in constructing index '%1'",
     357           0 :             indx->name);
     358             :   }
     359           0 : }
     360             : 
     361           0 : index_search_item_iterator::~index_search_item_iterator()
     362             : {
     363           0 :   delete[] temp_list;
     364           0 :   delete[] buf;
     365           0 :   delete[] query;
     366           0 :   delete out_of_date_files_iter;
     367           0 : }
     368             : 
     369           0 : int index_search_item_iterator::next(const linear_searcher &,
     370             :                                      const char **pp, int *lenp,
     371             :                                      reference_id *ridp)
     372             : {
     373           0 :   if (found_list) {
     374             :     for (;;) {
     375           0 :       int tagno = *found_list;
     376           0 :       if (tagno == -1)
     377           0 :         break;
     378           0 :       found_list++;
     379           0 :       if (get_tag(tagno, searcher, pp, lenp, ridp))
     380           0 :         return 1;
     381           0 :     }
     382           0 :     found_list = 0;
     383           0 :     next_out_of_date_file = indx->out_of_date_files;
     384             :   }
     385           0 :   while (next_out_of_date_file) {
     386           0 :     if (out_of_date_files_iter == 0)
     387             :       out_of_date_files_iter
     388           0 :         = next_out_of_date_file->make_search_item_iterator(query);
     389           0 :     if (out_of_date_files_iter->next(searcher, pp, lenp, ridp))
     390           0 :       return 1;
     391           0 :     delete out_of_date_files_iter;
     392           0 :     out_of_date_files_iter = 0;
     393           0 :     next_out_of_date_file = next_out_of_date_file->next;
     394             :   }
     395           0 :   return 0;
     396             : }
     397             : 
     398           0 : int index_search_item_iterator::get_tag(int tagno,
     399             :                                         const linear_searcher &searchr,
     400             :                                         const char **pp, int *lenp,
     401             :                                         reference_id *ridp)
     402             : {
     403           0 :   if (tagno < 0 || tagno >= indx->header.tags_size) {
     404           0 :     error("bad tag number");
     405           0 :     return 0;
     406             :   }
     407           0 :   tag *tp = indx->tags + tagno;
     408           0 :   const char *filename = indx->munge_filename(indx->pool + tp->filename_index);
     409           0 :   int fd = open(filename, O_RDONLY | O_BINARY);
     410           0 :   if (fd < 0) {
     411           0 :     error("can't open '%1': %2", filename, strerror(errno));
     412           0 :     return 0;
     413             :   }
     414             :   struct stat sb;
     415           0 :   if (fstat(fd, &sb) < 0) {
     416           0 :     error("can't fstat: %1", strerror(errno));
     417           0 :     close(fd);
     418           0 :     return 0;
     419             :   }
     420           0 :   time_t mtime = sb.st_mtime;
     421           0 :   if (mtime > indx->mtime) {
     422           0 :     indx->add_out_of_date_file(fd, filename,
     423           0 :                                indx->filename_id + tp->filename_index);
     424           0 :     return 0;
     425             :   }
     426           0 :   int res = 0;
     427           0 :   FILE *fp = fdopen(fd, FOPEN_RB);
     428           0 :   if (!fp) {
     429           0 :     error("fdopen failed");
     430           0 :     close(fd);
     431           0 :     return 0;
     432             :   }
     433           0 :   if (tp->start != 0 && fseek(fp, long(tp->start), 0) < 0)
     434           0 :     error("can't seek on '%1': %2", filename, strerror(errno));
     435             :   else {
     436           0 :     int length = tp->length;
     437           0 :     int err = 0;
     438           0 :     if (length == 0) {
     439           0 :       if (fstat(fileno(fp), &sb) < 0) {
     440           0 :         error("can't stat '%1': %2", filename, strerror(errno));
     441           0 :         err = 1;
     442             :       }
     443           0 :       else if (!S_ISREG(sb.st_mode)) {
     444           0 :         error("'%1' is not a regular file", filename);
     445           0 :         err = 1;
     446             :       }
     447             :       else
     448           0 :         length = int(sb.st_size);
     449             :     }
     450           0 :     if (!err) {
     451           0 :       if (length + 2 > buflen) {
     452           0 :         delete[] buf;
     453           0 :         buflen = length + 2;
     454           0 :         buf = new char[buflen];
     455             :       }
     456           0 :       if (fread(buf + 1, 1, length, fp) != (size_t)length)
     457           0 :         error("fread on '%1' failed: %2", filename, strerror(errno));
     458             :       else {
     459           0 :         buf[0] = '\n';
     460             :         // Remove the CR characters from CRLF pairs.
     461           0 :         int sidx = 1, didx = 1;
     462           0 :         for ( ; sidx < length + 1; sidx++, didx++)
     463             :           {
     464           0 :             if (buf[sidx] == '\r')
     465             :               {
     466           0 :                 if (buf[++sidx] != '\n')
     467           0 :                   buf[didx++] = '\r';
     468             :                 else
     469           0 :                   length--;
     470             :               }
     471           0 :             if (sidx != didx)
     472           0 :               buf[didx] = buf[sidx];
     473             :           }
     474           0 :         buf[length + 1] = '\n';
     475           0 :         res = searchr.search(buf + 1, buf + 2 + length, pp, lenp);
     476           0 :         if (res && ridp)
     477           0 :           *ridp = reference_id(indx->filename_id + tp->filename_index,
     478           0 :                                tp->start);
     479             :       }
     480             :     }
     481             :   }
     482           0 :   fclose(fp);
     483           0 :   return res;
     484             : }
     485             : 
     486           0 : const char *index_search_item::munge_filename(const char *filename)
     487             : {
     488           0 :   if (IS_ABSOLUTE(filename))
     489           0 :     return filename;
     490           0 :   const char *cwd = pool;
     491           0 :   int need_slash = (cwd[0] != 0
     492           0 :                     && strchr(DIR_SEPS, strchr(cwd, '\0')[-1]) == 0);
     493           0 :   size_t len = strlen(cwd) + strlen(filename) + need_slash + 1;
     494           0 :   if (len > filename_buflen) {
     495           0 :     delete[] filename_buffer;
     496           0 :     filename_buflen = len;
     497           0 :     filename_buffer = new char[len];
     498             :   }
     499           0 :   strcpy(filename_buffer, cwd);
     500           0 :   if (need_slash)
     501           0 :     strcat(filename_buffer, "/");
     502           0 :   strcat(filename_buffer, filename);
     503           0 :   return filename_buffer;
     504             : }
     505             : 
     506           0 : const int *index_search_item::search1(const char **pp, const char *end)
     507             : {
     508           0 :   while (*pp < end && !csalnum(**pp))
     509           0 :     *pp += 1;
     510           0 :   if (*pp >= end)
     511           0 :     return 0;
     512           0 :   const char *start = *pp;
     513           0 :   while (*pp < end && csalnum(**pp))
     514           0 :     *pp += 1;
     515           0 :   int len = *pp - start;
     516           0 :   if (len < header.shortest)
     517           0 :     return 0;
     518           0 :   if (len > header.truncate)
     519           0 :     len = header.truncate;
     520           0 :   int is_number = 1;
     521           0 :   for (int i = 0; i < len; i++)
     522           0 :     if (csdigit(start[i]))
     523           0 :       key_buffer[i] = start[i];
     524             :     else {
     525           0 :       key_buffer[i] = cmlower(start[i]);
     526           0 :       is_number = 0;
     527             :     }
     528           0 :   if (is_number && !(len == 4 && start[0] == '1' && start[1] == '9'))
     529           0 :     return 0;
     530           0 :   unsigned hc = hash(key_buffer, len);
     531           0 :   if (common_words_table) {
     532           0 :     for (int h = hc % common_words_table_size;
     533           0 :          common_words_table[h];
     534             :          --h) {
     535           0 :       if (strlen(common_words_table[h]) == (size_t)len
     536           0 :           && memcmp(common_words_table[h], key_buffer, len) == 0)
     537           0 :         return 0;
     538           0 :       if (h == 0)
     539           0 :         h = common_words_table_size;
     540             :     }
     541             :   }
     542           0 :   int li = table[int(hc % header.table_size)];
     543           0 :   return li < 0 ? &minus_one : lists + li;
     544             : }
     545             : 
     546           0 : static void merge(int *result, const int *s1, const int *s2)
     547             : {
     548           0 :   for (; *s1 >= 0; s1++) {
     549           0 :     while (*s2 >= 0 && *s2 < *s1)
     550           0 :       s2++;
     551           0 :     if (*s2 == *s1)
     552           0 :       *result++ = *s2;
     553             :   }
     554           0 :   *result++ = -1;
     555           0 : }
     556             : 
     557           0 : const int *index_search_item::search(const char *ptr, int length,
     558             :                                      int **temp_listp)
     559             : {
     560           0 :   const char *end = ptr + length;
     561           0 :   if (*temp_listp) {
     562           0 :     delete[] *temp_listp;
     563           0 :     *temp_listp = 0;
     564             :   }
     565           0 :   const int *first_list = 0;
     566           0 :   while (ptr < end && (first_list = search1(&ptr, end)) == 0)
     567             :     ;
     568           0 :   if (!first_list)
     569           0 :     return 0;
     570           0 :   if (*first_list < 0)
     571           0 :     return first_list;
     572           0 :   const int *second_list = 0;
     573           0 :   while (ptr < end && (second_list = search1(&ptr, end)) == 0)
     574             :     ;
     575           0 :   if (!second_list)
     576           0 :     return first_list;
     577           0 :   if (*second_list < 0)
     578           0 :     return second_list;
     579             :   const int *p;
     580           0 :   for (p = first_list; *p >= 0; p++)
     581             :     ;
     582           0 :   int len = p - first_list;
     583           0 :   for (p = second_list; *p >= 0; p++)
     584             :     ;
     585           0 :   if (p - second_list < len)
     586           0 :     len = p - second_list;
     587           0 :   int *matches = new int[len + 1];
     588           0 :   merge(matches, first_list, second_list);
     589           0 :   while (ptr < end) {
     590           0 :     const int *list = search1(&ptr, end);
     591           0 :     if (list != 0) {
     592           0 :       if (*list < 0) {
     593           0 :         delete[] matches;
     594           0 :         return list;
     595             :       }
     596           0 :       merge(matches, matches, list);
     597           0 :       if (*matches < 0) {
     598           0 :         delete[] matches;
     599           0 :         return &minus_one;
     600             :       }
     601             :     }
     602             :   }
     603           0 :   *temp_listp = matches;
     604           0 :   return matches;
     605             : }
     606             : 
     607           0 : void index_search_item::read_common_words_file()
     608             : {
     609           0 :   if (header.common <= 0)
     610           0 :     return;
     611           0 :   const char *common_words_file = munge_filename(strchr(pool, '\0') + 1);
     612           0 :   errno = 0;
     613           0 :   FILE *fp = fopen(common_words_file, "r");
     614           0 :   if (!fp) {
     615           0 :     error("can't open '%1': %2", common_words_file, strerror(errno));
     616           0 :     return;
     617             :   }
     618           0 :   common_words_table_size = ceil_prime(2 * header.common);
     619           0 :   common_words_table = new char *[common_words_table_size];
     620           0 :   for (int i = 0; i < common_words_table_size; i++)
     621           0 :     common_words_table[i] = 0;
     622           0 :   int count = 0;
     623           0 :   int key_len = 0;
     624             :   for (;;) {
     625           0 :     int c = getc(fp);
     626           0 :     while (c != EOF && !csalnum(c))
     627           0 :       c = getc(fp);
     628           0 :     if (c == EOF)
     629           0 :       break;
     630           0 :     do {
     631           0 :       if (key_len < header.truncate)
     632           0 :         key_buffer[key_len++] = cmlower(c);
     633           0 :       c = getc(fp);
     634           0 :     } while (c != EOF && csalnum(c));
     635           0 :     if (key_len >= header.shortest) {
     636           0 :       int h = hash(key_buffer, key_len) % common_words_table_size;
     637           0 :       while (common_words_table[h]) {
     638           0 :         if (h == 0)
     639           0 :           h = common_words_table_size;
     640           0 :         --h;
     641             :       }
     642           0 :       common_words_table[h] = new char[key_len + 1];
     643           0 :       memcpy(common_words_table[h], key_buffer, key_len);
     644           0 :       common_words_table[h][key_len] = '\0';
     645             :     }
     646           0 :     if (++count >= header.common)
     647           0 :       break;
     648           0 :     key_len = 0;
     649           0 :     if (c == EOF)
     650           0 :       break;
     651           0 :   }
     652           0 :   fclose(fp);
     653             : }
     654             : 
     655           0 : void index_search_item::add_out_of_date_file(int fd, const char *filename,
     656             :                                              int fid)
     657             : {
     658             :   search_item **pp;
     659           0 :   for (pp = &out_of_date_files; *pp; pp = &(*pp)->next)
     660           0 :     if ((*pp)->is_named(filename))
     661           0 :       return;
     662           0 :   *pp = make_linear_search_item(fd, filename, fid);
     663           0 :   warning("'%1' modified since index '%2' created", filename, name);
     664             : }
     665             : 
     666           0 : void index_search_item::check_files()
     667             : {
     668           0 :   const char *pool_end = pool + header.strings_size;
     669           0 :   for (const char *ptr = strchr(ignore_fields, '\0') + 1;
     670           0 :        ptr < pool_end;
     671           0 :        ptr = strchr(ptr, '\0') + 1) {
     672           0 :     const char *path = munge_filename(ptr);
     673             :     struct stat sb;
     674           0 :     if (stat(path, &sb) < 0)
     675           0 :       error("can't stat '%1': %2", path, strerror(errno));
     676           0 :     else if (sb.st_mtime > mtime) {
     677           0 :       int fd = open(path, O_RDONLY | O_BINARY);
     678           0 :       if (fd < 0)
     679           0 :         error("can't open '%1': %2", path, strerror(errno));
     680             :       else
     681           0 :         add_out_of_date_file(fd, path, filename_id + (ptr - pool));
     682             :     }
     683             :   }
     684           0 : }
     685             : 
     686             : // Local Variables:
     687             : // fill-column: 72
     688             : // mode: C++
     689             : // End:
     690             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14