cpp

Coverage Report

Created: 2023-09-13 01:07

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