2 Input line filename/username/hostname/variable/command completion.
3 (Let mc type for you...)
5 Copyright (C) 1995-2016
6 Free Software Foundation, Inc.
10 Slava Zanko <slavazanko@gmail.com>, 2013
11 Andrew Borodin <aborodin@vmail.ru>, 2013
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 /** \file lib/widget/input_complete.c
30 * \brief Source: Input line filename/username/hostname/variable/command completion
40 #include <sys/types.h>
45 #include "lib/global.h"
47 #include "lib/tty/tty.h"
48 #include "lib/tty/key.h" /* XCTRL and ALT macros */
49 #include "lib/vfs/vfs.h"
50 #include "lib/strescape.h"
51 #include "lib/strutil.h"
53 #include "lib/widget.h"
55 #include "input_complete.h"
57 /*** global variables ****************************************************************************/
59 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
60 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
61 extern char **environ
;
64 /*** file scope macro definitions ****************************************************************/
66 /* #define DO_COMPLETION_DEBUG */
67 #ifdef DO_COMPLETION_DEBUG
68 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
70 #define SHOW_C_CTX(func)
71 #endif /* DO_CMPLETION_DEBUG */
73 #define whitespace(c) ((c) == ' ' || (c) == '\t')
74 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
76 #define DO_INSERTION 1
79 /*** file scope type declarations ****************************************************************/
81 typedef char *CompletionFunction (const char *text
, int state
, input_complete_t flags
);
85 size_t in_command_position
;
91 input_complete_t flags
;
92 } try_complete_automation_state_t
;
94 /*** file scope variables ************************************************************************/
96 static char **hosts
= NULL
;
97 static char **hosts_p
= NULL
;
98 static int hosts_alloclen
= 0;
100 static int query_height
, query_width
;
101 static WInput
*input
;
103 static int start
= 0;
106 /*** file scope functions ************************************************************************/
107 /* --------------------------------------------------------------------------------------------- */
109 char **try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
);
110 void complete_engine_fill_completions (WInput
* in
);
112 #ifdef DO_COMPLETION_DEBUG
114 * Useful to print/debug completion flags
117 show_c_flags (input_complete_t flags
)
119 static char s_cf
[] = "FHCVUDS";
121 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) ? 'F' : ' ';
122 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) ? 'H' : ' ';
123 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) ? 'C' : ' ';
124 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) ? 'V' : ' ';
125 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) ? 'U' : ' ';
126 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) ? 'D' : ' ';
127 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) ? 'S' : ' ';
131 #endif /* DO_CMPLETION_DEBUG */
133 /* --------------------------------------------------------------------------------------------- */
136 filename_completion_function (const char *text
, int state
, input_complete_t flags
)
138 static DIR *directory
= NULL
;
139 static char *filename
= NULL
;
140 static char *dirname
= NULL
;
141 static char *users_dirname
= NULL
;
142 static size_t filename_len
;
143 int isdir
= 1, isexec
= 0;
144 static vfs_path_t
*dirname_vpath
= NULL
;
146 struct dirent
*entry
= NULL
;
148 SHOW_C_CTX ("filename_completion_function");
150 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
156 u_text
= strutils_shell_unescape (text
);
158 result
= filename_completion_function (u_text
, state
, flags
& (~INPUT_COMPLETE_SHELL_ESC
));
161 e_result
= strutils_shell_escape (result
);
167 /* If we're starting the match process, initialize us a bit. */
174 g_free (users_dirname
);
175 vfs_path_free (dirname_vpath
);
177 if ((*text
!= '\0') && (temp
= strrchr (text
, PATH_SEP
)) != NULL
)
179 filename
= g_strdup (++temp
);
180 dirname
= g_strndup (text
, temp
- text
);
184 dirname
= g_strdup (".");
185 filename
= g_strdup (text
);
188 /* We aren't done yet. We also support the "~user" syntax. */
190 /* Save the version of the directory that the user typed. */
191 users_dirname
= dirname
;
192 dirname
= tilde_expand (dirname
);
193 canonicalize_pathname (dirname
);
194 dirname_vpath
= vfs_path_from_str (dirname
);
196 /* Here we should do something with variable expansion
198 Maybe a dream - UNIMPLEMENTED yet. */
200 directory
= mc_opendir (dirname_vpath
);
201 filename_len
= strlen (filename
);
204 /* Now that we have some state, we can read the directory. */
206 while (directory
&& (entry
= mc_readdir (directory
)))
208 if (!str_is_valid_string (entry
->d_name
))
211 /* Special case for no filename.
212 All entries except "." and ".." match. */
213 if (filename_len
== 0)
215 if (DIR_IS_DOT (entry
->d_name
) || DIR_IS_DOTDOT (entry
->d_name
))
220 /* Otherwise, if these match up to the length of filename, then
221 it may be a match. */
222 if ((entry
->d_name
[0] != filename
[0]) ||
223 ((NLENGTH (entry
)) < filename_len
) ||
224 strncmp (filename
, entry
->d_name
, filename_len
))
230 struct stat tempstat
;
231 vfs_path_t
*tmp_vpath
;
233 tmp_vpath
= vfs_path_build_filename (dirname
, entry
->d_name
, (char *) NULL
);
236 if (mc_stat (tmp_vpath
, &tempstat
) == 0)
238 uid_t my_uid
= getuid ();
239 gid_t my_gid
= getgid ();
241 if (!S_ISDIR (tempstat
.st_mode
))
244 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
245 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
246 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
247 (tempstat
.st_mode
& 0001))
253 /* stat failed, strange. not a dir in any case */
256 vfs_path_free (tmp_vpath
);
258 if ((flags
& INPUT_COMPLETE_COMMANDS
) && (isexec
|| isdir
))
260 if ((flags
& INPUT_COMPLETE_CD
) && isdir
)
262 if (flags
& (INPUT_COMPLETE_FILENAMES
))
270 mc_closedir (directory
);
273 MC_PTR_FREE (dirname
);
274 vfs_path_free (dirname_vpath
);
275 dirname_vpath
= NULL
;
276 MC_PTR_FREE (filename
);
277 MC_PTR_FREE (users_dirname
);
284 temp
= g_string_sized_new (16);
286 if (users_dirname
!= NULL
&& (users_dirname
[0] != '.' || users_dirname
[1] != '\0'))
288 g_string_append (temp
, users_dirname
);
290 /* We need a '/' at the end. */
291 if (!IS_PATH_SEP (temp
->str
[temp
->len
- 1]))
292 g_string_append_c (temp
, PATH_SEP
);
294 g_string_append (temp
, entry
->d_name
);
296 g_string_append_c (temp
, PATH_SEP
);
298 return g_string_free (temp
, FALSE
);
302 /* --------------------------------------------------------------------------------------------- */
303 /** We assume here that text[0] == '~' , if you want to call it in another way,
304 you have to change the code */
307 username_completion_function (const char *text
, int state
, input_complete_t flags
)
309 static struct passwd
*entry
;
310 static size_t userlen
;
313 SHOW_C_CTX ("username_completion_function");
315 if (text
[0] == '\\' && text
[1] == '~')
318 { /* Initialization stuff */
320 userlen
= strlen (text
+ 1);
322 while ((entry
= getpwent ()) != NULL
)
324 /* Null usernames should result in all users as possible completions. */
327 if (text
[1] == entry
->pw_name
[0] && !strncmp (text
+ 1, entry
->pw_name
, userlen
))
332 return g_strconcat ("~", entry
->pw_name
, PATH_SEP_STR
, (char *) NULL
);
338 /* --------------------------------------------------------------------------------------------- */
339 /** We assume text [0] == '$' and want to have a look at text [1], if it is
340 equal to '{', so that we should append '}' at the end */
343 variable_completion_function (const char *text
, int state
, input_complete_t flags
)
346 static unsigned int isbrace
;
347 static size_t varlen
;
348 const char *p
= NULL
;
351 SHOW_C_CTX ("variable_completion_function");
354 { /* Initialization stuff */
355 isbrace
= (text
[1] == '{') ? 1 : 0;
356 varlen
= strlen (text
+ 1 + isbrace
);
362 p
= strchr (*env_p
, '=');
363 if (p
&& ((size_t) (p
- *env_p
) >= varlen
) && !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
374 temp
= g_string_new_len (*env_p
, p
- *env_p
);
378 g_string_prepend_c (temp
, '{');
379 g_string_append_c (temp
, '}');
381 g_string_prepend_c (temp
, '$');
385 return g_string_free (temp
, FALSE
);
389 /* --------------------------------------------------------------------------------------------- */
392 fetch_hosts (const char *filename
)
394 FILE *file
= fopen (filename
, "r");
395 char buffer
[256], *name
;
402 while (fgets (buffer
, sizeof (buffer
) - 1, file
) != NULL
)
404 /* Skip to first character. */
405 for (bi
= buffer
; bi
[0] != '\0' && str_isspace (bi
); str_next_char (&bi
));
407 /* Ignore comments... */
410 /* Handle $include. */
411 if (!strncmp (bi
, "$include ", 9))
413 char *includefile
= bi
+ 9;
416 /* Find start of filename. */
417 while (*includefile
&& whitespace (*includefile
))
421 /* Find end of filename. */
422 while (t
[0] != '\0' && !str_isspace (t
))
426 fetch_hosts (includefile
);
431 while (bi
[0] != '\0' && !str_isspace (bi
))
434 /* Get the host names separated by white space. */
435 while (bi
[0] != '\0' && bi
[0] != '#')
437 while (bi
[0] != '\0' && str_isspace (bi
))
441 for (lc_start
= bi
; bi
[0] != '\0' && !str_isspace (bi
); str_next_char (&bi
));
443 if (bi
- lc_start
== 0)
446 name
= g_strndup (lc_start
, bi
- lc_start
);
450 if (hosts_p
- hosts
>= hosts_alloclen
)
455 hosts_alloclen
+= 30;
456 hosts
= g_renew (char *, hosts
, hosts_alloclen
+ 1);
459 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
460 if (!strcmp (name
, *host_p
))
461 break; /* We do not want any duplicates */
462 if (host_p
== hosts_p
)
475 /* --------------------------------------------------------------------------------------------- */
478 hostname_completion_function (const char *text
, int state
, input_complete_t flags
)
480 static char **host_p
;
481 static unsigned int textstart
;
482 static size_t textlen
;
485 SHOW_C_CTX ("hostname_completion_function");
488 { /* Initialization stuff */
493 hosts
= g_new (char *, hosts_alloclen
+ 1);
496 p
= getenv ("HOSTFILE");
497 fetch_hosts (p
!= NULL
? p
: "/etc/hosts");
499 textstart
= (*text
== '@') ? 1 : 0;
500 textlen
= strlen (text
+ textstart
);
503 for (; *host_p
!= NULL
; host_p
++)
506 break; /* Match all of them */
507 if (strncmp (text
+ textstart
, *host_p
, textlen
) == 0)
521 temp
= g_string_sized_new (8);
524 g_string_append_c (temp
, '@');
525 g_string_append (temp
, *host_p
);
528 return g_string_free (temp
, FALSE
);
532 /* --------------------------------------------------------------------------------------------- */
534 * This is the function to call when the word to complete is in a position
535 * where a command word can be found. It looks around $PATH, looking for
536 * commands that match. It also scans aliases, function names, and the
537 * table of shell built-ins.
541 command_completion_function (const char *_text
, int state
, input_complete_t flags
)
544 static const char *path_end
;
545 static gboolean isabsolute
;
547 static size_t text_len
;
548 static const char *const *words
;
550 static char *cur_path
;
551 static char *cur_word
;
552 static int init_state
;
553 static const char *const bash_reserved
[] = {
554 "if", "then", "else", "elif", "fi", "case", "esac", "for",
555 "select", "while", "until", "do", "done", "in", "function", 0
557 static const char *const bash_builtins
[] = {
558 "alias", "bg", "bind", "break", "builtin", "cd", "command",
559 "continue", "declare", "dirs", "echo", "enable", "eval",
560 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
561 "help", "history", "jobs", "kill", "let", "local", "logout",
562 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
563 "shift", "source", "suspend", "test", "times", "trap", "type",
564 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
568 /* cppcheck-suppress uninitvar */
569 SHOW_C_CTX ("command_completion_function");
571 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
574 text
= strutils_shell_unescape (_text
);
575 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
578 { /* Initialize us a little bit */
579 isabsolute
= strchr (text
, PATH_SEP
) != NULL
;
582 words
= bash_reserved
;
584 text_len
= strlen (text
);
588 path
= g_strdup (getenv ("PATH"));
592 path_end
= strchr (p
, '\0');
593 while ((p
= strchr (p
, PATH_ENV_SEP
)) != NULL
)
604 p
= filename_completion_function (text
, state
, flags
);
610 p
= strutils_shell_escape (p
);
621 case 0: /* Reserved words */
622 for (; *words
!= NULL
; words
++)
623 if (strncmp (*words
, text
, text_len
) == 0)
626 return g_strdup (*(words
++));
629 words
= bash_builtins
;
630 case 1: /* Builtin commands */
631 for (; *words
!= NULL
; words
++)
632 if (strncmp (*words
, text
, text_len
) == 0)
635 return g_strdup (*(words
++));
642 case 2: /* And looking through the $PATH */
649 if (cur_path
>= path_end
)
651 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
652 cur_word
= mc_build_filename (expanded
, text
, NULL
);
654 canonicalize_pathname (cur_word
);
655 cur_path
= strchr (cur_path
, 0) + 1;
658 found
= filename_completion_function (cur_word
, state
- init_state
, flags
);
660 MC_PTR_FREE (cur_word
);
670 p
= strrchr (found
, PATH_SEP
);
674 found
= strutils_shell_escape (p
+ 1);
683 /* --------------------------------------------------------------------------------------------- */
686 match_compare (const void *a
, const void *b
)
688 return strcmp (*(char **) a
, *(char **) b
);
691 /* --------------------------------------------------------------------------------------------- */
692 /** Returns an array of char * matches with the longest common denominator
693 in the 1st entry. Then a NULL terminated list of different possible
695 You have to supply your own CompletionFunction with the word you
696 want to complete as the first argument and an count of previous matches
698 In case no matches were found we return NULL. */
701 completion_matches (const char *text
, CompletionFunction entry_function
, input_complete_t flags
)
703 /* Number of slots in match_list. */
704 size_t match_list_size
= 30;
705 /* The list of matches. */
707 /* Number of matches actually found. */
710 /* Temporary string binder. */
713 match_list
= g_new (char *, match_list_size
+ 1);
714 match_list
[1] = NULL
;
716 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
)
718 if (matches
+ 1 == match_list_size
)
720 match_list_size
+= 30;
721 match_list
= (char **) g_renew (char *, match_list
, match_list_size
+ 1);
723 match_list
[++matches
] = string
;
724 match_list
[matches
+ 1] = NULL
;
727 /* If there were any matches, then look through them finding out the
728 lowest common denominator. That then becomes match_list[0]. */
732 /* If only one match, just use that. */
735 match_list
[0] = match_list
[1];
736 match_list
[1] = NULL
;
741 int low
= 4096; /* Count of max-matched characters. */
744 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
746 /* And compare each member of the list with
747 the next, finding out where they stop matching.
748 If we find two equal strings, we have to put one away... */
751 while (j
< matches
+ 1)
756 for (si
= match_list
[i
], sj
= match_list
[j
]; si
[0] && sj
[0];)
759 ni
= str_get_next_char (si
);
760 nj
= str_get_next_char (sj
);
762 if (ni
- si
!= nj
- sj
)
764 if (strncmp (si
, sj
, ni
- si
) != 0)
771 if (si
[0] == '\0' && sj
[0] == '\0')
772 { /* Two equal strings */
773 g_free (match_list
[j
]);
777 continue; /* Look for a run of equal strings */
779 else if (low
> si
- match_list
[i
])
780 low
= si
- match_list
[i
];
781 if (i
+ 1 != j
) /* So there's some gap */
782 match_list
[i
+ 1] = match_list
[j
];
787 match_list
[matches
+ 1] = NULL
;
788 match_list
[0] = g_strndup (match_list
[1], low
);
791 else /* There were no matches. */
792 MC_PTR_FREE (match_list
);
797 /* --------------------------------------------------------------------------------------------- */
798 /** Check if directory completion is needed */
800 check_is_cd (const char *text
, int lc_start
, input_complete_t flags
)
804 SHOW_C_CTX ("check_is_cd");
806 if ((flags
& INPUT_COMPLETE_CD
) == 0)
809 /* Skip initial spaces */
811 q
= (char *) text
+ lc_start
;
812 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
815 /* Check if the command is "cd" and the cursor is after it */
816 return (p
[0] == 'c' && p
[1] == 'd' && str_isspace (p
+ 2) && p
+ 2 < q
);
819 /* --------------------------------------------------------------------------------------------- */
822 try_complete_commands_prepare (try_complete_automation_state_t
* state
, char *text
, int *lc_start
)
824 const char *command_separator_chars
= ";|&{(`";
831 ti
= str_get_prev_char (&text
[*lc_start
]);
832 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
837 state
->in_command_position
++;
838 else if (strchr (command_separator_chars
, ti
[0]) != NULL
)
840 state
->in_command_position
++;
843 int this_char
, prev_char
;
845 /* Handle the two character tokens '>&', '<&', and '>|'.
846 We are not in a command position after one of these. */
848 prev_char
= str_get_prev_char (ti
)[0];
851 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>'))
852 || (this_char
== '|' && prev_char
== '>') || (ti
!= text
853 && str_get_prev_char (ti
)[0] == '\\'))
854 state
->in_command_position
= 0;
859 /* --------------------------------------------------------------------------------------------- */
862 try_complete_find_start_sign (try_complete_automation_state_t
* state
)
864 if (state
->flags
& INPUT_COMPLETE_COMMANDS
)
865 state
->p
= strrchr (state
->word
, '`');
866 if (state
->flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
868 state
->q
= strrchr (state
->word
, '$');
870 /* don't substitute variable in \$ case */
871 if (strutils_is_char_escaped (state
->word
, state
->q
))
875 qlen
= strlen (state
->q
);
877 memmove (state
->q
- 1, state
->q
, qlen
+ 1);
879 state
->flags
&= ~INPUT_COMPLETE_VARIABLES
;
883 if (state
->flags
& INPUT_COMPLETE_HOSTNAMES
)
884 state
->r
= strrchr (state
->word
, '@');
885 if (state
->q
&& state
->q
[1] == '(' && (state
->flags
& INPUT_COMPLETE_COMMANDS
))
887 if (state
->q
> state
->p
)
888 state
->p
= str_get_next_char (state
->q
);
893 /* --------------------------------------------------------------------------------------------- */
896 try_complete_all_possible (try_complete_automation_state_t
* state
, char *text
, int *lc_start
)
898 char **matches
= NULL
;
900 if (state
->in_command_position
!= 0)
902 SHOW_C_CTX ("try_complete:cmd_subst");
904 completion_matches (state
->word
, command_completion_function
,
905 state
->flags
& (~INPUT_COMPLETE_FILENAMES
));
907 else if ((state
->flags
& INPUT_COMPLETE_FILENAMES
) != 0)
910 state
->flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
911 SHOW_C_CTX ("try_complete:filename_subst_1");
912 matches
= completion_matches (state
->word
, filename_completion_function
, state
->flags
);
914 if (matches
== NULL
&& state
->is_cd
&& !IS_PATH_SEP (*state
->word
) && *state
->word
!= '~')
916 state
->q
= text
+ *lc_start
;
917 for (state
->p
= text
;
918 *state
->p
&& state
->p
< state
->q
&& (*state
->p
== ' ' || *state
->p
== '\t');
919 str_next_char (&state
->p
))
921 if (!strncmp (state
->p
, "cd", 2))
923 *state
->p
&& state
->p
< state
->q
&& (*state
->p
== ' ' || *state
->p
== '\t');
924 str_next_char (&state
->p
))
926 if (state
->p
== state
->q
)
928 char *const cdpath_ref
= g_strdup (getenv ("CDPATH"));
929 char *cdpath
= cdpath_ref
;
932 c
= (cdpath
== NULL
) ? '\0' : ':';
934 while (!matches
&& c
== ':')
938 s
= strchr (cdpath
, ':');
939 /* cppcheck-suppress nullPointer */
941 s
= strchr (cdpath
, '\0');
946 state
->r
= mc_build_filename (cdpath
, state
->word
, NULL
);
947 SHOW_C_CTX ("try_complete:filename_subst_2");
949 completion_matches (state
->r
, filename_completion_function
,
954 cdpath
= str_get_next_char (s
);
963 /* --------------------------------------------------------------------------------------------- */
966 insert_text (WInput
* in
, char *text
, ssize_t size
)
970 buff_len
= str_length (in
->buffer
);
971 size
= min (size
, (ssize_t
) strlen (text
)) + start
- end
;
972 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
)
974 /* Expand the buffer */
976 Widget
*w
= WIDGET (in
);
978 narea
= g_try_realloc (in
->buffer
, in
->current_max_size
+ size
+ w
->cols
);
982 in
->current_max_size
+= size
+ w
->cols
;
985 if (strlen (in
->buffer
) + 1 < (size_t) in
->current_max_size
)
988 memmove (in
->buffer
+ end
+ size
, in
->buffer
+ end
, strlen (&in
->buffer
[end
]) + 1);
989 memmove (in
->buffer
+ start
, text
, size
- (start
- end
));
990 in
->point
+= str_length (in
->buffer
) - buff_len
;
991 input_update (in
, TRUE
);
997 /* --------------------------------------------------------------------------------------------- */
1000 query_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
1004 WDialog
*h
= DIALOG (w
);
1020 /* exit from completion list if input line is empty */
1026 /* Refill the list box and start again */
1027 else if (end
== min_end
)
1029 end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1030 input_handle_char (input
, parm
);
1031 h
->ret_value
= B_USER
;
1040 new_end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1042 for (i
= 0, e
= listbox_get_first_link (LISTBOX (h
->current
->data
));
1043 e
!= NULL
; i
++, e
= g_list_next (e
))
1045 WLEntry
*le
= LENTRY (e
->data
);
1047 if (strncmp (input
->buffer
+ start
, le
->text
, new_end
- start
) == 0)
1049 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1051 input_handle_char (input
, parm
);
1052 widget_redraw (WIDGET (h
->current
->data
));
1060 if (parm
< 32 || parm
> 255)
1063 if (input_key_is_in_map (input
, parm
) != 2)
1064 return MSG_NOT_HANDLED
;
1069 /* This means we want to refill the list box and start again */
1070 h
->ret_value
= B_USER
;
1075 static char buff
[MB_LEN_MAX
] = "";
1078 int need_redraw
= 0;
1080 char *last_text
= NULL
;
1082 buff
[bl
++] = (char) parm
;
1084 switch (str_is_valid_char (buff
, bl
))
1095 for (i
= 0, e
= listbox_get_first_link (LISTBOX (h
->current
->data
));
1096 e
!= NULL
; i
++, e
= g_list_next (e
))
1098 WLEntry
*le
= LENTRY (e
->data
);
1100 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
) == 0
1101 && strncmp (&le
->text
[end
- start
], buff
, bl
) == 0)
1103 if (need_redraw
== 0)
1106 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1107 last_text
= le
->text
;
1115 /* count symbols between start and end */
1116 for (si
= le
->text
+ start
; si
< le
->text
+ end
;
1117 str_next_char (&si
), si_num
++)
1119 for (sl
= last_text
+ start
; sl
< last_text
+ end
;
1120 str_next_char (&sl
), sl_num
++)
1123 /* pointers to next symbols */
1124 si
= &le
->text
[str_offset_to_pos (le
->text
, ++si_num
)];
1125 sl
= &last_text
[str_offset_to_pos (last_text
, ++sl_num
)];
1127 while (si
[0] != '\0' && sl
[0] != '\0')
1129 char *nexti
, *nextl
;
1131 nexti
= str_get_next_char (si
);
1132 nextl
= str_get_next_char (sl
);
1134 if (nexti
- si
!= nextl
- sl
|| strncmp (si
, sl
, nexti
- si
) != 0)
1143 last_text
= le
->text
;
1145 si
= &last_text
[str_offset_to_pos (last_text
, si_num
)];
1146 if (low
> si
- last_text
)
1147 low
= si
- last_text
;
1154 if (need_redraw
== 2)
1156 insert_text (input
, last_text
, low
);
1157 widget_redraw (WIDGET (h
->current
->data
));
1159 else if (need_redraw
== 1)
1161 h
->ret_value
= B_ENTER
;
1170 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
1174 /* --------------------------------------------------------------------------------------------- */
1176 /** Returns 1 if the user would like to see us again */
1178 complete_engine (WInput
* in
, int what_to_do
)
1180 if (in
->completions
!= NULL
&& str_offset_to_pos (in
->buffer
, in
->point
) != end
)
1181 input_free_completions (in
);
1183 if (in
->completions
== NULL
)
1184 complete_engine_fill_completions (in
);
1186 if (in
->completions
!= NULL
)
1188 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1]))
1190 char *lc_complete
= in
->completions
[0];
1191 if (insert_text (in
, lc_complete
, strlen (lc_complete
)))
1193 if (in
->completions
[1])
1196 input_free_completions (in
);
1201 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1])
1203 int maxlen
= 0, i
, count
= 0;
1205 int start_x
, start_y
;
1208 WListbox
*query_list
;
1210 for (p
= in
->completions
+ 1; *p
!= NULL
; count
++, p
++)
1212 i
= str_term_width1 (*p
);
1216 start_x
= WIDGET (in
)->x
;
1217 start_y
= WIDGET (in
)->y
;
1218 if (start_y
- 2 >= count
)
1220 y
= start_y
- 2 - count
;
1225 if (start_y
>= LINES
- start_y
- 1)
1233 h
= LINES
- start_y
- 1;
1236 x
= start
- in
->term_first_shown
- 2 + start_x
;
1248 query_dlg
= dlg_create (TRUE
, y
, x
, query_height
, query_width
,
1249 dialog_colors
, query_callback
, NULL
,
1250 "[Completion]", NULL
, DLG_COMPACT
);
1251 query_list
= listbox_new (1, 1, h
- 2, w
- 2, FALSE
, NULL
);
1252 add_widget (query_dlg
, query_list
);
1253 for (p
= in
->completions
+ 1; *p
; p
++)
1254 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
, 0, *p
, NULL
, FALSE
);
1255 dlg_run (query_dlg
);
1257 if (query_dlg
->ret_value
== B_ENTER
)
1259 listbox_get_current (query_list
, &q
, NULL
);
1261 insert_text (in
, q
, strlen (q
));
1263 if (q
|| end
!= min_end
)
1264 input_free_completions (in
);
1265 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1266 dlg_destroy (query_dlg
);
1276 /* --------------------------------------------------------------------------------------------- */
1277 /*** public functions ****************************************************************************/
1278 /* --------------------------------------------------------------------------------------------- */
1280 /** Returns an array of matches, or NULL if none. */
1282 try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
)
1284 try_complete_automation_state_t state
;
1285 char **matches
= NULL
;
1287 memset (&state
, 0, sizeof (state
));
1288 state
.flags
= flags
;
1290 SHOW_C_CTX ("try_complete");
1291 state
.word
= g_strndup (text
+ *lc_start
, *lc_end
- *lc_start
);
1293 state
.is_cd
= check_is_cd (text
, *lc_start
, state
.flags
);
1295 /* Determine if this could be a command word. It is if it appears at
1296 the start of the line (ignoring preceding whitespace), or if it
1297 appears after a character that separates commands. And we have to
1298 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
1299 if (!state
.is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
))
1300 try_complete_commands_prepare (&state
, text
, lc_start
);
1302 try_complete_find_start_sign (&state
);
1304 /* Command substitution? */
1305 if (state
.p
> state
.q
&& state
.p
> state
.r
)
1307 SHOW_C_CTX ("try_complete:cmd_backq_subst");
1308 matches
= completion_matches (str_cget_next_char (state
.p
),
1309 command_completion_function
,
1310 state
.flags
& (~INPUT_COMPLETE_FILENAMES
));
1312 *lc_start
+= str_get_next_char (state
.p
) - state
.word
;
1315 /* Variable name? */
1316 else if (state
.q
> state
.p
&& state
.q
> state
.r
)
1318 SHOW_C_CTX ("try_complete:var_subst");
1319 matches
= completion_matches (state
.q
, variable_completion_function
, state
.flags
);
1321 *lc_start
+= state
.q
- state
.word
;
1324 /* Starts with '@', then look through the known hostnames for
1325 completion first. */
1326 else if (state
.r
> state
.p
&& state
.r
> state
.q
)
1328 SHOW_C_CTX ("try_complete:host_subst");
1329 matches
= completion_matches (state
.r
, hostname_completion_function
, state
.flags
);
1331 *lc_start
+= state
.r
- state
.word
;
1334 /* Starts with '~' and there is no slash in the word, then
1335 try completing this word as a username. */
1336 if (!matches
&& *state
.word
== '~' && (state
.flags
& INPUT_COMPLETE_USERNAMES
)
1337 && !strchr (state
.word
, PATH_SEP
))
1339 SHOW_C_CTX ("try_complete:user_subst");
1340 matches
= completion_matches (state
.word
, username_completion_function
, state
.flags
);
1343 /* If this word is in a command position, then
1344 complete over possible command names, including aliases, functions,
1345 and command names. */
1346 if (matches
== NULL
)
1347 matches
= try_complete_all_possible (&state
, text
, lc_start
);
1349 /* And finally if nothing found, try complete directory name */
1350 if (matches
== NULL
)
1352 state
.in_command_position
= 0;
1353 matches
= try_complete_all_possible (&state
, text
, lc_start
);
1356 g_free (state
.word
);
1358 if (matches
!= NULL
&&
1359 (flags
& (INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_SHELL_ESC
)) !=
1360 (INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_SHELL_ESC
))
1362 /* FIXME: HACK? INPUT_COMPLETE_SHELL_ESC is used only in command line. */
1365 for (m
= matches
; *m
!= NULL
; m
++)
1370 *m
= strutils_shell_escape (*m
);
1378 /* --------------------------------------------------------------------------------------------- */
1381 complete_engine_fill_completions (WInput
* in
)
1384 const char *word_separators
;
1386 word_separators
= (in
->completion_flags
& INPUT_COMPLETE_SHELL_ESC
) ? " \t;|<>" : "\t;|<>";
1388 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1393 /* get symbol before in->point */
1396 for (i
= in
->point
- 1; i
> 0; i
--)
1400 for (; s
>= in
->buffer
; str_prev_char (&s
))
1402 start
= s
- in
->buffer
;
1403 if (strchr (word_separators
, *s
) != NULL
&& !strutils_is_char_escaped (in
->buffer
, s
))
1410 start
= s
- in
->buffer
;
1413 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1416 /* --------------------------------------------------------------------------------------------- */
1418 /* declared in lib/widget/input.h */
1420 complete (WInput
* in
)
1424 if (!str_is_valid_string (in
->buffer
))
1427 if (in
->completions
!= NULL
)
1428 engine_flags
= DO_QUERY
;
1431 engine_flags
= DO_INSERTION
;
1433 if (mc_global
.widget
.show_all_if_ambiguous
)
1434 engine_flags
|= DO_QUERY
;
1437 while (complete_engine (in
, engine_flags
))
1441 /* --------------------------------------------------------------------------------------------- */