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. */
29 #include <sys/types.h>
33 #include <mhl/memory.h>
34 #include <mhl/escape.h>
35 #include <mhl/string.h>
46 #include "key.h" /* XCTRL and ALT macros */
48 typedef char *CompletionFunction (char * text
, int state
, INPUT_COMPLETE_FLAGS flags
);
50 //#define DO_COMPLETION_DEBUG
51 #ifdef DO_COMPLETION_DEBUG
53 * Useful to print/debug completion flags
55 static const char * show_c_flags(INPUT_COMPLETE_FLAGS flags
)
57 static char s_cf
[] = "FHCVUDS";
59 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) ? 'F' : ' ';
60 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) ? 'H' : ' ';
61 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) ? 'C' : ' ';
62 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) ? 'V' : ' ';
63 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) ? 'U' : ' ';
64 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) ? 'D' : ' ';
65 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) ? 'S' : ' ';
69 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
71 #define SHOW_C_CTX(func)
72 #endif /* DO_CMPLETION_DEBUG */
75 filename_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
77 static DIR *directory
;
78 static char *filename
= NULL
;
79 static char *dirname
= NULL
;
80 static char *users_dirname
= NULL
;
81 static size_t filename_len
;
82 int isdir
= 1, isexec
= 0;
84 struct dirent
*entry
= NULL
;
86 SHOW_C_CTX("filename_completion_function");
88 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
89 text
= mhl_shell_unescape_buf (text
);
91 /* If we're starting the match process, initialize us a bit. */
97 g_free (users_dirname
);
99 if ((*text
) && (temp
= strrchr (text
, PATH_SEP
))){
100 filename
= g_strdup (++temp
);
101 dirname
= g_strndup (text
, temp
- text
);
103 dirname
= g_strdup (".");
104 filename
= g_strdup (text
);
107 /* We aren't done yet. We also support the "~user" syntax. */
109 /* Save the version of the directory that the user typed. */
110 users_dirname
= dirname
;
113 dirname
= tilde_expand (dirname
);
114 canonicalize_pathname (dirname
);
115 /* Here we should do something with variable expansion
117 Maybe a dream - UNIMPLEMENTED yet. */
119 directory
= mc_opendir (dirname
);
120 filename_len
= strlen (filename
);
123 /* Now that we have some state, we can read the directory. */
125 while (directory
&& (entry
= mc_readdir (directory
))){
126 /* Special case for no filename.
127 All entries except "." and ".." match. */
129 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
132 /* Otherwise, if these match up to the length of filename, then
133 it may be a match. */
134 if ((entry
->d_name
[0] != filename
[0]) ||
135 ((NLENGTH (entry
)) < filename_len
) ||
136 strncmp (filename
, entry
->d_name
, filename_len
))
139 isdir
= 1; isexec
= 0;
141 char *tmp
= g_malloc (3 + strlen (dirname
) + NLENGTH (entry
));
142 struct stat tempstat
;
144 strcpy (tmp
, dirname
);
145 strcat (tmp
, PATH_SEP_STR
);
146 strcat (tmp
, entry
->d_name
);
147 canonicalize_pathname (tmp
);
149 if (!mc_stat (tmp
, &tempstat
)){
150 uid_t my_uid
= getuid ();
151 gid_t my_gid
= getgid ();
153 if (!S_ISDIR (tempstat
.st_mode
)){
155 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
156 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
157 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
158 (tempstat
.st_mode
& 0001))
164 if ((flags
& INPUT_COMPLETE_COMMANDS
)
165 && (isexec
|| isdir
))
167 if ((flags
& INPUT_COMPLETE_CD
)
170 if (flags
& (INPUT_COMPLETE_FILENAMES
))
177 mc_closedir (directory
);
184 g_free (users_dirname
);
185 users_dirname
= NULL
;
190 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1])){
191 int dirlen
= strlen (users_dirname
);
192 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
193 strcpy (temp
, users_dirname
);
194 /* We need a `/' at the end. */
195 if (users_dirname
[dirlen
- 1] != PATH_SEP
){
196 temp
[dirlen
] = PATH_SEP
;
197 temp
[dirlen
+ 1] = 0;
199 strcat (temp
, entry
->d_name
);
201 temp
= g_malloc (2 + NLENGTH (entry
));
202 strcpy (temp
, entry
->d_name
);
205 strcat (temp
, PATH_SEP_STR
);
207 if (temp
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
209 SHELL_ESCAPED_STR e_temp
= mhl_shell_escape_dup(temp
);
217 /* We assume here that text[0] == '~' , if you want to call it in another way,
218 you have to change the code */
220 username_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
222 static struct passwd
*entry
;
225 SHOW_C_CTX("username_completion_function");
227 if (text
[0] == '\\' && text
[1] == '~') text
++;
228 if (!state
){ /* Initialization stuff */
230 userlen
= strlen (text
+ 1);
232 while ((entry
= getpwent ()) != NULL
){
233 /* Null usernames should result in all users as possible completions. */
236 else if (text
[1] == entry
->pw_name
[0] &&
237 !strncmp (text
+ 1, entry
->pw_name
, userlen
))
245 char *temp
= g_malloc (3 + strlen (entry
->pw_name
));
248 strcpy (temp
+ 1, entry
->pw_name
);
249 strcat (temp
, PATH_SEP_STR
);
254 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
255 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
256 extern char **environ
;
259 /* We assume text [0] == '$' and want to have a look at text [1], if it is
260 equal to '{', so that we should append '}' at the end */
262 variable_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
265 static int varlen
, isbrace
;
266 const char *p
= NULL
;
268 SHOW_C_CTX("variable_completion_function");
270 if (!state
){ /* Initialization stuff */
271 isbrace
= (text
[1] == '{');
272 varlen
= strlen (text
+ 1 + isbrace
);
277 p
= strchr (*env_p
, '=');
278 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
286 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
291 memcpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
293 strcpy (temp
+ 2 + (p
- *env_p
), "}");
295 temp
[1 + p
- *env_p
] = 0;
301 #define whitespace(c) ((c) == ' ' || (c) == '\t')
302 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
304 static char **hosts
= NULL
;
305 static char **hosts_p
= NULL
;
306 static int hosts_alloclen
= 0;
307 static void fetch_hosts (const char *filename
)
309 FILE *file
= fopen (filename
, "r");
310 char buffer
[256], *name
;
311 register int i
, start
;
316 while (fgets (buffer
, 255, file
) != NULL
){
317 /* Skip to first character. */
318 for (i
= 0; buffer
[i
] && cr_whitespace (buffer
[i
]); i
++);
319 /* Ignore comments... */
320 if (buffer
[i
] == '#')
322 /* Handle $include. */
323 if (!strncmp (buffer
+ i
, "$include ", 9)){
324 char *includefile
= buffer
+ i
+ 9;
327 /* Find start of filename. */
328 while (*includefile
&& whitespace (*includefile
))
332 /* Find end of filename. */
333 while (*t
&& !cr_whitespace (*t
))
337 fetch_hosts (includefile
);
342 while (buffer
[i
] && !cr_whitespace (buffer
[i
]))
345 /* Get the host names separated by white space. */
346 while (buffer
[i
] && buffer
[i
] != '#'){
347 while (buffer
[i
] && cr_whitespace (buffer
[i
]))
349 if (buffer
[i
] == '#')
351 for (start
= i
; buffer
[i
] && !cr_whitespace (buffer
[i
]); i
++);
354 name
= g_strndup (buffer
+ start
, i
- start
);
358 if (hosts_p
- hosts
>= hosts_alloclen
){
359 int j
= hosts_p
- hosts
;
361 hosts
= g_realloc ((void *)hosts
, ((hosts_alloclen
+= 30) + 1) * sizeof (char *));
364 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
365 if (!strcmp (name
, *host_p
))
366 break; /* We do not want any duplicates */
367 if (host_p
== hosts_p
){
379 hostname_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
381 static char **host_p
;
382 static int textstart
, textlen
;
384 SHOW_C_CTX("hostname_completion_function");
386 if (!state
){ /* Initialization stuff */
390 for (host_p
= hosts
; *host_p
; host_p
++)
394 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
397 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
399 textstart
= (*text
== '@') ? 1 : 0;
400 textlen
= strlen (text
+ textstart
);
405 break; /* Match all of them */
406 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
412 for (host_p
= hosts
; *host_p
; host_p
++)
418 char *temp
= g_malloc (2 + strlen (*host_p
));
422 strcpy (temp
+ textstart
, *host_p
);
429 * This is the function to call when the word to complete is in a position
430 * where a command word can be found. It looks around $PATH, looking for
431 * commands that match. It also scans aliases, function names, and the
432 * table of shell built-ins.
435 command_completion_function (char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
437 static const char *path_end
;
438 static int isabsolute
;
441 static const char *const *words
;
443 static char *cur_path
;
444 static char *cur_word
;
445 static int init_state
;
446 static const char *const bash_reserved
[] = {
447 "if", "then", "else", "elif", "fi", "case", "esac", "for",
448 "select", "while", "until", "do", "done", "in", "function", 0
450 static const char *const bash_builtins
[] = {
451 "alias", "bg", "bind", "break", "builtin", "cd", "command",
452 "continue", "declare", "dirs", "echo", "enable", "eval",
453 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
454 "help", "history", "jobs", "kill", "let", "local", "logout",
455 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
456 "shift", "source", "suspend", "test", "times", "trap", "type",
457 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
461 SHOW_C_CTX("command_completion_function");
463 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
466 text
= mhl_shell_unescape_buf(text
);
467 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
469 if (!state
) { /* Initialize us a little bit */
470 isabsolute
= strchr (text
, PATH_SEP
) != 0;
472 words
= bash_reserved
;
474 text_len
= strlen (text
);
475 if (!path
&& (path
= g_strdup (getenv ("PATH"))) != NULL
) {
477 path_end
= strchr (p
, 0);
478 while ((p
= strchr (p
, PATH_ENV_SEP
))) {
486 p
= filename_completion_function (text
, state
, flags
);
489 SHELL_ESCAPED_STR e_p
= mhl_shell_escape_dup(p
);
496 case 0: /* Reserved words */
498 if (!strncmp (*words
, text
, text_len
))
499 return g_strdup (*(words
++));
503 words
= bash_builtins
;
504 case 1: /* Builtin commands */
506 if (!strncmp (*words
, text
, text_len
))
507 return g_strdup (*(words
++));
515 case 2: /* And looking through the $PATH */
520 if (cur_path
>= path_end
)
522 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
523 cur_word
= mhl_str_dir_plus_file (expanded
, text
);
525 canonicalize_pathname (cur_word
);
526 cur_path
= strchr (cur_path
, 0) + 1;
530 filename_completion_function (cur_word
,
531 state
- init_state
, flags
);
544 if ((p
= strrchr (found
, PATH_SEP
)) != NULL
) {
547 SHELL_ESCAPED_STR e_p
= mhl_shell_escape_dup(p
);
556 match_compare (const void *a
, const void *b
)
558 return strcmp (*(char **)a
, *(char **)b
);
561 /* Returns an array of char * matches with the longest common denominator
562 in the 1st entry. Then a NULL terminated list of different possible
564 You have to supply your own CompletionFunction with the word you
565 want to complete as the first argument and an count of previous matches
567 In case no matches were found we return NULL. */
569 completion_matches (char *text
, CompletionFunction entry_function
, INPUT_COMPLETE_FLAGS flags
)
571 /* Number of slots in match_list. */
574 /* The list of matches. */
575 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
577 /* Number of matches actually found. */
580 /* Temporary string binder. */
583 match_list
[1] = NULL
;
585 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
){
586 if (matches
+ 1 == match_list_size
)
587 match_list
= (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
588 match_list
[++matches
] = string
;
589 match_list
[matches
+ 1] = NULL
;
592 /* If there were any matches, then look through them finding out the
593 lowest common denominator. That then becomes match_list[0]. */
597 int low
= 4096; /* Count of max-matched characters. */
599 /* If only one match, just use that. */
601 match_list
[0] = match_list
[1];
602 match_list
[1] = NULL
;
606 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
608 /* And compare each member of the list with
609 the next, finding out where they stop matching.
610 If we find two equal strings, we have to put one away... */
613 while (j
< matches
+ 1)
615 register int c1
, c2
, si
;
617 for (si
= 0;(c1
= match_list
[i
][si
]) && (c2
= match_list
[j
][si
]); si
++)
620 if (!c1
&& !match_list
[j
][si
]){ /* Two equal strings */
621 g_free (match_list
[j
]);
625 continue; /* Look for a run of equal strings */
627 if (low
> si
) low
= si
;
628 if (i
+ 1 != j
) /* So there's some gap */
629 match_list
[i
+ 1] = match_list
[j
];
633 match_list
[matches
+ 1] = NULL
;
634 match_list
[0] = g_strndup(match_list
[1], low
);
636 } else { /* There were no matches. */
643 /* Check if directory completion is needed */
645 check_is_cd (const char *text
, int start
, INPUT_COMPLETE_FLAGS flags
)
649 SHOW_C_CTX("check_is_cd");
650 if (!(flags
& INPUT_COMPLETE_CD
))
653 /* Skip initial spaces */
656 while (p
< q
&& *p
&& isspace ((unsigned char) *p
))
659 /* Check if the command is "cd" and the cursor is after it */
660 if (p
[0] == 'c' && p
[1] == 'd' && isspace ((unsigned char) p
[2])
667 /* Returns an array of matches, or NULL if none. */
669 try_complete (char *text
, int *start
, int *end
, INPUT_COMPLETE_FLAGS flags
)
671 int in_command_position
= 0;
673 char **matches
= NULL
;
674 const char *command_separator_chars
= ";|&{(`";
675 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
676 int is_cd
= check_is_cd (text
, *start
, flags
);
678 SHOW_C_CTX("try_complete");
682 word
= g_strdup (text
+ *start
);
685 /* Determine if this could be a command word. It is if it appears at
686 the start of the line (ignoring preceding whitespace), or if it
687 appears after a character that separates commands. And we have to
688 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
689 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
)){
691 for (i
= *start
- 1; i
> -1; i
--) {
692 if (text
[i
] == ' ' || text
[i
] == '\t'){
693 if (i
== 0 ) continue;
694 if (text
[i
-1] != '\\') {
701 in_command_position
++;
702 else if (strchr (command_separator_chars
, text
[i
])){
703 register int this_char
, prev_char
;
705 in_command_position
++;
708 /* Handle the two character tokens `>&', `<&', and `>|'.
709 We are not in a command position after one of these. */
711 prev_char
= text
[i
- 1];
713 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>')) ||
714 (this_char
== '|' && prev_char
== '>'))
715 in_command_position
= 0;
716 else if (i
> 0 && text
[i
-1] == '\\') /* Quoted */
717 in_command_position
= 0;
722 if (flags
& INPUT_COMPLETE_COMMANDS
)
723 p
= strrchr (word
, '`');
724 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
725 q
= strrchr (word
, '$');
726 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
727 r
= strrchr (word
, '@');
728 if (q
&& q
[1] == '(' && INPUT_COMPLETE_COMMANDS
){
734 /* Command substitution? */
736 SHOW_C_CTX("try_complete:cmd_backq_subst");
737 matches
= completion_matches (p
+ 1, command_completion_function
, flags
& (~INPUT_COMPLETE_FILENAMES
));
739 *start
+= p
+ 1 - word
;
743 else if (q
> p
&& q
> r
){
744 SHOW_C_CTX("try_complete:var_subst");
745 matches
= completion_matches (q
, variable_completion_function
, flags
);
750 /* Starts with '@', then look through the known hostnames for
752 else if (r
> p
&& r
> q
){
753 SHOW_C_CTX("try_complete:host_subst");
754 matches
= completion_matches (r
, hostname_completion_function
, flags
);
759 /* Starts with `~' and there is no slash in the word, then
760 try completing this word as a username. */
761 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
763 SHOW_C_CTX("try_complete:user_subst");
764 matches
= completion_matches (word
, username_completion_function
, flags
);
768 /* And finally if this word is in a command position, then
769 complete over possible command names, including aliases, functions,
770 and command names. */
771 if (!matches
&& in_command_position
)
773 SHOW_C_CTX("try_complete:cmd_subst");
774 matches
= completion_matches (word
, command_completion_function
, flags
& (~INPUT_COMPLETE_FILENAMES
));
777 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
)){
779 flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
780 SHOW_C_CTX("try_complete:filename_subst_1");
781 matches
= completion_matches (word
, filename_completion_function
, flags
);
782 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~'){
783 char *p
, *q
= text
+ *start
;
785 for (p
= text
; *p
&& p
< q
; p
++){
786 if (*p
== ' ' || *p
== '\t') {
787 if (p
== text
) continue;
788 if (*(p
-1) == '\\') {
794 if (!strncmp (p
, "cd", 2))
795 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); p
++){
796 if (p
== text
) continue;
797 if (*(p
-1) == '\\') {
803 char * const cdpath_ref
= g_strdup (getenv ("CDPATH"));
804 char *cdpath
= cdpath_ref
;
811 while (!matches
&& c
== ':'){
812 s
= strchr (cdpath
, ':');
814 s
= strchr (cdpath
, 0);
818 r
= mhl_str_dir_plus_file (cdpath
, word
);
819 SHOW_C_CTX("try_complete:filename_subst_2");
820 matches
= completion_matches (r
, filename_completion_function
, flags
);
836 void free_completions (WInput
*in
)
840 if (!in
->completions
)
842 for (p
=in
->completions
; *p
; p
++)
844 g_free (in
->completions
);
845 in
->completions
= NULL
;
848 static int query_height
, query_width
;
849 static WInput
*input
;
851 static int start
, end
;
853 static int insert_text (WInput
*in
, char *text
, ssize_t len
)
855 len
= min (len
, (ssize_t
) strlen (text
)) + start
- end
;
856 if (strlen (in
->buffer
) + len
>= (size_t) in
->current_max_len
){
857 /* Expand the buffer */
858 char *narea
= g_realloc (in
->buffer
, in
->current_max_len
+ len
+ in
->field_len
);
861 in
->current_max_len
+= len
+ in
->field_len
;
864 if (strlen (in
->buffer
)+1 < (size_t) in
->current_max_len
){
866 int i
= strlen (&in
->buffer
[end
]);
868 in
->buffer
[end
+ len
+ i
] = in
->buffer
[end
+ i
];
870 char *p
= in
->buffer
+ end
+ len
, *q
= in
->buffer
+ end
;
875 memcpy (in
->buffer
+ start
, text
, len
- start
+ end
);
877 update_input (in
, 1);
884 query_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
896 if (end
== min_end
) {
903 e1
= e
= ((WListbox
*) (h
->current
))->list
;
906 (input
->buffer
+ start
, e1
->text
,
908 listbox_select_entry ((WListbox
*) (h
->current
),
910 handle_char (input
, parm
);
912 send_message (h
->current
, WIDGET_DRAW
, 0);
921 if (parm
> 0xff || !is_printable (parm
)) {
922 if (is_in_input_map (input
, parm
) == 2) {
925 h
->ret_value
= B_USER
; /* This means we want to refill the
926 list box and start again */
930 return MSG_NOT_HANDLED
;
935 char *last_text
= NULL
;
937 e1
= e
= ((WListbox
*) (h
->current
))->list
;
940 (input
->buffer
+ start
, e1
->text
, end
- start
)) {
941 if (e1
->text
[end
- start
] == parm
) {
943 register int c1
, c2
, si
;
945 for (si
= end
- start
+ 1;
947 && (c2
= e1
->text
[si
]); si
++)
952 last_text
= e1
->text
;
956 listbox_select_entry ((WListbox
*) (h
->
959 last_text
= e1
->text
;
965 if (need_redraw
== 2) {
966 insert_text (input
, last_text
, low
);
967 send_message (h
->current
, WIDGET_DRAW
, 0);
968 } else if (need_redraw
== 1) {
969 h
->ret_value
= B_ENTER
;
978 return default_dlg_callback (h
, msg
, parm
);
982 #define DO_INSERTION 1
984 /* Returns 1 if the user would like to see us again */
986 complete_engine (WInput
*in
, int what_to_do
)
988 if (in
->completions
&& in
->point
!= end
)
989 free_completions (in
);
990 if (!in
->completions
){
992 for (start
= end
? end
- 1 : 0; start
> -1; start
--)
993 if (strchr (" \t;|<>", in
->buffer
[start
])){
994 if (start
> 0 && in
->buffer
[start
-1] == '\\')
1001 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1004 if (in
->completions
){
1005 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1])) {
1006 char * complete
= in
->completions
[0];
1007 if (insert_text (in
, complete
, strlen (complete
))){
1008 if (in
->completions
[1])
1011 free_completions (in
);
1015 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1]) {
1016 int maxlen
= 0, i
, count
= 0;
1018 int start_x
, start_y
;
1020 Dlg_head
*query_dlg
;
1021 WListbox
*query_list
;
1023 for (p
=in
->completions
+ 1; *p
; count
++, p
++) {
1024 if ((i
= strlen (*p
)) > maxlen
)
1027 start_x
= in
->widget
.x
;
1028 start_y
= in
->widget
.y
;
1029 if (start_y
- 2 >= count
) {
1030 y
= start_y
- 2 - count
;
1033 if (start_y
>= LINES
- start_y
- 1) {
1038 h
= LINES
- start_y
- 1;
1041 x
= start
- in
->first_shown
- 2 + start_x
;
1053 query_dlg
= create_dlg (y
, x
, query_height
, query_width
,
1054 dialog_colors
, query_callback
,
1055 "[Completion]", NULL
, DLG_COMPACT
);
1056 query_list
= listbox_new (1, 1, w
- 2, h
- 2, NULL
);
1057 add_widget (query_dlg
, query_list
);
1058 for (p
= in
->completions
+ 1; *p
; p
++)
1059 listbox_add_item (query_list
, 0, 0, *p
, NULL
);
1060 run_dlg (query_dlg
);
1062 if (query_dlg
->ret_value
== B_ENTER
){
1063 listbox_get_current (query_list
, &q
, NULL
);
1065 insert_text (in
, q
, strlen (q
));
1067 if (q
|| end
!= min_end
)
1068 free_completions (in
);
1069 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1070 destroy_dlg (query_dlg
);
1079 void complete (WInput
*in
)
1083 if (in
->completions
)
1084 engine_flags
= DO_QUERY
;
1087 engine_flags
= DO_INSERTION
;
1089 if (show_all_if_ambiguous
)
1090 engine_flags
|= DO_QUERY
;
1093 while (complete_engine (in
, engine_flags
));