def EvalArithLhs(self, anode, span_id):
        # type: (arith_expr_t, int) -> lvalue_t
        """
    For (( a[x] = 1 )) etc.
    """
        UP_anode = anode
        if anode.tag_() == arith_expr_e.Binary:
            anode = cast(arith_expr__Binary, UP_anode)
            if anode.op_id == Id.Arith_LBracket:
                var_name, span_id = self._VarRefOrWord(anode.left)
                if var_name is not None:
                    if self.mem.IsAssocArray(var_name, scope_e.Dynamic):
                        key = self.EvalWordToString(anode.right)
                        lval2 = lvalue.Keyed(var_name, key)
                        lval2.spids.append(span_id)
                        lval = lval2  # type: lvalue_t
                        return lval
                    else:
                        index = self.EvalToInt(anode.right)
                        lval3 = lvalue.Indexed(var_name, index)
                        lval3.spids.append(span_id)
                        lval = lval3
                        return lval

        var_name, span_id = self._VarRefOrWord(anode)
        if var_name is not None:
            lval1 = lvalue.Named(var_name)
            lval1.spids.append(span_id)
            lval = lval1
            return lval

        # e.g. unset 'x-y'.  status 2 for runtime parse error
        e_die('Invalid place to modify', span_id=span_id, status=2)
Beispiel #2
0
    def Replace(self, s, op):
        # type: (str, suffix_op__PatSub) -> str

        regex = '(%s)' % self.regex  # make it a group

        if op.replace_mode == Id.Lit_Slash:
            try:
                return _PatSubAll(s, regex,
                                  self.replace_str)  # loop over matches
            except RuntimeError as e:
                # libc.regex_first_group_match raises RuntimeError.
                # TODO: We want errors with
                e_die('Error matching regex %r: %s',
                      regex,
                      e,
                      span_id=self.slash_spid)

        if op.replace_mode == Id.Lit_Pound:
            regex = '^' + regex
        elif op.replace_mode == Id.Lit_Percent:
            regex = regex + '$'

        m = libc.regex_first_group_match(regex, s, 0)
        #log('regex = %r, s = %r, match = %r', regex, s, m)
        if m is None:
            return s
        start, end = m
        return s[:start] + self.replace_str + s[end:]
Beispiel #3
0
def _LookupVar(name, mem, exec_opts):
  val = mem.GetVar(name)
  # By default, undefined variables are the ZERO value.  TODO: Respect
  # nounset and raise an exception.
  if val.tag == value_e.Undef and exec_opts.nounset:
    e_die('Undefined variable %r', name)  # TODO: need token
  return val
Beispiel #4
0
  def _MakeProcess(self, node, job_state=None, disable_errexit=False):
    """
    Assume we will run the node in another process.  Return a process.
    """
    if node.tag == command_e.ControlFlow:
      # Pipeline or subshells with control flow are invalid, e.g.:
      # - break | less
      # - continue | less
      # - ( return )
      # NOTE: This could be done at parse time too.
      e_die('Invalid control flow %r in pipeline / subshell / background',
            node.token.val, token=node.token)

    # NOTE: If ErrExit(), we could be verbose about subprogram errors?  This
    # only really matters when executing 'exit 42', because the child shell
    # inherits errexit and will be verbose.  Other notes:
    #
    # - We might want errors to fit on a single line so they don't get
    # interleaved.
    # - We could turn the `exit` builtin into a FatalRuntimeError exception and
    # get this check for "free".
    thunk = process.SubProgramThunk(self, node,
                                    disable_errexit=disable_errexit)
    p = process.Process(thunk, job_state=job_state)
    return p
