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);
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);
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);
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 size = MIN (size, (ssize_t) text_len) + start - end;
987 if (strlen (in->buffer) + size >= (size_t) in->current_max_size)
988 {
989
990 char *narea;
991 Widget *w = WIDGET (in);
992
993 narea = g_try_realloc (in->buffer, in->current_max_size + size + w->cols);
994 if (narea != NULL)
995 {
996 in->buffer = narea;
997 in->current_max_size += size + w->cols;
998 }
999 }
1000 if (strlen (in->buffer) + 1 < (size_t) in->current_max_size)
1001 {
1002 if (size != 0)
1003 memmove (in->buffer + end + size, in->buffer + end, strlen (&in->buffer[end]) + 1);
1004 memmove (in->buffer + start, text, size - (start - end));
1005 in->point += str_length (in->buffer) - buff_len;
1006 input_update (in, TRUE);
1007 end += size;
1008 }
1009
1010 return size != 0;
1011 }
1012
1013
1014
1015 static cb_ret_t
1016 complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1017 {
1018 static int bl = 0;
1019
1020 WGroup *g = GROUP (w);
1021 WDialog *h = DIALOG (w);
1022
1023 switch (msg)
1024 {
1025 case MSG_KEY:
1026 switch (parm)
1027 {
1028 case KEY_LEFT:
1029 case KEY_RIGHT:
1030 bl = 0;
1031 h->ret_value = 0;
1032 dlg_stop (h);
1033 return MSG_HANDLED;
1034
1035 case KEY_BACKSPACE:
1036 bl = 0;
1037
1038 if (end == 0)
1039 {
1040 h->ret_value = 0;
1041 dlg_stop (h);
1042 }
1043
1044 else if (end == min_end)
1045 {
1046 end = str_get_prev_char (&input->buffer[end]) - input->buffer;
1047 input_handle_char (input, parm);
1048 h->ret_value = B_USER;
1049 dlg_stop (h);
1050 }
1051 else
1052 {
1053 int new_end;
1054 int i;
1055 GList *e;
1056
1057 new_end = str_get_prev_char (&input->buffer[end]) - input->buffer;
1058
1059 for (i = 0, e = listbox_get_first_link (LISTBOX (g->current->data));
1060 e != NULL; i++, e = g_list_next (e))
1061 {
1062 WLEntry *le = LENTRY (e->data);
1063
1064 if (strncmp (input->buffer + start, le->text, new_end - start) == 0)
1065 {
1066 listbox_select_entry (LISTBOX (g->current->data), i);
1067 end = new_end;
1068 input_handle_char (input, parm);
1069 widget_draw (WIDGET (g->current->data));
1070 break;
1071 }
1072 }
1073 }
1074 return MSG_HANDLED;
1075
1076 default:
1077 if (parm < 32 || parm > 255)
1078 {
1079 bl = 0;
1080 if (widget_lookup_key (WIDGET (input), parm) != CK_Complete)
1081 return MSG_NOT_HANDLED;
1082
1083 if (end == min_end)
1084 return MSG_HANDLED;
1085
1086
1087 h->ret_value = B_USER;
1088 dlg_stop (h);
1089 }
1090 else
1091 {
1092 static char buff[MB_LEN_MAX] = "";
1093 GList *e;
1094 int i;
1095 int need_redraw = 0;
1096 int low = 4096;
1097 char *last_text = NULL;
1098
1099 buff[bl++] = (char) parm;
1100 buff[bl] = '\0';
1101
1102 switch (str_is_valid_char (buff, bl))
1103 {
1104 case -1:
1105 bl = 0;
1106 MC_FALLTHROUGH;
1107 case -2:
1108 return MSG_HANDLED;
1109 default:
1110 break;
1111 }
1112
1113 for (i = 0, e = listbox_get_first_link (LISTBOX (g->current->data));
1114 e != NULL; i++, e = g_list_next (e))
1115 {
1116 WLEntry *le = LENTRY (e->data);
1117
1118 if (strncmp (input->buffer + start, le->text, end - start) == 0
1119 && strncmp (&le->text[end - start], buff, bl) == 0)
1120 {
1121 if (need_redraw == 0)
1122 {
1123 need_redraw = 1;
1124 listbox_select_entry (LISTBOX (g->current->data), i);
1125 last_text = le->text;
1126 }
1127 else
1128 {
1129 char *si, *sl;
1130 int si_num = 0;
1131 int sl_num = 0;
1132
1133
1134 for (si = le->text + start; si < le->text + end;
1135 str_next_char (&si), si_num++)
1136 ;
1137 for (sl = last_text + start; sl < last_text + end;
1138 str_next_char (&sl), sl_num++)
1139 ;
1140
1141
1142 si = &le->text[str_offset_to_pos (le->text, ++si_num)];
1143 sl = &last_text[str_offset_to_pos (last_text, ++sl_num)];
1144
1145 while (si[0] != '\0' && sl[0] != '\0')
1146 {
1147 char *nexti, *nextl;
1148
1149 nexti = str_get_next_char (si);
1150 nextl = str_get_next_char (sl);
1151
1152 if (nexti - si != nextl - sl || strncmp (si, sl, nexti - si) != 0)
1153 break;
1154
1155 si = nexti;
1156 sl = nextl;
1157
1158 si_num++;
1159 }
1160
1161 last_text = le->text;
1162
1163 si = &last_text[str_offset_to_pos (last_text, si_num)];
1164 if (low > si - last_text)
1165 low = si - last_text;
1166
1167 need_redraw = 2;
1168 }
1169 }
1170 }
1171
1172 if (need_redraw == 2)
1173 {
1174 insert_text (input, last_text, low);
1175 widget_draw (WIDGET (g->current->data));
1176 }
1177 else if (need_redraw == 1)
1178 {
1179 h->ret_value = B_ENTER;
1180 dlg_stop (h);
1181 }
1182 bl = 0;
1183 }
1184 }
1185 return MSG_HANDLED;
1186
1187 default:
1188 return dlg_default_callback (w, sender, msg, parm, data);
1189 }
1190 }
1191
1192
1193
1194
1195 static gboolean
1196 complete_engine (WInput * in, int what_to_do)
1197 {
1198 if (in->completions != NULL && str_offset_to_pos (in->buffer, in->point) != end)
1199 input_complete_free (in);
1200
1201 if (in->completions == NULL)
1202 complete_engine_fill_completions (in);
1203
1204 if (in->completions == NULL)
1205 tty_beep ();
1206 else
1207 {
1208 if ((what_to_do & DO_INSERTION) != 0
1209 || ((what_to_do & DO_QUERY) != 0 && in->completions[1] == NULL))
1210 {
1211 char *lc_complete = in->completions[0];
1212
1213 if (!insert_text (in, lc_complete, strlen (lc_complete)) || in->completions[1] != NULL)
1214 tty_beep ();
1215 else
1216 input_complete_free (in);
1217 }
1218
1219 if ((what_to_do & DO_QUERY) != 0 && in->completions != NULL && in->completions[1] != NULL)
1220 {
1221 int maxlen = 0, count = 0, i;
1222 int x, y, w, h;
1223 int start_x, start_y;
1224 char **p, *q;
1225 WDialog *complete_dlg;
1226 WListbox *complete_list;
1227
1228 for (p = in->completions + 1; *p != NULL; count++, p++)
1229 {
1230 i = str_term_width1 (*p);
1231 if (i > maxlen)
1232 maxlen = i;
1233 }
1234
1235 start_x = WIDGET (in)->x;
1236 start_y = WIDGET (in)->y;
1237 if (start_y - 2 >= count)
1238 {
1239 y = start_y - 2 - count;
1240 h = 2 + count;
1241 }
1242 else if (start_y >= LINES - start_y - 1)
1243 {
1244 y = 0;
1245 h = start_y;
1246 }
1247 else
1248 {
1249 y = start_y + 1;
1250 h = LINES - start_y - 1;
1251 }
1252 x = start - in->term_first_shown - 2 + start_x;
1253 w = maxlen + 4;
1254 if (x + w > COLS)
1255 x = COLS - w;
1256 if (x < 0)
1257 x = 0;
1258 if (x + w > COLS)
1259 w = COLS;
1260
1261 input = in;
1262 min_end = end;
1263 complete_height = h;
1264 complete_width = w;
1265
1266 complete_dlg =
1267 dlg_create (TRUE, y, x, complete_height, complete_width, WPOS_KEEP_DEFAULT, TRUE,
1268 dialog_colors, complete_callback, NULL, "[Completion]", NULL);
1269 complete_list = listbox_new (1, 1, h - 2, w - 2, FALSE, NULL);
1270 group_add_widget (GROUP (complete_dlg), complete_list);
1271
1272 for (p = in->completions + 1; *p != NULL; p++)
1273 listbox_add_item (complete_list, LISTBOX_APPEND_AT_END, 0, *p, NULL, FALSE);
1274
1275 i = dlg_run (complete_dlg);
1276 q = NULL;
1277 if (i == B_ENTER)
1278 {
1279 listbox_get_current (complete_list, &q, NULL);
1280 if (q != NULL)
1281 insert_text (in, q, strlen (q));
1282 }
1283 if (q != NULL || end != min_end)
1284 input_complete_free (in);
1285 dlg_destroy (complete_dlg);
1286
1287
1288 return (i == B_USER);
1289 }
1290 }
1291
1292 return FALSE;
1293 }
1294
1295
1296
1297
1298
1299
1300 char **
1301 try_complete (char *text, int *lc_start, int *lc_end, input_complete_t flags)
1302 {
1303 try_complete_automation_state_t state;
1304 char **matches = NULL;
1305
1306 memset (&state, 0, sizeof (state));
1307 state.flags = flags;
1308
1309 SHOW_C_CTX ("try_complete");
1310 state.word = g_strndup (text + *lc_start, *lc_end - *lc_start);
1311
1312 state.is_cd = check_is_cd (text, *lc_start, state.flags);
1313
1314
1315
1316
1317
1318 if (!state.is_cd && (flags & INPUT_COMPLETE_COMMANDS) != 0)
1319 try_complete_commands_prepare (&state, text, lc_start);
1320
1321 try_complete_find_start_sign (&state);
1322
1323
1324 if (state.p > state.q && state.p > state.r)
1325 {
1326 SHOW_C_CTX ("try_complete:cmd_backq_subst");
1327 matches = completion_matches (str_cget_next_char (state.p),
1328 command_completion_function,
1329 state.flags & (~INPUT_COMPLETE_FILENAMES));
1330 if (matches != NULL)
1331 *lc_start += str_get_next_char (state.p) - state.word;
1332 }
1333
1334
1335 else if (state.q > state.p && state.q > state.r)
1336 {
1337 SHOW_C_CTX ("try_complete:var_subst");
1338 matches = completion_matches (state.q, variable_completion_function, state.flags);
1339 if (matches != NULL)
1340 *lc_start += state.q - state.word;
1341 }
1342
1343
1344
1345 else if (state.r > state.p && state.r > state.q)
1346 {
1347 SHOW_C_CTX ("try_complete:host_subst");
1348 matches = completion_matches (state.r, hostname_completion_function, state.flags);
1349 if (matches != NULL)
1350 *lc_start += state.r - state.word;
1351 }
1352
1353
1354
1355 if (matches == NULL && *state.word == '~' && (state.flags & INPUT_COMPLETE_USERNAMES) != 0
1356 && strchr (state.word, PATH_SEP) == NULL)
1357 {
1358 SHOW_C_CTX ("try_complete:user_subst");
1359 matches = completion_matches (state.word, username_completion_function, state.flags);
1360 }
1361
1362
1363
1364
1365 if (matches == NULL)
1366 matches = try_complete_all_possible (&state, text, lc_start);
1367
1368
1369 if (matches == NULL)
1370 {
1371 state.in_command_position = 0;
1372 matches = try_complete_all_possible (&state, text, lc_start);
1373 }
1374
1375 g_free (state.word);
1376
1377 if (matches != NULL &&
1378 (flags & (INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_SHELL_ESC)) !=
1379 (INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_SHELL_ESC))
1380 {
1381
1382 char **m;
1383
1384 for (m = matches; *m != NULL; m++)
1385 {
1386 char *p;
1387
1388 p = *m;
1389 *m = strutils_shell_escape (*m);
1390 g_free (p);
1391 }
1392 }
1393
1394 return matches;
1395 }
1396
1397
1398
1399 void
1400 complete_engine_fill_completions (WInput * in)
1401 {
1402 char *s;
1403 const char *word_separators;
1404
1405 word_separators = (in->completion_flags & INPUT_COMPLETE_SHELL_ESC) ? " \t;|<>" : "\t;|<>";
1406
1407 end = str_offset_to_pos (in->buffer, in->point);
1408
1409 s = in->buffer;
1410 if (in->point != 0)
1411 {
1412
1413 size_t i;
1414
1415 for (i = in->point - 1; i > 0; i--)
1416 str_next_char (&s);
1417 }
1418
1419 for (; s >= in->buffer; str_prev_char (&s))
1420 {
1421 start = s - in->buffer;
1422 if (strchr (word_separators, *s) != NULL && !strutils_is_char_escaped (in->buffer, s))
1423 break;
1424 }
1425
1426 if (start < end)
1427 {
1428 str_next_char (&s);
1429 start = s - in->buffer;
1430 }
1431
1432 in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
1433 }
1434
1435
1436
1437
1438 void
1439 input_complete (WInput * in)
1440 {
1441 int engine_flags;
1442
1443 if (!str_is_valid_string (in->buffer))
1444 return;
1445
1446 if (in->completions != NULL)
1447 engine_flags = DO_QUERY;
1448 else
1449 {
1450 engine_flags = DO_INSERTION;
1451
1452 if (mc_global.widget.show_all_if_ambiguous)
1453 engine_flags |= DO_QUERY;
1454 }
1455
1456 while (complete_engine (in, engine_flags))
1457 ;
1458 }
1459
1460
1461
1462 void
1463 input_complete_free (WInput * in)
1464 {
1465 g_strfreev (in->completions);
1466 in->completions = NULL;
1467 }
1468
1469