Beispiel #1
0
  def _InitDefaults(self):
    # For some reason this is one of few variables EXPORTED.  bash and dash
    # both do it.  (e.g. env -i -- dash -c env)
    ExportGlobalString(self, 'PWD', posix.getcwd())

    # Default value; user may unset it.
    # $ echo -n "$IFS" | python -c 'import sys;print repr(sys.stdin.read())'
    # ' \t\n'
    SetGlobalString(self, 'IFS', split.DEFAULT_IFS)

    # NOTE: Should we put these in a namespace for Oil?
    SetGlobalString(self, 'UID', str(posix.getuid()))
    SetGlobalString(self, 'EUID', str(posix.geteuid()))

    SetGlobalString(self, 'HOSTNAME', str(libc.gethostname()))

    # In bash, this looks like 'linux-gnu', 'linux-musl', etc.  Scripts test
    # for 'darwin' and 'freebsd' too.  They generally don't like at 'gnu' or
    # 'musl'.  We don't have that info, so just make it 'linux'.
    SetGlobalString(self, 'OSTYPE', str(posix.uname()[0].lower()))

    # For getopts builtin
    SetGlobalString(self, 'OPTIND', '1')

    # For xtrace
    SetGlobalString(self, 'PS4', '+ ')

    # bash-completion uses this.  Value copied from bash.  It doesn't integrate
    # with 'readline' yet.
    SetGlobalString(self, 'COMP_WORDBREAKS', _READLINE_DELIMS)
Beispiel #2
0
  def Get(self, name):
    # type: (str) -> Any
    if name in self.cache:
      return self.cache[name]

    if name == 'euid':  # for \$ and \u
      value = posix.geteuid()
    elif name == 'hostname':  # for \h and \H
      value = libc.gethostname()
    elif name == 'user':  # for \u
      value = _GetUserName(self.Get('euid'))  # recursive call for caching
    else:
      raise AssertionError(name)

    self.cache[name] = value
    return value
Beispiel #3
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 #4
0
 def _GetEuid(self):
     # type: () -> int
     """Cached lookup."""
     if self.euid == -1:
         self.euid = posix.geteuid()
     return self.euid
Beispiel #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)