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