Ticket #1648: implemented single-line boxes.
[kaloumi3.git] / src / complete.c
blob7a27af23c9fb8ab8995eba27353f5af95002053e
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. */
23 /** \file complete.c
24 * \brief Source: Input line filename/username/hostname/variable/command completion
27 #include <config.h>
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <dirent.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <pwd.h>
37 #include <unistd.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"
47 #include "dialog.h"
48 #include "widget.h"
49 #include "wtools.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
59 static const char *
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' : ' ';
72 return s_cf;
75 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
76 #else
77 #define SHOW_C_CTX(func)
78 #endif /* DO_CMPLETION_DEBUG */
80 static char *
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))
96 char *u_text;
97 char *result;
98 char *e_result;
100 u_text = strutils_shell_unescape (text);
102 result = filename_completion_function (u_text, state, flags & (~INPUT_COMPLETE_SHELL_ESC));
103 g_free (u_text);
105 e_result = strutils_shell_escape (result);
106 g_free (result);
108 return e_result;
111 /* If we're starting the match process, initialize us a bit. */
112 if (!state)
114 const char *temp;
116 g_free (dirname);
117 g_free (filename);
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);
125 else
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
139 and `command`.
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))
151 continue;
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, ".."))
158 continue;
160 else
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))
167 continue;
169 isdir = 1;
170 isexec = 0;
172 char *tmp;
173 struct stat tempstat;
175 tmp = g_strconcat (dirname, PATH_SEP_STR, entry->d_name, (char *) NULL);
176 canonicalize_pathname (tmp);
177 /* Unix version */
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))
185 isdir = 0;
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))
190 isexec = 1;
193 else
195 /* stat failed, strange. not a dir in any case */
196 isdir = 0;
198 g_free (tmp);
200 if ((flags & INPUT_COMPLETE_COMMANDS) && (isexec || isdir))
201 break;
202 if ((flags & INPUT_COMPLETE_CD) && isdir)
203 break;
204 if (flags & (INPUT_COMPLETE_FILENAMES))
205 break;
208 if (!entry)
210 if (directory)
212 mc_closedir (directory);
213 directory = NULL;
215 g_free (dirname);
216 dirname = NULL;
217 g_free (filename);
218 filename = NULL;
219 g_free (users_dirname);
220 users_dirname = NULL;
221 return NULL;
223 else
225 char *temp;
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);
240 else
242 temp = g_malloc (2 + NLENGTH (entry));
243 strcpy (temp, entry->d_name);
245 if (isdir)
246 strcat (temp, PATH_SEP_STR);
248 return temp;
252 /* We assume here that text[0] == '~' , if you want to call it in another way,
253 you have to change the code */
254 static char *
255 username_completion_function (const char *text, int state, INPUT_COMPLETE_FLAGS flags)
257 static struct passwd *entry;
258 static size_t userlen;
260 (void) flags;
261 SHOW_C_CTX ("username_completion_function");
263 if (text[0] == '\\' && text[1] == '~')
264 text++;
265 if (!state)
266 { /* Initialization stuff */
267 setpwent ();
268 userlen = strlen (text + 1);
270 while ((entry = getpwent ()) != NULL)
272 /* Null usernames should result in all users as possible completions. */
273 if (userlen == 0)
274 break;
275 if (text[1] == entry->pw_name[0] && !strncmp (text + 1, entry->pw_name, userlen))
276 break;
279 if (entry)
280 return g_strconcat ("~", entry->pw_name, PATH_SEP_STR, (char *) NULL);
282 endpwent ();
283 return 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;
289 #endif
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 */
293 static char *
294 variable_completion_function (const char *text, int state, INPUT_COMPLETE_FLAGS flags)
296 static char **env_p;
297 static int varlen, isbrace;
298 const char *p = NULL;
300 (void) flags;
301 SHOW_C_CTX ("variable_completion_function");
303 if (!state)
304 { /* Initialization stuff */
305 isbrace = (text[1] == '{');
306 varlen = strlen (text + 1 + isbrace);
307 env_p = environ;
310 while (*env_p)
312 p = strchr (*env_p, '=');
313 if (p && p - *env_p >= varlen && !strncmp (text + 1 + isbrace, *env_p, varlen))
314 break;
315 env_p++;
318 if (!*env_p)
319 return NULL;
320 else
322 char *temp = g_malloc (2 + 2 * isbrace + p - *env_p);
324 *temp = '$';
325 if (isbrace)
326 temp[1] = '{';
327 memcpy (temp + 1 + isbrace, *env_p, p - *env_p);
328 if (isbrace)
329 strcpy (temp + 2 + (p - *env_p), "}");
330 else
331 temp[1 + p - *env_p] = 0;
332 env_p++;
333 return temp;
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;
343 static void
344 fetch_hosts (const char *filename)
346 FILE *file = fopen (filename, "r");
347 char buffer[256], *name;
348 char *start;
349 char *bi;
351 if (!file)
352 return;
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... */
360 if (bi[0] == '#')
361 continue;
362 /* Handle $include. */
363 if (!strncmp (bi, "$include ", 9))
365 char *includefile = bi + 9;
366 char *t;
368 /* Find start of filename. */
369 while (*includefile && whitespace (*includefile))
370 includefile++;
371 t = includefile;
373 /* Find end of filename. */
374 while (t[0] != '\0' && !str_isspace (t))
375 str_next_char (&t);
376 *t = '\0';
378 fetch_hosts (includefile);
379 continue;
382 /* Skip IP #s. */
383 while (bi[0] != '\0' && !str_isspace (bi))
384 str_next_char (&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))
390 str_next_char (&bi);
391 if (bi[0] == '#')
392 continue;
393 for (start = bi; bi[0] != '\0' && !str_isspace (bi); str_next_char (&bi));
395 if (bi - start == 0)
396 continue;
398 name = g_strndup (start, bi - start);
400 char **host_p;
402 if (hosts_p - hosts >= hosts_alloclen)
404 int j = hosts_p - hosts;
406 hosts =
407 g_realloc ((void *) hosts, ((hosts_alloclen += 30) + 1) * sizeof (char *));
408 hosts_p = hosts + j;
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)
415 *(hosts_p++) = name;
416 *hosts_p = NULL;
418 else
419 g_free (name);
423 fclose (file);
426 static char *
427 hostname_completion_function (const char *text, int state, INPUT_COMPLETE_FLAGS flags)
429 static char **host_p;
430 static int textstart, textlen;
432 (void) flags;
433 SHOW_C_CTX ("hostname_completion_function");
435 if (!state)
436 { /* Initialization stuff */
437 const char *p;
439 if (hosts != NULL)
441 for (host_p = hosts; *host_p; host_p++)
442 g_free (*host_p);
443 g_free (hosts);
445 hosts = g_new (char *, (hosts_alloclen = 30) + 1);
446 *hosts = NULL;
447 hosts_p = hosts;
448 fetch_hosts ((p = getenv ("HOSTFILE")) ? p : "/etc/hosts");
449 host_p = hosts;
450 textstart = (*text == '@') ? 1 : 0;
451 textlen = strlen (text + textstart);
454 while (*host_p)
456 if (!textlen)
457 break; /* Match all of them */
458 else if (!strncmp (text + textstart, *host_p, textlen))
459 break;
460 host_p++;
463 if (!*host_p)
465 for (host_p = hosts; *host_p; host_p++)
466 g_free (*host_p);
467 g_free (hosts);
468 hosts = NULL;
469 return NULL;
471 else
473 char *temp = g_malloc (2 + strlen (*host_p));
475 if (textstart)
476 *temp = '@';
477 strcpy (temp + textstart, *host_p);
478 host_p++;
479 return temp;
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.
489 static char *
490 command_completion_function (const char *_text, int state, INPUT_COMPLETE_FLAGS flags)
492 char *text;
493 static const char *path_end;
494 static gboolean isabsolute;
495 static int phase;
496 static size_t text_len;
497 static const char *const *words;
498 static char *path;
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
515 char *p, *found;
517 SHOW_C_CTX ("command_completion_function");
519 if (!(flags & INPUT_COMPLETE_COMMANDS))
520 return 0;
521 text = strutils_shell_unescape (_text);
522 flags &= ~INPUT_COMPLETE_SHELL_ESC;
524 if (!state)
525 { /* Initialize us a little bit */
526 isabsolute = strchr (text, PATH_SEP) != NULL;
527 if (!isabsolute)
529 words = bash_reserved;
530 phase = 0;
531 text_len = strlen (text);
532 if (!path && (path = g_strdup (getenv ("PATH"))) != NULL)
534 p = path;
535 path_end = strchr (p, 0);
536 while ((p = strchr (p, PATH_ENV_SEP)))
538 *p++ = 0;
544 if (isabsolute)
546 p = filename_completion_function (text, state, flags);
548 if (p)
550 char *temp_p = p;
551 p = strutils_shell_escape (p);
552 g_free (temp_p);
555 g_free (text);
556 return p;
559 found = NULL;
560 switch (phase)
562 case 0: /* Reserved words */
563 while (*words)
565 if (strncmp (*words, text, text_len) == 0)
567 g_free (text);
568 return g_strdup (*(words++));
570 words++;
572 phase++;
573 words = bash_builtins;
574 case 1: /* Builtin commands */
575 while (*words)
577 if (strncmp (*words, text, text_len) == 0)
579 g_free (text);
580 return g_strdup (*(words++));
582 words++;
584 phase++;
585 if (!path)
586 break;
587 cur_path = path;
588 cur_word = NULL;
589 case 2: /* And looking through the $PATH */
590 while (!found)
592 if (!cur_word)
594 char *expanded;
596 if (cur_path >= path_end)
597 break;
598 expanded = tilde_expand (*cur_path ? cur_path : ".");
599 cur_word = concat_dir_and_file (expanded, text);
600 g_free (expanded);
601 canonicalize_pathname (cur_word);
602 cur_path = strchr (cur_path, 0) + 1;
603 init_state = state;
605 found = filename_completion_function (cur_word, state - init_state, flags);
606 if (!found)
608 g_free (cur_word);
609 cur_word = NULL;
614 if (found == NULL)
616 g_free (path);
617 path = NULL;
619 else if ((p = strrchr (found, PATH_SEP)) != NULL)
621 char *tmp = found;
622 found = strutils_shell_escape (p + 1);
623 g_free (tmp);
626 g_free (text);
627 return found;
630 static int
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
638 completions follows.
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
641 as the second.
642 In case no matches were found we return NULL. */
643 static char **
644 completion_matches (const char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
646 /* Number of slots in match_list. */
647 int match_list_size;
649 /* The list of matches. */
650 char **match_list = g_new (char *, (match_list_size = 30) + 1);
652 /* Number of matches actually found. */
653 int matches = 0;
655 /* Temporary string binder. */
656 char *string;
658 match_list[1] = NULL;
660 while ((string = (*entry_function) (text, matches, flags)) != NULL)
662 if (matches + 1 == match_list_size)
663 match_list =
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]. */
671 if (matches)
673 register int i = 1;
674 int low = 4096; /* Count of max-matched characters. */
676 /* If only one match, just use that. */
677 if (matches == 1)
679 match_list[0] = match_list[1];
680 match_list[1] = NULL;
682 else
684 int j;
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... */
692 j = i + 1;
693 while (j < matches + 1)
695 char *si, *sj;
696 char *ni, *nj;
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)
705 break;
706 if (strncmp (si, sj, ni - si) != 0)
707 break;
709 si = ni;
710 sj = nj;
713 if (si[0] == '\0' && sj[0] == '\0')
714 { /* Two equal strings */
715 g_free (match_list[j]);
716 j++;
717 if (j > matches)
718 break;
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];
725 i++;
726 j++;
728 matches = i;
729 match_list[matches + 1] = NULL;
730 match_list[0] = g_strndup (match_list[1], low);
733 else
734 { /* There were no matches. */
735 g_free (match_list);
736 match_list = NULL;
738 return match_list;
741 /* Check if directory completion is needed */
742 static int
743 check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
745 char *p, *q;
746 int test = 0;
748 SHOW_C_CTX ("check_is_cd");
749 if (!(flags & INPUT_COMPLETE_CD))
750 return 0;
752 /* Skip initial spaces */
753 p = (char *) text;
754 q = (char *) text + start;
755 while (p < q && p[0] != '\0' && str_isspace (p))
756 str_next_char (&p);
758 /* Check if the command is "cd" and the cursor is after it */
759 text += p[0] == 'c';
760 str_next_char (&p);
761 text += p[0] == 'd';
762 str_next_char (&p);
763 text += str_isspace (p);
764 if (test == 3 && (p < q))
765 return 1;
767 return 0;
770 /* Returns an array of matches, or NULL if none. */
771 static char **
772 try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
774 int in_command_position = 0;
775 char *word;
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);
780 char *ti;
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'))
793 str_prev_char (&ti);
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++;
802 if (ti > text)
804 /* Handle the two character tokens `>&', `<&', and `>|'.
805 We are not in a command position after one of these. */
806 this_char = ti[0];
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)
827 if (q > p)
828 p = str_get_next_char (q);
829 q = NULL;
832 /* Command substitution? */
833 if (p > q && p > r)
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));
839 if (matches)
840 *start += str_get_next_char (p) - word;
843 /* Variable name? */
844 else if (q > p && q > r)
846 SHOW_C_CTX ("try_complete:var_subst");
847 matches = completion_matches (q, variable_completion_function, flags);
848 if (matches)
849 *start += q - word;
852 /* Starts with '@', then look through the known hostnames for
853 completion first. */
854 else if (r > p && r > q)
856 SHOW_C_CTX ("try_complete:host_subst");
857 matches = completion_matches (r, hostname_completion_function, flags);
858 if (matches)
859 *start += r - word;
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");
877 matches =
878 completion_matches (word, command_completion_function,
879 flags & (~INPUT_COMPLETE_FILENAMES));
882 else if (!matches && (flags & INPUT_COMPLETE_FILENAMES))
884 if (is_cd)
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 != '~')
890 q = text + *start;
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));
894 if (p == q)
896 char *const cdpath_ref = g_strdup (getenv ("CDPATH"));
897 char *cdpath = cdpath_ref;
898 char c, *s;
900 if (cdpath == NULL)
901 c = 0;
902 else
903 c = ':';
904 while (!matches && c == ':')
906 s = strchr (cdpath, ':');
907 if (s == NULL)
908 s = strchr (cdpath, 0);
909 c = *s;
910 *s = 0;
911 if (*cdpath)
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);
916 g_free (r);
918 *s = c;
919 cdpath = str_get_next_char (s);
921 g_free (cdpath_ref);
926 g_free (word);
928 return matches;
931 void
932 free_completions (WInput * in)
934 char **p;
936 if (!in->completions)
937 return;
938 for (p = in->completions; *p; p++)
939 g_free (*p);
940 g_free (in->completions);
941 in->completions = NULL;
944 static int query_height, query_width;
945 static WInput *input;
946 static int min_end;
947 static int start, end;
949 static int
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);
959 if (narea != NULL)
961 in->buffer = narea;
962 in->current_max_size += size + in->field_width;
965 if (strlen (in->buffer) + 1 < (size_t) in->current_max_size)
967 if (size > 0)
969 int i = strlen (&in->buffer[end]);
970 for (; i >= 0; i--)
971 in->buffer[end + size + i] = in->buffer[end + i];
973 else if (size < 0)
975 char *p = in->buffer + end + size, *q = in->buffer + end;
976 while (*q)
977 *(p++) = *(q++);
978 *p = 0;
980 memcpy (in->buffer + start, text, size - start + end);
981 in->point += str_length (in->buffer) - buff_len;
982 update_input (in, 1);
983 end += size;
985 return size != 0;
988 static cb_ret_t
989 query_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
991 static char buff[MB_LEN_MAX] = "";
992 static int bl = 0;
994 switch (msg)
996 case DLG_KEY:
997 switch (parm)
999 case KEY_LEFT:
1000 case KEY_RIGHT:
1001 bl = 0;
1002 h->ret_value = 0;
1003 dlg_stop (h);
1004 return MSG_HANDLED;
1006 case KEY_BACKSPACE:
1007 bl = 0;
1008 if (end == min_end)
1010 h->ret_value = 0;
1011 dlg_stop (h);
1013 else
1015 int i;
1016 GList *e;
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);
1029 break;
1033 return MSG_HANDLED;
1035 default:
1036 if (parm < 32 || parm > 256)
1038 bl = 0;
1039 if (is_in_input_map (input, parm) == 2)
1041 if (end == min_end)
1042 return MSG_HANDLED;
1043 h->ret_value = B_USER; /* This means we want to refill the
1044 list box and start again */
1045 dlg_stop (h);
1046 return MSG_HANDLED;
1048 else
1049 return MSG_NOT_HANDLED;
1051 else
1053 GList *e;
1054 int i;
1055 int need_redraw = 0;
1056 int low = 4096;
1057 char *last_text = NULL;
1059 buff[bl] = (char) parm;
1060 bl++;
1061 buff[bl] = '\0';
1062 switch (str_is_valid_char (buff, bl))
1064 case -1:
1065 bl = 0;
1066 case -2:
1067 return MSG_HANDLED;
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)
1079 if (need_redraw)
1081 char *si, *sl;
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)
1093 break;
1094 if (strncmp (si, sl, nexti - si) != 0)
1095 break;
1097 si = nexti;
1098 sl = nextl;
1101 if (low > si - &le->text[end - start])
1102 low = si - &le->text[end - start];
1104 last_text = le->text;
1105 need_redraw = 2;
1107 else
1109 need_redraw = 1;
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;
1125 dlg_stop (h);
1127 bl = 0;
1129 return MSG_HANDLED;
1131 break;
1133 default:
1134 return default_dlg_callback (h, sender, msg, parm, data);
1138 #define DO_INSERTION 1
1139 #define DO_QUERY 2
1140 /* Returns 1 if the user would like to see us again */
1141 static int
1142 complete_engine (WInput * in, int what_to_do)
1144 int s;
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]))
1156 if (start < end)
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] == '\\')
1161 break;
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])
1175 tty_beep ();
1176 else
1177 free_completions (in);
1179 else
1180 tty_beep ();
1182 if ((what_to_do & DO_QUERY) && in->completions && in->completions[1])
1184 int maxlen = 0, i, count = 0;
1185 int x, y, w, h;
1186 int start_x, start_y;
1187 char **p, *q;
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)
1193 maxlen = i;
1194 start_x = in->widget.x;
1195 start_y = in->widget.y;
1196 if (start_y - 2 >= count)
1198 y = start_y - 2 - count;
1199 h = 2 + count;
1201 else
1203 if (start_y >= LINES - start_y - 1)
1205 y = 0;
1206 h = start_y;
1208 else
1210 y = start_y + 1;
1211 h = LINES - start_y - 1;
1214 x = start - in->term_first_shown - 2 + start_x;
1215 w = maxlen + 4;
1216 if (x + w > COLS)
1217 x = COLS - w;
1218 if (x < 0)
1219 x = 0;
1220 if (x + w > COLS)
1221 w = COLS;
1222 input = in;
1223 min_end = end;
1224 query_height = h;
1225 query_width = w;
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);
1234 q = NULL;
1235 if (query_dlg->ret_value == B_ENTER)
1237 listbox_get_current (query_list, &q, NULL);
1238 if (q)
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);
1245 if (i == B_USER)
1246 return 1;
1249 else
1250 tty_beep ();
1251 return 0;
1254 void
1255 complete (WInput * in)
1257 int engine_flags;
1259 if (!str_is_valid_string (in->buffer))
1260 return;
1262 if (in->completions)
1263 engine_flags = DO_QUERY;
1264 else
1266 engine_flags = DO_INSERTION;
1268 if (show_all_if_ambiguous)
1269 engine_flags |= DO_QUERY;
1272 while (complete_engine (in, engine_flags));