1 #!/usr/bin/env python2
2 """
3 builtin_lib.py - Builtins that are bindings to libraries, e.g. GNU readline.
4 """
5 from __future__ import print_function
6
7 from _devbuild.gen import arg_types
8 from _devbuild.gen.syntax_asdl import loc
9 from core.error import e_usage
10 from core import pyutil
11 from core import vm
12 from frontend import flag_spec
13 from mycpp import mylib
14
15 from typing import Optional, TYPE_CHECKING
16 if TYPE_CHECKING:
17 from _devbuild.gen.runtime_asdl import cmd_value
18 from frontend.py_readline import Readline
19 from core.ui import ErrorFormatter
20 from core import shell
21
22
23 class Bind(vm._Builtin):
24 """For :, true, false."""
25
26 def __init__(self, readline, errfmt):
27 # type: (Optional[Readline], ErrorFormatter) -> None
28 self.readline = readline
29 self.errfmt = errfmt
30
31 def Run(self, cmd_val):
32 # type: (cmd_value.Argv) -> int
33 self.errfmt.Print_("warning: bind isn't implemented",
34 blame_loc=cmd_val.arg_locs[0])
35 return 1
36
37
38 class History(vm._Builtin):
39 """Show interactive command history."""
40
41 def __init__(self, readline, sh_files, errfmt, f):
42 # type: (Optional[Readline], shell.ShellFiles, ErrorFormatter, mylib.Writer) -> None
43 self.readline = readline
44 self.sh_files = sh_files
45 self.errfmt = errfmt
46 self.f = f # this hook is for unit testing only
47
48 def Run(self, cmd_val):
49 # type: (cmd_value.Argv) -> int
50 # NOTE: This builtin doesn't do anything in non-interactive mode in bash?
51 # It silently exits zero.
52 # zsh -c 'history' produces an error.
53 readline = self.readline
54 if not readline:
55 e_usage("is disabled because Oil wasn't compiled with 'readline'",
56 loc.Missing)
57
58 attrs, arg_r = flag_spec.ParseCmdVal('history', cmd_val)
59 arg = arg_types.history(attrs.attrs)
60
61 # Clear all history
62 if arg.c:
63 readline.clear_history()
64 return 0
65
66 if arg.a:
67 hist_file = self.sh_files.HistoryFile()
68 if hist_file is None:
69 return 1
70
71 try:
72 readline.write_history_file(hist_file)
73 except (IOError, OSError) as e:
74 self.errfmt.Print_(
75 'Error writing HISTFILE %r: %s' %
76 (hist_file, pyutil.strerror(e)), loc.Missing)
77 return 1
78
79 return 0
80
81 if arg.r:
82 hist_file = self.sh_files.HistoryFile()
83 if hist_file is None:
84 return 1
85
86 try:
87 readline.read_history_file(hist_file)
88 except (IOError, OSError) as e:
89 self.errfmt.Print_(
90 'Error reading HISTFILE %r: %s' %
91 (hist_file, pyutil.strerror(e)), loc.Missing)
92 return 1
93
94 return 0
95
96 # Delete history entry by id number
97 if arg.d >= 0:
98 cmd_index = arg.d - 1
99
100 try:
101 readline.remove_history_item(cmd_index)
102 except ValueError:
103 e_usage("couldn't find item %d" % arg.d, loc.Missing)
104
105 return 0
106
107 # Returns 0 items in non-interactive mode?
108 num_items = readline.get_current_history_length()
109 #log('len = %d', num_items)
110
111 num_arg, num_arg_loc = arg_r.Peek2()
112
113 if num_arg is None:
114 start_index = 1
115 else:
116 try:
117 num_to_show = int(num_arg)
118 except ValueError:
119 e_usage('got invalid argument %r' % num_arg, num_arg_loc)
120 start_index = max(1, num_items + 1 - num_to_show)
121
122 arg_r.Next()
123 if not arg_r.AtEnd():
124 e_usage('got too many arguments', loc.Missing)
125
126 # TODO:
127 # - Exclude lines that don't parse from the history! bash and zsh don't do
128 # that.
129 # - Consolidate multiline commands.
130
131 for i in xrange(start_index, num_items + 1): # 1-based index
132 item = readline.get_history_item(i)
133 self.f.write('%5d %s\n' % (i, item))
134 return 0