コード例 #1
0
ファイル: state.py プロジェクト: waldyrious/oil
    def Lookup(self, name, exec_required=True):
        """
    Returns the path itself (for relative path), the resolve path, or None.
    """
        if '/' in name:
            if path_stat.exists(name):
                return name
            else:
                return None

        # TODO: Could cache this computation to avoid allocating every time for all
        # the splitting.
        path_val = self.mem.GetVar('PATH')
        if path_val.tag == value_e.Str and path_val.s:
            path_list = path_val.s.split(':')
        else:
            path_list = []  # treat as empty path

        for path_dir in path_list:
            full_path = os_path.join(path_dir, name)

            # NOTE: dash and bash only check for EXISTENCE in 'command -v' (and 'type
            # -t').  OSH follows mksh and zsh.  Note that we can still get EPERM if
            # the permissions are changed between check and use.
            if exec_required:
                found = posix.access(full_path, posix.X_OK)
            else:
                found = path_stat.exists(full_path)  # for 'source'

            if found:
                return full_path

        return None
コード例 #2
0
    def Matches(self, comp):
        # type: (Api) -> Iterator[Union[Iterator, Iterator[str]]]
        to_complete = comp.to_complete

        # Problem: .. and ../.. don't complete /.
        # TODO: Set display_pos before fixing this.

        #import os
        #to_complete = os.path.normpath(to_complete)

        dirname, basename = os_path.split(to_complete)
        if dirname == '':  # We're completing in this directory
            to_list = '.'
        else:  # We're completing in some other directory
            to_list = dirname

        if 0:
            log('basename %r', basename)
            log('to_list %r', to_list)
            log('dirname %r', dirname)

        try:
            names = posix.listdir(to_list)
        except OSError as e:
            return  # nothing

        for name in names:
            path = os_path.join(dirname, name)

            if path.startswith(to_complete):
                if self.dirs_only:  # add_slash not used here
                    # NOTE: There is a duplicate isdir() check later to add a trailing
                    # slash.  Consolidate the checks for fewer stat() ops.  This is hard
                    # because all the completion actions must obey the same interface.
                    # We could have another type like candidate = File | Dir |
                    # OtherString ?
                    if path_stat.isdir(path):
                        yield path
                    continue

                if self.exec_only:
                    # TODO: Handle exception if file gets deleted in between listing and
                    # check?
                    if not posix.access(path, posix.X_OK_):
                        continue

                if self.add_slash and path_stat.isdir(path):
                    yield path + '/'
                else:
                    yield path
コード例 #3
0
    def Matches(self, comp):
        # type: (Api) -> Iterator[Union[Iterator, Iterator[str]]]
        """
    TODO: Cache is never cleared.

    - When we get a newer timestamp, we should clear the old one.
    - When PATH is changed, we can remove old entries.
    """
        val = self.mem.GetVar('PATH')
        if val.tag_() != value_e.Str:
            # No matches if not a string
            return
        assert isinstance(val, value__Str)  # for MyPy

        path_dirs = val.s.split(':')
        #log('path: %s', path_dirs)

        executables = []  # type: List[str]
        for d in path_dirs:
            try:
                st = posix.stat(d)
            except OSError as e:
                # There could be a directory that doesn't exist in the $PATH.
                continue
            key = (d, st.st_mtime)

            dir_exes = self.cache.get(key)
            if dir_exes is None:
                entries = posix.listdir(d)
                dir_exes = []
                for name in entries:
                    path = os_path.join(d, name)
                    # TODO: Handle exception if file gets deleted in between listing and
                    # check?
                    if not posix.access(path, posix.X_OK_):
                        continue
                    dir_exes.append(name)  # append the name, not the path

                self.cache[key] = dir_exes

            executables.extend(dir_exes)

        # TODO: Shouldn't do the prefix / space thing ourselves.  readline does
        # that at the END of the line.
        for word in executables:
            if word.startswith(comp.to_complete):
                yield word
コード例 #4
0
ファイル: bool_stat.py プロジェクト: moneytech/oil
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
コード例 #5
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)
コード例 #6
0
ファイル: expr_eval.py プロジェクト: mrshu/oil
    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)