-
Notifications
You must be signed in to change notification settings - Fork 182
/
bashline.c
4839 lines (4130 loc) · 132 KB
/
bashline.c
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* bashline.c -- Bash's interface to the readline library. */
/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#if defined (READLINE)
#include "bashtypes.h"
#include "posixstat.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if defined (HAVE_GRP_H)
# include <grp.h>
#endif
#if defined (HAVE_NETDB_H)
# include <netdb.h>
#endif
#include <signal.h>
#include <stdio.h>
#include "chartypes.h"
#include "bashansi.h"
#include "bashintl.h"
#include "shell.h"
#include "input.h"
#include "parser.h"
#include "builtins.h"
#include "bashhist.h"
#include "bashline.h"
#include "execute_cmd.h"
#include "findcmd.h"
#include "pathexp.h"
#include "shmbutil.h"
#include "trap.h"
#include "flags.h"
#include "timer.h"
#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
# include <mbstr.h> /* mbschr */
#endif
#include "builtins/common.h"
#include "builtins/builtext.h" /* for read_builtin */
#include <readline/rlconf.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <readline/rlmbutil.h>
#include <glob/glob.h>
#if defined (ALIAS)
# include "alias.h"
#endif
#if defined (PROGRAMMABLE_COMPLETION)
# include "pcomplete.h"
#endif
/* These should agree with the defines for emacs_mode and vi_mode in
rldefs.h, even though that's not a public readline header file. */
#ifndef EMACS_EDITING_MODE
# define NO_EDITING_MODE -1
# define EMACS_EDITING_MODE 1
# define VI_EDITING_MODE 0
#endif
/* Copied from rldefs.h, since that's not a public readline header file. */
#ifndef FUNCTION_TO_KEYMAP
#if defined (CRAY)
# define FUNCTION_TO_KEYMAP(map, key) (Keymap)((int)map[key].function)
# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)((int)(data))
#else
# define FUNCTION_TO_KEYMAP(map, key) (Keymap)(map[key].function)
# define KEYMAP_TO_FUNCTION(data) (rl_command_func_t *)(data)
#endif
#endif
#define RL_BOOLEAN_VARIABLE_VALUE(s) ((s)[0] == 'o' && (s)[1] == 'n' && (s)[2] == '\0')
#if defined (BRACE_COMPLETION)
extern int bash_brace_completion PARAMS((int, int));
#endif /* BRACE_COMPLETION */
/* To avoid including curses.h/term.h/termcap.h and that whole mess. */
#ifdef _MINIX
extern int tputs PARAMS((const char *string, int nlines, void (*outx)(int)));
#else
extern int tputs PARAMS((const char *string, int nlines, int (*outx)(int)));
#endif
/* Forward declarations */
/* Functions bound to keys in Readline for Bash users. */
static int shell_expand_line PARAMS((int, int));
static int display_shell_version PARAMS((int, int));
static int bash_ignore_filenames PARAMS((char **));
static int bash_ignore_everything PARAMS((char **));
static int bash_progcomp_ignore_filenames PARAMS((char **));
#if defined (BANG_HISTORY)
static char *history_expand_line_internal PARAMS((char *));
static int history_expand_line PARAMS((int, int));
static int tcsh_magic_space PARAMS((int, int));
#endif /* BANG_HISTORY */
#ifdef ALIAS
static int alias_expand_line PARAMS((int, int));
#endif
#if defined (BANG_HISTORY) && defined (ALIAS)
static int history_and_alias_expand_line PARAMS((int, int));
#endif
static int bash_forward_shellword PARAMS((int, int));
static int bash_backward_shellword PARAMS((int, int));
static int bash_kill_shellword PARAMS((int, int));
static int bash_backward_kill_shellword PARAMS((int, int));
static int bash_transpose_shellwords PARAMS((int, int));
static int bash_spell_correct_shellword PARAMS((int, int));
/* Helper functions for Readline. */
static char *restore_tilde PARAMS((char *, char *));
static char *maybe_restore_tilde PARAMS((char *, char *));
static char *bash_filename_rewrite_hook PARAMS((char *, int));
static void bash_directory_expansion PARAMS((char **));
static int bash_filename_stat_hook PARAMS((char **));
static int bash_command_name_stat_hook PARAMS((char **));
static int bash_directory_completion_hook PARAMS((char **));
static int filename_completion_ignore PARAMS((char **));
static int bash_push_line PARAMS((void));
static int executable_completion PARAMS((const char *, int));
static rl_icppfunc_t *save_directory_hook PARAMS((void));
static void restore_directory_hook PARAMS((rl_icppfunc_t));
static int directory_exists PARAMS((const char *, int));
static void cleanup_expansion_error PARAMS((void));
static void maybe_make_readline_line PARAMS((char *));
static void set_up_new_line PARAMS((char *));
static int check_redir PARAMS((int));
static char **attempt_shell_completion PARAMS((const char *, int, int));
static char *variable_completion_function PARAMS((const char *, int));
static char *hostname_completion_function PARAMS((const char *, int));
static char *command_subst_completion_function PARAMS((const char *, int));
static void build_history_completion_array PARAMS((void));
static char *history_completion_generator PARAMS((const char *, int));
static int dynamic_complete_history PARAMS((int, int));
static int bash_dabbrev_expand PARAMS((int, int));
static void initialize_hostname_list PARAMS((void));
static void add_host_name PARAMS((char *));
static void snarf_hosts_from_file PARAMS((char *));
static char **hostnames_matching PARAMS((char *));
static void _ignore_completion_names PARAMS((char **, sh_ignore_func_t *));
static int name_is_acceptable PARAMS((const char *));
static int test_for_directory PARAMS((const char *));
static int test_for_canon_directory PARAMS((const char *));
static int return_zero PARAMS((const char *));
static char *bash_dequote_filename PARAMS((char *, int));
static char *quote_word_break_chars PARAMS((char *));
static int bash_check_expchar PARAMS((char *, int, int *, int *));
static void set_filename_quote_chars PARAMS((int, int, int));
static void set_filename_bstab PARAMS((const char *));
static char *bash_quote_filename PARAMS((char *, int, char *));
#ifdef _MINIX
static void putx PARAMS((int));
#else
static int putx PARAMS((int));
#endif
static int readline_get_char_offset PARAMS((int));
static void readline_set_char_offset PARAMS((int, int *));
static Keymap get_cmd_xmap_from_edit_mode PARAMS((void));
static Keymap get_cmd_xmap_from_keymap PARAMS((Keymap));
static void init_unix_command_map PARAMS((void));
static int isolate_sequence PARAMS((char *, int, int, int *));
static int set_saved_history PARAMS((void));
#if defined (ALIAS)
static int posix_edit_macros PARAMS((int, int));
#endif
static int bash_event_hook PARAMS((void));
#if defined (PROGRAMMABLE_COMPLETION)
static int find_cmd_start PARAMS((int));
static int find_cmd_end PARAMS((int));
static char *find_cmd_name PARAMS((int, int *, int *));
static char *prog_complete_return PARAMS((const char *, int));
static char **prog_complete_matches;
#endif
extern int no_symbolic_links;
extern STRING_INT_ALIST word_token_alist[];
extern sh_timer *read_timeout;
/* SPECIFIC_COMPLETION_FUNCTIONS specifies that we have individual
completion functions which indicate what type of completion should be
done (at or before point) that can be bound to key sequences with
the readline library. */
#define SPECIFIC_COMPLETION_FUNCTIONS
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int bash_specific_completion PARAMS((int, rl_compentry_func_t *));
static int bash_complete_filename_internal PARAMS((int));
static int bash_complete_username_internal PARAMS((int));
static int bash_complete_hostname_internal PARAMS((int));
static int bash_complete_variable_internal PARAMS((int));
static int bash_complete_command_internal PARAMS((int));
static int bash_complete_filename PARAMS((int, int));
static int bash_possible_filename_completions PARAMS((int, int));
static int bash_complete_username PARAMS((int, int));
static int bash_possible_username_completions PARAMS((int, int));
static int bash_complete_hostname PARAMS((int, int));
static int bash_possible_hostname_completions PARAMS((int, int));
static int bash_complete_variable PARAMS((int, int));
static int bash_possible_variable_completions PARAMS((int, int));
static int bash_complete_command PARAMS((int, int));
static int bash_possible_command_completions PARAMS((int, int));
static int completion_glob_pattern PARAMS((char *));
static char *glob_complete_word PARAMS((const char *, int));
static int bash_glob_completion_internal PARAMS((int));
static int bash_glob_complete_word PARAMS((int, int));
static int bash_glob_expand_word PARAMS((int, int));
static int bash_glob_list_expansions PARAMS((int, int));
#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
static int edit_and_execute_command PARAMS((int, int, int, char *));
#if defined (VI_MODE)
static int vi_edit_and_execute_command PARAMS((int, int));
static int bash_vi_complete PARAMS((int, int));
#endif
static int emacs_edit_and_execute_command PARAMS((int, int));
/* Non-zero once initialize_readline () has been called. */
int bash_readline_initialized = 0;
/* If non-zero, we do hostname completion, breaking words at `@' and
trying to complete the stuff after the `@' from our own internal
host list. */
int perform_hostname_completion = 1;
/* If non-zero, we don't do command completion on an empty line. */
int no_empty_command_completion;
/* Set FORCE_FIGNORE if you want to honor FIGNORE even if it ignores the
only possible matches. Set to 0 if you want to match filenames if they
are the only possible matches, even if FIGNORE says to. */
int force_fignore = 1;
/* Perform spelling correction on directory names during word completion */
int dircomplete_spelling = 0;
/* Expand directory names during word/filename completion. */
#if DIRCOMPLETE_EXPAND_DEFAULT
int dircomplete_expand = 1;
int dircomplete_expand_relpath = 1;
#else
int dircomplete_expand = 0;
int dircomplete_expand_relpath = 0;
#endif
/* When non-zero, perform `normal' shell quoting on completed filenames
even when the completed name contains a directory name with a shell
variable reference, so dollar signs in a filename get quoted appropriately.
Set to zero to remove dollar sign (and braces or parens as needed) from
the set of characters that will be quoted. */
int complete_fullquote = 1;
static char *bash_completer_word_break_characters = " \t\n\"'@><=;|&(:";
static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";
/* )) */
static const char *default_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{~"; /*}*/
static char *custom_filename_quote_characters = 0;
static char filename_bstab[256];
static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;
static int dot_in_path = 0;
/* Set to non-zero when dabbrev-expand is running */
static int dabbrev_expand_active = 0;
/* What kind of quoting is performed by bash_quote_filename:
COMPLETE_DQUOTE = double-quoting the filename
COMPLETE_SQUOTE = single_quoting the filename
COMPLETE_BSQUOTE = backslash-quoting special chars in the filename
*/
#define COMPLETE_DQUOTE 1
#define COMPLETE_SQUOTE 2
#define COMPLETE_BSQUOTE 3
static int completion_quoting_style = COMPLETE_BSQUOTE;
/* Flag values for the final argument to bash_default_completion */
#define DEFCOMP_CMDPOS 1
static rl_command_func_t *vi_tab_binding = rl_complete;
/* Change the readline VI-mode keymaps into or out of Posix.2 compliance.
Called when the shell is put into or out of `posix' mode. */
void
posix_readline_initialize (on_or_off)
int on_or_off;
{
static char kseq[2] = { CTRL ('I'), 0 }; /* TAB */
if (on_or_off)
rl_variable_bind ("comment-begin", "#");
#if defined (VI_MODE)
if (on_or_off)
{
vi_tab_binding = rl_function_of_keyseq (kseq, vi_insertion_keymap, (int *)NULL);
rl_bind_key_in_map (CTRL ('I'), rl_insert, vi_insertion_keymap);
}
else
{
if (rl_function_of_keyseq (kseq, vi_insertion_keymap, (int *)NULL) == rl_insert)
rl_bind_key_in_map (CTRL ('I'), vi_tab_binding, vi_insertion_keymap);
}
#endif
}
void
reset_completer_word_break_chars ()
{
rl_completer_word_break_characters = perform_hostname_completion ? savestring (bash_completer_word_break_characters) : savestring (bash_nohostname_word_break_characters);
}
/* When this function returns, rl_completer_word_break_characters points to
dynamically allocated memory. */
int
enable_hostname_completion (on_or_off)
int on_or_off;
{
int old_value;
char *nv, *nval;
const char *at;
old_value = perform_hostname_completion;
if (on_or_off)
{
perform_hostname_completion = 1;
rl_special_prefixes = "$@";
}
else
{
perform_hostname_completion = 0;
rl_special_prefixes = "$";
}
/* Now we need to figure out how to appropriately modify and assign
rl_completer_word_break_characters depending on whether we want
hostname completion on or off. */
/* If this is the first time this has been called
(bash_readline_initialized == 0), use the sames values as before, but
allocate new memory for rl_completer_word_break_characters. */
if (bash_readline_initialized == 0 &&
(rl_completer_word_break_characters == 0 ||
rl_completer_word_break_characters == rl_basic_word_break_characters))
{
if (on_or_off)
rl_completer_word_break_characters = savestring (bash_completer_word_break_characters);
else
rl_completer_word_break_characters = savestring (bash_nohostname_word_break_characters);
}
else
{
/* See if we have anything to do. */
at = strchr (rl_completer_word_break_characters, '@');
if ((at == 0 && on_or_off == 0) || (at != 0 && on_or_off != 0))
return old_value;
/* We have something to do. Do it. */
nval = (char *)xmalloc (strlen (rl_completer_word_break_characters) + 1 + on_or_off);
if (on_or_off == 0)
{
/* Turn it off -- just remove `@' from word break chars. We want
to remove all occurrences of `@' from the char list, so we loop
rather than just copy the rest of the list over AT. */
for (nv = nval, at = rl_completer_word_break_characters; *at; )
if (*at != '@')
*nv++ = *at++;
else
at++;
*nv = '\0';
}
else
{
nval[0] = '@';
strcpy (nval + 1, rl_completer_word_break_characters);
}
free ((void *)rl_completer_word_break_characters);
rl_completer_word_break_characters = nval;
}
return (old_value);
}
/* Called once from parse.y if we are going to use readline. */
void
initialize_readline ()
{
rl_command_func_t *func;
char kseq[2];
if (bash_readline_initialized)
return;
rl_terminal_name = get_string_value ("TERM");
rl_instream = stdin;
rl_outstream = stderr;
/* Allow conditional parsing of the ~/.inputrc file. */
rl_readline_name = "Bash";
/* Add bindable names before calling rl_initialize so they may be
referenced in the various inputrc files. */
rl_add_defun ("shell-expand-line", shell_expand_line, -1);
#ifdef BANG_HISTORY
rl_add_defun ("history-expand-line", history_expand_line, -1);
rl_add_defun ("magic-space", tcsh_magic_space, -1);
#endif
rl_add_defun ("shell-forward-word", bash_forward_shellword, -1);
rl_add_defun ("shell-backward-word", bash_backward_shellword, -1);
rl_add_defun ("shell-kill-word", bash_kill_shellword, -1);
rl_add_defun ("shell-backward-kill-word", bash_backward_kill_shellword, -1);
rl_add_defun ("shell-transpose-words", bash_transpose_shellwords, -1);
rl_add_defun ("spell-correct-word", bash_spell_correct_shellword, -1);
rl_bind_key_if_unbound_in_map ('s', bash_spell_correct_shellword, emacs_ctlx_keymap);
#ifdef ALIAS
rl_add_defun ("alias-expand-line", alias_expand_line, -1);
# ifdef BANG_HISTORY
rl_add_defun ("history-and-alias-expand-line", history_and_alias_expand_line, -1);
# endif
#endif
/* Backwards compatibility. */
rl_add_defun ("insert-last-argument", rl_yank_last_arg, -1);
rl_add_defun ("display-shell-version", display_shell_version, -1);
rl_add_defun ("edit-and-execute-command", emacs_edit_and_execute_command, -1);
#if defined (VI_MODE)
rl_add_defun ("vi-edit-and-execute-command", vi_edit_and_execute_command, -1);
#endif
#if defined (BRACE_COMPLETION)
rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
#endif
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
rl_add_defun ("complete-filename", bash_complete_filename, -1);
rl_add_defun ("possible-filename-completions", bash_possible_filename_completions, -1);
rl_add_defun ("complete-username", bash_complete_username, -1);
rl_add_defun ("possible-username-completions", bash_possible_username_completions, -1);
rl_add_defun ("complete-hostname", bash_complete_hostname, -1);
rl_add_defun ("possible-hostname-completions", bash_possible_hostname_completions, -1);
rl_add_defun ("complete-variable", bash_complete_variable, -1);
rl_add_defun ("possible-variable-completions", bash_possible_variable_completions, -1);
rl_add_defun ("complete-command", bash_complete_command, -1);
rl_add_defun ("possible-command-completions", bash_possible_command_completions, -1);
rl_add_defun ("glob-complete-word", bash_glob_complete_word, -1);
rl_add_defun ("glob-expand-word", bash_glob_expand_word, -1);
rl_add_defun ("glob-list-expansions", bash_glob_list_expansions, -1);
#endif
rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);
rl_add_defun ("dabbrev-expand", bash_dabbrev_expand, -1);
/* Bind defaults before binding our custom shell keybindings. */
if (RL_ISSTATE(RL_STATE_INITIALIZED) == 0)
rl_initialize ();
/* Bind up our special shell functions. */
rl_bind_key_if_unbound_in_map (CTRL('E'), shell_expand_line, emacs_meta_keymap);
#ifdef BANG_HISTORY
rl_bind_key_if_unbound_in_map ('^', history_expand_line, emacs_meta_keymap);
#endif
rl_bind_key_if_unbound_in_map (CTRL ('V'), display_shell_version, emacs_ctlx_keymap);
/* In Bash, the user can switch editing modes with "set -o [vi emacs]",
so it is not necessary to allow C-M-j for context switching. Turn
off this occasionally confusing behaviour. */
kseq[0] = CTRL('J');
kseq[1] = '\0';
func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
if (func == rl_vi_editing_mode)
rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
kseq[0] = CTRL('M');
func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
if (func == rl_vi_editing_mode)
rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
#if defined (VI_MODE)
kseq[0] = CTRL('E');
func = rl_function_of_keyseq (kseq, vi_movement_keymap, (int *)NULL);
if (func == rl_emacs_editing_mode)
rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
#endif
#if defined (BRACE_COMPLETION)
rl_bind_key_if_unbound_in_map ('{', bash_brace_completion, emacs_meta_keymap); /*}*/
#endif /* BRACE_COMPLETION */
#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
rl_bind_key_if_unbound_in_map ('/', bash_complete_filename, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map ('/', bash_possible_filename_completions, emacs_ctlx_keymap);
/* Have to jump through hoops here because there is a default binding for
M-~ (rl_tilde_expand) */
kseq[0] = '~';
kseq[1] = '\0';
func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
if (func == 0 || func == rl_tilde_expand)
rl_bind_keyseq_in_map (kseq, bash_complete_username, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map ('~', bash_possible_username_completions, emacs_ctlx_keymap);
rl_bind_key_if_unbound_in_map ('@', bash_complete_hostname, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map ('@', bash_possible_hostname_completions, emacs_ctlx_keymap);
rl_bind_key_if_unbound_in_map ('$', bash_complete_variable, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map ('$', bash_possible_variable_completions, emacs_ctlx_keymap);
rl_bind_key_if_unbound_in_map ('!', bash_complete_command, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map ('!', bash_possible_command_completions, emacs_ctlx_keymap);
rl_bind_key_if_unbound_in_map ('g', bash_glob_complete_word, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map ('*', bash_glob_expand_word, emacs_ctlx_keymap);
rl_bind_key_if_unbound_in_map ('g', bash_glob_list_expansions, emacs_ctlx_keymap);
#endif /* SPECIFIC_COMPLETION_FUNCTIONS */
kseq[0] = TAB;
kseq[1] = '\0';
func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
if (func == 0 || func == rl_tab_insert)
rl_bind_key_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);
/* Tell the completer that we want a crack first. */
rl_attempted_completion_function = attempt_shell_completion;
/* Tell the completer that we might want to follow symbolic links or
do other expansion on directory names. */
set_directory_hook ();
rl_filename_rewrite_hook = bash_filename_rewrite_hook;
rl_filename_stat_hook = bash_filename_stat_hook;
/* Tell the filename completer we want a chance to ignore some names. */
rl_ignore_some_completions_function = filename_completion_ignore;
/* Bind C-xC-e to invoke emacs and run result as commands. */
rl_bind_key_if_unbound_in_map (CTRL ('E'), emacs_edit_and_execute_command, emacs_ctlx_keymap);
#if defined (VI_MODE)
rl_bind_key_if_unbound_in_map ('v', vi_edit_and_execute_command, vi_movement_keymap);
# if defined (ALIAS)
rl_bind_key_if_unbound_in_map ('@', posix_edit_macros, vi_movement_keymap);
# endif
rl_bind_key_in_map ('\\', bash_vi_complete, vi_movement_keymap);
rl_bind_key_in_map ('*', bash_vi_complete, vi_movement_keymap);
rl_bind_key_in_map ('=', bash_vi_complete, vi_movement_keymap);
#endif
rl_completer_quote_characters = "'\"";
/* This sets rl_completer_word_break_characters and rl_special_prefixes
to the appropriate values, depending on whether or not hostname
completion is enabled. */
enable_hostname_completion (perform_hostname_completion);
/* characters that need to be quoted when appearing in filenames. */
rl_filename_quote_characters = default_filename_quote_characters;
set_filename_bstab (rl_filename_quote_characters);
rl_filename_quoting_function = bash_quote_filename;
rl_filename_dequoting_function = bash_dequote_filename;
rl_char_is_quoted_p = char_is_quoted;
/* Add some default bindings for the "shellwords" functions, roughly
parallelling the default word bindings in emacs mode. */
rl_bind_key_if_unbound_in_map (CTRL('B'), bash_backward_shellword, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map (CTRL('D'), bash_kill_shellword, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map (CTRL('F'), bash_forward_shellword, emacs_meta_keymap);
rl_bind_key_if_unbound_in_map (CTRL('T'), bash_transpose_shellwords, emacs_meta_keymap);
#if 0
/* This is superfluous and makes it impossible to use tab completion in
vi mode even when explicitly binding it in ~/.inputrc. sv_strict_posix()
should already have called posix_readline_initialize() when
posixly_correct was set. */
if (posixly_correct)
posix_readline_initialize (1);
#endif
bash_readline_initialized = 1;
}
void
bashline_reinitialize ()
{
bash_readline_initialized = 0;
}
void
bashline_set_event_hook ()
{
rl_signal_event_hook = bash_event_hook;
}
void
bashline_reset_event_hook ()
{
rl_signal_event_hook = 0;
}
/* On Sun systems at least, rl_attempted_completion_function can end up
getting set to NULL, and rl_completion_entry_function set to do command
word completion if Bash is interrupted while trying to complete a command
word. This just resets all the completion functions to the right thing.
It's called from throw_to_top_level(). */
void
bashline_reset ()
{
tilde_initialize ();
rl_attempted_completion_function = attempt_shell_completion;
rl_completion_entry_function = NULL;
rl_ignore_some_completions_function = filename_completion_ignore;
complete_fullquote = 1;
rl_filename_quote_characters = default_filename_quote_characters;
set_filename_bstab (rl_filename_quote_characters);
set_directory_hook ();
rl_filename_stat_hook = bash_filename_stat_hook;
bashline_reset_event_hook ();
rl_sort_completion_matches = 1;
}
/* Contains the line to push into readline. */
static char *push_to_readline = (char *)NULL;
/* Push the contents of push_to_readline into the
readline buffer. */
static int
bash_push_line ()
{
if (push_to_readline)
{
rl_insert_text (push_to_readline);
free (push_to_readline);
push_to_readline = (char *)NULL;
rl_startup_hook = old_rl_startup_hook;
}
return 0;
}
/* Call this to set the initial text for the next line to read
from readline. */
int
bash_re_edit (line)
char *line;
{
FREE (push_to_readline);
push_to_readline = savestring (line);
old_rl_startup_hook = rl_startup_hook;
rl_startup_hook = bash_push_line;
return (0);
}
static int
display_shell_version (count, c)
int count, c;
{
rl_crlf ();
show_shell_version (0);
putc ('\r', rl_outstream);
fflush (rl_outstream);
rl_on_new_line ();
rl_redisplay ();
return 0;
}
/* **************************************************************** */
/* */
/* Readline Stuff */
/* */
/* **************************************************************** */
/* If the user requests hostname completion, then simply build a list
of hosts, and complete from that forever more, or at least until
HOSTFILE is unset. */
/* THIS SHOULD BE A STRINGLIST. */
/* The kept list of hostnames. */
static char **hostname_list = (char **)NULL;
/* The physical size of the above list. */
static int hostname_list_size;
/* The number of hostnames in the above list. */
static int hostname_list_length;
/* Whether or not HOSTNAME_LIST has been initialized. */
int hostname_list_initialized = 0;
/* Initialize the hostname completion table. */
static void
initialize_hostname_list ()
{
char *temp;
temp = get_string_value ("HOSTFILE");
if (temp == 0)
temp = get_string_value ("hostname_completion_file");
if (temp == 0)
temp = DEFAULT_HOSTS_FILE;
snarf_hosts_from_file (temp);
if (hostname_list)
hostname_list_initialized++;
}
/* Add NAME to the list of hosts. */
static void
add_host_name (name)
char *name;
{
if (hostname_list_length + 2 > hostname_list_size)
{
hostname_list_size = (hostname_list_size + 32) - (hostname_list_size % 32);
hostname_list = strvec_resize (hostname_list, hostname_list_size);
}
hostname_list[hostname_list_length++] = savestring (name);
hostname_list[hostname_list_length] = (char *)NULL;
}
#define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))
static void
snarf_hosts_from_file (filename)
char *filename;
{
FILE *file;
char *temp, buffer[256], name[256];
register int i, start;
file = fopen (filename, "r");
if (file == 0)
return;
while (temp = fgets (buffer, 255, file))
{
/* Skip to first character. */
for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++)
;
/* If comment or blank line, ignore. */
if (buffer[i] == '\0' || buffer[i] == '#')
continue;
/* If `preprocessor' directive, do the include. */
if (strncmp (buffer + i, "$include ", 9) == 0)
{
char *incfile, *t;
/* Find start of filename. */
for (incfile = buffer + i + 9; *incfile && whitespace (*incfile); incfile++)
;
/* Find end of filename. */
for (t = incfile; *t && cr_whitespace (*t) == 0; t++)
;
*t = '\0';
snarf_hosts_from_file (incfile);
continue;
}
/* Skip internet address if present. */
if (DIGIT (buffer[i]))
for (; buffer[i] && cr_whitespace (buffer[i]) == 0; i++);
/* Gobble up names. Each name is separated with whitespace. */
while (buffer[i])
{
for (; cr_whitespace (buffer[i]); i++)
;
if (buffer[i] == '\0' || buffer[i] == '#')
break;
/* Isolate the current word. */
for (start = i; buffer[i] && cr_whitespace (buffer[i]) == 0; i++)
;
if (i == start)
continue;
strncpy (name, buffer + start, i - start);
name[i - start] = '\0';
add_host_name (name);
}
}
fclose (file);
}
/* Return the hostname list. */
char **
get_hostname_list ()
{
if (hostname_list_initialized == 0)
initialize_hostname_list ();
return (hostname_list);
}
void
clear_hostname_list ()
{
register int i;
if (hostname_list_initialized == 0)
return;
for (i = 0; i < hostname_list_length; i++)
free (hostname_list[i]);
hostname_list_length = hostname_list_initialized = 0;
}
/* Return a NULL terminated list of hostnames which begin with TEXT.
Initialize the hostname list the first time if necessary.
The array is malloc ()'ed, but not the individual strings. */
static char **
hostnames_matching (text)
char *text;
{
register int i, len, nmatch, rsize;
char **result;
if (hostname_list_initialized == 0)
initialize_hostname_list ();
if (hostname_list_initialized == 0)
return ((char **)NULL);
/* Special case. If TEXT consists of nothing, then the whole list is
what is desired. */
if (*text == '\0')
{
result = strvec_create (1 + hostname_list_length);
for (i = 0; i < hostname_list_length; i++)
result[i] = hostname_list[i];
result[i] = (char *)NULL;
return (result);
}
/* Scan until found, or failure. */
len = strlen (text);
result = (char **)NULL;
for (i = nmatch = rsize = 0; i < hostname_list_length; i++)
{
if (STREQN (text, hostname_list[i], len) == 0)
continue;
/* OK, it matches. Add it to the list. */
if (nmatch >= (rsize - 1))
{
rsize = (rsize + 16) - (rsize % 16);
result = strvec_resize (result, rsize);
}
result[nmatch++] = hostname_list[i];
}
if (nmatch)
result[nmatch] = (char *)NULL;
return (result);
}
/* This vi mode command causes VI_EDIT_COMMAND to be run on the current
command being entered (if no explicit argument is given), otherwise on
a command from the history file. */
#define VI_EDIT_COMMAND "fc -e \"${VISUAL:-${EDITOR:-vi}}\""
#define EMACS_EDIT_COMMAND "fc -e \"${VISUAL:-${EDITOR:-emacs}}\""
#define POSIX_VI_EDIT_COMMAND "fc -e vi"
static int
edit_and_execute_command (count, c, editing_mode, edit_command)
int count, c, editing_mode;
char *edit_command;
{
char *command, *metaval;
int r, rrs, metaflag;
sh_parser_state_t ps;
rrs = rl_readline_state;
saved_command_line_count = current_command_line_count;
/* Accept the current line. */
rl_newline (1, c);
if (rl_explicit_arg)
{
command = (char *)xmalloc (strlen (edit_command) + 8);
sprintf (command, "%s %d", edit_command, count);
}
else
{
/* Take the command we were just editing, add it to the history file,
then call fc to operate on it. We have to add a dummy command to
the end of the history because fc ignores the last command (assumes
it's supposed to deal with the command before the `fc'). */
/* This breaks down when using command-oriented history and are not
finished with the command, so we should not ignore the last command */
using_history ();
current_command_line_count++; /* for rl_newline above */
bash_add_history (rl_line_buffer);
current_command_line_count = 0; /* for dummy history entry */
bash_add_history ("");
history_lines_this_session++;
using_history ();
command = savestring (edit_command);
}
metaval = rl_variable_value ("input-meta");
metaflag = RL_BOOLEAN_VARIABLE_VALUE (metaval);
if (rl_deprep_term_function)
(*rl_deprep_term_function) ();
rl_clear_signals ();
save_parser_state (&ps);
r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
restore_parser_state (&ps);
/* if some kind of reset_parser was called, undo it. */
reset_readahead_token ();
if (rl_prep_term_function)
(*rl_prep_term_function) (metaflag);
rl_set_signals ();
current_command_line_count = saved_command_line_count;
/* Now erase the contents of the current line and undo the effects of the