Ticket #2021 (MarkFileDown/MarkFileUp)
[free-mc.git] / src / complete.c
bloba7b8a5af08d00fd3adcd0fb20174b6d268852a0d
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 == 0)
114 const char *temp;
116 g_free (dirname);
117 g_free (filename);
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);
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 == NULL)
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 == 0)
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 != NULL)
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 == 0)
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 == NULL)
319 return NULL;
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;
522 text = strutils_shell_unescape (_text);
523 flags &= ~INPUT_COMPLETE_SHELL_ESC;
525 if (state == 0)
526 { /* Initialize us a little bit */
527 isabsolute = strchr (text, PATH_SEP) != NULL;
528 if (!isabsolute)
530 words = bash_reserved;
531 phase = 0;
532 text_len = strlen (text);
534 if (path == NULL)
536 path = g_strdup (getenv ("PATH"));
537 if (path != NULL)
539 p = path;
540 path_end = strchr (p, '\0');
541 while ((p = strchr (p, PATH_ENV_SEP)) != NULL)
543 *p++ = '\0';
550 if (isabsolute)
552 p = filename_completion_function (text, state, flags);
554 if (p != NULL)
556 char *temp_p = p;
557 p = strutils_shell_escape (p);
558 g_free (temp_p);
561 g_free (text);
562 return p;
565 found = NULL;
566 switch (phase)
568 case 0: /* Reserved words */
569 while (*words)
571 if (strncmp (*words, text, text_len) == 0)
573 g_free (text);
574 return g_strdup (*(words++));
576 words++;
578 phase++;
579 words = bash_builtins;
580 case 1: /* Builtin commands */
581 while (*words)
583 if (strncmp (*words, text, text_len) == 0)
585 g_free (text);
586 return g_strdup (*(words++));
588 words++;
590 phase++;
591 if (!path)
592 break;
593 cur_path = path;
594 cur_word = NULL;
595 case 2: /* And looking through the $PATH */
596 while (!found)
598 if (!cur_word)
600 char *expanded;
602 if (cur_path >= path_end)
603 break;
604 expanded = tilde_expand (*cur_path ? cur_path : ".");
605 cur_word = concat_dir_and_file (expanded, text);
606 g_free (expanded);
607 canonicalize_pathname (cur_word);
608 cur_path = strchr (cur_path, 0) + 1;
609 init_state = state;
611 found = filename_completion_function (cur_word, state - init_state, flags);
612 if (!found)
614 g_free (cur_word);
615 cur_word = NULL;
620 if (found == NULL)
622 g_free (path);
623 path = NULL;
625 else
627 p = strrchr (found, PATH_SEP);
628 if (p != NULL)
630 char *tmp = found;
631 found = strutils_shell_escape (p + 1);
632 g_free (tmp);
636 g_free (text);
637 return found;
640 static int
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
648 completions follows.
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
651 as the second.
652 In case no matches were found we return NULL. */
653 static char **
654 completion_matches (const char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
656 /* Number of slots in match_list. */
657 int match_list_size;
659 /* The list of matches. */
660 char **match_list = g_new (char *, (match_list_size = 30) + 1);
662 /* Number of matches actually found. */
663 int matches = 0;
665 /* Temporary string binder. */
666 char *string;
668 match_list[1] = NULL;
670 while ((string = (*entry_function) (text, matches, flags)) != NULL)
672 if (matches + 1 == match_list_size)
673 match_list =
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]. */
681 if (matches)
683 register int i = 1;
684 int low = 4096; /* Count of max-matched characters. */
686 /* If only one match, just use that. */
687 if (matches == 1)
689 match_list[0] = match_list[1];
690 match_list[1] = NULL;
692 else
694 int j;
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... */
702 j = i + 1;
703 while (j < matches + 1)
705 char *si, *sj;
706 char *ni, *nj;
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)
715 break;
716 if (strncmp (si, sj, ni - si) != 0)
717 break;
719 si = ni;
720 sj = nj;
723 if (si[0] == '\0' && sj[0] == '\0')
724 { /* Two equal strings */
725 g_free (match_list[j]);
726 j++;
727 if (j > matches)
728 break;
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];
735 i++;
736 j++;
738 matches = i;
739 match_list[matches + 1] = NULL;
740 match_list[0] = g_strndup (match_list[1], low);
743 else
744 { /* There were no matches. */
745 g_free (match_list);
746 match_list = NULL;
748 return match_list;
751 /* Check if directory completion is needed */
752 static int
753 check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
755 char *p, *q;
756 int test = 0;
758 SHOW_C_CTX ("check_is_cd");
759 if (!(flags & INPUT_COMPLETE_CD))
760 return 0;
762 /* Skip initial spaces */
763 p = (char *) text;
764 q = (char *) text + start;
765 while (p < q && p[0] != '\0' && str_isspace (p))
766 str_next_char (&p);
768 /* Check if the command is "cd" and the cursor is after it */
769 text += p[0] == 'c';
770 str_next_char (&p);
771 text += p[0] == 'd';
772 str_next_char (&p);
773 text += str_isspace (p);
774 if (test == 3 && (p < q))
775 return 1;
777 return 0;
780 /* Returns an array of matches, or NULL if none. */
781 static char **
782 try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
784 int in_command_position = 0;
785 char *word;
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);
790 char *ti;
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'))
803 str_prev_char (&ti);
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++;
812 if (ti > text)
814 /* Handle the two character tokens `>&', `<&', and `>|'.
815 We are not in a command position after one of these. */
816 this_char = ti[0];
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)
837 if (q > p)
838 p = str_get_next_char (q);
839 q = NULL;
842 /* Command substitution? */
843 if (p > q && p > r)
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));
849 if (matches)
850 *start += str_get_next_char (p) - word;
853 /* Variable name? */
854 else if (q > p && q > r)
856 SHOW_C_CTX ("try_complete:var_subst");
857 matches = completion_matches (q, variable_completion_function, flags);
858 if (matches)
859 *start += q - word;
862 /* Starts with '@', then look through the known hostnames for
863 completion first. */
864 else if (r > p && r > q)
866 SHOW_C_CTX ("try_complete:host_subst");
867 matches = completion_matches (r, hostname_completion_function, flags);
868 if (matches)
869 *start += r - word;
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");
887 matches =
888 completion_matches (word, command_completion_function,
889 flags & (~INPUT_COMPLETE_FILENAMES));
892 else if (!matches && (flags & INPUT_COMPLETE_FILENAMES))
894 if (is_cd)
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 != '~')
900 q = text + *start;
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));
904 if (p == q)
906 char *const cdpath_ref = g_strdup (getenv ("CDPATH"));
907 char *cdpath = cdpath_ref;
908 char c, *s;
910 if (cdpath == NULL)
911 c = 0;
912 else
913 c = ':';
914 while (!matches && c == ':')
916 s = strchr (cdpath, ':');
917 if (s == NULL)
918 s = strchr (cdpath, 0);
919 c = *s;
920 *s = 0;
921 if (*cdpath)
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);
926 g_free (r);
928 *s = c;
929 cdpath = str_get_next_char (s);
931 g_free (cdpath_ref);
936 g_free (word);
938 return matches;
941 void
942 free_completions (WInput * in)
944 char **p;
946 if (!in->completions)
947 return;
948 for (p = in->completions; *p; p++)
949 g_free (*p);
950 g_free (in->completions);
951 in->completions = NULL;
954 static int query_height, query_width;
955 static WInput *input;
956 static int min_end;
957 static int start, end;
959 static int
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);
969 if (narea != NULL)
971 in->buffer = narea;
972 in->current_max_size += size + in->field_width;
975 if (strlen (in->buffer) + 1 < (size_t) in->current_max_size)
977 if (size > 0)
979 int i = strlen (&in->buffer[end]);
980 for (; i >= 0; i--)
981 in->buffer[end + size + i] = in->buffer[end + i];
983 else if (size < 0)
985 char *p = in->buffer + end + size, *q = in->buffer + end;
986 while (*q)
987 *(p++) = *(q++);
988 *p = 0;
990 memcpy (in->buffer + start, text, size - start + end);
991 in->point += str_length (in->buffer) - buff_len;
992 update_input (in, 1);
993 end += size;
995 return size != 0;
998 static cb_ret_t
999 query_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1001 static char buff[MB_LEN_MAX] = "";
1002 static int bl = 0;
1004 switch (msg)
1006 case DLG_KEY:
1007 switch (parm)
1009 case KEY_LEFT:
1010 case KEY_RIGHT:
1011 bl = 0;
1012 h->ret_value = 0;
1013 dlg_stop (h);
1014 return MSG_HANDLED;
1016 case KEY_BACKSPACE:
1017 bl = 0;
1018 if (end == min_end)
1020 h->ret_value = 0;
1021 dlg_stop (h);
1023 else
1025 int i;
1026 GList *e;
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);
1039 break;
1043 return MSG_HANDLED;
1045 default:
1046 if (parm < 32 || parm > 256)
1048 bl = 0;
1049 if (is_in_input_map (input, parm) == 2)
1051 if (end == min_end)
1052 return MSG_HANDLED;
1053 h->ret_value = B_USER; /* This means we want to refill the
1054 list box and start again */
1055 dlg_stop (h);
1056 return MSG_HANDLED;
1058 else
1059 return MSG_NOT_HANDLED;
1061 else
1063 GList *e;
1064 int i;
1065 int need_redraw = 0;
1066 int low = 4096;
1067 char *last_text = NULL;
1069 buff[bl] = (char) parm;
1070 bl++;
1071 buff[bl] = '\0';
1072 switch (str_is_valid_char (buff, bl))
1074 case -1:
1075 bl = 0;
1076 case -2:
1077 return MSG_HANDLED;
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)
1089 if (need_redraw)
1091 char *si, *sl;
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)
1103 break;
1104 if (strncmp (si, sl, nexti - si) != 0)
1105 break;
1107 si = nexti;
1108 sl = nextl;
1111 if (low > si - &le->text[end - start])
1112 low = si - &le->text[end - start];
1114 last_text = le->text;
1115 need_redraw = 2;
1117 else
1119 need_redraw = 1;
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;
1135 dlg_stop (h);
1137 bl = 0;
1139 return MSG_HANDLED;
1141 break;
1143 default:
1144 return default_dlg_callback (h, sender, msg, parm, data);
1148 #define DO_INSERTION 1
1149 #define DO_QUERY 2
1150 /* Returns 1 if the user would like to see us again */
1151 static int
1152 complete_engine (WInput * in, int what_to_do)
1154 int s;
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]))
1166 if (start < end)
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] == '\\')
1171 break;
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])
1185 tty_beep ();
1186 else
1187 free_completions (in);
1189 else
1190 tty_beep ();
1192 if ((what_to_do & DO_QUERY) && in->completions && in->completions[1])
1194 int maxlen = 0, i, count = 0;
1195 int x, y, w, h;
1196 int start_x, start_y;
1197 char **p, *q;
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);
1204 if (i > maxlen)
1205 maxlen = i;
1207 start_x = in->widget.x;
1208 start_y = in->widget.y;
1209 if (start_y - 2 >= count)
1211 y = start_y - 2 - count;
1212 h = 2 + count;
1214 else
1216 if (start_y >= LINES - start_y - 1)
1218 y = 0;
1219 h = start_y;
1221 else
1223 y = start_y + 1;
1224 h = LINES - start_y - 1;
1227 x = start - in->term_first_shown - 2 + start_x;
1228 w = maxlen + 4;
1229 if (x + w > COLS)
1230 x = COLS - w;
1231 if (x < 0)
1232 x = 0;
1233 if (x + w > COLS)
1234 w = COLS;
1235 input = in;
1236 min_end = end;
1237 query_height = h;
1238 query_width = w;
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);
1247 q = NULL;
1248 if (query_dlg->ret_value == B_ENTER)
1250 listbox_get_current (query_list, &q, NULL);
1251 if (q)
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);
1258 if (i == B_USER)
1259 return 1;
1262 else
1263 tty_beep ();
1264 return 0;
1267 void
1268 complete (WInput * in)
1270 int engine_flags;
1272 if (!str_is_valid_string (in->buffer))
1273 return;
1275 if (in->completions)
1276 engine_flags = DO_QUERY;
1277 else
1279 engine_flags = DO_INSERTION;
1281 if (show_all_if_ambiguous)
1282 engine_flags |= DO_QUERY;
1285 while (complete_engine (in, engine_flags));