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