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.SetRefString(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 Run(self, cmd_val): # type: (cmd_value__Argv) -> int # TODO: Also hard usage error here too? attrs, arg_r = flag_spec.ParseOilCmdVal('run', cmd_val) arg = arg_types.run(attrs.attrs) if arg_r.Peek() is None: # HARD ERROR, not e_usage(), because errexit is often disabled! e_die("'run' expected a command to run", status=2) argv, spids = arg_r.Rest2() cmd_val2 = cmd_value.Argv(argv, spids, cmd_val.block) # Set in the 'except' block, e.g. if 'myfunc' failed failure_spid = runtime.NO_SPID try: # Temporarily turn ON errexit, and blame the 'run' spid. Note that # 'if run myproc' disables it and then enables it! with state.ctx_ErrExit(self.mutable_opts, True, cmd_val.arg_spids[0]): # Pass do_fork=True. Slight annoyance: the real value is a field of # command.Simple(). See _NoForkLast() in CommandEvaluator We have an # extra fork (miss out on an optimization) of code like ( status ls ) # or forkwait { status ls }, but that is NOT idiomatic code. status is # for functions. status = self.shell_ex.RunSimpleCommand(cmd_val2, True) #log('st %d', status) except error.ErrExit as e: # from functino call #log('e %d', e.exit_status) status = e.exit_status failure_spid = e.span_id # Do this before -allow-status-01 if arg.status_ok is not None: status = _AdjustStatus(arg.status_ok, status) if arg.allow_status_01 and status not in (0, 1): if failure_spid != runtime.NO_SPID: self.errfmt.Print_('(original failure)', span_id=failure_spid) self.errfmt.StderrLine('') raise error.ErrExit('fatal: status %d when --allow-status-01' % status, span_id=spids[0], status=status) if arg.assign_status is not None: var_name = arg.assign_status if var_name.startswith(':'): var_name = var_name[1:] state.SetRefString(self.mem, var_name, str(status)) return 0 # don't fail return status
def _Read(self, arg, names): # type: (arg_types.read, List[str]) -> int if arg.n >= 0: # read a certain number of bytes (-1 means unset) if len(names): name = names[0] else: name = 'REPLY' # default variable name stdin_fd = self.stdin.fileno() s = self._ReadN(stdin_fd, arg.n) state.SetRefString(self.mem, name, s) # Did we read all the bytes we wanted? return 0 if len(s) == arg.n else 1 if len(names) == 0: names.append('REPLY') # leftover words assigned to the last name if arg.a is not None: max_results = 0 # no max else: max_results = len(names) if arg.Z: # -0 is synonym for -r -d '' raw = True delim_char = '\0' else: raw = arg.r if arg.d is not None: if len(arg.d): delim_char = arg.d[0] else: delim_char = '\0' # -d '' delimits by NUL else: delim_char = '\n' # read a line # We have to read more than one line if there is a line continuation (and # it's not -r). parts = [] # type: List[mylib.BufWriter] join_next = False status = 0 while True: line, eof = _ReadUntilDelim(delim_char) if eof: # status 1 to terminate loop. (This is true even though we set # variables). status = 1 #log('LINE %r', line) if len(line) == 0: break spans = self.splitter.SplitForRead(line, not raw) done, join_next = _AppendParts(line, spans, max_results, join_next, parts) #log('PARTS %s continued %s', parts, continued) if done: break entries = [buf.getvalue() for buf in parts] num_parts = len(entries) if arg.a is not None: state.SetRefArray(self.mem, arg.a, entries) else: for i in xrange(max_results): if i < num_parts: s = entries[i] else: s = '' # if there are too many variables #log('read: %s = %s', names[i], s) state.SetRefString(self.mem, names[i], s) return status
def Fail(self): # type: () -> None """On failure, reset OPTARG.""" state.SetRefString(self.mem, 'OPTARG', '')
def SetArg(self, optarg): # type: (str) -> None """Set OPTARG.""" state.SetRefString(self.mem, 'OPTARG', optarg)
def IncIndex(self): # type: () -> None """Increment OPTIND.""" # Note: bash-completion uses a *local* OPTIND ! Not global. assert self._optind != -1 state.SetRefString(self.mem, 'OPTIND', str(self._optind + 1))
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int argv = cmd_val.argv[1:] arg_r = args.Reader(argv) arg = COMPADJUST_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 error.Usage('Invalid output variable name %r' % name) #print(arg) # TODO: How does the user test a completion function programmatically? Set # COMP_ARGV? val = self.mem.GetValue('COMP_ARGV') if val.tag != value_e.MaybeStrArray: raise error.Usage("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 = [] # type: List[str] for a in comp_argv: completion.AdjustArg(a, break_chars, adjusted_argv) if 'words' in var_names: state.SetRefArray(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.SetRefString(self.mem, 'split', split) if 'cur' in var_names: state.SetRefString(self.mem, 'cur', cur) if 'prev' in var_names: state.SetRefString(self.mem, 'prev', prev) if 'cword' in var_names: # Same weird invariant after adjustment state.SetRefString(self.mem, 'cword', str(n - 1)) return 0