def ParseOil(spec, arg_r):
        # type: (arg_def._OilFlags, Reader) -> Tuple[_Attributes, int]
        out = _Attributes(spec.defaults)

        while not arg_r.AtEnd():
            arg = arg_r.Peek()
            if arg == '--':
                out.saw_double_dash = True
                arg_r.Next()
                break

            if arg == '-':  # a valid argument
                break

            # TODO: Use FLAG_RE above
            if arg.startswith('-'):
                m = libc.regex_match(_FLAG_ERE, arg)
                if m is None:
                    e_usage('Invalid flag syntax: %r' % arg)
                _, flag, val = m  # group 0 is ignored; the whole match

                # TODO: we don't need arity 1 or 0?  Booleans are like --verbose=1,
                # --verbose (equivalent to turning it on) or --verbose=0.

                name = flag.replace('-', '_')
                if name in spec.arity1:  # e.g. read -t1.0
                    action = spec.arity1[name]
                    if val.startswith('='):
                        suffix = val[1:]  # could be empty, but remove = if any
                    else:
                        suffix = None
                    action.OnMatch(None, suffix, arg_r, out)
                else:
                    e_usage('Unrecognized flag %r' % arg)

                arg_r.Next()  # next arg

            else:  # a regular arg
                break

        return out, arg_r.i
Beispiel #2
0
    def testRegexParse(self):
        self.assertEqual(True, libc.regex_parse(r'.*\.py'))

        # Syntax errors
        self.assertRaises(RuntimeError, libc.regex_parse, r'*')
        self.assertRaises(RuntimeError, libc.regex_parse, '\\')
        self.assertRaises(RuntimeError, libc.regex_parse, '{')

        cases = [
            ('([a-z]+)([0-9]+)', 'foo123', ['foo123', 'foo', '123']),
            (r'.*\.py', 'foo.py', ['foo.py']),
            (r'.*\.py', 'abcd', None),
            # The match is unanchored
            (r'bc', 'abcd', ['bc']),
            # The match is unanchored
            (r'.c', 'abcd', ['bc'])
        ]

        for pat, s, expected in cases:
            #print('CASE %s' % pat)
            actual = libc.regex_match(pat, s)
            self.assertEqual(expected, actual)
Beispiel #3
0
    def _EvalMatch(self, left, right, set_match_result):
        """
    Args:
      set_match_result: Whether to assign
    """
        # TODO: Rename EggEx?
        if isinstance(right, str):
            pass
        elif isinstance(right, objects.Regex):
            right = right.AsPosixEre()
        else:
            raise RuntimeError("RHS of ~ should be string or Regex (got %s)" %
                               right.__class__.__name__)

        # TODO: We need an API that can populate _start() and _end() too
        matches = libc.regex_match(right, left)
        if matches:
            if set_match_result:
                self.mem.SetMatches(matches)
            return True
        else:
            if set_match_result:
                self.mem.ClearMatches()
            return False
