def ParseLikeEcho(self, argv): # type: (List[str]) -> Tuple[args._Attributes, int] """ echo is a special case. These work: echo -n echo -en - But don't respect -- - doesn't fail when an invalid flag is passed """ arg_r = args.Reader(argv) out = args._Attributes(self.defaults) while not arg_r.AtEnd(): arg = arg_r.Peek() chars = arg[1:] if arg.startswith('-') and chars: # Check if it looks like -en or not. # NOTE: Changed to list comprehension to avoid # LOAD_CLOSURE/MAKE_CLOSURE. if not all([c in self.arity0 for c in arg[1:]]): break # looks like args for ch in chars: assert ch in self.arity0 out.SetTrue(ch) else: break # Looks like an arg arg_r.Next() # next arg return out, arg_r.i
def Run(self, cmd_val): # type: (cmd_value__Assign) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() attrs = flag_spec.Parse('readonly', arg_r) arg = arg_types.readonly(attrs.attrs) if arg.p or len(cmd_val.pairs) == 0: return _PrintVariables(self.mem, cmd_val, attrs, True, builtin=_READONLY) for pair in cmd_val.pairs: if pair.rval is None: if arg.a: rval = value.MaybeStrArray([]) # type: value_t elif arg.A: rval = value.AssocArray({}) else: rval = None else: rval = pair.rval rval = _ReconcileTypes(rval, arg.a, arg.A, pair.spid) # NOTE: # - when rval is None, only flags are changed # - dynamic scope because flags on locals can be changed, etc. self.mem.SetVar(lvalue.Named(pair.var_name), rval, scope_e.Dynamic, flags=state.SetReadOnly) return 0
def __call__(self, cmd_val): # TODO: # - How to integrate this with auto-completion? Have to handle '+'. if len(cmd_val.argv) == 1: # 'set' without args shows visible variable names and values. According # to POSIX: # - the names should be sorted, and # - the code should be suitable for re-input to the shell. We have a # spec test for this. # Also: # - autoconf also wants them to fit on ONE LINE. # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set mapping = self.mem.GetAllVars() for name in sorted(mapping): str_val = mapping[name] code_str = '%s=%s' % (name, string_ops.ShellQuoteOneLine(str_val)) print(code_str) return 0 arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # skip 'set' arg = SET_SPEC.Parse(arg_r) # 'set -o' shows options. This is actually used by autoconf-generated # scripts! if arg.show_options: self.exec_opts.ShowOptions([]) return 0 SetExecOpts(self.exec_opts, arg.opt_changes, arg.shopt_changes) # Hm do we need saw_double_dash? if arg.saw_double_dash or not arg_r.AtEnd(): self.mem.SetArgv(arg_r.Rest()) return 0
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int # There are no flags, but we need it to respect -- arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # skip 'eval' arg = EVAL_SPEC.Parse(arg_r) if self.exec_opts.strict_eval_builtin(): code_str, eval_spid = arg_r.ReadRequired2('requires code string') if not arg_r.AtEnd(): raise error.Usage('requires exactly 1 argument') else: code_str = ' '.join(cmd_val.argv[arg_r.i:]) # code_str could be EMPTY, so just use the first one eval_spid = cmd_val.arg_spids[0] line_reader = reader.StringLineReader(code_str, self.arena) c_parser = self.parse_ctx.MakeOshParser(line_reader) src = source.EvalArg(eval_spid) self.arena.PushSource(src) try: return main_loop.Batch(self.cmd_ev, c_parser, self.arena) finally: self.arena.PopSource()
def __call__(self, argv): arg_r = args.Reader(argv) arg = COMPLETE_SPEC.Parse(arg_r) # TODO: process arg.opt_changes #log('arg %s', arg) commands = arg_r.Rest() if arg.D: commands.append( '__fallback') # if the command doesn't match anything if arg.E: commands.append('__first') # empty line if not commands: self.comp_lookup.PrintSpecs() return 0 base_opts = dict(arg.opt_changes) try: user_spec = self.spec_builder.Build(argv, arg, base_opts) except util.ParseError as e: # error printed above return 2 for command in commands: self.comp_lookup.RegisterName(command, base_opts, user_spec) patterns = [] for pat in patterns: self.comp_lookup.RegisterGlob(pat, base_opts, user_spec) return 0
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # skip 'exec' _ = EXEC_SPEC.Parse(arg_r) # no flags now, but accepts -- # Apply redirects in this shell. # NOTE: Redirects were processed earlier. if arg_r.AtEnd(): self.fd_state.MakePermanent() return 0 environ = self.mem.GetExported() i = arg_r.i cmd = cmd_val.argv[i] argv0_path = self.search_path.CachedLookup(cmd) if argv0_path is None: self.errfmt.Print('exec: %r not found', cmd, span_id=cmd_val.arg_spids[1]) raise SystemExit(127) # exec builtin never returns # shift off 'exec' c2 = cmd_value.Argv(cmd_val.argv[i:], cmd_val.arg_spids[i:], cmd_val.block) self.ext_prog.Exec(argv0_path, c2, environ) # NEVER RETURNS assert False, "This line should never be reached" # makes mypy happy
def __call__(self, argv): arg_r = args.Reader(argv) arg = INIT_COMPLETION_SPEC.Parse(arg_r) var_names = arg_r.Rest() # Output variables to set for name in var_names: # Ironically we could complete these if name not in ['cur', 'prev', 'words', 'cword']: raise args.UsageError('Invalid output variable name %r' % name) #print(arg) # TODO: How does the user test a completion function programmatically? Set # COMP_ARGV? val = self.mem.GetVar('COMP_ARGV') if val.tag != value_e.StrArray: raise args.UsageError("COMP_ARGV should be an array") comp_argv = val.strs # These are the ones from COMP_WORDBREAKS that we care about. The rest occur # "outside" of words. break_chars = [':', '='] if arg.s: # implied break_chars.remove('=') # NOTE: The syntax is -n := and not -n : -n =. omit_chars = arg.n or '' for c in omit_chars: if c in break_chars: break_chars.remove(c) # argv adjusted according to 'break_chars'. adjusted_argv = [] for a in comp_argv: completion.AdjustArg(a, break_chars, adjusted_argv) if 'words' in var_names: state.SetArrayDynamic(self.mem, 'words', adjusted_argv) n = len(adjusted_argv) cur = adjusted_argv[-1] prev = '' if n < 2 else adjusted_argv[-2] if arg.s: if cur.startswith( '--') and '=' in cur: # Split into flag name and value prev, cur = cur.split('=', 1) split = 'true' else: split = 'false' # Do NOT set 'split' without -s. Caller might not have declared it. # Also does not respect var_names, because we don't need it. state.SetStringDynamic(self.mem, 'split', split) if 'cur' in var_names: state.SetStringDynamic(self.mem, 'cur', cur) if 'prev' in var_names: state.SetStringDynamic(self.mem, 'prev', prev) if 'cword' in var_names: # Same weird invariant after adjustment state.SetStringDynamic(self.mem, 'cword', str(n - 1)) return 0
def __call__(self, argv): arg_r = args.Reader(argv) arg = COMPLETE_SPEC.Parse(arg_r) # TODO: process arg.opt_changes #log('arg %s', arg) commands = arg_r.Rest() if arg.D: commands.append( '__fallback') # if the command doesn't match anything if arg.E: commands.append('__first') # empty line if not commands: self.comp_state.PrintSpecs() return 0 comp_opts = completion.Options(arg.opt_changes) user_spec = _BuildUserSpec(argv, arg, comp_opts, self.ex) for command in commands: self.comp_state.RegisterName(command, comp_opts, user_spec) patterns = [] for pat in patterns: self.comp_state.RegisterGlob(pat, comp_opts, user_spec) return 0
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # skip 'hash' arg, i = HASH_SPEC.Parse(arg_r) rest = arg_r.Rest() if arg.r: if rest: raise args.UsageError('got extra arguments after -r') self.search_path.ClearCache() return 0 status = 0 if rest: for cmd in rest: # enter in cache full_path = self.search_path.CachedLookup(cmd) if full_path is None: ui.Stderr('hash: %r not found', cmd) status = 1 else: # print cache commands = self.search_path.CachedCommands() commands.sort() for cmd in commands: print(cmd) return status
def Run(self, cmd_val): # type: (cmd_value__Assign) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() attrs = flag_spec.Parse('export_', arg_r) arg = arg_types.export_(attrs.attrs) #arg = attrs if arg.f: e_usage( "doesn't accept -f because it's dangerous. " "(The code can usually be restructured with 'source')") if arg.p or len(cmd_val.pairs) == 0: return _PrintVariables(self.mem, cmd_val, attrs, True, builtin=_EXPORT) if arg.n: for pair in cmd_val.pairs: if pair.rval is not None: e_usage("doesn't accept RHS with -n", span_id=pair.spid) # NOTE: we don't care if it wasn't found, like bash. self.mem.ClearFlag(pair.var_name, state.ClearExport, scope_e.Dynamic) else: for pair in cmd_val.pairs: # NOTE: when rval is None, only flags are changed self.mem.SetVar(lvalue.Named(pair.var_name), pair.rval, scope_e.Dynamic, flags=state.SetExport) return 0
def Run(self, cmd_val): # type: (cmd_value__Assign) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() arg, arg_index = EXPORT_SPEC.Parse(arg_r) if arg.f: raise args.UsageError( "doesn't accept -f because it's dangerous. (The code can usually be restructured with 'source')" ) positional = cmd_val.argv[arg_index:] if arg.n: for pair in cmd_val.pairs: if pair.rval is not None: raise args.UsageError("doesn't accept RHS with -n", span_id=pair.spid) # NOTE: we don't care if it wasn't found, like bash. self.mem.ClearFlag(pair.lval.name, state.ClearExport, scope_e.Dynamic) else: for pair in cmd_val.pairs: # NOTE: when rval is None, only flags are changed self.mem.SetVar(pair.lval, pair.rval, scope_e.Dynamic, flags=state.SetExport) return 0
def _ParseCmdVal(spec, cmd_val): # type: (cmd_value__Argv) -> Tuple[args._Attributes, int] """For testing only """ arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # move past the builtin name return spec.Parse(arg_r), arg_r.i
def __call__(self, cmd_val): arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() arg, arg_index = READONLY_SPEC.Parse(arg_r) for pair in cmd_val.pairs: if pair.rval is None: if arg.a: rval = value.StrArray([]) elif arg.A: rval = value.AssocArray({}) else: rval = None else: rval = pair.rval if not _CheckType(rval, arg, self.errfmt, pair.spid): return 1 # NOTE: # - when rval is None, only flags are changed # - dynamic scope because flags on locals can be changed, etc. self.mem.SetVar(pair.lval, rval, (var_flags_e.ReadOnly, ), scope_e.Dynamic) return 0
def Run(self, cmd_val): # type: (cmd_value__Assign) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() arg, arg_index = READONLY_SPEC.Parse(arg_r) for pair in cmd_val.pairs: if pair.rval is None: if arg.a: rval = value.MaybeStrArray([]) # type: value_t elif arg.A: rval = value.AssocArray({}) else: rval = None else: rval = pair.rval if not _CheckType(rval, arg, self.errfmt, pair.spid): return 1 # NOTE: # - when rval is None, only flags are changed # - dynamic scope because flags on locals can be changed, etc. self.mem.SetVar(pair.lval, rval, scope_e.Dynamic, flags=state.SetReadOnly) return 0
def Run(self, cmd_val): arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # skip 'use' # TODO: # - Does shopt -s namespaces have to be on? # - I don't think so? It only affects 'procs', not funcs. arg = arg_r.Peek() # 'use bin' and 'use env' are for static analysis. No-ops at runtime. if arg in ('bin', 'env'): return 0 if arg == 'lib': # OPTIONAL lib arg_r.Next() # Cosmetic: separator for 'use bin __ grep sed'. Allowed for 'lib' to be # consistent. arg = arg_r.Peek() if arg == '__': # OPTIONAL __ arg_r.Next() # Now import everything. rest = arg_r.Rest() for path in rest: log('path %s', path) return 0
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int argv = cmd_val.argv[1:] arg_r = args.Reader(argv) arg = COMPLETE_SPEC.Parse(arg_r) # TODO: process arg.opt_changes #log('arg %s', arg) commands = arg_r.Rest() if arg.D: commands.append( '__fallback') # if the command doesn't match anything if arg.E: commands.append('__first') # empty line if not commands: self.comp_lookup.PrintSpecs() return 0 base_opts = dict(arg.opt_changes) try: user_spec = self.spec_builder.Build(argv, arg, base_opts) except error.Parse as e: # error printed above return 2 for command in commands: self.comp_lookup.RegisterName(command, base_opts, user_spec) # TODO: Hook this up patterns = [] # type: List[str] for pat in patterns: self.comp_lookup.RegisterGlob(pat, base_opts, user_spec) return 0
def Run(self, cmd_val): arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() arg, _ = GETLINE_SPEC.Parse(arg_r) if arg.cstr: # TODO: implement it # returns error if it can't decode raise NotImplementedError() var_name, var_spid = arg_r.ReadRequired2('requires a variable name') if var_name.startswith(':'): # optional : sigil var_name = var_name[1:] next_arg, next_spid = arg_r.Peek2() if next_arg is not None: raise error.Usage('got extra argument', span_id=next_spid) line = _ReadLine() if len(line) == 0: # EOF return 1 if not arg.end: if line.endswith('\r\n'): line = line[:-2] elif line.endswith('\n'): line = line[:-1] self.mem.SetVar(sh_lhs_expr.Name(var_name), value.Str(line), scope_e.LocalOnly) return 0
def ParseLikeEcho(spec_name, cmd_val): # type: (str, cmd_value__Argv) -> Tuple[args._Attributes, args.Reader] arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # move past the builtin name spec = FLAG_SPEC[spec_name] return args.ParseLikeEcho(spec, arg_r), arg_r
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # NOTE: If first char is a colon, error reporting is different. Alpine # might not use that? spec_str = arg_r.ReadRequired('requires an argspec') var_name, var_spid = arg_r.ReadRequired2( 'requires the name of a variable to set') spec = self.spec_cache.get(spec_str) if spec is None: spec = _ParseOptSpec(spec_str) self.spec_cache[spec_str] = spec user_argv = self.mem.GetArgv() if arg_r.AtEnd() else arg_r.Rest() #util.log('user_argv %s', user_argv) status, flag_char = _GetOpts(spec, user_argv, self.my_state, self.errfmt) if match.IsValidVarName(var_name): state.SetStringDynamic(self.mem, var_name, flag_char) else: # NOTE: The builtin has PARTIALLY set state. This happens in all shells # except mksh. raise error.Usage('got invalid variable name %r' % var_name, span_id=var_spid) return status
def ParseOilCmdVal(spec_name, cmd_val): # type: (str, cmd_value__Argv) -> Tuple[args._Attributes, args.Reader] """Parse argv using a given FlagSpecAndMore.""" arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # move past the builtin name spec = OIL_SPEC[spec_name] return args.ParseOil(spec, arg_r), arg_r
def __call__(self, arg_vec): if self.exec_opts.simple_echo: arg_r = args.Reader(arg_vec.strs, spids=arg_vec.spids) arg_r.Next() # skip 'echo' arg, _ = OIL_ECHO_SPEC.Parse(arg_r) #print(arg) i = 0 while not arg_r.AtEnd(): if i != 0: sys.stdout.write(arg.sep) s = arg_r.Peek() sys.stdout.write(s) arg_r.Next() i += 1 if arg.n: pass elif arg.end: sys.stdout.write(arg.end) return 0 argv = arg_vec.strs[1:] arg, arg_index = ECHO_SPEC.ParseLikeEcho(argv) argv = argv[arg_index:] if arg.e: new_argv = [] for a in argv: parts = [] for id_, value in match.ECHO_LEXER.Tokens(a): p = word_compile.EvalCStringToken(id_, value) # Unusual behavior: '\c' prints what is there and aborts processing! if p is None: new_argv.append(''.join(parts)) for i, a in enumerate(new_argv): if i != 0: sys.stdout.write(' ') # arg separator sys.stdout.write(a) return 0 # EARLY RETURN parts.append(p) new_argv.append(''.join(parts)) # Replace it argv = new_argv #log('echo argv %s', argv) for i, a in enumerate(argv): if i != 0: sys.stdout.write(' ') # arg separator sys.stdout.write(a) if not arg.n: sys.stdout.write('\n') return 0
def ParseCmdVal(self, cmd_val): # type: (cmd_value__Argv) -> Tuple[args._Attributes, int] """Used by builtins that don't need arg_r. Note: It might be more flexible to return arg_r instead if i. Then they can call arg_r.Rest(), etc. TODO: Remove in favor of ParseCmdVal above """ arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # move past the builtin name return self.Parse(arg_r), arg_r.i
def __call__(self, arg_vec): argv = arg_vec.strs[1:] arg_r = args.Reader(argv) arg = COMPOPT_SPEC.Parse(arg_r) if not self.comp_state.currently_completing: # bash also checks this. self.errfmt.Print('compopt: not currently executing a completion function') return 1 self.comp_state.dynamic_opts.update(arg.opt_changes) #log('compopt: %s', arg) #log('compopt %s', base_opts) return 0
def main(argv): arg_r = args.Reader(argv) arg_r.Next() # skip argv[0] arg = args.Parse(SPEC, arg_r) if not arg.f: stderr_line("readlink: -f must be passed") return 1 for path in arg_r.Rest(): res = libc.realpath(path) if res is None: return 1 print(res) return 0
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int argv = cmd_val.argv[1:] arg_r = args.Reader(argv) arg = COMPOPT_SPEC.Parse(arg_r) if not self.comp_state.currently_completing: # bash also checks this. self.errfmt.Print( 'compopt: not currently executing a completion function') return 1 self.comp_state.dynamic_opts.update(arg.opt_changes) #log('compopt: %s', arg) #log('compopt %s', base_opts) return 0
def __call__(self, cmd_val): arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) #arg_r.Next() # skip 'use' # TODO: GetVar() and print them if cmd_val.block: # TODO: flatten value.{Str,Obj} into a flat dict? namespace = self.ex.EvalBlock(cmd_val.block) # TODO: Use JSON library from pprint import pprint pprint(namespace, indent=2) return 0
def Run(self, cmd_val): argv = cmd_val.argv[1:] arg_r = args.Reader(argv) arg = COMPGEN_SPEC.Parse(arg_r) if arg_r.AtEnd(): to_complete = '' else: to_complete = arg_r.Peek() arg_r.Next() # bash allows extra arguments here. #if not arg_r.AtEnd(): # raise error.Usage('Extra arguments') matched = False base_opts = dict(arg.opt_changes) try: user_spec = self.spec_builder.Build(argv, arg, base_opts) except error.Parse as e: # error printed above return 2 # NOTE: Matching bash in passing dummy values for COMP_WORDS and COMP_CWORD, # and also showing ALL COMPREPLY reuslts, not just the ones that start with # the word to complete. matched = False comp = completion.Api() comp.Update(first='compgen', to_complete=to_complete, prev='', index=-1) try: for m, _ in user_spec.Matches(comp): matched = True print(m) except error.FatalRuntime: # - DynamicWordsAction: We already printed an error, so return failure. return 1 # - ShellFuncAction: We do NOT get FatalRuntimeError. We printed an error # in the executor, but RunFuncForCompletion swallows failures. See test # case in builtin-completion.test.sh. # TODO: # - need to dedupe results. return 0 if matched else 1
def __call__(self, argv): arg_r = args.Reader(argv) arg = COMPOPT_SPEC.Parse(arg_r) if not self.comp_state.currently_completing: # bash checks this. util.error( 'compopt: not currently executing a completion function') return 1 for name, b in arg.opt_changes: #log('setting %s = %s', name, b) self.comp_state.current_opts.Set(name, b) #log('compopt: %s', arg) #log('compopt %s', comp_opts) return 0
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # NOTE: If first char is a colon, error reporting is different. Alpine # might not use that? spec_str = arg_r.ReadRequired('requires an argspec') var_name, var_spid = arg_r.ReadRequired2( 'requires the name of a variable to set') try: spec = self.spec_cache[spec_str] except KeyError: spec = _ParseOptSpec(spec_str) self.spec_cache[spec_str] = spec # These errors are fatal errors, not like the builtin exiting with code 1. # Because the invariants of the shell have been violated! v = self.mem.GetVar('OPTIND') if v.tag != value_e.Str: e_die('OPTIND should be a string, got %r', v) try: optind = int(v.s) except ValueError: e_die("OPTIND doesn't look like an integer, got %r", v.s) user_argv = arg_r.Rest() or self.mem.GetArgv() #util.log('user_argv %s', user_argv) status, opt_char, optarg, optind = _GetOpts(spec, user_argv, optind, self.errfmt) # Bug fix: bash-completion uses a *local* OPTIND ! Not global. state.SetStringDynamic(self.mem, 'OPTARG', optarg) state.SetStringDynamic(self.mem, 'OPTIND', str(optind)) if match.IsValidVarName(var_name): state.SetStringDynamic(self.mem, var_name, opt_char) else: # NOTE: The builtin has PARTIALLY filed. This happens in all shells # except mksh. raise error.Usage('got invalid variable name %r' % var_name, span_id=var_spid) return status
def main(argv): # type: (List[str]) -> int loader = pyutil.GetResourceLoader() login_shell = False environ = {} # type: Dict[str, str] environ['PWD'] = posix.getcwd() arg_r = args.Reader(argv, spids=[runtime.NO_SPID] * len(argv)) try: status = pure.Main('osh', arg_r, environ, login_shell, loader, None) return status except error.Usage as e: #builtin.Help(['oil-usage'], util.GetResourceLoader()) log('oil: %s', e.msg) return 2 except RuntimeError as e: if 0: import traceback traceback.print_exc() # NOTE: The Python interpreter can cause this, e.g. on stack overflow. # f() { f; }; f will cause this msg = e.message # type: str stderr_line('osh fatal error: %s', msg) return 1 # Note: This doesn't happen in C++. except KeyboardInterrupt: print('') return 130 # 128 + 2 except OSError as e: if 0: import traceback traceback.print_exc() # test this with prlimit --nproc=1 --pid=$$ stderr_line('osh I/O error: %s', pyutil.strerror_OS(e)) return 2 # dash gives status 2 except IOError as e: # duplicate of above because CPython is inconsistent stderr_line('osh I/O error: %s', pyutil.strerror_IO(e)) return 2