Beispiel #5
0
  def _ValToArith(self, val, span_id):
    """Convert value_t to a Python int or list of strings."""
    assert isinstance(val, value_t), '%r %r' % (val, type(val))

    if val.tag == value_e.Undef:  # 'nounset' already handled before got here
      # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42.
      #log('blame_word %s   arena %s', blame_word, self.arena)
      e_die('Undefined value in arithmetic context', span_id=span_id)
      return 0

    if val.tag == value_e.Str:
      return _StringToInteger(val.s, span_id=span_id)  # calls e_die

    if val.tag == value_e.MaybeStrArray:  # array is valid on RHS, but not LHS
      return val.strs

    if val.tag == value_e.AssocArray:
      return val.d

    if val.tag == value_e.Obj:
      if isinstance(val.obj, int):
        return val.obj
      # NOTE: This doesn't happen because we convert it.
      #elif isinstance(val.obj, str):
      #  return _StringToInteger(val.obj, span_id=span_id)  # calls e_die
      else:
        e_die("Object %r can't be used in shell arirhmetic", val.obj)

    raise AssertionError(val)
Beispiel #6
0
    def _ValToArith(self, val, span_id, int_coerce=True):
        """Convert value_t to a Python int or list of strings."""
        assert isinstance(val, value_t), '%r %r' % (val, type(val))

        if int_coerce:
            if val.tag == value_e.Undef:  # 'nounset' already handled before got here
                # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42.
                #log('blame_word %s   arena %s', blame_word, self.arena)
                e_die('Coercing undefined value to 0 in arithmetic context',
                      span_id=span_id)
                return 0

            if val.tag == value_e.Str:
                # may raise FatalRuntimeError
                return _StringToInteger(val.s, span_id=span_id)

            if val.tag == value_e.StrArray:  # array is valid on RHS, but not on left
                return val.strs

            if val.tag == value_e.AssocArray:
                return val.d

            raise AssertionError(val)

        if val.tag == value_e.Undef:  # 'nounset' already handled before got here
            return ''  # I think nounset is handled elsewhere

        if val.tag == value_e.Str:
            return val.s

        if val.tag == value_e.StrArray:  # array is valid on RHS, but not on left
            return val.strs

        if val.tag == value_e.AssocArray:
            return val.d
Beispiel #7
0
def isatty(fd, s, blame_word):
  # type: (int, str, word_t) -> bool
  try:
    return posix.isatty(fd)
  # fd is user input, and causes this exception in the binding.
  except OverflowError:
    e_die('File descriptor %r is too big', s, word=blame_word)
Beispiel #8
0
    def _MakeProcess(self, node, parent_pipeline=None, inherit_errexit=True):
        # type: (command_t, process.Pipeline, bool) -> process.Process
        """
    Assume we will run the node in another process.  Return a process.
    """
        UP_node = node
        if node.tag_() == command_e.ControlFlow:
            node = cast(command__ControlFlow, UP_node)
            # Pipeline or subshells with control flow are invalid, e.g.:
            # - break | less
            # - continue | less
            # - ( return )
            # NOTE: This could be done at parse time too.
            if node.token.id != Id.ControlFlow_Exit:
                e_die(
                    'Invalid control flow %r in pipeline / subshell / background',
                    node.token.val,
                    token=node.token)

        # NOTE: If ErrExit(), we could be verbose about subprogram errors?  This
        # only really matters when executing 'exit 42', because the child shell
        # inherits errexit and will be verbose.  Other notes:
        #
        # - We might want errors to fit on a single line so they don't get
        # interleaved.
        # - We could turn the `exit` builtin into a FatalRuntimeError exception and
        # get this check for "free".
        thunk = process.SubProgramThunk(self.cmd_ev,
                                        node,
                                        inherit_errexit=inherit_errexit)
        p = process.Process(thunk,
                            self.job_state,
                            parent_pipeline=parent_pipeline)
        return p
Beispiel #9
0
 def Set(self, b):
   """User code calls this."""
   if True in self.stack:  # are we in a temporary state?
     # TODO: Add error context.
     e_die("Can't set 'errexit' in a context where it's disabled "
           "(if, !, && ||, while/until conditions)")
   self.errexit = b
