def _Visit(self, node): """ """ #log('VISIT %s', node.__class__.__name__) # NOTE: The tags are not unique!!! We would need this: # if isinstance(node, ast.command) and node.tag == command_e.Simple: # But it's easier to check the __class__ attribute. cls = node.__class__ if cls is command.Simple: #log('SimpleCommand %s', node.words) #log('--') #node.PrettyPrint() # Things to consider: # - source and . # - DONE builtins: get a list from builtin.py # - DONE functions: have to enter function definitions into a dictionary # - Commands that call others: sudo, su, find, xargs, etc. # - builtins that call others: exec, command # - except not command -v! if not node.words: return w = node.words[0] ok, argv0, _ = word_.StaticEval(w) if not ok: log("Couldn't statically evaluate %r", w) return if (builtin.ResolveSpecial(argv0) == builtin_e.NONE and builtin.ResolveAssign(argv0) == builtin_e.NONE and builtin.Resolve(argv0) == builtin_e.NONE): self.progs_used[argv0] = True # NOTE: If argv1 is $0, then we do NOT print a warning! if argv0 == 'sudo': if len(node.words) < 2: return w1 = node.words[1] ok, argv1, _ = word_.StaticEval(w1) if not ok: log("Couldn't statically evaluate %r", w) return # Should we mark them behind 'sudo'? e.g. "sudo apt install"? self.progs_used[argv1] = True elif cls is command.ShFunction: self.funcs_defined[node.name] = True
def RunSimpleCommand(self, argv, fork_external, span_id, funcs=True): """ Args: fork_external: for subshell ( ls / ) or ( command ls / ) """ # This happens when you write "$@" but have no arguments. if not argv: if self.exec_opts.strict_argv: e_die("Command evaluated to an empty argv array", span_id=span_id) else: return 0 # status 0, or skip it? arg0 = argv[0] builtin_id = builtin.ResolveSpecial(arg0) if builtin_id != builtin_e.NONE: try: status = self._RunBuiltin(builtin_id, argv, fork_external, span_id) except args.UsageError as e: ui.usage('osh %r usage error: %s', arg0, e) status = 2 # consistent error code for usage error return status # Builtins like 'true' can be redefined as functions. if funcs: func_node = self.funcs.get(arg0) if func_node is not None: # NOTE: Functions could call 'exit 42' directly, etc. status = self._RunFunc(func_node, argv[1:]) return status builtin_id = builtin.Resolve(arg0) if builtin_id != builtin_e.NONE: try: status = self._RunBuiltin(builtin_id, argv, fork_external, span_id) except args.UsageError as e: ui.usage('osh %r usage error: %s', arg0, e) status = 2 # consistent error code for usage error return status environ = self.mem.GetExported() # Include temporary variables if fork_external: thunk = process.ExternalThunk(self.ext_prog, argv, environ) p = process.Process(thunk) status = p.Run(self.waiter) return status self.ext_prog.Exec(argv, environ) # NEVER RETURNS
def RunSimpleCommand(self, arg_vec, fork_external, funcs=True): """Public interface to run a simple command (excluding assignment) Args: fork_external: for subshell ( ls / ) or ( command ls / ) """ argv = arg_vec.strs if arg_vec.spids: span_id = arg_vec.spids[0] else: span_id = const.NO_INTEGER # This happens when you write "$@" but have no arguments. if not argv: if self.exec_opts.strict_argv: e_die("Command evaluated to an empty argv array", span_id=span_id) else: return 0 # status 0, or skip it? arg0 = argv[0] builtin_id = builtin.ResolveAssign(arg0) if builtin_id != builtin_e.NONE: # command readonly is disallowed, for technical reasons. Could relax it # later. self.errfmt.Print("Can't run assignment builtin recursively", span_id=span_id) return 1 builtin_id = builtin.ResolveSpecial(arg0) if builtin_id != builtin_e.NONE: status = self._RunBuiltin(builtin_id, arg_vec, fork_external) # TODO: Enable this and fix spec test failures. # Also update _SPECIAL_BUILTINS in osh/builtin.py. #if status != 0: # e_die('special builtin failed', status=status) return status # Builtins like 'true' can be redefined as functions. if funcs: func_node = self.funcs.get(arg0) if func_node is not None: # NOTE: Functions could call 'exit 42' directly, etc. status = self._RunFunc(func_node, argv[1:]) return status builtin_id = builtin.Resolve(arg0) if builtin_id != builtin_e.NONE: return self._RunBuiltin(builtin_id, arg_vec, fork_external) environ = self.mem.GetExported() # Include temporary variables # Resolve argv[0] BEFORE forking. argv0_path = self.search_path.CachedLookup(argv[0]) if argv0_path is None: self.errfmt.Print('%r not found', argv[0], span_id=span_id) return 127 if fork_external: thunk = process.ExternalThunk(self.ext_prog, argv0_path, arg_vec, environ) p = process.Process(thunk, self.job_state) status = p.Run(self.waiter) return status self.ext_prog.Exec(argv0_path, arg_vec, environ) # NEVER RETURNS
def _RunBuiltin(self, builtin_id, argv, fork_external, span_id): argv = argv[1:] # Builtins don't need to know their own name. # # TODO: Convert everything to this new style # builtin_func = self.builtins.get(builtin_id) if builtin_func is not None: status = builtin_func(argv) elif builtin_id == builtin_e.EXEC: status = self._Exec(argv) # may never return # But if it returns, then we want to permanently apply the redirects # associated with it. self.fd_state.MakePermanent() elif builtin_id == builtin_e.READ: status = builtin.Read(argv, self.splitter, self.mem) elif builtin_id == builtin_e.ECHO: status = builtin.Echo(argv) elif builtin_id == builtin_e.PRINTF: status = builtin.Printf(argv, self.mem) elif builtin_id == builtin_e.SHIFT: status = builtin.Shift(argv, self.mem) elif builtin_id == builtin_e.CD: status = builtin.Cd(argv, self.mem, self.dir_stack) elif builtin_id == builtin_e.SET: status = builtin.Set(argv, self.exec_opts, self.mem) elif builtin_id == builtin_e.SHOPT: status = builtin.Shopt(argv, self.exec_opts) elif builtin_id == builtin_e.UNSET: status = builtin.Unset(argv, self.mem, self.funcs) elif builtin_id == builtin_e.EXPORT: status = builtin.Export(argv, self.mem) elif builtin_id == builtin_e.WAIT: status = builtin.Wait(argv, self.waiter, self.job_state, self.mem) elif builtin_id == builtin_e.JOBS: status = builtin.Jobs(argv, self.job_state) elif builtin_id == builtin_e.PUSHD: status = builtin.Pushd(argv, self.mem, self.dir_stack) elif builtin_id == builtin_e.POPD: status = builtin.Popd(argv, self.mem, self.dir_stack) elif builtin_id == builtin_e.DIRS: status = builtin.Dirs(argv, self.mem.GetVar('HOME'), self.dir_stack) elif builtin_id == builtin_e.PWD: status = builtin.Pwd(argv, self.mem) elif builtin_id in (builtin_e.SOURCE, builtin_e.DOT): status = self._Source(argv) elif builtin_id == builtin_e.TRAP: status = builtin.Trap(argv, self.traps, self.nodes_to_run, self) elif builtin_id == builtin_e.UMASK: status = builtin.Umask(argv) elif builtin_id == builtin_e.EVAL: status = self._Eval(argv, span_id) elif builtin_id == builtin_e.COLON: # special builtin like 'true' status = 0 elif builtin_id == builtin_e.TRUE: status = 0 elif builtin_id == builtin_e.FALSE: status = 1 elif builtin_id == builtin_e.TEST: status = builtin_bracket.Test(argv, False) elif builtin_id == builtin_e.BRACKET: status = builtin_bracket.Test(argv, True) # need_right_bracket elif builtin_id == builtin_e.GETOPTS: status = builtin.GetOpts(argv, self.mem) elif builtin_id == builtin_e.COMMAND: # TODO: Pull Command up to the top level? b = builtin.Command(self, self.funcs, self.mem) status = b(argv, fork_external, span_id) elif builtin_id == builtin_e.TYPE: path = self.mem.GetVar('PATH') status = builtin.Type(argv, self.funcs, path) elif builtin_id == builtin_e.HELP: loader = pyutil.GetResourceLoader() status = builtin.Help(argv, loader) elif builtin_id in (builtin_e.DECLARE, builtin_e.TYPESET): # These are synonyms status = builtin.DeclareTypeset(argv, self.mem, self.funcs) elif builtin_id == builtin_e.ALIAS: status = builtin.Alias(argv, self.aliases) elif builtin_id == builtin_e.UNALIAS: status = builtin.UnAlias(argv, self.aliases) elif builtin_id == builtin_e.REPR: status = builtin.Repr(argv, self.mem) elif builtin_id == builtin_e.BUILTIN: # NOTE: uses early return style if not argv: return 0 # this could be an error in strict mode? # Run regular builtin or special builtin to_run = builtin.Resolve(argv[0]) if to_run == builtin_e.NONE: to_run = builtin.ResolveSpecial(argv[0]) if to_run == builtin_e.NONE: util.error("builtin: %s: not a shell builtin", argv[0]) return 1 return self._RunBuiltin(to_run, argv, fork_external, span_id) else: raise AssertionError('Unhandled builtin: %s' % builtin_id) assert isinstance(status, int) return status
def _RunBuiltinAndRaise(self, builtin_id, arg_vec, fork_external): """ Raises: args.UsageError """ # Shift one arg. Builtins don't need to know their own name. argv = arg_vec.strs[1:] # Most builtins dispatch with a dictionary builtin_func = self.builtins.get(builtin_id) if builtin_func is not None: status = builtin_func(arg_vec) # Some builtins "belong" to the executor. elif builtin_id == builtin_e.EXEC: status = self._Exec(arg_vec) # may never return # But if it returns, then we want to permanently apply the redirects # associated with it. self.fd_state.MakePermanent() elif builtin_id == builtin_e.EVAL: status = self._Eval(arg_vec) elif builtin_id in (builtin_e.SOURCE, builtin_e.DOT): status = self._Source(arg_vec) elif builtin_id == builtin_e.COMMAND: # TODO: How do we handle fork_external? It doesn't fit the common # signature. We also don't handle 'command local', etc. b = builtin.Command(self, self.funcs, self.aliases, self.search_path) status = b(arg_vec, fork_external) elif builtin_id == builtin_e.BUILTIN: # NOTE: uses early return style if not argv: return 0 # this could be an error in strict mode? name = arg_vec.strs[1] # Run regular builtin or special builtin to_run = builtin.Resolve(name) if to_run == builtin_e.NONE: to_run = builtin.ResolveSpecial(name) if to_run == builtin_e.NONE: span_id = arg_vec.spids[1] if builtin.ResolveAssign(name) != builtin_e.NONE: # NOTE: There's a similar restriction for 'command' self.errfmt.Print("Can't run assignment builtin recursively", span_id=span_id) else: self.errfmt.Print("%r isn't a shell builtin", span_id=span_id) return 1 arg_vec2 = arg_vector(arg_vec.strs[1:], arg_vec.spids[1:]) status = self._RunBuiltinAndRaise(to_run, arg_vec2, fork_external) else: raise AssertionError('Unhandled builtin: %s' % builtin_id) assert isinstance(status, int) return status