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>
43 #include "key.h" /* XCTRL and ALT macros */
45 typedef char *CompletionFunction (char * text
, int state
, INPUT_COMPLETE_FLAGS flags
);
47 //#define DO_COMPLETION_DEBUG
48 #ifdef DO_COMPLETION_DEBUG
50 * Useful to print/debug completion flags
52 static const char * show_c_flags(INPUT_COMPLETE_FLAGS flags
)
54 static char s_cf
[] = "FHCVUDS";
56 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) ? 'F' : ' ';
57 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) ? 'H' : ' ';
58 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) ? 'C' : ' ';
59 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) ? 'V' : ' ';
60 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) ? 'U' : ' ';
61 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) ? 'D' : ' ';
62 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) ? 'S' : ' ';
66 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
68 #define SHOW_C_CTX(func)
69 #endif /* DO_CMPLETION_DEBUG */
72 filename_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
74 static DIR *directory
;
75 static char *filename
= NULL
;
76 static char *dirname
= NULL
;
77 static char *users_dirname
= NULL
;
78 static size_t filename_len
;
79 int isdir
= 1, isexec
= 0;
81 struct dirent
*entry
= NULL
;
83 SHOW_C_CTX("filename_completion_function");
85 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
86 text
= shell_unescape (text
);
88 /* If we're starting the match process, initialize us a bit. */
94 g_free (users_dirname
);
96 if ((*text
) && (temp
= strrchr (text
, PATH_SEP
))){
97 filename
= g_strdup (++temp
);
98 dirname
= g_strndup (text
, temp
- text
);
100 dirname
= g_strdup (".");
101 filename
= g_strdup (text
);
104 /* We aren't done yet. We also support the "~user" syntax. */
106 /* Save the version of the directory that the user typed. */
107 users_dirname
= dirname
;
110 dirname
= tilde_expand (dirname
);
111 canonicalize_pathname (dirname
);
112 /* Here we should do something with variable expansion
114 Maybe a dream - UNIMPLEMENTED yet. */
116 directory
= mc_opendir (dirname
);
117 filename_len
= strlen (filename
);
120 /* Now that we have some state, we can read the directory. */
122 while (directory
&& (entry
= mc_readdir (directory
))){
123 /* Special case for no filename.
124 All entries except "." and ".." match. */
126 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
129 /* Otherwise, if these match up to the length of filename, then
130 it may be a match. */
131 if ((entry
->d_name
[0] != filename
[0]) ||
132 ((NLENGTH (entry
)) < filename_len
) ||
133 strncmp (filename
, entry
->d_name
, filename_len
))
136 isdir
= 1; isexec
= 0;
138 char *tmp
= g_malloc (3 + strlen (dirname
) + NLENGTH (entry
));
139 struct stat tempstat
;
141 strcpy (tmp
, dirname
);
142 strcat (tmp
, PATH_SEP_STR
);
143 strcat (tmp
, entry
->d_name
);
144 canonicalize_pathname (tmp
);
146 if (!mc_stat (tmp
, &tempstat
)){
147 uid_t my_uid
= getuid ();
148 gid_t my_gid
= getgid ();
150 if (!S_ISDIR (tempstat
.st_mode
)){
152 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
153 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
154 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
155 (tempstat
.st_mode
& 0001))
161 if ((flags
& INPUT_COMPLETE_COMMANDS
)
162 && (isexec
|| isdir
))
164 if ((flags
& INPUT_COMPLETE_CD
)
167 if (flags
& (INPUT_COMPLETE_FILENAMES
))
174 mc_closedir (directory
);
181 g_free (users_dirname
);
182 users_dirname
= NULL
;
187 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1])){
188 int dirlen
= strlen (users_dirname
);
189 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
190 strcpy (temp
, users_dirname
);
191 /* We need a `/' at the end. */
192 if (users_dirname
[dirlen
- 1] != PATH_SEP
){
193 temp
[dirlen
] = PATH_SEP
;
194 temp
[dirlen
+ 1] = 0;
196 strcat (temp
, entry
->d_name
);
198 temp
= g_malloc (2 + NLENGTH (entry
));
199 strcpy (temp
, entry
->d_name
);
202 strcat (temp
, PATH_SEP_STR
);
204 if (temp
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
206 SHELL_ESCAPED_STR e_temp
= shell_escape(temp
);
214 /* We assume here that text[0] == '~' , if you want to call it in another way,
215 you have to change the code */
217 username_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
219 static struct passwd
*entry
;
222 SHOW_C_CTX("username_completion_function");
224 if (text
[0] == '\\' && text
[1] == '~') text
++;
225 if (!state
){ /* Initialization stuff */
227 userlen
= strlen (text
+ 1);
229 while ((entry
= getpwent ()) != NULL
){
230 /* Null usernames should result in all users as possible completions. */
233 else if (text
[1] == entry
->pw_name
[0] &&
234 !strncmp (text
+ 1, entry
->pw_name
, userlen
))
242 char *temp
= g_malloc (3 + strlen (entry
->pw_name
));
245 strcpy (temp
+ 1, entry
->pw_name
);
246 strcat (temp
, PATH_SEP_STR
);
251 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
252 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
253 extern char **environ
;
256 /* We assume text [0] == '$' and want to have a look at text [1], if it is
257 equal to '{', so that we should append '}' at the end */
259 variable_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
262 static int varlen
, isbrace
;
263 const char *p
= NULL
;
265 SHOW_C_CTX("variable_completion_function");
267 if (!state
){ /* Initialization stuff */
268 isbrace
= (text
[1] == '{');
269 varlen
= strlen (text
+ 1 + isbrace
);
274 p
= strchr (*env_p
, '=');
275 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
283 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
288 memcpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
290 strcpy (temp
+ 2 + (p
- *env_p
), "}");
292 temp
[1 + p
- *env_p
] = 0;
298 #define whitespace(c) ((c) == ' ' || (c) == '\t')
299 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
301 static char **hosts
= NULL
;
302 static char **hosts_p
= NULL
;
303 static int hosts_alloclen
= 0;
304 static void fetch_hosts (const char *filename
)
306 FILE *file
= fopen (filename
, "r");
307 char buffer
[256], *name
;
308 register int i
, start
;
313 while (fgets (buffer
, 255, file
) != NULL
){
314 /* Skip to first character. */
315 for (i
= 0; buffer
[i
] && cr_whitespace (buffer
[i
]); i
++);
316 /* Ignore comments... */
317 if (buffer
[i
] == '#')
319 /* Handle $include. */
320 if (!strncmp (buffer
+ i
, "$include ", 9)){
321 char *includefile
= buffer
+ i
+ 9;
324 /* Find start of filename. */
325 while (*includefile
&& whitespace (*includefile
))
329 /* Find end of filename. */
330 while (*t
&& !cr_whitespace (*t
))
334 fetch_hosts (includefile
);
339 while (buffer
[i
] && !cr_whitespace (buffer
[i
]))
342 /* Get the host names separated by white space. */
343 while (buffer
[i
] && buffer
[i
] != '#'){
344 while (buffer
[i
] && cr_whitespace (buffer
[i
]))
346 if (buffer
[i
] == '#')
348 for (start
= i
; buffer
[i
] && !cr_whitespace (buffer
[i
]); i
++);
351 name
= g_strndup (buffer
+ start
, i
- start
);
355 if (hosts_p
- hosts
>= hosts_alloclen
){
356 int j
= hosts_p
- hosts
;
358 hosts
= g_realloc ((void *)hosts
, ((hosts_alloclen
+= 30) + 1) * sizeof (char *));
361 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
362 if (!strcmp (name
, *host_p
))
363 break; /* We do not want any duplicates */
364 if (host_p
== hosts_p
){
376 hostname_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
378 static char **host_p
;
379 static int textstart
, textlen
;
381 SHOW_C_CTX("hostname_completion_function");
383 if (!state
){ /* Initialization stuff */
387 for (host_p
= hosts
; *host_p
; host_p
++)
391 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
394 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
396 textstart
= (*text
== '@') ? 1 : 0;
397 textlen
= strlen (text
+ textstart
);
402 break; /* Match all of them */
403 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
409 for (host_p
= hosts
; *host_p
; host_p
++)
415 char *temp
= g_malloc (2 + strlen (*host_p
));
419 strcpy (temp
+ textstart
, *host_p
);
426 * This is the function to call when the word to complete is in a position
427 * where a command word can be found. It looks around $PATH, looking for
428 * commands that match. It also scans aliases, function names, and the
429 * table of shell built-ins.
432 command_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
434 static const char *path_end
;
435 static int isabsolute
;
438 static const char *const *words
;
440 static char *cur_path
;
441 static char *cur_word
;
442 static int init_state
;
443 static const char *const bash_reserved
[] = {
444 "if", "then", "else", "elif", "fi", "case", "esac", "for",
445 "select", "while", "until", "do", "done", "in", "function", 0
447 static const char *const bash_builtins
[] = {
448 "alias", "bg", "bind", "break", "builtin", "cd", "command",
449 "continue", "declare", "dirs", "echo", "enable", "eval",
450 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
451 "help", "history", "jobs", "kill", "let", "local", "logout",
452 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
453 "shift", "source", "suspend", "test", "times", "trap", "type",
454 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
458 SHOW_C_CTX("command_completion_function");
460 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
463 text
= shell_unescape(text
);
464 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
466 if (!state
) { /* Initialize us a little bit */
467 isabsolute
= strchr (text
, PATH_SEP
) != 0;
469 words
= bash_reserved
;
471 text_len
= strlen (text
);
472 if (!path
&& (path
= g_strdup (getenv ("PATH"))) != NULL
) {
474 path_end
= strchr (p
, 0);
475 while ((p
= strchr (p
, PATH_ENV_SEP
))) {
483 p
= filename_completion_function (text
, state
, flags
);
486 SHELL_ESCAPED_STR e_p
= shell_escape(p
);
493 case 0: /* Reserved words */
495 if (!strncmp (*words
, text
, text_len
))
496 return g_strdup (*(words
++));
500 words
= bash_builtins
;
501 case 1: /* Builtin commands */
503 if (!strncmp (*words
, text
, text_len
))
504 return g_strdup (*(words
++));
512 case 2: /* And looking through the $PATH */
517 if (cur_path
>= path_end
)
519 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
520 cur_word
= concat_dir_and_file (expanded
, text
);
522 canonicalize_pathname (cur_word
);
523 cur_path
= strchr (cur_path
, 0) + 1;
527 filename_completion_function (cur_word
,
528 state
- init_state
, flags
);
541 if ((p
= strrchr (found
, PATH_SEP
)) != NULL
) {
544 SHELL_ESCAPED_STR e_p
= shell_escape(p
);
553 match_compare (const void *a
, const void *b
)
555 return strcmp (*(char **)a
, *(char **)b
);
558 /* Returns an array of char * matches with the longest common denominator
559 in the 1st entry. Then a NULL terminated list of different possible
561 You have to supply your own CompletionFunction with the word you
562 want to complete as the first argument and an count of previous matches
564 In case no matches were found we return NULL. */
566 completion_matches (char *text
, CompletionFunction entry_function
, INPUT_COMPLETE_FLAGS flags
)
568 /* Number of slots in match_list. */
571 /* The list of matches. */
572 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
574 /* Number of matches actually found. */
577 /* Temporary string binder. */
580 match_list
[1] = NULL
;
582 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
){
583 if (matches
+ 1 == match_list_size
)
584 match_list
= (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
585 match_list
[++matches
] = string
;
586 match_list
[matches
+ 1] = NULL
;
589 /* If there were any matches, then look through them finding out the
590 lowest common denominator. That then becomes match_list[0]. */
594 int low
= 4096; /* Count of max-matched characters. */
596 /* If only one match, just use that. */
598 match_list
[0] = match_list
[1];
599 match_list
[1] = NULL
;
603 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
605 /* And compare each member of the list with
606 the next, finding out where they stop matching.
607 If we find two equal strings, we have to put one away... */
610 while (j
< matches
+ 1)
612 register int c1
, c2
, si
;
614 for (si
= 0;(c1
= match_list
[i
][si
]) && (c2
= match_list
[j
][si
]); si
++)
617 if (!c1
&& !match_list
[j
][si
]){ /* Two equal strings */
618 g_free (match_list
[j
]);
622 continue; /* Look for a run of equal strings */
624 if (low
> si
) low
= si
;
625 if (i
+ 1 != j
) /* So there's some gap */
626 match_list
[i
+ 1] = match_list
[j
];
630 match_list
[matches
+ 1] = NULL
;
631 match_list
[0] = g_strndup(match_list
[1], low
);
633 } else { /* There were no matches. */
640 /* Check if directory completion is needed */
642 check_is_cd (const char *text
, int start
, INPUT_COMPLETE_FLAGS flags
)
646 SHOW_C_CTX("check_is_cd");
647 if (!(flags
& INPUT_COMPLETE_CD
))
650 /* Skip initial spaces */
653 while (p
< q
&& *p
&& isspace ((unsigned char) *p
))
656 /* Check if the command is "cd" and the cursor is after it */
657 if (p
[0] == 'c' && p
[1] == 'd' && isspace ((unsigned char) p
[2])
664 /* Returns an array of matches, or NULL if none. */
666 try_complete (char *text
, int *start
, int *end
, INPUT_COMPLETE_FLAGS flags
)
668 int in_command_position
= 0;
670 char **matches
= NULL
;
671 const char *command_separator_chars
= ";|&{(`";
672 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
673 int is_cd
= check_is_cd (text
, *start
, flags
);
675 SHOW_C_CTX("try_complete");
679 word
= g_strdup (text
+ *start
);
682 /* Determine if this could be a command word. It is if it appears at
683 the start of the line (ignoring preceding whitespace), or if it
684 appears after a character that separates commands. And we have to
685 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
686 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
)){
688 for (i
= *start
- 1; i
> -1; i
--) {
689 if (text
[i
] == ' ' || text
[i
] == '\t'){
690 if (i
== 0 ) continue;
691 if (text
[i
-1] != '\\') {
698 in_command_position
++;
699 else if (strchr (command_separator_chars
, text
[i
])){
700 register int this_char
, prev_char
;
702 in_command_position
++;
705 /* Handle the two character tokens `>&', `<&', and `>|'.
706 We are not in a command position after one of these. */
708 prev_char
= text
[i
- 1];
710 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>')) ||
711 (this_char
== '|' && prev_char
== '>'))
712 in_command_position
= 0;
713 else if (i
> 0 && text
[i
-1] == '\\') /* Quoted */
714 in_command_position
= 0;
719 if (flags
& INPUT_COMPLETE_COMMANDS
)
720 p
= strrchr (word
, '`');
721 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
722 q
= strrchr (word
, '$');
723 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
724 r
= strrchr (word
, '@');
725 if (q
&& q
[1] == '(' && INPUT_COMPLETE_COMMANDS
){
731 /* Command substitution? */
733 SHOW_C_CTX("try_complete:cmd_backq_subst");
734 matches
= completion_matches (p
+ 1, command_completion_function
, flags
& (~INPUT_COMPLETE_FILENAMES
));
736 *start
+= p
+ 1 - word
;
740 else if (q
> p
&& q
> r
){
741 SHOW_C_CTX("try_complete:var_subst");
742 matches
= completion_matches (q
, variable_completion_function
, flags
);
747 /* Starts with '@', then look through the known hostnames for
749 else if (r
> p
&& r
> q
){
750 SHOW_C_CTX("try_complete:host_subst");
751 matches
= completion_matches (r
, hostname_completion_function
, flags
);
756 /* Starts with `~' and there is no slash in the word, then
757 try completing this word as a username. */
758 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
760 SHOW_C_CTX("try_complete:user_subst");
761 matches
= completion_matches (word
, username_completion_function
, flags
);
765 /* And finally if this word is in a command position, then
766 complete over possible command names, including aliases, functions,
767 and command names. */
768 if (!matches
&& in_command_position
)
770 SHOW_C_CTX("try_complete:cmd_subst");
771 matches
= completion_matches (word
, command_completion_function
, flags
& (~INPUT_COMPLETE_FILENAMES
));
774 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
)){
776 flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
777 SHOW_C_CTX("try_complete:filename_subst_1");
778 matches
= completion_matches (word
, filename_completion_function
, flags
);
779 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~'){
780 char *p
, *q
= text
+ *start
;
782 for (p
= text
; *p
&& p
< q
; p
++){
783 if (*p
== ' ' || *p
== '\t') {
784 if (p
== text
) continue;
785 if (*(p
-1) == '\\') {
791 if (!strncmp (p
, "cd", 2))
792 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); p
++){
793 if (p
== text
) continue;
794 if (*(p
-1) == '\\') {
800 char * const cdpath_ref
= g_strdup (getenv ("CDPATH"));
801 char *cdpath
= cdpath_ref
;
808 while (!matches
&& c
== ':'){
809 s
= strchr (cdpath
, ':');
811 s
= strchr (cdpath
, 0);
815 r
= concat_dir_and_file (cdpath
, word
);
816 ignore_filenames
= 1;
817 matches
= completion_matches (r
, filename_completion_function
);
818 ignore_filenames
= 0;
834 void free_completions (WInput
*in
)
838 if (!in
->completions
)
840 for (p
=in
->completions
; *p
; p
++)
842 g_free (in
->completions
);
843 in
->completions
= NULL
;
846 static int query_height
, query_width
;
847 static WInput
*input
;
849 static int start
, end
;
851 static int insert_text (WInput
*in
, char *text
, ssize_t len
)
853 len
= min (len
, (ssize_t
) strlen (text
)) + start
- end
;
854 if (strlen (in
->buffer
) + len
>= (size_t) in
->current_max_len
){
855 /* Expand the buffer */
856 char *narea
= g_realloc (in
->buffer
, in
->current_max_len
+ len
+ in
->field_len
);
859 in
->current_max_len
+= len
+ in
->field_len
;
862 if (strlen (in
->buffer
)+1 < (size_t) in
->current_max_len
){
864 int i
= strlen (&in
->buffer
[end
]);
866 in
->buffer
[end
+ len
+ i
] = in
->buffer
[end
+ i
];
868 char *p
= in
->buffer
+ end
+ len
, *q
= in
->buffer
+ end
;
873 memcpy (in
->buffer
+ start
, text
, len
- start
+ end
);
875 update_input (in
, 1);
882 query_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
894 if (end
== min_end
) {
901 e1
= e
= ((WListbox
*) (h
->current
))->list
;
904 (input
->buffer
+ start
, e1
->text
,
906 listbox_select_entry ((WListbox
*) (h
->current
),
908 handle_char (input
, parm
);
910 send_message (h
->current
, WIDGET_DRAW
, 0);
919 if (parm
> 0xff || !is_printable (parm
)) {
920 if (is_in_input_map (input
, parm
) == 2) {
923 h
->ret_value
= B_USER
; /* This means we want to refill the
924 list box and start again */
928 return MSG_NOT_HANDLED
;
933 char *last_text
= NULL
;
935 e1
= e
= ((WListbox
*) (h
->current
))->list
;
938 (input
->buffer
+ start
, e1
->text
, end
- start
)) {
939 if (e1
->text
[end
- start
] == parm
) {
941 register int c1
, c2
, si
;
943 for (si
= end
- start
+ 1;
945 && (c2
= e1
->text
[si
]); si
++)
950 last_text
= e1
->text
;
954 listbox_select_entry ((WListbox
*) (h
->
957 last_text
= e1
->text
;
963 if (need_redraw
== 2) {
964 insert_text (input
, last_text
, low
);
965 send_message (h
->current
, WIDGET_DRAW
, 0);
966 } else if (need_redraw
== 1) {
967 h
->ret_value
= B_ENTER
;
976 return default_dlg_callback (h
, msg
, parm
);
980 #define DO_INSERTION 1
982 /* Returns 1 if the user would like to see us again */
984 complete_engine (WInput
*in
, int what_to_do
)
986 if (in
->completions
&& in
->point
!= end
)
987 free_completions (in
);
988 if (!in
->completions
){
990 for (start
= end
? end
- 1 : 0; start
> -1; start
--)
991 if (strchr (" \t;|<>", in
->buffer
[start
])){
992 if (start
> 0 && in
->buffer
[start
-1] == '\\')
999 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1002 if (in
->completions
){
1003 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1])) {
1004 char * complete
= in
->completions
[0];
1005 if (insert_text (in
, complete
, strlen (complete
))){
1006 if (in
->completions
[1])
1009 free_completions (in
);
1013 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1]) {
1014 int maxlen
= 0, i
, count
= 0;
1016 int start_x
, start_y
;
1018 Dlg_head
*query_dlg
;
1019 WListbox
*query_list
;
1021 for (p
=in
->completions
+ 1; *p
; count
++, p
++) {
1022 if ((i
= strlen (*p
)) > maxlen
)
1025 start_x
= in
->widget
.x
;
1026 start_y
= in
->widget
.y
;
1027 if (start_y
- 2 >= count
) {
1028 y
= start_y
- 2 - count
;
1031 if (start_y
>= LINES
- start_y
- 1) {
1036 h
= LINES
- start_y
- 1;
1039 x
= start
- in
->first_shown
- 2 + start_x
;
1051 query_dlg
= create_dlg (y
, x
, query_height
, query_width
,
1052 dialog_colors
, query_callback
,
1053 "[Completion]", NULL
, DLG_COMPACT
);
1054 query_list
= listbox_new (1, 1, w
- 2, h
- 2, NULL
);
1055 add_widget (query_dlg
, query_list
);
1056 for (p
= in
->completions
+ 1; *p
; p
++)
1057 listbox_add_item (query_list
, 0, 0, *p
, NULL
);
1058 run_dlg (query_dlg
);
1060 if (query_dlg
->ret_value
== B_ENTER
){
1061 listbox_get_current (query_list
, &q
, NULL
);
1063 insert_text (in
, q
, strlen (q
));
1065 if (q
|| end
!= min_end
)
1066 free_completions (in
);
1067 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1068 destroy_dlg (query_dlg
);
1077 void complete (WInput
*in
)
1081 if (in
->completions
)
1082 engine_flags
= DO_QUERY
;
1085 engine_flags
= DO_INSERTION
;
1087 if (show_all_if_ambiguous
)
1088 engine_flags
|= DO_QUERY
;
1091 while (complete_engine (in
, engine_flags
));