Beispiel #10
0
    def _ValToIntOrError(self, val, blame_word=None, span_id=runtime.NO_SPID):
        # type: (value_t, word_t, int) -> int
        if span_id == runtime.NO_SPID and blame_word:
            span_id = word_.LeftMostSpanForWord(blame_word)
        #log('_ValToIntOrError span=%s blame=%s', span_id, blame_word)

        try:
            if val.tag == value_e.Undef:  # 'nounset' already handled before got here
                # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42.
                #log('blame_word %s   arena %s', blame_word, self.arena)
                e_die('Undefined value in arithmetic context', span_id=span_id)

            if val.tag == value_e.Int:
                return val.i

            if val.tag == value_e.Str:
                return _StringToInteger(val.s, span_id=span_id)  # calls e_die

        except error.FatalRuntime as e:
            if self.exec_opts.strict_arith:
                raise
            else:
                span_id = word_.SpanIdFromError(e)
                self.errfmt.PrettyPrintError(e, prefix='warning: ')
                return 0

        # Arrays and associative arrays always fail -- not controlled by strict_arith.
        # In bash, (( a )) is like (( a[0] )), but I don't want that.
        # And returning '0' gives different results.
        e_die("Expected a value convertible to integer, got %s",
              val,
              span_id=span_id)
Beispiel #11
0
    def EvalWordToString(self, node):
        # type: (arith_expr_t) -> str
        """
    Args:
      node: arith_expr_t

    Returns:
      str

    Raises:
      error.FatalRuntime if the expression isn't a string
      Or if it contains a bare variable like a[x]

    These are allowed because they're unambiguous, unlike a[x]

    a[$x] a["$x"] a["x"] a['x']
    """
        UP_node = node
        if node.tag_(
        ) == arith_expr_e.ArithWord:  # $(( $x )) $(( ${x}${y} )), etc.
            node = cast(arith_expr__ArithWord, UP_node)
            val = self.word_ev.EvalWordToString(node.w)
            return val.s
        else:
            # TODO: location info for orginal
            e_die("Associative array keys must be strings: $x 'x' \"$x\" etc.")
Beispiel #12
0
  def EvalPlusEquals(self, lval, rhs_py):
    lhs_py = self.LookupVar(lval.name)
    if not isinstance(lhs_py, (int, float)):
      # TODO: Could point at the variable name
      e_die("Object of type %r doesn't support +=", lhs_py.__class__.__name__)

    return lhs_py + rhs_py
Beispiel #13
0
  def _MutateClassLiteral(self, node):
    # type: (re_t) -> None
    for i, term in enumerate(node.terms):
      s = None
      if term.tag == class_literal_term_e.SingleQuoted:
        s = word_eval.EvalSingleQuoted(term)
        spid = term.left.span_id

      elif term.tag == class_literal_term_e.DoubleQuoted:
        s = self.word_ev.EvalDoubleQuotedToString(term)
        spid = term.left.span_id

      elif term.tag == class_literal_term_e.BracedVarSub:
        s = self.word_ev.EvalBracedVarSubToString(term)
        spid = term.spids[0]

      elif term.tag == class_literal_term_e.SimpleVarSub:
        s = self.word_ev.EvalSimpleVarSubToString(term.token)
        spid = term.token.span_id

      elif term.tag == class_literal_term_e.CharLiteral:
        # What about \0?
        # At runtime, ERE should disallow it.  But we can also disallow it here.
        node.terms[i] = word_compile.EvalCharLiteralForRegex(term.tok)

      if s is not None:
        # A string like '\x7f\xff' should be presented like
        if len(s) > 1:
          for c in s:
            if ord(c) > 128:
              e_die("Express these bytes as character literals to avoid "
                    "confusing them with encoded characters", span_id=spid)

        node.terms[i] = class_literal_term.ByteSet(s, spid)
