OILS / core / shell.py View on Github | oilshell.org

1252 lines, 799 significant
1"""
2core/shell.py -- Entry point for the shell interpreter.
3"""
4from __future__ import print_function
5
6from errno import ENOENT
7import time as time_
8
9from _devbuild.gen import arg_types
10from _devbuild.gen.option_asdl import option_i, builtin_i
11from _devbuild.gen.syntax_asdl import (loc, source, source_t, IntParamBox,
12 debug_frame, debug_frame_t)
13from _devbuild.gen.runtime_asdl import scope_e
14from _devbuild.gen.value_asdl import (value, value_e, value_t, value_str, Obj)
15from core import alloc
16from core import comp_ui
17from core import dev
18from core import error
19from core import executor
20from core import completion
21from core import main_loop
22from core import optview
23from core import process
24from core import pyutil
25from core import state
26from display import ui
27from core import util
28from core import vm
29
30from frontend import args
31from frontend import flag_def # side effect: flags are defined!
32
33unused1 = flag_def
34from frontend import flag_util
35from frontend import location
36from frontend import reader
37from frontend import parse_lib
38
39from builtin import assign_osh
40from builtin import bracket_osh
41from builtin import completion_osh
42from builtin import completion_ysh
43from builtin import dirs_osh
44from builtin import error_ysh
45from builtin import hay_ysh
46from builtin import io_osh
47from builtin import io_ysh
48from builtin import json_ysh
49from builtin import meta_oils
50from builtin import misc_osh
51from builtin import module_ysh
52from builtin import printf_osh
53from builtin import process_osh
54from builtin import pure_osh
55from builtin import pure_ysh
56from builtin import readline_osh
57from builtin import read_osh
58from builtin import trap_osh
59
60from builtin import func_eggex
61from builtin import func_hay
62from builtin import func_misc
63from builtin import func_reflect
64
65from builtin import method_dict
66from builtin import method_io
67from builtin import method_list
68from builtin import method_other
69from builtin import method_str
70from builtin import method_type
71
72from osh import cmd_eval
73from osh import glob_
74from osh import history
75from osh import prompt
76from osh import sh_expr_eval
77from osh import split
78from osh import word_eval
79
80from mycpp import iolib
81from mycpp import mops
82from mycpp import mylib
83from mycpp.mylib import NewDict, iteritems, print_stderr, log
84from pylib import os_path
85from tools import deps
86from tools import fmt
87from tools import ysh_ify
88from ysh import expr_eval
89
90unused2 = log
91
92import libc
93import posix_ as posix
94
95from typing import List, Dict, Optional, TYPE_CHECKING, cast
96if TYPE_CHECKING:
97 from frontend.py_readline import Readline
98
99if mylib.PYTHON:
100 try:
101 from _devbuild.gen import help_meta # type: ignore
102 except ImportError:
103 help_meta = None
104
105
106def _InitDefaultCompletions(cmd_ev, complete_builtin, comp_lookup):
107 # type: (cmd_eval.CommandEvaluator, completion_osh.Complete, completion.Lookup) -> None
108
109 # register builtins and words
110 complete_builtin.Run(cmd_eval.MakeBuiltinArgv(['-E', '-A', 'command']))
111 # register path completion
112 # Add -o filenames? Or should that be automatic?
113 complete_builtin.Run(cmd_eval.MakeBuiltinArgv(['-D', '-A', 'file']))
114
115
116def _CompletionDemo(comp_lookup):
117 # type: (completion.Lookup) -> None
118
119 # Something for fun, to show off. Also: test that you don't repeatedly hit
120 # the file system / network / coprocess.
121 A1 = completion.TestAction(['foo.py', 'foo', 'bar.py'], 0.0)
122 l = [] # type: List[str]
123 for i in xrange(0, 5):
124 l.append('m%d' % i)
125
126 A2 = completion.TestAction(l, 0.1)
127 C1 = completion.UserSpec([A1, A2], [], [], completion.DefaultPredicate(),
128 '', '')
129 comp_lookup.RegisterName('slowc', {}, C1)
130
131
132def SourceStartupFile(
133 fd_state, # type: process.FdState
134 rc_path, # type: str
135 lang, # type: str
136 parse_ctx, # type: parse_lib.ParseContext
137 cmd_ev, # type: cmd_eval.CommandEvaluator
138 errfmt, # type: ui.ErrorFormatter
139):
140 # type: (...) -> None
141
142 # Right now this is called when the shell is interactive. (Maybe it should
143 # be called on login_shel too.)
144 #
145 # Terms:
146 # - interactive shell: Roughly speaking, no args or -c, and isatty() is true
147 # for stdin and stdout.
148 # - login shell: Started from the top level, e.g. from init or ssh.
149 #
150 # We're not going to copy everything bash does because it's too complex, but
151 # for reference:
152 # https://www.gnu.org/software/bash/manual/bash.html#Bash-Startup-Files
153 # Bash also has --login.
154
155 try:
156 f = fd_state.Open(rc_path)
157 except (IOError, OSError) as e:
158 # TODO: Could warn about nonexistent explicit --rcfile?
159 if e.errno != ENOENT:
160 raise # Goes to top level. Handle this better?
161 return
162
163 arena = parse_ctx.arena
164 rc_line_reader = reader.FileLineReader(f, arena)
165 rc_c_parser = parse_ctx.MakeOshParser(rc_line_reader)
166
167 with alloc.ctx_SourceCode(arena, source.MainFile(rc_path)):
168 # TODO: handle status, e.g. 2 for ParseError
169 unused = main_loop.Batch(cmd_ev, rc_c_parser, errfmt)
170
171 f.close()
172
173
174class ShellOptHook(state.OptHook):
175
176 def __init__(self, readline):
177 # type: (Optional[Readline]) -> None
178 self.readline = readline
179
180 def OnChange(self, opt0_array, opt_name, b):
181 # type: (List[bool], str, bool) -> bool
182 """This method is called whenever an option is changed.
183
184 Returns success or failure.
185 """
186 if opt_name == 'vi' or opt_name == 'emacs':
187 # TODO: Replace with a hook? Just like setting LANG= can have a hook.
188 if self.readline:
189 self.readline.parse_and_bind("set editing-mode " + opt_name)
190 else:
191 print_stderr(
192 "Warning: Can't set option %r because shell wasn't compiled with GNU readline"
193 % opt_name)
194 return False
195
196 # Invert: they are mutually exclusive!
197 if opt_name == 'vi':
198 opt0_array[option_i.emacs] = not b
199 elif opt_name == 'emacs':
200 opt0_array[option_i.vi] = not b
201
202 return True
203
204
205def _AddBuiltinFunc(mem, name, func):
206 # type: (state.Mem, str, vm._Callable) -> None
207 assert isinstance(func, vm._Callable), func
208 mem.AddBuiltin(name, value.BuiltinFunc(func))
209
210
211def InitAssignmentBuiltins(
212 mem, # type: state.Mem
213 procs, # type: state.Procs
214 exec_opts, # type: optview.Exec
215 errfmt, # type: ui.ErrorFormatter
216):
217 # type: (...) -> Dict[int, vm._AssignBuiltin]
218
219 assign_b = {} # type: Dict[int, vm._AssignBuiltin]
220
221 new_var = assign_osh.NewVar(mem, procs, exec_opts, errfmt)
222 assign_b[builtin_i.declare] = new_var
223 assign_b[builtin_i.typeset] = new_var
224 assign_b[builtin_i.local] = new_var
225
226 assign_b[builtin_i.export_] = assign_osh.Export(mem, errfmt)
227 assign_b[builtin_i.readonly] = assign_osh.Readonly(mem, errfmt)
228
229 return assign_b
230
231
232class ShellFiles(object):
233
234 def __init__(self, lang, home_dir, mem, flag):
235 # type: (str, str, state.Mem, arg_types.main) -> None
236 assert lang in ('osh', 'ysh'), lang
237 self.lang = lang
238 self.home_dir = home_dir
239 self.mem = mem
240 self.flag = flag
241
242 def _HistVar(self):
243 # type: () -> str
244 return 'HISTFILE' if self.lang == 'osh' else 'YSH_HISTFILE'
245
246 def _DefaultHistoryFile(self):
247 # type: () -> str
248 return os_path.join(self.home_dir,
249 '.local/share/oils/%s_history' % self.lang)
250
251 def InitAfterLoadingEnv(self):
252 # type: () -> None
253
254 hist_var = self._HistVar()
255 if self.mem.GetValue(hist_var).tag() == value_e.Undef:
256 # Note: if the directory doesn't exist, GNU readline ignores
257 state.SetGlobalString(self.mem, hist_var,
258 self._DefaultHistoryFile())
259
260 def HistoryFile(self):
261 # type: () -> Optional[str]
262 # TODO: In non-strict mode we should try to cast the HISTFILE value to a
263 # string following bash's rules
264
265 UP_val = self.mem.GetValue(self._HistVar())
266 if UP_val.tag() == value_e.Str:
267 val = cast(value.Str, UP_val)
268 return val.s
269 else:
270 # Note: if HISTFILE is an array, bash will return ${HISTFILE[0]}
271 return None
272 #return self._DefaultHistoryFile()
273
274 # TODO: can we recover line information here?
275 # might be useful to show where HISTFILE was set
276 #raise error.Strict("$HISTFILE should only ever be a string", loc.Missing)
277
278
279def Main(
280 lang, # type: str
281 arg_r, # type: args.Reader
282 environ, # type: Dict[str, str]
283 login_shell, # type: bool
284 loader, # type: pyutil._ResourceLoader
285 readline, # type: Optional[Readline]
286):
287 # type: (...) -> int
288 """The full shell lifecycle. Used by bin/osh and bin/ysh.
289
290 Args:
291 lang: 'osh' or 'ysh'
292 login_shell: Was - on argv[0]?
293 loader: to get help, version, grammar, etc.
294 readline: optional GNU readline
295 """
296 # Differences between osh and ysh:
297 # - oshrc vs yshrc
298 # - shopt -s ysh:all
299 # - Prompt
300 # - --help
301
302 argv0 = arg_r.Peek()
303 assert argv0 is not None
304 arg_r.Next()
305
306 assert lang in ('osh', 'ysh'), lang
307
308 try:
309 attrs = flag_util.ParseMore('main', arg_r)
310 except error.Usage as e:
311 print_stderr('%s usage error: %s' % (lang, e.msg))
312 return 2
313 flag = arg_types.main(attrs.attrs)
314
315 arena = alloc.Arena()
316 errfmt = ui.ErrorFormatter()
317
318 if flag.help:
319 util.HelpFlag(loader, '%s-usage' % lang, mylib.Stdout())
320 return 0
321 if flag.version:
322 util.VersionFlag(loader, mylib.Stdout())
323 return 0
324
325 if flag.tool == 'cat-em':
326 paths = arg_r.Rest()
327
328 status = 0
329 for p in paths:
330 try:
331 contents = loader.Get(p)
332 print(contents)
333 except (OSError, IOError):
334 print_stderr("cat-em: %r not found" % p)
335 status = 1
336 return status
337
338 debug_stack = [] # type: List[debug_frame_t]
339 if arg_r.AtEnd():
340 dollar0 = argv0
341 else:
342 dollar0 = arg_r.Peek() # the script name, or the arg after -c
343
344 frame0 = debug_frame.Main(dollar0)
345 debug_stack.append(frame0)
346
347 script_name = arg_r.Peek() # type: Optional[str]
348 arg_r.Next()
349
350 env_dict = NewDict() # type: Dict[str, value_t]
351 mem = state.Mem(dollar0,
352 arg_r.Rest(),
353 arena,
354 debug_stack,
355 env_dict=env_dict)
356
357 opt_hook = ShellOptHook(readline)
358 # Note: only MutableOpts needs mem, so it's not a true circular dep.
359 parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, opt_hook)
360 mem.exec_opts = exec_opts # circular dep
361 mutable_opts.Init()
362
363 # Set these BEFORE processing flags, so they can be overridden.
364 if lang == 'ysh':
365 mutable_opts.SetAnyOption('ysh:all', True)
366
367 pure_osh.SetOptionsFromFlags(mutable_opts, attrs.opt_changes,
368 attrs.shopt_changes)
369
370 version_str = pyutil.GetVersion(loader)
371 state.InitBuiltins(mem, environ, version_str)
372 state.InitDefaultVars(mem)
373
374 if exec_opts.no_copy_env():
375 #if 1:
376 for name, s in iteritems(environ):
377 env_dict[name] = value.Str(s)
378
379 mem.SetNamed(location.LName('ENV'), value.Dict(env_dict),
380 scope_e.GlobalOnly)
381 else:
382 state.CopyVarsFromEnv(mem, environ)
383
384 # PATH PWD SHELLOPTS, etc. must be set after CopyVarsFromEnv()
385 state.InitVarsAfterEnv(mem)
386
387 if attrs.show_options: # special case: sh -o
388 pure_osh.ShowOptions(mutable_opts, [])
389 return 0
390
391 # feedback between runtime and parser
392 aliases = NewDict() # type: Dict[str, str]
393
394 ysh_grammar = pyutil.LoadYshGrammar(loader)
395
396 if flag.do_lossless and not exec_opts.noexec():
397 raise error.Usage('--one-pass-parse requires noexec (-n)', loc.Missing)
398
399 # Tools always use one pass parse
400 # Note: osh --tool syntax-tree is like osh -n --one-pass-parse
401 do_lossless = True if len(flag.tool) else flag.do_lossless
402
403 parse_ctx = parse_lib.ParseContext(arena,
404 parse_opts,
405 aliases,
406 ysh_grammar,
407 do_lossless=do_lossless)
408
409 # Three ParseContext instances SHARE aliases.
410 comp_arena = alloc.Arena()
411 comp_arena.PushSource(source.Unused('completion'))
412 trail1 = parse_lib.Trail()
413 # do_lossless needs to be turned on to complete inside backticks. TODO:
414 # fix the issue where ` gets erased because it's not part of
415 # set_completer_delims().
416 comp_ctx = parse_lib.ParseContext(comp_arena,
417 parse_opts,
418 aliases,
419 ysh_grammar,
420 do_lossless=True)
421 comp_ctx.Init_Trail(trail1)
422
423 hist_arena = alloc.Arena()
424 hist_arena.PushSource(source.Unused('history'))
425 trail2 = parse_lib.Trail()
426 hist_ctx = parse_lib.ParseContext(hist_arena, parse_opts, aliases,
427 ysh_grammar)
428 hist_ctx.Init_Trail(trail2)
429
430 # Deps helps manages dependencies. These dependencies are circular:
431 # - cmd_ev and word_ev, arith_ev -- for command sub, arith sub
432 # - arith_ev and word_ev -- for $(( ${a} )) and $x$(( 1 ))
433 # - cmd_ev and builtins (which execute code, like eval)
434 # - prompt_ev needs word_ev for $PS1, which needs prompt_ev for @P
435 cmd_deps = cmd_eval.Deps()
436 cmd_deps.mutable_opts = mutable_opts
437
438 job_control = process.JobControl()
439 job_list = process.JobList()
440 fd_state = process.FdState(errfmt, job_control, job_list, mem, None, None,
441 exec_opts)
442
443 my_pid = posix.getpid()
444
445 debug_path = ''
446 debug_dir = environ.get('OILS_DEBUG_DIR')
447 if flag.debug_file is not None:
448 # --debug-file takes precedence over OSH_DEBUG_DIR
449 debug_path = flag.debug_file
450 elif debug_dir is not None:
451 debug_path = os_path.join(debug_dir, '%d-osh.log' % my_pid)
452
453 if len(debug_path):
454 # This will be created as an empty file if it doesn't exist, or it could be
455 # a pipe.
456 try:
457 debug_f = util.DebugFile(
458 fd_state.OpenForWrite(debug_path)) # type: util._DebugFile
459 except (IOError, OSError) as e:
460 print_stderr("%s: Couldn't open %r: %s" %
461 (lang, debug_path, posix.strerror(e.errno)))
462 return 2
463 else:
464 debug_f = util.NullDebugFile()
465
466 if flag.xtrace_to_debug_file:
467 trace_f = debug_f
468 else:
469 trace_f = util.DebugFile(mylib.Stderr())
470
471 trace_dir = environ.get('OILS_TRACE_DIR', '')
472 dumps = environ.get('OILS_TRACE_DUMPS', '')
473 streams = environ.get('OILS_TRACE_STREAMS', '')
474 multi_trace = dev.MultiTracer(my_pid, trace_dir, dumps, streams, fd_state)
475
476 tracer = dev.Tracer(parse_ctx, exec_opts, mutable_opts, mem, trace_f,
477 multi_trace)
478 fd_state.tracer = tracer # circular dep
479
480 signal_safe = iolib.InitSignalSafe()
481 trap_state = trap_osh.TrapState(signal_safe)
482
483 waiter = process.Waiter(job_list, exec_opts, signal_safe, tracer)
484 fd_state.waiter = waiter
485
486 cmd_deps.debug_f = debug_f
487
488 now = time_.time()
489 iso_stamp = time_.strftime("%Y-%m-%d %H:%M:%S", time_.localtime(now))
490
491 argv_buf = mylib.BufWriter()
492 dev.PrintShellArgv(arg_r.argv, argv_buf)
493
494 debug_f.writeln('%s [%d] Oils started with argv %s' %
495 (iso_stamp, my_pid, argv_buf.getvalue()))
496 if len(debug_path):
497 debug_f.writeln('Writing logs to %r' % debug_path)
498
499 interp = environ.get('OILS_HIJACK_SHEBANG', '')
500 search_path = state.SearchPath(mem)
501 ext_prog = process.ExternalProgram(interp, fd_state, errfmt, debug_f)
502
503 splitter = split.SplitContext(mem)
504 # TODO: This is instantiation is duplicated in osh/word_eval.py
505 globber = glob_.Globber(exec_opts)
506
507 # This could just be OILS_TRACE_DUMPS='crash:argv0'
508 crash_dump_dir = environ.get('OILS_CRASH_DUMP_DIR', '')
509 cmd_deps.dumper = dev.CrashDumper(crash_dump_dir, fd_state)
510
511 comp_lookup = completion.Lookup()
512
513 # Various Global State objects to work around readline interfaces
514 compopt_state = completion.OptionState()
515
516 comp_ui_state = comp_ui.State()
517 prompt_state = comp_ui.PromptState()
518
519 # The login program is supposed to set $HOME
520 # https://superuser.com/questions/271925/where-is-the-home-environment-variable-set
521 # state.InitMem(mem) must happen first
522 tilde_ev = word_eval.TildeEvaluator(mem, exec_opts)
523 home_dir = tilde_ev.GetMyHomeDir()
524 if home_dir is None:
525 # TODO: print errno from getpwuid()
526 print_stderr("%s: Failed to get home dir from $HOME or getpwuid()" %
527 lang)
528 return 1
529
530 sh_files = ShellFiles(lang, home_dir, mem, flag)
531 sh_files.InitAfterLoadingEnv()
532
533 #
534 # Executor and Evaluators (are circularly dependent)
535 #
536
537 # Global proc namespace. Funcs are defined in the common variable
538 # namespace.
539 procs = state.Procs(mem) # type: state.Procs
540
541 builtins = {} # type: Dict[int, vm._Builtin]
542
543 # e.g. s.startswith()
544 methods = {} # type: Dict[int, Dict[str, vm._Callable]]
545
546 hay_state = hay_ysh.HayState()
547
548 shell_ex = executor.ShellExecutor(mem, exec_opts, mutable_opts, procs,
549 hay_state, builtins, search_path,
550 ext_prog, waiter, tracer, job_control,
551 job_list, fd_state, trap_state, errfmt)
552
553 arith_ev = sh_expr_eval.ArithEvaluator(mem, exec_opts, mutable_opts,
554 parse_ctx, errfmt)
555 bool_ev = sh_expr_eval.BoolEvaluator(mem, exec_opts, mutable_opts,
556 parse_ctx, errfmt)
557 expr_ev = expr_eval.ExprEvaluator(mem, mutable_opts, methods, splitter,
558 errfmt)
559 word_ev = word_eval.NormalWordEvaluator(mem, exec_opts, mutable_opts,
560 tilde_ev, splitter, errfmt)
561
562 assign_b = InitAssignmentBuiltins(mem, procs, exec_opts, errfmt)
563 cmd_ev = cmd_eval.CommandEvaluator(mem, exec_opts, errfmt, procs, assign_b,
564 arena, cmd_deps, trap_state,
565 signal_safe)
566
567 # PromptEvaluator rendering is needed in non-interactive shells for @P.
568 prompt_ev = prompt.Evaluator(lang, version_str, parse_ctx, mem)
569
570 io_methods = NewDict() # type: Dict[str, value_t]
571 io_methods['promptVal'] = value.BuiltinFunc(method_io.PromptVal(prompt_ev))
572
573 # The M/ prefix means it's io->eval()
574 io_methods['M/eval'] = value.BuiltinFunc(
575 method_io.Eval(mem, cmd_ev, method_io.EVAL_NULL))
576 io_methods['M/evalToDict'] = value.BuiltinFunc(
577 method_io.Eval(mem, cmd_ev, method_io.EVAL_DICT))
578 io_methods['M/evalInFrame'] = value.BuiltinFunc(
579 method_io.EvalInFrame(mem, cmd_ev))
580 io_methods['M/evalExpr'] = value.BuiltinFunc(
581 func_reflect.EvalExpr(expr_ev))
582
583 # Identical to command sub
584 io_methods['captureStdout'] = value.BuiltinFunc(
585 method_io.CaptureStdout(mem, shell_ex))
586
587 # TODO:
588 io_methods['time'] = value.BuiltinFunc(method_io.Time())
589 io_methods['strftime'] = value.BuiltinFunc(method_io.Strftime())
590 io_methods['glob'] = value.BuiltinFunc(method_io.Glob())
591
592 io_props = {'stdin': value.Stdin} # type: Dict[str, value_t]
593 io_obj = Obj(Obj(None, io_methods), io_props)
594
595 vm_methods = NewDict() # type: Dict[str, value_t]
596 vm_methods['getFrame'] = value.BuiltinFunc(func_reflect.GetFrame(mem))
597 vm_props = NewDict() # type: Dict[str, value_t]
598 vm_obj = Obj(Obj(None, vm_methods), vm_props)
599
600 # Add basic type objects for flag parser
601 # flag -v --verbose (Bool, help='foo')
602 #
603 # TODO:
604 # - Add other types like Dict, CommandFlag
605 # - Obj(first, rest)
606 # - List() Dict() Obj() can do shallow copy with __call__
607 # - Bool() Int() Float() Str() List() Dict() conversions
608
609 # - type(x) should return these Obj, or perhaps typeObj(x)
610 # - __str__ method for echo $[type(x)] ?
611
612 i_func = method_type.Index__()
613 type_m = NewDict() # type: Dict[str, value_t]
614 type_m['__index__'] = value.BuiltinFunc(i_func)
615 type_obj_methods = Obj(None, type_m)
616
617 # Note: Func[Int -> Int] is something we should do?
618 for tag in [
619 value_e.Bool, value_e.Int, value_e.Float, value_e.Str,
620 value_e.List, value_e.Dict, value_e.Obj
621 ]:
622 type_name = value_str(tag, dot=False)
623 #log('%s %s' , type_name, tag)
624 type_obj = Obj(type_obj_methods, {'name': value.Str(type_name)})
625 mem.AddBuiltin(type_name, type_obj)
626
627 # Wire up circular dependencies.
628 vm.InitCircularDeps(arith_ev, bool_ev, expr_ev, word_ev, cmd_ev, shell_ex,
629 prompt_ev, io_obj, tracer)
630
631 unsafe_arith = sh_expr_eval.UnsafeArith(mem, exec_opts, mutable_opts,
632 parse_ctx, arith_ev, errfmt)
633 vm.InitUnsafeArith(mem, word_ev, unsafe_arith)
634
635 #
636 # Initialize Built-in Procs
637 #
638
639 b = builtins # short alias for initialization
640
641 if mylib.PYTHON:
642 if help_meta:
643 help_data = help_meta.TopicMetadata()
644 else:
645 help_data = NewDict() # minimal build
646 else:
647 help_data = help_meta.TopicMetadata()
648 b[builtin_i.help] = misc_osh.Help(lang, loader, help_data, errfmt)
649
650 # Interpreter state
651 b[builtin_i.set] = pure_osh.Set(mutable_opts, mem)
652 b[builtin_i.shopt] = pure_osh.Shopt(exec_opts, mutable_opts, cmd_ev)
653
654 b[builtin_i.hash] = pure_osh.Hash(search_path) # not really pure
655 b[builtin_i.trap] = trap_osh.Trap(trap_state, parse_ctx, tracer, errfmt)
656
657 b[builtin_i.shvar] = pure_ysh.Shvar(mem, search_path, cmd_ev)
658 b[builtin_i.ctx] = pure_ysh.Ctx(mem, cmd_ev)
659 b[builtin_i.push_registers] = pure_ysh.PushRegisters(mem, cmd_ev)
660
661 # Hay
662 b[builtin_i.hay] = hay_ysh.Hay(hay_state, mutable_opts, mem, cmd_ev)
663 b[builtin_i.haynode] = hay_ysh.HayNode_(hay_state, mem, cmd_ev)
664
665 # Interpreter introspection
666 b[builtin_i.type] = meta_oils.Type(procs, aliases, search_path, errfmt)
667 b[builtin_i.builtin] = meta_oils.Builtin(shell_ex, errfmt)
668 b[builtin_i.command] = meta_oils.Command(shell_ex, procs, aliases,
669 search_path)
670 # Part of YSH, but similar to builtin/command
671 b[builtin_i.runproc] = meta_oils.RunProc(shell_ex, procs, errfmt)
672 b[builtin_i.invoke] = meta_oils.Invoke(shell_ex, procs, errfmt)
673 b[builtin_i.extern_] = meta_oils.Extern(shell_ex, procs, errfmt)
674
675 # Meta builtins
676 module_invoke = module_ysh.ModuleInvoke(cmd_ev, tracer, errfmt)
677 b[builtin_i.use] = meta_oils.ShellFile(parse_ctx,
678 search_path,
679 cmd_ev,
680 fd_state,
681 tracer,
682 errfmt,
683 loader,
684 module_invoke=module_invoke)
685 source_builtin = meta_oils.ShellFile(parse_ctx, search_path, cmd_ev,
686 fd_state, tracer, errfmt, loader)
687 b[builtin_i.source] = source_builtin
688 b[builtin_i.dot] = source_builtin
689 b[builtin_i.eval] = meta_oils.Eval(parse_ctx, exec_opts, cmd_ev, tracer,
690 errfmt, mem)
691
692 # Module builtins
693 guards = NewDict() # type: Dict[str, bool]
694 b[builtin_i.source_guard] = module_ysh.SourceGuard(guards, exec_opts,
695 errfmt)
696 b[builtin_i.is_main] = module_ysh.IsMain(mem)
697
698 # Errors
699 b[builtin_i.error] = error_ysh.Error()
700 b[builtin_i.failed] = error_ysh.Failed(mem)
701 b[builtin_i.boolstatus] = error_ysh.BoolStatus(shell_ex, errfmt)
702 b[builtin_i.try_] = error_ysh.Try(mutable_opts, mem, cmd_ev, shell_ex,
703 errfmt)
704 b[builtin_i.assert_] = error_ysh.Assert(expr_ev, errfmt)
705
706 # Pure builtins
707 true_ = pure_osh.Boolean(0)
708 b[builtin_i.colon] = true_ # a "special" builtin
709 b[builtin_i.true_] = true_
710 b[builtin_i.false_] = pure_osh.Boolean(1)
711
712 b[builtin_i.alias] = pure_osh.Alias(aliases, errfmt)
713 b[builtin_i.unalias] = pure_osh.UnAlias(aliases, errfmt)
714
715 b[builtin_i.getopts] = pure_osh.GetOpts(mem, errfmt)
716
717 b[builtin_i.shift] = assign_osh.Shift(mem)
718 b[builtin_i.unset] = assign_osh.Unset(mem, procs, unsafe_arith, errfmt)
719
720 b[builtin_i.append] = pure_ysh.Append(mem, errfmt)
721
722 # test / [ differ by need_right_bracket
723 b[builtin_i.test] = bracket_osh.Test(False, exec_opts, mem, errfmt)
724 b[builtin_i.bracket] = bracket_osh.Test(True, exec_opts, mem, errfmt)
725
726 # Output
727 b[builtin_i.echo] = io_osh.Echo(exec_opts)
728 b[builtin_i.printf] = printf_osh.Printf(mem, parse_ctx, unsafe_arith,
729 errfmt)
730 b[builtin_i.write] = io_ysh.Write(mem, errfmt)
731 redir_builtin = io_ysh.RunBlock(mem, cmd_ev) # used only for redirects
732 b[builtin_i.redir] = redir_builtin
733 b[builtin_i.fopen] = redir_builtin # alias for backward compatibility
734
735 # (pp output format isn't stable)
736 b[builtin_i.pp] = io_ysh.Pp(expr_ev, mem, errfmt, procs, arena)
737
738 # Input
739 b[builtin_i.cat] = io_osh.Cat() # for $(<file)
740 b[builtin_i.read] = read_osh.Read(splitter, mem, parse_ctx, cmd_ev, errfmt)
741
742 mapfile = io_osh.MapFile(mem, errfmt, cmd_ev)
743 b[builtin_i.mapfile] = mapfile
744 b[builtin_i.readarray] = mapfile
745
746 # Dirs
747 dir_stack = dirs_osh.DirStack()
748 b[builtin_i.cd] = dirs_osh.Cd(mem, dir_stack, cmd_ev, errfmt)
749 b[builtin_i.pushd] = dirs_osh.Pushd(mem, dir_stack, errfmt)
750 b[builtin_i.popd] = dirs_osh.Popd(mem, dir_stack, errfmt)
751 b[builtin_i.dirs] = dirs_osh.Dirs(mem, dir_stack, errfmt)
752 b[builtin_i.pwd] = dirs_osh.Pwd(mem, errfmt)
753
754 b[builtin_i.times] = misc_osh.Times()
755
756 b[builtin_i.json] = json_ysh.Json(mem, errfmt, False)
757 b[builtin_i.json8] = json_ysh.Json(mem, errfmt, True)
758
759 ### Process builtins
760 b[builtin_i.exec_] = process_osh.Exec(mem, ext_prog, fd_state, search_path,
761 errfmt)
762 b[builtin_i.umask] = process_osh.Umask()
763 b[builtin_i.ulimit] = process_osh.Ulimit()
764 b[builtin_i.wait] = process_osh.Wait(waiter, job_list, mem, tracer, errfmt)
765
766 b[builtin_i.jobs] = process_osh.Jobs(job_list)
767 b[builtin_i.fg] = process_osh.Fg(job_control, job_list, waiter)
768 b[builtin_i.bg] = process_osh.Bg(job_list)
769
770 # Could be in process_ysh
771 b[builtin_i.fork] = process_osh.Fork(shell_ex)
772 b[builtin_i.forkwait] = process_osh.ForkWait(shell_ex)
773
774 # Interactive builtins depend on readline
775 b[builtin_i.bind] = readline_osh.Bind(readline, errfmt)
776 b[builtin_i.history] = readline_osh.History(readline, sh_files, errfmt,
777 mylib.Stdout())
778
779 # Completion
780 spec_builder = completion_osh.SpecBuilder(cmd_ev, parse_ctx, word_ev,
781 splitter, comp_lookup, help_data,
782 errfmt)
783 complete_builtin = completion_osh.Complete(spec_builder, comp_lookup)
784 b[builtin_i.complete] = complete_builtin
785 b[builtin_i.compgen] = completion_osh.CompGen(spec_builder)
786 b[builtin_i.compopt] = completion_osh.CompOpt(compopt_state, errfmt)
787 b[builtin_i.compadjust] = completion_osh.CompAdjust(mem)
788
789 comp_ev = word_eval.CompletionWordEvaluator(mem, exec_opts, mutable_opts,
790 tilde_ev, splitter, errfmt)
791
792 comp_ev.arith_ev = arith_ev
793 comp_ev.expr_ev = expr_ev
794 comp_ev.prompt_ev = prompt_ev
795 comp_ev.CheckCircularDeps()
796
797 root_comp = completion.RootCompleter(comp_ev, mem, comp_lookup,
798 compopt_state, comp_ui_state,
799 comp_ctx, debug_f)
800 b[builtin_i.compexport] = completion_ysh.CompExport(root_comp)
801
802 #
803 # Initialize Builtin-in Methods
804 #
805
806 methods[value_e.Str] = {
807 'startsWith': method_str.HasAffix(method_str.START),
808 'endsWith': method_str.HasAffix(method_str.END),
809 'trim': method_str.Trim(method_str.START | method_str.END),
810 'trimStart': method_str.Trim(method_str.START),
811 'trimEnd': method_str.Trim(method_str.END),
812 'upper': method_str.Upper(),
813 'lower': method_str.Lower(),
814 'split': method_str.Split(),
815
816 # finds a substring, optional position to start at
817 'find': None,
818
819 # replace substring, OR an eggex
820 # takes count=3, the max number of replacements to do.
821 'replace': method_str.Replace(mem, expr_ev),
822
823 # Like Python's re.search, except we put it on the string object
824 # It's more consistent with Str->find(substring, pos=0)
825 # It returns value.Match() rather than an integer
826 'search': method_str.SearchMatch(method_str.SEARCH),
827
828 # like Python's re.match()
829 'leftMatch': method_str.SearchMatch(method_str.LEFT_MATCH),
830
831 # like Python's re.fullmatch(), not sure if we really need it
832 'fullMatch': None,
833 }
834 methods[value_e.Dict] = {
835 # keys() values() get() are FREE functions, not methods
836 # I think items() isn't as necessary because dicts are ordered? YSH
837 # code shouldn't use the List of Lists representation.
838 'M/erase': method_dict.Erase(),
839 # could be d->tally() or d->increment(), but inc() is short
840 #
841 # call d->inc('mycounter')
842 # call d->inc('mycounter', 3)
843 'M/inc': None,
844
845 # call d->accum('mygroup', 'value')
846 'M/accum': None,
847
848 # DEPRECATED - use free functions
849 'get': method_dict.Get(),
850 'keys': method_dict.Keys(),
851 'values': method_dict.Values(),
852 }
853 methods[value_e.List] = {
854 'M/reverse': method_list.Reverse(),
855 'M/append': method_list.Append(),
856 'M/extend': method_list.Extend(),
857 'M/pop': method_list.Pop(),
858 'M/insert': None, # insert object before index
859 'M/remove': None, # insert object before index
860 'indexOf': method_list.IndexOf(), # return first index of value, or -1
861 # Python list() has index(), which raises ValueError
862 # But this is consistent with Str->find(), and doesn't
863 # use exceptions
864 'join': func_misc.Join(), # both a method and a func
865 }
866
867 methods[value_e.Match] = {
868 'group': func_eggex.MatchMethod(func_eggex.G, expr_ev),
869 'start': func_eggex.MatchMethod(func_eggex.S, None),
870 'end': func_eggex.MatchMethod(func_eggex.E, None),
871 }
872
873 methods[value_e.Place] = {
874 # __mut_setValue()
875
876 # instead of setplace keyword
877 'M/setValue': method_other.SetValue(mem),
878 }
879
880 methods[value_e.CommandFrag] = {
881 # var x = ^(echo hi)
882 # Export source code and line number
883 # Useful for test frameworks and so forth
884 'export': None,
885 }
886
887 #
888 # Initialize Built-in Funcs
889 #
890
891 parse_hay = func_hay.ParseHay(fd_state, parse_ctx, mem, errfmt)
892 eval_hay = func_hay.EvalHay(hay_state, mutable_opts, mem, cmd_ev)
893 hay_func = func_hay.HayFunc(hay_state)
894
895 _AddBuiltinFunc(mem, 'parseHay', parse_hay)
896 _AddBuiltinFunc(mem, 'evalHay', eval_hay)
897 _AddBuiltinFunc(mem, '_hay', hay_func)
898
899 _AddBuiltinFunc(mem, 'len', func_misc.Len())
900 _AddBuiltinFunc(mem, 'type', func_misc.Type())
901
902 g = func_eggex.MatchFunc(func_eggex.G, expr_ev, mem)
903 _AddBuiltinFunc(mem, '_group', g)
904 _AddBuiltinFunc(mem, '_match',
905 g) # TODO: remove this backward compat alias
906 _AddBuiltinFunc(mem, '_start',
907 func_eggex.MatchFunc(func_eggex.S, None, mem))
908 _AddBuiltinFunc(mem, '_end', func_eggex.MatchFunc(func_eggex.E, None, mem))
909
910 _AddBuiltinFunc(mem, 'id', func_reflect.Id())
911 # TODO: should this be parseCommandStr() vs. parseFile() for Hay?
912 _AddBuiltinFunc(mem, 'parseCommand',
913 func_reflect.ParseCommand(parse_ctx, mem, errfmt))
914 _AddBuiltinFunc(mem, 'parseExpr',
915 func_reflect.ParseExpr(parse_ctx, errfmt))
916
917 _AddBuiltinFunc(mem, 'shvarGet', func_reflect.Shvar_get(mem))
918 _AddBuiltinFunc(mem, 'getVar', func_reflect.GetVar(mem))
919 _AddBuiltinFunc(mem, 'setVar', func_reflect.SetVar(mem))
920
921 # TODO: implement bindFrame() to turn CommandFrag -> Command
922 # Then parseCommand() and parseHay() will not depend on mem; they will not
923 # bind a frame yet
924 #
925 # what about newFrame() and globalFrame()?
926 _AddBuiltinFunc(mem, 'bindFrame', func_reflect.BindFrame())
927
928 _AddBuiltinFunc(mem, 'Object', func_misc.Object())
929 _AddBuiltinFunc(mem, 'prototype', func_misc.Prototype())
930 _AddBuiltinFunc(mem, 'propView', func_misc.PropView())
931
932 # type conversions
933 _AddBuiltinFunc(mem, 'bool', func_misc.Bool())
934 _AddBuiltinFunc(mem, 'int', func_misc.Int())
935 _AddBuiltinFunc(mem, 'float', func_misc.Float())
936 _AddBuiltinFunc(mem, 'str', func_misc.Str_())
937 _AddBuiltinFunc(mem, 'list', func_misc.List_())
938 _AddBuiltinFunc(mem, 'dict', func_misc.DictFunc())
939
940 # Dict functions
941 _AddBuiltinFunc(mem, 'get', method_dict.Get())
942 _AddBuiltinFunc(mem, 'keys', method_dict.Keys())
943 _AddBuiltinFunc(mem, 'values', method_dict.Values())
944
945 _AddBuiltinFunc(mem, 'runes', func_misc.Runes())
946 _AddBuiltinFunc(mem, 'encodeRunes', func_misc.EncodeRunes())
947 _AddBuiltinFunc(mem, 'bytes', func_misc.Bytes())
948 _AddBuiltinFunc(mem, 'encodeBytes', func_misc.EncodeBytes())
949
950 # Str
951 #_AddBuiltinFunc(mem, 'strcmp', None)
952 # TODO: This should be Python style splitting
953 _AddBuiltinFunc(mem, 'split', func_misc.Split(splitter))
954 _AddBuiltinFunc(mem, 'shSplit', func_misc.Split(splitter))
955
956 # Float
957 _AddBuiltinFunc(mem, 'floatsEqual', func_misc.FloatsEqual())
958
959 # List
960 _AddBuiltinFunc(mem, 'join', func_misc.Join())
961 _AddBuiltinFunc(mem, 'maybe', func_misc.Maybe())
962 _AddBuiltinFunc(mem, 'glob', func_misc.Glob(globber))
963
964 # Serialize
965 _AddBuiltinFunc(mem, 'toJson8', func_misc.ToJson8(True))
966 _AddBuiltinFunc(mem, 'toJson', func_misc.ToJson8(False))
967
968 _AddBuiltinFunc(mem, 'fromJson8', func_misc.FromJson8(True))
969 _AddBuiltinFunc(mem, 'fromJson', func_misc.FromJson8(False))
970
971 # Demos
972 _AddBuiltinFunc(mem, '_a2sp', func_misc.BashArrayToSparse())
973 _AddBuiltinFunc(mem, '_opsp', func_misc.SparseOp())
974
975 mem.AddBuiltin('io', io_obj)
976 mem.AddBuiltin('vm', vm_obj)
977
978 # Special case for testing
979 mem.AddBuiltin('module-invoke', value.BuiltinProc(module_invoke))
980
981 #
982 # Is the shell interactive?
983 #
984
985 # History evaluation is a no-op if readline is None.
986 hist_ev = history.Evaluator(readline, hist_ctx, debug_f)
987
988 if flag.c is not None:
989 src = source.CFlag # type: source_t
990 line_reader = reader.StringLineReader(flag.c,
991 arena) # type: reader._Reader
992 if flag.i: # -c and -i can be combined
993 mutable_opts.set_interactive()
994
995 elif flag.i: # force interactive
996 src = source.Stdin(' -i')
997 line_reader = reader.InteractiveLineReader(arena, prompt_ev, hist_ev,
998 readline, prompt_state)
999 mutable_opts.set_interactive()
1000
1001 else:
1002 if script_name is None:
1003 if flag.headless:
1004 src = source.Headless
1005 line_reader = None # unused!
1006 # Not setting '-i' flag for now. Some people's bashrc may want it?
1007 else:
1008 stdin_ = mylib.Stdin()
1009 # --tool never starts a prompt
1010 if len(flag.tool) == 0 and stdin_.isatty():
1011 src = source.Interactive
1012 line_reader = reader.InteractiveLineReader(
1013 arena, prompt_ev, hist_ev, readline, prompt_state)
1014 mutable_opts.set_interactive()
1015 else:
1016 src = source.Stdin('')
1017 line_reader = reader.FileLineReader(stdin_, arena)
1018 else:
1019 src = source.MainFile(script_name)
1020 try:
1021 f = fd_state.Open(script_name)
1022 except (IOError, OSError) as e:
1023 print_stderr("%s: Couldn't open %r: %s" %
1024 (lang, script_name, posix.strerror(e.errno)))
1025 return 1
1026 line_reader = reader.FileLineReader(f, arena)
1027
1028 # Pretend it came from somewhere else
1029 if flag.location_str is not None:
1030 src = source.Synthetic(flag.location_str)
1031 assert line_reader is not None
1032 location_start_line = mops.BigTruncate(flag.location_start_line)
1033 if location_start_line != -1:
1034 line_reader.SetLineOffset(location_start_line)
1035
1036 arena.PushSource(src)
1037
1038 # Calculate ~/.config/oils/oshrc or yshrc. Used for both -i and --headless
1039 # We avoid cluttering the user's home directory. Some users may want to ln
1040 # -s ~/.config/oils/oshrc ~/oshrc or ~/.oshrc.
1041
1042 # https://unix.stackexchange.com/questions/24347/why-do-some-applications-use-config-appname-for-their-config-data-while-other
1043
1044 config_dir = '.config/oils'
1045 rc_paths = [] # type: List[str]
1046 if not flag.norc and (flag.headless or exec_opts.interactive()):
1047 # User's rcfile comes FIRST. Later we can add an 'after-rcdir' hook
1048 rc_path = flag.rcfile
1049 if rc_path is None:
1050 rc_paths.append(
1051 os_path.join(home_dir, '%s/%src' % (config_dir, lang)))
1052 else:
1053 rc_paths.append(rc_path)
1054
1055 # Load all files in ~/.config/oils/oshrc.d or oilrc.d
1056 # This way "installers" can avoid mutating oshrc directly
1057
1058 rc_dir = flag.rcdir
1059 if rc_dir is None:
1060 rc_dir = os_path.join(home_dir, '%s/%src.d' % (config_dir, lang))
1061
1062 rc_paths.extend(libc.glob(os_path.join(rc_dir, '*')))
1063 else:
1064 if flag.rcfile is not None: # bash doesn't have this warning, but it's useful
1065 print_stderr('%s warning: --rcfile ignored with --norc' % lang)
1066 if flag.rcdir is not None:
1067 print_stderr('%s warning: --rcdir ignored with --norc' % lang)
1068
1069 # Initialize even in non-interactive shell, for 'compexport'
1070 _InitDefaultCompletions(cmd_ev, complete_builtin, comp_lookup)
1071
1072 if flag.headless:
1073 state.InitInteractive(mem, lang)
1074 mutable_opts.set_redefine_const()
1075 mutable_opts.set_redefine_source()
1076
1077 # NOTE: rc files loaded AFTER _InitDefaultCompletions.
1078 for rc_path in rc_paths:
1079 with state.ctx_ThisDir(mem, rc_path):
1080 try:
1081 SourceStartupFile(fd_state, rc_path, lang, parse_ctx,
1082 cmd_ev, errfmt)
1083 except util.UserExit as e:
1084 return e.status
1085
1086 loop = main_loop.Headless(cmd_ev, parse_ctx, errfmt)
1087 try:
1088 # TODO: What other exceptions happen here?
1089 status = loop.Loop()
1090 except util.UserExit as e:
1091 status = e.status
1092
1093 # Same logic as interactive shell
1094 mut_status = IntParamBox(status)
1095 cmd_ev.RunTrapsOnExit(mut_status)
1096 status = mut_status.i
1097
1098 return status
1099
1100 # Note: headless mode above doesn't use c_parser
1101 assert line_reader is not None
1102 c_parser = parse_ctx.MakeOshParser(line_reader)
1103
1104 if exec_opts.interactive():
1105 state.InitInteractive(mem, lang)
1106 # bash: 'set -o emacs' is the default only in the interactive shell
1107 mutable_opts.set_emacs()
1108 mutable_opts.set_redefine_const()
1109 mutable_opts.set_redefine_source()
1110
1111 if readline:
1112 term_width = 0
1113 if flag.completion_display == 'nice':
1114 try:
1115 term_width = libc.get_terminal_width()
1116 except (IOError, OSError): # stdin not a terminal
1117 pass
1118
1119 if term_width != 0:
1120 display = comp_ui.NiceDisplay(
1121 term_width, comp_ui_state, prompt_state, debug_f, readline,
1122 signal_safe) # type: comp_ui._IDisplay
1123 else:
1124 display = comp_ui.MinimalDisplay(comp_ui_state, prompt_state,
1125 debug_f)
1126
1127 comp_ui.InitReadline(readline, sh_files.HistoryFile(), root_comp,
1128 display, debug_f)
1129
1130 _InitDefaultCompletions(cmd_ev, complete_builtin, comp_lookup)
1131 if flag.completion_demo:
1132 _CompletionDemo(comp_lookup)
1133
1134 else: # Without readline module
1135 display = comp_ui.MinimalDisplay(comp_ui_state, prompt_state,
1136 debug_f)
1137
1138 process.InitInteractiveShell(signal_safe) # Set signal handlers
1139
1140 # The interactive shell leads a process group which controls the terminal.
1141 # It MUST give up the terminal afterward, otherwise we get SIGTTIN /
1142 # SIGTTOU bugs.
1143 with process.ctx_TerminalControl(job_control, errfmt):
1144
1145 # NOTE: rc files loaded AFTER _InitDefaultCompletions.
1146 for rc_path in rc_paths:
1147 with state.ctx_ThisDir(mem, rc_path):
1148 try:
1149 SourceStartupFile(fd_state, rc_path, lang, parse_ctx,
1150 cmd_ev, errfmt)
1151 except util.UserExit as e:
1152 return e.status
1153
1154 assert line_reader is not None
1155 line_reader.Reset() # After sourcing startup file, render $PS1
1156
1157 prompt_plugin = prompt.UserPlugin(mem, parse_ctx, cmd_ev, errfmt)
1158 try:
1159 status = main_loop.Interactive(flag, cmd_ev, c_parser, display,
1160 prompt_plugin, waiter, errfmt)
1161 except util.UserExit as e:
1162 status = e.status
1163
1164 mut_status = IntParamBox(status)
1165 cmd_ev.RunTrapsOnExit(mut_status)
1166 status = mut_status.i
1167
1168 if readline:
1169 hist_file = sh_files.HistoryFile()
1170 if hist_file is not None:
1171 try:
1172 readline.write_history_file(hist_file)
1173 except (IOError, OSError):
1174 pass
1175
1176 return status
1177
1178 if flag.rcfile is not None: # bash doesn't have this warning, but it's useful
1179 print_stderr('%s warning: --rcfile ignored in non-interactive shell' %
1180 lang)
1181 if flag.rcdir is not None:
1182 print_stderr('%s warning: --rcdir ignored in non-interactive shell' %
1183 lang)
1184
1185 #
1186 # Tools that use the OSH/YSH parsing mode, etc.
1187 #
1188
1189 # flag.tool is '' if nothing is passed
1190 # osh --tool syntax-tree is equivalent to osh -n --one-pass-parse
1191 tool_name = 'syntax-tree' if exec_opts.noexec() else flag.tool
1192
1193 if len(tool_name):
1194 # Don't save tokens becaues it's slow
1195 if tool_name != 'syntax-tree':
1196 arena.SaveTokens()
1197
1198 try:
1199 node = main_loop.ParseWholeFile(c_parser)
1200 except error.Parse as e:
1201 errfmt.PrettyPrintError(e)
1202 return 2
1203
1204 if tool_name == 'syntax-tree':
1205 ui.PrintAst(node, flag)
1206
1207 elif tool_name == 'tokens':
1208 ysh_ify.PrintTokens(arena)
1209
1210 elif tool_name == 'lossless-cat': # for test/lossless.sh
1211 ysh_ify.LosslessCat(arena)
1212
1213 elif tool_name == 'fmt':
1214 fmt.Format(arena, node)
1215
1216 elif tool_name == 'test':
1217 raise AssertionError('TODO')
1218
1219 elif tool_name == 'ysh-ify':
1220 ysh_ify.Ysh_ify(arena, node)
1221
1222 elif tool_name == 'deps':
1223 if mylib.PYTHON:
1224 deps.Deps(node)
1225
1226 else:
1227 raise AssertionError(tool_name) # flag parser validated it
1228
1229 return 0
1230
1231 #
1232 # Run a shell script
1233 #
1234
1235 with state.ctx_ThisDir(mem, script_name):
1236 try:
1237 status = main_loop.Batch(cmd_ev,
1238 c_parser,
1239 errfmt,
1240 cmd_flags=cmd_eval.IsMainProgram)
1241 except util.UserExit as e:
1242 status = e.status
1243 except KeyboardInterrupt:
1244 # The interactive shell handles this in main_loop.Interactive
1245 status = 130 # 128 + 2
1246 mut_status = IntParamBox(status)
1247 cmd_ev.RunTrapsOnExit(mut_status)
1248
1249 multi_trace.WriteDumps()
1250
1251 # NOTE: We haven't closed the file opened with fd_state.Open
1252 return mut_status.i