Beispiel #4
0
    def Eval(self, node):
        # type: (bool_expr_t) -> bool

        UP_node = node
        with tagswitch(node) as case:
            if case(bool_expr_e.WordTest):
                node = cast(bool_expr__WordTest, UP_node)
                s = self._EvalCompoundWord(node.w)
                return bool(s)

            elif case(bool_expr_e.LogicalNot):
                node = cast(bool_expr__LogicalNot, UP_node)
                b = self.Eval(node.child)
                return not b

            elif case(bool_expr_e.LogicalAnd):
                node = cast(bool_expr__LogicalAnd, UP_node)
                # Short-circuit evaluation
                if self.Eval(node.left):
                    return self.Eval(node.right)
                else:
                    return False

            elif case(bool_expr_e.LogicalOr):
                node = cast(bool_expr__LogicalOr, UP_node)
                if self.Eval(node.left):
                    return True
                else:
                    return self.Eval(node.right)

            elif case(bool_expr_e.Unary):
                node = cast(bool_expr__Unary, UP_node)
                op_id = node.op_id
                s = self._EvalCompoundWord(node.child)

                # Now dispatch on arg type
                arg_type = consts.BoolArgType(
                    op_id)  # could be static in the LST?

                if arg_type == bool_arg_type_e.Path:
                    return bool_stat.DoUnaryOp(op_id, s)

                if arg_type == bool_arg_type_e.Str:
                    if op_id == Id.BoolUnary_z:
                        return not bool(s)
                    if op_id == Id.BoolUnary_n:
                        return bool(s)

                    raise AssertionError(op_id)  # should never happen

                if arg_type == bool_arg_type_e.Other:
                    if op_id == Id.BoolUnary_t:
                        try:
                            fd = int(s)
                        except ValueError:
                            # TODO: Need location information of [
                            e_die('Invalid file descriptor %r',
                                  s,
                                  word=node.child)
                        return bool_stat.isatty(fd, s, node.child)

                    # See whether 'set -o' options have been set
                    if op_id == Id.BoolUnary_o:
                        index = match.MatchOption(s)
                        if index == 0:
                            return False
                        else:
                            return self.exec_opts.opt_array[index]

                    e_die("%s isn't implemented",
                          ui.PrettyId(op_id))  # implicit location

                raise AssertionError(arg_type)  # should never happen

            elif case(bool_expr_e.Binary):
                node = cast(bool_expr__Binary, UP_node)

                op_id = node.op_id
                # Whether to glob escape
                with switch(op_id) as case2:
                    if case2(Id.BoolBinary_GlobEqual, Id.BoolBinary_GlobDEqual,
                             Id.BoolBinary_GlobNEqual):
                        quote_kind = quote_e.FnMatch
                    elif case2(Id.BoolBinary_EqualTilde):
                        quote_kind = quote_e.ERE
                    else:
                        quote_kind = quote_e.Default

                s1 = self._EvalCompoundWord(node.left)
                s2 = self._EvalCompoundWord(node.right, quote_kind=quote_kind)

                # Now dispatch on arg type
                arg_type = consts.BoolArgType(op_id)

                if arg_type == bool_arg_type_e.Path:
                    return bool_stat.DoBinaryOp(op_id, s1, s2)

                if arg_type == bool_arg_type_e.Int:
                    # NOTE: We assume they are constants like [[ 3 -eq 3 ]].
                    # Bash also allows [[ 1+2 -eq 3 ]].
                    i1 = self._StringToIntegerOrError(s1, blame_word=node.left)
                    i2 = self._StringToIntegerOrError(s2,
                                                      blame_word=node.right)

                    if op_id == Id.BoolBinary_eq:
                        return i1 == i2
                    if op_id == Id.BoolBinary_ne:
                        return i1 != i2
                    if op_id == Id.BoolBinary_gt:
                        return i1 > i2
                    if op_id == Id.BoolBinary_ge:
                        return i1 >= i2
                    if op_id == Id.BoolBinary_lt:
                        return i1 < i2
                    if op_id == Id.BoolBinary_le:
                        return i1 <= i2

                    raise AssertionError(op_id)  # should never happen

                if arg_type == bool_arg_type_e.Str:

                    if op_id in (Id.BoolBinary_GlobEqual,
                                 Id.BoolBinary_GlobDEqual):
                        #log('Matching %s against pattern %s', s1, s2)
                        return libc.fnmatch(s2, s1)

                    if op_id == Id.BoolBinary_GlobNEqual:
                        return not libc.fnmatch(s2, s1)

                    if op_id in (Id.BoolBinary_Equal, Id.BoolBinary_DEqual):
                        return s1 == s2

                    if op_id == Id.BoolBinary_NEqual:
                        return s1 != s2

                    if op_id == Id.BoolBinary_EqualTilde:
                        # TODO: This should go to --debug-file
                        #log('Matching %r against regex %r', s1, s2)
                        try:
                            matches = libc.regex_match(s2, s1)
                        except RuntimeError:
                            # Status 2 indicates a regex parse error.  This is fatal in OSH but
                            # not in bash, which treats [[ like a command with an exit code.
                            e_die("Invalid regex %r",
                                  s2,
                                  word=node.right,
                                  status=2)

                        if matches is None:
                            return False

                        self._SetRegexMatches(matches)
                        return True

                    if op_id == Id.Op_Less:
                        return s1 < s2

                    if op_id == Id.Op_Great:
                        return s1 > s2

                    raise AssertionError(op_id)  # should never happen

        raise AssertionError(node.tag_())
