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)
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 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)