cpp

Coverage Report

Created: 2022-07-20 01:16

/home/uke/oil/cpp/leaky_stdlib.cc
Line
Count
Source (jump to first uncovered line)
1
// leaky_stdlib.cc: Replacement for standard library modules
2
// and native/posixmodule.c
3
4
// clang-format off
5
#include "mycpp/myerror.h"  // for OSError; must come first
6
// clang-format on
7
8
#include "leaky_stdlib.h"
9
10
#include <errno.h>
11
#include <fcntl.h>      // open
12
#include <sys/stat.h>   // umask
13
#include <sys/types.h>  // umask
14
#include <sys/wait.h>   // WUNTRACED
15
#include <time.h>
16
#include <unistd.h>
17
18
#include "cpp/leaky_core_error.h"
19
#include "cpp/leaky_core_pyerror.h"
20
#include "mycpp/mylib_leaky.h"
21
using mylib::CopyStr;
22
using mylib::OverAllocatedStr;
23
24
namespace fcntl_ {
25
26
0
int fcntl(int fd, int cmd) {
27
0
  int result = ::fcntl(fd, cmd);
28
0
  if (result < 0) {
29
0
    throw new IOError(errno);
30
0
  }
31
0
  return result;
32
0
}
33
34
0
int fcntl(int fd, int cmd, int arg) {
35
0
  int result = ::fcntl(fd, cmd, arg);
36
0
  if (result < 0) {
37
0
    throw new IOError(errno);
38
0
  }
39
0
  return result;
40
0
}
41
42
}  // namespace fcntl_
43
44
namespace posix {
45
46
0
int umask(int mask) {
47
  // note: assuming mode_t fits in an int
48
0
  return ::umask(mask);
49
0
}
50
51
0
int open(Str* path, int flags, int perms) {
52
0
  mylib::Str0 path0(path);
53
0
  return ::open(path0.Get(), flags, perms);
54
0
}
55
56
0
void dup2(int oldfd, int newfd) {
57
0
  if (::dup2(oldfd, newfd) < 0) {
58
0
    throw new OSError(errno);
59
0
  }
60
0
}
61
1
void putenv(Str* name, Str* value) {
62
1
  assert(name->IsNulTerminated());
63
0
  assert(value->IsNulTerminated());
64
0
  int overwrite = 1;
65
1
  int ret = ::setenv(name->data_, value->data_, overwrite);
66
1
  if (ret < 0) {
67
0
    throw new IOError(errno);
68
0
  }
69
1
}
70
71
0
mylib::LineReader* fdopen(int fd, Str* c_mode) {
72
0
  mylib::Str0 c_mode0(c_mode);
73
0
  FILE* f = ::fdopen(fd, c_mode0.Get());
74
75
  // TODO: raise exception
76
0
  assert(f);
77
78
0
  return new mylib::CFileLineReader(f);
79
0
}
80
81
0
void execve(Str* argv0, List<Str*>* argv, Dict<Str*, Str*>* environ) {
82
0
  mylib::Str0 _argv0(argv0);
83
84
0
  int n_args = len(argv);
85
  // never deallocated
86
0
  char** _argv = static_cast<char**>(malloc((n_args + 1) * sizeof(char*)));
87
88
  // Annoying const_cast
89
  // https://stackoverflow.com/questions/190184/execv-and-const-ness
90
0
  for (int i = 0; i < n_args; ++i) {
91
0
    _argv[i] = const_cast<char*>(argv->index_(i)->data_);
92
0
  }
93
0
  _argv[n_args] = nullptr;
94
95
  // Convert environ into an array of pointers to strings of the form: "k=v".
96
0
  int n_env = len(environ);
97
0
  char** envp = static_cast<char**>(malloc((n_env + 1) * sizeof(char*)));
98
0
  int i = 0;
99
0
  for (const auto& kv : environ->items_) {
100
0
    Str* k = kv.first;
101
0
    Str* v = kv.second;
102
0
    int joined_len = k->len_ + v->len_ + 1;
103
0
    char* buf = static_cast<char*>(malloc(joined_len + 1));
104
0
    memcpy(buf, k->data_, k->len_);
105
0
    buf[k->len_] = '=';
106
0
    memcpy(buf + k->len_ + 1, v->data_, v->len_);
107
0
    buf[joined_len] = '\0';
108
0
    envp[i++] = buf;
109
0
  }
110
0
  envp[n_env] = nullptr;
111
112
0
  int ret = ::execve(_argv0.Get(), _argv, envp);
113
0
  if (ret == -1) {
114
0
    throw new OSError(errno);
115
0
  }
116
117
  // NOTE(Jesse): ::execve() is specified to never return on success.  If we
118
  // hit this assertion, it returned successfully (or at least something other
119
  // than -1) but should have overwritten our address space with the invoked
120
  // process'
121
0
  InvalidCodePath();
122
0
}
123
124
}  // namespace posix
125
126
namespace time_ {
127
128
0
void tzset() {
129
0
  ::tzset();
130
0
}
131
132
1
time_t time() {
133
1
  return ::time(nullptr);
134
1
}
135
136
// NOTE(Jesse): time_t is specified to be an arithmetic type by C++. On most
137
// systems it's a 64-bit integer.  64 bits is used because 32 will overflow in
138
// 2038.  Someone on a comittee somewhere thought of that when moving to 64-bit
139
// architectures to prevent breaking ABI again; on 32-bit systems it's usually
140
// 32 bits.  Point being, using anything but the time_t typedef here could
141
// (unlikely, but possible) produce weird behavior.
142
0
time_t localtime(time_t ts) {
143
0
  tm* loc_time = ::localtime(&ts);
144
0
  time_t result = mktime(loc_time);
145
0
  return result;
146
0
}
147
148
0
Str* strftime(Str* s, time_t ts) {
149
  // TODO: may not work with mylib_leaky.h
150
  // https://github.com/oilshell/oil/issues/1221
151
0
  assert(s->IsNulTerminated());
152
153
0
  tm* loc_time = ::localtime(&ts);
154
155
0
  const int max_len = 1024;
156
0
  Str* result = OverAllocatedStr(max_len);
157
0
  int n = strftime(result->data(), max_len, s->data_, loc_time);
158
0
  if (n == 0) {
159
    // bash silently truncates on large format string like
160
    //   printf '%(%Y)T'
161
    // Oil doesn't mask errors
162
    // No error location info, but leaving it out points reliably to 'printf'
163
0
    e_die(CopyStr("strftime() result exceeds 1024 bytes"));
164
0
  }
165
0
  result->SetObjLenFromStrLen(n);
166
0
  return result;
167
0
}
168
169
}  // namespace time_