cpp

Coverage Report

Created: 2024-03-13 14:13

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