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

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