This source file includes following definitions.
- show_c_flags
- filename_completion_function
- username_completion_function
- variable_completion_function
- fetch_hosts
- hostname_completion_function
- command_completion_function
- match_compare
- completion_matches
- check_is_cd
- try_complete_commands_prepare
- try_complete_find_start_sign
- try_complete_all_possible
- insert_text
- complete_callback
- complete_engine
- try_complete
- complete_engine_fill_completions
- input_complete
- input_complete_free
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 #include <config.h>
34
35 #include <ctype.h>
36 #include <limits.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <dirent.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <pwd.h>
44 #include <unistd.h>
45
46 #include "lib/global.h"
47
48 #include "lib/tty/tty.h"
49 #include "lib/tty/key.h"
50 #include "lib/vfs/vfs.h"
51 #include "lib/strescape.h"
52 #include "lib/strutil.h"
53 #include "lib/util.h"
54 #include "lib/widget.h"
55
56
57
58
59 #if (!(defined(__linux__) && defined (__USE_GNU)) && !defined(__CYGWIN__))
60 extern char **environ;
61 #endif
62
63
64
65
66 #ifdef DO_COMPLETION_DEBUG
67 #define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
68 #else
69 #define SHOW_C_CTX(func)
70 #endif
71
72 #define DO_INSERTION 1
73 #define DO_QUERY 2
74
75
76
77 typedef char *CompletionFunction (const char *text, int state, input_complete_t flags);
78
79 typedef struct
80 {
81 size_t in_command_position;
82 char *word;
83 char *p;
84 char *q;
85 char *r;
86 gboolean is_cd;
87 input_complete_t flags;
88 } try_complete_automation_state_t;
89
90
91
92 static char **hosts = NULL;
93 static char **hosts_p = NULL;
94 static int hosts_alloclen = 0;
95
96 static int complete_height, complete_width;
97 static WInput *input;
98 static int min_end;
99 static int start = 0;
100 static int end = 0;
101
102
103
104
105 char **try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags);
106 void complete_engine_fill_completions (WInput * in);
107
108 #ifdef DO_COMPLETION_DEBUG
109
110
111
112 static const char *
113 show_c_flags (input_complete_t flags)
114 {
115 static char s_cf[] = "FHCVUDS";
116
117 s_cf[0] = (flags & INPUT_COMPLETE_FILENAMES) != 0 ? 'F' : ' ';
118 s_cf[1] = (flags & INPUT_COMPLETE_HOSTNAMES) != 0 ? 'H' : ' ';
119 s_cf[2] = (flags & INPUT_COMPLETE_COMMANDS) != 0 ? 'C' : ' ';
120 s_cf[3] = (flags & INPUT_COMPLETE_VARIABLES) != 0 ? 'V' : ' ';
121 s_cf[4] = (flags & INPUT_COMPLETE_USERNAMES) != 0 ? 'U' : ' ';
122 s_cf[5] = (flags & INPUT_COMPLETE_CD) != 0 ? 'D' : ' ';
123 s_cf[6] = (flags & INPUT_COMPLETE_SHELL_ESC) != 0 ? 'S' : ' ';
124
125 return s_cf;
126 }
127 #endif
128
129
130
131 static char *
132 filename_completion_function (const char *text, int state, input_complete_t flags)
133 {
134 static DIR *directory = NULL;
135 static char *filename = NULL;
136 static char *dirname = NULL;
137 static char *users_dirname = NULL;
138 static size_t filename_len = 0;
139 static vfs_path_t *dirname_vpath = NULL;
140
141 gboolean isdir = TRUE, isexec = FALSE;
142 struct vfs_dirent *entry = NULL;
143
144 SHOW_C_CTX ("filename_completion_function");
145
146 if (text != NULL && (flags & INPUT_COMPLETE_SHELL_ESC) != 0)
147 {
148 char *u_text;
149 char *result;
150 char *e_result;
151
152 u_text = strutils_shell_unescape (text);
153
154 result = filename_completion_function (u_text, state, flags & (~INPUT_COMPLETE_SHELL_ESC));
155 g_free (u_text);
156
157 e_result = strutils_shell_escape (result);
158 g_free (result);
159
160 return e_result;
161 }
162
163
164 if (state == 0)
165 {
166 const char *temp;
167
168 g_free (dirname);
169 g_free (filename);
170 g_free (users_dirname);
171 vfs_path_free (dirname_vpath, TRUE);
172
173 if ((*text != '\0') && (temp = strrchr (text, PATH_SEP)) != NULL)
174 {
175 filename = g_strdup (++temp);
176 dirname = g_strndup (text, temp - text);
177 }
178 else
179 {
180 dirname = g_strdup (".");
181 filename = g_strdup (text);
182 }
183
184
185
186
187 users_dirname = dirname;
188 dirname = tilde_expand (dirname);
189 canonicalize_pathname (dirname);
190 dirname_vpath = vfs_path_from_str (dirname);
191
192
193
194
195
196 directory = mc_opendir (dirname_vpath);
197 filename_len = strlen (filename);
198 }
199
200
201
202 while (directory != NULL && (entry = mc_readdir (directory)) != NULL)
203 {
204 if (!str_is_valid_string (entry->d_name))
205 continue;
206
207
208
209 if (filename_len == 0)
210 {
211 if (DIR_IS_DOT (entry->d_name) || DIR_IS_DOTDOT (entry->d_name))
212 continue;
213 }
214 else
215 {
216
217
218 if ((entry->d_name[0] != filename[0]) ||
219 ((NLENGTH (entry)) < filename_len) ||
220 strncmp (filename, entry->d_name, filename_len) != 0)
221 continue;
222 }
223
224 isdir = TRUE;
225 isexec = FALSE;
226
227 {
228 struct stat tempstat;
229 vfs_path_t *tmp_vpath;
230
231 tmp_vpath = vfs_path_build_filename (dirname, entry->d_name, (char *) NULL);
232
233
234 if (mc_stat (tmp_vpath, &tempstat) == 0)
235 {
236 uid_t my_uid;
237 gid_t my_gid;
238
239 my_uid = getuid ();
240 my_gid = getgid ();
241
242 if (!S_ISDIR (tempstat.st_mode))
243 {
244 isdir = FALSE;
245
246 if ((my_uid == 0 && (tempstat.st_mode & 0111) != 0) ||
247 (my_uid == tempstat.st_uid && (tempstat.st_mode & 0100) != 0) ||
248 (my_gid == tempstat.st_gid && (tempstat.st_mode & 0010) != 0) ||
249 (tempstat.st_mode & 0001) != 0)
250 isexec = TRUE;
251 }
252 }
253 else
254 {
255
256 isdir = FALSE;
257 }
258 vfs_path_free (tmp_vpath, TRUE);
259 }
260
261 if ((flags & INPUT_COMPLETE_COMMANDS) != 0 && (isexec || isdir))
262 break;
263 if ((flags & INPUT_COMPLETE_CD) != 0 && isdir)
264 break;
265 if ((flags & INPUT_COMPLETE_FILENAMES) != 0)
266 break;
267 }
268
269 if (entry == NULL)
270 {
271 if (directory != NULL)
272 {
273 mc_closedir (directory);
274 directory = NULL;
275 }
276 MC_PTR_FREE (dirname);
277 vfs_path_free (dirname_vpath, TRUE);
278 dirname_vpath = NULL;
279 MC_PTR_FREE (filename);
280 MC_PTR_FREE (users_dirname);
281 return NULL;
282 }
283
284 {
285 GString *temp;
286
287 temp = g_string_sized_new (16);
288
289 if (users_dirname != NULL && (users_dirname[0] != '.' || users_dirname[1] != '\0'))
290 {
291 g_string_append (temp, users_dirname);
292
293
294 if (!IS_PATH_SEP (temp->str[temp->len - 1]))
295 g_string_append_c (temp, PATH_SEP);
296 }
297 g_string_append (temp, entry->d_name);
298 if (isdir)
299 g_string_append_c (temp, PATH_SEP);
300
301 return g_string_free (temp, FALSE);
302 }
303 }
304
305
306
307
308
309 static char *
310 username_completion_function (const char *text, int state, input_complete_t flags)
311 {
312 static struct passwd *entry = NULL;
313 static size_t userlen = 0;
314
315 (void) flags;
316 SHOW_C_CTX ("username_completion_function");
317
318 if (text[0] == '\\' && text[1] == '~')
319 text++;
320 if (state == 0)
321 {
322 setpwent ();
323 userlen = strlen (text + 1);
324 }
325
326 while ((entry = getpwent ()) != NULL)
327 {
328
329 if (userlen == 0)
330 break;
331 if (text[1] == entry->pw_name[0] && strncmp (text + 1, entry->pw_name, userlen) == 0)
332 break;
333 }
334
335 if (entry != NULL)
336 return g_strconcat ("~", entry->pw_name, PATH_SEP_STR, (char *) NULL);
337
338 endpwent ();
339 return NULL;
340 }
341
342
343
344
345
346 static char *
347 variable_completion_function (const char *text, int state, input_complete_t flags)
348 {
349 static char **env_p = NULL;
350 static gboolean isbrace = FALSE;
351 static size_t varlen = 0;
352 const char *p = NULL;
353
354 (void) flags;
355 SHOW_C_CTX ("variable_completion_function");
356
357 if (state == 0)
358 {
359 isbrace = (text[1] == '{');
360 varlen = strlen (text + 1 + isbrace);
361 env_p = environ;
362 }
363
364 while (*env_p != NULL)
365 {
366 p = strchr (*env_p, '=');
367 if (p != NULL && ((size_t) (p - *env_p) >= varlen)
368 && strncmp (text + 1 + isbrace, *env_p, varlen) == 0)
369 break;
370 env_p++;
371 }
372
373 if (*env_p == NULL)
374 return NULL;
375
376 {
377 GString *temp;
378
379 temp = g_string_new_len (*env_p, p - *env_p);
380
381 if (isbrace)
382 {
383 g_string_prepend_c (temp, '{');
384 g_string_append_c (temp, '}');
385 }
386 g_string_prepend_c (temp, '$');
387
388 env_p++;
389
390 return g_string_free (temp, FALSE);
391 }
392 }
393
394
395
396 static void
397 fetch_hosts (const char *filename)
398 {
399 FILE *file;
400 char buffer[256];
401 char *name;
402 char *lc_start;
403 char *bi;
404
405 file = fopen (filename, "r");
406 if (file == NULL)
407 return;
408
409 while (fgets (buffer, sizeof (buffer) - 1, file) != NULL)
410 {
411
412 for (bi = buffer; bi[0] != '\0' && str_isspace (bi); str_next_char (&bi))
413 ;
414
415
416 if (bi[0] == '#')
417 continue;
418
419
420 if (strncmp (bi, "$include ", 9) == 0)
421 {
422 char *includefile, *t;
423
424
425 includefile = bi + 9;
426 while (*includefile != '\0' && whitespace (*includefile))
427 includefile++;
428 t = includefile;
429
430
431 while (t[0] != '\0' && !str_isspace (t))
432 str_next_char (&t);
433 *t = '\0';
434
435 fetch_hosts (includefile);
436 continue;
437 }
438
439
440 while (bi[0] != '\0' && !str_isspace (bi))
441 str_next_char (&bi);
442
443
444 while (bi[0] != '\0' && bi[0] != '#')
445 {
446 while (bi[0] != '\0' && str_isspace (bi))
447 str_next_char (&bi);
448 if (bi[0] == '#')
449 continue;
450 for (lc_start = bi; bi[0] != '\0' && !str_isspace (bi); str_next_char (&bi))
451 ;
452
453 if (bi == lc_start)
454 continue;
455
456 name = g_strndup (lc_start, bi - lc_start);
457
458 {
459 char **host_p;
460 int j;
461
462 j = hosts_p - hosts;
463
464 if (j >= hosts_alloclen)
465 {
466 hosts_alloclen += 30;
467 hosts = g_renew (char *, hosts, hosts_alloclen + 1);
468 hosts_p = hosts + j;
469 }
470
471 for (host_p = hosts; host_p < hosts_p; host_p++)
472 if (strcmp (name, *host_p) == 0)
473 break;
474
475 if (host_p == hosts_p)
476 {
477 *(hosts_p++) = name;
478 *hosts_p = NULL;
479 }
480 else
481 g_free (name);
482 }
483 }
484 }
485
486 fclose (file);
487 }
488
489
490
491 static char *
492 hostname_completion_function (const char *text, int state, input_complete_t flags)
493 {
494 static char **host_p = NULL;
495 static size_t textstart = 0;
496 static size_t textlen = 0;
497
498 (void) flags;
499 SHOW_C_CTX ("hostname_completion_function");
500
501 if (state == 0)
502 {
503 const char *p;
504
505 g_strfreev (hosts);
506 hosts_alloclen = 30;
507 hosts = g_new (char *, hosts_alloclen + 1);
508 *hosts = NULL;
509 hosts_p = hosts;
510 p = getenv ("HOSTFILE");
511 fetch_hosts (p != NULL ? p : "/etc/hosts");
512 host_p = hosts;
513 textstart = (*text == '@') ? 1 : 0;
514 textlen = strlen (text + textstart);
515 }
516
517 for (; *host_p != NULL; host_p++)
518 {
519 if (textlen == 0)
520 break;
521 if (strncmp (text + textstart, *host_p, textlen) == 0)
522 break;
523 }
524
525 if (*host_p == NULL)
526 {
527 g_strfreev (hosts);
528 hosts = NULL;
529 return NULL;
530 }
531
532 {
533 GString *temp;
534
535 temp = g_string_sized_new (8);
536
537 if (textstart != 0)
538 g_string_append_c (temp, '@');
539 g_string_append (temp, *host_p);
540 host_p++;
541
542 return g_string_free (temp, FALSE);
543 }
544 }
545
546
547
548
549
550
551
552
553
554 static char *
555 command_completion_function (const char *text, int state, input_complete_t flags)
556 {
557 static const char *path_end = NULL;
558 static gboolean isabsolute = FALSE;
559 static int phase = 0;
560 static size_t text_len = 0;
561 static const char *const *words = NULL;
562 static char *path = NULL;
563 static char *cur_path = NULL;
564 static char *cur_word = NULL;
565 static int init_state = 0;
566 static const char *const bash_reserved[] = {
567 "if", "then", "else", "elif", "fi", "case", "esac", "for",
568 "select", "while", "until", "do", "done", "in", "function", 0
569 };
570 static const char *const bash_builtins[] = {
571 "alias", "bg", "bind", "break", "builtin", "cd", "command",
572 "continue", "declare", "dirs", "echo", "enable", "eval",
573 "exec", "exit", "export", "fc", "fg", "getopts", "hash",
574 "help", "history", "jobs", "kill", "let", "local", "logout",
575 "popd", "pushd", "pwd", "read", "readonly", "return", "set",
576 "shift", "source", "suspend", "test", "times", "trap", "type",
577 "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0
578 };
579
580 char *u_text;
581 char *p, *found;
582
583 SHOW_C_CTX ("command_completion_function");
584
585 if ((flags & INPUT_COMPLETE_COMMANDS) == 0)
586 return NULL;
587
588 u_text = strutils_shell_unescape (text);
589 flags &= ~INPUT_COMPLETE_SHELL_ESC;
590
591 if (state == 0)
592 {
593 isabsolute = strchr (u_text, PATH_SEP) != NULL;
594 if (!isabsolute)
595 {
596 words = bash_reserved;
597 phase = 0;
598 text_len = strlen (u_text);
599
600 if (path == NULL)
601 {
602 path = g_strdup (getenv ("PATH"));
603 if (path != NULL)
604 {
605 p = path;
606 path_end = strchr (p, '\0');
607 while ((p = strchr (p, PATH_ENV_SEP)) != NULL)
608 *p++ = '\0';
609 }
610 }
611 }
612 }
613
614 if (isabsolute)
615 {
616 p = filename_completion_function (u_text, state, flags);
617
618 if (p != NULL)
619 {
620 char *temp_p = p;
621
622 p = strutils_shell_escape (p);
623 g_free (temp_p);
624 }
625
626 g_free (u_text);
627 return p;
628 }
629
630 found = NULL;
631 switch (phase)
632 {
633 case 0:
634 for (; *words != NULL; words++)
635 if (strncmp (*words, u_text, text_len) == 0)
636 {
637 g_free (u_text);
638 return g_strdup (*(words++));
639 }
640 phase++;
641 words = bash_builtins;
642 MC_FALLTHROUGH;
643 case 1:
644 for (; *words != NULL; words++)
645 if (strncmp (*words, u_text, text_len) == 0)
646 {
647 g_free (u_text);
648 return g_strdup (*(words++));
649 }
650 phase++;
651 if (path == NULL)
652 break;
653 cur_path = path;
654 cur_word = NULL;
655 MC_FALLTHROUGH;
656 case 2:
657 while (found == NULL)
658 {
659 if (cur_word == NULL)
660 {
661 char *expanded;
662
663 if (cur_path >= path_end)
664 break;
665 expanded = tilde_expand (*cur_path != '\0' ? cur_path : ".");
666 cur_word = mc_build_filename (expanded, u_text, (char *) NULL);
667 g_free (expanded);
668 canonicalize_pathname (cur_word);
669 cur_path = strchr (cur_path, '\0') + 1;
670 init_state = state;
671 }
672 found = filename_completion_function (cur_word, state - init_state, flags);
673 if (found == NULL)
674 MC_PTR_FREE (cur_word);
675 }
676 MC_FALLTHROUGH;
677 default:
678 break;
679 }
680
681 if (found == NULL)
682 MC_PTR_FREE (path);
683 else
684 {
685 p = strrchr (found, PATH_SEP);
686 if (p != NULL)
687 {
688 char *tmp = found;
689
690 found = strutils_shell_escape (p + 1);
691 g_free (tmp);
692 }
693 }
694
695 g_free (u_text);
696 return found;
697 }
698
699
700
701 static int
702 match_compare (const void *a, const void *b)
703 {
704 return strcmp (*(char *const *) a, *(char *const *) b);
705 }
706
707
708
709
710
711
712
713
714
715
716 static char **
717 completion_matches (const char *text, CompletionFunction entry_function, input_complete_t flags)
718 {
719
720 size_t match_list_size = 30;
721
722 char **match_list;
723
724 size_t matches = 0;
725
726
727 char *string;
728
729 match_list = g_new (char *, match_list_size + 1);
730 match_list[1] = NULL;
731
732 while ((string = (*entry_function) (text, matches, flags)) != NULL)
733 {
734 if (matches + 1 == match_list_size)
735 {
736 match_list_size += 30;
737 match_list = (char **) g_renew (char *, match_list, match_list_size + 1);
738 }
739 match_list[++matches] = string;
740 match_list[matches + 1] = NULL;
741 }
742
743
744
745 if (matches == 0)
746 MC_PTR_FREE (match_list);
747 else
748 {
749
750 if (matches == 1)
751 {
752 match_list[0] = match_list[1];
753 match_list[1] = NULL;
754 }
755 else
756 {
757 size_t i = 1;
758 int low = 4096;
759 size_t j;
760
761 qsort (match_list + 1, matches, sizeof (char *), match_compare);
762
763
764
765
766
767 j = i + 1;
768 while (j < matches + 1)
769 {
770 char *si, *sj;
771 char *ni, *nj;
772
773 for (si = match_list[i], sj = match_list[j]; si[0] != '\0' && sj[0] != '\0';)
774 {
775
776 ni = str_get_next_char (si);
777 nj = str_get_next_char (sj);
778
779 if (ni - si != nj - sj)
780 break;
781 if (strncmp (si, sj, ni - si) != 0)
782 break;
783
784 si = ni;
785 sj = nj;
786 }
787
788 if (si[0] == '\0' && sj[0] == '\0')
789 {
790 g_free (match_list[j]);
791 j++;
792 if (j > matches)
793 break;
794 continue;
795 }
796 else if (low > si - match_list[i])
797 low = si - match_list[i];
798 if (i + 1 != j)
799 match_list[i + 1] = match_list[j];
800 i++;
801 j++;
802 }
803 matches = i;
804 match_list[matches + 1] = NULL;
805 match_list[0] = g_strndup (match_list[1], low);
806 }
807 }
808
809 return match_list;
810 }
811
812
813
814 static gboolean
815 check_is_cd (const char *text, int lc_start, input_complete_t flags)
816 {
817 const char *p, *q;
818
819 SHOW_C_CTX ("check_is_cd");
820
821 if ((flags & INPUT_COMPLETE_CD) == 0)
822 return FALSE;
823
824
825 p = text;
826 q = text + lc_start;
827 while (p < q && p[0] != '\0' && str_isspace (p))
828 str_cnext_char (&p);
829
830
831 return (p[0] == 'c' && p[1] == 'd' && str_isspace (p + 2) && p + 2 < q);
832 }
833
834
835
836 static void
837 try_complete_commands_prepare (try_complete_automation_state_t * state, char *text, int *lc_start)
838 {
839 const char *command_separator_chars = ";|&{(`";
840 char *ti;
841
842 if (*lc_start == 0)
843 ti = text;
844 else
845 {
846 ti = str_get_prev_char (&text[*lc_start]);
847 while (ti > text && whitespace (ti[0]))
848 str_prev_char (&ti);
849 }
850
851 if (ti == text)
852 state->in_command_position++;
853 else if (strchr (command_separator_chars, ti[0]) != NULL)
854 {
855 state->in_command_position++;
856 if (ti != text)
857 {
858 int this_char, prev_char;
859
860
861
862 this_char = ti[0];
863 prev_char = str_get_prev_char (ti)[0];
864
865
866 if ((this_char == '&' && (prev_char == '<' || prev_char == '>'))
867 || (this_char == '|' && prev_char == '>') || (ti != text
868 && str_get_prev_char (ti)[0] == '\\'))
869 state->in_command_position = 0;
870 }
871 }
872 }
873
874
875
876 static void
877 try_complete_find_start_sign (try_complete_automation_state_t * state)
878 {
879 if ((state->flags & INPUT_COMPLETE_COMMANDS) != 0)
880 state->p = strrchr (state->word, '`');
881 if ((state->flags & (INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_VARIABLES)) != 0)
882 {
883 state->q = strrchr (state->word, '$');
884
885
886 if (strutils_is_char_escaped (state->word, state->q))
887 {
888
889 str_move (state->q - 1, state->q);
890
891 state->flags &= ~INPUT_COMPLETE_VARIABLES;
892 state->q = NULL;
893 }
894 }
895 if ((state->flags & INPUT_COMPLETE_HOSTNAMES) != 0)
896 state->r = strrchr (state->word, '@');
897 if (state->q != NULL && state->q[1] == '(' && (state->flags & INPUT_COMPLETE_COMMANDS) != 0)
898 {
899 if (state->q > state->p)
900 state->p = str_get_next_char (state->q);
901 state->q = NULL;
902 }
903 }
904
905
906
907 static char **
908 try_complete_all_possible (try_complete_automation_state_t * state, char *text, int *lc_start)
909 {
910 char **matches = NULL;
911
912 if (state->in_command_position != 0)
913 {
914 SHOW_C_CTX ("try_complete:cmd_subst");
915 matches =
916 completion_matches (state->word, command_completion_function,
917 state->flags & (~INPUT_COMPLETE_FILENAMES));
918 }
919 else if ((state->flags & INPUT_COMPLETE_FILENAMES) != 0)
920 {
921 if (state->is_cd)
922 state->flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
923 SHOW_C_CTX ("try_complete:filename_subst_1");
924 matches = completion_matches (state->word, filename_completion_function, state->flags);
925
926 if (matches == NULL && state->is_cd && !IS_PATH_SEP (*state->word) && *state->word != '~')
927 {
928 state->q = text + *lc_start;
929 for (state->p = text;
930 *state->p != '\0' && state->p < state->q && whitespace (*state->p);
931 str_next_char (&state->p))
932 ;
933 if (strncmp (state->p, "cd", 2) == 0)
934 for (state->p += 2;
935 *state->p != '\0' && state->p < state->q && whitespace (*state->p);
936 str_next_char (&state->p))
937 ;
938 if (state->p == state->q)
939 {
940 char *cdpath_ref, *cdpath;
941 char c;
942
943 cdpath_ref = g_strdup (getenv ("CDPATH"));
944 cdpath = cdpath_ref;
945 c = (cdpath == NULL) ? '\0' : ':';
946
947 while (matches == NULL && c == ':')
948 {
949 char *s;
950
951 s = strchr (cdpath, ':');
952
953 if (s == NULL)
954 s = strchr (cdpath, '\0');
955 c = *s;
956 *s = '\0';
957 if (*cdpath != '\0')
958 {
959 state->r = mc_build_filename (cdpath, state->word, (char *) NULL);
960 SHOW_C_CTX ("try_complete:filename_subst_2");
961 matches =
962 completion_matches (state->r, filename_completion_function,
963 state->flags);
964 g_free (state->r);
965 }
966 *s = c;
967 cdpath = str_get_next_char (s);
968 }
969 g_free (cdpath_ref);
970 }
971 }
972 }
973 return matches;
974 }
975
976
977
978 static gboolean
979 insert_text (WInput * in, char *text, ssize_t size)
980 {
981 size_t text_len;
982 int buff_len;
983
984 text_len = strlen (text);
985 buff_len = str_length (in->buffer);
986 if (size < 0)
987 size = (ssize_t) text_len;
988 else
989 size = MIN (size, (ssize_t) text_len);
990 size += start - end;
991 if (strlen (in->buffer) + size >= (size_t) in->current_max_size)
992 {
993
994 char *narea;
995 Widget *w = WIDGET (in);
996
997 narea = g_try_realloc (in->buffer, in->current_max_size + size + w->cols);
998 if (narea != NULL)
999 {
1000 in->buffer = narea;
1001 in->current_max_size += size + w->cols;
1002 }
1003 }
1004 if (strlen (in->buffer) + 1 < (size_t) in->current_max_size)
1005 {
1006 if (size != 0)
1007 memmove (in->buffer + end + size, in->buffer + end, strlen (&in->buffer[end]) + 1);
1008 memmove (in->buffer + start, text, size - (start - end));
1009 in->point += str_length (in->buffer) - buff_len;
1010 input_update (in, TRUE);
1011 end += size;
1012 }
1013
1014 return size != 0;
1015 }
1016
1017
1018
1019 static cb_ret_t
1020 complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1021 {
1022 static int bl = 0;
1023
1024 WGroup *g = GROUP (w);
1025 WDialog *h = DIALOG (w);
1026
1027 switch (msg)
1028 {
1029 case MSG_KEY:
1030 switch (parm)
1031 {
1032 case KEY_LEFT:
1033 case KEY_RIGHT:
1034 bl = 0;
1035 h->ret_value = 0;
1036 dlg_stop (h);
1037 return MSG_HANDLED;
1038
1039 case KEY_BACKSPACE:
1040 bl = 0;
1041
1042 if (end == 0)
1043 {
1044 h->ret_value = 0;
1045 dlg_stop (h);
1046 }
1047
1048 else if (end == min_end)
1049 {
1050 end = str_get_prev_char (&input->buffer[end]) - input->buffer;
1051 input_handle_char (input, parm);
1052 h->ret_value = B_USER;
1053 dlg_stop (h);
1054 }
1055 else
1056 {
1057 int new_end;
1058 int i;
1059 GList *e;
1060
1061 new_end = str_get_prev_char (&input->buffer[end]) - input->buffer;
1062
1063 for (i = 0, e = listbox_get_first_link (LISTBOX (g->current->data));
1064 e != NULL; i++, e = g_list_next (e))
1065 {
1066 WLEntry *le = LENTRY (e->data);
1067
1068 if (strncmp (input->buffer + start, le->text, new_end - start) == 0)
1069 {
1070 listbox_select_entry (LISTBOX (g->current->data), i);
1071 end = new_end;
1072 input_handle_char (input, parm);
1073 widget_draw (WIDGET (g->current->data));
1074 break;
1075 }
1076 }
1077 }
1078 return MSG_HANDLED;
1079
1080 default:
1081 if (parm < 32 || parm > 255)
1082 {
1083 bl = 0;
1084 if (widget_lookup_key (WIDGET (input), parm) != CK_Complete)
1085 return MSG_NOT_HANDLED;
1086
1087 if (end == min_end)
1088 return MSG_HANDLED;
1089
1090
1091 h->ret_value = B_USER;
1092 dlg_stop (h);
1093 }
1094 else
1095 {
1096 static char buff[MB_LEN_MAX] = "";
1097 GList *e;
1098 int i;
1099 int need_redraw = 0;
1100 int low = 4096;
1101 char *last_text = NULL;
1102
1103 buff[bl++] = (char) parm;
1104 buff[bl] = '\0';
1105
1106 switch (str_is_valid_char (buff, bl))
1107 {
1108 case -1:
1109 bl = 0;
1110 MC_FALLTHROUGH;
1111 case -2:
1112 return MSG_HANDLED;
1113 default:
1114 break;
1115 }
1116
1117 for (i = 0, e = listbox_get_first_link (LISTBOX (g->current->data));
1118 e != NULL; i++, e = g_list_next (e))
1119 {
1120 WLEntry *le = LENTRY (e->data);
1121
1122 if (strncmp (input->buffer + start, le->text, end - start) == 0
1123 && strncmp (&le->text[end - start], buff, bl) == 0)
1124 {
1125 if (need_redraw == 0)
1126 {
1127 need_redraw = 1;
1128 listbox_select_entry (LISTBOX (g->current->data), i);
1129 last_text = le->text;
1130 }
1131 else
1132 {
1133 char *si, *sl;
1134 int si_num = 0;
1135 int sl_num = 0;
1136
1137
1138 for (si = le->text + start; si < le->text + end;
1139 str_next_char (&si), si_num++)
1140 ;
1141 for (sl = last_text + start; sl < last_text + end;
1142 str_next_char (&sl), sl_num++)
1143 ;
1144
1145
1146 si = &le->text[str_offset_to_pos (le->text, ++si_num)];
1147 sl = &last_text[str_offset_to_pos (last_text, ++sl_num)];
1148
1149 while (si[0] != '\0' && sl[0] != '\0')
1150 {
1151 char *nexti, *nextl;
1152
1153 nexti = str_get_next_char (si);
1154 nextl = str_get_next_char (sl);
1155
1156 if (nexti - si != nextl - sl || strncmp (si, sl, nexti - si) != 0)
1157 break;
1158
1159 si = nexti;
1160 sl = nextl;
1161
1162 si_num++;
1163 }
1164
1165 last_text = le->text;
1166
1167 si = &last_text[str_offset_to_pos (last_text, si_num)];
1168 if (low > si - last_text)
1169 low = si - last_text;
1170
1171 need_redraw = 2;
1172 }
1173 }
1174 }
1175
1176 if (need_redraw == 2)
1177 {
1178 insert_text (input, last_text, low);
1179 widget_draw (WIDGET (g->current->data));
1180 }
1181 else if (need_redraw == 1)
1182 {
1183 h->ret_value = B_ENTER;
1184 dlg_stop (h);
1185 }
1186 bl = 0;
1187 }
1188 }
1189 return MSG_HANDLED;
1190
1191 default:
1192 return dlg_default_callback (w, sender, msg, parm, data);
1193 }
1194 }
1195
1196
1197
1198
1199 static gboolean
1200 complete_engine (WInput * in, int what_to_do)
1201 {
1202 if (in->completions != NULL && str_offset_to_pos (in->buffer, in->point) != end)
1203 input_complete_free (in);
1204
1205 if (in->completions == NULL)
1206 complete_engine_fill_completions (in);
1207
1208 if (in->completions == NULL)
1209 tty_beep ();
1210 else
1211 {
1212 if ((what_to_do & DO_INSERTION) != 0
1213 || ((what_to_do & DO_QUERY) != 0 && in->completions[1] == NULL))
1214 {
1215 char *lc_complete = in->completions[0];
1216
1217 if (!insert_text (in, lc_complete, -1) || in->completions[1] != NULL)
1218 tty_beep ();
1219 else
1220 input_complete_free (in);
1221 }
1222
1223 if ((what_to_do & DO_QUERY) != 0 && in->completions != NULL && in->completions[1] != NULL)
1224 {
1225 int maxlen = 0, count = 0, i;
1226 int x, y, w, h;
1227 int start_x, start_y;
1228 char **p, *q;
1229 WDialog *complete_dlg;
1230 WListbox *complete_list;
1231
1232 for (p = in->completions + 1; *p != NULL; count++, p++)
1233 {
1234 i = str_term_width1 (*p);
1235 if (i > maxlen)
1236 maxlen = i;
1237 }
1238
1239 start_x = WIDGET (in)->x;
1240 start_y = WIDGET (in)->y;
1241 if (start_y - 2 >= count)
1242 {
1243 y = start_y - 2 - count;
1244 h = 2 + count;
1245 }
1246 else if (start_y >= LINES - start_y - 1)
1247 {
1248 y = 0;
1249 h = start_y;
1250 }
1251 else
1252 {
1253 y = start_y + 1;
1254 h = LINES - start_y - 1;
1255 }
1256 x = start - in->term_first_shown - 2 + start_x;
1257 w = maxlen + 4;
1258 if (x + w > COLS)
1259 x = COLS - w;
1260 if (x < 0)
1261 x = 0;
1262 if (x + w > COLS)
1263 w = COLS;
1264
1265 input = in;
1266 min_end = end;
1267 complete_height = h;
1268 complete_width = w;
1269
1270 complete_dlg =
1271 dlg_create (TRUE, y, x, complete_height, complete_width, WPOS_KEEP_DEFAULT, TRUE,
1272 dialog_colors, complete_callback, NULL, "[Completion]", NULL);
1273 complete_list = listbox_new (1, 1, h - 2, w - 2, FALSE, NULL);
1274 group_add_widget (GROUP (complete_dlg), complete_list);
1275
1276 for (p = in->completions + 1; *p != NULL; p++)
1277 listbox_add_item (complete_list, LISTBOX_APPEND_AT_END, 0, *p, NULL, FALSE);
1278
1279 i = dlg_run (complete_dlg);
1280 q = NULL;
1281 if (i == B_ENTER)
1282 {
1283 listbox_get_current (complete_list, &q, NULL);
1284 if (q != NULL)
1285 insert_text (in, q, -1);
1286 }
1287 if (q != NULL || end != min_end)
1288 input_complete_free (in);
1289 widget_destroy (WIDGET (complete_dlg));
1290
1291
1292 return (i == B_USER);
1293 }
1294 }
1295
1296 return FALSE;
1297 }
1298
1299
1300
1301
1302
1303
1304 char **
1305 try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
1306 {
1307 try_complete_automation_state_t state;
1308 char **matches = NULL;
1309
1310 memset (&state, 0, sizeof (state));
1311 state.flags = flags;
1312
1313 SHOW_C_CTX ("try_complete");
1314 state.word = g_strndup (text + *lc_start, *lc_end - *lc_start);
1315
1316 state.is_cd = check_is_cd (text, *lc_start, state.flags);
1317
1318
1319
1320
1321
1322 if (!state.is_cd && (flags & INPUT_COMPLETE_COMMANDS) != 0)
1323 try_complete_commands_prepare (&state, text, lc_start);
1324
1325 try_complete_find_start_sign (&state);
1326
1327
1328 if (state.p > state.q && state.p > state.r)
1329 {
1330 SHOW_C_CTX ("try_complete:cmd_backq_subst");
1331 matches = completion_matches (str_cget_next_char (state.p),
1332 command_completion_function,
1333 state.flags & (~INPUT_COMPLETE_FILENAMES));
1334 if (matches != NULL)
1335 *lc_start += str_get_next_char (state.p) - state.word;
1336 }
1337
1338
1339 else if (state.q > state.p && state.q > state.r)
1340 {
1341 SHOW_C_CTX ("try_complete:var_subst");
1342 matches = completion_matches (state.q, variable_completion_function, state.flags);
1343 if (matches != NULL)
1344 *lc_start += state.q - state.word;
1345 }
1346
1347
1348
1349 else if (state.r > state.p && state.r > state.q)
1350 {
1351 SHOW_C_CTX ("try_complete:host_subst");
1352 matches = completion_matches (state.r, hostname_completion_function, state.flags);
1353 if (matches != NULL)
1354 *lc_start += state.r - state.word;
1355 }
1356
1357
1358
1359 if (matches == NULL && *state.word == '~' && (state.flags & INPUT_COMPLETE_USERNAMES) != 0
1360 && strchr (state.word, PATH_SEP) == NULL)
1361 {
1362 SHOW_C_CTX ("try_complete:user_subst");
1363 matches = completion_matches (state.word, username_completion_function, state.flags);
1364 }
1365
1366
1367
1368
1369 if (matches == NULL)
1370 matches = try_complete_all_possible (&state, text, lc_start);
1371
1372
1373 if (matches == NULL)
1374 {
1375 state.in_command_position = 0;
1376 matches = try_complete_all_possible (&state, text, lc_start);
1377 }
1378
1379 g_free (state.word);
1380
1381 if (matches != NULL &&
1382 (flags & (INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_SHELL_ESC)) !=
1383 (INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_SHELL_ESC))
1384 {
1385
1386 char **m;
1387
1388 for (m = matches; *m != NULL; m++)
1389 {
1390 char *p;
1391
1392 p = *m;
1393 *m = strutils_shell_escape (*m);
1394 g_free (p);
1395 }
1396 }
1397
1398 return matches;
1399 }
1400
1401
1402
1403 void
1404 complete_engine_fill_completions (WInput * in)
1405 {
1406 char *s;
1407 const char *word_separators;
1408
1409 word_separators = (in->completion_flags & INPUT_COMPLETE_SHELL_ESC) ? " \t;|<>" : "\t;|<>";
1410
1411 end = str_offset_to_pos (in->buffer, in->point);
1412
1413 s = in->buffer;
1414 if (in->point != 0)
1415 {
1416
1417 size_t i;
1418
1419 for (i = in->point - 1; i > 0; i--)
1420 str_next_char (&s);
1421 }
1422
1423 for (; s >= in->buffer; str_prev_char (&s))
1424 {
1425 start = s - in->buffer;
1426 if (strchr (word_separators, *s) != NULL && !strutils_is_char_escaped (in->buffer, s))
1427 break;
1428 }
1429
1430 if (start < end)
1431 {
1432 str_next_char (&s);
1433 start = s - in->buffer;
1434 }
1435
1436 in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
1437 }
1438
1439
1440
1441
1442 void
1443 input_complete (WInput * in)
1444 {
1445 int engine_flags;
1446
1447 if (!str_is_valid_string (in->buffer))
1448 return;
1449
1450 if (in->completions != NULL)
1451 engine_flags = DO_QUERY;
1452 else
1453 {
1454 engine_flags = DO_INSERTION;
1455
1456 if (mc_global.widget.show_all_if_ambiguous)
1457 engine_flags |= DO_QUERY;
1458 }
1459
1460 while (complete_engine (in, engine_flags))
1461 ;
1462 }
1463
1464
1465
1466 void
1467 input_complete_free (WInput * in)
1468 {
1469 g_strfreev (in->completions);
1470 in->completions = NULL;
1471 }
1472
1473