def AppBundleMain(argv): login_shell = False b = os_path.basename(argv[0]) main_name, ext = os_path.splitext(b) if main_name.startswith('-'): login_shell = True main_name = main_name[1:] if main_name == 'oil' and ext: # oil.py or oil.ovm try: first_arg = argv[1] except IndexError: raise args.UsageError('Missing required applet name.') if first_arg in ('-h', '--help'): builtin.Help(['bundle-usage'], pyutil.GetResourceLoader()) sys.exit(0) if first_arg in ('-V', '--version'): _ShowVersion() sys.exit(0) main_name = first_arg if main_name.startswith('-'): # TODO: Remove duplication above login_shell = True main_name = main_name[1:] argv0 = argv[1] main_argv = argv[2:] else: argv0 = argv[0] main_argv = argv[1:] if main_name in ('osh', 'sh'): status = ShellMain('osh', argv0, main_argv, login_shell) _tlog('done osh main') return status elif main_name == 'oshc': try: return OshCommandMain(main_argv) except args.UsageError as e: ui.Stderr('oshc usage error: %s', e.msg) return 2 elif main_name == 'oil': return ShellMain('oil', argv0, main_argv, login_shell) elif main_name == 'wok': return WokMain(main_argv) elif main_name == 'boil': return BoilMain(main_argv) # For testing latency elif main_name == 'true': return 0 elif main_name == 'false': return 1 elif main_name == 'readlink': return readlink.main(main_argv) else: raise args.UsageError('Invalid applet name %r.' % main_name)
def Export(argv, mem): arg, i = EXPORT_SPEC.Parse(argv) if arg.n: for name in argv[i:]: m = match.IsValidVarName(name) if not m: raise args.UsageError('export: Invalid variable name %r' % name) # NOTE: bash doesn't care if it wasn't found. mem.ClearFlag(name, var_flags_e.Exported, scope_e.Dynamic) else: for arg in argv[i:]: parts = arg.split('=', 1) if len(parts) == 1: name = parts[0] val = None # Creates an empty variable else: name, s = parts val = value.Str(s) m = match.IsValidVarName(name) if not m: raise args.UsageError('export: Invalid variable name %r' % name) #log('%s %s', name, val) mem.SetVar(lvalue.LhsName(name), val, (var_flags_e.Exported, ), 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 = 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 AppBundleMain(argv): b = os.path.basename(argv[0]) main_name, ext = os.path.splitext(b) if main_name in ('opy_', 'opy') and ext: # opy_.py or opy.ovm try: first_arg = argv[1] except IndexError: raise args.UsageError('Missing required applet name.') # TODO: We don't have this if first_arg in ('-h', '--help'): #builtin.Help(['bundle-usage'], util.GetResourceLoader()) raise NotImplementedError('OPy help not implemented') sys.exit(0) if first_arg in ('-V', '--version'): _ShowVersion() sys.exit(0) main_name = first_arg argv0 = argv[1] main_argv = argv[2:] else: argv0 = argv[0] main_argv = argv[1:] if main_name == 'opy': status = OpyMain(argv0, main_argv) return status elif main_name == 'opyc': return opy_main.OpyCommandMain(main_argv) else: raise args.UsageError('Invalid applet name %r.' % main_name)
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 Run(self, cmd_val): # type: (cmd_value__Argv) -> int # NOTE: This builtin doesn't do anything in non-interactive mode in bash? # It silently exits zero. # zsh -c 'history' produces an error. readline_mod = self.readline_mod if not readline_mod: raise args.UsageError( "OSH wasn't compiled with the readline module.") arg, arg_index = HISTORY_SPEC.ParseCmdVal(cmd_val) # Clear all history if arg.c: readline_mod.clear_history() return 0 # Delete history entry by id number if arg.d: cmd_index = arg.d - 1 try: readline_mod.remove_history_item(cmd_index) except ValueError: raise args.UsageError("couldn't find item %d" % arg.d) return 0 # Returns 0 items in non-interactive mode? num_items = readline_mod.get_current_history_length() #log('len = %d', num_items) rest = cmd_val.argv[arg_index:] if len(rest) == 0: start_index = 1 elif len(rest) == 1: arg0 = rest[0] try: num_to_show = int(arg0) except ValueError: raise args.UsageError('Invalid argument %r' % arg0) start_index = max(1, num_items + 1 - num_to_show) else: raise args.UsageError('Too many arguments') # TODO: # - Exclude lines that don't parse from the history! bash and zsh don't do # that. # - Consolidate multiline commands. for i in xrange(start_index, num_items + 1): # 1-based index item = readline_mod.get_history_item(i) self.f.write('%5d %s\n' % (i, item)) return 0
def __call__(self, cmd_val): num_args = len(cmd_val.argv) - 1 if num_args == 0: n = 1 elif num_args == 1: arg = cmd_val.argv[1] try: n = int(arg) except ValueError: raise args.UsageError("Invalid shift argument %r" % arg) else: raise args.UsageError('got too many arguments') return self.mem.Shift(n)
def __call__(self, arg_vec): num_args = len(arg_vec.strs) - 1 if num_args == 0: n = 1 elif num_args == 1: arg = arg_vec.strs[1] try: n = int(arg) except ValueError: raise args.UsageError("Invalid shift argument %r" % arg) else: raise args.UsageError('got too many arguments') return self.mem.Shift(n)
def SetShoptOption(self, opt_name, b): """ For shopt -s/-u. """ # shopt -s all:oil turns on all Oil options, which includes all strict # # options if opt_name == 'all:oil': for attr in _ALL_OIL: if attr in _PARSE_OPTION_NAMES: self._SetParseOption(attr, b) else: setattr(self, attr, b) self.errexit.Set(b) # Special case return if opt_name == 'all:nice': for attr in _NICE_OPTION_NAMES: self._SetParseOption(attr, b) setattr(self, attr, b) return # shopt -s all:strict turns on all strict options if opt_name == 'all:strict': for attr in _ALL_STRICT: setattr(self, attr, b) self.errexit.Set(b) # Special case return if opt_name in SHOPT_OPTION_NAMES: setattr(self, opt_name, b) elif opt_name in _PARSE_OPTION_NAMES: self._SetParseOption(opt_name, b) else: raise args.UsageError('got invalid option %r' % opt_name)
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 __call__(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 args.UsageError('got extra argument', span_id=next_spid) # TODO: use a more efficient function in C line = builtin.ReadLineFromStdin() if not line: # 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 Run(self, cmd_val): # type: (cmd_value__Argv) -> int # How does this differ from 'fg'? It doesn't wait and it sets controlling # terminal? raise args.UsageError("isn't implemented")
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int argv = cmd_val.argv[1:] if len(argv) == 0: # umask() has a dumb API: you can't get it without modifying it first! # NOTE: dash disables interrupts around the two umask() calls, but that # shouldn't be a concern for us. Signal handlers won't call umask(). mask = posix.umask(0) posix.umask(mask) # print('0%03o' % mask) # octal format return 0 if len(argv) == 1: a = argv[0] try: new_mask = int(a, 8) except ValueError: # NOTE: This happens if we have '8' or '9' in the input too. ui.Stderr("osh warning: umask with symbolic input isn't implemented") return 1 else: posix.umask(new_mask) return 0 raise args.UsageError('umask: unexpected arguments')
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int num_args = len(cmd_val.argv) - 1 if num_args == 0: # TODO: It's suppose to try another dir before doing this? self.errfmt.Print('pushd: no other directory') return 1 elif num_args > 1: raise args.UsageError('got too many arguments') # TODO: 'cd' uses normpath? Is that inconsistent? dest_dir = os_path.abspath(cmd_val.argv[1]) try: posix.chdir(dest_dir) except OSError as e: self.errfmt.Print("pushd: %r: %s", dest_dir, posix.strerror(e.errno), span_id=cmd_val.arg_spids[1]) return 1 self.dir_stack.Push(dest_dir) _PrintDirStack(self.dir_stack, SINGLE_LINE, self.mem.GetVar('HOME')) state.ExportGlobalString(self.mem, 'PWD', dest_dir) self.mem.SetPwd(dest_dir) return 0
def __call__(self, arg_vec): if len(arg_vec.spids) > 1: raise args.UsageError('got extra argument', span_id=arg_vec.spids[1]) _PopDirStack(self.mem, self.dir_stack, self.errfmt) _PrintDirStack(self.dir_stack, SINGLE_LINE, self.mem.GetVar('HOME')) return 0
def ShowShoptOptions(self, opt_names): """ For 'shopt -p' """ opt_names = opt_names or SHOPT_OPTION_NAMES # show all for opt_name in opt_names: if opt_name not in SHOPT_OPTION_NAMES: raise args.UsageError('got invalid option %r' % opt_name) attr = opt_name.replace('-', '_') # for strict-* b = getattr(self, attr) print('shopt -%s %s' % ('s' if b else 'u', opt_name))
def _UnsetVar(self, name, spid): if not match.IsValidVarName(name): raise args.UsageError( 'got invalid variable name %r' % name, span_id=spid) ok, found = self.mem.Unset(lvalue.Named(name), scope_e.Dynamic) if not ok: self.errfmt.Print("Can't unset readonly variable %r", name, span_id=spid) return ok, found
def e_usage(msg, *pos_args, **kwargs): # type: (str, *Any, **Any) -> NoReturn """Convenience wrapper for arg parsing / validation errors. Usually causes a builtin to fail with status 2, but the script can continue if 'set +o errexit'. Main programs like bin/oil also use this. """ from frontend import args # TODO: Should be error.Usage raise args.UsageError(msg, *pos_args, **kwargs)
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int if len(cmd_val.arg_spids) > 1: raise args.UsageError('got extra argument', span_id=cmd_val.arg_spids[1]) if not _PopDirStack(self.mem, self.dir_stack, self.errfmt): return 1 # error _PrintDirStack(self.dir_stack, SINGLE_LINE, self.mem.GetVar('HOME')) return 0
def ShowShoptOptions(self, opt_names): """ For 'shopt -p' """ opt_names = opt_names or ALL_SHOPT_OPTIONS # show all for opt_name in opt_names: if opt_name in SHOPT_OPTION_NAMES: b = getattr(self, opt_name) elif opt_name in _PARSE_OPTION_NAMES: b = getattr(self.parse_opts, opt_name) else: raise args.UsageError('got invalid option %r' % opt_name) print('shopt -%s %s' % ('s' if b else 'u', opt_name))
def UnAlias(argv, aliases): if not argv: raise args.UsageError('unalias NAME...') status = 0 for name in argv: try: del aliases[name] except KeyError: util.error('alias %r is not defined', name) status = 1 return status
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int argv = cmd_val.argv[1:] arg, arg_index = ECHO_SPEC.ParseLikeEcho(argv) argv = argv[arg_index:] if arg.e: new_argv = [] for a in argv: parts = [] lex = match.EchoLexer(a) while True: id_, value = lex.Next() if id_ == Id.Eol_Tok: # Note: This is really a NUL terminator break 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 if self.exec_opts.strict_echo(): n = len(argv) if n == 0: pass elif n == 1: sys.stdout.write(argv[0]) else: # TODO: span_id could be more accurate raise args.UsageError( "takes at most one arg when strict_echo is on (hint: add quotes)") else: #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 ShowOptions(self, opt_names): """ For 'set -o' and 'shopt -p -o' """ # TODO: Maybe sort them differently? opt_names = opt_names or SET_OPTION_NAMES for opt_name in opt_names: if opt_name not in SET_OPTION_NAMES: raise args.UsageError('got invalid option %r' % opt_name) if opt_name == 'errexit': b = self.errexit.errexit else: b = getattr(self, opt_name) print('set %so %s' % ('-' if b else '+', opt_name))
def __call__(self, arg_vec): if len(arg_vec.strs) == 1: raise args.UsageError('unalias NAME...') status = 0 for i in xrange(1, len(arg_vec.strs)): name = arg_vec.strs[i] try: del self.aliases[name] except KeyError: self.errfmt.Print('No alias named %r', name, span_id=arg_vec.spids[i]) status = 1 return status
def __call__(self, argv): # NOTE: This builtin doesn't do anything in non-interactive mode in bash? # It silently exits zero. # zsh -c 'history' produces an error. readline_mod = self.readline_mod if not readline_mod: raise args.UsageError( "OSH wasn't compiled with the readline module.") #arg_r = args.Reader(argv) arg, i = HISTORY_SPEC.Parse(argv) # Returns 0 items in non-interactive mode? num_items = readline_mod.get_current_history_length() #log('len = %d', num_items) rest = argv[i:] if len(rest) == 0: start_index = 1 elif len(rest) == 1: arg0 = rest[0] try: num_to_show = int(arg0) except ValueError: raise args.UsageError('Invalid argument %r' % arg0) start_index = max(1, num_items + 1 - num_to_show) else: raise args.UsageError('Too many arguments') # TODO: # - Exclude lines that don't parse from the history! bash and zsh don't do # that. # - Consolidate multiline commands. for i in xrange(start_index, num_items + 1): # 1-based index item = readline_mod.get_history_item(i) print('%5d %s' % (i, item)) return 0
def __call__(self, cmd_val): # type: (cmd_value__Argv) -> int argv = cmd_val.argv if len(argv) == 1: raise args.UsageError('unalias NAME...') status = 0 for i in xrange(1, len(argv)): name = argv[i] try: del self.aliases[name] except KeyError: self.errfmt.Print('No alias named %r', name, span_id=cmd_val.arg_spids[i]) status = 1 return status
def _SetOption(self, opt_name, b): """Private version for synchronizing from SHELLOPTS.""" assert '_' not in opt_name if opt_name not in SET_OPTION_NAMES: raise args.UsageError('got invalid option %r' % opt_name) if opt_name == 'errexit': self.errexit.Set(b) elif opt_name in ('vi', 'emacs'): if self.readline: self.readline.parse_and_bind("set editing-mode " + opt_name); else: e_die("Can't set option %r because Oil wasn't built with the readline " "library.", opt_name) else: if opt_name == 'verbose' and b: log('Warning: set -o verbose not implemented') setattr(self, opt_name, b)
def __call__(self, arg_vec): status = 0 for i in xrange(1, len(arg_vec.strs)): name = arg_vec.strs[i] if not match.IsValidVarName(name): raise args.UsageError('got invalid variable name %r' % name, span_id=arg_vec.spids[i]) cell = self.mem.GetCell(name) if cell is None: print('%r is not defined' % name) status = 1 else: sys.stdout.write('%s = ' % name) cell.PrettyPrint() # may be color sys.stdout.write('\n') return status
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 args.UsageError('got invalid variable name %r' % var_name, span_id=var_spid) return status
def _SetOption(self, opt_name, b): """Private version for synchronizing from SHELLOPTS.""" assert '_' not in opt_name if opt_name not in SET_OPTION_NAMES: raise args.UsageError('Invalid option %r' % opt_name) if opt_name == 'errexit': self.errexit.Set(b) elif opt_name in ('vi', 'emacs'): if self.readline: self.readline.parse_and_bind("set editing-mode " + opt_name) else: # TODO error message copied from 'cmd_exec.py'; refactor? util.error('Oil was not built with readline/completion.') else: # strict-control-flow -> strict_control_flow opt_name = opt_name.replace('-', '_') if opt_name == 'verbose' and b: log('Warning: set -o verbose not implemented') setattr(self, opt_name, b)