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

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