manually merged 232_fix_init_chown_advanced
[midnight-commander.git] / src / complete.c
blob94e18953c541eddfe2694192c448c4b442b8c733
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 #include <config.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
33 #include <mhl/memory.h>
34 #include <mhl/escape.h>
35 #include <mhl/string.h>
37 #include "global.h"
38 #include "tty.h"
39 #include "win.h"
40 #include "color.h"
41 #include "dialog.h"
42 #include "widget.h"
43 #include "wtools.h"
44 #include "main.h"
45 #include "util.h"
46 #include "key.h" /* XCTRL and ALT macros */
48 typedef char *CompletionFunction (char * text, int state, INPUT_COMPLETE_FLAGS flags);
50 //#define DO_COMPLETION_DEBUG
51 #ifdef DO_COMPLETION_DEBUG
53 * Useful to print/debug completion flags
55 static const char * show_c_flags(INPUT_COMPLETE_FLAGS flags)
57 static char s_cf[] = "FHCVUDS";
59 s_cf[0] = (flags & INPUT_COMPLETE_FILENAMES) ? 'F' : ' ';
60 s_cf[1] = (flags & INPUT_COMPLETE_HOSTNAMES) ? 'H' : ' ';
61 s_cf[2] = (flags & INPUT_COMPLETE_COMMANDS) ? 'C' : ' ';
62 s_cf[3] = (flags & INPUT_COMPLETE_VARIABLES) ? 'V' : ' ';
63 s_cf[4] = (flags & INPUT_COMPLETE_USERNAMES) ? 'U' : ' ';
64 s_cf[5] = (flags & INPUT_COMPLETE_CD) ? 'D' : ' ';
65 s_cf[6] = (flags & INPUT_COMPLETE_SHELL_ESC) ? 'S' : ' ';
67 return s_cf;
69 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
70 #else
71 #define SHOW_C_CTX(func)
72 #endif /* DO_CMPLETION_DEBUG */
74 static char *
75 filename_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
77 static DIR *directory;
78 static char *filename = NULL;
79 static char *dirname = NULL;
80 static char *users_dirname = NULL;
81 static size_t filename_len;
82 int isdir = 1, isexec = 0;
84 struct dirent *entry = NULL;
86 SHOW_C_CTX("filename_completion_function");
88 if (text && (flags & INPUT_COMPLETE_SHELL_ESC))
89 text = mhl_shell_unescape_buf (text);
91 /* If we're starting the match process, initialize us a bit. */
92 if (!state){
93 const char *temp;
95 g_free (dirname);
96 g_free (filename);
97 g_free (users_dirname);
99 if ((*text) && (temp = strrchr (text, PATH_SEP))){
100 filename = g_strdup (++temp);
101 dirname = g_strndup (text, temp - text);
102 } else {
103 dirname = g_strdup (".");
104 filename = g_strdup (text);
107 /* We aren't done yet. We also support the "~user" syntax. */
109 /* Save the version of the directory that the user typed. */
110 users_dirname = dirname;
112 // FIXME: memleak ?
113 dirname = tilde_expand (dirname);
114 canonicalize_pathname (dirname);
115 /* Here we should do something with variable expansion
116 and `command`.
117 Maybe a dream - UNIMPLEMENTED yet. */
119 directory = mc_opendir (dirname);
120 filename_len = strlen (filename);
123 /* Now that we have some state, we can read the directory. */
125 while (directory && (entry = mc_readdir (directory))){
126 /* Special case for no filename.
127 All entries except "." and ".." match. */
128 if (!filename_len){
129 if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
130 continue;
131 } else {
132 /* Otherwise, if these match up to the length of filename, then
133 it may be a match. */
134 if ((entry->d_name[0] != filename[0]) ||
135 ((NLENGTH (entry)) < filename_len) ||
136 strncmp (filename, entry->d_name, filename_len))
137 continue;
139 isdir = 1; isexec = 0;
141 char *tmp = g_malloc (3 + strlen (dirname) + NLENGTH (entry));
142 struct stat tempstat;
144 strcpy (tmp, dirname);
145 strcat (tmp, PATH_SEP_STR);
146 strcat (tmp, entry->d_name);
147 canonicalize_pathname (tmp);
148 /* Unix version */
149 if (!mc_stat (tmp, &tempstat)){
150 uid_t my_uid = getuid ();
151 gid_t my_gid = getgid ();
153 if (!S_ISDIR (tempstat.st_mode)){
154 isdir = 0;
155 if ((!my_uid && (tempstat.st_mode & 0111)) ||
156 (my_uid == tempstat.st_uid && (tempstat.st_mode & 0100)) ||
157 (my_gid == tempstat.st_gid && (tempstat.st_mode & 0010)) ||
158 (tempstat.st_mode & 0001))
159 isexec = 1;
162 g_free (tmp);
164 if ((flags & INPUT_COMPLETE_COMMANDS)
165 && (isexec || isdir))
166 break;
167 if ((flags & INPUT_COMPLETE_CD)
168 && isdir)
169 break;
170 if (flags & (INPUT_COMPLETE_FILENAMES))
171 break;
172 continue;
175 if (!entry){
176 if (directory){
177 mc_closedir (directory);
178 directory = NULL;
180 g_free (dirname);
181 dirname = NULL;
182 g_free (filename);
183 filename = NULL;
184 g_free (users_dirname);
185 users_dirname = NULL;
186 return NULL;
187 } else {
188 char *temp;
190 if (users_dirname && (users_dirname[0] != '.' || users_dirname[1])){
191 int dirlen = strlen (users_dirname);
192 temp = g_malloc (3 + dirlen + NLENGTH (entry));
193 strcpy (temp, users_dirname);
194 /* We need a `/' at the end. */
195 if (users_dirname[dirlen - 1] != PATH_SEP){
196 temp[dirlen] = PATH_SEP;
197 temp[dirlen + 1] = 0;
199 strcat (temp, entry->d_name);
200 } else {
201 temp = g_malloc (2 + NLENGTH (entry));
202 strcpy (temp, entry->d_name);
204 if (isdir)
205 strcat (temp, PATH_SEP_STR);
207 if (temp && (flags & INPUT_COMPLETE_SHELL_ESC))
209 SHELL_ESCAPED_STR e_temp = mhl_shell_escape_dup(temp);
210 mhl_mem_free (temp);
211 temp = e_temp.s;
213 return temp;
217 /* We assume here that text[0] == '~' , if you want to call it in another way,
218 you have to change the code */
219 static char *
220 username_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
222 static struct passwd *entry;
223 static int userlen;
225 SHOW_C_CTX("username_completion_function");
227 if (text[0] == '\\' && text[1] == '~') text++;
228 if (!state){ /* Initialization stuff */
229 setpwent ();
230 userlen = strlen (text + 1);
232 while ((entry = getpwent ()) != NULL){
233 /* Null usernames should result in all users as possible completions. */
234 if (!userlen)
235 break;
236 else if (text[1] == entry->pw_name[0] &&
237 !strncmp (text + 1, entry->pw_name, userlen))
238 break;
241 if (!entry){
242 endpwent ();
243 return NULL;
244 } else {
245 char *temp = g_malloc (3 + strlen (entry->pw_name));
247 *temp = '~';
248 strcpy (temp + 1, entry->pw_name);
249 strcat (temp, PATH_SEP_STR);
250 return temp;
254 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
255 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
256 extern char **environ;
257 #endif
259 /* We assume text [0] == '$' and want to have a look at text [1], if it is
260 equal to '{', so that we should append '}' at the end */
261 static char *
262 variable_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
264 static char **env_p;
265 static int varlen, isbrace;
266 const char *p = NULL;
268 SHOW_C_CTX("variable_completion_function");
270 if (!state){ /* Initialization stuff */
271 isbrace = (text [1] == '{');
272 varlen = strlen (text + 1 + isbrace);
273 env_p = environ;
276 while (*env_p){
277 p = strchr (*env_p, '=');
278 if (p && p - *env_p >= varlen && !strncmp (text + 1 + isbrace, *env_p, varlen))
279 break;
280 env_p++;
283 if (!*env_p)
284 return NULL;
285 else {
286 char *temp = g_malloc (2 + 2 * isbrace + p - *env_p);
288 *temp = '$';
289 if (isbrace)
290 temp [1] = '{';
291 memcpy (temp + 1 + isbrace, *env_p, p - *env_p);
292 if (isbrace)
293 strcpy (temp + 2 + (p - *env_p), "}");
294 else
295 temp [1 + p - *env_p] = 0;
296 env_p++;
297 return temp;
301 #define whitespace(c) ((c) == ' ' || (c) == '\t')
302 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
304 static char **hosts = NULL;
305 static char **hosts_p = NULL;
306 static int hosts_alloclen = 0;
307 static void fetch_hosts (const char *filename)
309 FILE *file = fopen (filename, "r");
310 char buffer[256], *name;
311 register int i, start;
313 if (!file)
314 return;
316 while (fgets (buffer, 255, file) != NULL){
317 /* Skip to first character. */
318 for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++);
319 /* Ignore comments... */
320 if (buffer[i] == '#')
321 continue;
322 /* Handle $include. */
323 if (!strncmp (buffer + i, "$include ", 9)){
324 char *includefile = buffer + i + 9;
325 char *t;
327 /* Find start of filename. */
328 while (*includefile && whitespace (*includefile))
329 includefile++;
330 t = includefile;
332 /* Find end of filename. */
333 while (*t && !cr_whitespace (*t))
334 t++;
335 *t = '\0';
337 fetch_hosts (includefile);
338 continue;
341 /* Skip IP #s. */
342 while (buffer[i] && !cr_whitespace (buffer[i]))
343 i++;
345 /* Get the host names separated by white space. */
346 while (buffer[i] && buffer[i] != '#'){
347 while (buffer[i] && cr_whitespace (buffer[i]))
348 i++;
349 if (buffer[i] == '#')
350 continue;
351 for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++);
352 if (i - start == 0)
353 continue;
354 name = g_strndup (buffer + start, i - start);
356 char **host_p;
358 if (hosts_p - hosts >= hosts_alloclen){
359 int j = hosts_p - hosts;
361 hosts = g_realloc ((void *)hosts, ((hosts_alloclen += 30) + 1) * sizeof (char *));
362 hosts_p = hosts + j;
364 for (host_p = hosts; host_p < hosts_p; host_p++)
365 if (!strcmp (name, *host_p))
366 break; /* We do not want any duplicates */
367 if (host_p == hosts_p){
368 *(hosts_p++) = name;
369 *hosts_p = NULL;
370 } else
371 g_free (name);
375 fclose (file);
378 static char *
379 hostname_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
381 static char **host_p;
382 static int textstart, textlen;
384 SHOW_C_CTX("hostname_completion_function");
386 if (!state){ /* Initialization stuff */
387 const char *p;
389 if (hosts != NULL){
390 for (host_p = hosts; *host_p; host_p++)
391 g_free (*host_p);
392 g_free (hosts);
394 hosts = g_new (char *, (hosts_alloclen = 30) + 1);
395 *hosts = NULL;
396 hosts_p = hosts;
397 fetch_hosts ((p = getenv ("HOSTFILE")) ? p : "/etc/hosts");
398 host_p = hosts;
399 textstart = (*text == '@') ? 1 : 0;
400 textlen = strlen (text + textstart);
403 while (*host_p){
404 if (!textlen)
405 break; /* Match all of them */
406 else if (!strncmp (text + textstart, *host_p, textlen))
407 break;
408 host_p++;
411 if (!*host_p){
412 for (host_p = hosts; *host_p; host_p++)
413 g_free (*host_p);
414 g_free (hosts);
415 hosts = NULL;
416 return NULL;
417 } else {
418 char *temp = g_malloc (2 + strlen (*host_p));
420 if (textstart)
421 *temp = '@';
422 strcpy (temp + textstart, *host_p);
423 host_p++;
424 return temp;
429 * This is the function to call when the word to complete is in a position
430 * where a command word can be found. It looks around $PATH, looking for
431 * commands that match. It also scans aliases, function names, and the
432 * table of shell built-ins.
434 static char *
435 command_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
437 static const char *path_end;
438 static int isabsolute;
439 static int phase;
440 static int text_len;
441 static const char *const *words;
442 static char *path;
443 static char *cur_path;
444 static char *cur_word;
445 static int init_state;
446 static const char *const bash_reserved[] = {
447 "if", "then", "else", "elif", "fi", "case", "esac", "for",
448 "select", "while", "until", "do", "done", "in", "function", 0
450 static const char *const bash_builtins[] = {
451 "alias", "bg", "bind", "break", "builtin", "cd", "command",
452 "continue", "declare", "dirs", "echo", "enable", "eval",
453 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
454 "help", "history", "jobs", "kill", "let", "local", "logout",
455 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
456 "shift", "source", "suspend", "test", "times", "trap", "type",
457 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
459 char *p, *found;
461 SHOW_C_CTX("command_completion_function");
463 if (!(flags & INPUT_COMPLETE_COMMANDS))
464 return 0;
466 text = mhl_shell_unescape_buf(text);
467 flags &= ~INPUT_COMPLETE_SHELL_ESC;
469 if (!state) { /* Initialize us a little bit */
470 isabsolute = strchr (text, PATH_SEP) != 0;
471 if (!isabsolute) {
472 words = bash_reserved;
473 phase = 0;
474 text_len = strlen (text);
475 if (!path && (path = g_strdup (getenv ("PATH"))) != NULL) {
476 p = path;
477 path_end = strchr (p, 0);
478 while ((p = strchr (p, PATH_ENV_SEP))) {
479 *p++ = 0;
485 if (isabsolute) {
486 p = filename_completion_function (text, state, flags);
487 if (!p)
488 return 0;
489 SHELL_ESCAPED_STR e_p = mhl_shell_escape_dup(p);
490 mhl_mem_free(p);
491 return e_p.s;
494 found = NULL;
495 switch (phase) {
496 case 0: /* Reserved words */
497 while (*words) {
498 if (!strncmp (*words, text, text_len))
499 return g_strdup (*(words++));
500 words++;
502 phase++;
503 words = bash_builtins;
504 case 1: /* Builtin commands */
505 while (*words) {
506 if (!strncmp (*words, text, text_len))
507 return g_strdup (*(words++));
508 words++;
510 phase++;
511 if (!path)
512 break;
513 cur_path = path;
514 cur_word = NULL;
515 case 2: /* And looking through the $PATH */
516 while (!found) {
517 if (!cur_word) {
518 char *expanded;
520 if (cur_path >= path_end)
521 break;
522 expanded = tilde_expand (*cur_path ? cur_path : ".");
523 cur_word = mhl_str_dir_plus_file (expanded, text);
524 g_free (expanded);
525 canonicalize_pathname (cur_word);
526 cur_path = strchr (cur_path, 0) + 1;
527 init_state = state;
529 found =
530 filename_completion_function (cur_word,
531 state - init_state, flags);
532 if (!found) {
533 g_free (cur_word);
534 cur_word = NULL;
539 if (!found) {
540 g_free (path);
541 path = NULL;
542 return NULL;
544 if ((p = strrchr (found, PATH_SEP)) != NULL) {
545 p++;
547 SHELL_ESCAPED_STR e_p = mhl_shell_escape_dup(p);
548 mhl_mem_free(found);
549 return e_p.s;
551 return found;
555 static int
556 match_compare (const void *a, const void *b)
558 return strcmp (*(char **)a, *(char **)b);
561 /* Returns an array of char * matches with the longest common denominator
562 in the 1st entry. Then a NULL terminated list of different possible
563 completions follows.
564 You have to supply your own CompletionFunction with the word you
565 want to complete as the first argument and an count of previous matches
566 as the second.
567 In case no matches were found we return NULL. */
568 static char **
569 completion_matches (char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
571 /* Number of slots in match_list. */
572 int match_list_size;
574 /* The list of matches. */
575 char **match_list = g_new (char *, (match_list_size = 30) + 1);
577 /* Number of matches actually found. */
578 int matches = 0;
580 /* Temporary string binder. */
581 char *string;
583 match_list[1] = NULL;
585 while ((string = (*entry_function) (text, matches, flags)) != NULL){
586 if (matches + 1 == match_list_size)
587 match_list = (char **) g_realloc (match_list, ((match_list_size += 30) + 1) * sizeof (char *));
588 match_list[++matches] = string;
589 match_list[matches + 1] = NULL;
592 /* If there were any matches, then look through them finding out the
593 lowest common denominator. That then becomes match_list[0]. */
594 if (matches)
596 register int i = 1;
597 int low = 4096; /* Count of max-matched characters. */
599 /* If only one match, just use that. */
600 if (matches == 1){
601 match_list[0] = match_list[1];
602 match_list[1] = NULL;
603 } else {
604 int j;
606 qsort (match_list + 1, matches, sizeof (char *), match_compare);
608 /* And compare each member of the list with
609 the next, finding out where they stop matching.
610 If we find two equal strings, we have to put one away... */
612 j = i + 1;
613 while (j < matches + 1)
615 register int c1, c2, si;
617 for (si = 0;(c1 = match_list [i][si]) && (c2 = match_list [j][si]); si++)
618 if (c1 != c2) break;
620 if (!c1 && !match_list [j][si]){ /* Two equal strings */
621 g_free (match_list [j]);
622 j++;
623 if (j > matches)
624 break;
625 continue; /* Look for a run of equal strings */
626 } else
627 if (low > si) low = si;
628 if (i + 1 != j) /* So there's some gap */
629 match_list [i + 1] = match_list [j];
630 i++; j++;
632 matches = i;
633 match_list [matches + 1] = NULL;
634 match_list[0] = g_strndup(match_list[1], low);
636 } else { /* There were no matches. */
637 g_free (match_list);
638 match_list = NULL;
640 return match_list;
643 /* Check if directory completion is needed */
644 static int
645 check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
647 const char *p, *q;
649 SHOW_C_CTX("check_is_cd");
650 if (!(flags & INPUT_COMPLETE_CD))
651 return 0;
653 /* Skip initial spaces */
654 p = text;
655 q = text + start;
656 while (p < q && *p && isspace ((unsigned char) *p))
657 p++;
659 /* Check if the command is "cd" and the cursor is after it */
660 if (p[0] == 'c' && p[1] == 'd' && isspace ((unsigned char) p[2])
661 && (p + 2 < q))
662 return 1;
664 return 0;
667 /* Returns an array of matches, or NULL if none. */
668 static char **
669 try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
671 int in_command_position = 0;
672 char *word, c;
673 char **matches = NULL;
674 const char *command_separator_chars = ";|&{(`";
675 char *p = NULL, *q = NULL, *r = NULL;
676 int is_cd = check_is_cd (text, *start, flags);
678 SHOW_C_CTX("try_complete");
680 c = text [*end];
681 text [*end] = 0;
682 word = g_strdup (text + *start);
683 text [*end] = c;
685 /* Determine if this could be a command word. It is if it appears at
686 the start of the line (ignoring preceding whitespace), or if it
687 appears after a character that separates commands. And we have to
688 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
689 if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){
690 int i = *start - 1;
691 for (i = *start - 1; i > -1; i--) {
692 if (text[i] == ' ' || text[i] == '\t'){
693 if (i == 0 ) continue;
694 if (text[i-1] != '\\') {
695 i--;
696 break;
700 if (i < 0)
701 in_command_position++;
702 else if (strchr (command_separator_chars, text[i])){
703 register int this_char, prev_char;
705 in_command_position++;
707 if (i){
708 /* Handle the two character tokens `>&', `<&', and `>|'.
709 We are not in a command position after one of these. */
710 this_char = text[i];
711 prev_char = text[i - 1];
713 if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
714 (this_char == '|' && prev_char == '>'))
715 in_command_position = 0;
716 else if (i > 0 && text [i-1] == '\\') /* Quoted */
717 in_command_position = 0;
722 if (flags & INPUT_COMPLETE_COMMANDS)
723 p = strrchr (word, '`');
724 if (flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES))
725 q = strrchr (word, '$');
726 if (flags & INPUT_COMPLETE_HOSTNAMES)
727 r = strrchr (word, '@');
728 if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){
729 if (q > p)
730 p = q + 1;
731 q = NULL;
734 /* Command substitution? */
735 if (p > q && p > r){
736 SHOW_C_CTX("try_complete:cmd_backq_subst");
737 matches = completion_matches (p + 1, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
738 if (matches)
739 *start += p + 1 - word;
742 /* Variable name? */
743 else if (q > p && q > r){
744 SHOW_C_CTX("try_complete:var_subst");
745 matches = completion_matches (q, variable_completion_function, flags);
746 if (matches)
747 *start += q - word;
750 /* Starts with '@', then look through the known hostnames for
751 completion first. */
752 else if (r > p && r > q){
753 SHOW_C_CTX("try_complete:host_subst");
754 matches = completion_matches (r, hostname_completion_function, flags);
755 if (matches)
756 *start += r - word;
759 /* Starts with `~' and there is no slash in the word, then
760 try completing this word as a username. */
761 if (!matches && *word == '~' && (flags & INPUT_COMPLETE_USERNAMES) && !strchr (word, PATH_SEP))
763 SHOW_C_CTX("try_complete:user_subst");
764 matches = completion_matches (word, username_completion_function, flags);
768 /* And finally if this word is in a command position, then
769 complete over possible command names, including aliases, functions,
770 and command names. */
771 if (!matches && in_command_position)
773 SHOW_C_CTX("try_complete:cmd_subst");
774 matches = completion_matches (word, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
777 else if (!matches && (flags & INPUT_COMPLETE_FILENAMES)){
778 if (is_cd)
779 flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
780 SHOW_C_CTX("try_complete:filename_subst_1");
781 matches = completion_matches (word, filename_completion_function, flags);
782 if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
783 char *p, *q = text + *start;
785 for (p = text; *p && p < q; p++){
786 if (*p == ' ' || *p == '\t') {
787 if (p == text) continue;
788 if (*(p-1) == '\\') {
789 p--;
790 break;
794 if (!strncmp (p, "cd", 2))
795 for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); p++){
796 if (p == text) continue;
797 if (*(p-1) == '\\') {
798 p--;
799 break;
802 if (p == q){
803 char * const cdpath_ref = g_strdup (getenv ("CDPATH"));
804 char *cdpath = cdpath_ref;
805 char c, *s, *r;
807 if (cdpath == NULL)
808 c = 0;
809 else
810 c = ':';
811 while (!matches && c == ':'){
812 s = strchr (cdpath, ':');
813 if (s == NULL)
814 s = strchr (cdpath, 0);
815 c = *s;
816 *s = 0;
817 if (*cdpath){
818 r = mhl_str_dir_plus_file (cdpath, word);
819 SHOW_C_CTX("try_complete:filename_subst_2");
820 matches = completion_matches (r, filename_completion_function, flags);
821 g_free (r);
823 *s = c;
824 cdpath = s + 1;
826 g_free (cdpath_ref);
831 g_free (word);
833 return matches;
836 void free_completions (WInput *in)
838 char **p;
840 if (!in->completions)
841 return;
842 for (p=in->completions; *p; p++)
843 g_free (*p);
844 g_free (in->completions);
845 in->completions = NULL;
848 static int query_height, query_width;
849 static WInput *input;
850 static int min_end;
851 static int start, end;
853 static int insert_text (WInput *in, char *text, ssize_t len)
855 len = min (len, (ssize_t) strlen (text)) + start - end;
856 if (strlen (in->buffer) + len >= (size_t) in->current_max_len){
857 /* Expand the buffer */
858 char *narea = g_realloc (in->buffer, in->current_max_len + len + in->field_len);
859 if (narea){
860 in->buffer = narea;
861 in->current_max_len += len + in->field_len;
864 if (strlen (in->buffer)+1 < (size_t) in->current_max_len){
865 if (len > 0){
866 int i = strlen (&in->buffer [end]);
867 for (; i >= 0; i--)
868 in->buffer [end + len + i] = in->buffer [end + i];
869 } else if (len < 0){
870 char *p = in->buffer + end + len, *q = in->buffer + end;
871 while (*q)
872 *(p++) = *(q++);
873 *p = 0;
875 memcpy (in->buffer + start, text, len - start + end);
876 in->point += len;
877 update_input (in, 1);
878 end += len;
880 return len != 0;
883 static cb_ret_t
884 query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
886 switch (msg) {
887 case DLG_KEY:
888 switch (parm) {
889 case KEY_LEFT:
890 case KEY_RIGHT:
891 h->ret_value = 0;
892 dlg_stop (h);
893 return MSG_HANDLED;
895 case KEY_BACKSPACE:
896 if (end == min_end) {
897 h->ret_value = 0;
898 dlg_stop (h);
899 return MSG_HANDLED;
900 } else {
901 WLEntry *e, *e1;
903 e1 = e = ((WListbox *) (h->current))->list;
904 do {
905 if (!strncmp
906 (input->buffer + start, e1->text,
907 end - start - 1)) {
908 listbox_select_entry ((WListbox *) (h->current),
909 e1);
910 handle_char (input, parm);
911 end--;
912 send_message (h->current, WIDGET_DRAW, 0);
913 break;
915 e1 = e1->next;
916 } while (e != e1);
918 return MSG_HANDLED;
920 default:
921 if (parm > 0xff || !is_printable (parm)) {
922 if (is_in_input_map (input, parm) == 2) {
923 if (end == min_end)
924 return MSG_HANDLED;
925 h->ret_value = B_USER; /* This means we want to refill the
926 list box and start again */
927 dlg_stop (h);
928 return MSG_HANDLED;
929 } else
930 return MSG_NOT_HANDLED;
931 } else {
932 WLEntry *e, *e1;
933 int need_redraw = 0;
934 int low = 4096;
935 char *last_text = NULL;
937 e1 = e = ((WListbox *) (h->current))->list;
938 do {
939 if (!strncmp
940 (input->buffer + start, e1->text, end - start)) {
941 if (e1->text[end - start] == parm) {
942 if (need_redraw) {
943 register int c1, c2, si;
945 for (si = end - start + 1;
946 (c1 = last_text[si])
947 && (c2 = e1->text[si]); si++)
948 if (c1 != c2)
949 break;
950 if (low > si)
951 low = si;
952 last_text = e1->text;
953 need_redraw = 2;
954 } else {
955 need_redraw = 1;
956 listbox_select_entry ((WListbox *) (h->
957 current),
958 e1);
959 last_text = e1->text;
963 e1 = e1->next;
964 } while (e != e1);
965 if (need_redraw == 2) {
966 insert_text (input, last_text, low);
967 send_message (h->current, WIDGET_DRAW, 0);
968 } else if (need_redraw == 1) {
969 h->ret_value = B_ENTER;
970 dlg_stop (h);
973 return MSG_HANDLED;
975 break;
977 default:
978 return default_dlg_callback (h, msg, parm);
982 #define DO_INSERTION 1
983 #define DO_QUERY 2
984 /* Returns 1 if the user would like to see us again */
985 static int
986 complete_engine (WInput *in, int what_to_do)
988 if (in->completions && in->point != end)
989 free_completions (in);
990 if (!in->completions){
991 end = in->point;
992 for (start = end ? end - 1 : 0; start > -1; start--)
993 if (strchr (" \t;|<>", in->buffer [start])){
994 if (start > 0 && in->buffer [start-1] == '\\')
995 continue;
996 else
997 break;
999 if (start < end)
1000 start++;
1001 in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
1004 if (in->completions){
1005 if (what_to_do & DO_INSERTION || ((what_to_do & DO_QUERY) && !in->completions[1])) {
1006 char * complete = in->completions [0];
1007 if (insert_text (in, complete, strlen (complete))){
1008 if (in->completions [1])
1009 beep ();
1010 else
1011 free_completions (in);
1012 } else
1013 beep ();
1015 if ((what_to_do & DO_QUERY) && in->completions && in->completions [1]) {
1016 int maxlen = 0, i, count = 0;
1017 int x, y, w, h;
1018 int start_x, start_y;
1019 char **p, *q;
1020 Dlg_head *query_dlg;
1021 WListbox *query_list;
1023 for (p=in->completions + 1; *p; count++, p++) {
1024 if ((i = strlen (*p)) > maxlen)
1025 maxlen = i;
1027 start_x = in->widget.x;
1028 start_y = in->widget.y;
1029 if (start_y - 2 >= count) {
1030 y = start_y - 2 - count;
1031 h = 2 + count;
1032 } else {
1033 if (start_y >= LINES - start_y - 1) {
1034 y = 0;
1035 h = start_y;
1036 } else {
1037 y = start_y + 1;
1038 h = LINES - start_y - 1;
1041 x = start - in->first_shown - 2 + start_x;
1042 w = maxlen + 4;
1043 if (x + w > COLS)
1044 x = COLS - w;
1045 if (x < 0)
1046 x = 0;
1047 if (x + w > COLS)
1048 w = COLS;
1049 input = in;
1050 min_end = end;
1051 query_height = h;
1052 query_width = w;
1053 query_dlg = create_dlg (y, x, query_height, query_width,
1054 dialog_colors, query_callback,
1055 "[Completion]", NULL, DLG_COMPACT);
1056 query_list = listbox_new (1, 1, w - 2, h - 2, NULL);
1057 add_widget (query_dlg, query_list);
1058 for (p = in->completions + 1; *p; p++)
1059 listbox_add_item (query_list, 0, 0, *p, NULL);
1060 run_dlg (query_dlg);
1061 q = NULL;
1062 if (query_dlg->ret_value == B_ENTER){
1063 listbox_get_current (query_list, &q, NULL);
1064 if (q)
1065 insert_text (in, q, strlen (q));
1067 if (q || end != min_end)
1068 free_completions (in);
1069 i = query_dlg->ret_value; /* B_USER if user wants to start over again */
1070 destroy_dlg (query_dlg);
1071 if (i == B_USER)
1072 return 1;
1074 } else
1075 beep ();
1076 return 0;
1079 void complete (WInput *in)
1081 int engine_flags;
1083 if (in->completions)
1084 engine_flags = DO_QUERY;
1085 else
1087 engine_flags = DO_INSERTION;
1089 if (show_all_if_ambiguous)
1090 engine_flags |= DO_QUERY;
1093 while (complete_engine (in, engine_flags));