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
) && (temp
= strrchr (text
, PATH_SEP
)))
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
))
521 text
= strutils_shell_unescape (_text
);
522 flags
&= ~INPUT_COMPLETE_SHELL_ESC
;
525 { /* Initialize us a little bit */
526 isabsolute
= strchr (text
, PATH_SEP
) != NULL
;
529 words
= bash_reserved
;
531 text_len
= strlen (text
);
532 if (!path
&& (path
= g_strdup (getenv ("PATH"))) != NULL
)
535 path_end
= strchr (p
, 0);
536 while ((p
= strchr (p
, PATH_ENV_SEP
)))
546 p
= filename_completion_function (text
, state
, flags
);
551 p
= strutils_shell_escape (p
);
562 case 0: /* Reserved words */
565 if (strncmp (*words
, text
, text_len
) == 0)
568 return g_strdup (*(words
++));
573 words
= bash_builtins
;
574 case 1: /* Builtin commands */
577 if (strncmp (*words
, text
, text_len
) == 0)
580 return g_strdup (*(words
++));
589 case 2: /* And looking through the $PATH */
596 if (cur_path
>= path_end
)
598 expanded
= tilde_expand (*cur_path
? cur_path
: ".");
599 cur_word
= concat_dir_and_file (expanded
, text
);
601 canonicalize_pathname (cur_word
);
602 cur_path
= strchr (cur_path
, 0) + 1;
605 found
= filename_completion_function (cur_word
, state
- init_state
, flags
);
619 else if ((p
= strrchr (found
, PATH_SEP
)) != NULL
)
622 found
= strutils_shell_escape (p
+ 1);
631 match_compare (const void *a
, const void *b
)
633 return strcmp (*(char **) a
, *(char **) b
);
636 /* Returns an array of char * matches with the longest common denominator
637 in the 1st entry. Then a NULL terminated list of different possible
639 You have to supply your own CompletionFunction with the word you
640 want to complete as the first argument and an count of previous matches
642 In case no matches were found we return NULL. */
644 completion_matches (const char *text
, CompletionFunction entry_function
, INPUT_COMPLETE_FLAGS flags
)
646 /* Number of slots in match_list. */
649 /* The list of matches. */
650 char **match_list
= g_new (char *, (match_list_size
= 30) + 1);
652 /* Number of matches actually found. */
655 /* Temporary string binder. */
658 match_list
[1] = NULL
;
660 while ((string
= (*entry_function
) (text
, matches
, flags
)) != NULL
)
662 if (matches
+ 1 == match_list_size
)
664 (char **) g_realloc (match_list
, ((match_list_size
+= 30) + 1) * sizeof (char *));
665 match_list
[++matches
] = string
;
666 match_list
[matches
+ 1] = NULL
;
669 /* If there were any matches, then look through them finding out the
670 lowest common denominator. That then becomes match_list[0]. */
674 int low
= 4096; /* Count of max-matched characters. */
676 /* If only one match, just use that. */
679 match_list
[0] = match_list
[1];
680 match_list
[1] = NULL
;
686 qsort (match_list
+ 1, matches
, sizeof (char *), match_compare
);
688 /* And compare each member of the list with
689 the next, finding out where they stop matching.
690 If we find two equal strings, we have to put one away... */
693 while (j
< matches
+ 1)
698 for (si
= match_list
[i
], sj
= match_list
[j
]; si
[0] && sj
[0];)
701 ni
= str_get_next_char (si
);
702 nj
= str_get_next_char (sj
);
704 if (ni
- si
!= nj
- sj
)
706 if (strncmp (si
, sj
, ni
- si
) != 0)
713 if (si
[0] == '\0' && sj
[0] == '\0')
714 { /* Two equal strings */
715 g_free (match_list
[j
]);
719 continue; /* Look for a run of equal strings */
721 else if (low
> si
- match_list
[i
])
722 low
= si
- match_list
[i
];
723 if (i
+ 1 != j
) /* So there's some gap */
724 match_list
[i
+ 1] = match_list
[j
];
729 match_list
[matches
+ 1] = NULL
;
730 match_list
[0] = g_strndup (match_list
[1], low
);
734 { /* There were no matches. */
741 /* Check if directory completion is needed */
743 check_is_cd (const char *text
, int start
, INPUT_COMPLETE_FLAGS flags
)
748 SHOW_C_CTX ("check_is_cd");
749 if (!(flags
& INPUT_COMPLETE_CD
))
752 /* Skip initial spaces */
754 q
= (char *) text
+ start
;
755 while (p
< q
&& p
[0] != '\0' && str_isspace (p
))
758 /* Check if the command is "cd" and the cursor is after it */
763 text
+= str_isspace (p
);
764 if (test
== 3 && (p
< q
))
770 /* Returns an array of matches, or NULL if none. */
772 try_complete (char *text
, int *start
, int *end
, INPUT_COMPLETE_FLAGS flags
)
774 int in_command_position
= 0;
776 char **matches
= NULL
;
777 const char *command_separator_chars
= ";|&{(`";
778 char *p
= NULL
, *q
= NULL
, *r
= NULL
;
779 int is_cd
= check_is_cd (text
, *start
, flags
);
782 SHOW_C_CTX ("try_complete");
783 word
= g_strndup (text
+ *start
, *end
- *start
);
785 /* Determine if this could be a command word. It is if it appears at
786 the start of the line (ignoring preceding whitespace), or if it
787 appears after a character that separates commands. And we have to
788 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
789 if (!is_cd
&& (flags
& INPUT_COMPLETE_COMMANDS
))
791 ti
= str_get_prev_char (&text
[*start
]);
792 while (ti
> text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
794 if (ti
<= text
&& (ti
[0] == ' ' || ti
[0] == '\t'))
795 in_command_position
++;
796 else if (strchr (command_separator_chars
, ti
[0]))
798 register int this_char
, prev_char
;
800 in_command_position
++;
804 /* Handle the two character tokens `>&', `<&', and `>|'.
805 We are not in a command position after one of these. */
807 prev_char
= str_get_prev_char (ti
)[0];
809 if ((this_char
== '&' && (prev_char
== '<' || prev_char
== '>')) ||
810 (this_char
== '|' && prev_char
== '>'))
811 in_command_position
= 0;
813 else if (ti
> text
&& str_get_prev_char (ti
)[0] == '\\') /* Quoted */
814 in_command_position
= 0;
819 if (flags
& INPUT_COMPLETE_COMMANDS
)
820 p
= strrchr (word
, '`');
821 if (flags
& (INPUT_COMPLETE_COMMANDS
| INPUT_COMPLETE_VARIABLES
))
822 q
= strrchr (word
, '$');
823 if (flags
& INPUT_COMPLETE_HOSTNAMES
)
824 r
= strrchr (word
, '@');
825 if (q
&& q
[1] == '(' && INPUT_COMPLETE_COMMANDS
)
828 p
= str_get_next_char (q
);
832 /* Command substitution? */
835 SHOW_C_CTX ("try_complete:cmd_backq_subst");
836 matches
= completion_matches (str_cget_next_char (p
),
837 command_completion_function
,
838 flags
& (~INPUT_COMPLETE_FILENAMES
));
840 *start
+= str_get_next_char (p
) - word
;
844 else if (q
> p
&& q
> r
)
846 SHOW_C_CTX ("try_complete:var_subst");
847 matches
= completion_matches (q
, variable_completion_function
, flags
);
852 /* Starts with '@', then look through the known hostnames for
854 else if (r
> p
&& r
> q
)
856 SHOW_C_CTX ("try_complete:host_subst");
857 matches
= completion_matches (r
, hostname_completion_function
, flags
);
862 /* Starts with `~' and there is no slash in the word, then
863 try completing this word as a username. */
864 if (!matches
&& *word
== '~' && (flags
& INPUT_COMPLETE_USERNAMES
) && !strchr (word
, PATH_SEP
))
866 SHOW_C_CTX ("try_complete:user_subst");
867 matches
= completion_matches (word
, username_completion_function
, flags
);
871 /* And finally if this word is in a command position, then
872 complete over possible command names, including aliases, functions,
873 and command names. */
874 if (!matches
&& in_command_position
)
876 SHOW_C_CTX ("try_complete:cmd_subst");
878 completion_matches (word
, command_completion_function
,
879 flags
& (~INPUT_COMPLETE_FILENAMES
));
882 else if (!matches
&& (flags
& INPUT_COMPLETE_FILENAMES
))
885 flags
&= ~(INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_COMMANDS
);
886 SHOW_C_CTX ("try_complete:filename_subst_1");
887 matches
= completion_matches (word
, filename_completion_function
, flags
);
888 if (!matches
&& is_cd
&& *word
!= PATH_SEP
&& *word
!= '~')
891 for (p
= text
; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
892 if (!strncmp (p
, "cd", 2))
893 for (p
+= 2; *p
&& p
< q
&& (*p
== ' ' || *p
== '\t'); str_next_char (&p
));
896 char *const cdpath_ref
= g_strdup (getenv ("CDPATH"));
897 char *cdpath
= cdpath_ref
;
904 while (!matches
&& c
== ':')
906 s
= strchr (cdpath
, ':');
908 s
= strchr (cdpath
, 0);
913 r
= concat_dir_and_file (cdpath
, word
);
914 SHOW_C_CTX ("try_complete:filename_subst_2");
915 matches
= completion_matches (r
, filename_completion_function
, flags
);
919 cdpath
= str_get_next_char (s
);
932 free_completions (WInput
* in
)
936 if (!in
->completions
)
938 for (p
= in
->completions
; *p
; p
++)
940 g_free (in
->completions
);
941 in
->completions
= NULL
;
944 static int query_height
, query_width
;
945 static WInput
*input
;
947 static int start
, end
;
950 insert_text (WInput
* in
, char *text
, ssize_t size
)
952 int buff_len
= str_length (in
->buffer
);
954 size
= min (size
, (ssize_t
) strlen (text
)) + start
- end
;
955 if (strlen (in
->buffer
) + size
>= (size_t) in
->current_max_size
)
957 /* Expand the buffer */
958 char *narea
= g_try_realloc (in
->buffer
, in
->current_max_size
+ size
+ in
->field_width
);
962 in
->current_max_size
+= size
+ in
->field_width
;
965 if (strlen (in
->buffer
) + 1 < (size_t) in
->current_max_size
)
969 int i
= strlen (&in
->buffer
[end
]);
971 in
->buffer
[end
+ size
+ i
] = in
->buffer
[end
+ i
];
975 char *p
= in
->buffer
+ end
+ size
, *q
= in
->buffer
+ end
;
980 memcpy (in
->buffer
+ start
, text
, size
- start
+ end
);
981 in
->point
+= str_length (in
->buffer
) - buff_len
;
982 update_input (in
, 1);
989 query_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
991 static char buff
[MB_LEN_MAX
] = "";
1018 for (i
= 0, e
= ((WListbox
*) h
->current
)->list
;
1019 e
!= NULL
; i
++, e
= g_list_next (e
))
1021 WLEntry
*le
= (WLEntry
*) e
->data
;
1023 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
- 1) == 0)
1025 listbox_select_entry ((WListbox
*) h
->current
, i
);
1026 end
= str_get_prev_char (&(input
->buffer
[end
])) - input
->buffer
;
1027 handle_char (input
, parm
);
1028 send_message (h
->current
, WIDGET_DRAW
, 0);
1036 if (parm
< 32 || parm
> 256)
1039 if (is_in_input_map (input
, parm
) == 2)
1043 h
->ret_value
= B_USER
; /* This means we want to refill the
1044 list box and start again */
1049 return MSG_NOT_HANDLED
;
1055 int need_redraw
= 0;
1057 char *last_text
= NULL
;
1059 buff
[bl
] = (char) parm
;
1062 switch (str_is_valid_char (buff
, bl
))
1070 for (i
= 0, e
= ((WListbox
*) h
->current
)->list
;
1071 e
!= NULL
; i
++, e
= g_list_next (e
))
1073 WLEntry
*le
= (WLEntry
*) e
->data
;
1075 if (strncmp (input
->buffer
+ start
, le
->text
, end
- start
) == 0)
1077 if (strncmp (&le
->text
[end
- start
], buff
, bl
) == 0)
1082 char *nexti
, *nextl
;
1084 si
= &(le
->text
[end
- start
]);
1085 sl
= &(last_text
[end
- start
]);
1087 for (; si
[0] != '\0' && sl
[0] != '\0';)
1089 nexti
= str_get_next_char (si
);
1090 nextl
= str_get_next_char (sl
);
1092 if (nexti
- si
!= nextl
- sl
)
1094 if (strncmp (si
, sl
, nexti
- si
) != 0)
1101 if (low
> si
- &le
->text
[end
- start
])
1102 low
= si
- &le
->text
[end
- start
];
1104 last_text
= le
->text
;
1110 listbox_select_entry ((WListbox
*) h
->current
, i
);
1111 last_text
= le
->text
;
1117 if (need_redraw
== 2)
1119 insert_text (input
, last_text
, low
);
1120 send_message (h
->current
, WIDGET_DRAW
, 0);
1122 else if (need_redraw
== 1)
1124 h
->ret_value
= B_ENTER
;
1134 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1138 #define DO_INSERTION 1
1140 /* Returns 1 if the user would like to see us again */
1142 complete_engine (WInput
* in
, int what_to_do
)
1146 if (in
->completions
&& (str_offset_to_pos (in
->buffer
, in
->point
)) != end
)
1147 free_completions (in
);
1148 if (!in
->completions
)
1150 end
= str_offset_to_pos (in
->buffer
, in
->point
);
1151 for (s
= in
->point
? in
->point
- 1 : 0; s
>= 0; s
--)
1153 start
= str_offset_to_pos (in
->buffer
, s
);
1154 if (strchr (" \t;|<>", in
->buffer
[start
]))
1157 start
= str_offset_to_pos (in
->buffer
, s
+ 1);
1158 /* FIXME: maybe need check '\\' prev char
1159 if (start > 0 && in->buffer [start-1] == '\\')
1164 in
->completions
= try_complete (in
->buffer
, &start
, &end
, in
->completion_flags
);
1167 if (in
->completions
)
1169 if (what_to_do
& DO_INSERTION
|| ((what_to_do
& DO_QUERY
) && !in
->completions
[1]))
1171 char *lc_complete
= in
->completions
[0];
1172 if (insert_text (in
, lc_complete
, strlen (lc_complete
)))
1174 if (in
->completions
[1])
1177 free_completions (in
);
1182 if ((what_to_do
& DO_QUERY
) && in
->completions
&& in
->completions
[1])
1184 int maxlen
= 0, i
, count
= 0;
1186 int start_x
, start_y
;
1188 Dlg_head
*query_dlg
;
1189 WListbox
*query_list
;
1191 for (p
= in
->completions
+ 1; *p
; count
++, p
++)
1192 if ((i
= str_term_width1 (*p
)) > maxlen
)
1194 start_x
= in
->widget
.x
;
1195 start_y
= in
->widget
.y
;
1196 if (start_y
- 2 >= count
)
1198 y
= start_y
- 2 - count
;
1203 if (start_y
>= LINES
- start_y
- 1)
1211 h
= LINES
- start_y
- 1;
1214 x
= start
- in
->term_first_shown
- 2 + start_x
;
1226 query_dlg
= create_dlg (y
, x
, query_height
, query_width
,
1227 dialog_colors
, query_callback
,
1228 "[Completion]", NULL
, DLG_COMPACT
);
1229 query_list
= listbox_new (1, 1, h
- 2, w
- 2, FALSE
, NULL
);
1230 add_widget (query_dlg
, query_list
);
1231 for (p
= in
->completions
+ 1; *p
; p
++)
1232 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
, 0, *p
, NULL
);
1233 run_dlg (query_dlg
);
1235 if (query_dlg
->ret_value
== B_ENTER
)
1237 listbox_get_current (query_list
, &q
, NULL
);
1239 insert_text (in
, q
, strlen (q
));
1241 if (q
|| end
!= min_end
)
1242 free_completions (in
);
1243 i
= query_dlg
->ret_value
; /* B_USER if user wants to start over again */
1244 destroy_dlg (query_dlg
);
1255 complete (WInput
* in
)
1259 if (!str_is_valid_string (in
->buffer
))
1262 if (in
->completions
)
1263 engine_flags
= DO_QUERY
;
1266 engine_flags
= DO_INSERTION
;
1268 if (show_all_if_ambiguous
)
1269 engine_flags
|= DO_QUERY
;
1272 while (complete_engine (in
, engine_flags
));