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/>.
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
;
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;
130 struct dirent
*entry
= NULL
;
132 SHOW_C_CTX ("filename_completion_function");
134 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
140 u_text
= strutils_shell_unescape (text
);
142 result
= filename_completion_function (u_text
, state
, flags
& (~INPUT_COMPLETE_SHELL_ESC
));
145 e_result
= strutils_shell_escape (result
);
151 /* If we're starting the match process, initialize us a bit. */
158 g_free (users_dirname
);
160 if ((*text
!= '\0') && (temp
= strrchr (text
, PATH_SEP
)) != NULL
)
162 filename
= g_strdup (++temp
);
163 dirname
= g_strndup (text
, temp
- text
);
167 dirname
= g_strdup (".");
168 filename
= g_strdup (text
);
171 /* We aren't done yet. We also support the "~user" syntax. */
173 /* Save the version of the directory that the user typed. */
174 users_dirname
= dirname
;
175 dirname
= tilde_expand (dirname
);
176 canonicalize_pathname (dirname
);
178 /* Here we should do something with variable expansion
180 Maybe a dream - UNIMPLEMENTED yet. */
182 directory
= mc_opendir (dirname
);
183 filename_len
= strlen (filename
);
186 /* Now that we have some state, we can read the directory. */
188 while (directory
&& (entry
= mc_readdir (directory
)))
190 if (!str_is_valid_string (entry
->d_name
))
193 /* Special case for no filename.
194 All entries except "." and ".." match. */
195 if (filename_len
== 0)
197 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
202 /* Otherwise, if these match up to the length of filename, then
203 it may be a match. */
204 if ((entry
->d_name
[0] != filename
[0]) ||
205 ((NLENGTH (entry
)) < filename_len
) ||
206 strncmp (filename
, entry
->d_name
, filename_len
))
213 struct stat tempstat
;
215 vpath
= vfs_path_build_filename (dirname
, entry
->d_name
, (char *) NULL
);
218 if (mc_stat (vpath
, &tempstat
) == 0)
220 uid_t my_uid
= getuid ();
221 gid_t my_gid
= getgid ();
223 if (!S_ISDIR (tempstat
.st_mode
))
226 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
227 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
228 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
229 (tempstat
.st_mode
& 0001))
235 /* stat failed, strange. not a dir in any case */
238 vfs_path_free (vpath
);
240 if ((flags
& INPUT_COMPLETE_COMMANDS
) && (isexec
|| isdir
))
242 if ((flags
& INPUT_COMPLETE_CD
) && isdir
)
244 if (flags
& (INPUT_COMPLETE_FILENAMES
))
252 mc_closedir (directory
);
259 g_free (users_dirname
);
260 users_dirname
= NULL
;
267 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1]))
269 size_t dirlen
= strlen (users_dirname
);
270 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
271 strcpy (temp
, users_dirname
);
272 /* We need a `/' at the end. */
273 if (users_dirname
[dirlen
- 1] != PATH_SEP
)
275 temp
[dirlen
] = PATH_SEP
;
276 temp
[dirlen
+ 1] = '\0';
278 strcat (temp
, entry
->d_name
);
282 temp
= g_malloc (2 + NLENGTH (entry
));
283 strcpy (temp
, entry
->d_name
);
286 strcat (temp
, PATH_SEP_STR
);
292 /* --------------------------------------------------------------------------------------------- */
293 /** We assume here that text[0] == '~' , if you want to call it in another way,
294 you have to change the code */
297 username_completion_function (const char *text
, int state
, input_complete_t flags
)
299 static struct passwd
*entry
;
300 static size_t userlen
;
303 SHOW_C_CTX ("username_completion_function");
305 if (text
[0] == '\\' && text
[1] == '~')
308 { /* Initialization stuff */
310 userlen
= strlen (text
+ 1);
312 while ((entry
= getpwent ()) != NULL
)
314 /* Null usernames should result in all users as possible completions. */
317 if (text
[1] == entry
->pw_name
[0] && !strncmp (text
+ 1, entry
->pw_name
, userlen
))
322 return g_strconcat ("~", entry
->pw_name
, PATH_SEP_STR
, (char *) NULL
);
328 /* --------------------------------------------------------------------------------------------- */
329 /** We assume text [0] == '$' and want to have a look at text [1], if it is
330 equal to '{', so that we should append '}' at the end */
333 variable_completion_function (const char *text
, int state
, input_complete_t flags
)
336 static int varlen
, isbrace
;
337 const char *p
= NULL
;
340 SHOW_C_CTX ("variable_completion_function");
343 { /* Initialization stuff */
344 isbrace
= (text
[1] == '{');
345 varlen
= strlen (text
+ 1 + isbrace
);
351 p
= strchr (*env_p
, '=');
352 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
361 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
366 memcpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
368 strcpy (temp
+ 2 + (p
- *env_p
), "}");
370 temp
[1 + p
- *env_p
] = 0;
376 /* --------------------------------------------------------------------------------------------- */
379 fetch_hosts (const char *filename
)
381 FILE *file
= fopen (filename
, "r");
382 char buffer
[256], *name
;
389 while (fgets (buffer
, 255, file
) != NULL
)
391 /* Skip to first character. */
392 for (bi
= buffer
; bi
[0] != '\0' && str_isspace (bi
); str_next_char (&bi
));
394 /* Ignore comments... */
397 /* Handle $include. */
398 if (!strncmp (bi
, "$include ", 9))
400 char *includefile
= bi
+ 9;
403 /* Find start of filename. */
404 while (*includefile
&& whitespace (*includefile
))
408 /* Find end of filename. */
409 while (t
[0] != '\0' && !str_isspace (t
))
413 fetch_hosts (includefile
);
418 while (bi
[0] != '\0' && !str_isspace (bi
))
421 /* Get the host names separated by white space. */
422 while (bi
[0] != '\0' && bi
[0] != '#')
424 while (bi
[0] != '\0' && str_isspace (bi
))
428 for (lc_start
= bi
; bi
[0] != '\0' && !str_isspace (bi
); str_next_char (&bi
));
430 if (bi
- lc_start
== 0)
433 name
= g_strndup (lc_start
, bi
- lc_start
);
437 if (hosts_p
- hosts
>= hosts_alloclen
)
439 int j
= hosts_p
- hosts
;
442 g_realloc ((void *) hosts
, ((hosts_alloclen
+= 30) + 1) * sizeof (char *));
445 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
446 if (!strcmp (name
, *host_p
))
447 break; /* We do not want any duplicates */
448 if (host_p
== hosts_p
)
461 /* --------------------------------------------------------------------------------------------- */
464 hostname_completion_function (const char *text
, int state
, input_complete_t flags
)
466 static char **host_p
;
467 static int textstart
, textlen
;
470 SHOW_C_CTX ("hostname_completion_function");
473 { /* Initialization stuff */
478 for (host_p
= hosts
; *host_p
; host_p
++)
482 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
485 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
487 textstart
= (*text
== '@') ? 1 : 0;
488 textlen
= strlen (text
+ textstart
);
494 break; /* Match all of them */
495 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
502 for (host_p
= hosts
; *host_p
; host_p
++)
510 char *temp
= g_malloc (2 + strlen (*host_p
));
514 strcpy (temp
+ textstart
, *host_p
);
520 /* --------------------------------------------------------------------------------------------- */
522 * This is the function to call when the word to complete is in a position
523 * where a command word can be found. It looks around $PATH, looking for
524 * commands that match. It also scans aliases, function names, and the
525 * table of shell built-ins.
529 command_completion_function (const char *_text
, int state
, input_complete_t flags
)
532 static const char *path_end
;
533 static gboolean isabsolute
;
535 static size_t text_len
;
536 static const char *const *words
;
538 static char *cur_path
;
539 static char *cur_word
;
540 static int init_state
;
541 static const char *const bash_reserved
[] = {
542 "if", "then", "else", "elif", "fi", "case", "esac", "for",
543 "select", "while", "until", "do", "done", "in", "function", 0
545 static const char *const bash_builtins
[] = {
546 "alias", "bg", "bind", "break", "builtin", "cd", "command",
547 "continue", "declare", "dirs", "echo", "enable", "eval",
548 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
549 "help", "history", "jobs", "kill", "let", "local", "logout",
550 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
551 "shift", "source", "suspend", "test", "times", "trap", "type",
552 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
556 SHOW_C_CTX ("command_completion_function");
558 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
561 text
= strutils_shell_unescape (_text
);
562 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
565 { /* Initialize us a little bit */
566 isabsolute
= strchr (text
, PATH_SEP
) != NULL
;
569 words
= bash_reserved
;
571 text_len
= strlen (text
);
575 path
= g_strdup (getenv ("PATH"));
579 path_end
= strchr (p
, '\0');
580 while ((p
= strchr (p
, PATH_ENV_SEP
)) != NULL
)
591 p
= filename_completion_function (text
, state
, flags
);
596 p
= strutils_shell_escape (p
);
607 case 0: /* Reserved words */
610 if (strncmp (*words
, text
, text_len
) == 0)
613 return g_strdup (*(words
++));
618 words
= bash_builtins
;
619 case 1: /* Builtin commands */
622 if (strncmp (*words
, text
, text_len
) == 0)
625 return g_strdup (*(words
++));
634 case 2: /* And looking through the $PATH */
641 if (cur_path
>= path_end
)
643 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
644 cur_word
= concat_dir_and_file (expanded
, text
);
646 canonicalize_pathname (cur_word
);
647 cur_path
= strchr (cur_path
, 0) + 1;
650 found
= filename_completion_function (cur_word
, state
- init_state
, flags
);
666 p
= strrchr (found
, PATH_SEP
);
670 found
= strutils_shell_escape (p
+ 1);
679 /* --------------------------------------------------------------------------------------------- */
682 match_compare (const void *a
, const void *b
)
684 return strcmp (*(char **) a
, *(char **) b
);
687 /* --------------------------------------------------------------------------------------------- */
688 /** Returns an array of char * matches with the longest common denominator
689 in the 1st entry. Then a NULL terminated list of different possible
691 You have to supply your own CompletionFunction with the word you
692 want to complete as the first argument and an count of previous matches
694 In case no matches were found we return NULL. */
697 completion_matches (const char *text
, CompletionFunction entry_function
, input_complete_t flags
)
699 /* Number of slots in match_list. */
702 /* The list of matches. */
703 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
705 /* Number of matches actually found. */
708 /* Temporary string binder. */
711 match_list
[1] = NULL
;
713 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
)
715 if (matches
+ 1 == match_list_size
)
717 (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
718 match_list
[++matches
] = string
;
719 match_list
[matches
+ 1] = NULL
;
722 /* If there were any matches, then look through them finding out the
723 lowest common denominator. That then becomes match_list[0]. */
727 int low
= 4096; /* Count of max-matched characters. */
729 /* If only one match, just use that. */
732 match_list
[0] = match_list
[1];
733 match_list
[1] = NULL
;
739 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
741 /* And compare each member of the list with
742 the next, finding out where they stop matching.
743 If we find two equal strings, we have to put one away... */
746 while (j
< matches
+ 1)
751 for (si
= match_list
[i
], sj
= match_list
[j
]; si
[0] && sj
[0];)
754 ni
= str_get_next_char (si
);
755 nj
= str_get_next_char (sj
);
757 if (ni
- si
!= nj
- sj
)
759 if (strncmp (si
, sj
, ni
- si
) != 0)
766 if (si
[0] == '\0' && sj
[0] == '\0')
767 { /* Two equal strings */
768 g_free (match_list
[j
]);
772 continue; /* Look for a run of equal strings */
774 else if (low
> si
- match_list
[i
])
775 low
= si
- match_list
[i
];
776 if (i
+ 1 != j
) /* So there's some gap */
777 match_list
[i
+ 1] = match_list
[j
];
782 match_list
[matches
+ 1] = NULL
;
783 match_list
[0] = g_strndup (match_list
[1], low
);
787 { /* There were no matches. */
794 /* --------------------------------------------------------------------------------------------- */
795 /** Check if directory completion is needed */
797 check_is_cd (const char *text
, int lc_start
, input_complete_t flags
)
801 SHOW_C_CTX ("check_is_cd");
803 if ((flags
& INPUT_COMPLETE_CD
) == 0)
806 /* Skip initial spaces */
808 q
= (char *) text
+ lc_start
;
809 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
812 /* Check if the command is "cd" and the cursor is after it */
813 return (p
[0] == 'c' && p
[1] == 'd' && str_isspace (p
+ 2) && p
+ 2 < q
);
816 /* --------------------------------------------------------------------------------------------- */
817 /** Returns an array of matches, or NULL if none. */
819 try_complete (char *text
, int *lc_start
, int *lc_end
, input_complete_t flags
)
821 size_t in_command_position
= 0;
823 char **matches
= NULL
;
824 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
827 SHOW_C_CTX ("try_complete");
828 word
= g_strndup (text
+ *lc_start
, *lc_end
- *lc_start
);
830 is_cd
= check_is_cd (text
, *lc_start
, flags
);
832 /* Determine if this could be a command word. It is if it appears at
833 the start of the line (ignoring preceding whitespace), or if it
834 appears after a character that separates commands. And we have to
835 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
836 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
))
838 const char *command_separator_chars
= ";|&{(`";
845 ti
= str_get_prev_char (&text
[*lc_start
]);
846 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
851 in_command_position
++;
852 else if (strchr (command_separator_chars
, ti
[0]) != NULL
)
854 int this_char
, prev_char
;
856 in_command_position
++;
860 /* Handle the two character tokens `>&', `<&', and `>|'.
861 We are not in a command position after one of these. */
863 prev_char
= str_get_prev_char (ti
)[0];
866 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>'))
867 || (this_char
== '|' && prev_char
== '>') || (ti
!= text
868 && str_get_prev_char (ti
)[0] ==
870 in_command_position
= 0;
875 if (flags
& INPUT_COMPLETE_COMMANDS
)
876 p
= strrchr (word
, '`');
877 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
879 q
= strrchr (word
, '$');
881 /* don't substitute variable in \$ case */
882 if (q
!= NULL
&& q
!= word
&& q
[-1] == '\\')
888 memmove (q
- 1, q
, qlen
+ 1);
890 flags
&= ~INPUT_COMPLETE_VARIABLES
;
894 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
895 r
= strrchr (word
, '@');
896 if (q
&& q
[1] == '(' && (flags
& INPUT_COMPLETE_COMMANDS
))
899 p
= str_get_next_char (q
);
903 /* Command substitution? */
906 SHOW_C_CTX ("try_complete:cmd_backq_subst");
907 matches
= completion_matches (str_cget_next_char (p
),
908 command_completion_function
,
909 flags
& (~INPUT_COMPLETE_FILENAMES
));
911 *lc_start
+= str_get_next_char (p
) - word
;
915 else if (q
> p
&& q
> r
)
917 SHOW_C_CTX ("try_complete:var_subst");
918 matches
= completion_matches (q
, variable_completion_function
, flags
);
920 *lc_start
+= q
- word
;
923 /* Starts with '@', then look through the known hostnames for
925 else if (r
> p
&& r
> q
)
927 SHOW_C_CTX ("try_complete:host_subst");
928 matches
= completion_matches (r
, hostname_completion_function
, flags
);
930 *lc_start
+= r
- word
;
933 /* Starts with `~' and there is no slash in the word, then
934 try completing this word as a username. */
935 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
937 SHOW_C_CTX ("try_complete:user_subst");
938 matches
= completion_matches (word
, username_completion_function
, flags
);
942 /* And finally if this word is in a command position, then
943 complete over possible command names, including aliases, functions,
944 and command names. */
945 if (!matches
&& in_command_position
!= 0)
947 SHOW_C_CTX ("try_complete:cmd_subst");
949 completion_matches (word
, command_completion_function
,
950 flags
& (~INPUT_COMPLETE_FILENAMES
));
953 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
))
956 flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
957 SHOW_C_CTX ("try_complete:filename_subst_1");
958 matches
= completion_matches (word
, filename_completion_function
, flags
);
959 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~')
961 q
= text
+ *lc_start
;
962 for (p
= text
; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
963 if (!strncmp (p
, "cd", 2))
964 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
967 char *const cdpath_ref
= g_strdup (getenv ("CDPATH"));
968 char *cdpath
= cdpath_ref
;
975 while (!matches
&& c
== ':')
977 s
= strchr (cdpath
, ':');
979 s
= strchr (cdpath
, 0);
984 r
= concat_dir_and_file (cdpath
, word
);
985 SHOW_C_CTX ("try_complete:filename_subst_2");
986 matches
= completion_matches (r
, filename_completion_function
, flags
);
990 cdpath
= str_get_next_char (s
);
1002 /* --------------------------------------------------------------------------------------------- */
1005 insert_text (WInput
* in
, char *text
, ssize_t size
)
1007 int buff_len
= str_length (in
->buffer
);
1009 size
= min (size
, (ssize_t
) strlen (text
)) + start
- end
;
1010 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
)
1012 /* Expand the buffer */
1013 char *narea
= g_try_realloc (in
->buffer
, in
->current_max_size
+ size
+ in
->field_width
);
1017 in
->current_max_size
+= size
+ in
->field_width
;
1020 if (strlen (in
->buffer
) + 1 < (size_t) in
->current_max_size
)
1024 int i
= strlen (&in
->buffer
[end
]);
1026 in
->buffer
[end
+ size
+ i
] = in
->buffer
[end
+ i
];
1030 char *p
= in
->buffer
+ end
+ size
, *q
= in
->buffer
+ end
;
1035 memcpy (in
->buffer
+ start
, text
, size
- start
+ end
);
1036 in
->point
+= str_length (in
->buffer
) - buff_len
;
1037 input_update (in
, TRUE
);
1043 /* --------------------------------------------------------------------------------------------- */
1046 query_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1048 static char buff
[MB_LEN_MAX
] = "";
1065 /* exit from completion list if input line is empty */
1071 /* Refill the list box and start again */
1072 else if (end
== min_end
)
1074 end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1075 input_handle_char (input
, parm
);
1076 h
->ret_value
= B_USER
;
1086 new_end
= str_get_prev_char (&input
->buffer
[end
]) - input
->buffer
;
1088 for (i
= 0, e
= ((WListbox
*) h
->current
->data
)->list
;
1089 e
!= NULL
; i
++, e
= g_list_next (e
))
1091 WLEntry
*le
= (WLEntry
*) e
->data
;
1093 if (strncmp (input
->buffer
+ start
, le
->text
, new_end
- start
) == 0)
1095 listbox_select_entry ((WListbox
*) h
->current
->data
, i
);
1097 input_handle_char (input
, parm
);
1098 send_message ((Widget
*) h
->current
->data
, WIDGET_DRAW
, 0);
1106 if (parm
< 32 || parm
> 255)
1109 if (input_key_is_in_map (input
, parm
) != 2)
1110 return MSG_NOT_HANDLED
;
1115 /* This means we want to refill the list box and start again */
1116 h
->ret_value
= B_USER
;
1124 int need_redraw
= 0;
1126 char *last_text
= NULL
;
1128 buff
[bl
++] = (char) parm
;
1130 switch (str_is_valid_char (buff
, bl
))
1139 for (i
= 0, e
= ((WListbox
*) h
->current
->data
)->list
;
1140 e
!= NULL
; i
++, e
= g_list_next (e
))
1142 WLEntry
*le
= (WLEntry
*) e
->data
;
1144 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
) == 0
1145 && strncmp (&le
->text
[end
- start
], buff
, bl
) == 0)
1147 if (need_redraw
== 0)
1150 listbox_select_entry ((WListbox
*) h
->current
->data
, i
);
1151 last_text
= le
->text
;
1159 /* count symbols between start and end */
1160 for (si
= le
->text
+ start
; si
< le
->text
+ end
;
1161 str_next_char (&si
), si_num
++)
1163 for (sl
= last_text
+ start
; sl
< last_text
+ end
;
1164 str_next_char (&sl
), sl_num
++)
1167 /* pointers to next symbols */
1168 si
= &le
->text
[str_offset_to_pos (le
->text
, ++si_num
)];
1169 sl
= &last_text
[str_offset_to_pos (last_text
, ++sl_num
)];
1171 while (si
[0] != '\0' && sl
[0] != '\0')
1173 char *nexti
, *nextl
;
1175 nexti
= str_get_next_char (si
);
1176 nextl
= str_get_next_char (sl
);
1178 if (nexti
- si
!= nextl
- sl
|| strncmp (si
, sl
, nexti
- si
) != 0)
1187 last_text
= le
->text
;
1189 si
= &last_text
[str_offset_to_pos (last_text
, si_num
)];
1190 if (low
> si
- last_text
)
1191 low
= si
- last_text
;
1198 if (need_redraw
== 2)
1200 insert_text (input
, last_text
, low
);
1201 send_message ((Widget
*) h
->current
->data
, WIDGET_DRAW
, 0);
1203 else if (need_redraw
== 1)
1205 h
->ret_value
= B_ENTER
;
1215 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1219 /* --------------------------------------------------------------------------------------------- */
1220 /** Returns 1 if the user would like to see us again */
1223 complete_engine (WInput
* in
, int what_to_do
)
1225 if (in
->completions
!= NULL
&& str_offset_to_pos (in
->buffer
, in
->point
) != end
)
1226 input_free_completions (in
);
1227 if (in
->completions
== NULL
)
1231 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1236 /* get symbol before in->point */
1238 for (i
= in
->point
- 1; i
> 0; i
--)
1242 for (; s
>= in
->buffer
; str_prev_char (&s
))
1244 start
= s
- in
->buffer
;
1245 if (strchr (" \t;|<>", *s
) != NULL
)
1252 start
= s
- in
->buffer
;
1255 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1258 if (in
->completions
!= NULL
)
1260 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1]))
1262 char *lc_complete
= in
->completions
[0];
1263 if (insert_text (in
, lc_complete
, strlen (lc_complete
)))
1265 if (in
->completions
[1])
1268 input_free_completions (in
);
1273 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1])
1275 int maxlen
= 0, i
, count
= 0;
1277 int start_x
, start_y
;
1279 Dlg_head
*query_dlg
;
1280 WListbox
*query_list
;
1282 for (p
= in
->completions
+ 1; *p
!= NULL
; count
++, p
++)
1284 i
= str_term_width1 (*p
);
1288 start_x
= in
->widget
.x
;
1289 start_y
= in
->widget
.y
;
1290 if (start_y
- 2 >= count
)
1292 y
= start_y
- 2 - count
;
1297 if (start_y
>= LINES
- start_y
- 1)
1305 h
= LINES
- start_y
- 1;
1308 x
= start
- in
->term_first_shown
- 2 + start_x
;
1320 query_dlg
= create_dlg (TRUE
, y
, x
, query_height
, query_width
,
1321 dialog_colors
, query_callback
,
1322 "[Completion]", NULL
, DLG_COMPACT
);
1323 query_list
= listbox_new (1, 1, h
- 2, w
- 2, FALSE
, NULL
);
1324 add_widget (query_dlg
, query_list
);
1325 for (p
= in
->completions
+ 1; *p
; p
++)
1326 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
, 0, *p
, NULL
);
1327 run_dlg (query_dlg
);
1329 if (query_dlg
->ret_value
== B_ENTER
)
1331 listbox_get_current (query_list
, &q
, NULL
);
1333 insert_text (in
, q
, strlen (q
));
1335 if (q
|| end
!= min_end
)
1336 input_free_completions (in
);
1337 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1338 destroy_dlg (query_dlg
);
1348 /* --------------------------------------------------------------------------------------------- */
1349 /*** public functions ****************************************************************************/
1350 /* --------------------------------------------------------------------------------------------- */
1352 /* declared in lib/widget/input.h */
1354 complete (WInput
* in
)
1358 if (!str_is_valid_string (in
->buffer
))
1361 if (in
->completions
!= NULL
)
1362 engine_flags
= DO_QUERY
;
1365 engine_flags
= DO_INSERTION
;
1367 if (mc_global
.widget
.show_all_if_ambiguous
)
1368 engine_flags
|= DO_QUERY
;
1371 while (complete_engine (in
, engine_flags
))
1375 /* --------------------------------------------------------------------------------------------- */