Beispiel #1
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 #2
0
def DoUnaryOp(op_id, s):
  # type: (Id_t, str) -> bool

  # 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
Beispiel #3
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.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:
                        return False

                    return stat.S_ISLNK(mode)

                try:
                    st = posix.stat(s)
                except OSError:
                    # TODO: Signal extra debug information?
                    #log("Error from stat(%r): %s" % (s, e))
                    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_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

                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, word=node.child)
                    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.enum_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:
                    # 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.Redir_Less:  # pun
                    return s1 < s2

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

                raise NotImplementedError(op_id)

        raise AssertionError(node.tag)