OILS / cpp / osh.cc View on Github | oils.pub

164 lines, 87 significant
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
13using error::e_die;
14using id_kind_asdl::Id; // used below
15using syntax_asdl::loc;
16
17namespace arith_parse {
18
19GcGlobal<tdop::ParserSpec> kArithSpec_ = {
20 ObjHeader::Global(TypeTag::OtherClass)};
21tdop::ParserSpec* kArithSpec = &kArithSpec_.obj;
22
23} // namespace arith_parse
24
25namespace bool_stat {
26
27bool 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
40bool 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_b:
64 return S_ISBLK(mode);
65
66 case Id::BoolUnary_c:
67 return S_ISCHR(mode);
68
69 case Id::BoolUnary_d:
70 return S_ISDIR(mode);
71
72 case Id::BoolUnary_f:
73 return S_ISREG(mode);
74
75 case Id::BoolUnary_g:
76 return mode & S_ISGID;
77
78 // NOTE(Jesse): This implementation MAY have a bug. On my system (Ubuntu
79 // 20.04) it returns a correct result if the user is root (elevated with
80 // sudo) and no execute bits are set for a file.
81 //
82 // A bug worked around in the python `posix` module here is that the above
83 // (working) scenario is not always the case.
84 //
85 // https://github.com/python/cpython/blob/8d999cbf4adea053be6dbb612b9844635c4dfb8e/Modules/posixmodule.c#L2547
86 //
87 // As well as the dash source code found here (relative to this repo
88 // root):
89 //
90 // _cache/spec-bin/dash-0.5.10.2/src/bltin/test.c
91 // See `test_file_access()`
92 //
93 // We could also use the `stat` struct to manually compute the
94 // permissions, as shown in the above `test.c`, though the code is
95 // somewhat obtuse.
96 //
97 // There is further discussion of this issue in:
98 // https://github.com/oilshell/oil/pull/1168
99 //
100 // And a bug filed for it at:
101 //
102 // https://github.com/oilshell/oil/issues/1170
103
104 case Id::BoolUnary_k:
105 return (mode & S_ISVTX) != 0;
106
107 case Id::BoolUnary_p:
108 return S_ISFIFO(mode);
109
110 case Id::BoolUnary_r:
111 return faccessat(AT_FDCWD, zPath, R_OK, AT_EACCESS) == 0;
112
113 case Id::BoolUnary_s:
114 return st.st_size != 0;
115
116 case Id::BoolUnary_u:
117 return mode & S_ISUID;
118
119 case Id::BoolUnary_w:
120 return faccessat(AT_FDCWD, zPath, W_OK, AT_EACCESS) == 0;
121
122 case Id::BoolUnary_x:
123 return faccessat(AT_FDCWD, zPath, X_OK, AT_EACCESS) == 0;
124
125 case Id::BoolUnary_G:
126 return st.st_gid == getegid();
127
128 case Id::BoolUnary_O:
129 return st.st_uid == geteuid();
130
131 case Id::BoolUnary_S:
132 return S_ISSOCK(mode);
133 }
134 }
135
136 FAIL(kShouldNotGetHere);
137}
138
139bool DoBinaryOp(Id_t op_id, BigStr* s1, BigStr* s2) {
140 int m1 = 0;
141 struct stat st1;
142 if (stat(s1->data_, &st1) == 0) {
143 m1 = st1.st_mtime;
144 }
145
146 int m2 = 0;
147 struct stat st2;
148 if (stat(s2->data_, &st2) == 0) {
149 m2 = st2.st_mtime;
150 }
151
152 switch (op_id) {
153 case Id::BoolBinary_nt:
154 return m1 > m2;
155 case Id::BoolBinary_ot:
156 return m1 < m2;
157 case Id::BoolBinary_ef:
158 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
159 }
160
161 FAIL(kShouldNotGetHere);
162}
163
164} // namespace bool_stat