Beispiel #14
0
def _LookupVar(name, mem, exec_opts):
    # type: (str, Mem, optview.Exec) -> value_t
    val = mem.GetVar(name)
    # By default, undefined variables are the ZERO value.  TODO: Respect
    # nounset and raise an exception.
    if val.tag_() == value_e.Undef and exec_opts.nounset():
        e_die('Undefined variable %r', name)  # TODO: need token
    return val
Beispiel #15
0
    def EvalWordToString(self, word, do_fnmatch=False, do_ere=False):
        """
    Args:
      word: CompoundWord

    Used for redirect arg, ControlFlow arg, ArithWord, BoolWord, etc.

    do_fnmatch is true for case $pat and RHS of [[ == ]].

    pat="*.py"
    case $x in
      $pat) echo 'matches glob pattern' ;;
      "$pat") echo 'equal to glob string' ;;  # must be glob escaped
    esac

    TODO: Raise AssertionError if it has ExtGlobPart.
    """
        if word.tag == word_e.EmptyWord:
            return value.Str('')

        part_vals = []
        for p in word.parts:
            self._EvalWordPart(p, part_vals, quoted=False)

        strs = []
        for part_val in part_vals:
            if part_val.tag == part_value_e.String:
                # [[ foo == */"*".py ]] or case *.py) ... esac
                if do_fnmatch and not part_val.do_split_glob:
                    s = glob_.GlobEscape(part_val.s)
                elif do_ere and not part_val.do_split_glob:
                    s = glob_.ExtendedRegexEscape(part_val.s)
                else:
                    s = part_val.s
            else:
                if self.exec_opts.strict_array:
                    # Examples: echo f > "$@"; local foo="$@"

                    # TODO: This attributes too coarsely, to the word rather than the
                    # parts.  Problem: the word is a TREE of parts, but we only have a
                    # flat list of part_vals.  The only case where we really get arrays
                    # is "$@", "${a[@]}", "${a[@]//pat/replace}", etc.
                    e_die(
                        "This word should evaluate to a string, but part of it was an "
                        "array",
                        word=word)

                    # TODO: Maybe add detail like this.
                    #e_die('RHS of assignment should only have strings.  '
                    #      'To assign arrays, use b=( "${a[@]}" )')
                else:
                    # It appears to not respect IFS
                    s = ' '.join(s for s in part_val.strs if s is not None)

            strs.append(s)

        return value.Str(''.join(strs))
Beispiel #16
0
  def RunSimpleCommand(self, cmd_val, do_fork, call_procs=True):
    # type: (cmd_value__Argv, bool, bool) -> int
    argv = cmd_val.argv
    span_id = cmd_val.arg_spids[0] if cmd_val.arg_spids else runtime.NO_SPID

    arg0 = argv[0]

    builtin_id = consts.LookupSpecialBuiltin(arg0)
    if builtin_id != consts.NO_INDEX:
      return self.RunBuiltin(builtin_id, cmd_val)

    func_node = self.procs.get(arg0)
    if func_node is not None:
      if (self.exec_opts.strict_errexit() and 
          self.mutable_opts.errexit.SpidIfDisabled() != runtime.NO_SPID):
        # NOTE: This would be checked below, but this gives a better error
        # message.
        e_die("can't disable errexit running a function. "
              "Maybe wrap the function in a process with the at-splice "
              "pattern.", span_id=span_id)

      # NOTE: Functions could call 'exit 42' directly, etc.
      status = self.cmd_ev.RunProc(func_node, argv[1:])
      return status

    builtin_id = consts.LookupNormalBuiltin(arg0)
    if builtin_id != consts.NO_INDEX:
      return self.RunBuiltin(builtin_id, cmd_val)

    # See how many tests will pass
    if mylib.PYTHON:
      import subprocess
      try:
        status = subprocess.call(cmd_val.argv)
      except OSError as e:
        log('Error running %s: %s', cmd_val.argv, e)
        return 1
      return status

    log('Unhandled SimpleCommand')
    f = mylib.Stdout()
    #ast_f = fmt.DetectConsoleOutput(f)
    # Stupid Eclipse debugger doesn't display ANSI
    ast_f = fmt.TextOutput(f)
    tree = cmd_val.PrettyTree()

    ast_f.FileHeader()
    fmt.PrintTree(tree, ast_f)
    ast_f.FileFooter()
    ast_f.write('\n')

    return 0
