Line | Count | Source (jump to first uncovered line) |
1 | | // osh.cc |
2 | | |
3 | | #include "cpp/osh.h" |
4 | | |
5 | | #include <fcntl.h> // AT_* Constants |
6 | | #include <sys/stat.h> |
7 | | #include <unistd.h> |
8 | | |
9 | | #include "mycpp/gc_builtins.h" |
10 | | // To avoid circular dependency with e_die() |
11 | | #include "prebuilt/core/error.mycpp.h" |
12 | | |
13 | | using error::e_die; |
14 | | using id_kind_asdl::Id; // used below |
15 | | using syntax_asdl::loc; |
16 | | |
17 | | namespace arith_parse { |
18 | | |
19 | | GcGlobal<tdop::ParserSpec> kArithSpec_ = { |
20 | | ObjHeader::Global(TypeTag::OtherClass)}; |
21 | | tdop::ParserSpec* kArithSpec = &kArithSpec_.obj; |
22 | | |
23 | | } // namespace arith_parse |
24 | | |
25 | | namespace bool_stat { |
26 | | |
27 | 2 | bool isatty(BigStr* fd_str, word_t* blame_word) { |
28 | 2 | int fd; |
29 | 2 | try { |
30 | 2 | fd = to_int(fd_str); |
31 | 2 | } catch (ValueError* e) { |
32 | 1 | e_die(StrFormat("Invalid file descriptor %r", fd_str), |
33 | 1 | Alloc<loc::Word>(blame_word)); |
34 | 1 | } |
35 | | // note: we don't check errno |
36 | 2 | int result = ::isatty(fd); |
37 | 1 | return result; |
38 | 2 | } |
39 | | |
40 | 0 | bool DoUnaryOp(Id_t op_id, BigStr* s) { |
41 | 0 | const char* zPath = s->data_; |
42 | |
|
43 | 0 | if (op_id == Id::BoolUnary_h || op_id == Id::BoolUnary_L) { |
44 | 0 | struct stat st; |
45 | 0 | if (lstat(zPath, &st) < 0) { |
46 | 0 | return false; |
47 | 0 | } |
48 | | |
49 | 0 | return S_ISLNK(st.st_mode); |
50 | 0 | } else { |
51 | 0 | struct stat st; |
52 | 0 | if (stat(zPath, &st) < 0) { |
53 | 0 | return false; |
54 | 0 | } |
55 | | |
56 | 0 | auto mode = st.st_mode; |
57 | |
|
58 | 0 | switch (op_id) { |
59 | 0 | case Id::BoolUnary_a: // synonyms for existence |
60 | 0 | case Id::BoolUnary_e: |
61 | 0 | return true; |
62 | | |
63 | 0 | case Id::BoolUnary_c: |
64 | 0 | return S_ISCHR(mode); |
65 | | |
66 | 0 | case Id::BoolUnary_d: |
67 | 0 | return S_ISDIR(mode); |
68 | | |
69 | 0 | case Id::BoolUnary_f: |
70 | 0 | return S_ISREG(mode); |
71 | | |
72 | 0 | case Id::BoolUnary_g: |
73 | 0 | return mode & S_ISGID; |
74 | | |
75 | | // NOTE(Jesse): This implementation MAY have a bug. On my system (Ubuntu |
76 | | // 20.04) it returns a correct result if the user is root (elevated with |
77 | | // sudo) and no execute bits are set for a file. |
78 | | // |
79 | | // A bug worked around in the python `posix` module here is that the above |
80 | | // (working) scenario is not always the case. |
81 | | // |
82 | | // https://github.com/python/cpython/blob/8d999cbf4adea053be6dbb612b9844635c4dfb8e/Modules/posixmodule.c#L2547 |
83 | | // |
84 | | // As well as the dash source code found here (relative to this repo |
85 | | // root): |
86 | | // |
87 | | // _cache/spec-bin/dash-0.5.10.2/src/bltin/test.c |
88 | | // See `test_file_access()` |
89 | | // |
90 | | // We could also use the `stat` struct to manually compute the |
91 | | // permissions, as shown in the above `test.c`, though the code is |
92 | | // somewhat obtuse. |
93 | | // |
94 | | // There is further discussion of this issue in: |
95 | | // https://github.com/oilshell/oil/pull/1168 |
96 | | // |
97 | | // And a bug filed for it at: |
98 | | // |
99 | | // https://github.com/oilshell/oil/issues/1170 |
100 | | |
101 | 0 | case Id::BoolUnary_k: |
102 | 0 | return (mode & S_ISVTX) != 0; |
103 | | |
104 | 0 | case Id::BoolUnary_p: |
105 | 0 | return S_ISFIFO(mode); |
106 | | |
107 | 0 | case Id::BoolUnary_r: |
108 | 0 | return faccessat(AT_FDCWD, zPath, R_OK, AT_EACCESS) == 0; |
109 | | |
110 | 0 | case Id::BoolUnary_s: |
111 | 0 | return st.st_size != 0; |
112 | | |
113 | 0 | case Id::BoolUnary_u: |
114 | 0 | return mode & S_ISUID; |
115 | | |
116 | 0 | case Id::BoolUnary_w: |
117 | 0 | return faccessat(AT_FDCWD, zPath, W_OK, AT_EACCESS) == 0; |
118 | | |
119 | 0 | case Id::BoolUnary_x: |
120 | 0 | return faccessat(AT_FDCWD, zPath, X_OK, AT_EACCESS) == 0; |
121 | | |
122 | 0 | case Id::BoolUnary_G: |
123 | 0 | return st.st_gid == getegid(); |
124 | | |
125 | 0 | case Id::BoolUnary_O: |
126 | 0 | return st.st_uid == geteuid(); |
127 | | |
128 | 0 | case Id::BoolUnary_S: |
129 | 0 | return S_ISSOCK(mode); |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | 0 | FAIL(kShouldNotGetHere); |
134 | 0 | } |
135 | | |
136 | 0 | bool DoBinaryOp(Id_t op_id, BigStr* s1, BigStr* s2) { |
137 | 0 | int m1 = 0; |
138 | 0 | struct stat st1; |
139 | 0 | if (stat(s1->data_, &st1) == 0) { |
140 | 0 | m1 = st1.st_mtime; |
141 | 0 | } |
142 | |
|
143 | 0 | int m2 = 0; |
144 | 0 | struct stat st2; |
145 | 0 | if (stat(s2->data_, &st2) == 0) { |
146 | 0 | m2 = st2.st_mtime; |
147 | 0 | } |
148 | |
|
149 | 0 | switch (op_id) { |
150 | 0 | case Id::BoolBinary_nt: |
151 | 0 | return m1 > m2; |
152 | 0 | case Id::BoolBinary_ot: |
153 | 0 | return m1 < m2; |
154 | 0 | case Id::BoolBinary_ef: |
155 | 0 | return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; |
156 | 0 | } |
157 | | |
158 | 0 | FAIL(kShouldNotGetHere); |
159 | 0 | } |
160 | | |
161 | | } // namespace bool_stat |