2 Input line filename/username/hostname/variable/command completion.
3 (Let mc type for you...)
5 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
7 the Free Software Foundation, Inc.
11 Slava Zanko <slavazanko@gmail.com>, 2013
12 Andrew Borodin <aborodin@vmail.ru>, 2013
14 This file is part of the Midnight Commander.
16 The Midnight Commander is free software: you can redistribute it
17 and/or modify it under the terms of the GNU General Public License as
18 published by the Free Software Foundation, either version 3 of the License,
19 or (at your option) any later version.
21 The Midnight Commander is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 /** \file lib/widget/input_complete.c
31 * \brief Source: Input line filename/username/hostname/variable/command completion
41 #include <sys/types.h>
46 #include "lib/global.h"
48 #include "lib/tty/tty.h"
49 #include "lib/tty/key.h" /* XCTRL and ALT macros */
50 #include "lib/vfs/vfs.h"
51 #include "lib/strescape.h"
52 #include "lib/strutil.h"
54 #include "lib/widget.h"
56 #include "input_complete.h"
58 /*** global variables ****************************************************************************/
60 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
61 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
62 extern char **environ
;
65 /*** file scope macro definitions ****************************************************************/
67 /* #define DO_COMPLETION_DEBUG */
68 #ifdef DO_COMPLETION_DEBUG
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 */
74 #define whitespace(c) ((c) == ' ' || (c) == '\t')
75 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
77 #define DO_INSERTION 1
80 /*** file scope type declarations ****************************************************************/
82 typedef char *CompletionFunction (const char *text
, int state
, input_complete_t flags
);
86 size_t in_command_position
;
92 input_complete_t flags
;
93 } try_complete_automation_state_t
;
95 /*** file scope variables ************************************************************************/
97 static char **hosts
= NULL
;
98 static char **hosts_p
= NULL
;
99 static int hosts_alloclen
= 0;
101 static int query_height
, query_width
;
102 static WInput
*input
;
104 static int start
= 0;
107 /*** file scope functions ************************************************************************/
108 /* --------------------------------------------------------------------------------------------- */
110 char **try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
);
111 void complete_engine_fill_completions (WInput
* in
);
113 #ifdef DO_COMPLETION_DEBUG
115 * Useful to print/debug completion flags
118 show_c_flags (input_complete_t flags
)
120 static char s_cf
[] = "FHCVUDS";
122 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) ? 'F' : ' ';
123 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) ? 'H' : ' ';
124 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) ? 'C' : ' ';
125 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) ? 'V' : ' ';
126 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) ? 'U' : ' ';
127 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) ? 'D' : ' ';
128 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) ? 'S' : ' ';
132 #endif /* DO_CMPLETION_DEBUG */
134 /* --------------------------------------------------------------------------------------------- */
137 filename_completion_function (const char *text
, int state
, input_complete_t flags
)
139 static DIR *directory
= NULL
;
140 static char *filename
= NULL
;
141 static char *dirname
= NULL
;
142 static char *users_dirname
= NULL
;
143 static size_t filename_len
;
144 int isdir
= 1, isexec
= 0;
145 static vfs_path_t
*dirname_vpath
= NULL
;
147 struct dirent
*entry
= NULL
;
149 SHOW_C_CTX ("filename_completion_function");
151 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
157 u_text
= strutils_shell_unescape (text
);
159 result
= filename_completion_function (u_text
, state
, flags
& (~INPUT_COMPLETE_SHELL_ESC
));
162 e_result
= strutils_shell_escape (result
);
168 /* If we're starting the match process, initialize us a bit. */
175 g_free (users_dirname
);
176 vfs_path_free (dirname_vpath
);
178 if ((*text
!= '\0') && (temp
= strrchr (text
, PATH_SEP
)) != NULL
)
180 filename
= g_strdup (++temp
);
181 dirname
= g_strndup (text
, temp
- text
);
185 dirname
= g_strdup (".");
186 filename
= g_strdup (text
);
189 /* We aren't done yet. We also support the "~user" syntax. */
191 /* Save the version of the directory that the user typed. */
192 users_dirname
= dirname
;
193 dirname
= tilde_expand (dirname
);
194 canonicalize_pathname (dirname
);
195 dirname_vpath
= vfs_path_from_str (dirname
);
197 /* Here we should do something with variable expansion
199 Maybe a dream - UNIMPLEMENTED yet. */
201 directory
= mc_opendir (dirname_vpath
);
202 filename_len
= strlen (filename
);
205 /* Now that we have some state, we can read the directory. */
207 while (directory
&& (entry
= mc_readdir (directory
)))
209 if (!str_is_valid_string (entry
->d_name
))
212 /* Special case for no filename.
213 All entries except "." and ".." match. */
214 if (filename_len
== 0)
216 if (DIR_IS_DOT (entry
->d_name
) || DIR_IS_DOTDOT (entry
->d_name
))
221 /* Otherwise, if these match up to the length of filename, then
222 it may be a match. */
223 if ((entry
->d_name
[0] != filename
[0]) ||
224 ((NLENGTH (entry
)) < filename_len
) ||
225 strncmp (filename
, entry
->d_name
, filename_len
))
231 struct stat tempstat
;
232 vfs_path_t
*tmp_vpath
;
234 tmp_vpath
= vfs_path_build_filename (dirname
, entry
->d_name
, (char *) NULL
);
237 if (mc_stat (tmp_vpath
, &tempstat
) == 0)
239 uid_t my_uid
= getuid ();
240 gid_t my_gid
= getgid ();
242 if (!S_ISDIR (tempstat
.st_mode
))
245 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
246 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
247 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
248 (tempstat
.st_mode
& 0001))
254 /* stat failed, strange. not a dir in any case */
257 vfs_path_free (tmp_vpath
);
259 if ((flags
& INPUT_COMPLETE_COMMANDS
) && (isexec
|| isdir
))
261 if ((flags
& INPUT_COMPLETE_CD
) && isdir
)
263 if (flags
& (INPUT_COMPLETE_FILENAMES
))
271 mc_closedir (directory
);
276 vfs_path_free (dirname_vpath
);
277 dirname_vpath
= NULL
;
280 g_free (users_dirname
);
281 users_dirname
= NULL
;
288 temp
= g_string_sized_new (16);
290 if (users_dirname
!= NULL
&& (users_dirname
[0] != '.' || users_dirname
[1] != '\0'))
292 g_string_append (temp
, users_dirname
);
294 /* We need a '/' at the end. */
295 if (temp
->str
[temp
->len
- 1] != PATH_SEP
)
296 g_string_append_c (temp
, PATH_SEP
);
298 g_string_append (temp
, entry
->d_name
);
300 g_string_append_c (temp
, PATH_SEP
);
302 return g_string_free (temp
, FALSE
);
306 /* --------------------------------------------------------------------------------------------- */
307 /** We assume here that text[0] == '~' , if you want to call it in another way,
308 you have to change the code */
311 username_completion_function (const char *text
, int state
, input_complete_t flags
)
313 static struct passwd
*entry
;
314 static size_t userlen
;
317 SHOW_C_CTX ("username_completion_function");
319 if (text
[0] == '\\' && text
[1] == '~')
322 { /* Initialization stuff */
324 userlen
= strlen (text
+ 1);
326 while ((entry
= getpwent ()) != NULL
)
328 /* Null usernames should result in all users as possible completions. */
331 if (text
[1] == entry
->pw_name
[0] && !strncmp (text
+ 1, entry
->pw_name
, userlen
))
336 return g_strconcat ("~", entry
->pw_name
, PATH_SEP_STR
, (char *) NULL
);
342 /* --------------------------------------------------------------------------------------------- */
343 /** We assume text [0] == '$' and want to have a look at text [1], if it is
344 equal to '{', so that we should append '}' at the end */
347 variable_completion_function (const char *text
, int state
, input_complete_t flags
)
350 static unsigned int isbrace
;
351 static size_t varlen
;
352 const char *p
= NULL
;
355 SHOW_C_CTX ("variable_completion_function");
358 { /* Initialization stuff */
359 isbrace
= (text
[1] == '{') ? 1 : 0;
360 varlen
= strlen (text
+ 1 + isbrace
);
366 p
= strchr (*env_p
, '=');
367 if (p
&& ((size_t) (p
- *env_p
) >= varlen
) && !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
378 temp
= g_string_new_len (*env_p
, p
- *env_p
);
382 g_string_prepend_c (temp
, '{');
383 g_string_append_c (temp
, '}');
385 g_string_prepend_c (temp
, '$');
389 return g_string_free (temp
, FALSE
);
393 /* --------------------------------------------------------------------------------------------- */
396 fetch_hosts (const char *filename
)
398 FILE *file
= fopen (filename
, "r");
399 char buffer
[256], *name
;
406 while (fgets (buffer
, 255, file
) != NULL
)
408 /* Skip to first character. */
409 for (bi
= buffer
; bi
[0] != '\0' && str_isspace (bi
); str_next_char (&bi
));
411 /* Ignore comments... */
414 /* Handle $include. */
415 if (!strncmp (bi
, "$include ", 9))
417 char *includefile
= bi
+ 9;
420 /* Find start of filename. */
421 while (*includefile
&& whitespace (*includefile
))
425 /* Find end of filename. */
426 while (t
[0] != '\0' && !str_isspace (t
))
430 fetch_hosts (includefile
);
435 while (bi
[0] != '\0' && !str_isspace (bi
))
438 /* Get the host names separated by white space. */
439 while (bi
[0] != '\0' && bi
[0] != '#')
441 while (bi
[0] != '\0' && str_isspace (bi
))
445 for (lc_start
= bi
; bi
[0] != '\0' && !str_isspace (bi
); str_next_char (&bi
));
447 if (bi
- lc_start
== 0)
450 name
= g_strndup (lc_start
, bi
- lc_start
);
454 if (hosts_p
- hosts
>= hosts_alloclen
)
459 hosts_alloclen
+= 30;
460 hosts
= g_renew (char *, hosts
, hosts_alloclen
+ 1);
463 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
464 if (!strcmp (name
, *host_p
))
465 break; /* We do not want any duplicates */
466 if (host_p
== hosts_p
)
479 /* --------------------------------------------------------------------------------------------- */
482 hostname_completion_function (const char *text
, int state
, input_complete_t flags
)
484 static char **host_p
;
485 static unsigned int textstart
;
486 static size_t textlen
;
489 SHOW_C_CTX ("hostname_completion_function");
492 { /* Initialization stuff */
497 hosts
= g_new (char *, hosts_alloclen
+ 1);
500 p
= getenv ("HOSTFILE");
501 fetch_hosts (p
!= NULL
? p
: "/etc/hosts");
503 textstart
= (*text
== '@') ? 1 : 0;
504 textlen
= strlen (text
+ textstart
);
507 for (; *host_p
!= NULL
; host_p
++)
510 break; /* Match all of them */
511 if (strncmp (text
+ textstart
, *host_p
, textlen
) == 0)
525 temp
= g_string_sized_new (8);
528 g_string_append_c (temp
, '@');
529 g_string_append (temp
, *host_p
);
532 return g_string_free (temp
, FALSE
);
536 /* --------------------------------------------------------------------------------------------- */
538 * This is the function to call when the word to complete is in a position
539 * where a command word can be found. It looks around $PATH, looking for
540 * commands that match. It also scans aliases, function names, and the
541 * table of shell built-ins.
545 command_completion_function (const char *_text
, int state
, input_complete_t flags
)
548 static const char *path_end
;
549 static gboolean isabsolute
;
551 static size_t text_len
;
552 static const char *const *words
;
554 static char *cur_path
;
555 static char *cur_word
;
556 static int init_state
;
557 static const char *const bash_reserved
[] = {
558 "if", "then", "else", "elif", "fi", "case", "esac", "for",
559 "select", "while", "until", "do", "done", "in", "function", 0
561 static const char *const bash_builtins
[] = {
562 "alias", "bg", "bind", "break", "builtin", "cd", "command",
563 "continue", "declare", "dirs", "echo", "enable", "eval",
564 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
565 "help", "history", "jobs", "kill", "let", "local", "logout",
566 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
567 "shift", "source", "suspend", "test", "times", "trap", "type",
568 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
572 /* cppcheck-suppress uninitvar */
573 SHOW_C_CTX ("command_completion_function");
575 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
578 text
= strutils_shell_unescape (_text
);
579 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
582 { /* Initialize us a little bit */
583 isabsolute
= strchr (text
, PATH_SEP
) != NULL
;
586 words
= bash_reserved
;
588 text_len
= strlen (text
);
592 path
= g_strdup (getenv ("PATH"));
596 path_end
= strchr (p
, '\0');
597 while ((p
= strchr (p
, PATH_ENV_SEP
)) != NULL
)
608 p
= filename_completion_function (text
, state
, flags
);
614 p
= strutils_shell_escape (p
);
625 case 0: /* Reserved words */
626 for (; *words
!= NULL
; words
++)
627 if (strncmp (*words
, text
, text_len
) == 0)
630 return g_strdup (*(words
++));
633 words
= bash_builtins
;
634 case 1: /* Builtin commands */
635 for (; *words
!= NULL
; words
++)
636 if (strncmp (*words
, text
, text_len
) == 0)
639 return g_strdup (*(words
++));
646 case 2: /* And looking through the $PATH */
653 if (cur_path
>= path_end
)
655 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
656 cur_word
= mc_build_filename (expanded
, text
, NULL
);
658 canonicalize_pathname (cur_word
);
659 cur_path
= strchr (cur_path
, 0) + 1;
662 found
= filename_completion_function (cur_word
, state
- init_state
, flags
);
678 p
= strrchr (found
, PATH_SEP
);
682 found
= strutils_shell_escape (p
+ 1);
691 /* --------------------------------------------------------------------------------------------- */
694 match_compare (const void *a
, const void *b
)
696 return strcmp (*(char **) a
, *(char **) b
);
699 /* --------------------------------------------------------------------------------------------- */
700 /** Returns an array of char * matches with the longest common denominator
701 in the 1st entry. Then a NULL terminated list of different possible
703 You have to supply your own CompletionFunction with the word you
704 want to complete as the first argument and an count of previous matches
706 In case no matches were found we return NULL. */
709 completion_matches (const char *text
, CompletionFunction entry_function
, input_complete_t flags
)
711 /* Number of slots in match_list. */
712 size_t match_list_size
= 30;
713 /* The list of matches. */
715 /* Number of matches actually found. */
718 /* Temporary string binder. */
721 match_list
= g_new (char *, match_list_size
+ 1);
722 match_list
[1] = NULL
;
724 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
)
726 if (matches
+ 1 == match_list_size
)
728 match_list_size
+= 30;
729 match_list
= (char **) g_renew (char *, match_list
, match_list_size
+ 1);
731 match_list
[++matches
] = string
;
732 match_list
[matches
+ 1] = NULL
;
735 /* If there were any matches, then look through them finding out the
736 lowest common denominator. That then becomes match_list[0]. */
740 /* If only one match, just use that. */
743 match_list
[0] = match_list
[1];
744 match_list
[1] = NULL
;
749 int low
= 4096; /* Count of max-matched characters. */
752 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
754 /* And compare each member of the list with
755 the next, finding out where they stop matching.
756 If we find two equal strings, we have to put one away... */
759 while (j
< matches
+ 1)
764 for (si
= match_list
[i
], sj
= match_list
[j
]; si
[0] && sj
[0];)
767 ni
= str_get_next_char (si
);
768 nj
= str_get_next_char (sj
);
770 if (ni
- si
!= nj
- sj
)
772 if (strncmp (si
, sj
, ni
- si
) != 0)
779 if (si
[0] == '\0' && sj
[0] == '\0')
780 { /* Two equal strings */
781 g_free (match_list
[j
]);
785 continue; /* Look for a run of equal strings */
787 else if (low
> si
- match_list
[i
])
788 low
= si
- match_list
[i
];
789 if (i
+ 1 != j
) /* So there's some gap */
790 match_list
[i
+ 1] = match_list
[j
];
795 match_list
[matches
+ 1] = NULL
;
796 match_list
[0] = g_strndup (match_list
[1], low
);
800 { /* There were no matches. */
807 /* --------------------------------------------------------------------------------------------- */
808 /** Check if directory completion is needed */
810 check_is_cd (const char *text
, int lc_start
, input_complete_t flags
)
814 SHOW_C_CTX ("check_is_cd");
816 if ((flags
& INPUT_COMPLETE_CD
) == 0)
819 /* Skip initial spaces */
821 q
= (char *) text
+ lc_start
;
822 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
825 /* Check if the command is "cd" and the cursor is after it */
826 return (p
[0] == 'c' && p
[1] == 'd' && str_isspace (p
+ 2) && p
+ 2 < q
);
829 /* --------------------------------------------------------------------------------------------- */
832 try_complete_commands_prepare (try_complete_automation_state_t
* state
, char *text
, int *lc_start
)
834 const char *command_separator_chars
= ";|&{(`";
841 ti
= str_get_prev_char (&text
[*lc_start
]);
842 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
847 state
->in_command_position
++;
848 else if (strchr (command_separator_chars
, ti
[0]) != NULL
)
850 state
->in_command_position
++;
853 int this_char
, prev_char
;
855 /* Handle the two character tokens '>&', '<&', and '>|'.
856 We are not in a command position after one of these. */
858 prev_char
= str_get_prev_char (ti
)[0];
861 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>'))
862 || (this_char
== '|' && prev_char
== '>') || (ti
!= text
863 && str_get_prev_char (ti
)[0] == '\\'))
864 state
->in_command_position
= 0;
869 /* --------------------------------------------------------------------------------------------- */
872 try_complete_find_start_sign (try_complete_automation_state_t
* state
)
874 if (state
->flags
& INPUT_COMPLETE_COMMANDS
)
875 state
->p
= strrchr (state
->word
, '`');
876 if (state
->flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
878 state
->q
= strrchr (state
->word
, '$');
880 /* don't substitute variable in \$ case */
881 if (strutils_is_char_escaped (state
->word
, state
->q
))
885 qlen
= strlen (state
->q
);
887 memmove (state
->q
- 1, state
->q
, qlen
+ 1);
889 state
->flags
&= ~INPUT_COMPLETE_VARIABLES
;
893 if (state
->flags
& INPUT_COMPLETE_HOSTNAMES
)
894 state
->r
= strrchr (state
->word
, '@');
895 if (state
->q
&& state
->q
[1] == '(' && (state
->flags
& INPUT_COMPLETE_COMMANDS
))
897 if (state
->q
> state
->p
)
898 state
->p
= str_get_next_char (state
->q
);
903 /* --------------------------------------------------------------------------------------------- */
906 try_complete_all_possible (try_complete_automation_state_t
* state
, char *text
, int *lc_start
)
908 char **matches
= NULL
;
910 if (state
->in_command_position
!= 0)
912 SHOW_C_CTX ("try_complete:cmd_subst");
914 completion_matches (state
->word
, command_completion_function
,
915 state
->flags
& (~INPUT_COMPLETE_FILENAMES
));
917 else if ((state
->flags
& INPUT_COMPLETE_FILENAMES
) != 0)
920 state
->flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
921 SHOW_C_CTX ("try_complete:filename_subst_1");
922 matches
= completion_matches (state
->word
, filename_completion_function
, state
->flags
);
924 if (matches
== NULL
&& state
->is_cd
&& *state
->word
!= PATH_SEP
&& *state
->word
!= '~')
926 state
->q
= text
+ *lc_start
;
927 for (state
->p
= text
;
928 *state
->p
&& state
->p
< state
->q
&& (*state
->p
== ' ' || *state
->p
== '\t');
929 str_next_char (&state
->p
))
931 if (!strncmp (state
->p
, "cd", 2))
933 *state
->p
&& state
->p
< state
->q
&& (*state
->p
== ' ' || *state
->p
== '\t');
934 str_next_char (&state
->p
))
936 if (state
->p
== state
->q
)
938 char *const cdpath_ref
= g_strdup (getenv ("CDPATH"));
939 char *cdpath
= cdpath_ref
;
942 c
= (cdpath
== NULL
) ? '\0' : ':';
944 while (!matches
&& c
== ':')
948 s
= strchr (cdpath
, ':');
950 s
= strchr (cdpath
, '\0');
955 state
->r
= mc_build_filename (cdpath
, state
->word
, NULL
);
956 SHOW_C_CTX ("try_complete:filename_subst_2");
958 completion_matches (state
->r
, filename_completion_function
,
963 cdpath
= str_get_next_char (s
);
972 /* --------------------------------------------------------------------------------------------- */
975 insert_text (WInput
* in
, char *text
, ssize_t size
)
979 buff_len
= str_length (in
->buffer
);
980 size
= min (size
, (ssize_t
) strlen (text
)) + start
- end
;
981 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
)
983 /* Expand the buffer */
985 Widget
*w
= WIDGET (in
);
987 narea
= g_try_realloc (in
->buffer
, in
->current_max_size
+ size
+ w
->cols
);
991 in
->current_max_size
+= size
+ w
->cols
;
994 if (strlen (in
->buffer
) + 1 < (size_t) in
->current_max_size
)
997 memmove (in
->buffer
+ end
+ size
, in
->buffer
+ end
, strlen (&in
->buffer
[end
]) + 1);
998 memmove (in
->buffer
+ start
, text
, size
- (start
- end
));
999 in
->point
+= str_length (in
->buffer
) - buff_len
;
1000 input_update (in
, TRUE
);
1006 /* --------------------------------------------------------------------------------------------- */
1009 query_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
1013 WDialog
*h
= DIALOG (w
);
1029 /* exit from completion list if input line is empty */
1035 /* Refill the list box and start again */
1036 else if (end
== min_end
)
1038 end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1039 input_handle_char (input
, parm
);
1040 h
->ret_value
= B_USER
;
1050 new_end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1052 for (i
= 0, e
= LISTBOX (h
->current
->data
)->list
;
1053 e
!= NULL
; i
++, e
= g_list_next (e
))
1055 WLEntry
*le
= LENTRY (e
->data
);
1057 if (strncmp (input
->buffer
+ start
, le
->text
, new_end
- start
) == 0)
1059 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1061 input_handle_char (input
, parm
);
1062 widget_redraw (WIDGET (h
->current
->data
));
1070 if (parm
< 32 || parm
> 255)
1073 if (input_key_is_in_map (input
, parm
) != 2)
1074 return MSG_NOT_HANDLED
;
1079 /* This means we want to refill the list box and start again */
1080 h
->ret_value
= B_USER
;
1086 static char buff
[MB_LEN_MAX
] = "";
1089 int need_redraw
= 0;
1091 char *last_text
= NULL
;
1093 buff
[bl
++] = (char) parm
;
1095 switch (str_is_valid_char (buff
, bl
))
1104 for (i
= 0, e
= LISTBOX (h
->current
->data
)->list
;
1105 e
!= NULL
; i
++, e
= g_list_next (e
))
1107 WLEntry
*le
= LENTRY (e
->data
);
1109 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
) == 0
1110 && strncmp (&le
->text
[end
- start
], buff
, bl
) == 0)
1112 if (need_redraw
== 0)
1115 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1116 last_text
= le
->text
;
1124 /* count symbols between start and end */
1125 for (si
= le
->text
+ start
; si
< le
->text
+ end
;
1126 str_next_char (&si
), si_num
++)
1128 for (sl
= last_text
+ start
; sl
< last_text
+ end
;
1129 str_next_char (&sl
), sl_num
++)
1132 /* pointers to next symbols */
1133 si
= &le
->text
[str_offset_to_pos (le
->text
, ++si_num
)];
1134 sl
= &last_text
[str_offset_to_pos (last_text
, ++sl_num
)];
1136 while (si
[0] != '\0' && sl
[0] != '\0')
1138 char *nexti
, *nextl
;
1140 nexti
= str_get_next_char (si
);
1141 nextl
= str_get_next_char (sl
);
1143 if (nexti
- si
!= nextl
- sl
|| strncmp (si
, sl
, nexti
- si
) != 0)
1152 last_text
= le
->text
;
1154 si
= &last_text
[str_offset_to_pos (last_text
, si_num
)];
1155 if (low
> si
- last_text
)
1156 low
= si
- last_text
;
1163 if (need_redraw
== 2)
1165 insert_text (input
, last_text
, low
);
1166 widget_redraw (WIDGET (h
->current
->data
));
1168 else if (need_redraw
== 1)
1170 h
->ret_value
= B_ENTER
;
1180 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
1184 /* --------------------------------------------------------------------------------------------- */
1186 /** Returns 1 if the user would like to see us again */
1188 complete_engine (WInput
* in
, int what_to_do
)
1190 if (in
->completions
!= NULL
&& str_offset_to_pos (in
->buffer
, in
->point
) != end
)
1191 input_free_completions (in
);
1193 if (in
->completions
== NULL
)
1194 complete_engine_fill_completions (in
);
1196 if (in
->completions
!= NULL
)
1198 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1]))
1200 char *lc_complete
= in
->completions
[0];
1201 if (insert_text (in
, lc_complete
, strlen (lc_complete
)))
1203 if (in
->completions
[1])
1206 input_free_completions (in
);
1211 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1])
1213 int maxlen
= 0, i
, count
= 0;
1215 int start_x
, start_y
;
1218 WListbox
*query_list
;
1220 for (p
= in
->completions
+ 1; *p
!= NULL
; count
++, p
++)
1222 i
= str_term_width1 (*p
);
1226 start_x
= WIDGET (in
)->x
;
1227 start_y
= WIDGET (in
)->y
;
1228 if (start_y
- 2 >= count
)
1230 y
= start_y
- 2 - count
;
1235 if (start_y
>= LINES
- start_y
- 1)
1243 h
= LINES
- start_y
- 1;
1246 x
= start
- in
->term_first_shown
- 2 + start_x
;
1258 query_dlg
= dlg_create (TRUE
, y
, x
, query_height
, query_width
,
1259 dialog_colors
, query_callback
, NULL
,
1260 "[Completion]", NULL
, DLG_COMPACT
);
1261 query_list
= listbox_new (1, 1, h
- 2, w
- 2, FALSE
, NULL
);
1262 add_widget (query_dlg
, query_list
);
1263 for (p
= in
->completions
+ 1; *p
; p
++)
1264 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
, 0, *p
, NULL
);
1265 dlg_run (query_dlg
);
1267 if (query_dlg
->ret_value
== B_ENTER
)
1269 listbox_get_current (query_list
, &q
, NULL
);
1271 insert_text (in
, q
, strlen (q
));
1273 if (q
|| end
!= min_end
)
1274 input_free_completions (in
);
1275 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1276 dlg_destroy (query_dlg
);
1286 /* --------------------------------------------------------------------------------------------- */
1287 /*** public functions ****************************************************************************/
1288 /* --------------------------------------------------------------------------------------------- */
1290 /** Returns an array of matches, or NULL if none. */
1292 try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
)
1294 try_complete_automation_state_t state
;
1295 char **matches
= NULL
;
1297 memset (&state
, 0, sizeof (try_complete_automation_state_t
));
1298 state
.flags
= flags
;
1300 SHOW_C_CTX ("try_complete");
1301 state
.word
= g_strndup (text
+ *lc_start
, *lc_end
- *lc_start
);
1303 state
.is_cd
= check_is_cd (text
, *lc_start
, state
.flags
);
1305 /* Determine if this could be a command word. It is if it appears at
1306 the start of the line (ignoring preceding whitespace), or if it
1307 appears after a character that separates commands. And we have to
1308 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
1309 if (!state
.is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
))
1310 try_complete_commands_prepare (&state
, text
, lc_start
);
1312 try_complete_find_start_sign (&state
);
1314 /* Command substitution? */
1315 if (state
.p
> state
.q
&& state
.p
> state
.r
)
1317 SHOW_C_CTX ("try_complete:cmd_backq_subst");
1318 matches
= completion_matches (str_cget_next_char (state
.p
),
1319 command_completion_function
,
1320 state
.flags
& (~INPUT_COMPLETE_FILENAMES
));
1322 *lc_start
+= str_get_next_char (state
.p
) - state
.word
;
1325 /* Variable name? */
1326 else if (state
.q
> state
.p
&& state
.q
> state
.r
)
1328 SHOW_C_CTX ("try_complete:var_subst");
1329 matches
= completion_matches (state
.q
, variable_completion_function
, state
.flags
);
1331 *lc_start
+= state
.q
- state
.word
;
1334 /* Starts with '@', then look through the known hostnames for
1335 completion first. */
1336 else if (state
.r
> state
.p
&& state
.r
> state
.q
)
1338 SHOW_C_CTX ("try_complete:host_subst");
1339 matches
= completion_matches (state
.r
, hostname_completion_function
, state
.flags
);
1341 *lc_start
+= state
.r
- state
.word
;
1344 /* Starts with '~' and there is no slash in the word, then
1345 try completing this word as a username. */
1346 if (!matches
&& *state
.word
== '~' && (state
.flags
& INPUT_COMPLETE_USERNAMES
)
1347 && !strchr (state
.word
, PATH_SEP
))
1349 SHOW_C_CTX ("try_complete:user_subst");
1350 matches
= completion_matches (state
.word
, username_completion_function
, state
.flags
);
1353 /* If this word is in a command position, then
1354 complete over possible command names, including aliases, functions,
1355 and command names. */
1356 if (matches
== NULL
)
1357 matches
= try_complete_all_possible (&state
, text
, lc_start
);
1359 /* And finally if nothing found, try complete directory name */
1360 if (matches
== NULL
)
1362 state
.in_command_position
= 0;
1363 matches
= try_complete_all_possible (&state
, text
, lc_start
);
1366 g_free (state
.word
);
1368 if (matches
!= NULL
&&
1369 (flags
& (INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_SHELL_ESC
)) !=
1370 (INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_SHELL_ESC
))
1372 /* FIXME: HACK? INPUT_COMPLETE_SHELL_ESC is used only in command line. */
1375 for (m
= matches
; *m
!= NULL
; m
++)
1380 *m
= strutils_shell_escape (*m
);
1388 /* --------------------------------------------------------------------------------------------- */
1391 complete_engine_fill_completions (WInput
* in
)
1394 const char *word_separators
;
1396 word_separators
= (in
->completion_flags
& INPUT_COMPLETE_SHELL_ESC
) ? " \t;|<>" : "\t;|<>";
1398 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1403 /* get symbol before in->point */
1406 for (i
= in
->point
- 1; i
> 0; i
--)
1410 for (; s
>= in
->buffer
; str_prev_char (&s
))
1412 start
= s
- in
->buffer
;
1413 if (strchr (word_separators
, *s
) != NULL
&& !strutils_is_char_escaped (in
->buffer
, s
))
1420 start
= s
- in
->buffer
;
1423 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1426 /* --------------------------------------------------------------------------------------------- */
1428 /* declared in lib/widget/input.h */
1430 complete (WInput
* in
)
1434 if (!str_is_valid_string (in
->buffer
))
1437 if (in
->completions
!= NULL
)
1438 engine_flags
= DO_QUERY
;
1441 engine_flags
= DO_INSERTION
;
1443 if (mc_global
.widget
.show_all_if_ambiguous
)
1444 engine_flags
|= DO_QUERY
;
1447 while (complete_engine (in
, engine_flags
))
1451 /* --------------------------------------------------------------------------------------------- */