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