cpp

Coverage Report

Created: 2024-07-25 17:37

/home/uke/oil/cpp/stdlib.cc
Line
Count
Source (jump to first uncovered line)
1
// stdlib.cc: Replacement for standard library modules
2
// and native/posixmodule.c
3
4
#include "stdlib.h"
5
6
#include <dirent.h>  // closedir(), opendir(), readdir()
7
#include <errno.h>
8
#include <fcntl.h>      // open
9
#include <math.h>       // isinf, isnan
10
#include <signal.h>     // kill
11
#include <sys/stat.h>   // umask
12
#include <sys/types.h>  // umask
13
#include <sys/wait.h>   // WUNTRACED
14
#include <time.h>
15
#include <unistd.h>
16
17
#include "mycpp/runtime.h"
18
// To avoid circular dependency with e_die()
19
#include "prebuilt/core/error.mycpp.h"
20
21
using error::e_die;
22
23
namespace math {
24
25
0
bool isinf(double f) {
26
0
  return ::isinf(f);
27
0
}
28
29
0
bool isnan(double f) {
30
0
  return ::isnan(f);
31
0
}
32
33
}  // namespace math
34
35
namespace fcntl_ {
36
37
0
int fcntl(int fd, int cmd) {
38
0
  int result = ::fcntl(fd, cmd);
39
0
  if (result < 0) {
40
0
    throw Alloc<IOError>(errno);
41
0
  }
42
0
  return result;
43
0
}
44
45
0
int fcntl(int fd, int cmd, int arg) {
46
0
  int result = ::fcntl(fd, cmd, arg);
47
0
  if (result < 0) {
48
0
    throw Alloc<IOError>(errno);
49
0
  }
50
0
  return result;
51
0
}
52
53
}  // namespace fcntl_
54
55
namespace posix {
56
57
0
mode_t umask(mode_t mask) {
58
  // No error case: always succeeds
59
0
  return ::umask(mask);
60
0
}
61
62
2
int open(BigStr* path, int flags, int perms) {
63
2
  int result = ::open(path->data_, flags, perms);
64
2
  if (result < 0) {
65
1
    throw Alloc<OSError>(errno);
66
1
  }
67
1
  return result;
68
2
}
69
70
0
void dup2(int oldfd, int newfd) {
71
0
  if (::dup2(oldfd, newfd) < 0) {
72
0
    throw Alloc<OSError>(errno);
73
0
  }
74
0
}
75
1
void putenv(BigStr* name, BigStr* value) {
76
1
  int overwrite = 1;
77
1
  int ret = ::setenv(name->data_, value->data_, overwrite);
78
1
  if (ret < 0) {
79
0
    throw Alloc<IOError>(errno);
80
0
  }
81
1
}
82
83
0
mylib::File* fdopen(int fd, BigStr* c_mode) {
84
  // CPython checks if it's a directory first
85
0
  struct stat buf;
86
0
  if (fstat(fd, &buf) == 0 && S_ISDIR(buf.st_mode)) {
87
0
    throw Alloc<OSError>(EISDIR);
88
0
  }
89
90
  // CPython does some fcntl() stuff with mode == 'a', which we don't support
91
0
  DCHECK(c_mode->data_[0] != 'a');
92
93
0
  FILE* f = ::fdopen(fd, c_mode->data_);
94
0
  if (f == nullptr) {
95
0
    throw Alloc<OSError>(errno);
96
0
  }
97
98
0
  return Alloc<mylib::CFile>(f);
99
0
}
100
101
void execve(BigStr* argv0, List<BigStr*>* argv,
102
0
            Dict<BigStr*, BigStr*>* environ) {
103
0
  int n_args = len(argv);
104
0
  int n_env = len(environ);
105
0
  int combined_size = 0;
106
0
  for (DictIter<BigStr*, BigStr*> it(environ); !it.Done(); it.Next()) {
107
0
    BigStr* k = it.Key();
108
0
    BigStr* v = it.Value();
109
110
0
    int joined_len = len(k) + len(v) + 2;  // = and NUL terminator
111
0
    combined_size += joined_len;
112
0
  }
113
0
  const int argv_size = (n_args + 1) * sizeof(char*);
114
0
  const int env_size = (n_env + 1) * sizeof(char*);
115
0
  combined_size += argv_size;
116
0
  combined_size += env_size;
117
0
  char* combined_buf = static_cast<char*>(malloc(combined_size));
118
119
  // never deallocated
120
0
  char** _argv = reinterpret_cast<char**>(combined_buf);
121
0
  combined_buf += argv_size;
122
123
  // Annoying const_cast
124
  // https://stackoverflow.com/questions/190184/execv-and-const-ness
125
0
  for (int i = 0; i < n_args; ++i) {
126
0
    _argv[i] = const_cast<char*>(argv->at(i)->data_);
127
0
  }
128
0
  _argv[n_args] = nullptr;
129
130
  // Convert environ into an array of pointers to strings of the form: "k=v".
131
0
  char** envp = reinterpret_cast<char**>(combined_buf);
132
0
  combined_buf += env_size;
133
0
  int env_index = 0;
134
0
  for (DictIter<BigStr*, BigStr*> it(environ); !it.Done(); it.Next()) {
135
0
    BigStr* k = it.Key();
136
0
    BigStr* v = it.Value();
137
138
0
    char* buf = combined_buf;
139
0
    int joined_len = len(k) + len(v) + 1;
140
0
    combined_buf += joined_len + 1;
141
0
    memcpy(buf, k->data_, len(k));
142
0
    buf[len(k)] = '=';
143
0
    memcpy(buf + len(k) + 1, v->data_, len(v));
144
0
    buf[joined_len] = '\0';
145
146
0
    envp[env_index++] = buf;
147
0
  }
148
0
  envp[n_env] = nullptr;
149
150
0
  int ret = ::execve(argv0->data_, _argv, envp);
151
0
  if (ret == -1) {
152
0
    throw Alloc<OSError>(errno);
153
0
  }
154
155
  // ::execve() never returns on success
156
0
  FAIL(kShouldNotGetHere);
157
0
}
158
159
0
void kill(int pid, int sig) {
160
0
  if (::kill(pid, sig) != 0) {
161
0
    throw Alloc<OSError>(errno);
162
0
  }
163
0
}
164
165
0
void killpg(int pgid, int sig) {
166
0
  if (::killpg(pgid, sig) != 0) {
167
0
    throw Alloc<OSError>(errno);
168
0
  }
169
0
}
170
171
2
List<BigStr*>* listdir(BigStr* path) {
172
2
  DIR* dirp = opendir(path->data());
173
2
  if (dirp == NULL) {
174
1
    throw Alloc<OSError>(errno);
175
1
  }
176
177
1
  auto* ret = Alloc<List<BigStr*>>();
178
24
  while (true) {
179
24
    errno = 0;
180
24
    struct dirent* ep = readdir(dirp);
181
24
    if (ep == NULL) {
182
1
      if (errno != 0) {
183
0
        closedir(dirp);
184
0
        throw Alloc<OSError>(errno);
185
0
      }
186
1
      break;  // no more files
187
1
    }
188
    // Skip . and ..
189
23
    int name_len = strlen(ep->d_name);
190
23
    if (ep->d_name[0] == '.' &&
191
23
        (name_len == 1 || (ep->d_name[1] == '.' && name_len == 2))) {
192
2
      continue;
193
2
    }
194
21
    ret->append(StrFromC(ep->d_name, name_len));
195
21
  }
196
197
1
  closedir(dirp);
198
199
1
  return ret;
200
1
}
201
202
}  // namespace posix
203
204
namespace time_ {
205
206
1
void tzset() {
207
  // No error case: no return value
208
1
  ::tzset();
209
1
}
210
211
1
time_t time() {
212
1
  time_t result = ::time(nullptr);
213
1
  if (result < 0) {
214
0
    throw Alloc<IOError>(errno);
215
0
  }
216
1
  return result;
217
1
}
218
219
// NOTE(Jesse): time_t is specified to be an arithmetic type by C++. On most
220
// systems it's a 64-bit integer.  64 bits is used because 32 will overflow in
221
// 2038.  Someone on a committee somewhere thought of that when moving to
222
// 64-bit architectures to prevent breaking ABI again; on 32-bit systems it's
223
// usually 32 bits.  Point being, using anything but the time_t typedef here
224
// could (unlikely, but possible) produce weird behavior.
225
0
time_t localtime(time_t ts) {
226
  // localtime returns a pointer to a static buffer
227
0
  tm* loc_time = ::localtime(&ts);
228
229
0
  time_t result = mktime(loc_time);
230
0
  if (result < 0) {
231
0
    throw Alloc<IOError>(errno);
232
0
  }
233
0
  return result;
234
0
}
235
236
3
BigStr* strftime(BigStr* s, time_t ts) {
237
3
  tm* loc_time = ::localtime(&ts);
238
239
3
  const int max_len = 1024;
240
3
  BigStr* result = OverAllocatedStr(max_len);
241
3
  int n = strftime(result->data(), max_len, s->data_, loc_time);
242
3
  if (n == 0) {
243
    // bash silently truncates on large format string like
244
    //   printf '%(%Y)T'
245
    // Oil doesn't mask errors
246
    // Leaving out location info points to 'printf' builtin
247
248
0
    e_die(StrFromC("strftime() result exceeds 1024 bytes"));
249
0
  }
250
3
  result->MaybeShrink(n);
251
3
  return result;
252
3
}
253
254
1
void sleep(int seconds) {
255
1
  ::sleep(seconds);
256
1
}
257
258
}  // namespace time_