Beispiel #5
0
  def testPatSubRegexesLibc(self):
    r = libc.regex_parse('^(.*)git.*(.*)')
    print(r)

    # It matches.  But we need to get the positions out!
    print(libc.regex_match('^(.*)git.*(.*)', '~/git/oil'))
Beispiel #6
0
    def Eval(self, node):
        #print('!!', node.tag)

        if node.tag == bool_expr_e.WordTest:
            s = self._EvalCompoundWord(node.w)
            return bool(s)

        if node.tag == bool_expr_e.LogicalNot:
            b = self.Eval(node.child)
            return not b

        if node.tag == bool_expr_e.LogicalAnd:
            # Short-circuit evaluation
            if self.Eval(node.left):
                return self.Eval(node.right)
            else:
                return False

        if node.tag == bool_expr_e.LogicalOr:
            if self.Eval(node.left):
                return True
            else:
                return self.Eval(node.right)

        if node.tag == bool_expr_e.BoolUnary:
            op_id = node.op_id
            s = self._EvalCompoundWord(node.child)

            # Now dispatch on arg type
            arg_type = BOOL_OPS[op_id]
            if arg_type == OperandType.Path:
                try:
                    mode = os.stat(s).st_mode
                except OSError as e:  # Python 3: FileNotFoundError
                    # TODO: Signal extra debug information?
                    #self._AddErrorContext("Error from stat(%r): %s" % (s, e))
                    return False

                if op_id == Id.BoolUnary_f:
                    return stat.S_ISREG(mode)

            if arg_type == OperandType.Str:
                if op_id == Id.BoolUnary_z:
                    return not bool(s)
                if op_id == Id.BoolUnary_n:
                    return bool(s)

                raise NotImplementedError(op_id)

            raise NotImplementedError(arg_type)

        #if node.id == Id.Node_BinaryExpr:
        if node.tag == bool_expr_e.BoolBinary:
            op_id = node.op_id

            s1 = self._EvalCompoundWord(node.left)
            # Whehter to glob escape
            do_fnmatch = op_id in (Id.BoolBinary_Equal, Id.BoolBinary_DEqual,
                                   Id.BoolBinary_NEqual)
            s2 = self._EvalCompoundWord(node.right, do_fnmatch=do_fnmatch)

            # Now dispatch on arg type
            arg_type = BOOL_OPS[op_id]

            if arg_type == OperandType.Path:
                st1 = os.stat(s1)
                st2 = os.stat(s2)

                if op_id == Id.BoolBinary_nt:
                    return True  # TODO: test newer than (mtime)

            if arg_type == OperandType.Int:
                # NOTE: We assume they are constants like [[ 3 -eq 3 ]].
                # Bash also allows [[ 1+2 -eq 3 ]].
                i1 = self._StringToIntegerOrError(s1)
                i2 = self._StringToIntegerOrError(s2)

                if op_id == Id.BoolBinary_eq:
                    return i1 == i2
                if op_id == Id.BoolBinary_ne:
                    return i1 != i2

                raise NotImplementedError(op_id)

            if arg_type == OperandType.Str:
                # TODO:
                # - Compare arrays.  (Although bash coerces them to string first)

                if op_id in (Id.BoolBinary_Equal, Id.BoolBinary_DEqual):
                    #log('Comparing %s and %s', s2, s1)
                    return libc.fnmatch(s2, s1)

                if op_id == Id.BoolBinary_NEqual:
                    return not libc.fnmatch(s2, s1)

                if op_id == Id.BoolBinary_EqualTilde:
                    # NOTE: regex matching can't fail if compilation succeeds.
                    match = libc.regex_match(s2, s1)
                    # TODO: BASH_REMATCH or REGEX_MATCH
                    if match == 1:
                        self._SetRegexMatches('TODO')
                        is_match = True
                    elif match == 0:
                        is_match = False
                    elif match == -1:
                        raise AssertionError(
                            "Invalid regex %r: should have been caught at compile time"
                            % s2)
                    else:
                        raise AssertionError

                    return is_match

                if op_id == Id.Redir_Less:  # pun
                    return s1 < s2

                if op_id == Id.Redir_Great:  # pun
                    return s1 > s2

                raise NotImplementedError(op_id)

        # We could have govered all node IDs
        raise AssertionError(IdName(node.id))
