1 /* Input line filename/username/hostname/variable/command completion.
2 (Let mc type for you...)
4 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2007 Free Software Foundation, Inc.
7 Written by: 1995 Jakub Jelinek
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
24 * \brief Source: Input line filename/username/hostname/variable/command completion
34 #include <sys/types.h>
39 #include "lib/global.h"
41 #include "lib/tty/tty.h"
42 #include "lib/tty/key.h" /* XCTRL and ALT macros */
43 #include "lib/vfs/mc-vfs/vfs.h"
44 #include "lib/strescape.h"
45 #include "lib/strutil.h"
50 #include "main.h" /* show_all_if_ambiguous */
52 typedef char *CompletionFunction (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
);
54 /* #define DO_COMPLETION_DEBUG */
55 #ifdef DO_COMPLETION_DEBUG
57 * Useful to print/debug completion flags
60 show_c_flags (INPUT_COMPLETE_FLAGS flags
)
62 static char s_cf
[] = "FHCVUDS";
64 s_cf
[0] = (flags
& INPUT_COMPLETE_FILENAMES
) ? 'F' : ' ';
65 s_cf
[1] = (flags
& INPUT_COMPLETE_HOSTNAMES
) ? 'H' : ' ';
66 s_cf
[2] = (flags
& INPUT_COMPLETE_COMMANDS
) ? 'C' : ' ';
67 s_cf
[3] = (flags
& INPUT_COMPLETE_VARIABLES
) ? 'V' : ' ';
68 s_cf
[4] = (flags
& INPUT_COMPLETE_USERNAMES
) ? 'U' : ' ';
69 s_cf
[5] = (flags
& INPUT_COMPLETE_CD
) ? 'D' : ' ';
70 s_cf
[6] = (flags
& INPUT_COMPLETE_SHELL_ESC
) ? 'S' : ' ';
75 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
77 #define SHOW_C_CTX(func)
78 #endif /* DO_CMPLETION_DEBUG */
81 filename_completion_function (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
83 static DIR *directory
;
84 static char *filename
= NULL
;
85 static char *dirname
= NULL
;
86 static char *users_dirname
= NULL
;
87 static size_t filename_len
;
88 int isdir
= 1, isexec
= 0;
90 struct dirent
*entry
= NULL
;
92 SHOW_C_CTX ("filename_completion_function");
94 if (text
&& (flags
& INPUT_COMPLETE_SHELL_ESC
))
100 u_text
= strutils_shell_unescape (text
);
102 result
= filename_completion_function (u_text
, state
, flags
& (~INPUT_COMPLETE_SHELL_ESC
));
105 e_result
= strutils_shell_escape (result
);
111 /* If we're starting the match process, initialize us a bit. */
118 g_free (users_dirname
);
120 if ((*text
!= '\0') && (temp
= strrchr (text
, PATH_SEP
)) != NULL
)
122 filename
= g_strdup (++temp
);
123 dirname
= g_strndup (text
, temp
- text
);
127 dirname
= g_strdup (".");
128 filename
= g_strdup (text
);
131 /* We aren't done yet. We also support the "~user" syntax. */
133 /* Save the version of the directory that the user typed. */
134 users_dirname
= dirname
;
135 dirname
= tilde_expand (dirname
);
136 canonicalize_pathname (dirname
);
138 /* Here we should do something with variable expansion
140 Maybe a dream - UNIMPLEMENTED yet. */
142 directory
= mc_opendir (dirname
);
143 filename_len
= strlen (filename
);
146 /* Now that we have some state, we can read the directory. */
148 while (directory
&& (entry
= mc_readdir (directory
)))
150 if (!str_is_valid_string (entry
->d_name
))
153 /* Special case for no filename.
154 All entries except "." and ".." match. */
155 if (filename_len
== 0)
157 if (!strcmp (entry
->d_name
, ".") || !strcmp (entry
->d_name
, ".."))
162 /* Otherwise, if these match up to the length of filename, then
163 it may be a match. */
164 if ((entry
->d_name
[0] != filename
[0]) ||
165 ((NLENGTH (entry
)) < filename_len
) ||
166 strncmp (filename
, entry
->d_name
, filename_len
))
173 struct stat tempstat
;
175 tmp
= g_strconcat (dirname
, PATH_SEP_STR
, entry
->d_name
, (char *) NULL
);
176 canonicalize_pathname (tmp
);
178 if (!mc_stat (tmp
, &tempstat
))
180 uid_t my_uid
= getuid ();
181 gid_t my_gid
= getgid ();
183 if (!S_ISDIR (tempstat
.st_mode
))
186 if ((!my_uid
&& (tempstat
.st_mode
& 0111)) ||
187 (my_uid
== tempstat
.st_uid
&& (tempstat
.st_mode
& 0100)) ||
188 (my_gid
== tempstat
.st_gid
&& (tempstat
.st_mode
& 0010)) ||
189 (tempstat
.st_mode
& 0001))
195 /* stat failed, strange. not a dir in any case */
200 if ((flags
& INPUT_COMPLETE_COMMANDS
) && (isexec
|| isdir
))
202 if ((flags
& INPUT_COMPLETE_CD
) && isdir
)
204 if (flags
& (INPUT_COMPLETE_FILENAMES
))
212 mc_closedir (directory
);
219 g_free (users_dirname
);
220 users_dirname
= NULL
;
227 if (users_dirname
&& (users_dirname
[0] != '.' || users_dirname
[1]))
229 size_t dirlen
= strlen (users_dirname
);
230 temp
= g_malloc (3 + dirlen
+ NLENGTH (entry
));
231 strcpy (temp
, users_dirname
);
232 /* We need a `/' at the end. */
233 if (users_dirname
[dirlen
- 1] != PATH_SEP
)
235 temp
[dirlen
] = PATH_SEP
;
236 temp
[dirlen
+ 1] = '\0';
238 strcat (temp
, entry
->d_name
);
242 temp
= g_malloc (2 + NLENGTH (entry
));
243 strcpy (temp
, entry
->d_name
);
246 strcat (temp
, PATH_SEP_STR
);
252 /* We assume here that text[0] == '~' , if you want to call it in another way,
253 you have to change the code */
255 username_completion_function (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
257 static struct passwd
*entry
;
258 static size_t userlen
;
261 SHOW_C_CTX ("username_completion_function");
263 if (text
[0] == '\\' && text
[1] == '~')
266 { /* Initialization stuff */
268 userlen
= strlen (text
+ 1);
270 while ((entry
= getpwent ()) != NULL
)
272 /* Null usernames should result in all users as possible completions. */
275 if (text
[1] == entry
->pw_name
[0] && !strncmp (text
+ 1, entry
->pw_name
, userlen
))
280 return g_strconcat ("~", entry
->pw_name
, PATH_SEP_STR
, (char *) NULL
);
286 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
287 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
288 extern char **environ
;
291 /* We assume text [0] == '$' and want to have a look at text [1], if it is
292 equal to '{', so that we should append '}' at the end */
294 variable_completion_function (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
297 static int varlen
, isbrace
;
298 const char *p
= NULL
;
301 SHOW_C_CTX ("variable_completion_function");
304 { /* Initialization stuff */
305 isbrace
= (text
[1] == '{');
306 varlen
= strlen (text
+ 1 + isbrace
);
312 p
= strchr (*env_p
, '=');
313 if (p
&& p
- *env_p
>= varlen
&& !strncmp (text
+ 1 + isbrace
, *env_p
, varlen
))
322 char *temp
= g_malloc (2 + 2 * isbrace
+ p
- *env_p
);
327 memcpy (temp
+ 1 + isbrace
, *env_p
, p
- *env_p
);
329 strcpy (temp
+ 2 + (p
- *env_p
), "}");
331 temp
[1 + p
- *env_p
] = 0;
337 #define whitespace(c) ((c) == ' ' || (c) == '\t')
338 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
340 static char **hosts
= NULL
;
341 static char **hosts_p
= NULL
;
342 static int hosts_alloclen
= 0;
344 fetch_hosts (const char *filename
)
346 FILE *file
= fopen (filename
, "r");
347 char buffer
[256], *name
;
354 while (fgets (buffer
, 255, file
) != NULL
)
356 /* Skip to first character. */
357 for (bi
= buffer
; bi
[0] != '\0' && str_isspace (bi
); str_next_char (&bi
));
359 /* Ignore comments... */
362 /* Handle $include. */
363 if (!strncmp (bi
, "$include ", 9))
365 char *includefile
= bi
+ 9;
368 /* Find start of filename. */
369 while (*includefile
&& whitespace (*includefile
))
373 /* Find end of filename. */
374 while (t
[0] != '\0' && !str_isspace (t
))
378 fetch_hosts (includefile
);
383 while (bi
[0] != '\0' && !str_isspace (bi
))
386 /* Get the host names separated by white space. */
387 while (bi
[0] != '\0' && bi
[0] != '#')
389 while (bi
[0] != '\0' && str_isspace (bi
))
393 for (start
= bi
; bi
[0] != '\0' && !str_isspace (bi
); str_next_char (&bi
));
398 name
= g_strndup (start
, bi
- start
);
402 if (hosts_p
- hosts
>= hosts_alloclen
)
404 int j
= hosts_p
- hosts
;
407 g_realloc ((void *) hosts
, ((hosts_alloclen
+= 30) + 1) * sizeof (char *));
410 for (host_p
= hosts
; host_p
< hosts_p
; host_p
++)
411 if (!strcmp (name
, *host_p
))
412 break; /* We do not want any duplicates */
413 if (host_p
== hosts_p
)
427 hostname_completion_function (const char *text
, int state
, INPUT_COMPLETE_FLAGS flags
)
429 static char **host_p
;
430 static int textstart
, textlen
;
433 SHOW_C_CTX ("hostname_completion_function");
436 { /* Initialization stuff */
441 for (host_p
= hosts
; *host_p
; host_p
++)
445 hosts
= g_new (char *, (hosts_alloclen
= 30) + 1);
448 fetch_hosts ((p
= getenv ("HOSTFILE")) ? p
: "/etc/hosts");
450 textstart
= (*text
== '@') ? 1 : 0;
451 textlen
= strlen (text
+ textstart
);
457 break; /* Match all of them */
458 else if (!strncmp (text
+ textstart
, *host_p
, textlen
))
465 for (host_p
= hosts
; *host_p
; host_p
++)
473 char *temp
= g_malloc (2 + strlen (*host_p
));
477 strcpy (temp
+ textstart
, *host_p
);
484 * This is the function to call when the word to complete is in a position
485 * where a command word can be found. It looks around $PATH, looking for
486 * commands that match. It also scans aliases, function names, and the
487 * table of shell built-ins.
490 command_completion_function (const char *_text
, int state
, INPUT_COMPLETE_FLAGS flags
)
493 static const char *path_end
;
494 static gboolean isabsolute
;
496 static size_t text_len
;
497 static const char *const *words
;
499 static char *cur_path
;
500 static char *cur_word
;
501 static int init_state
;
502 static const char *const bash_reserved
[] = {
503 "if", "then", "else", "elif", "fi", "case", "esac", "for",
504 "select", "while", "until", "do", "done", "in", "function", 0
506 static const char *const bash_builtins
[] = {
507 "alias", "bg", "bind", "break", "builtin", "cd", "command",
508 "continue", "declare", "dirs", "echo", "enable", "eval",
509 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
510 "help", "history", "jobs", "kill", "let", "local", "logout",
511 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
512 "shift", "source", "suspend", "test", "times", "trap", "type",
513 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
517 SHOW_C_CTX ("command_completion_function");
519 if (!(flags
& INPUT_COMPLETE_COMMANDS
))
522 text
= strutils_shell_unescape (_text
);
523 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
526 { /* Initialize us a little bit */
527 isabsolute
= strchr (text
, PATH_SEP
) != NULL
;
530 words
= bash_reserved
;
532 text_len
= strlen (text
);
536 path
= g_strdup (getenv ("PATH"));
540 path_end
= strchr (p
, '\0');
541 while ((p
= strchr (p
, PATH_ENV_SEP
)) != NULL
)
552 p
= filename_completion_function (text
, state
, flags
);
557 p
= strutils_shell_escape (p
);
568 case 0: /* Reserved words */
571 if (strncmp (*words
, text
, text_len
) == 0)
574 return g_strdup (*(words
++));
579 words
= bash_builtins
;
580 case 1: /* Builtin commands */
583 if (strncmp (*words
, text
, text_len
) == 0)
586 return g_strdup (*(words
++));
595 case 2: /* And looking through the $PATH */
602 if (cur_path
>= path_end
)
604 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
605 cur_word
= concat_dir_and_file (expanded
, text
);
607 canonicalize_pathname (cur_word
);
608 cur_path
= strchr (cur_path
, 0) + 1;
611 found
= filename_completion_function (cur_word
, state
- init_state
, flags
);
627 p
= strrchr (found
, PATH_SEP
);
631 found
= strutils_shell_escape (p
+ 1);
641 match_compare (const void *a
, const void *b
)
643 return strcmp (*(char **) a
, *(char **) b
);
646 /* Returns an array of char * matches with the longest common denominator
647 in the 1st entry. Then a NULL terminated list of different possible
649 You have to supply your own CompletionFunction with the word you
650 want to complete as the first argument and an count of previous matches
652 In case no matches were found we return NULL. */
654 completion_matches (const char *text
, CompletionFunction entry_function
, INPUT_COMPLETE_FLAGS flags
)
656 /* Number of slots in match_list. */
659 /* The list of matches. */
660 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
662 /* Number of matches actually found. */
665 /* Temporary string binder. */
668 match_list
[1] = NULL
;
670 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
)
672 if (matches
+ 1 == match_list_size
)
674 (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
675 match_list
[++matches
] = string
;
676 match_list
[matches
+ 1] = NULL
;
679 /* If there were any matches, then look through them finding out the
680 lowest common denominator. That then becomes match_list[0]. */
684 int low
= 4096; /* Count of max-matched characters. */
686 /* If only one match, just use that. */
689 match_list
[0] = match_list
[1];
690 match_list
[1] = NULL
;
696 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
698 /* And compare each member of the list with
699 the next, finding out where they stop matching.
700 If we find two equal strings, we have to put one away... */
703 while (j
< matches
+ 1)
708 for (si
= match_list
[i
], sj
= match_list
[j
]; si
[0] && sj
[0];)
711 ni
= str_get_next_char (si
);
712 nj
= str_get_next_char (sj
);
714 if (ni
- si
!= nj
- sj
)
716 if (strncmp (si
, sj
, ni
- si
) != 0)
723 if (si
[0] == '\0' && sj
[0] == '\0')
724 { /* Two equal strings */
725 g_free (match_list
[j
]);
729 continue; /* Look for a run of equal strings */
731 else if (low
> si
- match_list
[i
])
732 low
= si
- match_list
[i
];
733 if (i
+ 1 != j
) /* So there's some gap */
734 match_list
[i
+ 1] = match_list
[j
];
739 match_list
[matches
+ 1] = NULL
;
740 match_list
[0] = g_strndup (match_list
[1], low
);
744 { /* There were no matches. */
751 /* Check if directory completion is needed */
753 check_is_cd (const char *text
, int start
, INPUT_COMPLETE_FLAGS flags
)
758 SHOW_C_CTX ("check_is_cd");
759 if (!(flags
& INPUT_COMPLETE_CD
))
762 /* Skip initial spaces */
764 q
= (char *) text
+ start
;
765 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
768 /* Check if the command is "cd" and the cursor is after it */
773 text
+= str_isspace (p
);
774 if (test
== 3 && (p
< q
))
780 /* Returns an array of matches, or NULL if none. */
782 try_complete (char *text
, int *start
, int *end
, INPUT_COMPLETE_FLAGS flags
)
784 int in_command_position
= 0;
786 char **matches
= NULL
;
787 const char *command_separator_chars
= ";|&{(`";
788 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
789 int is_cd
= check_is_cd (text
, *start
, flags
);
792 SHOW_C_CTX ("try_complete");
793 word
= g_strndup (text
+ *start
, *end
- *start
);
795 /* Determine if this could be a command word. It is if it appears at
796 the start of the line (ignoring preceding whitespace), or if it
797 appears after a character that separates commands. And we have to
798 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
799 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
))
801 ti
= str_get_prev_char (&text
[*start
]);
802 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
804 if (ti
<= text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
805 in_command_position
++;
806 else if (strchr (command_separator_chars
, ti
[0]))
808 register int this_char
, prev_char
;
810 in_command_position
++;
814 /* Handle the two character tokens `>&', `<&', and `>|'.
815 We are not in a command position after one of these. */
817 prev_char
= str_get_prev_char (ti
)[0];
819 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>')) ||
820 (this_char
== '|' && prev_char
== '>'))
821 in_command_position
= 0;
823 else if (ti
> text
&& str_get_prev_char (ti
)[0] == '\\') /* Quoted */
824 in_command_position
= 0;
829 if (flags
& INPUT_COMPLETE_COMMANDS
)
830 p
= strrchr (word
, '`');
831 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
832 q
= strrchr (word
, '$');
833 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
834 r
= strrchr (word
, '@');
835 if (q
&& q
[1] == '(' && INPUT_COMPLETE_COMMANDS
)
838 p
= str_get_next_char (q
);
842 /* Command substitution? */
845 SHOW_C_CTX ("try_complete:cmd_backq_subst");
846 matches
= completion_matches (str_cget_next_char (p
),
847 command_completion_function
,
848 flags
& (~INPUT_COMPLETE_FILENAMES
));
850 *start
+= str_get_next_char (p
) - word
;
854 else if (q
> p
&& q
> r
)
856 SHOW_C_CTX ("try_complete:var_subst");
857 matches
= completion_matches (q
, variable_completion_function
, flags
);
862 /* Starts with '@', then look through the known hostnames for
864 else if (r
> p
&& r
> q
)
866 SHOW_C_CTX ("try_complete:host_subst");
867 matches
= completion_matches (r
, hostname_completion_function
, flags
);
872 /* Starts with `~' and there is no slash in the word, then
873 try completing this word as a username. */
874 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
876 SHOW_C_CTX ("try_complete:user_subst");
877 matches
= completion_matches (word
, username_completion_function
, flags
);
881 /* And finally if this word is in a command position, then
882 complete over possible command names, including aliases, functions,
883 and command names. */
884 if (!matches
&& in_command_position
)
886 SHOW_C_CTX ("try_complete:cmd_subst");
888 completion_matches (word
, command_completion_function
,
889 flags
& (~INPUT_COMPLETE_FILENAMES
));
892 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
))
895 flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
896 SHOW_C_CTX ("try_complete:filename_subst_1");
897 matches
= completion_matches (word
, filename_completion_function
, flags
);
898 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~')
901 for (p
= text
; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
902 if (!strncmp (p
, "cd", 2))
903 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
906 char *const cdpath_ref
= g_strdup (getenv ("CDPATH"));
907 char *cdpath
= cdpath_ref
;
914 while (!matches
&& c
== ':')
916 s
= strchr (cdpath
, ':');
918 s
= strchr (cdpath
, 0);
923 r
= concat_dir_and_file (cdpath
, word
);
924 SHOW_C_CTX ("try_complete:filename_subst_2");
925 matches
= completion_matches (r
, filename_completion_function
, flags
);
929 cdpath
= str_get_next_char (s
);
942 free_completions (WInput
* in
)
946 if (!in
->completions
)
948 for (p
= in
->completions
; *p
; p
++)
950 g_free (in
->completions
);
951 in
->completions
= NULL
;
954 static int query_height
, query_width
;
955 static WInput
*input
;
957 static int start
, end
;
960 insert_text (WInput
* in
, char *text
, ssize_t size
)
962 int buff_len
= str_length (in
->buffer
);
964 size
= min (size
, (ssize_t
) strlen (text
)) + start
- end
;
965 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
)
967 /* Expand the buffer */
968 char *narea
= g_try_realloc (in
->buffer
, in
->current_max_size
+ size
+ in
->field_width
);
972 in
->current_max_size
+= size
+ in
->field_width
;
975 if (strlen (in
->buffer
) + 1 < (size_t) in
->current_max_size
)
979 int i
= strlen (&in
->buffer
[end
]);
981 in
->buffer
[end
+ size
+ i
] = in
->buffer
[end
+ i
];
985 char *p
= in
->buffer
+ end
+ size
, *q
= in
->buffer
+ end
;
990 memcpy (in
->buffer
+ start
, text
, size
- start
+ end
);
991 in
->point
+= str_length (in
->buffer
) - buff_len
;
992 update_input (in
, 1);
999 query_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1001 static char buff
[MB_LEN_MAX
] = "";
1028 for (i
= 0, e
= ((WListbox
*) h
->current
)->list
;
1029 e
!= NULL
; i
++, e
= g_list_next (e
))
1031 WLEntry
*le
= (WLEntry
*) e
->data
;
1033 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
- 1) == 0)
1035 listbox_select_entry ((WListbox
*) h
->current
, i
);
1036 end
= str_get_prev_char (&(input
->buffer
[end
])) - input
->buffer
;
1037 handle_char (input
, parm
);
1038 send_message (h
->current
, WIDGET_DRAW
, 0);
1046 if (parm
< 32 || parm
> 256)
1049 if (is_in_input_map (input
, parm
) == 2)
1053 h
->ret_value
= B_USER
; /* This means we want to refill the
1054 list box and start again */
1059 return MSG_NOT_HANDLED
;
1065 int need_redraw
= 0;
1067 char *last_text
= NULL
;
1069 buff
[bl
] = (char) parm
;
1072 switch (str_is_valid_char (buff
, bl
))
1080 for (i
= 0, e
= ((WListbox
*) h
->current
)->list
;
1081 e
!= NULL
; i
++, e
= g_list_next (e
))
1083 WLEntry
*le
= (WLEntry
*) e
->data
;
1085 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
) == 0)
1087 if (strncmp (&le
->text
[end
- start
], buff
, bl
) == 0)
1092 char *nexti
, *nextl
;
1094 si
= &(le
->text
[end
- start
]);
1095 sl
= &(last_text
[end
- start
]);
1097 for (; si
[0] != '\0' && sl
[0] != '\0';)
1099 nexti
= str_get_next_char (si
);
1100 nextl
= str_get_next_char (sl
);
1102 if (nexti
- si
!= nextl
- sl
)
1104 if (strncmp (si
, sl
, nexti
- si
) != 0)
1111 if (low
> si
- &le
->text
[end
- start
])
1112 low
= si
- &le
->text
[end
- start
];
1114 last_text
= le
->text
;
1120 listbox_select_entry ((WListbox
*) h
->current
, i
);
1121 last_text
= le
->text
;
1127 if (need_redraw
== 2)
1129 insert_text (input
, last_text
, low
);
1130 send_message (h
->current
, WIDGET_DRAW
, 0);
1132 else if (need_redraw
== 1)
1134 h
->ret_value
= B_ENTER
;
1144 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1148 #define DO_INSERTION 1
1150 /* Returns 1 if the user would like to see us again */
1152 complete_engine (WInput
* in
, int what_to_do
)
1156 if (in
->completions
&& (str_offset_to_pos (in
->buffer
, in
->point
)) != end
)
1157 free_completions (in
);
1158 if (!in
->completions
)
1160 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1161 for (s
= in
->point
? in
->point
- 1 : 0; s
>= 0; s
--)
1163 start
= str_offset_to_pos (in
->buffer
, s
);
1164 if (strchr (" \t;|<>", in
->buffer
[start
]))
1167 start
= str_offset_to_pos (in
->buffer
, s
+ 1);
1168 /* FIXME: maybe need check '\\' prev char
1169 if (start > 0 && in->buffer [start-1] == '\\')
1174 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1177 if (in
->completions
)
1179 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1]))
1181 char *lc_complete
= in
->completions
[0];
1182 if (insert_text (in
, lc_complete
, strlen (lc_complete
)))
1184 if (in
->completions
[1])
1187 free_completions (in
);
1192 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1])
1194 int maxlen
= 0, i
, count
= 0;
1196 int start_x
, start_y
;
1198 Dlg_head
*query_dlg
;
1199 WListbox
*query_list
;
1201 for (p
= in
->completions
+ 1; *p
!= NULL
; count
++, p
++)
1203 i
= str_term_width1 (*p
);
1207 start_x
= in
->widget
.x
;
1208 start_y
= in
->widget
.y
;
1209 if (start_y
- 2 >= count
)
1211 y
= start_y
- 2 - count
;
1216 if (start_y
>= LINES
- start_y
- 1)
1224 h
= LINES
- start_y
- 1;
1227 x
= start
- in
->term_first_shown
- 2 + start_x
;
1239 query_dlg
= create_dlg (y
, x
, query_height
, query_width
,
1240 dialog_colors
, query_callback
,
1241 "[Completion]", NULL
, DLG_COMPACT
);
1242 query_list
= listbox_new (1, 1, h
- 2, w
- 2, FALSE
, NULL
);
1243 add_widget (query_dlg
, query_list
);
1244 for (p
= in
->completions
+ 1; *p
; p
++)
1245 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
, 0, *p
, NULL
);
1246 run_dlg (query_dlg
);
1248 if (query_dlg
->ret_value
== B_ENTER
)
1250 listbox_get_current (query_list
, &q
, NULL
);
1252 insert_text (in
, q
, strlen (q
));
1254 if (q
|| end
!= min_end
)
1255 free_completions (in
);
1256 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1257 destroy_dlg (query_dlg
);
1268 complete (WInput
* in
)
1272 if (!str_is_valid_string (in
->buffer
))
1275 if (in
->completions
)
1276 engine_flags
= DO_QUERY
;
1279 engine_flags
= DO_INSERTION
;
1281 if (show_all_if_ambiguous
)
1282 engine_flags
|= DO_QUERY
;
1285 while (complete_engine (in
, engine_flags
));