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.
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file lib/widget/input_complete.c
29 * \brief Source: Input line filename/username/hostname/variable/command completion
39 #include <sys/types.h>
44 #include "lib/global.h"
46 #include "lib/tty/tty.h"
47 #include "lib/tty/key.h" /* XCTRL and ALT macros */
48 #include "lib/vfs/vfs.h"
49 #include "lib/strescape.h"
50 #include "lib/strutil.h"
52 #include "lib/widget.h"
54 #include "input_complete.h"
56 /*** global variables ****************************************************************************/
58 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
59 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
60 extern char **environ
;
63 /*** file scope macro definitions ****************************************************************/
65 /* #define DO_COMPLETION_DEBUG */
66 #ifdef DO_COMPLETION_DEBUG
67 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
69 #define SHOW_C_CTX(func)
70 #endif /* DO_CMPLETION_DEBUG */
72 #define whitespace(c) ((c) == ' ' || (c) == '\t')
73 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
75 #define DO_INSERTION 1
78 /*** file scope type declarations ****************************************************************/
80 typedef char *CompletionFunction (const char *text
, int state
, input_complete_t flags
);
82 /*** file scope variables ************************************************************************/
84 static char **hosts
= NULL
;
85 static char **hosts_p
= NULL
;
86 static int hosts_alloclen
= 0;
88 static int query_height
, query_width
;
94 /*** file scope functions ************************************************************************/
95 /* --------------------------------------------------------------------------------------------- */
97 #ifdef DO_COMPLETION_DEBUG
99 * Useful to print/debug completion flags
102 show_c_flags (input_complete_t flags
)
104 static char s_cf
[] = "FHCVUDS";
106 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) ? 'F' : ' ';
107 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) ? 'H' : ' ';
108 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) ? 'C' : ' ';
109 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) ? 'V' : ' ';
110 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) ? 'U' : ' ';
111 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) ? 'D' : ' ';
112 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) ? 'S' : ' ';
116 #endif /* DO_CMPLETION_DEBUG */
118 /* --------------------------------------------------------------------------------------------- */
121 filename_completion_function (const char *text
, int state
, input_complete_t flags
)
123 static DIR *directory
= NULL
;
124 static char *filename
= NULL
;
125 static char *dirname
= NULL
;
126 static char *users_dirname
= NULL
;
127 static size_t filename_len
;
128 int isdir
= 1, isexec
= 0;
129 static vfs_path_t
*dirname_vpath
= NULL
;
131 struct dirent
*entry
= NULL
;
133 SHOW_C_CTX ("filename_completion_function");
135 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
141 u_text
= strutils_shell_unescape (text
);
143 result
= filename_completion_function (u_text
, state
, flags
& (~INPUT_COMPLETE_SHELL_ESC
));
146 e_result
= strutils_shell_escape (result
);
152 /* If we're starting the match process, initialize us a bit. */
159 g_free (users_dirname
);
160 vfs_path_free (dirname_vpath
);
162 if ((*text
!= '\0') && (temp
= strrchr (text
, PATH_SEP
)) != NULL
)
164 filename
= g_strdup (++temp
);
165 dirname
= g_strndup (text
, temp
- text
);
169 dirname
= g_strdup (".");
170 filename
= g_strdup (text
);
173 /* We aren't done yet. We also support the "~user" syntax. */
175 /* Save the version of the directory that the user typed. */
176 users_dirname
= dirname
;
177 dirname
= tilde_expand (dirname
);
178 canonicalize_pathname (dirname
);
179 dirname_vpath
= vfs_path_from_str (dirname
);
181 /* Here we should do something with variable expansion
183 Maybe a dream - UNIMPLEMENTED yet. */
185 directory
= mc_opendir (dirname_vpath
);
186 filename_len
= strlen (filename
);
189 /* Now that we have some state, we can read the directory. */
191 while (directory
&& (entry
= mc_readdir (directory
)))
193 if (!str_is_valid_string (entry
->d_name
))
196 /* Special case for no filename.
197 All entries except "." and ".." match. */
198 if (filename_len
== 0)
200 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
205 /* Otherwise, if these match up to the length of filename, then
206 it may be a match. */
207 if ((entry
->d_name
[0] != filename
[0]) ||
208 ((NLENGTH (entry
)) < filename_len
) ||
209 strncmp (filename
, entry
->d_name
, filename_len
))
215 struct stat tempstat
;
216 vfs_path_t
*tmp_vpath
;
218 tmp_vpath
= vfs_path_build_filename (dirname
, entry
->d_name
, (char *) NULL
);
221 if (mc_stat (tmp_vpath
, &tempstat
) == 0)
223 uid_t my_uid
= getuid ();
224 gid_t my_gid
= getgid ();
226 if (!S_ISDIR (tempstat
.st_mode
))
229 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
230 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
231 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
232 (tempstat
.st_mode
& 0001))
238 /* stat failed, strange. not a dir in any case */
241 vfs_path_free (tmp_vpath
);
243 if ((flags
& INPUT_COMPLETE_COMMANDS
) && (isexec
|| isdir
))
245 if ((flags
& INPUT_COMPLETE_CD
) && isdir
)
247 if (flags
& (INPUT_COMPLETE_FILENAMES
))
255 mc_closedir (directory
);
260 vfs_path_free (dirname_vpath
);
261 dirname_vpath
= NULL
;
264 g_free (users_dirname
);
265 users_dirname
= NULL
;
272 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1]))
274 size_t dirlen
= strlen (users_dirname
);
275 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
276 strcpy (temp
, users_dirname
);
277 /* We need a `/' at the end. */
278 if (users_dirname
[dirlen
- 1] != PATH_SEP
)
280 temp
[dirlen
] = PATH_SEP
;
281 temp
[dirlen
+ 1] = '\0';
283 strcat (temp
, entry
->d_name
);
287 temp
= g_malloc (2 + NLENGTH (entry
));
288 strcpy (temp
, entry
->d_name
);
291 strcat (temp
, PATH_SEP_STR
);
297 /* --------------------------------------------------------------------------------------------- */
298 /** We assume here that text[0] == '~' , if you want to call it in another way,
299 you have to change the code */
302 username_completion_function (const char *text
, int state
, input_complete_t flags
)
304 static struct passwd
*entry
;
305 static size_t userlen
;
308 SHOW_C_CTX ("username_completion_function");
310 if (text
[0] == '\\' && text
[1] == '~')
313 { /* Initialization stuff */
315 userlen
= strlen (text
+ 1);
317 while ((entry
= getpwent ()) != NULL
)
319 /* Null usernames should result in all users as possible completions. */
322 if (text
[1] == entry
->pw_name
[0] && !strncmp (text
+ 1, entry
->pw_name
, userlen
))
327 return g_strconcat ("~", entry
->pw_name
, PATH_SEP_STR
, (char *) NULL
);
333 /* --------------------------------------------------------------------------------------------- */
334 /** We assume text [0] == '$' and want to have a look at text [1], if it is
335 equal to '{', so that we should append '}' at the end */
338 variable_completion_function (const char *text
, int state
, input_complete_t flags
)
341 static int varlen
, isbrace
;
342 const char *p
= NULL
;
345 SHOW_C_CTX ("variable_completion_function");
348 { /* Initialization stuff */
349 isbrace
= (text
[1] == '{');
350 varlen
= strlen (text
+ 1 + isbrace
);
356 p
= strchr (*env_p
, '=');
357 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
366 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
371 memcpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
373 strcpy (temp
+ 2 + (p
- *env_p
), "}");
375 temp
[1 + p
- *env_p
] = 0;
381 /* --------------------------------------------------------------------------------------------- */
384 fetch_hosts (const char *filename
)
386 FILE *file
= fopen (filename
, "r");
387 char buffer
[256], *name
;
394 while (fgets (buffer
, 255, file
) != NULL
)
396 /* Skip to first character. */
397 for (bi
= buffer
; bi
[0] != '\0' && str_isspace (bi
); str_next_char (&bi
));
399 /* Ignore comments... */
402 /* Handle $include. */
403 if (!strncmp (bi
, "$include ", 9))
405 char *includefile
= bi
+ 9;
408 /* Find start of filename. */
409 while (*includefile
&& whitespace (*includefile
))
413 /* Find end of filename. */
414 while (t
[0] != '\0' && !str_isspace (t
))
418 fetch_hosts (includefile
);
423 while (bi
[0] != '\0' && !str_isspace (bi
))
426 /* Get the host names separated by white space. */
427 while (bi
[0] != '\0' && bi
[0] != '#')
429 while (bi
[0] != '\0' && str_isspace (bi
))
433 for (lc_start
= bi
; bi
[0] != '\0' && !str_isspace (bi
); str_next_char (&bi
));
435 if (bi
- lc_start
== 0)
438 name
= g_strndup (lc_start
, bi
- lc_start
);
442 if (hosts_p
- hosts
>= hosts_alloclen
)
444 int j
= hosts_p
- hosts
;
447 g_realloc ((void *) hosts
, ((hosts_alloclen
+= 30) + 1) * sizeof (char *));
450 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
451 if (!strcmp (name
, *host_p
))
452 break; /* We do not want any duplicates */
453 if (host_p
== hosts_p
)
466 /* --------------------------------------------------------------------------------------------- */
469 hostname_completion_function (const char *text
, int state
, input_complete_t flags
)
471 static char **host_p
;
472 static int textstart
, textlen
;
475 SHOW_C_CTX ("hostname_completion_function");
478 { /* Initialization stuff */
483 for (host_p
= hosts
; *host_p
; host_p
++)
487 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
490 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
492 textstart
= (*text
== '@') ? 1 : 0;
493 textlen
= strlen (text
+ textstart
);
499 break; /* Match all of them */
500 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
507 for (host_p
= hosts
; *host_p
; host_p
++)
515 char *temp
= g_malloc (2 + strlen (*host_p
));
519 strcpy (temp
+ textstart
, *host_p
);
525 /* --------------------------------------------------------------------------------------------- */
527 * This is the function to call when the word to complete is in a position
528 * where a command word can be found. It looks around $PATH, looking for
529 * commands that match. It also scans aliases, function names, and the
530 * table of shell built-ins.
534 command_completion_function (const char *_text
, int state
, input_complete_t flags
)
537 static const char *path_end
;
538 static gboolean isabsolute
;
540 static size_t text_len
;
541 static const char *const *words
;
543 static char *cur_path
;
544 static char *cur_word
;
545 static int init_state
;
546 static const char *const bash_reserved
[] = {
547 "if", "then", "else", "elif", "fi", "case", "esac", "for",
548 "select", "while", "until", "do", "done", "in", "function", 0
550 static const char *const bash_builtins
[] = {
551 "alias", "bg", "bind", "break", "builtin", "cd", "command",
552 "continue", "declare", "dirs", "echo", "enable", "eval",
553 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
554 "help", "history", "jobs", "kill", "let", "local", "logout",
555 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
556 "shift", "source", "suspend", "test", "times", "trap", "type",
557 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
561 SHOW_C_CTX ("command_completion_function");
563 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
566 text
= strutils_shell_unescape (_text
);
567 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
570 { /* Initialize us a little bit */
571 isabsolute
= strchr (text
, PATH_SEP
) != NULL
;
574 words
= bash_reserved
;
576 text_len
= strlen (text
);
580 path
= g_strdup (getenv ("PATH"));
584 path_end
= strchr (p
, '\0');
585 while ((p
= strchr (p
, PATH_ENV_SEP
)) != NULL
)
596 p
= filename_completion_function (text
, state
, flags
);
601 p
= strutils_shell_escape (p
);
612 case 0: /* Reserved words */
615 if (strncmp (*words
, text
, text_len
) == 0)
618 return g_strdup (*(words
++));
623 words
= bash_builtins
;
624 case 1: /* Builtin commands */
627 if (strncmp (*words
, text
, text_len
) == 0)
630 return g_strdup (*(words
++));
639 case 2: /* And looking through the $PATH */
646 if (cur_path
>= path_end
)
648 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
649 cur_word
= mc_build_filename (expanded
, text
, NULL
);
651 canonicalize_pathname (cur_word
);
652 cur_path
= strchr (cur_path
, 0) + 1;
655 found
= filename_completion_function (cur_word
, state
- init_state
, flags
);
671 p
= strrchr (found
, PATH_SEP
);
675 found
= strutils_shell_escape (p
+ 1);
684 /* --------------------------------------------------------------------------------------------- */
687 match_compare (const void *a
, const void *b
)
689 return strcmp (*(char **) a
, *(char **) b
);
692 /* --------------------------------------------------------------------------------------------- */
693 /** Returns an array of char * matches with the longest common denominator
694 in the 1st entry. Then a NULL terminated list of different possible
696 You have to supply your own CompletionFunction with the word you
697 want to complete as the first argument and an count of previous matches
699 In case no matches were found we return NULL. */
702 completion_matches (const char *text
, CompletionFunction entry_function
, input_complete_t flags
)
704 /* Number of slots in match_list. */
707 /* The list of matches. */
708 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
710 /* Number of matches actually found. */
713 /* Temporary string binder. */
716 match_list
[1] = NULL
;
718 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
)
720 if (matches
+ 1 == match_list_size
)
722 (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
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 int low
= 4096; /* Count of max-matched characters. */
734 /* If only one match, just use that. */
737 match_list
[0] = match_list
[1];
738 match_list
[1] = NULL
;
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
);
792 { /* There were no matches. */
799 /* --------------------------------------------------------------------------------------------- */
800 /** Check if directory completion is needed */
802 check_is_cd (const char *text
, int lc_start
, input_complete_t flags
)
806 SHOW_C_CTX ("check_is_cd");
808 if ((flags
& INPUT_COMPLETE_CD
) == 0)
811 /* Skip initial spaces */
813 q
= (char *) text
+ lc_start
;
814 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
817 /* Check if the command is "cd" and the cursor is after it */
818 return (p
[0] == 'c' && p
[1] == 'd' && str_isspace (p
+ 2) && p
+ 2 < q
);
821 /* --------------------------------------------------------------------------------------------- */
822 /** Returns an array of matches, or NULL if none. */
824 try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
)
826 size_t in_command_position
= 0;
828 char **matches
= NULL
;
829 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
832 SHOW_C_CTX ("try_complete");
833 word
= g_strndup (text
+ *lc_start
, *lc_end
- *lc_start
);
835 is_cd
= check_is_cd (text
, *lc_start
, flags
);
837 /* Determine if this could be a command word. It is if it appears at
838 the start of the line (ignoring preceding whitespace), or if it
839 appears after a character that separates commands. And we have to
840 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
841 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
))
843 const char *command_separator_chars
= ";|&{(`";
850 ti
= str_get_prev_char (&text
[*lc_start
]);
851 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
856 in_command_position
++;
857 else if (strchr (command_separator_chars
, ti
[0]) != NULL
)
859 int this_char
, prev_char
;
861 in_command_position
++;
865 /* Handle the two character tokens `>&', `<&', and `>|'.
866 We are not in a command position after one of these. */
868 prev_char
= str_get_prev_char (ti
)[0];
871 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>'))
872 || (this_char
== '|' && prev_char
== '>') || (ti
!= text
873 && str_get_prev_char (ti
)[0] ==
875 in_command_position
= 0;
880 if (flags
& INPUT_COMPLETE_COMMANDS
)
881 p
= strrchr (word
, '`');
882 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
884 q
= strrchr (word
, '$');
886 /* don't substitute variable in \$ case */
887 if (q
!= NULL
&& q
!= word
&& q
[-1] == '\\')
893 memmove (q
- 1, q
, qlen
+ 1);
895 flags
&= ~INPUT_COMPLETE_VARIABLES
;
899 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
900 r
= strrchr (word
, '@');
901 if (q
&& q
[1] == '(' && (flags
& INPUT_COMPLETE_COMMANDS
))
904 p
= str_get_next_char (q
);
908 /* Command substitution? */
911 SHOW_C_CTX ("try_complete:cmd_backq_subst");
912 matches
= completion_matches (str_cget_next_char (p
),
913 command_completion_function
,
914 flags
& (~INPUT_COMPLETE_FILENAMES
));
916 *lc_start
+= str_get_next_char (p
) - word
;
920 else if (q
> p
&& q
> r
)
922 SHOW_C_CTX ("try_complete:var_subst");
923 matches
= completion_matches (q
, variable_completion_function
, flags
);
925 *lc_start
+= q
- word
;
928 /* Starts with '@', then look through the known hostnames for
930 else if (r
> p
&& r
> q
)
932 SHOW_C_CTX ("try_complete:host_subst");
933 matches
= completion_matches (r
, hostname_completion_function
, flags
);
935 *lc_start
+= r
- word
;
938 /* Starts with `~' and there is no slash in the word, then
939 try completing this word as a username. */
940 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
942 SHOW_C_CTX ("try_complete:user_subst");
943 matches
= completion_matches (word
, username_completion_function
, flags
);
947 /* And finally if this word is in a command position, then
948 complete over possible command names, including aliases, functions,
949 and command names. */
950 if (!matches
&& in_command_position
!= 0)
952 SHOW_C_CTX ("try_complete:cmd_subst");
954 completion_matches (word
, command_completion_function
,
955 flags
& (~INPUT_COMPLETE_FILENAMES
));
958 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
))
961 flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
962 SHOW_C_CTX ("try_complete:filename_subst_1");
963 matches
= completion_matches (word
, filename_completion_function
, flags
);
964 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~')
966 q
= text
+ *lc_start
;
967 for (p
= text
; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
968 if (!strncmp (p
, "cd", 2))
969 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
972 char *const cdpath_ref
= g_strdup (getenv ("CDPATH"));
973 char *cdpath
= cdpath_ref
;
980 while (!matches
&& c
== ':')
982 s
= strchr (cdpath
, ':');
984 s
= strchr (cdpath
, 0);
989 r
= mc_build_filename (cdpath
, word
, NULL
);
990 SHOW_C_CTX ("try_complete:filename_subst_2");
991 matches
= completion_matches (r
, filename_completion_function
, flags
);
995 cdpath
= str_get_next_char (s
);
1007 /* --------------------------------------------------------------------------------------------- */
1010 insert_text (WInput
* in
, char *text
, ssize_t size
)
1012 int buff_len
= str_length (in
->buffer
);
1014 size
= min (size
, (ssize_t
) strlen (text
)) + start
- end
;
1015 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
)
1017 /* Expand the buffer */
1018 char *narea
= g_try_realloc (in
->buffer
, in
->current_max_size
+ size
+ in
->field_width
);
1022 in
->current_max_size
+= size
+ in
->field_width
;
1025 if (strlen (in
->buffer
) + 1 < (size_t) in
->current_max_size
)
1029 int i
= strlen (&in
->buffer
[end
]);
1031 in
->buffer
[end
+ size
+ i
] = in
->buffer
[end
+ i
];
1035 char *p
= in
->buffer
+ end
+ size
, *q
= in
->buffer
+ end
;
1040 memcpy (in
->buffer
+ start
, text
, size
- start
+ end
);
1041 in
->point
+= str_length (in
->buffer
) - buff_len
;
1042 input_update (in
, TRUE
);
1048 /* --------------------------------------------------------------------------------------------- */
1051 query_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
1053 static char buff
[MB_LEN_MAX
] = "";
1056 WDialog
*h
= DIALOG (w
);
1072 /* exit from completion list if input line is empty */
1078 /* Refill the list box and start again */
1079 else if (end
== min_end
)
1081 end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1082 input_handle_char (input
, parm
);
1083 h
->ret_value
= B_USER
;
1093 new_end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1095 for (i
= 0, e
= LISTBOX (h
->current
->data
)->list
;
1096 e
!= NULL
; i
++, e
= g_list_next (e
))
1098 WLEntry
*le
= LENTRY (e
->data
);
1100 if (strncmp (input
->buffer
+ start
, le
->text
, new_end
- start
) == 0)
1102 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1104 input_handle_char (input
, parm
);
1105 send_message (h
->current
->data
, NULL
, MSG_DRAW
, 0, NULL
);
1113 if (parm
< 32 || parm
> 255)
1116 if (input_key_is_in_map (input
, parm
) != 2)
1117 return MSG_NOT_HANDLED
;
1122 /* This means we want to refill the list box and start again */
1123 h
->ret_value
= B_USER
;
1131 int need_redraw
= 0;
1133 char *last_text
= NULL
;
1135 buff
[bl
++] = (char) parm
;
1137 switch (str_is_valid_char (buff
, bl
))
1146 for (i
= 0, e
= LISTBOX (h
->current
->data
)->list
;
1147 e
!= NULL
; i
++, e
= g_list_next (e
))
1149 WLEntry
*le
= LENTRY (e
->data
);
1151 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
) == 0
1152 && strncmp (&le
->text
[end
- start
], buff
, bl
) == 0)
1154 if (need_redraw
== 0)
1157 listbox_select_entry (LISTBOX (h
->current
->data
), i
);
1158 last_text
= le
->text
;
1166 /* count symbols between start and end */
1167 for (si
= le
->text
+ start
; si
< le
->text
+ end
;
1168 str_next_char (&si
), si_num
++)
1170 for (sl
= last_text
+ start
; sl
< last_text
+ end
;
1171 str_next_char (&sl
), sl_num
++)
1174 /* pointers to next symbols */
1175 si
= &le
->text
[str_offset_to_pos (le
->text
, ++si_num
)];
1176 sl
= &last_text
[str_offset_to_pos (last_text
, ++sl_num
)];
1178 while (si
[0] != '\0' && sl
[0] != '\0')
1180 char *nexti
, *nextl
;
1182 nexti
= str_get_next_char (si
);
1183 nextl
= str_get_next_char (sl
);
1185 if (nexti
- si
!= nextl
- sl
|| strncmp (si
, sl
, nexti
- si
) != 0)
1194 last_text
= le
->text
;
1196 si
= &last_text
[str_offset_to_pos (last_text
, si_num
)];
1197 if (low
> si
- last_text
)
1198 low
= si
- last_text
;
1205 if (need_redraw
== 2)
1207 insert_text (input
, last_text
, low
);
1208 send_message (h
->current
->data
, NULL
, MSG_DRAW
, 0, NULL
);
1210 else if (need_redraw
== 1)
1212 h
->ret_value
= B_ENTER
;
1222 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
1226 /* --------------------------------------------------------------------------------------------- */
1227 /** Returns 1 if the user would like to see us again */
1230 complete_engine (WInput
* in
, int what_to_do
)
1232 if (in
->completions
!= NULL
&& str_offset_to_pos (in
->buffer
, in
->point
) != end
)
1233 input_free_completions (in
);
1234 if (in
->completions
== NULL
)
1238 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1243 /* get symbol before in->point */
1245 for (i
= in
->point
- 1; i
> 0; i
--)
1249 for (; s
>= in
->buffer
; str_prev_char (&s
))
1251 start
= s
- in
->buffer
;
1252 if (strchr (" \t;|<>", *s
) != NULL
)
1259 start
= s
- in
->buffer
;
1262 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1265 if (in
->completions
!= NULL
)
1267 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1]))
1269 char *lc_complete
= in
->completions
[0];
1270 if (insert_text (in
, lc_complete
, strlen (lc_complete
)))
1272 if (in
->completions
[1])
1275 input_free_completions (in
);
1280 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1])
1282 int maxlen
= 0, i
, count
= 0;
1284 int start_x
, start_y
;
1287 WListbox
*query_list
;
1289 for (p
= in
->completions
+ 1; *p
!= NULL
; count
++, p
++)
1291 i
= str_term_width1 (*p
);
1295 start_x
= WIDGET (in
)->x
;
1296 start_y
= WIDGET (in
)->y
;
1297 if (start_y
- 2 >= count
)
1299 y
= start_y
- 2 - count
;
1304 if (start_y
>= LINES
- start_y
- 1)
1312 h
= LINES
- start_y
- 1;
1315 x
= start
- in
->term_first_shown
- 2 + start_x
;
1327 query_dlg
= create_dlg (TRUE
, y
, x
, query_height
, query_width
,
1328 dialog_colors
, query_callback
, NULL
,
1329 "[Completion]", NULL
, DLG_COMPACT
);
1330 query_list
= listbox_new (1, 1, h
- 2, w
- 2, FALSE
, NULL
);
1331 add_widget (query_dlg
, query_list
);
1332 for (p
= in
->completions
+ 1; *p
; p
++)
1333 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
, 0, *p
, NULL
);
1334 run_dlg (query_dlg
);
1336 if (query_dlg
->ret_value
== B_ENTER
)
1338 listbox_get_current (query_list
, &q
, NULL
);
1340 insert_text (in
, q
, strlen (q
));
1342 if (q
|| end
!= min_end
)
1343 input_free_completions (in
);
1344 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1345 destroy_dlg (query_dlg
);
1355 /* --------------------------------------------------------------------------------------------- */
1356 /*** public functions ****************************************************************************/
1357 /* --------------------------------------------------------------------------------------------- */
1359 /* declared in lib/widget/input.h */
1361 complete (WInput
* in
)
1365 if (!str_is_valid_string (in
->buffer
))
1368 if (in
->completions
!= NULL
)
1369 engine_flags
= DO_QUERY
;
1372 engine_flags
= DO_INSERTION
;
1374 if (mc_global
.widget
.show_all_if_ambiguous
)
1375 engine_flags
|= DO_QUERY
;
1378 while (complete_engine (in
, engine_flags
))
1382 /* --------------------------------------------------------------------------------------------- */