OILS / osh / bool_stat.py View on Github | oilshell.org

135 lines, 85 significant
1#!/usr/bin/env python2
2"""Bool_stat.py.
3
4Not translating this file directly.
5"""
6from __future__ import print_function
7
8import stat
9import posix_ as posix
10from posix_ import X_OK, R_OK, W_OK # refers directly to C macro
11from _devbuild.gen.id_kind_asdl import Id, Id_t
12from _devbuild.gen.syntax_asdl import word_t, loc
13from core.error import e_die
14from core import ui
15
16
17def isatty(fd_str, blame_word):
18 # type: (str, word_t) -> bool
19 try:
20 fd = int(fd_str)
21 except ValueError:
22 e_die('Invalid file descriptor %r' % fd_str, loc.Word(blame_word))
23
24 try:
25 return posix.isatty(fd)
26 # fd is user input, and causes this exception in the binding.
27 except OverflowError:
28 e_die('File descriptor %r is too big' % fd_str, loc.Word(blame_word))
29
30
31def DoUnaryOp(op_id, s):
32 # type: (Id_t, str) -> bool
33
34 # Only use lstat if we're testing for a symlink.
35 if op_id in (Id.BoolUnary_h, Id.BoolUnary_L):
36 try:
37 mode = posix.lstat(s).st_mode
38 except OSError:
39 # TODO: simple_test_builtin should this as status=2.
40 #e_die("lstat() error: %s", e, word=node.child)
41 return False
42
43 return stat.S_ISLNK(mode)
44
45 try:
46 st = posix.stat(s)
47 except OSError as e:
48 # TODO: simple_test_builtin should this as status=2.
49 # Problem: we really need errno, because test -f / is bad argument,
50 # while test -f /nonexistent is a good argument but failed. Gah.
51 # ENOENT vs. ENAMETOOLONG.
52 #e_die("stat() error: %s", e, word=node.child)
53 return False
54 mode = st.st_mode
55
56 if op_id in (Id.BoolUnary_e, Id.BoolUnary_a): # -a is alias for -e
57 return True
58
59 if op_id == Id.BoolUnary_b:
60 return stat.S_ISBLK(mode)
61
62 if op_id == Id.BoolUnary_c:
63 return stat.S_ISCHR(mode)
64
65 if op_id == Id.BoolUnary_d:
66 return stat.S_ISDIR(mode)
67
68 if op_id == Id.BoolUnary_f:
69 return stat.S_ISREG(mode)
70
71 if op_id == Id.BoolUnary_g:
72 return bool(stat.S_IMODE(mode) & stat.S_ISGID)
73
74 if op_id == Id.BoolUnary_k:
75 # need 'bool' for MyPy
76 return bool(stat.S_IMODE(mode) & stat.S_ISVTX)
77
78 if op_id == Id.BoolUnary_p:
79 return stat.S_ISFIFO(mode)
80
81 if op_id == Id.BoolUnary_r:
82 return posix.access(s, R_OK)
83
84 if op_id == Id.BoolUnary_s:
85 return st.st_size != 0
86
87 if op_id == Id.BoolUnary_u:
88 return bool(stat.S_IMODE(mode) & stat.S_ISUID)
89
90 if op_id == Id.BoolUnary_w:
91 return posix.access(s, W_OK)
92
93 if op_id == Id.BoolUnary_x:
94 return posix.access(s, X_OK)
95
96 if op_id == Id.BoolUnary_G:
97 return st.st_gid == posix.getegid()
98
99 if op_id == Id.BoolUnary_O:
100 return st.st_uid == posix.geteuid()
101
102 if op_id == Id.BoolUnary_S:
103 return stat.S_ISSOCK(mode)
104
105 e_die("%s isn't implemented" % ui.PrettyId(op_id), loc.Missing)
106
107
108def DoBinaryOp(op_id, s1, s2):
109 # type: (Id_t, str, str) -> bool
110 try:
111 st1 = posix.stat(s1)
112 except OSError:
113 st1 = None
114 try:
115 st2 = posix.stat(s2)
116 except OSError:
117 st2 = None
118
119 if op_id in (Id.BoolBinary_nt, Id.BoolBinary_ot):
120 # pretend it's a very old file
121 m1 = 0 if st1 is None else st1.st_mtime
122 m2 = 0 if st2 is None else st2.st_mtime
123 if op_id == Id.BoolBinary_nt:
124 return m1 > m2
125 else:
126 return m1 < m2
127
128 if op_id == Id.BoolBinary_ef:
129 if st1 is None:
130 return False
131 if st2 is None:
132 return False
133 return st1.st_dev == st2.st_dev and st1.st_ino == st2.st_ino
134
135 raise AssertionError(op_id)