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')
75 #define DO_INSERTION 1
78 /*** file scope type declarations ****************************************************************/
80 typedef char *CompletionFunction (const char *text
, int state
, input_complete_t flags
);
84 size_t in_command_position
;
90 input_complete_t flags
;
91 } try_complete_automation_state_t
;
93 /*** file scope variables ************************************************************************/
95 static char **hosts
= NULL
;
96 static char **hosts_p
= NULL
;
97 static int hosts_alloclen
= 0;
99 static int query_height
, query_width
;
100 static WInput
*input
;
102 static int start
= 0;
105 /*** file scope functions ************************************************************************/
106 /* --------------------------------------------------------------------------------------------- */
108 char **try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
);
109 void complete_engine_fill_completions (WInput
* in
);
111 #ifdef DO_COMPLETION_DEBUG
113 * Useful to print/debug completion flags
116 show_c_flags (input_complete_t flags
)
118 static char s_cf
[] = "FHCVUDS";
120 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) != 0 ? 'F' : ' ';
121 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) != 0 ? 'H' : ' ';
122 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) != 0 ? 'C' : ' ';
123 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) != 0 ? 'V' : ' ';
124 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) != 0 ? 'U' : ' ';
125 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) != 0 ? 'D' : ' ';
126 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) != 0 ? 'S' : ' ';
130 #endif /* DO_CMPLETION_DEBUG */
132 /* --------------------------------------------------------------------------------------------- */
135 filename_completion_function (const char *text
, int state
, input_complete_t flags
)
137 static DIR *directory
= NULL
;
138 static char *filename
= NULL
;
139 static char *dirname
= NULL
;
140 static char *users_dirname
= NULL
;
141 static size_t filename_len
= 0;
142 static vfs_path_t
*dirname_vpath
= NULL
;
144 gboolean isdir
= TRUE
, isexec
= FALSE
;
145 struct dirent
*entry
= NULL
;
147 SHOW_C_CTX ("filename_completion_function");
149 if (text
!= NULL
&& (flags
& INPUT_COMPLETE_SHELL_ESC
) != 0)
155 u_text
= strutils_shell_unescape (text
);
157 result
= filename_completion_function (u_text
, state
, flags
& (~INPUT_COMPLETE_SHELL_ESC
));
160 e_result
= strutils_shell_escape (result
);
166 /* If we're starting the match process, initialize us a bit. */
173 g_free (users_dirname
);
174 vfs_path_free (dirname_vpath
);
176 if ((*text
!= '\0') && (temp
= strrchr (text
, PATH_SEP
)) != NULL
)
178 filename
= g_strdup (++temp
);
179 dirname
= g_strndup (text
, temp
- text
);
183 dirname
= g_strdup (".");
184 filename
= g_strdup (text
);
187 /* We aren't done yet. We also support the "~user" syntax. */
189 /* Save the version of the directory that the user typed. */
190 users_dirname
= dirname
;
191 dirname
= tilde_expand (dirname
);
192 canonicalize_pathname (dirname
);
193 dirname_vpath
= vfs_path_from_str (dirname
);
195 /* Here we should do something with variable expansion
197 Maybe a dream - UNIMPLEMENTED yet. */
199 directory
= mc_opendir (dirname_vpath
);
200 filename_len
= strlen (filename
);
203 /* Now that we have some state, we can read the directory. */
205 while (directory
!= NULL
&& (entry
= mc_readdir (directory
)) != NULL
)
207 if (!str_is_valid_string (entry
->d_name
))
210 /* Special case for no filename.
211 All entries except "." and ".." match. */
212 if (filename_len
== 0)
214 if (DIR_IS_DOT (entry
->d_name
) || DIR_IS_DOTDOT (entry
->d_name
))
219 /* Otherwise, if these match up to the length of filename, then
220 it may be a match. */
221 if ((entry
->d_name
[0] != filename
[0]) ||
222 ((NLENGTH (entry
)) < filename_len
) ||
223 strncmp (filename
, entry
->d_name
, filename_len
) != 0)
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)
245 if (!S_ISDIR (tempstat
.st_mode
))
249 if ((my_uid
== 0 && (tempstat
.st_mode
& 0111) != 0) ||
250 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100) != 0) ||
251 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010) != 0) ||
252 (tempstat
.st_mode
& 0001) != 0)
258 /* stat failed, strange. not a dir in any case */
261 vfs_path_free (tmp_vpath
);
264 if ((flags
& INPUT_COMPLETE_COMMANDS
) != 0 && (isexec
|| isdir
))
266 if ((flags
& INPUT_COMPLETE_CD
) != 0 && isdir
)
268 if ((flags
& INPUT_COMPLETE_FILENAMES
) != 0)
274 if (directory
!= NULL
)
276 mc_closedir (directory
);
279 MC_PTR_FREE (dirname
);
280 vfs_path_free (dirname_vpath
);
281 dirname_vpath
= NULL
;
282 MC_PTR_FREE (filename
);
283 MC_PTR_FREE (users_dirname
);
290 temp
= g_string_sized_new (16);
292 if (users_dirname
!= NULL
&& (users_dirname
[0] != '.' || users_dirname
[1] != '\0'))
294 g_string_append (temp
, users_dirname
);
296 /* We need a '/' at the end. */
297 if (!IS_PATH_SEP (temp
->str
[temp
->len
- 1]))
298 g_string_append_c (temp
, PATH_SEP
);
300 g_string_append (temp
, entry
->d_name
);
302 g_string_append_c (temp
, PATH_SEP
);
304 return g_string_free (temp
, FALSE
);
308 /* --------------------------------------------------------------------------------------------- */
309 /** We assume here that text[0] == '~' , if you want to call it in another way,
310 you have to change the code */
313 username_completion_function (const char *text
, int state
, input_complete_t flags
)
315 static struct passwd
*entry
= NULL
;
316 static size_t userlen
= 0;
319 SHOW_C_CTX ("username_completion_function");
321 if (text
[0] == '\\' && text
[1] == '~')
324 { /* Initialization stuff */
326 userlen
= strlen (text
+ 1);
329 while ((entry
= getpwent ()) != NULL
)
331 /* Null usernames should result in all users as possible completions. */
334 if (text
[1] == entry
->pw_name
[0] && strncmp (text
+ 1, entry
->pw_name
, userlen
) == 0)
339 return g_strconcat ("~", entry
->pw_name
, PATH_SEP_STR
, (char *) NULL
);
345 /* --------------------------------------------------------------------------------------------- */
346 /** We assume text [0] == '$' and want to have a look at text [1], if it is
347 equal to '{', so that we should append '}' at the end */
350 variable_completion_function (const char *text
, int state
, input_complete_t flags
)
352 static char **env_p
= NULL
;
353 static gboolean isbrace
= FALSE
;
354 static size_t varlen
= 0;
355 const char *p
= NULL
;
358 SHOW_C_CTX ("variable_completion_function");
361 { /* Initialization stuff */
362 isbrace
= (text
[1] == '{');
363 varlen
= strlen (text
+ 1 + isbrace
);
367 while (*env_p
!= NULL
)
369 p
= strchr (*env_p
, '=');
370 if (p
!= NULL
&& ((size_t) (p
- *env_p
) >= varlen
)
371 && strncmp (text
+ 1 + isbrace
, *env_p
, varlen
) == 0)
382 temp
= g_string_new_len (*env_p
, p
- *env_p
);
386 g_string_prepend_c (temp
, '{');
387 g_string_append_c (temp
, '}');
389 g_string_prepend_c (temp
, '$');
393 return g_string_free (temp
, FALSE
);
397 /* --------------------------------------------------------------------------------------------- */
400 fetch_hosts (const char *filename
)
408 file
= fopen (filename
, "r");
412 while (fgets (buffer
, sizeof (buffer
) - 1, file
) != NULL
)
414 /* Skip to first character. */
415 for (bi
= buffer
; bi
[0] != '\0' && str_isspace (bi
); str_next_char (&bi
))
418 /* Ignore comments... */
422 /* Handle $include. */
423 if (strncmp (bi
, "$include ", 9) == 0)
425 char *includefile
, *t
;
427 /* Find start of filename. */
428 includefile
= bi
+ 9;
429 while (*includefile
!= '\0' && whitespace (*includefile
))
433 /* Find end of filename. */
434 while (t
[0] != '\0' && !str_isspace (t
))
438 fetch_hosts (includefile
);
443 while (bi
[0] != '\0' && !str_isspace (bi
))
446 /* Get the host names separated by white space. */
447 while (bi
[0] != '\0' && bi
[0] != '#')
449 while (bi
[0] != '\0' && str_isspace (bi
))
453 for (lc_start
= bi
; bi
[0] != '\0' && !str_isspace (bi
); str_next_char (&bi
))
459 name
= g_strndup (lc_start
, bi
- lc_start
);
467 if (j
>= hosts_alloclen
)
469 hosts_alloclen
+= 30;
470 hosts
= g_renew (char *, hosts
, hosts_alloclen
+ 1);
474 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
475 if (strcmp (name
, *host_p
) == 0)
476 break; /* We do not want any duplicates */
478 if (host_p
== hosts_p
)
492 /* --------------------------------------------------------------------------------------------- */
495 hostname_completion_function (const char *text
, int state
, input_complete_t flags
)
497 static char **host_p
= NULL
;
498 static size_t textstart
= 0;
499 static size_t textlen
= 0;
502 SHOW_C_CTX ("hostname_completion_function");
505 { /* Initialization stuff */
510 hosts
= g_new (char *, hosts_alloclen
+ 1);
513 p
= getenv ("HOSTFILE");
514 fetch_hosts (p
!= NULL
? p
: "/etc/hosts");
516 textstart
= (*text
== '@') ? 1 : 0;
517 textlen
= strlen (text
+ textstart
);
520 for (; *host_p
!= NULL
; host_p
++)
523 break; /* Match all of them */
524 if (strncmp (text
+ textstart
, *host_p
, textlen
) == 0)
538 temp
= g_string_sized_new (8);
541 g_string_append_c (temp
, '@');
542 g_string_append (temp
, *host_p
);
545 return g_string_free (temp
, FALSE
);
549 /* --------------------------------------------------------------------------------------------- */
551 * This is the function to call when the word to complete is in a position
552 * where a command word can be found. It looks around $PATH, looking for
553 * commands that match. It also scans aliases, function names, and the
554 * table of shell built-ins.
558 command_completion_function (const char *text
, int state
, input_complete_t flags
)
560 static const char *path_end
= NULL
;
561 static gboolean isabsolute
= FALSE
;
562 static int phase
= 0;
563 static size_t text_len
= 0;
564 static const char *const *words
= NULL
;
565 static char *path
= NULL
;
566 static char *cur_path
= NULL
;
567 static char *cur_word
= NULL
;
568 static int init_state
= 0;
569 static const char *const bash_reserved
[] = {
570 "if", "then", "else", "elif", "fi", "case", "esac", "for",
571 "select", "while", "until", "do", "done", "in", "function", 0
573 static const char *const bash_builtins
[] = {
574 "alias", "bg", "bind", "break", "builtin", "cd", "command",
575 "continue", "declare", "dirs", "echo", "enable", "eval",
576 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
577 "help", "history", "jobs", "kill", "let", "local", "logout",
578 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
579 "shift", "source", "suspend", "test", "times", "trap", "type",
580 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
586 SHOW_C_CTX ("command_completion_function");
588 if ((flags
& INPUT_COMPLETE_COMMANDS
) == 0)
591 u_text
= strutils_shell_unescape (text
);
592 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
595 { /* Initialize us a little bit */
596 isabsolute
= strchr (u_text
, PATH_SEP
) != NULL
;
599 words
= bash_reserved
;
601 text_len
= strlen (u_text
);
605 path
= g_strdup (getenv ("PATH"));
609 path_end
= strchr (p
, '\0');
610 while ((p
= strchr (p
, PATH_ENV_SEP
)) != NULL
)
619 p
= filename_completion_function (u_text
, state
, flags
);
625 p
= strutils_shell_escape (p
);
636 case 0: /* Reserved words */
637 for (; *words
!= NULL
; words
++)
638 if (strncmp (*words
, u_text
, text_len
) == 0)
641 return g_strdup (*(words
++));
644 words
= bash_builtins
;
646 case 1: /* Builtin commands */
647 for (; *words
!= NULL
; words
++)
648 if (strncmp (*words
, u_text
, text_len
) == 0)
651 return g_strdup (*(words
++));
659 case 2: /* And looking through the $PATH */
660 while (found
== NULL
)
662 if (cur_word
== NULL
)
666 if (cur_path
>= path_end
)
668 expanded
= tilde_expand (*cur_path
!= '\0' ? cur_path
: ".");
669 cur_word
= mc_build_filename (expanded
, u_text
, (char *) NULL
);
671 canonicalize_pathname (cur_word
);
672 cur_path
= strchr (cur_path
, '\0') + 1;
675 found
= filename_completion_function (cur_word
, state
- init_state
, flags
);
677 MC_PTR_FREE (cur_word
);
688 p
= strrchr (found
, PATH_SEP
);
693 found
= strutils_shell_escape (p
+ 1);
702 /* --------------------------------------------------------------------------------------------- */
705 match_compare (const void *a
, const void *b
)
707 return strcmp (*(char *const *) a
, *(char *const *) b
);
710 /* --------------------------------------------------------------------------------------------- */
711 /** Returns an array of char * matches with the longest common denominator
712 in the 1st entry. Then a NULL terminated list of different possible
714 You have to supply your own CompletionFunction with the word you
715 want to complete as the first argument and an count of previous matches
717 In case no matches were found we return NULL. */
720 completion_matches (const char *text
, CompletionFunction entry_function
, input_complete_t flags
)
722 /* Number of slots in match_list. */
723 size_t match_list_size
= 30;
724 /* The list of matches. */
726 /* Number of matches actually found. */
729 /* Temporary string binder. */
732 match_list
= g_new (char *, match_list_size
+ 1);
733 match_list
[1] = NULL
;
735 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
)
737 if (matches
+ 1 == match_list_size
)
739 match_list_size
+= 30;
740 match_list
= (char **) g_renew (char *, match_list
, match_list_size
+ 1);
742 match_list
[++matches
] = string
;
743 match_list
[matches
+ 1] = NULL
;
746 /* If there were any matches, then look through them finding out the
747 lowest common denominator. That then becomes match_list[0]. */
749 MC_PTR_FREE (match_list
); /* There were no matches. */
752 /* If only one match, just use that. */
755 match_list
[0] = match_list
[1];
756 match_list
[1] = NULL
;
761 int low
= 4096; /* Count of max-matched characters. */
764 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
766 /* And compare each member of the list with
767 the next, finding out where they stop matching.
768 If we find two equal strings, we have to put one away... */
771 while (j
< matches
+ 1)
776 for (si
= match_list
[i
], sj
= match_list
[j
]; si
[0] != '\0' && sj
[0] != '\0';)
779 ni
= str_get_next_char (si
);
780 nj
= str_get_next_char (sj
);
782 if (ni
- si
!= nj
- sj
)
784 if (strncmp (si
, sj
, ni
- si
) != 0)
791 if (si
[0] == '\0' && sj
[0] == '\0')
792 { /* Two equal strings */
793 g_free (match_list
[j
]);
797 continue; /* Look for a run of equal strings */
799 else if (low
> si
- match_list
[i
])
800 low
= si
- match_list
[i
];
801 if (i
+ 1 != j
) /* So there's some gap */
802 match_list
[i
+ 1] = match_list
[j
];
807 match_list
[matches
+ 1] = NULL
;
808 match_list
[0] = g_strndup (match_list
[1], low
);
815 /* --------------------------------------------------------------------------------------------- */
816 /** Check if directory completion is needed */
818 check_is_cd (const char *text
, int lc_start
, input_complete_t flags
)
822 SHOW_C_CTX ("check_is_cd");
824 if ((flags
& INPUT_COMPLETE_CD
) == 0)
827 /* Skip initial spaces */
830 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
833 /* Check if the command is "cd" and the cursor is after it */
834 return (p
[0] == 'c' && p
[1] == 'd' && str_isspace (p
+ 2) && p
+ 2 < q
);
837 /* --------------------------------------------------------------------------------------------- */
840 try_complete_commands_prepare (try_complete_automation_state_t
* state
, char *text
, int *lc_start
)
842 const char *command_separator_chars
= ";|&{(`";
849 ti
= str_get_prev_char (&text
[*lc_start
]);
850 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
855 state
->in_command_position
++;
856 else if (strchr (command_separator_chars
, ti
[0]) != NULL
)
858 state
->in_command_position
++;
861 int this_char
, prev_char
;
863 /* Handle the two character tokens '>&', '<&', and '>|'.
864 We are not in a command position after one of these. */
866 prev_char
= str_get_prev_char (ti
)[0];
869 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>'))
870 || (this_char
== '|' && prev_char
== '>') || (ti
!= text
871 && str_get_prev_char (ti
)[0] == '\\'))
872 state
->in_command_position
= 0;
877 /* --------------------------------------------------------------------------------------------- */
880 try_complete_find_start_sign (try_complete_automation_state_t
* state
)
882 if ((state
->flags
& INPUT_COMPLETE_COMMANDS
) != 0)
883 state
->p
= strrchr (state
->word
, '`');
884 if ((state
->flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
)) != 0)
886 state
->q
= strrchr (state
->word
, '$');
888 /* don't substitute variable in \$ case */
889 if (strutils_is_char_escaped (state
->word
, state
->q
))
893 qlen
= strlen (state
->q
);
895 memmove (state
->q
- 1, state
->q
, qlen
+ 1);
897 state
->flags
&= ~INPUT_COMPLETE_VARIABLES
;
901 if ((state
->flags
& INPUT_COMPLETE_HOSTNAMES
) != 0)
902 state
->r
= strrchr (state
->word
, '@');
903 if (state
->q
!= NULL
&& state
->q
[1] == '(' && (state
->flags
& INPUT_COMPLETE_COMMANDS
) != 0)
905 if (state
->q
> state
->p
)
906 state
->p
= str_get_next_char (state
->q
);
911 /* --------------------------------------------------------------------------------------------- */
914 try_complete_all_possible (try_complete_automation_state_t
* state
, char *text
, int *lc_start
)
916 char **matches
= NULL
;
918 if (state
->in_command_position
!= 0)
920 SHOW_C_CTX ("try_complete:cmd_subst");
922 completion_matches (state
->word
, command_completion_function
,
923 state
->flags
& (~INPUT_COMPLETE_FILENAMES
));
925 else if ((state
->flags
& INPUT_COMPLETE_FILENAMES
) != 0)
928 state
->flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
929 SHOW_C_CTX ("try_complete:filename_subst_1");
930 matches
= completion_matches (state
->word
, filename_completion_function
, state
->flags
);
932 if (matches
== NULL
&& state
->is_cd
&& !IS_PATH_SEP (*state
->word
) && *state
->word
!= '~')
934 state
->q
= text
+ *lc_start
;
935 for (state
->p
= text
;
936 *state
->p
!= '\0' && state
->p
< state
->q
&& (*state
->p
== ' '
937 || *state
->p
== '\t');
938 str_next_char (&state
->p
))
940 if (strncmp (state
->p
, "cd", 2) == 0)
942 *state
->p
!= '\0' && state
->p
< state
->q
&& (*state
->p
== ' '
943 || *state
->p
== '\t');
944 str_next_char (&state
->p
))
946 if (state
->p
== state
->q
)
948 char *cdpath_ref
, *cdpath
;
951 cdpath_ref
= g_strdup (getenv ("CDPATH"));
953 c
= (cdpath
== NULL
) ? '\0' : ':';
955 while (matches
== NULL
&& c
== ':')
959 s
= strchr (cdpath
, ':');
960 /* cppcheck-suppress nullPointer */
962 s
= strchr (cdpath
, '\0');
967 state
->r
= mc_build_filename (cdpath
, state
->word
, (char *) NULL
);
968 SHOW_C_CTX ("try_complete:filename_subst_2");
970 completion_matches (state
->r
, filename_completion_function
,
975 cdpath
= str_get_next_char (s
);
984 /* --------------------------------------------------------------------------------------------- */
987 insert_text (WInput
* in
, char *text
, ssize_t size
)
992 text_len
= strlen (text
);
993 buff_len
= str_length (in
->buffer
);
994 size
= MIN (size
, (ssize_t
) text_len
) + start
- end
;
995 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
)
997 /* Expand the buffer */
999 Widget
*w
= WIDGET (in
);
1001 narea
= g_try_realloc (in
->buffer
, in
->current_max_size
+ size
+ w
->cols
);
1005 in
->current_max_size
+= size
+ w
->cols
;
1008 if (strlen (in
->buffer
) + 1 < (size_t) in
->current_max_size
)
1011 memmove (in
->buffer
+ end
+ size
, in
->buffer
+ end
, strlen (&in
->buffer
[end
]) + 1);
1012 memmove (in
->buffer
+ start
, text
, size
- (start
- end
));
1013 in
->point
+= str_length (in
->buffer
) - buff_len
;
1014 input_update (in
, TRUE
);
1021 /* --------------------------------------------------------------------------------------------- */
1024 query_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
1028 WDialog
*h
= DIALOG (w
);
1044 /* exit from completion list if input line is empty */
1050 /* Refill the list box and start again */
1051 else if (end
== min_end
)
1053 end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1054 input_handle_char (input
, parm
);
1055 h
->ret_value
= B_USER
;
1064 new_end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1066 for (i
= 0, e
= listbox_get_first_link (LISTBOX (h
->current
->data
));
1067 e
!= NULL
; i
++, e
= g_list_next (e
))
1069 WLEntry
*le
= LENTRY (e
->data
);
1071 if (strncmp (input
->buffer
+ start
, le
->text
, new_end
- start
) == 0)
1073 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1075 input_handle_char (input
, parm
);
1076 widget_redraw (WIDGET (h
->current
->data
));
1084 if (parm
< 32 || parm
> 255)
1087 if (input_key_is_in_map (input
, parm
) != 2)
1088 return MSG_NOT_HANDLED
;
1093 /* This means we want to refill the list box and start again */
1094 h
->ret_value
= B_USER
;
1099 static char buff
[MB_LEN_MAX
] = "";
1102 int need_redraw
= 0;
1104 char *last_text
= NULL
;
1106 buff
[bl
++] = (char) parm
;
1109 switch (str_is_valid_char (buff
, bl
))
1120 for (i
= 0, e
= listbox_get_first_link (LISTBOX (h
->current
->data
));
1121 e
!= NULL
; i
++, e
= g_list_next (e
))
1123 WLEntry
*le
= LENTRY (e
->data
);
1125 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
) == 0
1126 && strncmp (&le
->text
[end
- start
], buff
, bl
) == 0)
1128 if (need_redraw
== 0)
1131 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1132 last_text
= le
->text
;
1140 /* count symbols between start and end */
1141 for (si
= le
->text
+ start
; si
< le
->text
+ end
;
1142 str_next_char (&si
), si_num
++)
1144 for (sl
= last_text
+ start
; sl
< last_text
+ end
;
1145 str_next_char (&sl
), sl_num
++)
1148 /* pointers to next symbols */
1149 si
= &le
->text
[str_offset_to_pos (le
->text
, ++si_num
)];
1150 sl
= &last_text
[str_offset_to_pos (last_text
, ++sl_num
)];
1152 while (si
[0] != '\0' && sl
[0] != '\0')
1154 char *nexti
, *nextl
;
1156 nexti
= str_get_next_char (si
);
1157 nextl
= str_get_next_char (sl
);
1159 if (nexti
- si
!= nextl
- sl
|| strncmp (si
, sl
, nexti
- si
) != 0)
1168 last_text
= le
->text
;
1170 si
= &last_text
[str_offset_to_pos (last_text
, si_num
)];
1171 if (low
> si
- last_text
)
1172 low
= si
- last_text
;
1179 if (need_redraw
== 2)
1181 insert_text (input
, last_text
, low
);
1182 widget_redraw (WIDGET (h
->current
->data
));
1184 else if (need_redraw
== 1)
1186 h
->ret_value
= B_ENTER
;
1195 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
1199 /* --------------------------------------------------------------------------------------------- */
1201 /** Returns TRUE if the user would like to see us again */
1203 complete_engine (WInput
* in
, int what_to_do
)
1205 if (in
->completions
!= NULL
&& str_offset_to_pos (in
->buffer
, in
->point
) != end
)
1206 input_free_completions (in
);
1208 if (in
->completions
== NULL
)
1209 complete_engine_fill_completions (in
);
1211 if (in
->completions
== NULL
)
1215 if ((what_to_do
& DO_INSERTION
) != 0
1216 || ((what_to_do
& DO_QUERY
) != 0 && in
->completions
[1] == NULL
))
1218 char *lc_complete
= in
->completions
[0];
1220 if (!insert_text (in
, lc_complete
, strlen (lc_complete
)) || in
->completions
[1] != NULL
)
1223 input_free_completions (in
);
1226 if ((what_to_do
& DO_QUERY
) != 0 && in
->completions
!= NULL
&& in
->completions
[1] != NULL
)
1228 int maxlen
= 0, count
= 0, i
;
1230 int start_x
, start_y
;
1233 WListbox
*query_list
;
1235 for (p
= in
->completions
+ 1; *p
!= NULL
; count
++, p
++)
1237 i
= str_term_width1 (*p
);
1242 start_x
= WIDGET (in
)->x
;
1243 start_y
= WIDGET (in
)->y
;
1244 if (start_y
- 2 >= count
)
1246 y
= start_y
- 2 - count
;
1249 else if (start_y
>= LINES
- start_y
- 1)
1257 h
= LINES
- start_y
- 1;
1259 x
= start
- in
->term_first_shown
- 2 + start_x
;
1273 query_dlg
= dlg_create (TRUE
, y
, x
, query_height
, query_width
, WPOS_KEEP_DEFAULT
, TRUE
,
1274 dialog_colors
, query_callback
, NULL
, "[Completion]", NULL
);
1275 query_list
= listbox_new (1, 1, h
- 2, w
- 2, FALSE
, NULL
);
1276 add_widget (query_dlg
, query_list
);
1278 for (p
= in
->completions
+ 1; *p
!= NULL
; p
++)
1279 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
, 0, *p
, NULL
, FALSE
);
1281 i
= dlg_run (query_dlg
);
1285 listbox_get_current (query_list
, &q
, NULL
);
1287 insert_text (in
, q
, strlen (q
));
1289 if (q
!= NULL
|| end
!= min_end
)
1290 input_free_completions (in
);
1291 dlg_destroy (query_dlg
);
1293 /* B_USER if user wants to start over again */
1294 return (i
== B_USER
);
1301 /* --------------------------------------------------------------------------------------------- */
1302 /*** public functions ****************************************************************************/
1303 /* --------------------------------------------------------------------------------------------- */
1305 /** Returns an array of matches, or NULL if none. */
1307 try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
)
1309 try_complete_automation_state_t state
;
1310 char **matches
= NULL
;
1312 memset (&state
, 0, sizeof (state
));
1313 state
.flags
= flags
;
1315 SHOW_C_CTX ("try_complete");
1316 state
.word
= g_strndup (text
+ *lc_start
, *lc_end
- *lc_start
);
1318 state
.is_cd
= check_is_cd (text
, *lc_start
, state
.flags
);
1320 /* Determine if this could be a command word. It is if it appears at
1321 the start of the line (ignoring preceding whitespace), or if it
1322 appears after a character that separates commands. And we have to
1323 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
1324 if (!state
.is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
) != 0)
1325 try_complete_commands_prepare (&state
, text
, lc_start
);
1327 try_complete_find_start_sign (&state
);
1329 /* Command substitution? */
1330 if (state
.p
> state
.q
&& state
.p
> state
.r
)
1332 SHOW_C_CTX ("try_complete:cmd_backq_subst");
1333 matches
= completion_matches (str_cget_next_char (state
.p
),
1334 command_completion_function
,
1335 state
.flags
& (~INPUT_COMPLETE_FILENAMES
));
1336 if (matches
!= NULL
)
1337 *lc_start
+= str_get_next_char (state
.p
) - state
.word
;
1340 /* Variable name? */
1341 else if (state
.q
> state
.p
&& state
.q
> state
.r
)
1343 SHOW_C_CTX ("try_complete:var_subst");
1344 matches
= completion_matches (state
.q
, variable_completion_function
, state
.flags
);
1345 if (matches
!= NULL
)
1346 *lc_start
+= state
.q
- state
.word
;
1349 /* Starts with '@', then look through the known hostnames for
1350 completion first. */
1351 else if (state
.r
> state
.p
&& state
.r
> state
.q
)
1353 SHOW_C_CTX ("try_complete:host_subst");
1354 matches
= completion_matches (state
.r
, hostname_completion_function
, state
.flags
);
1355 if (matches
!= NULL
)
1356 *lc_start
+= state
.r
- state
.word
;
1359 /* Starts with '~' and there is no slash in the word, then
1360 try completing this word as a username. */
1361 if (matches
== NULL
&& *state
.word
== '~' && (state
.flags
& INPUT_COMPLETE_USERNAMES
) != 0
1362 && strchr (state
.word
, PATH_SEP
) == NULL
)
1364 SHOW_C_CTX ("try_complete:user_subst");
1365 matches
= completion_matches (state
.word
, username_completion_function
, state
.flags
);
1368 /* If this word is in a command position, then
1369 complete over possible command names, including aliases, functions,
1370 and command names. */
1371 if (matches
== NULL
)
1372 matches
= try_complete_all_possible (&state
, text
, lc_start
);
1374 /* And finally if nothing found, try complete directory name */
1375 if (matches
== NULL
)
1377 state
.in_command_position
= 0;
1378 matches
= try_complete_all_possible (&state
, text
, lc_start
);
1381 g_free (state
.word
);
1383 if (matches
!= NULL
&&
1384 (flags
& (INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_SHELL_ESC
)) !=
1385 (INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_SHELL_ESC
))
1387 /* FIXME: HACK? INPUT_COMPLETE_SHELL_ESC is used only in command line. */
1390 for (m
= matches
; *m
!= NULL
; m
++)
1395 *m
= strutils_shell_escape (*m
);
1403 /* --------------------------------------------------------------------------------------------- */
1406 complete_engine_fill_completions (WInput
* in
)
1409 const char *word_separators
;
1411 word_separators
= (in
->completion_flags
& INPUT_COMPLETE_SHELL_ESC
) ? " \t;|<>" : "\t;|<>";
1413 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1418 /* get symbol before in->point */
1421 for (i
= in
->point
- 1; i
> 0; i
--)
1425 for (; s
>= in
->buffer
; str_prev_char (&s
))
1427 start
= s
- in
->buffer
;
1428 if (strchr (word_separators
, *s
) != NULL
&& !strutils_is_char_escaped (in
->buffer
, s
))
1435 start
= s
- in
->buffer
;
1438 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1441 /* --------------------------------------------------------------------------------------------- */
1443 /* declared in lib/widget/input.h */
1445 complete (WInput
* in
)
1449 if (!str_is_valid_string (in
->buffer
))
1452 if (in
->completions
!= NULL
)
1453 engine_flags
= DO_QUERY
;
1456 engine_flags
= DO_INSERTION
;
1458 if (mc_global
.widget
.show_all_if_ambiguous
)
1459 engine_flags
|= DO_QUERY
;
1462 while (complete_engine (in
, engine_flags
))
1466 /* --------------------------------------------------------------------------------------------- */