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 | bool isatty(BigStr* fd_str, word_t* blame_word) {
|
28 | int fd;
|
29 | try {
|
30 | fd = to_int(fd_str);
|
31 | } catch (ValueError* e) {
|
32 | e_die(StrFormat("Invalid file descriptor %r", fd_str),
|
33 | Alloc<loc::Word>(blame_word));
|
34 | }
|
35 | // note: we don't check errno
|
36 | int result = ::isatty(fd);
|
37 | return result;
|
38 | }
|
39 |
|
40 | bool DoUnaryOp(Id_t op_id, BigStr* s) {
|
41 | const char* zPath = s->data_;
|
42 |
|
43 | if (op_id == Id::BoolUnary_h || op_id == Id::BoolUnary_L) {
|
44 | struct stat st;
|
45 | if (lstat(zPath, &st) < 0) {
|
46 | return false;
|
47 | }
|
48 |
|
49 | return S_ISLNK(st.st_mode);
|
50 | } else {
|
51 | struct stat st;
|
52 | if (stat(zPath, &st) < 0) {
|
53 | return false;
|
54 | }
|
55 |
|
56 | auto mode = st.st_mode;
|
57 |
|
58 | switch (op_id) {
|
59 | case Id::BoolUnary_a: // synonyms for existence
|
60 | case Id::BoolUnary_e:
|
61 | return true;
|
62 |
|
63 | case Id::BoolUnary_c:
|
64 | return S_ISCHR(mode);
|
65 |
|
66 | case Id::BoolUnary_d:
|
67 | return S_ISDIR(mode);
|
68 |
|
69 | case Id::BoolUnary_f:
|
70 | return S_ISREG(mode);
|
71 |
|
72 | case Id::BoolUnary_g:
|
73 | 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 | case Id::BoolUnary_k:
|
102 | return (mode & S_ISVTX) != 0;
|
103 |
|
104 | case Id::BoolUnary_p:
|
105 | return S_ISFIFO(mode);
|
106 |
|
107 | case Id::BoolUnary_r:
|
108 | return faccessat(AT_FDCWD, zPath, R_OK, AT_EACCESS) == 0;
|
109 |
|
110 | case Id::BoolUnary_s:
|
111 | return st.st_size != 0;
|
112 |
|
113 | case Id::BoolUnary_u:
|
114 | return mode & S_ISUID;
|
115 |
|
116 | case Id::BoolUnary_w:
|
117 | return faccessat(AT_FDCWD, zPath, W_OK, AT_EACCESS) == 0;
|
118 |
|
119 | case Id::BoolUnary_x:
|
120 | return faccessat(AT_FDCWD, zPath, X_OK, AT_EACCESS) == 0;
|
121 |
|
122 | case Id::BoolUnary_G:
|
123 | return st.st_gid == getegid();
|
124 |
|
125 | case Id::BoolUnary_O:
|
126 | return st.st_uid == geteuid();
|
127 |
|
128 | case Id::BoolUnary_S:
|
129 | return S_ISSOCK(mode);
|
130 | }
|
131 | }
|
132 |
|
133 | FAIL(kShouldNotGetHere);
|
134 | }
|
135 |
|
136 | bool DoBinaryOp(Id_t op_id, BigStr* s1, BigStr* s2) {
|
137 | int m1 = 0;
|
138 | struct stat st1;
|
139 | if (stat(s1->data_, &st1) == 0) {
|
140 | m1 = st1.st_mtime;
|
141 | }
|
142 |
|
143 | int m2 = 0;
|
144 | struct stat st2;
|
145 | if (stat(s2->data_, &st2) == 0) {
|
146 | m2 = st2.st_mtime;
|
147 | }
|
148 |
|
149 | switch (op_id) {
|
150 | case Id::BoolBinary_nt:
|
151 | return m1 > m2;
|
152 | case Id::BoolBinary_ot:
|
153 | return m1 < m2;
|
154 | case Id::BoolBinary_ef:
|
155 | return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
|
156 | }
|
157 |
|
158 | FAIL(kShouldNotGetHere);
|
159 | }
|
160 |
|
161 | } // namespace bool_stat
|