Beispiel #17
0
  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
Beispiel #18
0
    def _EmptyStrOrError(self, val, token=None):
        assert isinstance(val, value_t), val

        if val.tag == value_e.Undef:
            if self.exec_opts.nounset:
                if token is None:
                    e_die('Undefined variable')
                else:
                    name = token.val[1:] if token.val.startswith(
                        '$') else token.val
                    e_die('Undefined variable %r', name, token=token)
            else:
                return value.Str('')
        else:
            return val
Beispiel #19
0
    def _CheckOilKeyword(self, keyword_id, lval, cell):
        """Check that 'var' and setvar/set are used correctly.

    NOTE: These are dynamic checks, but the syntactic difference between
    definition and mutation will help translate the Oil subset of OSH to static
    languages.
    """

        if cell and keyword_id == Id.KW_Var:
            # TODO: Point at the ORIGINAL declaration!
            e_die("%r has already been declared", lval.name)

        if cell is None and keyword_id in (Id.KW_Set, Id.KW_SetVar):
            # NOTE: all 3 variants of 'lvalue' have 'name'
            e_die("%r hasn't been declared", lval.name)
    def _UnsetVar(self, arg, spid, proc_fallback):
        # type: (str, int, bool) -> bool
        """
    Returns:
      bool: whether the 'unset' builtin should succeed with code 0.
    """
        arena = self.parse_ctx.arena

        a_parser = self.parse_ctx.MakeArithParser(arg)
        arena.PushSource(source.ArgvWord(spid))
        try:
            anode = a_parser.Parse()
        except error.Parse as e:
            # show parse error
            ui.PrettyPrintError(e, arena)
            # point to word
            e_usage('Invalid unset expression', span_id=spid)
        finally:
            arena.PopSource()

        lval = self.arith_ev.EvalArithLhs(anode, spid)

        # Prevent attacks like these by default:
        #
        # unset -v 'A["$(echo K; rm *)"]'
        if not self.exec_opts.eval_unsafe_arith(
        ) and lval.tag_() != lvalue_e.Named:
            e_die(
                'Expected a variable name.  Expressions are allowed with shopt -s eval_unsafe_arith',
                span_id=spid)

        #log('lval %s', lval)
        found = False
        try:
            # not strict
            found = self.mem.Unset(lval, scope_e.Dynamic, False)
        except error.Runtime as e:
            # note: in bash, myreadonly=X fails, but declare myreadonly=X doens't
            # fail because it's a builtin.  So I guess the same is true of 'unset'.
            e.span_id = spid
            ui.PrettyPrintError(e, arena)
            return False

        if proc_fallback and not found:
            if arg in self.funcs:
                del self.funcs[arg]

        return True
Beispiel #21
0
 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)
Beispiel #22
0
    def _EvalLhsAndLookupArith(self, node):
        """
    Args:
      node: lhs_expr
    Returns:
      int or list of strings, lvalue_t
    """
        val, lval = EvalLhsAndLookup(node, self, self.mem, self.exec_opts)

        if val.tag == value_e.StrArray:
            e_die("Can't use assignment like ++ or += on arrays")

        # TODO: attribute a span ID here.  There are a few cases, like UnaryAssign
        # and BinaryAssign.
        span_id = word.SpanForLhsExpr(node)
        i = self._ValToArithOrError(val, span_id=span_id)
        return i, lval
