| 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
|