Beispiel #7
0
  def Eval(self, node):
    #print('!!', node.tag)

    if node.tag == bool_expr_e.WordTest:
      s = self._EvalCompoundWord(node.w)
      return bool(s)

    if node.tag == bool_expr_e.LogicalNot:
      b = self.Eval(node.child)
      return not b

    if node.tag == bool_expr_e.LogicalAnd:
      # Short-circuit evaluation
      if self.Eval(node.left):
        return self.Eval(node.right)
      else:
        return False

    if node.tag == bool_expr_e.LogicalOr:
      if self.Eval(node.left):
        return True
      else:
        return self.Eval(node.right)

    if node.tag == bool_expr_e.BoolUnary:
      op_id = node.op_id
      s = self._EvalCompoundWord(node.child)

      # Now dispatch on arg type
      arg_type = BOOL_ARG_TYPES[op_id]  # could be static in the LST?

      if arg_type == bool_arg_type_e.Path:
        # Only use lstat if we're testing for a symlink.
        if op_id in (Id.BoolUnary_h, Id.BoolUnary_L):
          try:
            mode = posix.lstat(s).st_mode
          except OSError:
            return False

          return stat.S_ISLNK(mode)

        try:
          mode = posix.stat(s).st_mode
        except OSError:
          # TODO: Signal extra debug information?
          #log("Error from stat(%r): %s" % (s, e))
          return False

        if op_id in (Id.BoolUnary_e, Id.BoolUnary_a):  # -a is alias for -e
          return True

        if op_id == Id.BoolUnary_f:
          return stat.S_ISREG(mode)

        if op_id == Id.BoolUnary_d:
          return stat.S_ISDIR(mode)

        if op_id == Id.BoolUnary_x:
          return posix.access(s, posix.X_OK)

        if op_id == Id.BoolUnary_r:
          return posix.access(s, posix.R_OK)

        if op_id == Id.BoolUnary_w:
          return posix.access(s, posix.W_OK)

        raise NotImplementedError(op_id)

      if arg_type == bool_arg_type_e.Str:
        if op_id == Id.BoolUnary_z:
          return not bool(s)
        if op_id == Id.BoolUnary_n:
          return bool(s)

        raise NotImplementedError(op_id)

      if arg_type == bool_arg_type_e.Other:
        if op_id == Id.BoolUnary_t:
          try:
            fd = int(s)
          except ValueError:
            # TODO: Need location information of [
            e_die('Invalid file descriptor %r', s)
          return posix.isatty(fd)

        raise NotImplementedError(op_id)

      raise NotImplementedError(arg_type)

    if node.tag == bool_expr_e.BoolBinary:
      op_id = node.op_id

      s1 = self._EvalCompoundWord(node.left)
      # Whether to glob escape
      do_fnmatch = op_id in (Id.BoolBinary_GlobEqual, Id.BoolBinary_GlobDEqual,
                             Id.BoolBinary_GlobNEqual)
      do_ere = (op_id == Id.BoolBinary_EqualTilde)
      s2 = self._EvalCompoundWord(node.right, do_fnmatch=do_fnmatch,
                                  do_ere=do_ere)

      # Now dispatch on arg type
      arg_type = BOOL_ARG_TYPES[op_id]

      if arg_type == bool_arg_type_e.Path:
        st1 = posix.stat(s1)
        st2 = posix.stat(s2)

        # TODO: test newer than (mtime)
        if op_id == Id.BoolBinary_nt:
          return st1[stat.ST_MTIME] > st2[stat.ST_MTIME]
        if op_id == Id.BoolBinary_ot:
          return st1[stat.ST_MTIME] < st2[stat.ST_MTIME]

        raise NotImplementedError(op_id)

      if arg_type == bool_arg_type_e.Int:
        # NOTE: We assume they are constants like [[ 3 -eq 3 ]].
        # Bash also allows [[ 1+2 -eq 3 ]].
        i1 = self._StringToIntegerOrError(s1, blame_word=node.left)
        i2 = self._StringToIntegerOrError(s2, blame_word=node.right)

        if op_id == Id.BoolBinary_eq:
          return i1 == i2
        if op_id == Id.BoolBinary_ne:
          return i1 != i2
        if op_id == Id.BoolBinary_gt:
          return i1 > i2
        if op_id == Id.BoolBinary_ge:
          return i1 >= i2
        if op_id == Id.BoolBinary_lt:
          return i1 < i2
        if op_id == Id.BoolBinary_le:
          return i1 <= i2

        raise NotImplementedError(op_id)

      if arg_type == bool_arg_type_e.Str:

        if op_id in (Id.BoolBinary_GlobEqual, Id.BoolBinary_GlobDEqual):
          #log('Matching %s against pattern %s', s1, s2)

          # TODO: Respect extended glob?  * and ! and ? are quoted improperly.
          # But @ and + are OK.
          return libc.fnmatch(s2, s1)

        if op_id == Id.BoolBinary_GlobNEqual:
          return not libc.fnmatch(s2, s1)

        if op_id in (Id.BoolBinary_Equal, Id.BoolBinary_DEqual):
          return s1 == s2

        if op_id == Id.BoolBinary_NEqual:
          return s1 != s2

        if op_id == Id.BoolBinary_EqualTilde:
          #log('Matching %r against regex %r', s1, s2)
          try:
            matches = libc.regex_match(s2, s1)
          except RuntimeError:
            # 2 means a parse error.  Note this is a fatal error in OSH but not
            # in bash.
            e_die("Invalid regex %r", s2, word=node.right, status=2)

          if matches is None:
            return False

          self._SetRegexMatches(matches)
          return True

        if op_id == Id.Redir_Less:  # pun
          return s1 < s2

        if op_id == Id.Redir_Great:  # pun
          return s1 > s2

        raise NotImplementedError(op_id)

    raise AssertionError(node.tag)