Beispiel #23
0
    def _EvalLhsAndLookupArith(self, node):
        """
    Args:
      node: sh_lhs_expr

    Returns:
      (Python object, lvalue_t)
    """
        val, lval = EvalLhsAndLookup(node, self, self.mem, self.exec_opts)

        if val.tag == value_e.MaybeStrArray:
            e_die("Can't use assignment like ++ or += on arrays")

        # TODO: attribute a span ID here.  There are a few cases, like UnaryAssign
        # and BinaryAssign.
        span_id = word_.SpanForLhsExpr(node)
        i = self._ValToIntOrError(val, span_id=span_id)
        return i, lval
Beispiel #24
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
Beispiel #25
0
    def LookupVar(self, var_name):
        """Convert to a Python object so we can calculate on it natively."""

        # Lookup WITHOUT dynamic scope.
        val = self.mem.GetVar(var_name, lookup_mode=scope_e.LocalOnly)
        if val.tag == value_e.Undef:
            val = self.mem.GetVar(var_name, lookup_mode=scope_e.GlobalOnly)
            if val.tag == value_e.Undef:
                # TODO: Location info
                e_die('Undefined variable %r', var_name)

        if val.tag == value_e.Str:
            return val.s
        if val.tag == value_e.MaybeStrArray:
            return val.strs  # node: has None
        if val.tag == value_e.AssocArray:
            return val.d
        if val.tag == value_e.Obj:
            return val.obj
Beispiel #26
0
    def _ValToIntOrError(self, val, span_id=runtime.NO_SPID):
        # type: (value_t, int) -> int
        try:
            UP_val = val
            with tagswitch(val) as case:
                if case(value_e.Undef
                        ):  # 'nounset' already handled before got here
                    # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42.
                    #log('blame_word %s   arena %s', blame_word, self.arena)
                    e_die('Undefined value in arithmetic context',
                          span_id=span_id)

                elif case(value_e.Int):
                    val = cast(value__Int, UP_val)
                    return val.i

                elif case(value_e.Str):
                    val = cast(value__Str, UP_val)
                    return _StringToInteger(val.s,
                                            span_id=span_id)  # calls e_die

                elif case(value_e.Obj):
                    # Note: this handles var x = 42; echo $(( x > 2 )).
                    if mylib.PYTHON:
                        val = cast(value__Obj, UP_val)
                        if isinstance(val.obj, int):
                            return val.obj
                    raise AssertionError()  # not in C++

        except error.FatalRuntime as e:
            if self.exec_opts.strict_arith():
                raise
            else:
                span_id = word_.SpanIdFromError(e)
                self.errfmt.PrettyPrintError(e, prefix='warning: ')
                return 0

        # Arrays and associative arrays always fail -- not controlled by strict_arith.
        # In bash, (( a )) is like (( a[0] )), but I don't want that.
        # And returning '0' gives different results.
        e_die("Expected a value convertible to integer, got %s",
              ui.ValType(val),
              span_id=span_id)
Beispiel #27
0
def GetOpts(argv, mem):
    """
  Vars to set:
    OPTIND - initialized to 1 at startup
    OPTARG - argument
  Vars used:
    OPTERR: disable printing of error messages
  """
    # TODO: need to handle explicit args.

    try:
        # NOTE: If first char is a colon, error reporting is different.  Alpine
        # might not use that?
        spec_str = argv[0]
        var_name = argv[1]
    except IndexError:
        raise args.UsageError('getopts optstring name [arg]')

    try:
        spec = _GETOPTS_CACHE[spec_str]
    except KeyError:
        spec = _ParseOptSpec(spec_str)
        _GETOPTS_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 = 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 = argv[2:] or mem.GetArgv()
    status, opt_char, optarg, optind = _GetOpts(spec, user_argv, optind)

    # Bug fix: bash-completion uses a *local* OPTIND !  Not global.
    state.SetStringDynamic(mem, var_name, opt_char)
    state.SetStringDynamic(mem, 'OPTARG', optarg)
    state.SetStringDynamic(mem, 'OPTIND', str(optind))
    return status
