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

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