OILS / builtin / io_osh.py View on Github | oils.pub

142 lines, 96 significant
1from __future__ import print_function
2
3from _devbuild.gen import arg_types
4from _devbuild.gen.id_kind_asdl import Id
5from _devbuild.gen.value_asdl import (value, value_t)
6from builtin import read_osh
7from core import optview
8from core import pyos
9from core import state
10from core import vm
11from frontend import flag_util
12from frontend import match
13from frontend import typed_args
14from mycpp import mylib
15from mycpp.mylib import log
16from osh import word_compile
17
18import posix_ as posix
19
20from typing import List, Dict, TYPE_CHECKING
21if TYPE_CHECKING:
22 from _devbuild.gen.runtime_asdl import cmd_value
23 from display import ui
24 from osh import cmd_eval
25
26_ = log
27
28
29class Echo(vm._Builtin):
30 """echo builtin.
31
32 shopt -s simple_echo disables -e and -n.
33 """
34
35 def __init__(self, exec_opts):
36 # type: (optview.Exec) -> None
37 self.exec_opts = exec_opts
38 self.f = mylib.Stdout()
39
40 # Reuse this constant instance
41 self.simple_flag = None # type: arg_types.echo
42
43 def _SimpleFlag(self):
44 # type: () -> arg_types.echo
45 """For arg.e and arg.n without parsing."""
46 if self.simple_flag is None:
47 attrs = {} # type: Dict[str, value_t]
48 attrs['e'] = value.Bool(False)
49 attrs['n'] = value.Bool(False)
50 self.simple_flag = arg_types.echo(attrs)
51 return self.simple_flag
52
53 def Run(self, cmd_val):
54 # type: (cmd_value.Argv) -> int
55 argv = cmd_val.argv[1:]
56
57 if self.exec_opts.simple_echo():
58 typed_args.DoesNotAccept(cmd_val.proc_args) # Disallow echo (42)
59 arg = self._SimpleFlag() # Avoid parsing -e -n
60 else:
61 attrs, arg_r = flag_util.ParseLikeEcho('echo', cmd_val)
62 arg = arg_types.echo(attrs.attrs)
63 argv = arg_r.Rest()
64
65 backslash_c = False # \c terminates input
66
67 if arg.e:
68 new_argv = [] # type: List[str]
69 for a in argv:
70 parts = [] # type: List[str]
71 lex = match.EchoLexer(a)
72 while not backslash_c:
73 id_, s = lex.Next()
74 if id_ == Id.Eol_Tok: # Note: This is really a NUL terminator
75 break
76
77 p = word_compile.EvalCStringToken(id_, s)
78
79 # Unusual behavior: '\c' prints what is there and aborts
80 # processing!
81 if p is None:
82 backslash_c = True
83 break
84
85 parts.append(p)
86
87 new_argv.append(''.join(parts))
88 if backslash_c: # no more args either
89 break
90
91 # Replace it
92 argv = new_argv
93
94 buf = mylib.BufWriter()
95
96 #log('echo argv %s', argv)
97 for i, a in enumerate(argv):
98 if i != 0:
99 buf.write(' ') # arg separator
100 buf.write(a)
101
102 if not arg.n and not backslash_c:
103 buf.write('\n')
104
105 self.f.write(buf.getvalue())
106 return 0
107
108
109class MapFile(vm._Builtin):
110 """Mapfile / readarray."""
111
112 def __init__(self, mem, errfmt, cmd_ev):
113 # type: (state.Mem, ui.ErrorFormatter, cmd_eval.CommandEvaluator) -> None
114 self.mem = mem
115 self.errfmt = errfmt
116 self.cmd_ev = cmd_ev
117
118 def Run(self, cmd_val):
119 # type: (cmd_value.Argv) -> int
120 attrs, arg_r = flag_util.ParseCmdVal('mapfile', cmd_val)
121 arg = arg_types.mapfile(attrs.attrs)
122
123 var_name, _ = arg_r.Peek2()
124 if var_name is None:
125 var_name = 'MAPFILE'
126
127 lines = [] # type: List[str]
128 while True:
129 # bash uses this slow algorithm; YSH could provide read --all-lines
130 try:
131 line, eof = read_osh.ReadLineSlowly(self.cmd_ev,
132 with_eol=not arg.t)
133 except pyos.ReadError as e:
134 self.errfmt.PrintMessage("mapfile: read() error: %s" %
135 posix.strerror(e.err_num))
136 return 1
137 if eof:
138 break
139 lines.append(line)
140
141 state.BuiltinSetArray(self.mem, var_name, lines)
142 return 0