Beispiel #8
0
  def Eval(self, node):
    #print('!!', node.tag)

    if node.tag == bool_expr_e.WordTest:
      s = self._EvalCompoundWord(node.w)
      return bool(s)

    if node.tag == bool_expr_e.LogicalNot:
      b = self.Eval(node.child)
      return not b

    if node.tag == bool_expr_e.LogicalAnd:
      # Short-circuit evaluation
      if self.Eval(node.left):
        return self.Eval(node.right)
      else:
        return False

    if node.tag == bool_expr_e.LogicalOr:
      if self.Eval(node.left):
        return True
      else:
        return self.Eval(node.right)

    if node.tag == bool_expr_e.Unary:
      op_id = node.op_id
      s = self._EvalCompoundWord(node.child)

      # Now dispatch on arg type
      arg_type = BOOL_ARG_TYPES[op_id.enum_id]  # could be static in the LST?

      if arg_type == bool_arg_type_e.Path:
        # Only use lstat if we're testing for a symlink.
        if op_id in (Id.BoolUnary_h, Id.BoolUnary_L):
          try:
            mode = posix.lstat(s).st_mode
          except OSError:
            # TODO: simple_test_builtin should this as status=2.
            #e_die("lstat() error: %s", e, word=node.child)
            return False

          return stat.S_ISLNK(mode)

        try:
          st = posix.stat(s)
        except OSError as e:
          # TODO: simple_test_builtin should this as status=2.
          # Problem: we really need errno, because test -f / is bad argument,
          # while test -f /nonexistent is a good argument but failed.  Gah.
          # ENOENT vs. ENAMETOOLONG.
          #e_die("stat() error: %s", e, word=node.child)
          return False
        mode = st.st_mode

        if op_id in (Id.BoolUnary_e, Id.BoolUnary_a):  # -a is alias for -e
          return True

        if op_id == Id.BoolUnary_f:
          return stat.S_ISREG(mode)

        if op_id == Id.BoolUnary_d:
          return stat.S_ISDIR(mode)

        if op_id == Id.BoolUnary_b:
          return stat.S_ISBLK(mode)

        if op_id == Id.BoolUnary_c:
          return stat.S_ISCHR(mode)

        if op_id == Id.BoolUnary_p:
          return stat.S_ISFIFO(mode)

        if op_id == Id.BoolUnary_S:
          return stat.S_ISSOCK(mode)

        if op_id == Id.BoolUnary_x:
          return posix.access(s, posix.X_OK)

        if op_id == Id.BoolUnary_r:
          return posix.access(s, posix.R_OK)

        if op_id == Id.BoolUnary_w:
          return posix.access(s, posix.W_OK)

        if op_id == Id.BoolUnary_s:
          return st.st_size != 0

        if op_id == Id.BoolUnary_O:
          return st.st_uid == posix.geteuid()

        if op_id == Id.BoolUnary_G:
          return st.st_gid == posix.getegid()

        e_die("%s isn't implemented", op_id)  # implicit location

      if arg_type == bool_arg_type_e.Str:
        if op_id == Id.BoolUnary_z:
          return not bool(s)
        if op_id == Id.BoolUnary_n:
          return bool(s)

        raise AssertionError(op_id)  # should never happen

      if arg_type == bool_arg_type_e.Other:
        if op_id == Id.BoolUnary_t:
          try:
            fd = int(s)
          except ValueError:
            # TODO: Need location information of [
            e_die('Invalid file descriptor %r', s, word=node.child)
          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=node.child)

        # See whether 'set -o' options have been set
        if op_id == Id.BoolUnary_o:
          b = getattr(self.exec_opts, s, None)
          return False if b is None else b

        e_die("%s isn't implemented", op_id)  # implicit location

      raise AssertionError(arg_type)  # should never happen

    if node.tag == bool_expr_e.Binary:
      op_id = node.op_id

      s1 = self._EvalCompoundWord(node.left)
      # Whether to glob escape
      do_fnmatch = op_id in (Id.BoolBinary_GlobEqual, Id.BoolBinary_GlobDEqual,
                             Id.BoolBinary_GlobNEqual)
      do_ere = (op_id == Id.BoolBinary_EqualTilde)
      s2 = self._EvalCompoundWord(node.right, do_fnmatch=do_fnmatch,
                                  do_ere=do_ere)

      # Now dispatch on arg type
      arg_type = BOOL_ARG_TYPES[op_id.enum_id]

      if arg_type == bool_arg_type_e.Path:
        try:
          st1 = posix.stat(s1)
        except OSError:
          st1 = None
        try:
          st2 = posix.stat(s2)
        except OSError:
          st2 = None

        if op_id in (Id.BoolBinary_nt, Id.BoolBinary_ot):
          # pretend it's a very old file
          m1 = 0 if st1 is None else st1.st_mtime
          m2 = 0 if st2 is None else st2.st_mtime
          if op_id == Id.BoolBinary_nt:
            return m1 > m2
          else:
            return m1 < m2

        if op_id == Id.BoolBinary_ef:
          if st1 is None:
            return False
          if st2 is None:
            return False
          return st1.st_dev == st2.st_dev and st1.st_ino == st2.st_ino

        raise AssertionError(op_id)

      if arg_type == bool_arg_type_e.Int:
        # NOTE: We assume they are constants like [[ 3 -eq 3 ]].
        # Bash also allows [[ 1+2 -eq 3 ]].
        i1 = self._StringToIntegerOrError(s1, blame_word=node.left)
        i2 = self._StringToIntegerOrError(s2, blame_word=node.right)

        if op_id == Id.BoolBinary_eq:
          return i1 == i2
        if op_id == Id.BoolBinary_ne:
          return i1 != i2
        if op_id == Id.BoolBinary_gt:
          return i1 > i2
        if op_id == Id.BoolBinary_ge:
          return i1 >= i2
        if op_id == Id.BoolBinary_lt:
          return i1 < i2
        if op_id == Id.BoolBinary_le:
          return i1 <= i2

        raise AssertionError(op_id)  # should never happen

      if arg_type == bool_arg_type_e.Str:

        if op_id in (Id.BoolBinary_GlobEqual, Id.BoolBinary_GlobDEqual):
          #log('Matching %s against pattern %s', s1, s2)
          return libc.fnmatch(s2, s1)

        if op_id == Id.BoolBinary_GlobNEqual:
          return not libc.fnmatch(s2, s1)

        if op_id in (Id.BoolBinary_Equal, Id.BoolBinary_DEqual):
          return s1 == s2

        if op_id == Id.BoolBinary_NEqual:
          return s1 != s2

        if op_id == Id.BoolBinary_EqualTilde:
          # TODO: This should go to --debug-file
          #log('Matching %r against regex %r', s1, s2)
          try:
            matches = libc.regex_match(s2, s1)
          except RuntimeError:
            # Status 2 indicates a regex parse error.  This is fatal in OSH but
            # not in bash, which treats [[ like a command with an exit code.
            e_die("Invalid regex %r", s2, word=node.right, status=2)

          if matches is None:
            return False

          self._SetRegexMatches(matches)
          return True

        if op_id == Id.Op_Less:
          return s1 < s2

        if op_id == Id.Op_Great:
          return s1 > s2

        raise AssertionError(op_id)  # should never happen

    raise AssertionError(node.tag)
