1 /* Input line filename/username/hostname/variable/command completion.
2 (Let mc type for you...)
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2007 Free Software Foundation, Inc.
7 Written by: 1995 Jakub Jelinek
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
30 #include <sys/types.h>
34 #include "mhl/memory.h"
35 #include "mhl/escape.h"
46 #include "key.h" /* XCTRL and ALT macros */
48 typedef char *CompletionFunction (char *, int);
50 /* This flag is used in filename_completion_function */
51 static int ignore_filenames
= 0;
53 /* This flag is used by command_completion_function */
54 /* to hint the filename_completion_function */
55 static int look_for_executables
= 0;
58 filename_completion_function (char *text
, int state
)
60 static DIR *directory
;
61 static char *filename
= NULL
;
62 static char *dirname
= NULL
;
63 static char *users_dirname
= NULL
;
64 static size_t filename_len
;
65 int isdir
= 1, isexec
= 0;
67 struct dirent
*entry
= NULL
;
69 /* If we're starting the match process, initialize us a bit. */
75 g_free (users_dirname
);
77 if ((*text
) && (temp
= strrchr (text
, PATH_SEP
))){
78 filename
= g_strdup (++temp
);
79 dirname
= g_strndup (text
, temp
- text
);
81 dirname
= g_strdup (".");
82 filename
= g_strdup (text
);
85 /* We aren't done yet. We also support the "~user" syntax. */
87 /* Save the version of the directory that the user typed. */
88 users_dirname
= dirname
;
91 dirname
= tilde_expand (dirname
);
92 canonicalize_pathname (dirname
);
93 /* Here we should do something with variable expansion
95 Maybe a dream - UNIMPLEMENTED yet. */
97 directory
= mc_opendir (dirname
);
98 filename_len
= strlen (filename
);
101 /* Now that we have some state, we can read the directory. */
103 while (directory
&& (entry
= mc_readdir (directory
))){
104 /* Special case for no filename.
105 All entries except "." and ".." match. */
107 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
110 /* Otherwise, if these match up to the length of filename, then
111 it may be a match. */
112 if ((entry
->d_name
[0] != filename
[0]) ||
113 ((NLENGTH (entry
)) < filename_len
) ||
114 strncmp (filename
, entry
->d_name
, filename_len
))
117 isdir
= 1; isexec
= 0;
119 char *tmp
= g_malloc (3 + strlen (dirname
) + NLENGTH (entry
));
120 struct stat tempstat
;
122 strcpy (tmp
, dirname
);
123 strcat (tmp
, PATH_SEP_STR
);
124 strcat (tmp
, entry
->d_name
);
125 canonicalize_pathname (tmp
);
127 if (!mc_stat (tmp
, &tempstat
)){
128 uid_t my_uid
= getuid ();
129 gid_t my_gid
= getgid ();
131 if (!S_ISDIR (tempstat
.st_mode
)){
133 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
134 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
135 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
136 (tempstat
.st_mode
& 0001))
142 switch (look_for_executables
)
147 case 1: if (!isexec
&& !isdir
)
151 if (ignore_filenames
&& !isdir
)
158 mc_closedir (directory
);
165 g_free (users_dirname
);
166 users_dirname
= NULL
;
171 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1])){
172 int dirlen
= strlen (users_dirname
);
173 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
174 strcpy (temp
, users_dirname
);
175 /* We need a `/' at the end. */
176 if (users_dirname
[dirlen
- 1] != PATH_SEP
){
177 temp
[dirlen
] = PATH_SEP
;
178 temp
[dirlen
+ 1] = 0;
180 strcat (temp
, entry
->d_name
);
182 temp
= g_malloc (2 + NLENGTH (entry
));
183 strcpy (temp
, entry
->d_name
);
186 strcat (temp
, PATH_SEP_STR
);
191 /* We assume here that text[0] == '~' , if you want to call it in another way,
192 you have to change the code */
194 username_completion_function (char *text
, int state
)
196 static struct passwd
*entry
;
199 if (text
[0] == '\\' && text
[1] == '~') text
++;
200 if (!state
){ /* Initialization stuff */
202 userlen
= strlen (text
+ 1);
204 while ((entry
= getpwent ()) != NULL
){
205 /* Null usernames should result in all users as possible completions. */
208 else if (text
[1] == entry
->pw_name
[0] &&
209 !strncmp (text
+ 1, entry
->pw_name
, userlen
))
217 char *temp
= g_malloc (3 + strlen (entry
->pw_name
));
220 strcpy (temp
+ 1, entry
->pw_name
);
221 strcat (temp
, PATH_SEP_STR
);
226 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
227 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
228 extern char **environ
;
231 /* We assume text [0] == '$' and want to have a look at text [1], if it is
232 equal to '{', so that we should append '}' at the end */
234 variable_completion_function (char *text
, int state
)
237 static int varlen
, isbrace
;
238 const char *p
= NULL
;
240 if (!state
){ /* Initialization stuff */
241 isbrace
= (text
[1] == '{');
242 varlen
= strlen (text
+ 1 + isbrace
);
247 p
= strchr (*env_p
, '=');
248 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
256 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
261 memcpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
263 strcpy (temp
+ 2 + (p
- *env_p
), "}");
265 temp
[1 + p
- *env_p
] = 0;
271 #define whitespace(c) ((c) == ' ' || (c) == '\t')
272 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
274 static char **hosts
= NULL
;
275 static char **hosts_p
= NULL
;
276 static int hosts_alloclen
= 0;
277 static void fetch_hosts (const char *filename
)
279 FILE *file
= fopen (filename
, "r");
280 char buffer
[256], *name
;
281 register int i
, start
;
286 while (fgets (buffer
, 255, file
) != NULL
){
287 /* Skip to first character. */
288 for (i
= 0; buffer
[i
] && cr_whitespace (buffer
[i
]); i
++);
289 /* Ignore comments... */
290 if (buffer
[i
] == '#')
292 /* Handle $include. */
293 if (!strncmp (buffer
+ i
, "$include ", 9)){
294 char *includefile
= buffer
+ i
+ 9;
297 /* Find start of filename. */
298 while (*includefile
&& whitespace (*includefile
))
302 /* Find end of filename. */
303 while (*t
&& !cr_whitespace (*t
))
307 fetch_hosts (includefile
);
312 while (buffer
[i
] && !cr_whitespace (buffer
[i
]))
315 /* Get the host names separated by white space. */
316 while (buffer
[i
] && buffer
[i
] != '#'){
317 while (buffer
[i
] && cr_whitespace (buffer
[i
]))
319 if (buffer
[i
] == '#')
321 for (start
= i
; buffer
[i
] && !cr_whitespace (buffer
[i
]); i
++);
324 name
= g_strndup (buffer
+ start
, i
- start
);
328 if (hosts_p
- hosts
>= hosts_alloclen
){
329 int j
= hosts_p
- hosts
;
331 hosts
= g_realloc ((void *)hosts
, ((hosts_alloclen
+= 30) + 1) * sizeof (char *));
334 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
335 if (!strcmp (name
, *host_p
))
336 break; /* We do not want any duplicates */
337 if (host_p
== hosts_p
){
349 hostname_completion_function (char *text
, int state
)
351 static char **host_p
;
352 static int textstart
, textlen
;
354 if (!state
){ /* Initialization stuff */
358 for (host_p
= hosts
; *host_p
; host_p
++)
362 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
365 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
367 textstart
= (*text
== '@') ? 1 : 0;
368 textlen
= strlen (text
+ textstart
);
373 break; /* Match all of them */
374 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
380 for (host_p
= hosts
; *host_p
; host_p
++)
386 char *temp
= g_malloc (2 + strlen (*host_p
));
390 strcpy (temp
+ textstart
, *host_p
);
397 * This is the function to call when the word to complete is in a position
398 * where a command word can be found. It looks around $PATH, looking for
399 * commands that match. It also scans aliases, function names, and the
400 * table of shell built-ins.
403 command_completion_function (char *text
, int state
)
405 text
= mhl_shell_unescape_buf(text
);
406 static const char *path_end
;
407 static int isabsolute
;
410 static const char *const *words
;
412 static char *cur_path
;
413 static char *cur_word
;
414 static int init_state
;
415 static const char *const bash_reserved
[] = {
416 "if", "then", "else", "elif", "fi", "case", "esac", "for",
417 "select", "while", "until", "do", "done", "in", "function", 0
419 static const char *const bash_builtins
[] = {
420 "alias", "bg", "bind", "break", "builtin", "cd", "command",
421 "continue", "declare", "dirs", "echo", "enable", "eval",
422 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
423 "help", "history", "jobs", "kill", "let", "local", "logout",
424 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
425 "shift", "source", "suspend", "test", "times", "trap", "type",
426 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
430 if (!state
) { /* Initialize us a little bit */
431 isabsolute
= strchr (text
, PATH_SEP
) != 0;
432 look_for_executables
= isabsolute
? 1 : 2;
434 words
= bash_reserved
;
436 text_len
= strlen (text
);
437 if (!path
&& (path
= g_strdup (getenv ("PATH"))) != NULL
) {
439 path_end
= strchr (p
, 0);
440 while ((p
= strchr (p
, PATH_ENV_SEP
))) {
448 p
= filename_completion_function (text
, state
);
451 look_for_executables
= 0;
455 SHELL_ESCAPED_STR e_p
= mhl_shell_escape_dup(p
);
462 case 0: /* Reserved words */
464 if (!strncmp (*words
, text
, text_len
))
465 return g_strdup (*(words
++));
469 words
= bash_builtins
;
470 case 1: /* Builtin commands */
472 if (!strncmp (*words
, text
, text_len
))
473 return g_strdup (*(words
++));
481 case 2: /* And looking through the $PATH */
486 if (cur_path
>= path_end
)
488 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
489 cur_word
= concat_dir_and_file (expanded
, text
);
491 canonicalize_pathname (cur_word
);
492 cur_path
= strchr (cur_path
, 0) + 1;
496 filename_completion_function (cur_word
,
506 look_for_executables
= 0;
511 if ((p
= strrchr (found
, PATH_SEP
)) != NULL
) {
515 /* TODO: shell escape? */
523 match_compare (const void *a
, const void *b
)
525 return strcmp (*(char **)a
, *(char **)b
);
528 /* Returns an array of char * matches with the longest common denominator
529 in the 1st entry. Then a NULL terminated list of different possible
531 You have to supply your own CompletionFunction with the word you
532 want to complete as the first argument and an count of previous matches
534 In case no matches were found we return NULL. */
536 completion_matches (char *text
, CompletionFunction entry_function
)
538 /* Number of slots in match_list. */
541 /* The list of matches. */
542 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
544 /* Number of matches actually found. */
547 /* Temporary string binder. */
550 match_list
[1] = NULL
;
552 while ((string
= (*entry_function
) (text
, matches
)) != NULL
){
553 if (matches
+ 1 == match_list_size
)
554 match_list
= (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
555 match_list
[++matches
] = string
;
556 match_list
[matches
+ 1] = NULL
;
559 /* If there were any matches, then look through them finding out the
560 lowest common denominator. That then becomes match_list[0]. */
564 int low
= 4096; /* Count of max-matched characters. */
566 /* If only one match, just use that. */
568 match_list
[0] = match_list
[1];
569 match_list
[1] = NULL
;
573 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
575 /* And compare each member of the list with
576 the next, finding out where they stop matching.
577 If we find two equal strings, we have to put one away... */
580 while (j
< matches
+ 1)
582 register int c1
, c2
, si
;
584 for (si
= 0;(c1
= match_list
[i
][si
]) && (c2
= match_list
[j
][si
]); si
++)
587 if (!c1
&& !match_list
[j
][si
]){ /* Two equal strings */
588 g_free (match_list
[j
]);
592 continue; /* Look for a run of equal strings */
594 if (low
> si
) low
= si
;
595 if (i
+ 1 != j
) /* So there's some gap */
596 match_list
[i
+ 1] = match_list
[j
];
600 match_list
[matches
+ 1] = NULL
;
601 match_list
[0] = g_strndup(match_list
[1], low
);
603 } else { /* There were no matches. */
610 /* Check if directory completion is needed */
612 check_is_cd (const char *text
, int start
, INPUT_COMPLETE_FLAGS flags
)
616 if (flags
& INPUT_COMPLETE_CD
)
619 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
622 /* Skip initial spaces */
625 while (p
< q
&& *p
&& isspace ((unsigned char) *p
))
628 /* Check if the command is "cd" and the cursor is after it */
629 if (p
[0] == 'c' && p
[1] == 'd' && isspace ((unsigned char) p
[2])
636 /* Returns an array of matches, or NULL if none. */
638 try_complete (char *text
, int *start
, int *end
, int flags
)
640 int in_command_position
= 0;
642 char **matches
= NULL
;
643 const char *command_separator_chars
= ";|&{(`";
644 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
645 int is_cd
= check_is_cd (text
, *start
, flags
);
648 ignore_filenames
= 0;
651 word
= g_strdup (text
+ *start
);
654 /* Determine if this could be a command word. It is if it appears at
655 the start of the line (ignoring preceding whitespace), or if it
656 appears after a character that separates commands. And we have to
657 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
658 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
)){
660 for (i
= *start
- 1; i
> -1; i
--) {
661 if (text
[i
] == ' ' || text
[i
] == '\t'){
662 if (i
== 0 ) continue;
663 if (text
[i
-1] == '\\') {
670 in_command_position
++;
671 else if (strchr (command_separator_chars
, text
[i
])){
672 register int this_char
, prev_char
;
674 in_command_position
++;
677 /* Handle the two character tokens `>&', `<&', and `>|'.
678 We are not in a command position after one of these. */
680 prev_char
= text
[i
- 1];
682 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>')) ||
683 (this_char
== '|' && prev_char
== '>'))
684 in_command_position
= 0;
685 else if (i
> 0 && text
[i
-1] == '\\') /* Quoted */
686 in_command_position
= 0;
691 if (flags
& INPUT_COMPLETE_COMMANDS
)
692 p
= strrchr (word
, '`');
693 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
694 q
= strrchr (word
, '$');
695 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
696 r
= strrchr (word
, '@');
697 if (q
&& q
[1] == '(' && INPUT_COMPLETE_COMMANDS
){
703 /* Command substitution? */
705 matches
= completion_matches (p
+ 1, command_completion_function
);
707 *start
+= p
+ 1 - word
;
711 else if (q
> p
&& q
> r
){
712 matches
= completion_matches (q
, variable_completion_function
);
717 /* Starts with '@', then look through the known hostnames for
719 else if (r
> p
&& r
> q
){
720 matches
= completion_matches (r
, hostname_completion_function
);
725 /* Starts with `~' and there is no slash in the word, then
726 try completing this word as a username. */
727 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
728 matches
= completion_matches (word
, username_completion_function
);
731 /* And finally if this word is in a command position, then
732 complete over possible command names, including aliases, functions,
733 and command names. */
734 if (!matches
&& in_command_position
)
735 matches
= completion_matches (word
, command_completion_function
);
737 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
)){
739 ignore_filenames
= 1;
740 matches
= completion_matches (word
, filename_completion_function
);
741 ignore_filenames
= 0;
742 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~'){
743 char *p
, *q
= text
+ *start
;
745 for (p
= text
; *p
&& p
< q
; p
++){
746 if (*p
== ' ' || *p
== '\t') {
747 if (p
== text
) continue;
748 if (*(p
-1) == '\\') {
754 if (!strncmp (p
, "cd", 2))
755 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); p
++){
756 if (p
== text
) continue;
757 if (*(p
-1) == '\\') {
763 char * const cdpath_ref
= g_strdup (getenv ("CDPATH"));
764 char *cdpath
= cdpath_ref
;
771 while (!matches
&& c
== ':'){
772 s
= strchr (cdpath
, ':');
774 s
= strchr (cdpath
, 0);
778 r
= concat_dir_and_file (cdpath
, word
);
779 ignore_filenames
= 1;
780 matches
= completion_matches (r
, filename_completion_function
);
781 ignore_filenames
= 0;
797 void free_completions (WInput
*in
)
801 if (!in
->completions
)
803 for (p
=in
->completions
; *p
; p
++)
805 g_free (in
->completions
);
806 in
->completions
= NULL
;
809 static int query_height
, query_width
;
810 static WInput
*input
;
812 static int start
, end
;
814 static int insert_text (WInput
*in
, char *text
, ssize_t len
)
816 len
= min (len
, (ssize_t
) strlen (text
)) + start
- end
;
817 if (strlen (in
->buffer
) + len
>= (size_t) in
->current_max_len
){
818 /* Expand the buffer */
819 char *narea
= g_realloc (in
->buffer
, in
->current_max_len
+ len
+ in
->field_len
);
822 in
->current_max_len
+= len
+ in
->field_len
;
825 if (strlen (in
->buffer
)+1 < (size_t) in
->current_max_len
){
827 int i
= strlen (&in
->buffer
[end
]);
829 in
->buffer
[end
+ len
+ i
] = in
->buffer
[end
+ i
];
831 char *p
= in
->buffer
+ end
+ len
, *q
= in
->buffer
+ end
;
836 memcpy (in
->buffer
+ start
, text
, len
- start
+ end
);
838 update_input (in
, 1);
845 query_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
857 if (end
== min_end
) {
864 e1
= e
= ((WListbox
*) (h
->current
))->list
;
867 (input
->buffer
+ start
, e1
->text
,
869 listbox_select_entry ((WListbox
*) (h
->current
),
871 handle_char (input
, parm
);
873 send_message (h
->current
, WIDGET_DRAW
, 0);
882 if (parm
> 0xff || !is_printable (parm
)) {
883 if (is_in_input_map (input
, parm
) == 2) {
886 h
->ret_value
= B_USER
; /* This means we want to refill the
887 list box and start again */
891 return MSG_NOT_HANDLED
;
896 char *last_text
= NULL
;
898 e1
= e
= ((WListbox
*) (h
->current
))->list
;
901 (input
->buffer
+ start
, e1
->text
, end
- start
)) {
902 if (e1
->text
[end
- start
] == parm
) {
904 register int c1
, c2
, si
;
906 for (si
= end
- start
+ 1;
908 && (c2
= e1
->text
[si
]); si
++)
913 last_text
= e1
->text
;
917 listbox_select_entry ((WListbox
*) (h
->
920 last_text
= e1
->text
;
926 if (need_redraw
== 2) {
927 insert_text (input
, last_text
, low
);
928 send_message (h
->current
, WIDGET_DRAW
, 0);
929 } else if (need_redraw
== 1) {
930 h
->ret_value
= B_ENTER
;
939 return default_dlg_callback (h
, msg
, parm
);
943 #define DO_INSERTION 1
945 /* Returns 1 if the user would like to see us again */
947 complete_engine (WInput
*in
, int what_to_do
)
949 char *complete
= NULL
;
950 if (in
->completions
&& in
->point
!= end
)
951 free_completions (in
);
952 if (!in
->completions
){
954 for (start
= end
? end
- 1 : 0; start
> -1; start
--)
955 if (strchr (" \t;|<>", in
->buffer
[start
])){
956 if (start
> 0 && in
->buffer
[start
-1] == '\\')
963 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
966 if (in
->completions
){
967 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1])) {
968 char * complete
= in
->completions
[0];
969 if (insert_text (in
, complete
, strlen (complete
))){
970 if (in
->completions
[1])
973 free_completions (in
);
977 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1]) {
978 int maxlen
= 0, i
, count
= 0;
980 int start_x
, start_y
;
983 WListbox
*query_list
;
985 for (p
=in
->completions
+ 1; *p
; count
++, p
++) {
986 if ((i
= strlen (*p
)) > maxlen
)
989 start_x
= in
->widget
.x
;
990 start_y
= in
->widget
.y
;
991 if (start_y
- 2 >= count
) {
992 y
= start_y
- 2 - count
;
995 if (start_y
>= LINES
- start_y
- 1) {
1000 h
= LINES
- start_y
- 1;
1003 x
= start
- in
->first_shown
- 2 + start_x
;
1015 query_dlg
= create_dlg (y
, x
, query_height
, query_width
,
1016 dialog_colors
, query_callback
,
1017 "[Completion]", NULL
, DLG_COMPACT
);
1018 query_list
= listbox_new (1, 1, w
- 2, h
- 2, NULL
);
1019 add_widget (query_dlg
, query_list
);
1020 for (p
= in
->completions
+ 1; *p
; p
++)
1021 listbox_add_item (query_list
, 0, 0, *p
, NULL
);
1022 run_dlg (query_dlg
);
1024 if (query_dlg
->ret_value
== B_ENTER
){
1025 listbox_get_current (query_list
, &q
, NULL
);
1027 insert_text (in
, q
, strlen (q
));
1029 if (q
|| end
!= min_end
)
1030 free_completions (in
);
1031 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1032 destroy_dlg (query_dlg
);
1041 //void complete (WInput *in, COMPLETION_STYLE style)
1042 void complete (WInput
*in
)
1046 if (in
->completions
)
1047 engine_flags
= DO_QUERY
;
1050 engine_flags
= DO_INSERTION
;
1052 if (show_all_if_ambiguous
)
1053 engine_flags
|= DO_QUERY
;
1056 while (complete_engine (in
, engine_flags
));