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