Revert some functions (mhl_mem_free to g_free, etc)
[midnight-commander.git] / src / complete.c
blobee1b8ef9fd973d216bfd2c09a2296702ee73ed43
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>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
34 #include "global.h"
35 #include "tty.h"
36 #include "win.h"
37 #include "color.h"
38 #include "dialog.h"
39 #include "widget.h"
40 #include "wtools.h"
41 #include "main.h"
42 #include "util.h"
43 #include "key.h" /* XCTRL and ALT macros */
45 typedef char *CompletionFunction (char * text, int state, INPUT_COMPLETE_FLAGS flags);
47 //#define DO_COMPLETION_DEBUG
48 #ifdef DO_COMPLETION_DEBUG
50 * Useful to print/debug completion flags
52 static const char * show_c_flags(INPUT_COMPLETE_FLAGS flags)
54 static char s_cf[] = "FHCVUDS";
56 s_cf[0] = (flags & INPUT_COMPLETE_FILENAMES) ? 'F' : ' ';
57 s_cf[1] = (flags & INPUT_COMPLETE_HOSTNAMES) ? 'H' : ' ';
58 s_cf[2] = (flags & INPUT_COMPLETE_COMMANDS) ? 'C' : ' ';
59 s_cf[3] = (flags & INPUT_COMPLETE_VARIABLES) ? 'V' : ' ';
60 s_cf[4] = (flags & INPUT_COMPLETE_USERNAMES) ? 'U' : ' ';
61 s_cf[5] = (flags & INPUT_COMPLETE_CD) ? 'D' : ' ';
62 s_cf[6] = (flags & INPUT_COMPLETE_SHELL_ESC) ? 'S' : ' ';
64 return s_cf;
66 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
67 #else
68 #define SHOW_C_CTX(func)
69 #endif /* DO_CMPLETION_DEBUG */
71 static char *
72 filename_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
74 static DIR *directory;
75 static char *filename = NULL;
76 static char *dirname = NULL;
77 static char *users_dirname = NULL;
78 static size_t filename_len;
79 int isdir = 1, isexec = 0;
81 struct dirent *entry = NULL;
83 SHOW_C_CTX("filename_completion_function");
85 if (text && (flags & INPUT_COMPLETE_SHELL_ESC))
86 text = shell_unescape (text);
88 /* If we're starting the match process, initialize us a bit. */
89 if (!state){
90 const char *temp;
92 g_free (dirname);
93 g_free (filename);
94 g_free (users_dirname);
96 if ((*text) && (temp = strrchr (text, PATH_SEP))){
97 filename = g_strdup (++temp);
98 dirname = g_strndup (text, temp - text);
99 } else {
100 dirname = g_strdup (".");
101 filename = g_strdup (text);
104 /* We aren't done yet. We also support the "~user" syntax. */
106 /* Save the version of the directory that the user typed. */
107 users_dirname = dirname;
109 // FIXME: memleak ?
110 dirname = tilde_expand (dirname);
111 canonicalize_pathname (dirname);
112 /* Here we should do something with variable expansion
113 and `command`.
114 Maybe a dream - UNIMPLEMENTED yet. */
116 directory = mc_opendir (dirname);
117 filename_len = strlen (filename);
120 /* Now that we have some state, we can read the directory. */
122 while (directory && (entry = mc_readdir (directory))){
123 /* Special case for no filename.
124 All entries except "." and ".." match. */
125 if (!filename_len){
126 if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
127 continue;
128 } else {
129 /* Otherwise, if these match up to the length of filename, then
130 it may be a match. */
131 if ((entry->d_name[0] != filename[0]) ||
132 ((NLENGTH (entry)) < filename_len) ||
133 strncmp (filename, entry->d_name, filename_len))
134 continue;
136 isdir = 1; isexec = 0;
138 char *tmp = g_malloc (3 + strlen (dirname) + NLENGTH (entry));
139 struct stat tempstat;
141 strcpy (tmp, dirname);
142 strcat (tmp, PATH_SEP_STR);
143 strcat (tmp, entry->d_name);
144 canonicalize_pathname (tmp);
145 /* Unix version */
146 if (!mc_stat (tmp, &tempstat)){
147 uid_t my_uid = getuid ();
148 gid_t my_gid = getgid ();
150 if (!S_ISDIR (tempstat.st_mode)){
151 isdir = 0;
152 if ((!my_uid && (tempstat.st_mode & 0111)) ||
153 (my_uid == tempstat.st_uid && (tempstat.st_mode & 0100)) ||
154 (my_gid == tempstat.st_gid && (tempstat.st_mode & 0010)) ||
155 (tempstat.st_mode & 0001))
156 isexec = 1;
159 g_free (tmp);
161 if ((flags & INPUT_COMPLETE_COMMANDS)
162 && (isexec || isdir))
163 break;
164 if ((flags & INPUT_COMPLETE_CD)
165 && isdir)
166 break;
167 if (flags & (INPUT_COMPLETE_FILENAMES))
168 break;
169 continue;
172 if (!entry){
173 if (directory){
174 mc_closedir (directory);
175 directory = NULL;
177 g_free (dirname);
178 dirname = NULL;
179 g_free (filename);
180 filename = NULL;
181 g_free (users_dirname);
182 users_dirname = NULL;
183 return NULL;
184 } else {
185 char *temp;
187 if (users_dirname && (users_dirname[0] != '.' || users_dirname[1])){
188 int dirlen = strlen (users_dirname);
189 temp = g_malloc (3 + dirlen + NLENGTH (entry));
190 strcpy (temp, users_dirname);
191 /* We need a `/' at the end. */
192 if (users_dirname[dirlen - 1] != PATH_SEP){
193 temp[dirlen] = PATH_SEP;
194 temp[dirlen + 1] = 0;
196 strcat (temp, entry->d_name);
197 } else {
198 temp = g_malloc (2 + NLENGTH (entry));
199 strcpy (temp, entry->d_name);
201 if (isdir)
202 strcat (temp, PATH_SEP_STR);
204 if (temp && (flags & INPUT_COMPLETE_SHELL_ESC))
206 SHELL_ESCAPED_STR e_temp = shell_escape(temp);
207 g_free (temp);
208 temp = e_temp.s;
210 return temp;
214 /* We assume here that text[0] == '~' , if you want to call it in another way,
215 you have to change the code */
216 static char *
217 username_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
219 static struct passwd *entry;
220 static int userlen;
222 SHOW_C_CTX("username_completion_function");
224 if (text[0] == '\\' && text[1] == '~') text++;
225 if (!state){ /* Initialization stuff */
226 setpwent ();
227 userlen = strlen (text + 1);
229 while ((entry = getpwent ()) != NULL){
230 /* Null usernames should result in all users as possible completions. */
231 if (!userlen)
232 break;
233 else if (text[1] == entry->pw_name[0] &&
234 !strncmp (text + 1, entry->pw_name, userlen))
235 break;
238 if (!entry){
239 endpwent ();
240 return NULL;
241 } else {
242 char *temp = g_malloc (3 + strlen (entry->pw_name));
244 *temp = '~';
245 strcpy (temp + 1, entry->pw_name);
246 strcat (temp, PATH_SEP_STR);
247 return temp;
251 /* Linux declares environ in <unistd.h>, so don't repeat it here. */
252 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
253 extern char **environ;
254 #endif
256 /* We assume text [0] == '$' and want to have a look at text [1], if it is
257 equal to '{', so that we should append '}' at the end */
258 static char *
259 variable_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
261 static char **env_p;
262 static int varlen, isbrace;
263 const char *p = NULL;
265 SHOW_C_CTX("variable_completion_function");
267 if (!state){ /* Initialization stuff */
268 isbrace = (text [1] == '{');
269 varlen = strlen (text + 1 + isbrace);
270 env_p = environ;
273 while (*env_p){
274 p = strchr (*env_p, '=');
275 if (p && p - *env_p >= varlen && !strncmp (text + 1 + isbrace, *env_p, varlen))
276 break;
277 env_p++;
280 if (!*env_p)
281 return NULL;
282 else {
283 char *temp = g_malloc (2 + 2 * isbrace + p - *env_p);
285 *temp = '$';
286 if (isbrace)
287 temp [1] = '{';
288 memcpy (temp + 1 + isbrace, *env_p, p - *env_p);
289 if (isbrace)
290 strcpy (temp + 2 + (p - *env_p), "}");
291 else
292 temp [1 + p - *env_p] = 0;
293 env_p++;
294 return temp;
298 #define whitespace(c) ((c) == ' ' || (c) == '\t')
299 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
301 static char **hosts = NULL;
302 static char **hosts_p = NULL;
303 static int hosts_alloclen = 0;
304 static void fetch_hosts (const char *filename)
306 FILE *file = fopen (filename, "r");
307 char buffer[256], *name;
308 register int i, start;
310 if (!file)
311 return;
313 while (fgets (buffer, 255, file) != NULL){
314 /* Skip to first character. */
315 for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++);
316 /* Ignore comments... */
317 if (buffer[i] == '#')
318 continue;
319 /* Handle $include. */
320 if (!strncmp (buffer + i, "$include ", 9)){
321 char *includefile = buffer + i + 9;
322 char *t;
324 /* Find start of filename. */
325 while (*includefile && whitespace (*includefile))
326 includefile++;
327 t = includefile;
329 /* Find end of filename. */
330 while (*t && !cr_whitespace (*t))
331 t++;
332 *t = '\0';
334 fetch_hosts (includefile);
335 continue;
338 /* Skip IP #s. */
339 while (buffer[i] && !cr_whitespace (buffer[i]))
340 i++;
342 /* Get the host names separated by white space. */
343 while (buffer[i] && buffer[i] != '#'){
344 while (buffer[i] && cr_whitespace (buffer[i]))
345 i++;
346 if (buffer[i] == '#')
347 continue;
348 for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++);
349 if (i - start == 0)
350 continue;
351 name = g_strndup (buffer + start, i - start);
353 char **host_p;
355 if (hosts_p - hosts >= hosts_alloclen){
356 int j = hosts_p - hosts;
358 hosts = g_realloc ((void *)hosts, ((hosts_alloclen += 30) + 1) * sizeof (char *));
359 hosts_p = hosts + j;
361 for (host_p = hosts; host_p < hosts_p; host_p++)
362 if (!strcmp (name, *host_p))
363 break; /* We do not want any duplicates */
364 if (host_p == hosts_p){
365 *(hosts_p++) = name;
366 *hosts_p = NULL;
367 } else
368 g_free (name);
372 fclose (file);
375 static char *
376 hostname_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
378 static char **host_p;
379 static int textstart, textlen;
381 SHOW_C_CTX("hostname_completion_function");
383 if (!state){ /* Initialization stuff */
384 const char *p;
386 if (hosts != NULL){
387 for (host_p = hosts; *host_p; host_p++)
388 g_free (*host_p);
389 g_free (hosts);
391 hosts = g_new (char *, (hosts_alloclen = 30) + 1);
392 *hosts = NULL;
393 hosts_p = hosts;
394 fetch_hosts ((p = getenv ("HOSTFILE")) ? p : "/etc/hosts");
395 host_p = hosts;
396 textstart = (*text == '@') ? 1 : 0;
397 textlen = strlen (text + textstart);
400 while (*host_p){
401 if (!textlen)
402 break; /* Match all of them */
403 else if (!strncmp (text + textstart, *host_p, textlen))
404 break;
405 host_p++;
408 if (!*host_p){
409 for (host_p = hosts; *host_p; host_p++)
410 g_free (*host_p);
411 g_free (hosts);
412 hosts = NULL;
413 return NULL;
414 } else {
415 char *temp = g_malloc (2 + strlen (*host_p));
417 if (textstart)
418 *temp = '@';
419 strcpy (temp + textstart, *host_p);
420 host_p++;
421 return temp;
426 * This is the function to call when the word to complete is in a position
427 * where a command word can be found. It looks around $PATH, looking for
428 * commands that match. It also scans aliases, function names, and the
429 * table of shell built-ins.
431 static char *
432 command_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
434 static const char *path_end;
435 static int isabsolute;
436 static int phase;
437 static int text_len;
438 static const char *const *words;
439 static char *path;
440 static char *cur_path;
441 static char *cur_word;
442 static int init_state;
443 static const char *const bash_reserved[] = {
444 "if", "then", "else", "elif", "fi", "case", "esac", "for",
445 "select", "while", "until", "do", "done", "in", "function", 0
447 static const char *const bash_builtins[] = {
448 "alias", "bg", "bind", "break", "builtin", "cd", "command",
449 "continue", "declare", "dirs", "echo", "enable", "eval",
450 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
451 "help", "history", "jobs", "kill", "let", "local", "logout",
452 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
453 "shift", "source", "suspend", "test", "times", "trap", "type",
454 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
456 char *p, *found;
458 SHOW_C_CTX("command_completion_function");
460 if (!(flags & INPUT_COMPLETE_COMMANDS))
461 return 0;
463 text = shell_unescape(text);
464 flags &= ~INPUT_COMPLETE_SHELL_ESC;
466 if (!state) { /* Initialize us a little bit */
467 isabsolute = strchr (text, PATH_SEP) != 0;
468 if (!isabsolute) {
469 words = bash_reserved;
470 phase = 0;
471 text_len = strlen (text);
472 if (!path && (path = g_strdup (getenv ("PATH"))) != NULL) {
473 p = path;
474 path_end = strchr (p, 0);
475 while ((p = strchr (p, PATH_ENV_SEP))) {
476 *p++ = 0;
482 if (isabsolute) {
483 p = filename_completion_function (text, state, flags);
484 if (!p)
485 return 0;
486 SHELL_ESCAPED_STR e_p = shell_escape(p);
487 g_free(p);
488 return e_p.s;
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 = concat_dir_and_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++;
544 SHELL_ESCAPED_STR e_p = shell_escape(p);
545 g_free(found);
546 return e_p.s;
548 return found;
552 static int
553 match_compare (const void *a, const void *b)
555 return strcmp (*(char **)a, *(char **)b);
558 /* Returns an array of char * matches with the longest common denominator
559 in the 1st entry. Then a NULL terminated list of different possible
560 completions follows.
561 You have to supply your own CompletionFunction with the word you
562 want to complete as the first argument and an count of previous matches
563 as the second.
564 In case no matches were found we return NULL. */
565 static char **
566 completion_matches (char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
568 /* Number of slots in match_list. */
569 int match_list_size;
571 /* The list of matches. */
572 char **match_list = g_new (char *, (match_list_size = 30) + 1);
574 /* Number of matches actually found. */
575 int matches = 0;
577 /* Temporary string binder. */
578 char *string;
580 match_list[1] = NULL;
582 while ((string = (*entry_function) (text, matches, flags)) != NULL){
583 if (matches + 1 == match_list_size)
584 match_list = (char **) g_realloc (match_list, ((match_list_size += 30) + 1) * sizeof (char *));
585 match_list[++matches] = string;
586 match_list[matches + 1] = NULL;
589 /* If there were any matches, then look through them finding out the
590 lowest common denominator. That then becomes match_list[0]. */
591 if (matches)
593 register int i = 1;
594 int low = 4096; /* Count of max-matched characters. */
596 /* If only one match, just use that. */
597 if (matches == 1){
598 match_list[0] = match_list[1];
599 match_list[1] = NULL;
600 } else {
601 int j;
603 qsort (match_list + 1, matches, sizeof (char *), match_compare);
605 /* And compare each member of the list with
606 the next, finding out where they stop matching.
607 If we find two equal strings, we have to put one away... */
609 j = i + 1;
610 while (j < matches + 1)
612 register int c1, c2, si;
614 for (si = 0;(c1 = match_list [i][si]) && (c2 = match_list [j][si]); si++)
615 if (c1 != c2) break;
617 if (!c1 && !match_list [j][si]){ /* Two equal strings */
618 g_free (match_list [j]);
619 j++;
620 if (j > matches)
621 break;
622 continue; /* Look for a run of equal strings */
623 } else
624 if (low > si) low = si;
625 if (i + 1 != j) /* So there's some gap */
626 match_list [i + 1] = match_list [j];
627 i++; j++;
629 matches = i;
630 match_list [matches + 1] = NULL;
631 match_list[0] = g_strndup(match_list[1], low);
633 } else { /* There were no matches. */
634 g_free (match_list);
635 match_list = NULL;
637 return match_list;
640 /* Check if directory completion is needed */
641 static int
642 check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
644 const char *p, *q;
646 SHOW_C_CTX("check_is_cd");
647 if (!(flags & INPUT_COMPLETE_CD))
648 return 0;
650 /* Skip initial spaces */
651 p = text;
652 q = text + start;
653 while (p < q && *p && isspace ((unsigned char) *p))
654 p++;
656 /* Check if the command is "cd" and the cursor is after it */
657 if (p[0] == 'c' && p[1] == 'd' && isspace ((unsigned char) p[2])
658 && (p + 2 < q))
659 return 1;
661 return 0;
664 /* Returns an array of matches, or NULL if none. */
665 static char **
666 try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
668 int in_command_position = 0;
669 char *word, c;
670 char **matches = NULL;
671 const char *command_separator_chars = ";|&{(`";
672 char *p = NULL, *q = NULL, *r = NULL;
673 int is_cd = check_is_cd (text, *start, flags);
675 SHOW_C_CTX("try_complete");
677 c = text [*end];
678 text [*end] = 0;
679 word = g_strdup (text + *start);
680 text [*end] = c;
682 /* Determine if this could be a command word. It is if it appears at
683 the start of the line (ignoring preceding whitespace), or if it
684 appears after a character that separates commands. And we have to
685 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
686 if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){
687 int i = *start - 1;
688 for (i = *start - 1; i > -1; i--) {
689 if (text[i] == ' ' || text[i] == '\t'){
690 if (i == 0 ) continue;
691 if (text[i-1] != '\\') {
692 i--;
693 break;
697 if (i < 0)
698 in_command_position++;
699 else if (strchr (command_separator_chars, text[i])){
700 register int this_char, prev_char;
702 in_command_position++;
704 if (i){
705 /* Handle the two character tokens `>&', `<&', and `>|'.
706 We are not in a command position after one of these. */
707 this_char = text[i];
708 prev_char = text[i - 1];
710 if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
711 (this_char == '|' && prev_char == '>'))
712 in_command_position = 0;
713 else if (i > 0 && text [i-1] == '\\') /* Quoted */
714 in_command_position = 0;
719 if (flags & INPUT_COMPLETE_COMMANDS)
720 p = strrchr (word, '`');
721 if (flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES))
722 q = strrchr (word, '$');
723 if (flags & INPUT_COMPLETE_HOSTNAMES)
724 r = strrchr (word, '@');
725 if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){
726 if (q > p)
727 p = q + 1;
728 q = NULL;
731 /* Command substitution? */
732 if (p > q && p > r){
733 SHOW_C_CTX("try_complete:cmd_backq_subst");
734 matches = completion_matches (p + 1, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
735 if (matches)
736 *start += p + 1 - word;
739 /* Variable name? */
740 else if (q > p && q > r){
741 SHOW_C_CTX("try_complete:var_subst");
742 matches = completion_matches (q, variable_completion_function, flags);
743 if (matches)
744 *start += q - word;
747 /* Starts with '@', then look through the known hostnames for
748 completion first. */
749 else if (r > p && r > q){
750 SHOW_C_CTX("try_complete:host_subst");
751 matches = completion_matches (r, hostname_completion_function, flags);
752 if (matches)
753 *start += r - word;
756 /* Starts with `~' and there is no slash in the word, then
757 try completing this word as a username. */
758 if (!matches && *word == '~' && (flags & INPUT_COMPLETE_USERNAMES) && !strchr (word, PATH_SEP))
760 SHOW_C_CTX("try_complete:user_subst");
761 matches = completion_matches (word, username_completion_function, flags);
765 /* And finally if this word is in a command position, then
766 complete over possible command names, including aliases, functions,
767 and command names. */
768 if (!matches && in_command_position)
770 SHOW_C_CTX("try_complete:cmd_subst");
771 matches = completion_matches (word, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
774 else if (!matches && (flags & INPUT_COMPLETE_FILENAMES)){
775 if (is_cd)
776 flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
777 SHOW_C_CTX("try_complete:filename_subst_1");
778 matches = completion_matches (word, filename_completion_function, flags);
779 if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
780 char *p, *q = text + *start;
782 for (p = text; *p && p < q; p++){
783 if (*p == ' ' || *p == '\t') {
784 if (p == text) continue;
785 if (*(p-1) == '\\') {
786 p--;
787 break;
791 if (!strncmp (p, "cd", 2))
792 for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); p++){
793 if (p == text) continue;
794 if (*(p-1) == '\\') {
795 p--;
796 break;
799 if (p == q){
800 char * const cdpath_ref = g_strdup (getenv ("CDPATH"));
801 char *cdpath = cdpath_ref;
802 char c, *s, *r;
804 if (cdpath == NULL)
805 c = 0;
806 else
807 c = ':';
808 while (!matches && c == ':'){
809 s = strchr (cdpath, ':');
810 if (s == NULL)
811 s = strchr (cdpath, 0);
812 c = *s;
813 *s = 0;
814 if (*cdpath){
815 r = concat_dir_and_file (cdpath, word);
816 ignore_filenames = 1;
817 matches = completion_matches (r, filename_completion_function);
818 ignore_filenames = 0;
819 g_free (r);
821 *s = c;
822 cdpath = s + 1;
824 g_free (cdpath_ref);
829 g_free (word);
831 return matches;
834 void free_completions (WInput *in)
836 char **p;
838 if (!in->completions)
839 return;
840 for (p=in->completions; *p; p++)
841 g_free (*p);
842 g_free (in->completions);
843 in->completions = NULL;
846 static int query_height, query_width;
847 static WInput *input;
848 static int min_end;
849 static int start, end;
851 static int insert_text (WInput *in, char *text, ssize_t len)
853 len = min (len, (ssize_t) strlen (text)) + start - end;
854 if (strlen (in->buffer) + len >= (size_t) in->current_max_len){
855 /* Expand the buffer */
856 char *narea = g_realloc (in->buffer, in->current_max_len + len + in->field_len);
857 if (narea){
858 in->buffer = narea;
859 in->current_max_len += len + in->field_len;
862 if (strlen (in->buffer)+1 < (size_t) in->current_max_len){
863 if (len > 0){
864 int i = strlen (&in->buffer [end]);
865 for (; i >= 0; i--)
866 in->buffer [end + len + i] = in->buffer [end + i];
867 } else if (len < 0){
868 char *p = in->buffer + end + len, *q = in->buffer + end;
869 while (*q)
870 *(p++) = *(q++);
871 *p = 0;
873 memcpy (in->buffer + start, text, len - start + end);
874 in->point += len;
875 update_input (in, 1);
876 end += len;
878 return len != 0;
881 static cb_ret_t
882 query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
884 switch (msg) {
885 case DLG_KEY:
886 switch (parm) {
887 case KEY_LEFT:
888 case KEY_RIGHT:
889 h->ret_value = 0;
890 dlg_stop (h);
891 return MSG_HANDLED;
893 case KEY_BACKSPACE:
894 if (end == min_end) {
895 h->ret_value = 0;
896 dlg_stop (h);
897 return MSG_HANDLED;
898 } else {
899 WLEntry *e, *e1;
901 e1 = e = ((WListbox *) (h->current))->list;
902 do {
903 if (!strncmp
904 (input->buffer + start, e1->text,
905 end - start - 1)) {
906 listbox_select_entry ((WListbox *) (h->current),
907 e1);
908 handle_char (input, parm);
909 end--;
910 send_message (h->current, WIDGET_DRAW, 0);
911 break;
913 e1 = e1->next;
914 } while (e != e1);
916 return MSG_HANDLED;
918 default:
919 if (parm > 0xff || !is_printable (parm)) {
920 if (is_in_input_map (input, parm) == 2) {
921 if (end == min_end)
922 return MSG_HANDLED;
923 h->ret_value = B_USER; /* This means we want to refill the
924 list box and start again */
925 dlg_stop (h);
926 return MSG_HANDLED;
927 } else
928 return MSG_NOT_HANDLED;
929 } else {
930 WLEntry *e, *e1;
931 int need_redraw = 0;
932 int low = 4096;
933 char *last_text = NULL;
935 e1 = e = ((WListbox *) (h->current))->list;
936 do {
937 if (!strncmp
938 (input->buffer + start, e1->text, end - start)) {
939 if (e1->text[end - start] == parm) {
940 if (need_redraw) {
941 register int c1, c2, si;
943 for (si = end - start + 1;
944 (c1 = last_text[si])
945 && (c2 = e1->text[si]); si++)
946 if (c1 != c2)
947 break;
948 if (low > si)
949 low = si;
950 last_text = e1->text;
951 need_redraw = 2;
952 } else {
953 need_redraw = 1;
954 listbox_select_entry ((WListbox *) (h->
955 current),
956 e1);
957 last_text = e1->text;
961 e1 = e1->next;
962 } while (e != e1);
963 if (need_redraw == 2) {
964 insert_text (input, last_text, low);
965 send_message (h->current, WIDGET_DRAW, 0);
966 } else if (need_redraw == 1) {
967 h->ret_value = B_ENTER;
968 dlg_stop (h);
971 return MSG_HANDLED;
973 break;
975 default:
976 return default_dlg_callback (h, msg, parm);
980 #define DO_INSERTION 1
981 #define DO_QUERY 2
982 /* Returns 1 if the user would like to see us again */
983 static int
984 complete_engine (WInput *in, int what_to_do)
986 if (in->completions && in->point != end)
987 free_completions (in);
988 if (!in->completions){
989 end = in->point;
990 for (start = end ? end - 1 : 0; start > -1; start--)
991 if (strchr (" \t;|<>", in->buffer [start])){
992 if (start > 0 && in->buffer [start-1] == '\\')
993 continue;
994 else
995 break;
997 if (start < end)
998 start++;
999 in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
1002 if (in->completions){
1003 if (what_to_do & DO_INSERTION || ((what_to_do & DO_QUERY) && !in->completions[1])) {
1004 char * complete = in->completions [0];
1005 if (insert_text (in, complete, strlen (complete))){
1006 if (in->completions [1])
1007 beep ();
1008 else
1009 free_completions (in);
1010 } else
1011 beep ();
1013 if ((what_to_do & DO_QUERY) && in->completions && in->completions [1]) {
1014 int maxlen = 0, i, count = 0;
1015 int x, y, w, h;
1016 int start_x, start_y;
1017 char **p, *q;
1018 Dlg_head *query_dlg;
1019 WListbox *query_list;
1021 for (p=in->completions + 1; *p; count++, p++) {
1022 if ((i = strlen (*p)) > maxlen)
1023 maxlen = i;
1025 start_x = in->widget.x;
1026 start_y = in->widget.y;
1027 if (start_y - 2 >= count) {
1028 y = start_y - 2 - count;
1029 h = 2 + count;
1030 } else {
1031 if (start_y >= LINES - start_y - 1) {
1032 y = 0;
1033 h = start_y;
1034 } else {
1035 y = start_y + 1;
1036 h = LINES - start_y - 1;
1039 x = start - in->first_shown - 2 + start_x;
1040 w = maxlen + 4;
1041 if (x + w > COLS)
1042 x = COLS - w;
1043 if (x < 0)
1044 x = 0;
1045 if (x + w > COLS)
1046 w = COLS;
1047 input = in;
1048 min_end = end;
1049 query_height = h;
1050 query_width = w;
1051 query_dlg = create_dlg (y, x, query_height, query_width,
1052 dialog_colors, query_callback,
1053 "[Completion]", NULL, DLG_COMPACT);
1054 query_list = listbox_new (1, 1, w - 2, h - 2, NULL);
1055 add_widget (query_dlg, query_list);
1056 for (p = in->completions + 1; *p; p++)
1057 listbox_add_item (query_list, 0, 0, *p, NULL);
1058 run_dlg (query_dlg);
1059 q = NULL;
1060 if (query_dlg->ret_value == B_ENTER){
1061 listbox_get_current (query_list, &q, NULL);
1062 if (q)
1063 insert_text (in, q, strlen (q));
1065 if (q || end != min_end)
1066 free_completions (in);
1067 i = query_dlg->ret_value; /* B_USER if user wants to start over again */
1068 destroy_dlg (query_dlg);
1069 if (i == B_USER)
1070 return 1;
1072 } else
1073 beep ();
1074 return 0;
1077 void complete (WInput *in)
1079 int engine_flags;
1081 if (in->completions)
1082 engine_flags = DO_QUERY;
1083 else
1085 engine_flags = DO_INSERTION;
1087 if (show_all_if_ambiguous)
1088 engine_flags |= DO_QUERY;
1091 while (complete_engine (in, engine_flags));