1 /* Input line filename/username/hostname/variable/command completion.
2 (Let mc type for you...)
4 Copyright (C) 1995 The Free Software Foundation
6 Written by: 1995 Jakub Jelinek
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
30 /* unistd.h defines _POSIX_VERSION on POSIX.1 systems. */
31 #if defined(HAVE_DIRENT_H) || defined(_POSIX_VERSION)
32 # ifdef NEEDS_LOCAL_DIRENT_H
37 # define NLENGTH(dirent) (strlen ((dirent)->d_name))
39 # define dirent direct
40 # define NLENGTH(dirent) ((dirent)->d_namlen)
42 # ifdef HAVE_SYS_NDIR_H
43 # include <sys/ndir.h>
44 # endif /* HAVE_SYS_NDIR_H */
46 # ifdef HAVE_SYS_DIR_H
48 # endif /* HAVE_SYS_DIR_H */
52 # endif /* HAVE_NDIR_H */
53 #endif /* not (HAVE_DIRENT_H or _POSIX_VERSION) */
54 #include <sys/types.h>
56 #ifndef HAS_NO_GRP_PWD_H
69 #include "key.h" /* XCTRL and ALT macros */
70 #include "../vfs/vfs.h"
72 /* This flag is used in filename_completion_function */
73 int ignore_filenames
= 0;
75 /* This flag is used by command_completion_function */
76 /* to hint the filename_completion_function */
77 int look_for_executables
= 0;
80 filename_completion_function (char *text
, int state
)
82 static DIR *directory
;
83 static char *filename
= NULL
;
84 static char *dirname
= NULL
;
85 static char *users_dirname
= NULL
;
86 static int filename_len
;
87 int isdir
= 1, isexec
= 0;
89 struct dirent
*entry
= NULL
;
91 /* If we're starting the match process, initialize us a bit. */
100 g_free (users_dirname
);
102 filename
= g_strdup (text
);
105 dirname
= g_strdup (text
);
107 temp
= strrchr (dirname
, PATH_SEP
);
110 strcpy (filename
, ++temp
);
114 strcpy (dirname
, ".");
116 /* We aren't done yet. We also support the "~user" syntax. */
118 /* Save the version of the directory that the user typed. */
119 users_dirname
= g_strdup (dirname
);
123 temp_dirname
= tilde_expand (dirname
);
126 g_free (users_dirname
);
128 dirname
= users_dirname
= filename
= NULL
;
132 dirname
= temp_dirname
;
133 canonicalize_pathname (dirname
);
134 /* Here we should do something with variable expansion
136 Maybe a dream - UNIMPLEMENTED yet. */
138 directory
= mc_opendir (dirname
);
139 filename_len
= strlen (filename
);
142 /* Now that we have some state, we can read the directory. */
144 while (directory
&& (entry
= mc_readdir (directory
))){
145 /* Special case for no filename.
146 All entries except "." and ".." match. */
148 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
151 /* Otherwise, if these match up to the length of filename, then
152 it may be a match. */
153 if ((entry
->d_name
[0] != filename
[0]) ||
154 ((NLENGTH (entry
)) < filename_len
) ||
155 strncmp (filename
, entry
->d_name
, filename_len
))
158 isdir
= 1; isexec
= 0;
160 char *tmp
= g_malloc (3 + strlen (dirname
) + NLENGTH (entry
));
161 struct stat tempstat
;
163 strcpy (tmp
, dirname
);
164 strcat (tmp
, PATH_SEP_STR
);
165 strcat (tmp
, entry
->d_name
);
166 canonicalize_pathname (tmp
);
168 if (!mc_stat (tmp
, &tempstat
)){
169 uid_t my_uid
= getuid ();
170 gid_t my_gid
= getgid ();
172 if (!S_ISDIR (tempstat
.st_mode
)){
174 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
175 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
176 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
177 (tempstat
.st_mode
& 0001))
183 switch (look_for_executables
)
188 case 1: if (!isexec
&& !isdir
)
192 if (ignore_filenames
&& !isdir
)
199 mc_closedir (directory
);
211 g_free (users_dirname
);
212 users_dirname
= NULL
;
218 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1])){
219 int dirlen
= strlen (users_dirname
);
220 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
221 strcpy (temp
, users_dirname
);
222 /* We need a `/' at the end. */
223 if (users_dirname
[dirlen
- 1] != PATH_SEP
){
224 temp
[dirlen
] = PATH_SEP
;
225 temp
[dirlen
+ 1] = 0;
227 strcat (temp
, entry
->d_name
);
229 temp
= g_malloc (2 + NLENGTH (entry
));
230 strcpy (temp
, entry
->d_name
);
233 strcat (temp
, PATH_SEP_STR
);
238 /* We assume here that text[0] == '~' , if you want to call it in another way,
239 you have to change the code */
241 char *username_completion_function (char *text
, int state
)
247 username_completion_function (char *text
, int state
)
249 static struct passwd
*entry
;
252 if (!state
){ /* Initialization stuff */
254 userlen
= strlen (text
+ 1);
256 while ((entry
= getpwent ()) != NULL
){
257 /* Null usernames should result in all users as possible completions. */
260 else if (text
[1] == entry
->pw_name
[0] &&
261 !strncmp (text
+ 1, entry
->pw_name
, userlen
))
269 char *temp
= g_malloc (3 + strlen (entry
->pw_name
));
272 strcpy (temp
+ 1, entry
->pw_name
);
273 strcat (temp
, PATH_SEP_STR
);
278 extern char **environ
;
281 /* We assume text [0] == '$' and want to have a look at text [1], if it is
282 equal to '{', so that we should append '}' at the end */
284 variable_completion_function (char *text
, int state
)
287 static int varlen
, isbrace
;
290 if (!state
){ /* Initialization stuff */
291 isbrace
= (text
[1] == '{');
292 varlen
= strlen (text
+ 1 + isbrace
);
297 p
= strchr (*env_p
, '=');
298 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
306 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
311 strncpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
313 strcpy (temp
+ 2 + (p
- *env_p
), "}");
315 temp
[1 + p
- *env_p
] = 0;
321 #define whitespace(c) ((c) == ' ' || (c) == '\t')
322 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
324 static char **hosts
= NULL
;
325 static char **hosts_p
= NULL
;
326 static int hosts_alloclen
= 0;
327 static void fetch_hosts (char *filename
)
329 FILE *file
= fopen (filename
, "r");
330 char *temp
, buffer
[256], *name
;
331 register int i
, start
;
336 while ((temp
= fgets (buffer
, 255, file
)) != NULL
){
337 /* Skip to first character. */
338 for (i
= 0; buffer
[i
] && cr_whitespace (buffer
[i
]); i
++);
339 /* Ignore comments... */
340 if (buffer
[i
] == '#')
342 /* Handle $include. */
343 if (!strncmp (buffer
+ i
, "$include ", 9)){
344 char *includefile
= buffer
+ i
+ 9;
347 /* Find start of filename. */
348 while (*includefile
&& whitespace (*includefile
))
352 /* Find end of filename. */
353 while (*t
&& !cr_whitespace (*t
))
357 fetch_hosts (includefile
);
362 for (; buffer
[i
] && !cr_whitespace (buffer
[i
]); i
++);
364 /* Get the host names separated by white space. */
365 while (buffer
[i
] && buffer
[i
] != '#'){
366 for (; i
&& cr_whitespace (buffer
[i
]); i
++);
367 if (buffer
[i
] == '#')
369 for (start
= i
; buffer
[i
] && !cr_whitespace (buffer
[i
]); i
++);
372 name
= (char *) g_malloc (i
- start
+ 1);
373 strncpy (name
, buffer
+ start
, i
- start
);
374 name
[i
- start
] = 0;
378 if (hosts_p
- hosts
>= hosts_alloclen
){
379 int j
= hosts_p
- hosts
;
381 hosts
= g_realloc ((void *)hosts
, ((hosts_alloclen
+= 30) + 1) * sizeof (char *));
384 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
385 if (!strcmp (name
, *host_p
))
386 break; /* We do not want any duplicates */
387 if (host_p
== hosts_p
){
399 hostname_completion_function (char *text
, int state
)
401 static char **host_p
;
402 static int textstart
, textlen
;
404 if (!state
){ /* Initialization stuff */
408 for (host_p
= hosts
; *host_p
; host_p
++)
412 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
415 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
417 textstart
= (*text
== '@') ? 1 : 0;
418 textlen
= strlen (text
+ textstart
);
423 break; /* Match all of them */
424 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
430 for (host_p
= hosts
; *host_p
; host_p
++)
436 char *temp
= g_malloc (2 + strlen (*host_p
));
440 strcpy (temp
+ textstart
, *host_p
);
447 * This is the function to call when the word to complete is in a position
448 * where a command word can be found. It looks around $PATH, looking for
449 * commands that match. It also scans aliases, function names, and the
450 * table of shell built-ins.
453 command_completion_function (char *text
, int state
)
455 static int isabsolute
;
460 static char *cur_path
;
461 static char *cur_word
;
462 static int init_state
;
463 static char *bash_reserved
[] = { "if", "then", "else", "elif", "fi",
464 "case", "esac", "for", "select", "while",
465 "until", "do", "done", "in", "function" , 0};
466 static char *bash_builtins
[] = { "alias", "bg", "bind", "break", "builtin",
467 "cd", "command", "continue", "declare",
468 "dirs", "echo", "enable", "eval", "exec",
469 "exit", "export", "fc", "fg", "getopts",
470 "hash", "help", "history", "jobs", "kill",
471 "let", "local", "logout", "popd", "pushd",
472 "pwd", "read", "readonly", "return", "set",
473 "shift", "source", "suspend", "test",
474 "times", "trap", "type", "typeset",
475 "ulimit", "umask", "unalias", "unset",
479 if (!state
){ /* Initialize us a little bit */
480 isabsolute
= strchr (text
, PATH_SEP
) != 0;
481 look_for_executables
= isabsolute
? 1 : 2;
483 words
= bash_reserved
;
485 text_len
= strlen (text
);
490 path
= g_malloc (strlen (p
) + 2);
492 path
[strlen (p
) + 1] = 0;
493 p
= strchr (path
, PATH_ENV_SEP
);
496 p
= strchr (p
+ 1, PATH_ENV_SEP
);
503 p
= filename_completion_function (text
, state
);
505 look_for_executables
= 0;
511 case 0: /* Reserved words */
513 if (!strncmp (*words
, text
, text_len
))
514 return g_strdup (*(words
++));
518 words
= bash_builtins
;
519 case 1: /* Builtin commands */
521 if (!strncmp (*words
, text
, text_len
))
522 return g_strdup (*(words
++));
530 case 2: /* And looking through the $PATH */
537 expanded
= tilde_expand (cur_path
);
543 p
= canonicalize_pathname (expanded
);
544 cur_word
= g_malloc (strlen (p
) + 2 + text_len
);
545 strcpy (cur_word
, p
);
546 if (cur_word
[strlen (cur_word
) - 1] != PATH_SEP
)
547 strcat (cur_word
, PATH_SEP_STR
);
548 strcat (cur_word
, text
);
550 cur_path
= strchr (cur_path
, 0) + 1;
553 found
= filename_completion_function (cur_word
, state
- init_state
);
562 look_for_executables
= 0;
567 if ((p
= strrchr (found
, PATH_SEP
)) != NULL
){
578 match_compare (const void *a
, const void *b
)
580 return strcmp (*(char **)a
, *(char **)b
);
583 /* Returns an array of char * matches with the longest common denominator
584 in the 1st entry. Then a NULL terminated list of different possible
586 You have to supply your own CompletionFunction with the word you
587 want to complete as the first argument and an count of previous matches
589 In case no matches were found we return NULL. */
591 completion_matches (char *text
, CompletionFunction entry_function
)
593 /* Number of slots in match_list. */
596 /* The list of matches. */
597 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
599 /* Number of matches actually found. */
602 /* Temporary string binder. */
605 match_list
[1] = NULL
;
607 while ((string
= (*entry_function
) (text
, matches
)) != NULL
){
608 if (matches
+ 1 == match_list_size
)
609 match_list
= (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
610 match_list
[++matches
] = string
;
611 match_list
[matches
+ 1] = NULL
;
614 /* If there were any matches, then look through them finding out the
615 lowest common denominator. That then becomes match_list[0]. */
619 int low
= 4096; /* Count of max-matched characters. */
621 /* If only one match, just use that. */
623 match_list
[0] = match_list
[1];
624 match_list
[1] = (char *)NULL
;
628 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
630 /* And compare each member of the list with
631 the next, finding out where they stop matching.
632 If we find two equal strings, we have to put one away... */
635 while (j
< matches
+ 1)
637 register int c1
, c2
, si
;
639 for (si
= 0;(c1
= match_list
[i
][si
]) && (c2
= match_list
[j
][si
]); si
++)
642 if (!c1
&& !match_list
[j
][si
]){ /* Two equal strings */
643 g_free (match_list
[j
]);
648 if (low
> si
) low
= si
;
649 if (i
+ 1 != j
) /* So there's some gap */
650 match_list
[i
+ 1] = match_list
[j
];
654 match_list
[matches
+ 1] = NULL
;
655 match_list
[0] = g_malloc (low
+ 1);
656 strncpy (match_list
[0], match_list
[1], low
);
657 match_list
[0][low
] = 0;
659 } else { /* There were no matches. */
667 check_is_cd (char *text
, int start
, int flags
)
669 char *p
, *q
= text
+ start
;
671 for (p
= text
; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); p
++);
672 if (((flags
& INPUT_COMPLETE_COMMANDS
) &&
673 !strncmp (p
, "cd", 2) && (p
[2] == ' ' || p
[2] == '\t') &&
675 (flags
& INPUT_COMPLETE_CD
))
680 /* Returns an array of matches, or NULL if none. */
682 try_complete (char *text
, int *start
, int *end
, int flags
)
684 int in_command_position
= 0, i
;
686 char **matches
= NULL
;
687 char *command_separator_chars
= ";|&{(`";
688 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
689 int is_cd
= check_is_cd (text
, *start
, flags
);
691 ignore_filenames
= 0;
694 word
= g_strdup (text
+ *start
);
697 /* Determine if this could be a command word. It is if it appears at
698 the start of the line (ignoring preceding whitespace), or if it
699 appears after a character that separates commands. And we have to
700 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
701 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
)){
703 while (i
> -1 && (text
[i
] == ' ' || text
[i
] == '\t'))
706 in_command_position
++;
707 else if (strchr (command_separator_chars
, text
[i
])){
708 register int this_char
, prev_char
;
710 in_command_position
++;
713 /* Handle the two character tokens `>&', `<&', and `>|'.
714 We are not in a command position after one of these. */
716 prev_char
= text
[i
- 1];
718 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>')) ||
719 (this_char
== '|' && prev_char
== '>'))
720 in_command_position
= 0;
721 else if (i
> 0 && text
[i
-1] == '\\') /* Quoted */
722 in_command_position
= 0;
727 if (flags
& INPUT_COMPLETE_COMMANDS
)
728 p
= strrchr (word
, '`');
729 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
730 q
= strrchr (word
, '$');
731 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
732 r
= strrchr (word
, '@');
733 if (q
&& q
[1] == '(' && INPUT_COMPLETE_COMMANDS
){
739 /* Command substitution? */
741 matches
= completion_matches (p
+ 1, command_completion_function
);
743 *start
+= p
+ 1 - word
;
747 else if (q
> p
&& q
> r
){
748 matches
= completion_matches (q
, variable_completion_function
);
753 /* Starts with '@', then look through the known hostnames for
755 else if (r
> p
&& r
> q
){
756 matches
= completion_matches (r
, hostname_completion_function
);
761 /* Starts with `~' and there is no slash in the word, then
762 try completing this word as a username. */
763 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
764 matches
= completion_matches (word
, username_completion_function
);
767 /* And finally if this word is in a command position, then
768 complete over possible command names, including aliases, functions,
769 and command names. */
770 if (!matches
&& in_command_position
)
771 matches
= completion_matches (word
, command_completion_function
);
773 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
)){
775 ignore_filenames
= 1;
776 matches
= completion_matches (word
, filename_completion_function
);
777 ignore_filenames
= 0;
778 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~'){
779 char *p
, *q
= text
+ *start
;
781 for (p
= text
; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); p
++);
782 if (!strncmp (p
, "cd", 2))
783 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); p
++);
785 char *cdpath
= getenv ("CDPATH");
792 while (!matches
&& c
== ':'){
793 s
= strchr (cdpath
, ':');
795 s
= strchr (cdpath
, 0);
799 r
= concat_dir_and_file (cdpath
, word
);
800 ignore_filenames
= 1;
801 matches
= completion_matches (r
, filename_completion_function
);
802 ignore_filenames
= 0;
818 void free_completions (WInput
*in
)
822 if (!in
->completions
)
824 for (p
=in
->completions
; *p
; p
++)
826 g_free (in
->completions
);
827 in
->completions
= NULL
;
830 static int query_height
, query_width
;
831 static WInput
*input
;
832 static int start
, end
, min_end
;
834 static int insert_text (WInput
*in
, char *text
, int len
)
836 len
= min (len
, strlen (text
)) + start
- end
;
837 if (strlen (in
->buffer
) + len
>= in
->current_max_len
){
838 /* Expand the buffer */
839 char *narea
= g_realloc (in
->buffer
, in
->current_max_len
+ len
+ in
->field_len
);
842 in
->current_max_len
+= len
+ in
->field_len
;
845 if (strlen (in
->buffer
)+1 < in
->current_max_len
){
847 int i
, l
= strlen (&in
->buffer
[end
]);
848 for (i
= l
+ 1; i
>= 0; i
--)
849 in
->buffer
[end
+ len
+ i
] = in
->buffer
[end
+ i
];
851 char *p
= in
->buffer
+ end
+ len
, *q
= in
->buffer
+ end
;
856 strncpy (in
->buffer
+ start
, text
, len
- start
+ end
);
858 update_input (in
, 1);
864 static int query_callback (Dlg_head
* h
, int Par
, int Msg
)
869 attrset (COLOR_NORMAL
);
871 draw_box (h
, 0, 0, query_height
, query_width
);
893 e1
= e
= ((WListbox
*)(h
->current
->widget
))->list
;
895 if (!strncmp (input
->buffer
+ start
, e1
->text
, end
- start
- 1)){
896 listbox_select_entry((WListbox
*)(h
->current
->widget
), e1
);
897 handle_char (input
, Par
);
899 send_message (h
, h
->current
->widget
,
909 if (Par
> 0xff || !is_printable (Par
)){
910 if (is_in_input_map (input
, Par
) == 2){
913 h
->ret_value
= B_USER
; /* This means we want to refill the
914 list box and start again */
923 char *last_text
= NULL
;
925 e1
= e
= ((WListbox
*)(h
->current
->widget
))->list
;
927 if (!strncmp (input
->buffer
+ start
, e1
->text
, end
- start
)){
928 if (e1
->text
[end
- start
] == Par
){
930 register int c1
, c2
, si
;
932 for (si
= end
- start
+ 1;
933 (c1
= last_text
[si
]) &&
934 (c2
= e1
->text
[si
]); si
++)
939 last_text
= e1
->text
;
943 listbox_select_entry((WListbox
*)(h
->current
->widget
), e1
);
944 last_text
= e1
->text
;
950 if (need_redraw
== 2){
951 insert_text (input
, last_text
, low
);
952 send_message (h
, h
->current
->widget
,WIDGET_DRAW
,0);
953 } else if (need_redraw
== 1){
954 h
->ret_value
= B_ENTER
;
965 static int querylist_callback (void *data
)
970 #define DO_INSERTION 1
972 /* Returns 1 if the user would like to see us again */
974 complete_engine (WInput
*in
, int what_to_do
)
976 if (in
->completions
&& in
->point
!= end
)
977 free_completions (in
);
978 if (!in
->completions
){
980 for (start
= end
? end
- 1 : 0; start
> -1; start
--)
981 if (strchr (" \t;|<>", in
->buffer
[start
]))
985 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
987 if (in
->completions
){
988 if (what_to_do
& DO_INSERTION
) {
989 if (insert_text (in
, in
->completions
[0], strlen (in
->completions
[0]))){
990 if (in
->completions
[1])
993 free_completions (in
);
997 /* FIXME: evil evil evil. We do not go into the query completion engine
998 * because we do not have a Gtk dialog for it. Gtk-ted does not like
999 * this; if we enable this code, it will crash.
1002 if ((what_to_do
& DO_QUERY
) && in
->completions
[1]) {
1003 int maxlen
= 0, i
, count
= 0;
1005 int start_x
, start_y
;
1007 Dlg_head
*query_dlg
;
1008 WListbox
*query_list
;
1010 for (p
=in
->completions
+ 1; *p
; count
++, p
++)
1011 if ((i
= strlen (*p
)) > maxlen
)
1013 start_x
= in
->widget
.x
;
1014 start_y
= in
->widget
.y
;
1015 if (start_y
- 2 >= count
) {
1016 y
= start_y
- 2 - count
;
1019 if (start_y
>= LINES
- start_y
- 1) {
1024 h
= LINES
- start_y
- 1;
1027 x
= start
- in
->first_shown
- 2 + start_x
;
1039 query_dlg
= create_dlg (y
, x
, query_height
, query_width
,
1040 dialog_colors
, query_callback
,
1041 "[Completion-query]", "complete", DLG_NONE
);
1042 query_list
= listbox_new (1, 1, w
- 2, h
- 2, 0, querylist_callback
, NULL
);
1043 add_widget (query_dlg
, query_list
);
1044 for (p
= in
->completions
+ 1; *p
; p
++)
1045 listbox_add_item (query_list
, 0, 0, *p
, NULL
);
1046 run_dlg (query_dlg
);
1048 if (query_dlg
->ret_value
== B_ENTER
){
1049 listbox_get_current (query_list
, &q
, NULL
);
1051 insert_text (in
, q
, strlen (q
));
1053 if (q
|| end
!= min_end
)
1054 free_completions (in
);
1055 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1056 destroy_dlg (query_dlg
);
1061 if (what_to_do
& DO_QUERY
)
1069 void complete (WInput
*in
)
1071 if (in
->completions
)
1072 while (complete_engine (in
, DO_QUERY
));
1073 else if (show_all_if_ambiguous
){
1074 complete_engine (in
, DO_INSERTION
);
1075 while (complete_engine (in
, DO_QUERY
));
1077 complete_engine (in
, DO_INSERTION
);