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)
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
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
def _GetEuid(self): # type: () -> int """Cached lookup.""" if self.euid == -1: self.euid = posix.geteuid() return self.euid
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)