Just a little correction at the it.po file.
[midnight-commander.git] / src / complete.c
blob9ee6c320d87036a8844024fcf8f8d37b8abd1147
1 /* Input line filename/username/hostname/variable/command completion.
2 (Let mc type for you...)
4 Copyright (C) 1995 The Free Software Foundation
6 Written by: 1995 Jakub Jelinek
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 #include <config.h>
23 #include <stdio.h>
24 #include <string.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
29 #include <sys/types.h>
30 #include <sys/stat.h>
32 #include "global.h"
33 #include "tty.h"
34 #include "win.h"
35 #include "color.h"
36 #include "dlg.h"
37 #include "widget.h"
38 #include "dialog.h"
39 #include "wtools.h"
40 #include "complete.h"
41 #include "main.h"
42 #include "key.h" /* XCTRL and ALT macros */
43 #include "../vfs/vfs.h"
45 /* This flag is used in filename_completion_function */
46 static int ignore_filenames = 0;
48 /* This flag is used by command_completion_function */
49 /* to hint the filename_completion_function */
50 static int look_for_executables = 0;
52 static char *
53 filename_completion_function (char *text, int state)
55 static DIR *directory;
56 static char *filename = NULL;
57 static char *dirname = NULL;
58 static char *users_dirname = NULL;
59 static int filename_len;
60 int isdir = 1, isexec = 0;
62 struct dirent *entry = NULL;
64 /* If we're starting the match process, initialize us a bit. */
65 if (!state){
66 char *temp;
68 if (dirname)
69 g_free (dirname);
70 if (filename)
71 g_free (filename);
72 if (users_dirname)
73 g_free (users_dirname);
75 if ((*text) && (temp = strrchr (text, PATH_SEP))){
76 filename = g_strdup (++temp);
77 dirname = g_strndup (text, temp - text);
78 } else {
79 dirname = g_strdup (".");
80 filename = g_strdup (text);
83 /* We aren't done yet. We also support the "~user" syntax. */
85 /* Save the version of the directory that the user typed. */
86 users_dirname = dirname;
88 dirname = tilde_expand (dirname);
89 if (!dirname){
90 g_free (users_dirname);
91 g_free (filename);
92 users_dirname = filename = NULL;
93 return NULL;
95 canonicalize_pathname (dirname);
96 /* Here we should do something with variable expansion
97 and `command`.
98 Maybe a dream - UNIMPLEMENTED yet. */
100 directory = mc_opendir (dirname);
101 filename_len = strlen (filename);
104 /* Now that we have some state, we can read the directory. */
106 while (directory && (entry = mc_readdir (directory))){
107 /* Special case for no filename.
108 All entries except "." and ".." match. */
109 if (!filename_len){
110 if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, ".."))
111 continue;
112 } else {
113 /* Otherwise, if these match up to the length of filename, then
114 it may be a match. */
115 if ((entry->d_name[0] != filename[0]) ||
116 ((NLENGTH (entry)) < filename_len) ||
117 strncmp (filename, entry->d_name, filename_len))
118 continue;
120 isdir = 1; isexec = 0;
122 char *tmp = g_malloc (3 + strlen (dirname) + NLENGTH (entry));
123 struct stat tempstat;
125 strcpy (tmp, dirname);
126 strcat (tmp, PATH_SEP_STR);
127 strcat (tmp, entry->d_name);
128 canonicalize_pathname (tmp);
129 /* Unix version */
130 if (!mc_stat (tmp, &tempstat)){
131 uid_t my_uid = getuid ();
132 gid_t my_gid = getgid ();
134 if (!S_ISDIR (tempstat.st_mode)){
135 isdir = 0;
136 if ((!my_uid && (tempstat.st_mode & 0111)) ||
137 (my_uid == tempstat.st_uid && (tempstat.st_mode & 0100)) ||
138 (my_gid == tempstat.st_gid && (tempstat.st_mode & 0010)) ||
139 (tempstat.st_mode & 0001))
140 isexec = 1;
143 g_free (tmp);
145 switch (look_for_executables)
147 case 2: if (!isexec)
148 continue;
149 break;
150 case 1: if (!isexec && !isdir)
151 continue;
152 break;
154 if (ignore_filenames && !isdir)
155 continue;
156 break;
159 if (!entry){
160 if (directory){
161 mc_closedir (directory);
162 directory = NULL;
164 if (dirname){
165 g_free (dirname);
166 dirname = NULL;
168 if (filename){
169 g_free (filename);
170 filename = NULL;
172 if (users_dirname){
173 g_free (users_dirname);
174 users_dirname = NULL;
176 return NULL;
177 } else {
178 char *temp;
180 if (users_dirname && (users_dirname[0] != '.' || users_dirname[1])){
181 int dirlen = strlen (users_dirname);
182 temp = g_malloc (3 + dirlen + NLENGTH (entry));
183 strcpy (temp, users_dirname);
184 /* We need a `/' at the end. */
185 if (users_dirname[dirlen - 1] != PATH_SEP){
186 temp[dirlen] = PATH_SEP;
187 temp[dirlen + 1] = 0;
189 strcat (temp, entry->d_name);
190 } else {
191 temp = g_malloc (2 + NLENGTH (entry));
192 strcpy (temp, entry->d_name);
194 if (isdir)
195 strcat (temp, PATH_SEP_STR);
196 return temp;
200 /* We assume here that text[0] == '~' , if you want to call it in another way,
201 you have to change the code */
202 #ifdef NATIVE_WIN32
203 char *username_completion_function (char *text, int state)
205 return NULL;
207 #else
208 static char *
209 username_completion_function (char *text, int state)
211 static struct passwd *entry;
212 static int userlen;
214 if (!state){ /* Initialization stuff */
215 setpwent ();
216 userlen = strlen (text + 1);
218 while ((entry = getpwent ()) != NULL){
219 /* Null usernames should result in all users as possible completions. */
220 if (!userlen)
221 break;
222 else if (text[1] == entry->pw_name[0] &&
223 !strncmp (text + 1, entry->pw_name, userlen))
224 break;
227 if (!entry){
228 endpwent ();
229 return NULL;
230 } else {
231 char *temp = g_malloc (3 + strlen (entry->pw_name));
233 *temp = '~';
234 strcpy (temp + 1, entry->pw_name);
235 strcat (temp, PATH_SEP_STR);
236 return temp;
240 extern char **environ;
241 #endif /* NATIVE_WIN32 */
243 /* We assume text [0] == '$' and want to have a look at text [1], if it is
244 equal to '{', so that we should append '}' at the end */
245 static char *
246 variable_completion_function (char *text, int state)
248 static char **env_p;
249 static int varlen, isbrace;
250 char *p = 0;
252 if (!state){ /* Initialization stuff */
253 isbrace = (text [1] == '{');
254 varlen = strlen (text + 1 + isbrace);
255 env_p = environ;
258 while (*env_p){
259 p = strchr (*env_p, '=');
260 if (p && p - *env_p >= varlen && !strncmp (text + 1 + isbrace, *env_p, varlen))
261 break;
262 env_p++;
265 if (!*env_p)
266 return NULL;
267 else {
268 char *temp = g_malloc (2 + 2 * isbrace + p - *env_p);
270 *temp = '$';
271 if (isbrace)
272 temp [1] = '{';
273 strncpy (temp + 1 + isbrace, *env_p, p - *env_p);
274 if (isbrace)
275 strcpy (temp + 2 + (p - *env_p), "}");
276 else
277 temp [1 + p - *env_p] = 0;
278 env_p++;
279 return temp;
283 #define whitespace(c) ((c) == ' ' || (c) == '\t')
284 #define cr_whitespace(c) (whitespace (c) || (c) == '\n' || (c) == '\r')
286 static char **hosts = NULL;
287 static char **hosts_p = NULL;
288 static int hosts_alloclen = 0;
289 static void fetch_hosts (char *filename)
291 FILE *file = fopen (filename, "r");
292 char *temp, buffer[256], *name;
293 register int i, start;
295 if (!file)
296 return;
298 while ((temp = fgets (buffer, 255, file)) != NULL){
299 /* Skip to first character. */
300 for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++);
301 /* Ignore comments... */
302 if (buffer[i] == '#')
303 continue;
304 /* Handle $include. */
305 if (!strncmp (buffer + i, "$include ", 9)){
306 char *includefile = buffer + i + 9;
307 char *t;
309 /* Find start of filename. */
310 while (*includefile && whitespace (*includefile))
311 includefile++;
312 t = includefile;
314 /* Find end of filename. */
315 while (*t && !cr_whitespace (*t))
316 t++;
317 *t = '\0';
319 fetch_hosts (includefile);
320 continue;
323 /* Skip IP #s. */
324 while (buffer[i] && !cr_whitespace (buffer[i]))
325 i++;
327 /* Get the host names separated by white space. */
328 while (buffer[i] && buffer[i] != '#'){
329 while (buffer[i] && cr_whitespace (buffer[i]))
330 i++;
331 if (buffer[i] == '#')
332 continue;
333 for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++);
334 if (i - start == 0)
335 continue;
336 name = g_strndup (buffer + start, i - start);
338 char **host_p;
340 if (hosts_p - hosts >= hosts_alloclen){
341 int j = hosts_p - hosts;
343 hosts = g_realloc ((void *)hosts, ((hosts_alloclen += 30) + 1) * sizeof (char *));
344 hosts_p = hosts + j;
346 for (host_p = hosts; host_p < hosts_p; host_p++)
347 if (!strcmp (name, *host_p))
348 break; /* We do not want any duplicates */
349 if (host_p == hosts_p){
350 *(hosts_p++) = name;
351 *hosts_p = NULL;
352 } else
353 g_free (name);
357 fclose (file);
360 static char *
361 hostname_completion_function (char *text, int state)
363 static char **host_p;
364 static int textstart, textlen;
366 if (!state){ /* Initialization stuff */
367 char *p;
369 if (hosts != NULL){
370 for (host_p = hosts; *host_p; host_p++)
371 g_free (*host_p);
372 g_free (hosts);
374 hosts = g_new (char *, (hosts_alloclen = 30) + 1);
375 *hosts = NULL;
376 hosts_p = hosts;
377 fetch_hosts ((p = getenv ("HOSTFILE")) ? p : "/etc/hosts");
378 host_p = hosts;
379 textstart = (*text == '@') ? 1 : 0;
380 textlen = strlen (text + textstart);
383 while (*host_p){
384 if (!textlen)
385 break; /* Match all of them */
386 else if (!strncmp (text + textstart, *host_p, textlen))
387 break;
388 host_p++;
391 if (!*host_p){
392 for (host_p = hosts; *host_p; host_p++)
393 g_free (*host_p);
394 g_free (hosts);
395 hosts = NULL;
396 return NULL;
397 } else {
398 char *temp = g_malloc (2 + strlen (*host_p));
400 if (textstart)
401 *temp = '@';
402 strcpy (temp + textstart, *host_p);
403 host_p++;
404 return temp;
409 * This is the function to call when the word to complete is in a position
410 * where a command word can be found. It looks around $PATH, looking for
411 * commands that match. It also scans aliases, function names, and the
412 * table of shell built-ins.
414 static char *
415 command_completion_function (char *text, int state)
417 static char *path_end;
418 static int isabsolute;
419 static int phase;
420 static int text_len;
421 static const char * const * words;
422 static char *path;
423 static char *cur_path;
424 static char *cur_word;
425 static int init_state;
426 static const char * const bash_reserved [] = {
427 "if", "then", "else", "elif", "fi", "case", "esac", "for", "select",
428 "while", "until", "do", "done", "in", "function" , 0
430 static const char * const bash_builtins [] = {
431 "alias", "bg", "bind", "break", "builtin", "cd", "command", "continue",
432 "declare", "dirs", "echo", "enable", "eval", "exec", "exit", "export",
433 "fc", "fg", "getopts", "hash", "help", "history", "jobs", "kill", "let",
434 "local", "logout", "popd", "pushd", "pwd", "read", "readonly", "return",
435 "set", "shift", "source", "suspend", "test", "times", "trap", "type",
436 "typeset", "ulimit", "umask", "unalias", "unset", "wait" , 0
438 char *p, *found;
440 if (!state){ /* Initialize us a little bit */
441 isabsolute = strchr (text, PATH_SEP) != 0;
442 look_for_executables = isabsolute ? 1 : 2;
443 if (!isabsolute){
444 words = bash_reserved;
445 phase = 0;
446 text_len = strlen (text);
447 path = getenv ("PATH");
448 if (path){
449 p = path = g_strdup (path);
450 path_end = strchr (p, 0);
451 while ((p = strchr (p, PATH_ENV_SEP))){
452 *p++ = 0;
458 if (isabsolute){
459 p = filename_completion_function (text, state);
460 if (!p)
461 look_for_executables = 0;
462 return p;
465 found = NULL;
466 switch (phase){
467 case 0: /* Reserved words */
468 while (*words){
469 if (!strncmp (*words, text, text_len))
470 return g_strdup (*(words++));
471 words++;
473 phase++;
474 words = bash_builtins;
475 case 1: /* Builtin commands */
476 while (*words){
477 if (!strncmp (*words, text, text_len))
478 return g_strdup (*(words++));
479 words++;
481 phase++;
482 if (!path)
483 break;
484 cur_path = path;
485 cur_word = NULL;
486 case 2: /* And looking through the $PATH */
487 while (!found){
488 if (!cur_word){
489 char *expanded;
491 if (cur_path >= path_end)
492 break;
493 expanded = tilde_expand (*cur_path ? cur_path : ".");
494 if (!expanded){
495 g_free (path);
496 path = NULL;
497 return NULL;
499 p = canonicalize_pathname (expanded);
500 cur_word = concat_dir_and_file (p, text);
501 g_free (p);
502 cur_path = strchr (cur_path, 0) + 1;
503 init_state = state;
505 found = filename_completion_function (cur_word, state - init_state);
506 if (!found){
507 g_free (cur_word);
508 cur_word = NULL;
513 if (!found){
514 look_for_executables = 0;
515 if (path)
516 g_free (path);
517 return NULL;
519 if ((p = strrchr (found, PATH_SEP)) != NULL){
520 p++;
521 p = g_strdup (p);
522 g_free (found);
523 return p;
525 return found;
529 static int
530 match_compare (const void *a, const void *b)
532 return strcmp (*(char **)a, *(char **)b);
535 /* Returns an array of char * matches with the longest common denominator
536 in the 1st entry. Then a NULL terminated list of different possible
537 completions follows.
538 You have to supply your own CompletionFunction with the word you
539 want to complete as the first argument and an count of previous matches
540 as the second.
541 In case no matches were found we return NULL. */
542 static char **
543 completion_matches (char *text, CompletionFunction entry_function)
545 /* Number of slots in match_list. */
546 int match_list_size;
548 /* The list of matches. */
549 char **match_list = g_new (char *, (match_list_size = 30) + 1);
551 /* Number of matches actually found. */
552 int matches = 0;
554 /* Temporary string binder. */
555 char *string;
557 match_list[1] = NULL;
559 while ((string = (*entry_function) (text, matches)) != NULL){
560 if (matches + 1 == match_list_size)
561 match_list = (char **) g_realloc (match_list, ((match_list_size += 30) + 1) * sizeof (char *));
562 match_list[++matches] = string;
563 match_list[matches + 1] = NULL;
566 /* If there were any matches, then look through them finding out the
567 lowest common denominator. That then becomes match_list[0]. */
568 if (matches)
570 register int i = 1;
571 int low = 4096; /* Count of max-matched characters. */
573 /* If only one match, just use that. */
574 if (matches == 1){
575 match_list[0] = match_list[1];
576 match_list[1] = (char *)NULL;
577 } else {
578 int j;
580 qsort (match_list + 1, matches, sizeof (char *), match_compare);
582 /* And compare each member of the list with
583 the next, finding out where they stop matching.
584 If we find two equal strings, we have to put one away... */
586 j = i + 1;
587 while (j < matches + 1)
589 register int c1, c2, si;
591 for (si = 0;(c1 = match_list [i][si]) && (c2 = match_list [j][si]); si++)
592 if (c1 != c2) break;
594 if (!c1 && !match_list [j][si]){ /* Two equal strings */
595 g_free (match_list [j]);
596 j++;
597 if (j > matches)
598 break;
599 } else
600 if (low > si) low = si;
601 if (i + 1 != j) /* So there's some gap */
602 match_list [i + 1] = match_list [j];
603 i++; j++;
605 matches = i;
606 match_list [matches + 1] = NULL;
607 match_list[0] = g_malloc (low + 1);
608 strncpy (match_list[0], match_list[1], low);
609 match_list[0][low] = 0;
611 } else { /* There were no matches. */
612 g_free (match_list);
613 match_list = NULL;
615 return match_list;
618 static int
619 check_is_cd (char *text, int start, int flags)
621 char *p, *q = text + start;
623 for (p = text; *p && p < q && (*p == ' ' || *p == '\t'); p++);
624 if (((flags & INPUT_COMPLETE_COMMANDS) &&
625 !strncmp (p, "cd", 2) && (p [2] == ' ' || p [2] == '\t') &&
626 p + 2 < q) ||
627 (flags & INPUT_COMPLETE_CD))
628 return 1;
629 return 0;
632 /* Returns an array of matches, or NULL if none. */
633 static char **
634 try_complete (char *text, int *start, int *end, int flags)
636 int in_command_position = 0, i;
637 char *word, c;
638 char **matches = NULL;
639 char *command_separator_chars = ";|&{(`";
640 char *p = NULL, *q = NULL, *r = NULL;
641 int is_cd = check_is_cd (text, *start, flags);
643 ignore_filenames = 0;
644 c = text [*end];
645 text [*end] = 0;
646 word = g_strdup (text + *start);
647 text [*end] = c;
649 /* Determine if this could be a command word. It is if it appears at
650 the start of the line (ignoring preceding whitespace), or if it
651 appears after a character that separates commands. And we have to
652 be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
653 if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){
654 i = *start - 1;
655 while (i > -1 && (text[i] == ' ' || text[i] == '\t'))
656 i--;
657 if (i < 0)
658 in_command_position++;
659 else if (strchr (command_separator_chars, text[i])){
660 register int this_char, prev_char;
662 in_command_position++;
664 if (i){
665 /* Handle the two character tokens `>&', `<&', and `>|'.
666 We are not in a command position after one of these. */
667 this_char = text[i];
668 prev_char = text[i - 1];
670 if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
671 (this_char == '|' && prev_char == '>'))
672 in_command_position = 0;
673 else if (i > 0 && text [i-1] == '\\') /* Quoted */
674 in_command_position = 0;
679 if (flags & INPUT_COMPLETE_COMMANDS)
680 p = strrchr (word, '`');
681 if (flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES))
682 q = strrchr (word, '$');
683 if (flags & INPUT_COMPLETE_HOSTNAMES)
684 r = strrchr (word, '@');
685 if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){
686 if (q > p)
687 p = q + 1;
688 q = NULL;
691 /* Command substitution? */
692 if (p > q && p > r){
693 matches = completion_matches (p + 1, command_completion_function);
694 if (matches)
695 *start += p + 1 - word;
698 /* Variable name? */
699 else if (q > p && q > r){
700 matches = completion_matches (q, variable_completion_function);
701 if (matches)
702 *start += q - word;
705 /* Starts with '@', then look through the known hostnames for
706 completion first. */
707 else if (r > p && r > q){
708 matches = completion_matches (r, hostname_completion_function);
709 if (matches)
710 *start += r - word;
713 /* Starts with `~' and there is no slash in the word, then
714 try completing this word as a username. */
715 if (!matches && *word == '~' && (flags & INPUT_COMPLETE_USERNAMES) && !strchr (word, PATH_SEP))
716 matches = completion_matches (word, username_completion_function);
719 /* And finally if this word is in a command position, then
720 complete over possible command names, including aliases, functions,
721 and command names. */
722 if (!matches && in_command_position)
723 matches = completion_matches (word, command_completion_function);
725 else if (!matches && (flags & INPUT_COMPLETE_FILENAMES)){
726 if (is_cd)
727 ignore_filenames = 1;
728 matches = completion_matches (word, filename_completion_function);
729 ignore_filenames = 0;
730 if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
731 char *p, *q = text + *start;
733 for (p = text; *p && p < q && (*p == ' ' || *p == '\t'); p++);
734 if (!strncmp (p, "cd", 2))
735 for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); p++);
736 if (p == q){
737 char *cdpath = getenv ("CDPATH");
738 char c, *s, *r;
740 if (cdpath == NULL)
741 c = 0;
742 else
743 c = ':';
744 while (!matches && c == ':'){
745 s = strchr (cdpath, ':');
746 if (s == NULL)
747 s = strchr (cdpath, 0);
748 c = *s;
749 *s = 0;
750 if (*cdpath){
751 r = concat_dir_and_file (cdpath, word);
752 ignore_filenames = 1;
753 matches = completion_matches (r, filename_completion_function);
754 ignore_filenames = 0;
755 g_free (r);
757 *s = c;
758 cdpath = s + 1;
764 if (word)
765 g_free (word);
767 return matches;
770 void free_completions (WInput *in)
772 char **p;
774 if (!in->completions)
775 return;
776 for (p=in->completions; *p; p++)
777 g_free (*p);
778 g_free (in->completions);
779 in->completions = NULL;
782 static int query_height, query_width;
783 static WInput *input;
784 static int min_end;
785 static int start, end;
787 static int insert_text (WInput *in, char *text, int len)
789 len = min (len, strlen (text)) + start - end;
790 if (strlen (in->buffer) + len >= in->current_max_len){
791 /* Expand the buffer */
792 char *narea = g_realloc (in->buffer, in->current_max_len + len + in->field_len);
793 if (narea){
794 in->buffer = narea;
795 in->current_max_len += len + in->field_len;
798 if (strlen (in->buffer)+1 < in->current_max_len){
799 if (len > 0){
800 int i = strlen (&in->buffer [end]);
801 for (; i >= 0; i--)
802 in->buffer [end + len + i] = in->buffer [end + i];
803 } else if (len < 0){
804 char *p = in->buffer + end + len, *q = in->buffer + end;
805 while (*q)
806 *(p++) = *(q++);
807 *p = 0;
809 strncpy (in->buffer + start, text, len - start + end);
810 in->point += len;
811 update_input (in, 1);
812 end += len;
814 return len != 0;
817 static int
818 query_callback (Dlg_head * h, int Par, int Msg)
820 switch (Msg) {
821 case DLG_DRAW:
822 common_dialog_repaint (h);
823 break;
825 case DLG_KEY:
826 switch (Par) {
827 case KEY_LEFT:
828 case KEY_RIGHT:
829 h->ret_value = 0;
830 dlg_stop (h);
831 return 1;
833 case KEY_BACKSPACE:
834 if (end == min_end) {
835 h->ret_value = 0;
836 dlg_stop (h);
837 return 1;
838 } else {
839 WLEntry *e, *e1;
841 e1 = e = ((WListbox *) (h->current->widget))->list;
842 do {
843 if (!strncmp
844 (input->buffer + start, e1->text,
845 end - start - 1)) {
846 listbox_select_entry ((WListbox *) (h->current->
847 widget), e1);
848 handle_char (input, Par);
849 end--;
850 send_message (h->current->widget, WIDGET_DRAW, 0);
851 break;
853 e1 = e1->next;
854 } while (e != e1);
856 return 1;
858 default:
859 if (Par > 0xff || !is_printable (Par)) {
860 if (is_in_input_map (input, Par) == 2) {
861 if (end == min_end)
862 return 1;
863 h->ret_value = B_USER; /* This means we want to refill the
864 list box and start again */
865 dlg_stop (h);
866 return 1;
867 } else
868 return 0;
869 } else {
870 WLEntry *e, *e1;
871 int need_redraw = 0;
872 int low = 4096;
873 char *last_text = NULL;
875 e1 = e = ((WListbox *) (h->current->widget))->list;
876 do {
877 if (!strncmp
878 (input->buffer + start, e1->text, end - start)) {
879 if (e1->text[end - start] == Par) {
880 if (need_redraw) {
881 register int c1, c2, si;
883 for (si = end - start + 1;
884 (c1 = last_text[si]) &&
885 (c2 = e1->text[si]); si++)
886 if (c1 != c2)
887 break;
888 if (low > si)
889 low = si;
890 last_text = e1->text;
891 need_redraw = 2;
892 } else {
893 need_redraw = 1;
894 listbox_select_entry ((WListbox *) (h->
895 current->
896 widget),
897 e1);
898 last_text = e1->text;
902 e1 = e1->next;
903 } while (e != e1);
904 if (need_redraw == 2) {
905 insert_text (input, last_text, low);
906 send_message (h->current->widget, WIDGET_DRAW, 0);
907 } else if (need_redraw == 1) {
908 h->ret_value = B_ENTER;
909 dlg_stop (h);
912 return 1;
914 break;
916 return 0;
919 static int querylist_callback (void *data)
921 return 1;
924 #define DO_INSERTION 1
925 #define DO_QUERY 2
926 /* Returns 1 if the user would like to see us again */
927 static int
928 complete_engine (WInput *in, int what_to_do)
930 if (in->completions && in->point != end)
931 free_completions (in);
932 if (!in->completions){
933 end = in->point;
934 for (start = end ? end - 1 : 0; start > -1; start--)
935 if (strchr (" \t;|<>", in->buffer [start]))
936 break;
937 if (start < end)
938 start++;
939 in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
941 if (in->completions){
942 if (what_to_do & DO_INSERTION) {
943 if (insert_text (in, in->completions [0], strlen (in->completions [0]))){
944 if (in->completions [1])
945 beep ();
946 else
947 free_completions (in);
948 } else
949 beep ();
951 /* FIXME: evil evil evil. We do not go into the query completion engine
952 * because we do not have a Gtk dialog for it. Gtk-ted does not like
953 * this; if we enable this code, it will crash.
955 if ((what_to_do & DO_QUERY) && in->completions [1]) {
956 int maxlen = 0, i, count = 0;
957 int x, y, w, h;
958 int start_x, start_y;
959 char **p, *q;
960 Dlg_head *query_dlg;
961 WListbox *query_list;
963 for (p=in->completions + 1; *p; count++, p++)
964 if ((i = strlen (*p)) > maxlen)
965 maxlen = i;
966 start_x = in->widget.x;
967 start_y = in->widget.y;
968 if (start_y - 2 >= count) {
969 y = start_y - 2 - count;
970 h = 2 + count;
971 } else {
972 if (start_y >= LINES - start_y - 1) {
973 y = 0;
974 h = start_y;
975 } else {
976 y = start_y + 1;
977 h = LINES - start_y - 1;
980 x = start - in->first_shown - 2 + start_x;
981 w = maxlen + 4;
982 if (x + w > COLS)
983 x = COLS - w;
984 if (x < 0)
985 x = 0;
986 if (x + w > COLS)
987 w = COLS;
988 input = in;
989 min_end = end;
990 query_height = h;
991 query_width = w;
992 query_dlg = create_dlg (y, x, query_height, query_width,
993 dialog_colors, query_callback,
994 "[Completion]", NULL, DLG_COMPACT);
995 query_list = listbox_new (1, 1, w - 2, h - 2, 0, querylist_callback, NULL);
996 add_widget (query_dlg, query_list);
997 for (p = in->completions + 1; *p; p++)
998 listbox_add_item (query_list, 0, 0, *p, NULL);
999 run_dlg (query_dlg);
1000 q = NULL;
1001 if (query_dlg->ret_value == B_ENTER){
1002 listbox_get_current (query_list, &q, NULL);
1003 if (q)
1004 insert_text (in, q, strlen (q));
1006 if (q || end != min_end)
1007 free_completions (in);
1008 i = query_dlg->ret_value; /* B_USER if user wants to start over again */
1009 destroy_dlg (query_dlg);
1010 if (i == B_USER)
1011 return 1;
1013 } else
1014 beep ();
1015 return 0;
1018 void complete (WInput *in)
1020 if (in->completions)
1021 while (complete_engine (in, DO_QUERY));
1022 else if (show_all_if_ambiguous){
1023 complete_engine (in, DO_INSERTION);
1024 while (complete_engine (in, DO_QUERY));
1025 } else
1026 complete_engine (in, DO_INSERTION);