Beispiel #28
0
  def _ValToArith(self, val, span_id):
    """Convert value_t to a Python int or list of strings."""
    assert isinstance(val, value_t), '%r %r' % (val, type(val))

    if val.tag == value_e.Undef:  # 'nounset' already handled before got here
      # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42.
      #log('blame_word %s   arena %s', blame_word, self.arena)
      e_die('Undefined value in arithmetic context '
            '(0 if shopt -u strict-arith)', span_id=span_id)
      return 0

    if val.tag == value_e.Str:
      return _StringToInteger(val.s, span_id=span_id)  # calls e_die

    if val.tag == value_e.StrArray:  # array is valid on RHS, but not on left
      return val.strs

    if val.tag == value_e.AssocArray:
      return val.d

    raise AssertionError(val)
Beispiel #29
0
def _ClassLiteralToPosixEre(term, parts):
    # type: (class_literal_term_t, List[str]) -> None

    UP_term = term
    tag = term.tag_()

    if tag == class_literal_term_e.Range:
        term = cast(class_literal_term__Range, UP_term)
        # \\ \^ \- can be used in ranges?
        start = glob_.EreCharClassEscape(term.start)
        end = glob_.EreCharClassEscape(term.end)
        parts.append('%s-%s' % (start, end))
        return

    if tag == class_literal_term_e.ByteSet:
        term = cast(class_literal_term__ByteSet, UP_term)
        # This escaping is different than ExtendedRegexEscape.
        parts.append(glob_.EreCharClassEscape(term.bytes))
        return

    if tag == class_literal_term_e.CodePoint:
        term = cast(class_literal_term__CodePoint, UP_term)
        code_point = term.i
        if code_point < 128:
            parts.append(chr(code_point))
        else:
            e_die("ERE can't express code point %d",
                  code_point,
                  span_id=term.spid)
        return

    if tag == class_literal_term_e.PerlClass:
        term = cast(perl_class, UP_term)
        n = term.name
        chars = PERL_CLASS[term.name]  # looks like '[:digit:]'
        if term.negated:
            e_die("Perl classes can't be negated in ERE",
                  span_id=term.negated.span_id)
        else:
            pat = '%s' % chars
        parts.append(pat)
        return

    if tag == class_literal_term_e.PosixClass:
        term = cast(posix_class, UP_term)
        n = term.name  # looks like 'digit'
        if term.negated:
            e_die("POSIX classes can't be negated in ERE",
                  span_id=term.negated.span_id)
        else:
            pat = '[:%s:]' % n
        parts.append(pat)
        return

    if tag == class_literal_term_e.CharLiteral:
        term = cast(class_literal_term__CharLiteral, UP_term)
        parts.append(term.tok.val)
        return

    raise NotImplementedError(tag)
Beispiel #30
0
  def _RunFunc(self, func_node, argv):
    """Used to run SimpleCommand and to run registered completion hooks."""
    # These are redirects at DEFINITION SITE.  You can also have redirects at
    # the CALL SITE.  For example:
    #
    # f() { echo hi; } 1>&2
    # f 2>&1

    try:
      def_redirects = self._EvalRedirects(func_node)
    except util.RedirectEvalError as e:
      ui.PrettyPrintError(e, self.arena)
      return 1

    if def_redirects:
      if not self.fd_state.Push(def_redirects, self.waiter):
        return 1  # error

    self.mem.PushCall(func_node.name, func_node.spids[0], argv)

    # Redirects still valid for functions.
    # Here doc causes a pipe and Process(SubProgramThunk).
    try:
      status = self._Execute(func_node.body)
    except _ControlFlow as e:
      if e.IsReturn():
        status = e.StatusCode()
      else:
        # break/continue used in the wrong place.
        e_die('Unexpected %r (in function call)', e.token.val, token=e.token)
    except (util.FatalRuntimeError, util.ParseError) as e:
      self.dumper.MaybeCollect(self, e)  # Do this before unwinding stack
      raise
    finally:
      self.mem.PopCall()
      if def_redirects:
        self.fd_state.Pop()

    return status