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