home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / id-utils-3.2-src.tgz / tar.out / fsf / id-utils / src / lid.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  37KB  |  1,625 lines

  1. /* lid.c -- primary query interface for mkid database
  2.    Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
  3.    Written by Greg McGary <gkm@gnu.ai.mit.edu>
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  18.  
  19. #include <config.h>
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. #include "xstdlib.h"
  23. #include <signal.h>
  24. #include <errno.h>
  25. #include <sys/types.h>
  26. #include <sys/wait.h>
  27. #include <assert.h>
  28. #include <getopt.h>
  29. #include "xstring.h"
  30. #include "xunistd.h"
  31. #include "xnls.h"
  32. #include "xmalloc.h"
  33. #include "idfile.h"
  34. #include "xstring.h"
  35. #include "error.h"
  36. #include "pathmax.h"
  37. #include "xalloca.h"
  38. #if HAVE_LIMITS_H
  39. # include <limits.h>
  40. #endif
  41. #if WITH_REGEX
  42. # include <regex.h>
  43. #else
  44. # include <rx.h>
  45. #endif
  46.  
  47. typedef void (*report_func_t) __P((char const *name, struct file_link **flinkv));
  48. typedef int (*query_func_t) __P((char const *arg, report_func_t));
  49.  
  50. enum delimiter_style
  51. {
  52.   ds_bogus,
  53.   ds_contextual,
  54.   ds_word,
  55.   ds_substring
  56. };
  57.  
  58. enum pattern_style
  59. {
  60.   ps_bogus,
  61.   ps_contextual,
  62.   ps_literal,
  63.   ps_regexp
  64. };
  65.  
  66. enum key_style
  67. {
  68.   ks_bogus,
  69.   ks_none,
  70.   ks_token,
  71.   ks_pattern
  72. };
  73.  
  74. enum result_style
  75. {
  76.   rs_bogus,
  77.   rs_none,
  78.   rs_filenames,
  79.   rs_grep,
  80.   rs_edit
  81. };
  82.  
  83. enum radix
  84. {
  85.   radix_oct = 1,
  86.   radix_dec = 2,
  87.   radix_hex = 4,
  88.   radix_all = radix_dec | radix_oct | radix_hex
  89. };
  90.  
  91. void usage __P((void));
  92. static void help_me __P((void));
  93. void lower_caseify __P((char *str));
  94. enum key_style parse_key_style __P((char const *arg));
  95. enum result_style parse_result_style __P((char const *arg));
  96. query_func_t get_query_func __P((char *pattern));
  97. report_func_t get_report_func __P((void));
  98. void report_filenames __P((char const *name, struct file_link **flinkv));
  99. void report_grep __P((char const *name, struct file_link **flinkv));
  100. void report_edit __P((char const *name, struct file_link **flinkv));
  101. void report_nothing __P((char const *name, struct file_link **flinkv));
  102. int vector_cardinality __P((void *vector));
  103. int search_flinkv __P((struct file_link **flinkv));
  104. int query_literal_word __P((char const *pattern, report_func_t report_func));
  105. int query_literal_prefix __P((char const *pattern, report_func_t report_func));
  106. int query_regexp __P((char const *pattern_0, report_func_t report_func));
  107. char const *maybe_add_word_delimiters __P((char const *pattern_0));
  108. int query_number __P((char const *pattern, report_func_t report_func));
  109. int query_ambiguous_prefix __P((unsigned int, report_func_t report_func));
  110. int query_literal_substring __P((char const *pattern, report_func_t report_func));
  111. void parse_frequency_arg __P((char const *arg));
  112. int desired_frequency __P((char const *tok));
  113. char *strcasestr __P((char const *s1, char const *s2));
  114. char const *file_regexp __P((char const *name_0, char const *left_delimit, char const *right_delimit));
  115. off_t query_binary_search __P((char const *token));
  116. int is_regexp __P((char *name));
  117. int has_left_delimiter __P((char const *pattern));
  118. int has_right_delimiter __P((char const *pattern));
  119. int file_name_wildcard __P((char const *re, char const *fn));
  120. int word_match __P((char const *name_0, char const *line));
  121. int get_radix __P((char const *str));
  122. int is_number __P((char const *str));
  123. int stoi __P((char const *str));
  124. int otoi __P((char const *str));
  125. int dtoi __P((char const *str));
  126. int xtoi __P((char const *str));
  127. unsigned char *tree8_to_bits __P((unsigned char *bits_vec, unsigned char const *hits_tree8));
  128. void tree8_to_bits_1 __P((unsigned char **bits_vec, unsigned char const **hits_tree8, int level));
  129. struct file_link **tree8_to_flinkv __P((unsigned char const *hits_tree8));
  130. struct file_link **bits_to_flinkv __P((unsigned char const *bits_vec));
  131.  
  132. #if HAVE_TERMIOS_H || HAVE_TERMIO_H || HAVE_SGTTY_H
  133. void savetty __P((void));
  134. void restoretty __P((void));
  135. void linetty __P((void));
  136. void chartty __P((void));
  137. #endif
  138.  
  139. #define    TOLOWER(c)    (isupper (c) ? tolower (c) : (c))
  140. #define IS_ALNUM(c)    (isalnum (c) || (c) == '_')
  141.  
  142. /* Sorry about all the globals, but it's really cleaner this way. */
  143.  
  144. char anchor_dir[BUFSIZ];
  145. int tree8_levels;
  146. unsigned int bits_vec_size;
  147. struct idhead idh;
  148. char *hits_buf_1;
  149. char *hits_buf_2;
  150. unsigned char *bits_vec;
  151.  
  152. /* The name this program was run with. */
  153.  
  154. char const *program_name;
  155.  
  156. /* If nonzero, display usage information and exit.  */
  157.  
  158. static int show_help;
  159.  
  160. /* If nonzero, print the version on standard output then exit.  */
  161.  
  162. static int show_version;
  163.  
  164. /* Which radixes do we want? */
  165.  
  166. int radix_flag = radix_all;
  167.  
  168. /* If nonzero, ignore differences in alphabetic case while matching.  */
  169.  
  170. int ignore_case_flag = 0;
  171.  
  172. /* How will patterns will be delimited? */
  173.  
  174. enum delimiter_style delimiter_style = ds_contextual;
  175.  
  176. /* How will patterns be interpreted? */
  177.  
  178. enum pattern_style pattern_style = ps_contextual;
  179.  
  180. /* How will keys be presented? */
  181.  
  182. enum key_style key_style = ks_token;
  183.  
  184. /* How will query results be presented? */
  185.  
  186. enum result_style result_style = rs_filenames;
  187.  
  188. /* How shall we separate file names when result_style == rs_filenames?  */
  189.  
  190. enum separator_style separator_style = ss_contextual;
  191.  
  192. /* If non-zero, list identifiers that are are non-unique within this
  193.    number of leading characters.  */
  194.  
  195. unsigned int ambiguous_prefix_length = 0;
  196.  
  197. /* The style of report.  */
  198.  
  199. report_func_t report_function;
  200.  
  201. /* The style of query.  */
  202.  
  203. query_func_t query_function;
  204.  
  205. /* Lower and upper bounds on occurrence frequency.  */
  206.  
  207. unsigned int frequency_low = 1;
  208. unsigned int frequency_high = USHRT_MAX;
  209.  
  210. struct file_link *cw_dlink;
  211. struct file_link **members_0;
  212.  
  213. static struct option const long_options[] =
  214. {
  215.   { "file", required_argument, 0, 'f' },
  216.   { "frequency", required_argument, 0, 'F' },
  217.   { "ambiguous", required_argument, 0, 'a' },
  218.   { "key", required_argument, 0, 'k' },
  219.   { "result", required_argument, 0, 'R' },
  220.   { "separator", required_argument, 0, 'S' },
  221.   { "ignore-case", no_argument, 0, 'i' },
  222.   { "literal", no_argument, 0, 'l' },
  223.   { "regexp", no_argument, 0, 'r' },
  224.   { "word", no_argument, 0, 'w' },
  225.   { "substring", no_argument, 0, 's' },
  226.   { "hex", no_argument, 0, 'x' },
  227.   { "decimal", no_argument, 0, 'd' },
  228.   { "octal", no_argument, 0, 'o' },
  229.   { "help", no_argument, &show_help, 1 },
  230.   { "version", no_argument, &show_version, 1 },
  231.   { 0 }
  232. };
  233.  
  234. void
  235. usage (void)
  236. {
  237.   fprintf (stderr, _("Try `%s --help' for more information.\n"),
  238.        program_name);
  239.   exit (1);
  240. }
  241.  
  242. static void
  243. help_me (void)
  244. {
  245.   printf (_("\
  246. Usage: %s [OPTION]... PATTERN...\n\
  247. "), program_name);
  248.  
  249.   printf (_("\
  250. Query ID database and report results.\n\
  251. By default, output consists of multiple lines, each line containing the\n\
  252. matched identifier followed by the list of file names in which it occurs.\n\
  253. \n\
  254.   -f, --file=FILE       file name of ID database\n\
  255. \n\
  256.   -i, --ignore-case     match PATTERN case insensitively\n\
  257.   -l, --literal         match PATTERN as a literal string\n\
  258.   -r, --regexp          match PATTERN as a regular expression\n\
  259.   -w, --word            match PATTERN as a delimited word\n\
  260.   -s, --substring       match PATTERN as a substring\n\
  261.             Note: If PATTERN contains extended regular expression meta-\n\
  262.             characters, it is interpreted as a regular expression substring.\n\
  263.             Otherwise, PATTERN is interpreted as a literal word.\n\
  264. \n\
  265.   -k, --key=STYLE       STYLE is one of `token', `pattern' or `none'\n\
  266.   -R, --result=STYLE    STYLE is one of `filenames', `grep', `edit' or `none'\n\
  267.   -S, --separator=STYLE STYLE is one of `braces', `space' or `newline' and\n\
  268.                         only applies to file names when `--result=filenames'\n\
  269.             The above STYLE options control how query results are presented.\n\
  270.             Defaults are --key=token --result=filenames --separator=%s\n\
  271. \n\
  272.   -F, --frequency=FREQ  find tokens that occur FREQ times, where FREQ\n\
  273.                         is a range expressed as `N..M'.  If N is omitted, it\n\
  274.                         defaults to 1, if M is omitted it defaults to MAX_USHRT\n\
  275.   -a, --ambiguous=LEN   find tokens whose names are ambiguous for LEN chars\n\
  276. \n\
  277.   -x, --hex             only find numbers expressed as hexadecimal\n\
  278.   -d, --decimal         only find numbers expressed as decimal\n\
  279.   -o, --octal           only find numbers expressed as octal\n\
  280.             By default, searches match numbers of any radix.\n\
  281. \n\
  282.       --help            display this help and exit\n\
  283.       --version         output version information and exit\n\
  284. "),
  285.       (separator_style == ss_braces ? _("braces") : _("space")));
  286.   exit (0);
  287. }
  288.  
  289. int
  290. main (int argc, char **argv)
  291. {
  292.   program_name = argv[0];
  293.   idh.idh_file_name = 0;
  294.  
  295.   /* Set locale according to user's wishes.  */
  296.   setlocale (LC_ALL, "");
  297.  
  298.   /* Tell program which translations to use and where to find.  */
  299.   bindtextdomain (PACKAGE, LOCALEDIR);
  300.   textdomain (PACKAGE);
  301.  
  302.   for (;;)
  303.     {
  304.       int optc = getopt_long (argc, argv, "f:F:a:k:R:S:ilrwsxdo",
  305.                   long_options, (int *) 0);
  306.       if (optc < 0)
  307.     break;
  308.       switch (optc)
  309.     {
  310.     case 0:
  311.       break;
  312.  
  313.     case 'f':
  314.       idh.idh_file_name = optarg;
  315.       break;
  316.  
  317.     case 'F':
  318.       parse_frequency_arg (optarg);
  319.       break;
  320.  
  321.     case 'a':
  322.       ambiguous_prefix_length = stoi (optarg);
  323.       break;
  324.  
  325.     case 'k':
  326.       key_style = parse_key_style (optarg);
  327.       break;
  328.  
  329.     case 'R':
  330.       result_style = parse_result_style (optarg);
  331.       break;
  332.  
  333.     case 'S':
  334.       separator_style = parse_separator_style (optarg);
  335.       break;
  336.  
  337.     case 'i':
  338.       ignore_case_flag = REG_ICASE;
  339.       break;
  340.  
  341.     case 'l':
  342.       pattern_style = ps_literal;
  343.       break;
  344.  
  345.     case 'r':
  346.       pattern_style = ps_regexp;
  347.       break;
  348.  
  349.     case 'e':
  350.       pattern_style = ps_regexp;
  351.       error (0, 0, _("notice: use of `-e' is deprecated, use `-r' instead"));
  352.       break;
  353.  
  354.     case 'w':
  355.       delimiter_style = ds_word;
  356.       break;
  357.  
  358.     case 's':
  359.       delimiter_style = ds_substring;
  360.       break;
  361.  
  362.     case 'x':
  363.       radix_flag |= radix_hex;
  364.       break;
  365.  
  366.     case 'd':
  367.       radix_flag |= radix_dec;
  368.       break;
  369.  
  370.     case 'o':
  371.       radix_flag |= radix_oct;
  372.       break;
  373.  
  374.     default:
  375.       usage ();
  376.     }
  377.     }
  378.  
  379.   if (show_version)
  380.     {
  381.       printf ("%s - %s\n", program_name, PACKAGE_VERSION);
  382.       exit (0);
  383.     }
  384.  
  385.   if (show_help)
  386.     help_me ();
  387.  
  388.   if (separator_style == ss_contextual)
  389.     {
  390.       if (isatty (STDOUT_FILENO))
  391.     separator_style = DEFAULT_SEPARATOR_STYLE;
  392.       else if (key_style == ks_none)
  393.     separator_style = ss_newline;
  394.       else
  395.     separator_style = ss_space;
  396.     }
  397.  
  398.   argc -= optind;
  399.   argv += optind;
  400.   if (argc == 0)
  401.     {
  402.       static char *dot = (char *) ".";
  403.       argc = 1;
  404.       argv = ˙
  405.     }
  406.  
  407.   /* Look for the ID database up the tree */
  408.   idh.idh_file_name = locate_id_file_name (idh.idh_file_name);
  409.   if (idh.idh_file_name == 0)
  410.     error (1, errno, _("can't locate `ID'"));
  411.  
  412.   init_idh_obstacks (&idh);
  413.   init_idh_tables (&idh);
  414.  
  415.   cw_dlink = get_current_dir_link ();
  416.  
  417.   /* Determine absolute name of the directory name to which database
  418.      constituent files are relative. */
  419.   members_0 = read_id_file (idh.idh_file_name, &idh);
  420.   bits_vec_size = (idh.idh_files + 7) / 4; /* more than enough */
  421.   tree8_levels = tree8_count_levels (idh.idh_files);
  422.  
  423.   hits_buf_1 = MALLOC (char, idh.idh_buf_size);
  424.   hits_buf_2 = MALLOC (char, idh.idh_buf_size);
  425.   bits_vec = MALLOC (unsigned char, bits_vec_size);
  426.  
  427.   report_function = get_report_func ();
  428.   if (ambiguous_prefix_length)
  429.     {
  430.       if (!query_ambiguous_prefix (ambiguous_prefix_length, report_function))
  431.     fprintf (stderr, _("All identifiers are non-ambiguous within the first %d characters\n"),
  432.          ambiguous_prefix_length);
  433.     }
  434.   else
  435.     {
  436.       while (argc)
  437.     {
  438.       char *pattern = (argc--, *argv++);
  439.       if (ignore_case_flag)
  440.         lower_caseify (pattern);
  441.       query_function = get_query_func (pattern);
  442.       (*query_function) (pattern, report_function);
  443.     }
  444.     }
  445.  
  446.   fclose (idh.idh_FILE);
  447.   exit (0);
  448. }
  449.  
  450. void
  451. lower_caseify (char *str)
  452. {
  453.   while (*str)
  454.     {
  455.       *str = TOLOWER (*str);
  456.       str++;
  457.     }
  458. }
  459.  
  460. enum key_style
  461. parse_key_style (char const *arg)
  462. {
  463.   MAYBE_RETURN_PREFIX_MATCH (arg, "none", ks_none);
  464.   MAYBE_RETURN_PREFIX_MATCH (arg, "token", ks_token);
  465.   MAYBE_RETURN_PREFIX_MATCH (arg, "pattern", ks_pattern);
  466.   error (0, 0, _("invalid `--key' style: `%s'"), arg);
  467.   usage ();
  468.   return ks_bogus;
  469. }
  470.  
  471. enum result_style
  472. parse_result_style (char const *arg)
  473. {
  474.   MAYBE_RETURN_PREFIX_MATCH (arg, "none", rs_none);
  475.   MAYBE_RETURN_PREFIX_MATCH (arg, "filenames", rs_filenames);
  476.   MAYBE_RETURN_PREFIX_MATCH (arg, "grep", rs_grep);
  477.   MAYBE_RETURN_PREFIX_MATCH (arg, "edit", rs_edit);
  478.   error (0, 0, _("invalid `--result' style: `%s'"), arg);
  479.   usage ();
  480.   return rs_bogus;
  481. }
  482.  
  483. query_func_t
  484. get_query_func (char *pattern)
  485. {
  486.   switch (pattern_style)
  487.     {
  488.     case ps_regexp:
  489.       return query_regexp;
  490.  
  491.     case ps_literal:
  492.       if (delimiter_style == ds_substring)
  493.     return query_literal_substring;
  494.       else
  495.     return query_literal_word;
  496.  
  497.     default:
  498.       if (is_regexp (pattern))
  499.     return query_regexp;
  500.       else if (has_left_delimiter (pattern))
  501.     return query_literal_prefix;
  502.       else if (delimiter_style == ds_substring)
  503.     return query_literal_substring;
  504.       else if (is_number (pattern))
  505.     return query_number;
  506.       else if (delimiter_style == ds_word)
  507.     return query_literal_word;
  508.       else
  509.     return query_literal_word;
  510.     }
  511. }
  512.  
  513. report_func_t
  514. get_report_func (void)
  515. {
  516.   switch (result_style)
  517.     {
  518.     case rs_filenames: return report_filenames;
  519.     case rs_grep: return report_grep;
  520.     case rs_edit: return report_edit;
  521.     default: return report_nothing;
  522.     }
  523. }
  524.  
  525. void
  526. report_filenames (char const *name, struct file_link **flinkv)
  527. {
  528.   if (name && key_style != ks_none)
  529.     printf ("%-14s ", name);
  530.   print_filenames (flinkv, separator_style);
  531. }
  532.  
  533. void
  534. report_grep (char const *name, struct file_link **flinkv)
  535. {
  536.   char line[BUFSIZ];
  537.   char const *pattern = 0;
  538.   regex_t compiled;
  539.   int line_number;
  540.  
  541.   if (key_style == ks_pattern)
  542.     {
  543.       pattern = file_regexp (name, "[^a-zA-Z0-9_\300-\377]_*", "[^a-zA-Z0-9_\300-\377]");
  544.       if (pattern)
  545.     {
  546.       int regcomp_errno = regcomp (&compiled, pattern,
  547.                        ignore_case_flag | REG_EXTENDED);
  548.       if (regcomp_errno)
  549.         {
  550.           char buf[BUFSIZ];
  551.           regerror (regcomp_errno, &compiled, buf, sizeof (buf));
  552.           error (1, 0, "%s", buf);
  553.         }
  554.     }
  555.     }
  556.  
  557.   line[0] = ' ';        /* sentry */
  558.   while (*flinkv)
  559.     {
  560.       char *file_name = ALLOCA (char, PATH_MAX);
  561.       FILE *gid_FILE;
  562.  
  563.       maybe_relative_file_name (file_name, *flinkv++, cw_dlink);
  564.       gid_FILE = fopen (file_name, "r");
  565.       if (gid_FILE == 0)
  566.     error (0, errno, "can't open `%s'", file_name);
  567.  
  568.       line_number = 0;
  569.       while (fgets (&line[1], sizeof (line), gid_FILE))
  570.     {
  571.       line_number++;
  572.       if (pattern)
  573.         {
  574.           int regexec_errno = regexec (&compiled, line, 0, 0, 0);
  575.           if (regexec_errno == REG_ESPACE)
  576.         error (0, 0, "can't match regular-expression: memory exhausted");
  577.           else if (regexec_errno)
  578.         continue;
  579.         }
  580.       else if (!word_match (name, line))
  581.         continue;
  582.       printf ("%s:%d:%s", file_name, line_number, &line[1]);
  583.     }
  584.       fclose (gid_FILE);
  585.     }
  586. }
  587.  
  588. void
  589. report_edit (char const *name, struct file_link **flinkv)
  590. {
  591.   static char const *editor;
  592.   static char const *eid_arg;
  593.   static char const *eid_right_del;
  594.   static char const *eid_left_del;
  595.   char regexp_buf[BUFSIZ];
  596.   char ed_arg_buffer[BUFSIZ];
  597.   char const *pattern;
  598.   int c;
  599.   int skip;
  600.  
  601.   if (editor == 0)
  602.     {
  603.       editor = getenv ("VISUAL");
  604.       if (editor == 0)
  605.     {
  606.       editor = getenv ("EDITOR");
  607.       if (editor == 0)
  608.         editor = "vi";
  609.     }
  610.     }
  611.  
  612.   if (eid_arg == 0)
  613.     {
  614.       int using_vi = strequ ("vi", basename (editor));
  615.  
  616.       eid_arg = getenv ("EIDARG");
  617.       if (eid_arg == 0)
  618.     eid_arg = (using_vi ? "+1;/%s/" : "");
  619.  
  620.       eid_left_del = getenv ("EIDLDEL");
  621.       if (eid_left_del == 0)
  622.     eid_left_del = (using_vi ? "\\<" : "");
  623.  
  624.       eid_right_del = getenv ("EIDRDEL");
  625.       if (eid_right_del == 0)
  626.     eid_right_del = (using_vi ? "\\>" : "");
  627.     }
  628.  
  629.   report_filenames (name, flinkv);
  630.   savetty ();
  631.   for (;;)
  632.     {
  633.       /* FIXME: i18n of responses */
  634.       printf (_("edit? [y1-9^S/nq] "));
  635.       fflush (stdout);
  636.       chartty ();
  637.       c = (getchar () & 0177);
  638.       restoretty ();
  639.       switch (TOLOWER (c))
  640.     {
  641.     case '/': case ('s' & 037):
  642.       putchar ('/');
  643.       skip = search_flinkv (flinkv);
  644.       if (skip < 0)
  645.         continue;
  646.       flinkv += skip;
  647.       goto editit;
  648.  
  649.     case '0': case '1': case '2': case '3': case '4':
  650.     case '5': case '6': case '7': case '8': case '9':
  651.       putchar (c);
  652.       skip = c - '0';
  653.       break;
  654.  
  655.     case 'y':
  656.       putchar (c);
  657.       skip = 0;
  658.       break;
  659.  
  660.     case '\n':
  661.     case '\r':
  662.       putchar ('y');
  663.       skip = 0;
  664.       break;
  665.  
  666.     case 'q':
  667.       putchar (c);
  668.       putchar ('\n');
  669.       exit (0);
  670.  
  671.     case 'n':
  672.       putchar (c);
  673.       putchar ('\n');
  674.       return;
  675.  
  676.     default:
  677.       putchar (c);
  678.       putchar ('\n');
  679.       continue;
  680.     }
  681.  
  682.       putchar ('\n');
  683.       while (skip--)
  684.     if (*++flinkv == 0)
  685.       continue;
  686.       break;
  687.     }
  688. editit:
  689.  
  690.   if (key_style == ks_pattern)
  691.     pattern = file_regexp (name, eid_left_del, eid_right_del);
  692.   else
  693.     pattern = 0;
  694.   if (pattern == 0)
  695.     {
  696.       pattern = regexp_buf;
  697.       sprintf (regexp_buf, "%s%s%s", eid_left_del, name, eid_right_del);
  698.     }
  699.  
  700.   switch (fork ())
  701.     {
  702.     case -1:
  703.       error (1, errno, _("can't fork"));
  704.       break;
  705.  
  706.     case 0:
  707.       {
  708.     char **argv_0 = MALLOC (char *, 3 + vector_cardinality (flinkv));
  709.     char **argv = argv_0 + 2;
  710.     while (*flinkv)
  711.       *argv++ = maybe_relative_file_name (0, *flinkv++, cw_dlink);
  712.     *argv = 0;
  713.     argv = argv_0 + 1;
  714.     if (eid_arg)
  715.       {
  716.         sprintf (ed_arg_buffer, eid_arg, pattern);
  717.         *argv-- = ed_arg_buffer;
  718.       }
  719.     *(char const **) argv = editor;
  720.     execvp (editor, argv);
  721.     error (0, errno, _("can't exec `%s'"), editor);
  722.       }
  723.  
  724.     default:
  725.       {
  726.     void (*oldint) __P((int)) = signal (SIGINT, SIG_IGN);
  727.     void (*oldquit) __P((int)) = signal (SIGQUIT, SIG_IGN);
  728.  
  729.     while (wait (0) == -1 && errno == EINTR)
  730.       ;
  731.  
  732.     signal (SIGINT, oldint);
  733.     signal (SIGQUIT, oldquit);
  734.       }
  735.       break;
  736.     }
  737. }
  738.  
  739. void
  740. report_nothing (char const *name, struct file_link **flinkv)
  741. {
  742.   if (key_style != ks_none)
  743.     puts (name);
  744. }
  745.  
  746. int
  747. vector_cardinality (void *vector)
  748. {
  749.   void **v = (void **) vector;
  750.   int count = 0;
  751.  
  752.   while (*v++)
  753.     count++;
  754.   return count;
  755. }
  756.  
  757. int
  758. search_flinkv (struct file_link **flinkv)
  759. {
  760.   char pattern[BUFSIZ];
  761.   unsigned int count;
  762.  
  763.   if (gets (pattern) == 0)
  764.     return -1;
  765.  
  766.   for (count = 0; *flinkv; count++, flinkv++)
  767.     {
  768.       char *file_name = ALLOCA (char, PATH_MAX);
  769.       maybe_relative_file_name (file_name, *flinkv, cw_dlink);
  770.       if (strcasestr (file_name, pattern))
  771.     return count;
  772.     }
  773.   return -1;
  774. }
  775.  
  776. int
  777. query_literal_word (char const *arg, report_func_t report_func)
  778. {
  779.   if (query_binary_search (arg) == 0)
  780.     return 0;
  781.   gets_past_00 (hits_buf_1, idh.idh_FILE);
  782.   assert (*hits_buf_1);
  783.   if (!desired_frequency (hits_buf_1))
  784.     return 0;
  785.   (*report_func) (hits_buf_1, tree8_to_flinkv (token_hits_addr (hits_buf_1)));
  786.   return 1;
  787. }
  788.  
  789. int
  790. query_literal_prefix (char const *arg, report_func_t report_func)
  791. {
  792.   int count;
  793.   unsigned int length;
  794.  
  795.   if (query_binary_search (++arg) == 0)
  796.     return 0;
  797.  
  798.   length = strlen (arg);
  799.   count = 0;
  800.   if (key_style != ks_token)
  801.     memset (bits_vec, 0, bits_vec_size);
  802.   while (gets_past_00 (hits_buf_1, idh.idh_FILE) > 0)
  803.     {
  804.       assert (*hits_buf_1);
  805.       if (!desired_frequency (hits_buf_1))
  806.     continue;
  807.       if (!strnequ (arg, hits_buf_1, length))
  808.     break;
  809.       if (key_style == ks_token)
  810.     (*report_func) (hits_buf_1, tree8_to_flinkv (token_hits_addr (hits_buf_1)));
  811.       else
  812.     tree8_to_bits (bits_vec, token_hits_addr (hits_buf_1));
  813.       count++;
  814.     }
  815.   if (key_style != ks_token && count)
  816.     (*report_func) (--arg, bits_to_flinkv (bits_vec));
  817.  
  818.   return count;
  819. }
  820.  
  821. int
  822. query_regexp (char const *pattern_0, report_func_t report_func)
  823. {
  824.   int count;
  825.   regex_t compiled;
  826.   int regcomp_errno;
  827.   char const *pattern = pattern_0;
  828.  
  829.   pattern = maybe_add_word_delimiters (pattern);
  830.   regcomp_errno = regcomp (&compiled, pattern,
  831.                ignore_case_flag | REG_EXTENDED);
  832.   if (regcomp_errno)
  833.     {
  834.       char buf[BUFSIZ];
  835.       regerror (regcomp_errno, &compiled, buf, sizeof (buf));
  836.       error (1, 0, "%s", buf);
  837.     }
  838.   fseek (idh.idh_FILE, idh.idh_tokens_offset, SEEK_SET);
  839.  
  840.   count = 0;
  841.   if (key_style != ks_token)
  842.     memset (bits_vec, 0, bits_vec_size);
  843.   while (gets_past_00 (hits_buf_1, idh.idh_FILE) > 0)
  844.     {
  845.       int regexec_errno;
  846.       assert (*hits_buf_1);
  847.       if (!desired_frequency (hits_buf_1))
  848.     continue;
  849.       regexec_errno = regexec (&compiled, hits_buf_1, 0, 0, 0);
  850.       if (regexec_errno == REG_ESPACE)
  851.     error (0, 0, _("can't match regular-expression: memory exhausted"));
  852.       else if (regexec_errno)
  853.     continue;
  854.       if (key_style == ks_token)
  855.     (*report_func) (hits_buf_1, tree8_to_flinkv (token_hits_addr (hits_buf_1)));
  856.       else
  857.     tree8_to_bits (bits_vec, token_hits_addr (hits_buf_1));
  858.       count++;
  859.     }
  860.   if (key_style != ks_token && count)
  861.     (*report_func) (pattern, bits_to_flinkv (bits_vec));
  862.  
  863.   if (pattern != pattern_0)
  864.     free ((char *) pattern);
  865.  
  866.   return count;
  867. }
  868.  
  869. char const *
  870. maybe_add_word_delimiters (char const *pattern_0)
  871. {
  872.   if (delimiter_style != ds_word)
  873.     return pattern_0;
  874.   else
  875.     {
  876.       int length = strlen (pattern_0);
  877.       int has_left = has_left_delimiter (pattern_0);
  878.       int has_right = has_right_delimiter (&pattern_0[length]);
  879.       if (has_left && has_right)
  880.     return pattern_0;
  881.       else
  882.     {
  883.       char *pattern = MALLOC (char, length + 4);
  884.       if (has_left)
  885.         strcpy (pattern, pattern_0);
  886.       else
  887.         {
  888.           length += 2;
  889.           strcpy (pattern, "\\<");
  890.           strcpy (pattern + 2, pattern_0);
  891.         }
  892.       if (!has_right)
  893.         strcpy (pattern + length, "\\>");
  894.       return pattern;
  895.     }
  896.     }
  897. }
  898.  
  899. int
  900. query_number (char const *arg, report_func_t report_func)
  901. {
  902.   int count;
  903.   int radix;
  904.   int val;
  905.   int hit_digits = 0;
  906.  
  907.   radix = (val = stoi (arg)) ? radix_all : get_radix (arg);
  908.   fseek (idh.idh_FILE, idh.idh_tokens_offset, SEEK_SET);
  909.  
  910.   count = 0;
  911.   if (key_style != ks_token)
  912.     memset (bits_vec, 0, bits_vec_size);
  913.   while (gets_past_00 (hits_buf_1, idh.idh_FILE) > 0)
  914.     {
  915.       if (hit_digits)
  916.     {
  917.       if (!isdigit (*hits_buf_1))
  918.         break;
  919.     }
  920.       else
  921.     {
  922.       if (isdigit (*hits_buf_1))
  923.         hit_digits = 1;
  924.     }
  925.  
  926.       if (!((radix_flag ? radix_flag : radix) & get_radix (hits_buf_1))
  927.       || stoi (hits_buf_1) != val)
  928.     continue;
  929.       if (key_style == ks_token)
  930.     (*report_func) (hits_buf_1, tree8_to_flinkv (token_hits_addr (hits_buf_1)));
  931.       else
  932.     tree8_to_bits (bits_vec, token_hits_addr (hits_buf_1));
  933.       count++;
  934.     }
  935.   if (key_style != ks_token && count)
  936.     (*report_func) (arg, bits_to_flinkv (bits_vec));
  937.  
  938.   return count;
  939. }
  940.  
  941. /* Find identifiers that are non-unique within the first `count'
  942.    characters.  */
  943.  
  944. int
  945. query_ambiguous_prefix (unsigned int limit, report_func_t report_func)
  946. {
  947.   char *old = hits_buf_1;
  948.   char *new = hits_buf_2;
  949.   int consecutive = 0;
  950.   int count = 0;
  951.   char name[1024];
  952.  
  953.   if (limit <= 1)
  954.     usage ();
  955.   assert (limit < sizeof(name));
  956.  
  957.   name[0] = '^';
  958.   *new = '\0';
  959.   fseek (idh.idh_FILE, idh.idh_tokens_offset, SEEK_SET);
  960.   while (gets_past_00 (old, idh.idh_FILE) > 0)
  961.     {
  962.       char *tmp;
  963.       if (!(token_flags (old) & TOK_NAME))
  964.     continue;
  965.       tmp = old;
  966.       old = new;
  967.       new = tmp;
  968.       if (!strnequ (new, old, limit))
  969.     {
  970.       if (consecutive && key_style != ks_token)
  971.         {
  972.           strncpy (&name[1], old, limit);
  973.           (*report_func) (name, bits_to_flinkv (bits_vec));
  974.         }
  975.       consecutive = 0;
  976.       continue;
  977.     }
  978.       if (!consecutive++)
  979.     {
  980.       if (key_style != ks_token)
  981.         tree8_to_bits (bits_vec, token_hits_addr (old));
  982.       else
  983.         (*report_func) (old, tree8_to_flinkv (token_hits_addr (old)));
  984.       count++;
  985.     }
  986.       if (key_style == ks_token)
  987.     (*report_func) (new, tree8_to_flinkv (token_hits_addr (new)));
  988.       else
  989.     tree8_to_bits (bits_vec, token_hits_addr (new));
  990.       count++;
  991.     }
  992.   if (consecutive && key_style != ks_token)
  993.     {
  994.       strncpy (&name[1], new, limit);
  995.       (*report_func) (name, bits_to_flinkv (bits_vec));
  996.     }
  997.   return count;
  998. }
  999.  
  1000. int
  1001. query_literal_substring (char const *arg, report_func_t report_func)
  1002. {
  1003.   int count;
  1004.   char *(*strstr_func) __P((char const *, char const *));
  1005.  
  1006.   fseek (idh.idh_FILE, idh.idh_tokens_offset, SEEK_SET);
  1007.  
  1008.   count = 0;
  1009.   if (key_style != ks_token)
  1010.     memset (bits_vec, 0, bits_vec_size);
  1011.   strstr_func = (ignore_case_flag ? strcasestr : strstr);
  1012.   while (gets_past_00 (hits_buf_1, idh.idh_FILE) > 0)
  1013.     {
  1014.       assert (*hits_buf_1);
  1015.       if (!desired_frequency (hits_buf_1))
  1016.     continue;
  1017.       if ((*strstr_func) (hits_buf_1, arg) == 0)
  1018.     continue;
  1019.       if (key_style == ks_token)
  1020.     (*report_func) (hits_buf_1, tree8_to_flinkv (token_hits_addr (hits_buf_1)));
  1021.       else
  1022.     tree8_to_bits (bits_vec, token_hits_addr (hits_buf_1));
  1023.       count++;
  1024.     }
  1025.   if (key_style != ks_token && count)
  1026.     (*report_func) (arg, bits_to_flinkv (bits_vec));
  1027.  
  1028.   return count;
  1029. }
  1030.  
  1031. void
  1032. parse_frequency_arg (char const *arg)
  1033. {
  1034.   if (strnequ (arg, "..", 2))
  1035.     frequency_low = 1;
  1036.   else
  1037.     {
  1038.       frequency_low = atoi (arg);
  1039.       while (isdigit (*arg))
  1040.     arg++;
  1041.       if (strnequ (arg, "..", 2))
  1042.     arg += 2;
  1043.     }
  1044.   if (*arg)
  1045.     frequency_high = atoi (arg);
  1046.   else if (strnequ (&arg[-1], "..", 2))
  1047.     frequency_high = USHRT_MAX;
  1048.   else
  1049.     frequency_high = frequency_low;
  1050.   if (frequency_low > frequency_high)
  1051.     {
  1052.       unsigned int tmp = frequency_low;
  1053.       frequency_low = frequency_high;
  1054.       frequency_high = tmp;
  1055.     }
  1056. }
  1057.  
  1058. int
  1059. desired_frequency (char const *tok)
  1060. {
  1061.   unsigned int count = token_count (tok);
  1062.   return (frequency_low <= count && count <= frequency_high);
  1063. }
  1064.  
  1065. /* if string `s2' occurs in `s1', return a pointer to the first match.
  1066.    Ignore differences in alphabetic case.  */
  1067.  
  1068. char *
  1069. strcasestr (char const *s1, char const *s2)
  1070. {
  1071.   char const *s1p;
  1072.   char const *s2p;
  1073.   char const *s1last;
  1074.  
  1075.   for (s1last = &s1[strlen (s1) - strlen (s2)]; s1 <= s1last; s1++)
  1076.     for (s1p = s1, s2p = s2; TOLOWER (*s1p) == TOLOWER (*s2p); s1p++)
  1077.       if (*++s2p == '\0')
  1078.     return (char *) s1;
  1079.   return 0;
  1080. }
  1081.  
  1082. /* Convert the regular expression that we used to locate identifiers
  1083.    in the id database into one suitable for locating the identifiers
  1084.    in files.  */
  1085.  
  1086. char const *
  1087. file_regexp (char const *name_0, char const *left_delimit, char const *right_delimit)
  1088. {
  1089.   static char pat_buf[BUFSIZ];
  1090.   char *name = (char *) name_0;
  1091.  
  1092.   if (query_function == query_number && key_style == ks_pattern)
  1093.     {
  1094.       sprintf (pat_buf, "%s0*[Xx]*0*%d[Ll]*%s", left_delimit, stoi (name), right_delimit);
  1095.       return pat_buf;
  1096.     }
  1097.  
  1098.   if (!is_regexp (name) && name[0] != '^')
  1099.     return 0;
  1100.  
  1101.   if (name[0] == '^')
  1102.     name_0++;
  1103.   else
  1104.     left_delimit = "";
  1105.   while (*++name)
  1106.     ;
  1107.   if (*--name == '$')
  1108.     *name = '\0';
  1109.   else
  1110.     right_delimit = "";
  1111.  
  1112.   sprintf (pat_buf, "%s%s%s", left_delimit, name_0, right_delimit);
  1113.   return pat_buf;
  1114. }
  1115.  
  1116. off_t
  1117. query_binary_search (char const *token_0)
  1118. {
  1119.   off_t offset = 0;
  1120.   off_t start = idh.idh_tokens_offset - 2;
  1121.   off_t end = idh.idh_end_offset;
  1122.   off_t anchor_offset = 0;
  1123.   int order = -1;
  1124.  
  1125.   while (start < end)
  1126.     {
  1127.       int c;
  1128.       int incr = 1;
  1129.       char const *token;
  1130.  
  1131.       offset = start + (end - start) / 2;
  1132.       fseek (idh.idh_FILE, offset, SEEK_SET);
  1133.       offset += skip_past_00 (idh.idh_FILE);
  1134.       if (offset >= end)
  1135.     {
  1136.       offset = start + 2;
  1137.       fseek (idh.idh_FILE, offset, SEEK_SET);
  1138.     }
  1139.  
  1140.       /* compare the token names */
  1141.       token = token_0;
  1142.       while (*token == (c = getc (idh.idh_FILE)) && *token && c)
  1143.     {
  1144.       token++;
  1145.       incr++;
  1146.     }
  1147.       if (c && !*token && query_function == query_literal_prefix)
  1148.     anchor_offset = offset;
  1149.       order = *token - c;
  1150.  
  1151.       if (order < 0)
  1152.     end = offset - 2;
  1153.       else if (order > 0)
  1154.     start = offset + incr + skip_past_00 (idh.idh_FILE) - 2;
  1155.       else
  1156.     break;
  1157.     }
  1158.  
  1159.   if (order)
  1160.     {
  1161.       if (anchor_offset)
  1162.     offset = anchor_offset;
  1163.       else
  1164.     return 0;
  1165.     }
  1166.   fseek (idh.idh_FILE, offset, SEEK_SET);
  1167.   return offset;
  1168. }
  1169.  
  1170. /* Are there any regexp meta-characters in name?? */
  1171.  
  1172. int
  1173. is_regexp (char *name)
  1174. {
  1175.   int backslash = 0;
  1176.  
  1177.   if (*name == '^')
  1178.     name++;
  1179.   else if (strnequ (name, "\\<", 2))
  1180.     name += 2;
  1181.   while (*name)
  1182.     {
  1183.       if (*name == '\\')
  1184.     {
  1185.       if (strchr ("<>", name[1]))
  1186.         return 1;
  1187.       name++, backslash++;
  1188.     }
  1189.       else if (strchr ("[]().*+^$", *name))
  1190.     return 1;
  1191.       name++;
  1192.     }
  1193.   if (backslash)
  1194.     while (*name)
  1195.       {
  1196.     if (*name == '\\')
  1197.       strcpy (name, name + 1);
  1198.     name++;
  1199.       }
  1200.   return 0;
  1201. }
  1202.  
  1203. int
  1204. has_left_delimiter (char const *pattern)
  1205. {
  1206.   return (*pattern == '^' || strnequ (pattern, "\\<", 2));
  1207. }
  1208.  
  1209. int
  1210. has_right_delimiter (char const *pattern)
  1211. {
  1212.   return (pattern[-1] == '$' || strequ (pattern - 2, "\\>"));
  1213. }
  1214.  
  1215. /* file_name_wildcard implements a simple pattern matcher that
  1216.    emulates the shell wild card capability.
  1217.  
  1218.    * - any string of chars
  1219.    ? - any char
  1220.    [] - any char in set (if first char is !, any not in set)
  1221.    \ - literal match next char */
  1222.  
  1223. int
  1224. file_name_wildcard (char const *pattern, char const *fn)
  1225. {
  1226.   int c;
  1227.   int i;
  1228.   char set[256];
  1229.   int revset;
  1230.  
  1231.   while ((c = *pattern++) != '\0')
  1232.     {
  1233.       if (c == '*')
  1234.     {
  1235.       if (*pattern == '\0')
  1236.         return 1;        /* match anything at end */
  1237.       while (*fn != '\0')
  1238.         {
  1239.           if (file_name_wildcard (pattern, fn))
  1240.         return 1;
  1241.           ++fn;
  1242.         }
  1243.       return 0;
  1244.     }
  1245.       else if (c == '?')
  1246.     {
  1247.       if (*fn++ == '\0')
  1248.         return 0;
  1249.     }
  1250.       else if (c == '[')
  1251.     {
  1252.       c = *pattern++;
  1253.       memset (set, 0, 256);
  1254.       if (c == '!')
  1255.         {
  1256.           revset = 1;
  1257.           c = *pattern++;
  1258.         }
  1259.       else
  1260.         revset = 0;
  1261.       while (c != ']')
  1262.         {
  1263.           if (c == '\\')
  1264.         c = *pattern++;
  1265.           set[c] = 1;
  1266.           if ((*pattern == '-') && (*(pattern + 1) != ']'))
  1267.         {
  1268.           pattern += 1;
  1269.           while (++c <= *pattern)
  1270.             set[c] = 1;
  1271.           ++pattern;
  1272.         }
  1273.           c = *pattern++;
  1274.         }
  1275.       if (revset)
  1276.         for (i = 1; i < 256; ++i)
  1277.           set[i] = !set[i];
  1278.       if (!set[(int) *fn++])
  1279.         return 0;
  1280.     }
  1281.       else
  1282.     {
  1283.       if (c == '\\')
  1284.         c = *pattern++;
  1285.       if (c != *fn++)
  1286.         return 0;
  1287.     }
  1288.     }
  1289.   return (*fn == '\0');
  1290. }
  1291.  
  1292. /* Does `name' occur in `line' delimited by non-alphanumerics?? */
  1293.  
  1294. int
  1295. word_match (char const *name_0, char const *line)
  1296. {
  1297.   char const *name = name_0;
  1298.  
  1299.   for (;;)
  1300.     {
  1301.       /* find an initial-character match */
  1302.       while (*line != *name)
  1303.     {
  1304.       if (*line == '\0' || *line == '\n')
  1305.         return 0;
  1306.       line++;
  1307.     }
  1308.       /* do we have a word delimiter on the left ?? */
  1309.       if (IS_ALNUM (line[-1]))
  1310.     {
  1311.       line++;
  1312.       continue;
  1313.     }
  1314.       /* march down both strings as long as we match */
  1315.       while (*++name == *++line)
  1316.     ;
  1317.       /* is this the end of `name', is there a word delimiter ?? */
  1318.       if (*name == '\0' && !IS_ALNUM (*line))
  1319.     return 1;
  1320.       name = name_0;
  1321.     }
  1322. }
  1323.  
  1324. /* Use the C lexical rules to determine an ascii number's radix.  The
  1325.    radix is returned as a bit map, so that more than one radix may
  1326.    apply.  In particular, it is impossible to determine the radix of
  1327.    0, so return all possibilities.  */
  1328.  
  1329. int
  1330. get_radix (char const *str)
  1331. {
  1332.   if (!isdigit (*str))
  1333.     return 0;
  1334.   if (*str != '0')
  1335.     return radix_dec;
  1336.   str++;
  1337.   if (*str == 'x' || *str == 'X')
  1338.     return radix_hex;
  1339.   while (*str && *str == '0')
  1340.     str++;
  1341.   return (*str ? radix_oct : (radix_oct | radix_dec));
  1342. }
  1343.  
  1344. int
  1345. is_number (char const *str)
  1346. {
  1347.   if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
  1348.     {
  1349.       str += 2;
  1350.       str += strspn (str, "0123456789aAbBcCdDeEfF");
  1351.     }
  1352.   else
  1353.     str += strspn (str, "0123456789");
  1354.   str += strspn (str, "uUlL");
  1355.   return (*str == '\0');
  1356. }
  1357.  
  1358. /* Convert an ascii string number to an integer.  Determine the radix
  1359.    before converting.  */
  1360.  
  1361. int
  1362. stoi (char const *str)
  1363. {
  1364.   switch (get_radix (str))
  1365.     {
  1366.     case radix_dec:
  1367.       return (dtoi (str));
  1368.     case radix_oct:
  1369.       return (otoi (&str[1]));
  1370.     case radix_hex:
  1371.       return (xtoi (&str[2]));
  1372.     case radix_dec | radix_oct:
  1373.       return 0;
  1374.     default:
  1375.       return -1;
  1376.     }
  1377. }
  1378.  
  1379. /* Convert an ascii octal number to an integer. */
  1380.  
  1381. int
  1382. otoi (char const *str)
  1383. {
  1384.   int n = 0;
  1385.  
  1386.   while (*str >= '0' && *str <= '7')
  1387.     {
  1388.       n *= 010;
  1389.       n += *str++ - '0';
  1390.     }
  1391.   while (*str && strchr ("uUlL", *str))
  1392.     str++;
  1393.   return (*str ? -1 : n);
  1394. }
  1395.  
  1396. /* Convert an ascii decimal number to an integer. */
  1397.  
  1398. int
  1399. dtoi (char const *str)
  1400. {
  1401.   int n = 0;
  1402.  
  1403.   while (isdigit (*str))
  1404.     {
  1405.       n *= 10;
  1406.       n += *str++ - '0';
  1407.     }
  1408.   while (*str && strchr ("uUlL", *str))
  1409.     str++;
  1410.   return (*str ? -1 : n);
  1411. }
  1412.  
  1413. /* Convert an ascii hex number to an integer. */
  1414.  
  1415. int
  1416. xtoi (char const *str)
  1417. {
  1418.   int n = 0;
  1419.  
  1420.   while (isxdigit (*str))
  1421.     {
  1422.       n *= 0x10;
  1423.       if (isdigit (*str))
  1424.     n += *str++ - '0';
  1425.       else if (islower (*str))
  1426.     n += 0xa + *str++ - 'a';
  1427.       else
  1428.     n += 0xA + *str++ - 'A';
  1429.     }
  1430.   while (*str && strchr ("uUlL", *str))
  1431.     str++;
  1432.   return (*str ? -1 : n);
  1433. }
  1434.  
  1435. unsigned char *
  1436. tree8_to_bits (unsigned char *bv_0, unsigned char const *hits_tree8)
  1437. {
  1438.   unsigned char* bv = bv_0;
  1439.   tree8_to_bits_1 (&bv, &hits_tree8, tree8_levels);
  1440.   return bv_0;
  1441. }
  1442.  
  1443. void
  1444. tree8_to_bits_1 (unsigned char **bv, unsigned char const **hits_tree8, int level)
  1445. {
  1446.   int hits = *(*hits_tree8)++;
  1447.  
  1448.   if (--level)
  1449.     {
  1450.       int incr = 1 << ((level - 1) * 3);
  1451.       int bit;
  1452.       for (bit = 1; bit & 0xff; bit <<= 1)
  1453.     {
  1454.       if (bit & hits)
  1455.         tree8_to_bits_1 (bv, hits_tree8, level);
  1456.       else
  1457.         *bv += incr;
  1458.     }
  1459.     }
  1460.   else
  1461.     *(*bv)++ |= hits;
  1462. }
  1463.  
  1464. struct file_link **
  1465. bits_to_flinkv (unsigned char const *bv)
  1466. {
  1467.   int const reserved_flinkv_slots = 3;
  1468.   static struct file_link **flinkv_0;
  1469.   struct file_link **flinkv;
  1470.   struct file_link **members = members_0;
  1471.   struct file_link **end = &members_0[idh.idh_files];
  1472.  
  1473.   if (flinkv_0 == 0)
  1474.     flinkv_0 = MALLOC (struct file_link *, idh.idh_files + reserved_flinkv_slots + 2);
  1475.   flinkv = &flinkv_0[reserved_flinkv_slots];
  1476.  
  1477.   for (;;)
  1478.     {
  1479.       int hits;
  1480.       int bit;
  1481.  
  1482.       while (*bv == 0)
  1483.     {
  1484.       bv++;
  1485.       members += 8;
  1486.       if (members >= end)
  1487.         goto out;
  1488.     }
  1489.       hits = *bv++;
  1490.       for (bit = 1; bit & 0xff; bit <<= 1)
  1491.     {
  1492.       if (bit & hits)
  1493.         *flinkv++ = *members;
  1494.       if (++members >= end)
  1495.         goto out;
  1496.     }
  1497.     }
  1498. out:
  1499.   *flinkv = 0;
  1500.   return &flinkv_0[reserved_flinkv_slots];
  1501. }
  1502.  
  1503. struct file_link **
  1504. tree8_to_flinkv (unsigned char const *hits_tree8)
  1505. {
  1506.   memset (bits_vec, 0, bits_vec_size);
  1507.   return bits_to_flinkv (tree8_to_bits (bits_vec, hits_tree8));
  1508. }
  1509.  
  1510. #if HAVE_TERMIOS_H
  1511.  
  1512. #include <termios.h>
  1513. struct termios linemode;
  1514. struct termios charmode;
  1515. struct termios savemode;
  1516. #define GET_TTY_MODES(modes) tcgetattr (0, (modes))
  1517. #define SET_TTY_MODES(modes) tcsetattr(0, TCSANOW, (modes))
  1518.  
  1519. #else /* not HAVE_TERMIOS_H */
  1520.  
  1521. # if HAVE_SYS_IOCTL_H
  1522. #  include <sys/ioctl.h>
  1523. # endif
  1524.  
  1525. # if HAVE_TERMIO_H
  1526.  
  1527. #  include <termio.h>
  1528. struct termio linemode;
  1529. struct termio charmode;
  1530. struct termio savemode;
  1531. #define GET_TTY_MODES(modes) ioctl (0, TCGETA, (modes))
  1532. #define SET_TTY_MODES(modes) ioctl (0, TCSETA, (modes))
  1533.  
  1534. # else /* not HAVE_TERMIO_H */
  1535.  
  1536. #  if HAVE_SGTTY_H
  1537.  
  1538. #   include <sgtty.h>
  1539. struct sgttyb linemode;
  1540. struct sgttyb charmode;
  1541. struct sgttyb savemode;
  1542.  
  1543. #   ifdef TIOCGETP
  1544. #define GET_TTY_MODES(modes) ioctl (0, TIOCGETP, (modes))
  1545. #define SET_TTY_MODES(modes) ioctl (0, TIOCSETP, (modes))
  1546. #   else
  1547. #define GET_TTY_MODES(modes) gtty (0, (modes))
  1548. #define SET_TTY_MODES(modes) stty (0, (modes))
  1549. #   endif
  1550.  
  1551. #  else /* not HAVE_SGTTY_H */
  1552.  
  1553. #define GET_TTY_MODES(modes)
  1554. #define SET_TTY_MODES(modes)
  1555.  
  1556. #  endif /* not HAVE_SGTTY_H */
  1557. # endif /* not HAVE_TERMIO_H */
  1558. #endif /* not HAVE_TERMIOS_H */
  1559.  
  1560. #if HAVE_TERMIOS_H || HAVE_TERMIO_H
  1561.  
  1562. void
  1563. savetty (void)
  1564. {
  1565.   GET_TTY_MODES (&savemode);
  1566.   charmode = linemode = savemode;
  1567.  
  1568.   charmode.c_lflag &= ~(ECHO | ICANON | ISIG);
  1569.   charmode.c_cc[VMIN] = 1;
  1570.   charmode.c_cc[VTIME] = 0;
  1571.  
  1572.   linemode.c_lflag |= (ECHO | ICANON | ISIG);
  1573.   linemode.c_cc[VEOF] = 'd' & 037;
  1574.   linemode.c_cc[VEOL] = 0377;
  1575. }
  1576.  
  1577. #else /* not (HAVE_TERMIOS_H || HAVE_TERMIO_H) */
  1578.  
  1579. # if HAVE_SGTTY_H
  1580.  
  1581. void
  1582. savetty (void)
  1583. {
  1584. #  ifdef TIOCGETP
  1585.   ioctl(0, TIOCGETP, &savemode);
  1586. #  else
  1587.   gtty(0, &savemode);
  1588. #  endif
  1589.   charmode = linemode = savemode;
  1590.  
  1591.   charmode.sg_flags &= ~ECHO;
  1592.   charmode.sg_flags |= RAW;
  1593.  
  1594.   linemode.sg_flags |= ECHO;
  1595.   linemode.sg_flags &= ~RAW;
  1596. }
  1597.  
  1598. # else /* not HAVE_SGTTY_H */
  1599.  
  1600. void
  1601. savetty (void)
  1602. {
  1603. }
  1604.  
  1605. # endif /* not HAVE_SGTTY_H */
  1606. #endif /* not (HAVE_TERMIOS_H || HAVE_TERMIO_H) */
  1607.  
  1608. void
  1609. restoretty (void)
  1610. {
  1611.   SET_TTY_MODES (&savemode);
  1612. }
  1613.  
  1614. void
  1615. linetty (void)
  1616. {
  1617.   SET_TTY_MODES (&linemode);
  1618. }
  1619.  
  1620. void
  1621. chartty (void)
  1622. {
  1623.   SET_TTY_MODES (&charmode);
  1624. }
  1625.