home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / groff-1.09-src.lha / src / amiga / groff-1.09 / indxbib / indxbib.cc < prev    next >
C/C++ Source or Header  |  1994-02-21  |  18KB  |  744 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include </gnu/include/string.h>
  24. #include <assert.h>
  25. #include <errno.h>
  26.  
  27. #include "posix.h"
  28. #include "lib.h"
  29. #include "errarg.h"
  30. #include "error.h"
  31. #include "stringclass.h"
  32. #include "cset.h"
  33. #include "cmap.h"
  34.  
  35. #include "defs.h"
  36. #include "index.h"
  37.  
  38. extern "C" {
  39.   // Sun's stdlib.h fails to declare this.
  40.   char *mktemp(char *);
  41. }
  42.  
  43. #define DEFAULT_HASH_TABLE_SIZE 997
  44. #define TEMP_INDEX_TEMPLATE "indxbibXXXXXX"
  45.  
  46. // (2^n - MALLOC_OVERHEAD) should be a good argument for malloc().
  47.  
  48. #define MALLOC_OVERHEAD 16
  49.  
  50. #ifdef BLOCK_SIZE
  51. #undef BLOCK_SIZE
  52. #endif
  53.  
  54. const int BLOCK_SIZE = ((1024 - MALLOC_OVERHEAD - sizeof(struct block *)
  55.              - sizeof(int)) / sizeof(int));
  56. struct block {
  57.   block *next;
  58.   int used;
  59.   int v[BLOCK_SIZE];
  60.   
  61.   block(block *p = 0) : next(p), used(0) { }
  62. };
  63.  
  64. struct block;
  65.  
  66. union table_entry {
  67.   block *ptr;
  68.   int count;
  69. };
  70.  
  71. struct word_list {
  72.   word_list *next;
  73.   char *str;
  74.   int len;
  75.   word_list(const char *, int, word_list *);
  76. };
  77.  
  78. table_entry *hash_table;
  79. int hash_table_size = DEFAULT_HASH_TABLE_SIZE;
  80. // We make this the same size as hash_table so we only have to do one
  81. // mod per key.
  82. static word_list **common_words_table = 0;
  83. char *key_buffer;
  84.  
  85. FILE *indxfp;
  86. int ntags = 0;
  87. string filenames;
  88. char *temp_index_file = 0;
  89.  
  90. const char *ignore_fields = "XYZ";
  91. const char *common_words_file = COMMON_WORDS_FILE;
  92. int n_ignore_words = 100;
  93. int truncate_len = 6;
  94. int shortest_len = 3;
  95. int max_keys_per_item = 100;
  96.  
  97. static void usage();
  98. static void write_hash_table();
  99. static void init_hash_table();
  100. static void read_common_words_file();
  101. static int store_key(char *s, int len);
  102. static void possibly_store_key(char *s, int len);
  103. static int do_whole_file(const char *filename);
  104. static int do_file(const char *filename);
  105. static void store_reference(int filename_index, int pos, int len);
  106. static void check_integer_arg(char opt, const char *arg, int min, int *res);
  107. static void store_filename(const char *);
  108. static void fwrite_or_die(const void *ptr, int size, int nitems, FILE *fp);
  109. static char *get_cwd();
  110.  
  111. extern "C" {
  112.   void cleanup();
  113.   long dir_name_max(const char *);
  114.   void catch_fatal_signals();
  115.   void ignore_fatal_signals();
  116. }
  117.  
  118. int main(int argc, char **argv)
  119. {
  120.   program_name = argv[0];
  121.   static char stderr_buf[BUFSIZ];
  122.   setbuf(stderr, stderr_buf);
  123.   
  124.   const char *basename = 0;
  125.   typedef int (*parser_t)(const char *);
  126.   parser_t parser = do_file;
  127.   const char *directory = 0;
  128.   const char *foption = 0;
  129.   int opt;
  130.   while ((opt = getopt(argc, argv, "c:o:h:i:k:l:t:n:c:d:f:vw")) != EOF)
  131.     switch (opt) {
  132.     case 'c':
  133.       common_words_file = optarg;
  134.       break;
  135.     case 'd':
  136.       directory = optarg;
  137.       break;
  138.     case 'f':
  139.       foption = optarg;
  140.       break;
  141.     case 'h':
  142.       check_integer_arg('h', optarg, 1, &hash_table_size);
  143.       if (!is_prime(hash_table_size)) {
  144.     while (!is_prime(++hash_table_size))
  145.       ;
  146.     warning("%1 not prime: using %2 instead", optarg, hash_table_size);
  147.       }
  148.       break;
  149.     case 'i':
  150.       ignore_fields = optarg;
  151.       break;
  152.     case 'k':
  153.       check_integer_arg('k', optarg, 1, &max_keys_per_item);
  154.       break;
  155.     case 'l':
  156.       check_integer_arg('l', optarg, 0, &shortest_len);
  157.       break;
  158.     case 'n':
  159.       check_integer_arg('n', optarg, 0, &n_ignore_words);
  160.       break;
  161.     case 'o':
  162.       basename = optarg;
  163.       break;
  164.     case 't':
  165.       check_integer_arg('t', optarg, 1, &truncate_len);
  166.       break;
  167.     case 'w':
  168.       parser = do_whole_file;
  169.       break;
  170.     case 'v':
  171.       {
  172.     extern const char *version_string;
  173.     fprintf(stderr, "GNU indxbib version %s\n", version_string);
  174.     fflush(stderr);
  175.     break;
  176.       }
  177.     case '?':
  178.       usage();
  179.       break;
  180.     default:
  181.       assert(0);
  182.       break;
  183.     }
  184.   if (optind >= argc && foption == 0)
  185.     fatal("no files and no -f option");
  186.   if (!directory) {
  187.     char *path = get_cwd();
  188.     store_filename(path);
  189.     a_delete path;
  190.   }
  191.   else
  192.     store_filename(directory);
  193.   init_hash_table();
  194.   store_filename(common_words_file);
  195.   store_filename(ignore_fields);
  196.   key_buffer = new char[truncate_len];
  197.   read_common_words_file();
  198.   if (!basename)
  199.     basename = optind < argc ? argv[optind] : DEFAULT_INDEX_NAME;
  200.   const char *p = strrchr(basename, '/');
  201.   long name_max;
  202.   if (p) {
  203.     char *dir = strsave(basename);
  204.     dir[p - basename] = '\0';
  205.     name_max = dir_name_max(dir);
  206.     a_delete dir;
  207.   }
  208.   else
  209.     name_max = dir_name_max(".");
  210.   const char *filename = p ? p + 1 : basename;
  211.   if (name_max >= 0 && strlen(filename) + sizeof(INDEX_SUFFIX) - 1 > name_max)
  212.     fatal("`%1.%2' is too long for a filename", filename, INDEX_SUFFIX);
  213.   if (p) {
  214.     p++;
  215.     temp_index_file = new char[p - basename + sizeof(TEMP_INDEX_TEMPLATE)];
  216.     memcpy(temp_index_file, basename, p - basename);
  217.     strcpy(temp_index_file + (p - basename), TEMP_INDEX_TEMPLATE);
  218.   }
  219.   else {
  220.     temp_index_file = strsave(TEMP_INDEX_TEMPLATE);
  221.   }
  222.   if (!mktemp(temp_index_file) || !temp_index_file[0])
  223.     fatal("cannot create file name for temporary file");
  224.   catch_fatal_signals();
  225.   int fd = creat(temp_index_file, S_IRUSR|S_IRGRP|S_IROTH);
  226.   if (fd < 0)
  227.     fatal("can't create temporary index file: %1", strerror(errno));
  228.   indxfp = fdopen(fd, "w");
  229.   if (indxfp == 0)
  230.     fatal("fdopen failed");
  231.   if (fseek(indxfp, sizeof(index_header), 0) < 0)
  232.     fatal("can't seek past index header: %1", strerror(errno));
  233.   int failed = 0;
  234.   if (foption) {
  235.     FILE *fp = stdin;
  236.     if (strcmp(foption, "-") != 0) {
  237.       errno = 0;
  238.       fp = fopen(foption, "r");
  239.       if (!fp)
  240.     fatal("can't open `%1': %2", foption, strerror(errno));
  241.     }
  242.     string path;
  243.     int lineno = 1;
  244.     for (;;) {
  245.       for (int c = getc(fp); c != '\n' && c != EOF; c = getc(fp)) {
  246.     if (c == '\0')
  247.       error_with_file_and_line(foption, lineno,
  248.                    "nul character in pathname ignored");
  249.     else
  250.       path += c;
  251.       }
  252.       if (path.length() > 0) {
  253.     path += '\0';
  254.     if (!(*parser)(path.contents()))
  255.       failed = 1;
  256.     path.clear();
  257.       }
  258.       if (c == EOF)
  259.     break;
  260.       lineno++;
  261.     }
  262.     if (fp != stdin)
  263.       fclose(fp);
  264.   }
  265.   for (int i = optind; i < argc; i++)
  266.     if (!(*parser)(argv[i]))
  267.       failed = 1;
  268.   write_hash_table();
  269.   if (fclose(indxfp) < 0)
  270.     fatal("error closing temporary index file: %1", strerror(errno));
  271.   char *index_file = new char[strlen(basename) + sizeof(INDEX_SUFFIX)];    
  272.   strcpy(index_file, basename);
  273.   strcat(index_file, INDEX_SUFFIX);
  274. #ifdef HAVE_RENAME
  275.   if (rename(temp_index_file, index_file) < 0)
  276.     fatal("can't rename temporary index file: %1", strerror(errno));
  277. #else /* not HAVE_RENAME */
  278.   ignore_fatal_signals();
  279.   if (unlink(index_file) < 0) {
  280.     if (errno != ENOENT)
  281.       fatal("can't unlink `%1': %2", index_file, strerror(errno));
  282.   }
  283.   if (link(temp_index_file, index_file) < 0)
  284.     fatal("can't link temporary index file: %1", strerror(errno));
  285.   if (unlink(temp_index_file) < 0)
  286.     fatal("can't unlink temporary index file: %1", strerror(errno));
  287. #endif /* not HAVE_RENAME */
  288.   temp_index_file = 0;
  289.   return failed;
  290. }
  291.  
  292. static void usage()
  293. {
  294.   fprintf(stderr,
  295. "usage: %s [-vw] [-c file] [-d dir] [-f file] [-h n] [-i XYZ] [-k n]\n"
  296. "       [-l n] [-n n] [-o base] [-t n] [files...]\n",
  297.       program_name);
  298.   exit(1);
  299. }
  300.  
  301. static void check_integer_arg(char opt, const char *arg, int min, int *res)
  302. {
  303.   char *ptr;
  304.   long n = strtol(arg, &ptr, 10);
  305.   if (n == 0 && ptr == arg)
  306.     error("argument to -%1 not an integer", opt);
  307.   else if (n < min)
  308.     error("argument to -%1 must not be less than %2", opt, min);
  309.   else {
  310.     if (n > INT_MAX)
  311.       error("argument to -%1 greater than maximum integer", opt);
  312.     else if (*ptr != '\0')
  313.       error("junk after integer argument to -%1", opt);
  314.     *res = int(n);
  315.   }
  316. }
  317.  
  318. static char *get_cwd()
  319. {
  320.   char *buf;
  321.   int size = 12;
  322.  
  323.   for (;;) {
  324.     buf = new char[size];
  325.     if (getcwd(buf, size))
  326.       break;
  327.     if (errno != ERANGE)
  328.       fatal("cannot get current working directory: %1", strerror(errno));
  329.     a_delete buf;
  330.     if (size == INT_MAX)
  331.       fatal("current working directory longer than INT_MAX");
  332.     if (size > INT_MAX/2)
  333.       size = INT_MAX;
  334.     else
  335.       size *= 2;
  336.   }
  337.   return buf;
  338. }
  339.  
  340. word_list::word_list(const char *s, int n, word_list *p)
  341. : next(p), len(n)
  342. {
  343.   str = new char[n];
  344.   memcpy(str, s, n);
  345. }
  346.  
  347. static void read_common_words_file()
  348. {
  349.   if (n_ignore_words <= 0)
  350.     return;
  351.   errno = 0;
  352.   FILE *fp = fopen(common_words_file, "r");
  353.   if (!fp)
  354.     fatal("can't open `%1': %2", common_words_file, strerror(errno));
  355.   common_words_table = new word_list * [hash_table_size];
  356.   for (int i = 0; i < hash_table_size; i++)
  357.     common_words_table[i] = 0;
  358.   int count = 0;
  359.   int key_len = 0;
  360.   for (;;) {
  361.     int c = getc(fp);
  362.     while (c != EOF && !csalnum(c))
  363.       c = getc(fp);
  364.     if (c == EOF)
  365.       break;
  366.     do {
  367.       if (key_len < truncate_len)
  368.     key_buffer[key_len++] = cmlower(c);
  369.       c = getc(fp);
  370.     } while (c != EOF && csalnum(c));
  371.     if (key_len >= shortest_len) {
  372.       int h = hash(key_buffer, key_len) % hash_table_size;
  373.       common_words_table[h] = new word_list(key_buffer, key_len,
  374.                         common_words_table[h]);
  375.     }
  376.     if (++count >= n_ignore_words)
  377.       break;
  378.     key_len = 0;
  379.     if (c == EOF)
  380.       break;
  381.   }
  382.   n_ignore_words = count;
  383.   fclose(fp);
  384. }
  385.  
  386. static int do_whole_file(const char *filename)
  387. {
  388.   errno = 0;
  389.   FILE *fp = fopen(filename, "r");
  390.   if (!fp) {
  391.     error("can't open `%1': %2", filename, strerror(errno));
  392.     return 0;
  393.   }
  394.   int count = 0;
  395.   int key_len = 0;
  396.   int c;
  397.   while ((c = getc(fp)) != EOF) {
  398.     if (csalnum(c)) {
  399.       key_len = 1;
  400.       key_buffer[0] = c;
  401.       while ((c = getc(fp)) != EOF) {
  402.     if (!csalnum(c))
  403.       break;
  404.     if (key_len < truncate_len)
  405.       key_buffer[key_len++] = c;
  406.       }
  407.       if (store_key(key_buffer, key_len)) {
  408.     if (++count >= max_keys_per_item)
  409.       break;
  410.       }
  411.       if (c == EOF)
  412.     break;
  413.     }
  414.   }
  415.   store_reference(filenames.length(), 0, 0);
  416.   store_filename(filename);
  417.   fclose(fp);
  418.   return 1;
  419. }
  420.  
  421. static int do_file(const char *filename)
  422. {
  423.   errno = 0;
  424.   FILE *fp = fopen(filename, "r");
  425.   if (fp == 0) {
  426.     error("can't open `%1': %2", filename, strerror(errno));
  427.     return 0;
  428.   }
  429.   int filename_index = filenames.length();
  430.   store_filename(filename);
  431.  
  432.   enum {
  433.     START,    // at the start of the file; also in between references
  434.     BOL,    // in the middle of a reference, at the beginning of the line
  435.     PERCENT,    // seen a percent at the beginning of the line
  436.     IGNORE,    // ignoring a field
  437.     IGNORE_BOL,    // at the beginning of a line ignoring a field
  438.     KEY,    // in the middle of a key
  439.     DISCARD,    // after truncate_len bytes of a key
  440.     MIDDLE    // in between keys
  441.   } state = START;
  442.   
  443.   // In states START, BOL, IGNORE_BOL, space_count how many spaces at
  444.   // the beginning have been seen.  In states PERCENT, IGNORE, KEY,
  445.   // MIDDLE space_count must be 0.
  446.   int space_count = 0;
  447.   int byte_count = 0;        // bytes read
  448.   int key_len = 0;
  449.   int ref_start = -1;        // position of start of current reference
  450.   for (;;) {
  451.     int c = getc(fp);
  452.     if (c == EOF)
  453.       break;
  454.     byte_count++;
  455.     switch (state) {
  456.     case START:
  457.       if (c == ' ' || c == '\t') {
  458.     space_count++;
  459.     break;
  460.       }
  461.       if (c == '\n') {
  462.     space_count = 0;
  463.     break;
  464.       }
  465.       ref_start = byte_count - space_count - 1;
  466.       space_count = 0;
  467.       if (c == '%')
  468.     state = PERCENT;
  469.       else if (csalnum(c)) {
  470.     state = KEY;
  471.     key_buffer[0] = c;
  472.     key_len = 1;
  473.       }
  474.       else
  475.     state = MIDDLE;
  476.       break;
  477.     case BOL:
  478.       switch (c) {
  479.       case '%':
  480.     if (space_count > 0) {
  481.       space_count = 0;
  482.       state = MIDDLE;
  483.     }
  484.     else
  485.       state = PERCENT;
  486.     break;
  487.       case ' ':
  488.       case '\t':
  489.     space_count++;
  490.     break;
  491.       case '\n':
  492.     store_reference(filename_index, ref_start,
  493.             byte_count - 1 - space_count - ref_start);
  494.     state = START;
  495.     space_count = 0;
  496.     break;
  497.       default:
  498.     space_count = 0;
  499.     if (csalnum(c)) {
  500.       state = KEY;
  501.       key_buffer[0] = c;
  502.       key_len = 1;
  503.     }
  504.     else
  505.       state = MIDDLE;
  506.       }
  507.       break;
  508.     case PERCENT:
  509.       if (strchr(ignore_fields, c) != 0)
  510.     state = IGNORE;
  511.       else if (c == '\n')
  512.     state = BOL;
  513.       else
  514.     state = MIDDLE;
  515.       break;
  516.     case IGNORE:
  517.       if (c == '\n')
  518.     state = IGNORE_BOL;
  519.       break;
  520.     case IGNORE_BOL:
  521.       switch (c) {
  522.       case '%':
  523.     if (space_count > 0) {
  524.       state = IGNORE;
  525.       space_count = 0;
  526.     }
  527.     else
  528.       state = PERCENT;
  529.     break;
  530.       case ' ':
  531.       case '\t':
  532.     space_count++;
  533.     break;
  534.       case '\n':
  535.     store_reference(filename_index, ref_start,
  536.             byte_count - 1 - space_count - ref_start);
  537.     state = START;
  538.     space_count = 0;
  539.     break;
  540.       default:
  541.     space_count = 0;
  542.     state = IGNORE;
  543.       }
  544.       break;
  545.     case KEY:
  546.       if (csalnum(c)) {
  547.     if (key_len < truncate_len)
  548.       key_buffer[key_len++] = c;
  549.     else
  550.       state = DISCARD;
  551.       }
  552.       else {
  553.     possibly_store_key(key_buffer, key_len);
  554.     key_len = 0;
  555.     if (c == '\n')
  556.       state = BOL;
  557.     else
  558.       state = MIDDLE;
  559.       }
  560.       break;
  561.     case DISCARD:
  562.       if (!csalnum(c)) {
  563.     possibly_store_key(key_buffer, key_len);
  564.     key_len = 0;
  565.     if (c == '\n')
  566.       state = BOL;
  567.     else
  568.       state = MIDDLE;
  569.       }
  570.       break;
  571.     case MIDDLE:
  572.       if (csalnum(c)) {
  573.     state = KEY;
  574.     key_buffer[0] = c;
  575.     key_len = 1;
  576.       }
  577.       else if (c == '\n')
  578.     state = BOL;
  579.       break;
  580.     default:
  581.       assert(0);
  582.     }
  583.   }
  584.   switch (state) {
  585.   case START:
  586.     break;
  587.   case DISCARD:
  588.   case KEY:
  589.     possibly_store_key(key_buffer, key_len);
  590.     // fall through
  591.   case BOL:
  592.   case PERCENT:
  593.   case IGNORE_BOL:
  594.   case IGNORE:
  595.   case MIDDLE:
  596.     store_reference(filename_index, ref_start,
  597.             byte_count - ref_start - space_count);
  598.     break;
  599.   default:
  600.     assert(0);
  601.   }
  602.   fclose(fp);
  603.   return 1;
  604. }
  605.  
  606. static void store_reference(int filename_index, int pos, int len)
  607. {
  608.   tag t;
  609.   t.filename_index = filename_index;
  610.   t.start = pos;
  611.   t.length = len;
  612.   fwrite_or_die(&t, sizeof(t), 1, indxfp);
  613.   ntags++;
  614. }
  615.  
  616. static void store_filename(const char *fn)
  617. {
  618.   filenames += fn;
  619.   filenames += '\0';
  620. }
  621.  
  622. static void init_hash_table()
  623. {
  624.   hash_table = new table_entry[hash_table_size];
  625.   for (int i = 0; i < hash_table_size; i++)
  626.     hash_table[i].ptr = 0;
  627. }
  628.  
  629. static void possibly_store_key(char *s, int len)
  630. {
  631.   static int last_tagno = -1;
  632.   static int key_count;
  633.   if (last_tagno != ntags) {
  634.     last_tagno = ntags;
  635.     key_count = 0;
  636.   }
  637.   if (key_count < max_keys_per_item) {
  638.     if (store_key(s, len))
  639.       key_count++;
  640.   }
  641. }
  642.  
  643. static int store_key(char *s, int len)
  644. {
  645.   if (len < shortest_len)
  646.     return 0;
  647.   int is_number = 1;
  648.   for (int i = 0; i < len; i++)
  649.     if (!csdigit(s[i])) {
  650.       is_number = 0;
  651.       s[i] = cmlower(s[i]);
  652.     }
  653.   if (is_number && !(len == 4 && s[0] == '1' && s[1] == '9'))
  654.     return 0;
  655.   int h = hash(s, len) % hash_table_size;
  656.   if (common_words_table) {
  657.     for (word_list *ptr = common_words_table[h]; ptr; ptr = ptr->next)
  658.       if (len == ptr->len && memcmp(s, ptr->str, len) == 0)
  659.     return 0;
  660.   }
  661.   table_entry *pp =  hash_table + h;
  662.   if (!pp->ptr)
  663.     pp->ptr = new block;
  664.   else if (pp->ptr->v[pp->ptr->used - 1] == ntags)
  665.     return 1;
  666.   else if (pp->ptr->used >= BLOCK_SIZE)
  667.     pp->ptr = new block(pp->ptr);
  668.   pp->ptr->v[(pp->ptr->used)++] = ntags;
  669.   return 1;
  670. }
  671.  
  672. static void write_hash_table()
  673. {
  674.   const int minus_one = -1;
  675.   int li = 0;
  676.   for (int i = 0; i < hash_table_size; i++) {
  677.     block *ptr = hash_table[i].ptr;
  678.     if (!ptr)
  679.       hash_table[i].count = -1;
  680.     else {
  681.       hash_table[i].count = li;
  682.       block *rev = 0;
  683.       while (ptr) {
  684.     block *tem = ptr;
  685.     ptr = ptr->next;
  686.     tem->next = rev;
  687.     rev = tem;
  688.       }
  689.       while (rev) {
  690.     fwrite_or_die(rev->v, sizeof(int), rev->used, indxfp);
  691.     li += rev->used;
  692.     block *tem = rev;
  693.     rev = rev->next;
  694.     delete tem;
  695.       }
  696.       fwrite_or_die(&minus_one, sizeof(int), 1, indxfp);
  697.       li += 1;
  698.     }
  699.   }
  700.   if (sizeof(table_entry) == sizeof(int))
  701.     fwrite_or_die(hash_table, sizeof(int), hash_table_size, indxfp);
  702.   else {
  703.     // write it out word by word
  704.     for (int i = 0; i < hash_table_size; i++)
  705.       fwrite_or_die(&hash_table[i].count, sizeof(int), 1, indxfp);
  706.   }
  707.   fwrite_or_die(filenames.contents(), 1, filenames.length(), indxfp);
  708.   if (fseek(indxfp, 0, 0) < 0)
  709.     fatal("error seeking on index file: %1", strerror(errno));
  710.   index_header h;
  711.   h.magic = INDEX_MAGIC;
  712.   h.version = INDEX_VERSION;
  713.   h.tags_size = ntags;
  714.   h.lists_size = li;
  715.   h.table_size = hash_table_size;
  716.   h.strings_size = filenames.length();
  717.   h.truncate = truncate_len;
  718.   h.shortest = shortest_len;
  719.   h.common = n_ignore_words;
  720.   fwrite_or_die(&h, sizeof(h), 1, indxfp);
  721. }
  722.  
  723. static void fwrite_or_die(const void *ptr, int size, int nitems, FILE *fp)
  724. {
  725.   if (fwrite(ptr, size, nitems, fp) != nitems)
  726.     fatal("fwrite failed: %1", strerror(errno));
  727. }
  728.  
  729. void fatal_error_exit()
  730. {
  731.   cleanup();
  732.   exit(3);
  733. }
  734.  
  735. extern "C" {
  736.  
  737. void cleanup()
  738. {
  739.   if (temp_index_file)
  740.     unlink(temp_index_file);
  741. }
  742.  
  743. }
  744.