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. */
24 * \brief Source: Input line filename/username/hostname/variable/command completion
34 #include <sys/types.h>
41 #include "../src/tty/tty.h"
42 #include "../src/tty/key.h" /* XCTRL and ALT macros */
47 #include "main.h" /* show_all_if_ambiguous */
49 #include "../src/strescape.h"
52 typedef char *CompletionFunction (const char * text
, int state
, INPUT_COMPLETE_FLAGS flags
);
54 /* #define DO_COMPLETION_DEBUG */
55 #ifdef DO_COMPLETION_DEBUG
57 * Useful to print/debug completion flags
59 static const char * show_c_flags(INPUT_COMPLETE_FLAGS flags
)
61 static char s_cf
[] = "FHCVUDS";
63 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) ? 'F' : ' ';
64 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) ? 'H' : ' ';
65 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) ? 'C' : ' ';
66 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) ? 'V' : ' ';
67 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) ? 'U' : ' ';
68 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) ? 'D' : ' ';
69 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) ? 'S' : ' ';
73 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
75 #define SHOW_C_CTX(func)
76 #endif /* DO_CMPLETION_DEBUG */
79 filename_completion_function (const char * text
, int state
, INPUT_COMPLETE_FLAGS flags
)
81 static DIR *directory
;
82 static char *filename
= NULL
;
83 static char *dirname
= NULL
;
84 static char *users_dirname
= NULL
;
85 static size_t filename_len
;
86 int isdir
= 1, isexec
= 0;
88 struct dirent
*entry
= NULL
;
90 SHOW_C_CTX("filename_completion_function");
92 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
98 u_text
= strutils_shell_unescape (text
);
100 result
= filename_completion_function (u_text
, state
, flags
& (~INPUT_COMPLETE_SHELL_ESC
));
103 e_result
= strutils_shell_escape (result
);
109 /* If we're starting the match process, initialize us a bit. */
115 g_free (users_dirname
);
117 if ((*text
) && (temp
= strrchr (text
, PATH_SEP
))){
118 filename
= g_strdup (++temp
);
119 dirname
= g_strndup (text
, temp
- text
);
121 dirname
= g_strdup (".");
122 filename
= g_strdup (text
);
125 /* We aren't done yet. We also support the "~user" syntax. */
127 /* Save the version of the directory that the user typed. */
128 users_dirname
= dirname
;
129 dirname
= tilde_expand (dirname
);
130 canonicalize_pathname (dirname
);
132 /* Here we should do something with variable expansion
134 Maybe a dream - UNIMPLEMENTED yet. */
136 directory
= mc_opendir (dirname
);
137 filename_len
= strlen (filename
);
140 /* Now that we have some state, we can read the directory. */
142 while (directory
&& (entry
= mc_readdir (directory
))){
143 if (!str_is_valid_string (entry
->d_name
))
146 /* Special case for no filename.
147 All entries except "." and ".." match. */
148 if (filename_len
== 0) {
149 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
152 /* Otherwise, if these match up to the length of filename, then
153 it may be a match. */
154 if ((entry
->d_name
[0] != filename
[0]) ||
155 ((NLENGTH (entry
)) < filename_len
) ||
156 strncmp (filename
, entry
->d_name
, filename_len
))
159 isdir
= 1; isexec
= 0;
162 struct stat tempstat
;
164 tmp
= g_strconcat (dirname
, PATH_SEP_STR
, entry
->d_name
, (char *) NULL
);
165 canonicalize_pathname (tmp
);
167 if (!mc_stat (tmp
, &tempstat
)){
168 uid_t my_uid
= getuid ();
169 gid_t my_gid
= getgid ();
171 if (!S_ISDIR (tempstat
.st_mode
)){
173 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
174 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
175 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
176 (tempstat
.st_mode
& 0001))
182 /* stat failed, strange. not a dir in any case */
187 if ((flags
& INPUT_COMPLETE_COMMANDS
)
188 && (isexec
|| isdir
))
190 if ((flags
& INPUT_COMPLETE_CD
)
193 if (flags
& (INPUT_COMPLETE_FILENAMES
))
199 mc_closedir (directory
);
206 g_free (users_dirname
);
207 users_dirname
= NULL
;
212 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1])){
213 size_t dirlen
= strlen (users_dirname
);
214 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
215 strcpy (temp
, users_dirname
);
216 /* We need a `/' at the end. */
217 if (users_dirname
[dirlen
- 1] != PATH_SEP
){
218 temp
[dirlen
] = PATH_SEP
;
219 temp
[dirlen
+ 1] = 0;
221 strcat (temp
, entry
->d_name
);
223 temp
= g_malloc (2 + NLENGTH (entry
));
224 strcpy (temp
, entry
->d_name
);
227 strcat (temp
, PATH_SEP_STR
);
233 /* We assume here that text[0] == '~' , if you want to call it in another way,
234 you have to change the code */
236 username_completion_function (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
238 static struct passwd
*entry
;
239 static size_t userlen
;
242 SHOW_C_CTX("username_completion_function");
244 if (text
[0] == '\\' && text
[1] == '~')
246 if (!state
){ /* Initialization stuff */
248 userlen
= strlen (text
+ 1);
250 while ((entry
= getpwent ()) != NULL
){
251 /* Null usernames should result in all users as possible completions. */
254 if (text
[1] == entry
->pw_name
[0]
255 && !strncmp (text
+ 1, entry
->pw_name
, userlen
))
260 return g_strconcat ("~", entry
->pw_name
, PATH_SEP_STR
, (char *) NULL
);
266 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
267 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
268 extern char **environ
;
271 /* We assume text [0] == '$' and want to have a look at text [1], if it is
272 equal to '{', so that we should append '}' at the end */
274 variable_completion_function (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
277 static int varlen
, isbrace
;
278 const char *p
= NULL
;
281 SHOW_C_CTX("variable_completion_function");
283 if (!state
){ /* Initialization stuff */
284 isbrace
= (text
[1] == '{');
285 varlen
= strlen (text
+ 1 + isbrace
);
290 p
= strchr (*env_p
, '=');
291 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
299 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
304 memcpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
306 strcpy (temp
+ 2 + (p
- *env_p
), "}");
308 temp
[1 + p
- *env_p
] = 0;
314 #define whitespace(c) ((c) == ' ' || (c) == '\t')
315 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
317 static char **hosts
= NULL
;
318 static char **hosts_p
= NULL
;
319 static int hosts_alloclen
= 0;
320 static void fetch_hosts (const char *filename
)
322 FILE *file
= fopen (filename
, "r");
323 char buffer
[256], *name
;
330 while (fgets (buffer
, 255, file
) != NULL
){
331 /* Skip to first character. */
333 bi
[0] != '\0' && str_isspace (bi
);
334 str_next_char (&bi
));
336 /* Ignore comments... */
339 /* Handle $include. */
340 if (!strncmp (bi
, "$include ", 9)){
341 char *includefile
= bi
+ 9;
344 /* Find start of filename. */
345 while (*includefile
&& whitespace (*includefile
))
349 /* Find end of filename. */
350 while (t
[0] != '\0' && !str_isspace (t
))
354 fetch_hosts (includefile
);
359 while (bi
[0] != '\0' && !str_isspace (bi
))
362 /* Get the host names separated by white space. */
363 while (bi
[0] != '\0' && bi
[0] != '#'){
364 while (bi
[0] != '\0' && str_isspace (bi
))
369 bi
[0] != '\0' && !str_isspace (bi
);
370 str_next_char (&bi
));
372 if (bi
- start
== 0) continue;
374 name
= g_strndup (start
, bi
- start
);
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 (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
401 static char **host_p
;
402 static int textstart
, textlen
;
405 SHOW_C_CTX("hostname_completion_function");
407 if (!state
){ /* Initialization stuff */
411 for (host_p
= hosts
; *host_p
; host_p
++)
415 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
418 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
420 textstart
= (*text
== '@') ? 1 : 0;
421 textlen
= strlen (text
+ textstart
);
426 break; /* Match all of them */
427 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
433 for (host_p
= hosts
; *host_p
; host_p
++)
439 char *temp
= g_malloc (2 + strlen (*host_p
));
443 strcpy (temp
+ textstart
, *host_p
);
450 * This is the function to call when the word to complete is in a position
451 * where a command word can be found. It looks around $PATH, looking for
452 * commands that match. It also scans aliases, function names, and the
453 * table of shell built-ins.
456 command_completion_function (const char *_text
, int state
, INPUT_COMPLETE_FLAGS flags
)
459 static const char *path_end
;
460 static gboolean isabsolute
;
463 static const char *const *words
;
465 static char *cur_path
;
466 static char *cur_word
;
467 static int init_state
;
468 static const char *const bash_reserved
[] = {
469 "if", "then", "else", "elif", "fi", "case", "esac", "for",
470 "select", "while", "until", "do", "done", "in", "function", 0
472 static const char *const bash_builtins
[] = {
473 "alias", "bg", "bind", "break", "builtin", "cd", "command",
474 "continue", "declare", "dirs", "echo", "enable", "eval",
475 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
476 "help", "history", "jobs", "kill", "let", "local", "logout",
477 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
478 "shift", "source", "suspend", "test", "times", "trap", "type",
479 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
483 SHOW_C_CTX("command_completion_function");
485 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
487 text
= strutils_shell_unescape(_text
);
488 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
490 if (!state
) { /* Initialize us a little bit */
491 isabsolute
= strchr (text
, PATH_SEP
) != NULL
;
493 words
= bash_reserved
;
495 text_len
= strlen (text
);
496 if (!path
&& (path
= g_strdup (getenv ("PATH"))) != NULL
) {
498 path_end
= strchr (p
, 0);
499 while ((p
= strchr (p
, PATH_ENV_SEP
))) {
507 p
= filename_completion_function (text
, state
, flags
);
511 p
= strutils_shell_escape (p
);
521 case 0: /* Reserved words */
523 if (!strncmp (*words
, text
, text_len
))
524 return g_strdup (*(words
++));
528 words
= bash_builtins
;
529 case 1: /* Builtin commands */
531 if (!strncmp (*words
, text
, text_len
))
532 return g_strdup (*(words
++));
540 case 2: /* And looking through the $PATH */
545 if (cur_path
>= path_end
)
547 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
548 cur_word
= concat_dir_and_file (expanded
, text
);
550 canonicalize_pathname (cur_word
);
551 cur_path
= strchr (cur_path
, 0) + 1;
555 filename_completion_function (cur_word
,
556 state
- init_state
, flags
);
567 } else if ((p
= strrchr (found
, PATH_SEP
)) != NULL
) {
569 found
= strutils_shell_escape (p
+ 1);
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 (const char *text
, CompletionFunction entry_function
, INPUT_COMPLETE_FLAGS flags
)
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
, flags
)) != 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] = 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)
640 for (si
= match_list
[i
], sj
= match_list
[j
];
643 ni
= str_get_next_char (si
);
644 nj
= str_get_next_char (sj
);
646 if (ni
- si
!= nj
- sj
) break;
647 if (strncmp (si
, sj
, ni
- si
) != 0) break;
653 if (si
[0] == '\0' && sj
[0] == '\0'){ /* Two equal strings */
654 g_free (match_list
[j
]);
658 continue; /* Look for a run of equal strings */
660 if (low
> si
- match_list
[i
]) low
= si
- match_list
[i
];
661 if (i
+ 1 != j
) /* So there's some gap */
662 match_list
[i
+ 1] = match_list
[j
];
666 match_list
[matches
+ 1] = NULL
;
667 match_list
[0] = g_strndup(match_list
[1], low
);
669 } else { /* There were no matches. */
676 /* Check if directory completion is needed */
678 check_is_cd (const char *text
, int start
, INPUT_COMPLETE_FLAGS flags
)
683 SHOW_C_CTX("check_is_cd");
684 if (!(flags
& INPUT_COMPLETE_CD
))
687 /* Skip initial spaces */
689 q
= (char*)text
+ start
;
690 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
693 /* Check if the command is "cd" and the cursor is after it */
698 text
+= str_isspace (p
);
699 if (test
== 3 && (p
< q
))
705 /* Returns an array of matches, or NULL if none. */
707 try_complete (char *text
, int *start
, int *end
, INPUT_COMPLETE_FLAGS flags
)
709 int in_command_position
= 0;
711 char **matches
= NULL
;
712 const char *command_separator_chars
= ";|&{(`";
713 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
714 int is_cd
= check_is_cd (text
, *start
, flags
);
717 SHOW_C_CTX("try_complete");
718 word
= g_strndup (text
+ *start
, *end
- *start
);
720 /* Determine if this could be a command word. It is if it appears at
721 the start of the line (ignoring preceding whitespace), or if it
722 appears after a character that separates commands. And we have to
723 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
724 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
)){
725 ti
= str_get_prev_char (&text
[*start
]);
726 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
728 if (ti
<= text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
729 in_command_position
++;
730 else if (strchr (command_separator_chars
, ti
[0])){
731 register int this_char
, prev_char
;
733 in_command_position
++;
736 /* Handle the two character tokens `>&', `<&', and `>|'.
737 We are not in a command position after one of these. */
739 prev_char
= str_get_prev_char (ti
)[0];
741 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>')) ||
742 (this_char
== '|' && prev_char
== '>'))
743 in_command_position
= 0;
745 else if (ti
> text
&& str_get_prev_char (ti
)[0] == '\\') /* Quoted */
746 in_command_position
= 0;
751 if (flags
& INPUT_COMPLETE_COMMANDS
)
752 p
= strrchr (word
, '`');
753 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
754 q
= strrchr (word
, '$');
755 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
756 r
= strrchr (word
, '@');
757 if (q
&& q
[1] == '(' && INPUT_COMPLETE_COMMANDS
){
759 p
= str_get_next_char (q
);
763 /* Command substitution? */
765 SHOW_C_CTX("try_complete:cmd_backq_subst");
766 matches
= completion_matches (str_cget_next_char (p
),
767 command_completion_function
,
768 flags
& (~INPUT_COMPLETE_FILENAMES
));
770 *start
+= str_get_next_char (p
) - word
;
774 else if (q
> p
&& q
> r
){
775 SHOW_C_CTX("try_complete:var_subst");
776 matches
= completion_matches (q
, variable_completion_function
, flags
);
781 /* Starts with '@', then look through the known hostnames for
783 else if (r
> p
&& r
> q
){
784 SHOW_C_CTX("try_complete:host_subst");
785 matches
= completion_matches (r
, hostname_completion_function
, flags
);
790 /* Starts with `~' and there is no slash in the word, then
791 try completing this word as a username. */
792 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
794 SHOW_C_CTX("try_complete:user_subst");
795 matches
= completion_matches (word
, username_completion_function
, flags
);
799 /* And finally if this word is in a command position, then
800 complete over possible command names, including aliases, functions,
801 and command names. */
802 if (!matches
&& in_command_position
)
804 SHOW_C_CTX("try_complete:cmd_subst");
805 matches
= completion_matches (word
, command_completion_function
, flags
& (~INPUT_COMPLETE_FILENAMES
));
808 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
)){
810 flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
811 SHOW_C_CTX("try_complete:filename_subst_1");
812 matches
= completion_matches (word
, filename_completion_function
, flags
);
813 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~'){
815 for (p
= text
; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
816 if (!strncmp (p
, "cd", 2))
817 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
819 char * const cdpath_ref
= g_strdup (getenv ("CDPATH"));
820 char *cdpath
= cdpath_ref
;
827 while (!matches
&& c
== ':'){
828 s
= strchr (cdpath
, ':');
830 s
= strchr (cdpath
, 0);
834 r
= concat_dir_and_file (cdpath
, word
);
835 SHOW_C_CTX("try_complete:filename_subst_2");
836 matches
= completion_matches (r
, filename_completion_function
, flags
);
840 cdpath
= str_get_next_char (s
);
852 void free_completions (WInput
*in
)
856 if (!in
->completions
)
858 for (p
=in
->completions
; *p
; p
++)
860 g_free (in
->completions
);
861 in
->completions
= NULL
;
864 static int query_height
, query_width
;
865 static WInput
*input
;
867 static int start
, end
;
869 static int insert_text (WInput
*in
, char *text
, ssize_t size
)
871 int buff_len
= str_length (in
->buffer
);
873 size
= min (size
, (ssize_t
) strlen (text
)) + start
- end
;
874 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
){
875 /* Expand the buffer */
876 char *narea
= g_realloc (in
->buffer
, in
->current_max_size
877 + size
+ in
->field_width
);
880 in
->current_max_size
+= size
+ in
->field_width
;
883 if (strlen (in
->buffer
)+1 < (size_t) in
->current_max_size
){
885 int i
= strlen (&in
->buffer
[end
]);
887 in
->buffer
[end
+ size
+ i
] = in
->buffer
[end
+ i
];
888 } else if (size
< 0){
889 char *p
= in
->buffer
+ end
+ size
, *q
= in
->buffer
+ end
;
894 memcpy (in
->buffer
+ start
, text
, size
- start
+ end
);
895 in
->point
+= str_length (in
->buffer
) - buff_len
;
896 update_input (in
, 1);
903 query_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
905 static char buff
[MB_LEN_MAX
] = "";
920 if (end
== min_end
) {
927 e1
= e
= ((WListbox
*) (h
->current
))->list
;
929 if (!strncmp (input
->buffer
+ start
,
930 e1
->text
, end
- start
- 1)) {
932 listbox_select_entry ((WListbox
*) (h
->current
), e1
);
933 end
= str_get_prev_char (&(input
->buffer
[end
]))
935 handle_char (input
, parm
);
936 send_message (h
->current
, WIDGET_DRAW
, 0);
945 if (parm
< 32 || parm
> 256) {
947 if (is_in_input_map (input
, parm
) == 2) {
950 h
->ret_value
= B_USER
; /* This means we want to refill the
951 list box and start again */
955 return MSG_NOT_HANDLED
;
960 char *last_text
= NULL
;
962 buff
[bl
] = (char) parm
;
965 switch (str_is_valid_char (buff
, bl
)) {
972 e1
= e
= ((WListbox
*) (h
->current
))->list
;
974 if (!strncmp (input
->buffer
+ start
,
975 e1
->text
, end
- start
)) {
977 if (strncmp (&e1
->text
[end
- start
], buff
, bl
) == 0) {
981 si
= &(e1
->text
[end
- start
]);
982 sl
= &(last_text
[end
- start
]);
984 for (; si
[0] != '\0' && sl
[0] != '\0';) {
986 ni
= str_get_next_char (si
);
987 nl
= str_get_next_char (sl
);
989 if (ni
- si
!= nl
- sl
) break;
990 if (strncmp (si
, sl
, ni
- si
) != 0) break;
996 if (low
> si
- &e1
->text
[end
- start
])
997 low
= si
- &e1
->text
[end
- start
];
999 last_text
= e1
->text
;
1003 listbox_select_entry ((WListbox
*) (h
->
1006 last_text
= e1
->text
;
1012 if (need_redraw
== 2) {
1013 insert_text (input
, last_text
, low
);
1014 send_message (h
->current
, WIDGET_DRAW
, 0);
1015 } else if (need_redraw
== 1) {
1016 h
->ret_value
= B_ENTER
;
1026 return default_dlg_callback (h
, msg
, parm
);
1030 #define DO_INSERTION 1
1032 /* Returns 1 if the user would like to see us again */
1034 complete_engine (WInput
*in
, int what_to_do
)
1038 if (in
->completions
&& (str_offset_to_pos (in
->buffer
, in
->point
)) != end
)
1039 free_completions (in
);
1040 if (!in
->completions
){
1041 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1042 for (s
= in
->point
? in
->point
- 1 : 0; s
>= 0; s
--) {
1043 start
= str_offset_to_pos (in
->buffer
, s
);
1044 if (strchr (" \t;|<>", in
->buffer
[start
])) {
1045 if (start
< end
) start
= str_offset_to_pos (in
->buffer
, s
+ 1);
1046 /* FIXME: maybe need check '\\' prev char
1047 if (start > 0 && in->buffer [start-1] == '\\')
1052 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1055 if (in
->completions
){
1056 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1])) {
1057 char * lc_complete
= in
->completions
[0];
1058 if (insert_text (in
, lc_complete
, strlen (lc_complete
))){
1059 if (in
->completions
[1])
1062 free_completions (in
);
1066 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1]) {
1067 int maxlen
= 0, i
, count
= 0;
1069 int start_x
, start_y
;
1071 Dlg_head
*query_dlg
;
1072 WListbox
*query_list
;
1074 for (p
=in
->completions
+ 1; *p
; count
++, p
++)
1075 if ((i
= str_term_width1 (*p
)) > maxlen
)
1077 start_x
= in
->widget
.x
;
1078 start_y
= in
->widget
.y
;
1079 if (start_y
- 2 >= count
) {
1080 y
= start_y
- 2 - count
;
1083 if (start_y
>= LINES
- start_y
- 1) {
1088 h
= LINES
- start_y
- 1;
1091 x
= start
- in
->term_first_shown
- 2 + start_x
;
1103 query_dlg
= create_dlg (y
, x
, query_height
, query_width
,
1104 dialog_colors
, query_callback
,
1105 "[Completion]", NULL
, DLG_COMPACT
);
1106 query_list
= listbox_new (1, 1, h
- 2, w
- 2, NULL
);
1107 add_widget (query_dlg
, query_list
);
1108 for (p
= in
->completions
+ 1; *p
; p
++)
1109 listbox_add_item (query_list
, 0, 0, *p
, NULL
);
1110 run_dlg (query_dlg
);
1112 if (query_dlg
->ret_value
== B_ENTER
){
1113 listbox_get_current (query_list
, &q
, NULL
);
1115 insert_text (in
, q
, strlen (q
));
1117 if (q
|| end
!= min_end
)
1118 free_completions (in
);
1119 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1120 destroy_dlg (query_dlg
);
1129 void complete (WInput
*in
)
1133 if (!str_is_valid_string (in
->buffer
)) return;
1135 if (in
->completions
)
1136 engine_flags
= DO_QUERY
;
1139 engine_flags
= DO_INSERTION
;
1141 if (show_all_if_ambiguous
)
1142 engine_flags
|= DO_QUERY
;
1145 while (complete_engine (in
, engine_flags
));