Removed type SHELL_ESCAPED_STR in favour of plain char*
[midnight-commander.git] / src / complete.c
blob6a7b19d9176f6d72305d4e4e2e4c231a27c31d95
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 temp = mhl_shell_escape_dup(temp);
211 return temp;
215 /* We assume here that text[0] == '~' , if you want to call it in another way,
216 you have to change the code */
217 static char *
218 username_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
220 static struct passwd *entry;
221 static int userlen;
223 SHOW_C_CTX("username_completion_function");
225 if (text[0] == '\\' && text[1] == '~') text++;
226 if (!state){ /* Initialization stuff */
227 setpwent ();
228 userlen = strlen (text + 1);
230 while ((entry = getpwent ()) != NULL){
231 /* Null usernames should result in all users as possible completions. */
232 if (!userlen)
233 break;
234 else if (text[1] == entry->pw_name[0] &&
235 !strncmp (text + 1, entry->pw_name, userlen))
236 break;
239 if (!entry){
240 endpwent ();
241 return NULL;
242 } else {
243 char *temp = g_malloc (3 + strlen (entry->pw_name));
245 *temp = '~';
246 strcpy (temp + 1, entry->pw_name);
247 strcat (temp, PATH_SEP_STR);
248 return temp;
252 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
253 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
254 extern char **environ;
255 #endif
257 /* We assume text [0] == '$' and want to have a look at text [1], if it is
258 equal to '{', so that we should append '}' at the end */
259 static char *
260 variable_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
262 static char **env_p;
263 static int varlen, isbrace;
264 const char *p = NULL;
266 SHOW_C_CTX("variable_completion_function");
268 if (!state){ /* Initialization stuff */
269 isbrace = (text [1] == '{');
270 varlen = strlen (text + 1 + isbrace);
271 env_p = environ;
274 while (*env_p){
275 p = strchr (*env_p, '=');
276 if (p && p - *env_p >= varlen && !strncmp (text + 1 + isbrace, *env_p, varlen))
277 break;
278 env_p++;
281 if (!*env_p)
282 return NULL;
283 else {
284 char *temp = g_malloc (2 + 2 * isbrace + p - *env_p);
286 *temp = '$';
287 if (isbrace)
288 temp [1] = '{';
289 memcpy (temp + 1 + isbrace, *env_p, p - *env_p);
290 if (isbrace)
291 strcpy (temp + 2 + (p - *env_p), "}");
292 else
293 temp [1 + p - *env_p] = 0;
294 env_p++;
295 return temp;
299 #define whitespace(c) ((c) == ' ' || (c) == '\t')
300 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
302 static char **hosts = NULL;
303 static char **hosts_p = NULL;
304 static int hosts_alloclen = 0;
305 static void fetch_hosts (const char *filename)
307 FILE *file = fopen (filename, "r");
308 char buffer[256], *name;
309 register int i, start;
311 if (!file)
312 return;
314 while (fgets (buffer, 255, file) != NULL){
315 /* Skip to first character. */
316 for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++);
317 /* Ignore comments... */
318 if (buffer[i] == '#')
319 continue;
320 /* Handle $include. */
321 if (!strncmp (buffer + i, "$include ", 9)){
322 char *includefile = buffer + i + 9;
323 char *t;
325 /* Find start of filename. */
326 while (*includefile && whitespace (*includefile))
327 includefile++;
328 t = includefile;
330 /* Find end of filename. */
331 while (*t && !cr_whitespace (*t))
332 t++;
333 *t = '\0';
335 fetch_hosts (includefile);
336 continue;
339 /* Skip IP #s. */
340 while (buffer[i] && !cr_whitespace (buffer[i]))
341 i++;
343 /* Get the host names separated by white space. */
344 while (buffer[i] && buffer[i] != '#'){
345 while (buffer[i] && cr_whitespace (buffer[i]))
346 i++;
347 if (buffer[i] == '#')
348 continue;
349 for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++);
350 if (i - start == 0)
351 continue;
352 name = g_strndup (buffer + start, i - start);
354 char **host_p;
356 if (hosts_p - hosts >= hosts_alloclen){
357 int j = hosts_p - hosts;
359 hosts = g_realloc ((void *)hosts, ((hosts_alloclen += 30) + 1) * sizeof (char *));
360 hosts_p = hosts + j;
362 for (host_p = hosts; host_p < hosts_p; host_p++)
363 if (!strcmp (name, *host_p))
364 break; /* We do not want any duplicates */
365 if (host_p == hosts_p){
366 *(hosts_p++) = name;
367 *hosts_p = NULL;
368 } else
369 g_free (name);
373 fclose (file);
376 static char *
377 hostname_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
379 static char **host_p;
380 static int textstart, textlen;
382 SHOW_C_CTX("hostname_completion_function");
384 if (!state){ /* Initialization stuff */
385 const char *p;
387 if (hosts != NULL){
388 for (host_p = hosts; *host_p; host_p++)
389 g_free (*host_p);
390 g_free (hosts);
392 hosts = g_new (char *, (hosts_alloclen = 30) + 1);
393 *hosts = NULL;
394 hosts_p = hosts;
395 fetch_hosts ((p = getenv ("HOSTFILE")) ? p : "/etc/hosts");
396 host_p = hosts;
397 textstart = (*text == '@') ? 1 : 0;
398 textlen = strlen (text + textstart);
401 while (*host_p){
402 if (!textlen)
403 break; /* Match all of them */
404 else if (!strncmp (text + textstart, *host_p, textlen))
405 break;
406 host_p++;
409 if (!*host_p){
410 for (host_p = hosts; *host_p; host_p++)
411 g_free (*host_p);
412 g_free (hosts);
413 hosts = NULL;
414 return NULL;
415 } else {
416 char *temp = g_malloc (2 + strlen (*host_p));
418 if (textstart)
419 *temp = '@';
420 strcpy (temp + textstart, *host_p);
421 host_p++;
422 return temp;
427 * This is the function to call when the word to complete is in a position
428 * where a command word can be found. It looks around $PATH, looking for
429 * commands that match. It also scans aliases, function names, and the
430 * table of shell built-ins.
432 static char *
433 command_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
435 static const char *path_end;
436 static int isabsolute;
437 static int phase;
438 static int text_len;
439 static const char *const *words;
440 static char *path;
441 static char *cur_path;
442 static char *cur_word;
443 static int init_state;
444 static const char *const bash_reserved[] = {
445 "if", "then", "else", "elif", "fi", "case", "esac", "for",
446 "select", "while", "until", "do", "done", "in", "function", 0
448 static const char *const bash_builtins[] = {
449 "alias", "bg", "bind", "break", "builtin", "cd", "command",
450 "continue", "declare", "dirs", "echo", "enable", "eval",
451 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
452 "help", "history", "jobs", "kill", "let", "local", "logout",
453 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
454 "shift", "source", "suspend", "test", "times", "trap", "type",
455 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
457 char *p, *found;
459 SHOW_C_CTX("command_completion_function");
461 if (!(flags & INPUT_COMPLETE_COMMANDS))
462 return 0;
464 text = mhl_shell_unescape_buf(text);
465 flags &= ~INPUT_COMPLETE_SHELL_ESC;
467 if (!state) { /* Initialize us a little bit */
468 isabsolute = strchr (text, PATH_SEP) != 0;
469 if (!isabsolute) {
470 words = bash_reserved;
471 phase = 0;
472 text_len = strlen (text);
473 if (!path && (path = g_strdup (getenv ("PATH"))) != NULL) {
474 p = path;
475 path_end = strchr (p, 0);
476 while ((p = strchr (p, PATH_ENV_SEP))) {
477 *p++ = 0;
483 if (isabsolute) {
484 p = filename_completion_function (text, state, flags);
485 if (!p)
486 return 0;
487 p = mhl_shell_escape_dup(p);
488 return p;
491 found = NULL;
492 switch (phase) {
493 case 0: /* Reserved words */
494 while (*words) {
495 if (!strncmp (*words, text, text_len))
496 return g_strdup (*(words++));
497 words++;
499 phase++;
500 words = bash_builtins;
501 case 1: /* Builtin commands */
502 while (*words) {
503 if (!strncmp (*words, text, text_len))
504 return g_strdup (*(words++));
505 words++;
507 phase++;
508 if (!path)
509 break;
510 cur_path = path;
511 cur_word = NULL;
512 case 2: /* And looking through the $PATH */
513 while (!found) {
514 if (!cur_word) {
515 char *expanded;
517 if (cur_path >= path_end)
518 break;
519 expanded = tilde_expand (*cur_path ? cur_path : ".");
520 cur_word = mhl_str_dir_plus_file (expanded, text);
521 g_free (expanded);
522 canonicalize_pathname (cur_word);
523 cur_path = strchr (cur_path, 0) + 1;
524 init_state = state;
526 found =
527 filename_completion_function (cur_word,
528 state - init_state, flags);
529 if (!found) {
530 g_free (cur_word);
531 cur_word = NULL;
536 if (!found) {
537 g_free (path);
538 path = NULL;
539 return NULL;
541 if ((p = strrchr (found, PATH_SEP)) != NULL) {
542 p++;
543 p = mhl_shell_escape_dup(p);
544 g_free(found);
545 return p;
547 return found;
551 static int
552 match_compare (const void *a, const void *b)
554 return strcmp (*(char **)a, *(char **)b);
557 /* Returns an array of char * matches with the longest common denominator
558 in the 1st entry. Then a NULL terminated list of different possible
559 completions follows.
560 You have to supply your own CompletionFunction with the word you
561 want to complete as the first argument and an count of previous matches
562 as the second.
563 In case no matches were found we return NULL. */
564 static char **
565 completion_matches (char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
567 /* Number of slots in match_list. */
568 int match_list_size;
570 /* The list of matches. */
571 char **match_list = g_new (char *, (match_list_size = 30) + 1);
573 /* Number of matches actually found. */
574 int matches = 0;
576 /* Temporary string binder. */
577 char *string;
579 match_list[1] = NULL;
581 while ((string = (*entry_function) (text, matches, flags)) != NULL){
582 if (matches + 1 == match_list_size)
583 match_list = (char **) g_realloc (match_list, ((match_list_size += 30) + 1) * sizeof (char *));
584 match_list[++matches] = string;
585 match_list[matches + 1] = NULL;
588 /* If there were any matches, then look through them finding out the
589 lowest common denominator. That then becomes match_list[0]. */
590 if (matches)
592 register int i = 1;
593 int low = 4096; /* Count of max-matched characters. */
595 /* If only one match, just use that. */
596 if (matches == 1){
597 match_list[0] = match_list[1];
598 match_list[1] = NULL;
599 } else {
600 int j;
602 qsort (match_list + 1, matches, sizeof (char *), match_compare);
604 /* And compare each member of the list with
605 the next, finding out where they stop matching.
606 If we find two equal strings, we have to put one away... */
608 j = i + 1;
609 while (j < matches + 1)
611 register int c1, c2, si;
613 for (si = 0;(c1 = match_list [i][si]) && (c2 = match_list [j][si]); si++)
614 if (c1 != c2) break;
616 if (!c1 && !match_list [j][si]){ /* Two equal strings */
617 g_free (match_list [j]);
618 j++;
619 if (j > matches)
620 break;
621 continue; /* Look for a run of equal strings */
622 } else
623 if (low > si) low = si;
624 if (i + 1 != j) /* So there's some gap */
625 match_list [i + 1] = match_list [j];
626 i++; j++;
628 matches = i;
629 match_list [matches + 1] = NULL;
630 match_list[0] = g_strndup(match_list[1], low);
632 } else { /* There were no matches. */
633 g_free (match_list);
634 match_list = NULL;
636 return match_list;
639 /* Check if directory completion is needed */
640 static int
641 check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
643 const char *p, *q;
645 SHOW_C_CTX("check_is_cd");
646 if (!(flags & INPUT_COMPLETE_CD))
647 return 0;
649 /* Skip initial spaces */
650 p = text;
651 q = text + start;
652 while (p < q && *p && isspace ((unsigned char) *p))
653 p++;
655 /* Check if the command is "cd" and the cursor is after it */
656 if (p[0] == 'c' && p[1] == 'd' && isspace ((unsigned char) p[2])
657 && (p + 2 < q))
658 return 1;
660 return 0;
663 /* Returns an array of matches, or NULL if none. */
664 static char **
665 try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
667 int in_command_position = 0;
668 char *word, c;
669 char **matches = NULL;
670 const char *command_separator_chars = ";|&{(`";
671 char *p = NULL, *q = NULL, *r = NULL;
672 int is_cd = check_is_cd (text, *start, flags);
674 SHOW_C_CTX("try_complete");
676 c = text [*end];
677 text [*end] = 0;
678 word = g_strdup (text + *start);
679 text [*end] = c;
681 /* Determine if this could be a command word. It is if it appears at
682 the start of the line (ignoring preceding whitespace), or if it
683 appears after a character that separates commands. And we have to
684 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
685 if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){
686 int i = *start - 1;
687 for (i = *start - 1; i > -1; i--) {
688 if (text[i] == ' ' || text[i] == '\t'){
689 if (i == 0 ) continue;
690 if (text[i-1] != '\\') {
691 i--;
692 break;
696 if (i < 0)
697 in_command_position++;
698 else if (strchr (command_separator_chars, text[i])){
699 register int this_char, prev_char;
701 in_command_position++;
703 if (i){
704 /* Handle the two character tokens `>&', `<&', and `>|'.
705 We are not in a command position after one of these. */
706 this_char = text[i];
707 prev_char = text[i - 1];
709 if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
710 (this_char == '|' && prev_char == '>'))
711 in_command_position = 0;
712 else if (i > 0 && text [i-1] == '\\') /* Quoted */
713 in_command_position = 0;
718 if (flags & INPUT_COMPLETE_COMMANDS)
719 p = strrchr (word, '`');
720 if (flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES))
721 q = strrchr (word, '$');
722 if (flags & INPUT_COMPLETE_HOSTNAMES)
723 r = strrchr (word, '@');
724 if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){
725 if (q > p)
726 p = q + 1;
727 q = NULL;
730 /* Command substitution? */
731 if (p > q && p > r){
732 SHOW_C_CTX("try_complete:cmd_backq_subst");
733 matches = completion_matches (p + 1, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
734 if (matches)
735 *start += p + 1 - word;
738 /* Variable name? */
739 else if (q > p && q > r){
740 SHOW_C_CTX("try_complete:var_subst");
741 matches = completion_matches (q, variable_completion_function, flags);
742 if (matches)
743 *start += q - word;
746 /* Starts with '@', then look through the known hostnames for
747 completion first. */
748 else if (r > p && r > q){
749 SHOW_C_CTX("try_complete:host_subst");
750 matches = completion_matches (r, hostname_completion_function, flags);
751 if (matches)
752 *start += r - word;
755 /* Starts with `~' and there is no slash in the word, then
756 try completing this word as a username. */
757 if (!matches && *word == '~' && (flags & INPUT_COMPLETE_USERNAMES) && !strchr (word, PATH_SEP))
759 SHOW_C_CTX("try_complete:user_subst");
760 matches = completion_matches (word, username_completion_function, flags);
764 /* And finally if this word is in a command position, then
765 complete over possible command names, including aliases, functions,
766 and command names. */
767 if (!matches && in_command_position)
769 SHOW_C_CTX("try_complete:cmd_subst");
770 matches = completion_matches (word, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
773 else if (!matches && (flags & INPUT_COMPLETE_FILENAMES)){
774 if (is_cd)
775 flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
776 SHOW_C_CTX("try_complete:filename_subst_1");
777 matches = completion_matches (word, filename_completion_function, flags);
778 if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
779 char *p, *q = text + *start;
781 for (p = text; *p && p < q; p++){
782 if (*p == ' ' || *p == '\t') {
783 if (p == text) continue;
784 if (*(p-1) == '\\') {
785 p--;
786 break;
790 if (!strncmp (p, "cd", 2))
791 for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); p++){
792 if (p == text) continue;
793 if (*(p-1) == '\\') {
794 p--;
795 break;
798 if (p == q){
799 char * const cdpath_ref = g_strdup (getenv ("CDPATH"));
800 char *cdpath = cdpath_ref;
801 char c, *s, *r;
803 if (cdpath == NULL)
804 c = 0;
805 else
806 c = ':';
807 while (!matches && c == ':'){
808 s = strchr (cdpath, ':');
809 if (s == NULL)
810 s = strchr (cdpath, 0);
811 c = *s;
812 *s = 0;
813 if (*cdpath){
814 r = mhl_str_dir_plus_file (cdpath, word);
815 SHOW_C_CTX("try_complete:filename_subst_2");
816 matches = completion_matches (r, filename_completion_function, flags);
817 g_free (r);
819 *s = c;
820 cdpath = s + 1;
822 g_free (cdpath_ref);
827 g_free (word);
829 return matches;
832 void free_completions (WInput *in)
834 char **p;
836 if (!in->completions)
837 return;
838 for (p=in->completions; *p; p++)
839 g_free (*p);
840 g_free (in->completions);
841 in->completions = NULL;
844 static int query_height, query_width;
845 static WInput *input;
846 static int min_end;
847 static int start, end;
849 static int insert_text (WInput *in, char *text, ssize_t len)
851 len = min (len, (ssize_t) strlen (text)) + start - end;
852 if (strlen (in->buffer) + len >= (size_t) in->current_max_len){
853 /* Expand the buffer */
854 char *narea = g_realloc (in->buffer, in->current_max_len + len + in->field_len);
855 if (narea){
856 in->buffer = narea;
857 in->current_max_len += len + in->field_len;
860 if (strlen (in->buffer)+1 < (size_t) in->current_max_len){
861 if (len > 0){
862 int i = strlen (&in->buffer [end]);
863 for (; i >= 0; i--)
864 in->buffer [end + len + i] = in->buffer [end + i];
865 } else if (len < 0){
866 char *p = in->buffer + end + len, *q = in->buffer + end;
867 while (*q)
868 *(p++) = *(q++);
869 *p = 0;
871 memcpy (in->buffer + start, text, len - start + end);
872 in->point += len;
873 update_input (in, 1);
874 end += len;
876 return len != 0;
879 static cb_ret_t
880 query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
882 switch (msg) {
883 case DLG_KEY:
884 switch (parm) {
885 case KEY_LEFT:
886 case KEY_RIGHT:
887 h->ret_value = 0;
888 dlg_stop (h);
889 return MSG_HANDLED;
891 case KEY_BACKSPACE:
892 if (end == min_end) {
893 h->ret_value = 0;
894 dlg_stop (h);
895 return MSG_HANDLED;
896 } else {
897 WLEntry *e, *e1;
899 e1 = e = ((WListbox *) (h->current))->list;
900 do {
901 if (!strncmp
902 (input->buffer + start, e1->text,
903 end - start - 1)) {
904 listbox_select_entry ((WListbox *) (h->current),
905 e1);
906 handle_char (input, parm);
907 end--;
908 send_message (h->current, WIDGET_DRAW, 0);
909 break;
911 e1 = e1->next;
912 } while (e != e1);
914 return MSG_HANDLED;
916 default:
917 if (parm > 0xff || !is_printable (parm)) {
918 if (is_in_input_map (input, parm) == 2) {
919 if (end == min_end)
920 return MSG_HANDLED;
921 h->ret_value = B_USER; /* This means we want to refill the
922 list box and start again */
923 dlg_stop (h);
924 return MSG_HANDLED;
925 } else
926 return MSG_NOT_HANDLED;
927 } else {
928 WLEntry *e, *e1;
929 int need_redraw = 0;
930 int low = 4096;
931 char *last_text = NULL;
933 e1 = e = ((WListbox *) (h->current))->list;
934 do {
935 if (!strncmp
936 (input->buffer + start, e1->text, end - start)) {
937 if (e1->text[end - start] == parm) {
938 if (need_redraw) {
939 register int c1, c2, si;
941 for (si = end - start + 1;
942 (c1 = last_text[si])
943 && (c2 = e1->text[si]); si++)
944 if (c1 != c2)
945 break;
946 if (low > si)
947 low = si;
948 last_text = e1->text;
949 need_redraw = 2;
950 } else {
951 need_redraw = 1;
952 listbox_select_entry ((WListbox *) (h->
953 current),
954 e1);
955 last_text = e1->text;
959 e1 = e1->next;
960 } while (e != e1);
961 if (need_redraw == 2) {
962 insert_text (input, last_text, low);
963 send_message (h->current, WIDGET_DRAW, 0);
964 } else if (need_redraw == 1) {
965 h->ret_value = B_ENTER;
966 dlg_stop (h);
969 return MSG_HANDLED;
971 break;
973 default:
974 return default_dlg_callback (h, msg, parm);
978 #define DO_INSERTION 1
979 #define DO_QUERY 2
980 /* Returns 1 if the user would like to see us again */
981 static int
982 complete_engine (WInput *in, int what_to_do)
984 if (in->completions && in->point != end)
985 free_completions (in);
986 if (!in->completions){
987 end = in->point;
988 for (start = end ? end - 1 : 0; start > -1; start--)
989 if (strchr (" \t;|<>", in->buffer [start])){
990 if (start > 0 && in->buffer [start-1] == '\\')
991 continue;
992 else
993 break;
995 if (start < end)
996 start++;
997 in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
1000 if (in->completions){
1001 if (what_to_do & DO_INSERTION || ((what_to_do & DO_QUERY) && !in->completions[1])) {
1002 char * complete = in->completions [0];
1003 if (insert_text (in, complete, strlen (complete))){
1004 if (in->completions [1])
1005 beep ();
1006 else
1007 free_completions (in);
1008 } else
1009 beep ();
1011 if ((what_to_do & DO_QUERY) && in->completions && in->completions [1]) {
1012 int maxlen = 0, i, count = 0;
1013 int x, y, w, h;
1014 int start_x, start_y;
1015 char **p, *q;
1016 Dlg_head *query_dlg;
1017 WListbox *query_list;
1019 for (p=in->completions + 1; *p; count++, p++) {
1020 if ((i = strlen (*p)) > maxlen)
1021 maxlen = i;
1023 start_x = in->widget.x;
1024 start_y = in->widget.y;
1025 if (start_y - 2 >= count) {
1026 y = start_y - 2 - count;
1027 h = 2 + count;
1028 } else {
1029 if (start_y >= LINES - start_y - 1) {
1030 y = 0;
1031 h = start_y;
1032 } else {
1033 y = start_y + 1;
1034 h = LINES - start_y - 1;
1037 x = start - in->first_shown - 2 + start_x;
1038 w = maxlen + 4;
1039 if (x + w > COLS)
1040 x = COLS - w;
1041 if (x < 0)
1042 x = 0;
1043 if (x + w > COLS)
1044 w = COLS;
1045 input = in;
1046 min_end = end;
1047 query_height = h;
1048 query_width = w;
1049 query_dlg = create_dlg (y, x, query_height, query_width,
1050 dialog_colors, query_callback,
1051 "[Completion]", NULL, DLG_COMPACT);
1052 query_list = listbox_new (1, 1, w - 2, h - 2, NULL);
1053 add_widget (query_dlg, query_list);
1054 for (p = in->completions + 1; *p; p++)
1055 listbox_add_item (query_list, 0, 0, *p, NULL);
1056 run_dlg (query_dlg);
1057 q = NULL;
1058 if (query_dlg->ret_value == B_ENTER){
1059 listbox_get_current (query_list, &q, NULL);
1060 if (q)
1061 insert_text (in, q, strlen (q));
1063 if (q || end != min_end)
1064 free_completions (in);
1065 i = query_dlg->ret_value; /* B_USER if user wants to start over again */
1066 destroy_dlg (query_dlg);
1067 if (i == B_USER)
1068 return 1;
1070 } else
1071 beep ();
1072 return 0;
1075 void complete (WInput *in)
1077 int engine_flags;
1079 if (in->completions)
1080 engine_flags = DO_QUERY;
1081 else
1083 engine_flags = DO_INSERTION;
1085 if (show_all_if_ambiguous)
1086 engine_flags |= DO_QUERY;
1089 while (complete_engine (in, engine_flags));