Beispiel #9
0
    def Eval(self, node):
        #print('!!', node.tag)

        if node.tag == bool_expr_e.WordTest:
            s = self._EvalCompoundWord(node.w)
            return bool(s)

        if node.tag == bool_expr_e.LogicalNot:
            b = self.Eval(node.child)
            return not b

        if node.tag == bool_expr_e.LogicalAnd:
            # Short-circuit evaluation
            if self.Eval(node.left):
                return self.Eval(node.right)
            else:
                return False

        if node.tag == bool_expr_e.LogicalOr:
            if self.Eval(node.left):
                return True
            else:
                return self.Eval(node.right)

        if node.tag == bool_expr_e.BoolUnary:
            op_id = node.op_id
            s = self._EvalCompoundWord(node.child)

            # Now dispatch on arg type
            arg_type = BOOL_ARG_TYPES[op_id]  # could be static in the LST?

            if arg_type == bool_arg_type_e.Path:
                # Only use lstat if we're testing for a symlink.
                if op_id in (Id.BoolUnary_h, Id.BoolUnary_L):
                    try:
                        mode = os.lstat(s).st_mode
                    except OSError:
                        return False

                    return stat.S_ISLNK(mode)

                try:
                    mode = os.stat(s).st_mode
                except OSError:
                    # TODO: Signal extra debug information?
                    #self._AddErrorContext("Error from stat(%r): %s" % (s, e))
                    return False

                if op_id in (Id.BoolUnary_e,
                             Id.BoolUnary_a):  # -a is alias for -e
                    return True

                if op_id == Id.BoolUnary_f:
                    return stat.S_ISREG(mode)

                if op_id == Id.BoolUnary_d:
                    return stat.S_ISDIR(mode)

                if op_id == Id.BoolUnary_x:
                    return os.access(s, os.X_OK)

                if op_id == Id.BoolUnary_r:
                    return os.access(s, os.R_OK)

                if op_id == Id.BoolUnary_w:
                    return os.access(s, os.W_OK)

                raise NotImplementedError(op_id)

            if arg_type == bool_arg_type_e.Str:
                if op_id == Id.BoolUnary_z:
                    return not bool(s)
                if op_id == Id.BoolUnary_n:
                    return bool(s)

                raise NotImplementedError(op_id)

            if arg_type == bool_arg_type_e.Other:
                if op_id == Id.BoolUnary_t:
                    try:
                        fd = int(s)
                    except ValueError:
                        # TODO: Need location information of [
                        e_die('Invalid file descriptor %r', s)
                    return os.isatty(fd)

                raise NotImplementedError(op_id)

            raise NotImplementedError(arg_type)

        if node.tag == bool_expr_e.BoolBinary:
            op_id = node.op_id

            s1 = self._EvalCompoundWord(node.left)
            # Whehter to glob escape
            do_fnmatch = op_id in (Id.BoolBinary_GlobEqual,
                                   Id.BoolBinary_GlobDEqual,
                                   Id.BoolBinary_GlobNEqual)
            s2 = self._EvalCompoundWord(node.right, do_fnmatch=do_fnmatch)

            # Now dispatch on arg type
            arg_type = BOOL_ARG_TYPES[op_id]

            if arg_type == bool_arg_type_e.Path:
                st1 = os.stat(s1)
                st2 = os.stat(s2)

                # TODO: test newer than (mtime)
                if op_id == Id.BoolBinary_nt:
                    return st1[stat.ST_MTIME] > st2[stat.ST_MTIME]
                if op_id == Id.BoolBinary_ot:
                    return st1[stat.ST_MTIME] < st2[stat.ST_MTIME]

                raise NotImplementedError(op_id)

            if arg_type == bool_arg_type_e.Int:
                # NOTE: We assume they are constants like [[ 3 -eq 3 ]].
                # Bash also allows [[ 1+2 -eq 3 ]].
                i1 = self._StringToIntegerOrError(s1)
                i2 = self._StringToIntegerOrError(s2)

                if op_id == Id.BoolBinary_eq:
                    return i1 == i2
                if op_id == Id.BoolBinary_ne:
                    return i1 != i2
                if op_id == Id.BoolBinary_gt:
                    return i1 > i2
                if op_id == Id.BoolBinary_ge:
                    return i1 >= i2
                if op_id == Id.BoolBinary_lt:
                    return i1 < i2
                if op_id == Id.BoolBinary_le:
                    return i1 <= i2

                raise NotImplementedError(op_id)

            if arg_type == bool_arg_type_e.Str:
                # TODO:
                # - Compare arrays.  (Although bash coerces them to string first)

                if op_id in (Id.BoolBinary_GlobEqual,
                             Id.BoolBinary_GlobDEqual):
                    #log('Comparing %s and %s', s2, s1)
                    return libc.fnmatch(s2, s1)

                if op_id == Id.BoolBinary_GlobNEqual:
                    return not libc.fnmatch(s2, s1)

                if op_id in (Id.BoolBinary_Equal, Id.BoolBinary_DEqual):
                    return s1 == s2

                if op_id == Id.BoolBinary_NEqual:
                    return s1 != s2

                if op_id == Id.BoolBinary_EqualTilde:
                    # NOTE: regex matching can't fail if compilation succeeds.
                    match = libc.regex_match(s2, s1)
                    # TODO: BASH_REMATCH or REGEX_MATCH
                    if match == 1:
                        self._SetRegexMatches('TODO')
                        is_match = True
                    elif match == 0:
                        is_match = False
                    elif match == -1:
                        raise AssertionError(
                            "Invalid regex %r: should have been caught at compile time"
                            % s2)
                    else:
                        raise AssertionError

                    return is_match

                if op_id == Id.Redir_Less:  # pun
                    return s1 < s2

                if op_id == Id.Redir_Great:  # pun
                    return s1 > s2

                raise NotImplementedError(op_id)

        raise AssertionError(node.tag)