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

1253 lines, 796 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 sh_init
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 reader
36from frontend import parse_lib
37
38from builtin import assign_osh
39from builtin import bracket_osh
40from builtin import completion_osh
41from builtin import completion_ysh
42from builtin import dirs_osh
43from builtin import error_ysh
44from builtin import hay_ysh
45from builtin import io_osh
46from builtin import io_ysh
47from builtin import json_ysh
48from builtin import meta_oils
49from builtin import misc_osh
50from builtin import module_ysh
51from builtin import printf_osh
52from builtin import process_osh
53from builtin import pure_osh
54from builtin import pure_ysh
55from builtin import readline_osh
56from builtin import read_osh
57from builtin import trap_osh
58
59from builtin import func_eggex
60from builtin import func_hay
61from builtin import func_misc
62from builtin import func_reflect
63
64from builtin import method_dict
65from builtin import method_io
66from builtin import method_list
67from builtin import method_other
68from builtin import method_str
69from builtin import method_type
70
71from osh import cmd_eval
72from osh import glob_
73from osh import history
74from osh import prompt
75from osh import sh_expr_eval
76from osh import split
77from osh import word_eval
78
79from mycpp import iolib
80from mycpp import mops
81from mycpp import mylib
82from mycpp.mylib import NewDict, print_stderr, log
83from pylib import os_path
84from tools import deps
85from tools import fmt
86from tools import ysh_ify
87from ysh import expr_eval
88
89unused2 = log
90
91import libc
92import posix_ as posix
93
94from typing import List, Dict, Optional, TYPE_CHECKING, cast
95if TYPE_CHECKING:
96 from frontend.py_readline import Readline
97
98if mylib.PYTHON:
99 try:
100 from _devbuild.gen import help_meta # type: ignore
101 except ImportError:
102 help_meta = None
103
104
105def _InitDefaultCompletions(cmd_ev, complete_builtin, comp_lookup):
106 # type: (cmd_eval.CommandEvaluator, completion_osh.Complete, completion.Lookup) -> None
107
108 # register builtins and words
109 complete_builtin.Run(cmd_eval.MakeBuiltinArgv(['-E', '-A', 'command']))
110 # register path completion
111 # Add -o filenames? Or should that be automatic?
112 complete_builtin.Run(cmd_eval.MakeBuiltinArgv(['-D', '-A', 'file']))
113
114
115def _CompletionDemo(comp_lookup):
116 # type: (completion.Lookup) -> None
117
118 # Something for fun, to show off. Also: test that you don't repeatedly hit
119 # the file system / network / coprocess.
120 A1 = completion.TestAction(['foo.py', 'foo', 'bar.py'], 0.0)
121 l = [] # type: List[str]
122 for i in xrange(0, 5):
123 l.append('m%d' % i)
124
125 A2 = completion.TestAction(l, 0.1)
126 C1 = completion.UserSpec([A1, A2], [], [], completion.DefaultPredicate(),
127 '', '')
128 comp_lookup.RegisterName('slowc', {}, C1)
129
130
131def SourceStartupFile(
132 fd_state, # type: process.FdState
133 rc_path, # type: str
134 lang, # type: str
135 parse_ctx, # type: parse_lib.ParseContext
136 cmd_ev, # type: cmd_eval.CommandEvaluator
137 errfmt, # type: ui.ErrorFormatter
138):
139 # type: (...) -> None
140
141 # Right now this is called when the shell is interactive. (Maybe it should
142 # be called on login_shel too.)
143 #
144 # Terms:
145 # - interactive shell: Roughly speaking, no args or -c, and isatty() is true
146 # for stdin and stdout.
147 # - login shell: Started from the top level, e.g. from init or ssh.
148 #
149 # We're not going to copy everything bash does because it's too complex, but
150 # for reference:
151 # https://www.gnu.org/software/bash/manual/bash.html#Bash-Startup-Files
152 # Bash also has --login.
153
154 try:
155 f = fd_state.Open(rc_path)
156 except (IOError, OSError) as e:
157 # TODO: Could warn about nonexistent explicit --rcfile?
158 if e.errno != ENOENT:
159 raise # Goes to top level. Handle this better?
160 return
161
162 arena = parse_ctx.arena
163 rc_line_reader = reader.FileLineReader(f, arena)
164 rc_c_parser = parse_ctx.MakeOshParser(rc_line_reader)
165
166 with alloc.ctx_SourceCode(arena, source.MainFile(rc_path)):
167 # TODO: handle status, e.g. 2 for ParseError
168 unused = main_loop.Batch(cmd_ev, rc_c_parser, errfmt)
169
170 f.close()
171
172
173class ShellOptHook(state.OptHook):
174
175 def __init__(self, readline):
176 # type: (Optional[Readline]) -> None
177 self.readline = readline
178
179 def OnChange(self, opt0_array, opt_name, b):
180 # type: (List[bool], str, bool) -> bool
181 """This method is called whenever an option is changed.
182
183 Returns success or failure.
184 """
185 if opt_name == 'vi' or opt_name == 'emacs':
186 # TODO: Replace with a hook? Just like setting LANG= can have a hook.
187 if self.readline:
188 self.readline.parse_and_bind("set editing-mode " + opt_name)
189 else:
190 print_stderr(
191 "Warning: Can't set option %r because shell wasn't compiled with GNU readline"
192 % opt_name)
193 return False
194
195 # Invert: they are mutually exclusive!
196 if opt_name == 'vi':
197 opt0_array[option_i.emacs] = not b
198 elif opt_name == 'emacs':
199 opt0_array[option_i.vi] = not b
200
201 return True
202
203
204def _AddBuiltinFunc(mem, name, func):
205 # type: (state.Mem, str, vm._Callable) -> None
206 assert isinstance(func, vm._Callable), func
207 mem.AddBuiltin(name, value.BuiltinFunc(func))
208
209
210def InitAssignmentBuiltins(
211 mem, # type: state.Mem
212 procs, # type: state.Procs
213 exec_opts, # type: optview.Exec
214 errfmt, # type: ui.ErrorFormatter
215):
216 # type: (...) -> Dict[int, vm._AssignBuiltin]
217
218 assign_b = {} # type: Dict[int, vm._AssignBuiltin]
219
220 new_var = assign_osh.NewVar(mem, procs, exec_opts, errfmt)
221 assign_b[builtin_i.declare] = new_var
222 assign_b[builtin_i.typeset] = new_var
223 assign_b[builtin_i.local] = new_var
224
225 assign_b[builtin_i.export_] = assign_osh.Export(mem, errfmt)
226 assign_b[builtin_i.readonly] = assign_osh.Readonly(mem, errfmt)
227
228 return assign_b
229
230
231class ShellFiles(object):
232
233 def __init__(self, lang, home_dir, mem, flag):
234 # type: (str, str, state.Mem, arg_types.main) -> None
235 assert lang in ('osh', 'ysh'), lang
236 self.lang = lang
237 self.home_dir = home_dir
238 self.mem = mem
239 self.flag = flag
240
241 def _HistVar(self):
242 # type: () -> str
243 return 'HISTFILE' if self.lang == 'osh' else 'YSH_HISTFILE'
244
245 def _DefaultHistoryFile(self):
246 # type: () -> str
247 return os_path.join(self.home_dir,
248 '.local/share/oils/%s_history' % self.lang)
249
250 def InitAfterLoadingEnv(self):
251 # type: () -> None
252
253 hist_var = self._HistVar()
254 if self.mem.GetValue(hist_var).tag() == value_e.Undef:
255 default_val = self._DefaultHistoryFile()
256 # Note: if the directory doesn't exist, GNU readline ignores it
257 # This is like
258 # HISTFILE=foo
259 # setglobal HISTFILE = 'foo'
260 # Not like:
261 # export HISTFILE=foo
262 # setglobal ENV.HISTFILE = 'foo'
263 #
264 # Note: bash only sets this in interactive shells
265 state.SetGlobalString(self.mem, hist_var, default_val)
266
267 def HistoryFile(self):
268 # type: () -> Optional[str]
269 # TODO: In non-strict mode we should try to cast the HISTFILE value to a
270 # string following bash's rules
271
272 #return state.GetStringFromEnv(self.mem, self._HistVar())
273
274 UP_val = self.mem.GetValue(self._HistVar())
275 if UP_val.tag() == value_e.Str:
276 val = cast(value.Str, UP_val)
277 return val.s
278 else:
279 # Note: if HISTFILE is an array, bash will return ${HISTFILE[0]}
280 return None
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 sh_init.InitBuiltins(mem, version_str)
377 sh_init.InitDefaultVars(mem)
378
379 sh_init.CopyVarsFromEnv(exec_opts, environ, mem)
380
381 # PATH PWD SHELLOPTS, etc. must be set after CopyVarsFromEnv()
382 sh_init.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 sh_init.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 sh_init.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