cpp

Coverage Report

Created: 2023-03-07 20:24

/home/andy/git/oilshell/oil/cpp/core.cc
Line
Count
Source (jump to first uncovered line)
1
// core.cc
2
3
#include "cpp/core.h"
4
5
#include <ctype.h>  // ispunct()
6
#include <errno.h>
7
#include <math.h>  // fmod()
8
#include <pwd.h>   // passwd
9
#include <signal.h>
10
#include <sys/resource.h>  // getrusage
11
#include <sys/select.h>    // select(), FD_ISSET, FD_SET, FD_ZERO
12
#include <sys/stat.h>      // stat
13
#include <sys/times.h>     // tms / times()
14
#include <sys/utsname.h>   // uname
15
#include <sys/wait.h>      // waitpid()
16
#include <time.h>          // time()
17
#include <unistd.h>        // getuid(), environ
18
19
#include "_gen/frontend/consts.h"  // gVersion
20
21
namespace pyos {
22
23
SignalSafe* gSignalSafe = nullptr;
24
25
1
Tuple2<int, int> WaitPid() {
26
1
  int status;
27
1
  int result = ::waitpid(-1, &status, WUNTRACED);
28
1
  if (result < 0) {
29
1
    if (errno == EINTR && gSignalSafe->PollSigInt()) {
30
0
      throw Alloc<KeyboardInterrupt>();
31
0
    }
32
1
    return Tuple2<int, int>(-1, errno);
33
1
  }
34
0
  return Tuple2<int, int>(result, status);
35
1
}
36
37
2
Tuple2<int, int> Read(int fd, int n, List<Str*>* chunks) {
38
2
  Str* s = OverAllocatedStr(n);  // Allocate enough for the result
39
40
2
  int length = ::read(fd, s->data(), n);
41
2
  if (length < 0) {
42
0
    if (errno == EINTR && gSignalSafe->PollSigInt()) {
43
0
      throw Alloc<KeyboardInterrupt>();
44
0
    }
45
0
    return Tuple2<int, int>(-1, errno);
46
0
  }
47
2
  if (length == 0) {
48
1
    return Tuple2<int, int>(length, 0);
49
1
  }
50
51
  // Now we know how much data we got back
52
1
  s->MaybeShrink(length);
53
1
  chunks->append(s);
54
55
1
  return Tuple2<int, int>(length, 0);
56
2
}
57
58
3
Tuple2<int, int> ReadByte(int fd) {
59
3
  unsigned char buf[1];
60
3
  ssize_t n = read(fd, &buf, 1);
61
3
  if (n < 0) {  // read error
62
0
    if (errno == EINTR && gSignalSafe->PollSigInt()) {
63
0
      throw Alloc<KeyboardInterrupt>();
64
0
    }
65
0
    return Tuple2<int, int>(-1, errno);
66
3
  } else if (n == 0) {  // EOF
67
1
    return Tuple2<int, int>(EOF_SENTINEL, 0);
68
2
  } else {  // return character
69
2
    return Tuple2<int, int>(buf[0], 0);
70
2
  }
71
3
}
72
73
// for read --line
74
0
Str* ReadLine() {
75
0
  assert(0);  // Does this get called?
76
0
}
77
78
1
Dict<Str*, Str*>* Environ() {
79
1
  auto d = NewDict<Str*, Str*>();
80
81
63
  for (char** env = environ; *env; ++env) {
82
62
    char* pair = *env;
83
84
62
    char* eq = strchr(pair, '=');
85
62
    assert(eq != nullptr);  // must look like KEY=value
86
87
0
    int len = strlen(pair);
88
89
62
    int key_len = eq - pair;
90
62
    Str* key = StrFromC(pair, key_len);
91
92
62
    int val_len = len - key_len - 1;
93
62
    Str* val = StrFromC(eq + 1, val_len);
94
95
62
    d->set(key, val);
96
62
  }
97
98
1
  return d;
99
1
}
100
101
3
int Chdir(Str* dest_dir) {
102
3
  if (chdir(dest_dir->data_) == 0) {
103
2
    return 0;  // success
104
2
  } else {
105
1
    return errno;
106
1
  }
107
3
}
108
109
1
Str* GetMyHomeDir() {
110
1
  uid_t uid = getuid();  // always succeeds
111
112
  // Don't free this.  (May return a pointer to a static area)
113
1
  struct passwd* entry = getpwuid(uid);
114
1
  if (entry == nullptr) {
115
0
    return nullptr;
116
0
  }
117
1
  Str* s = StrFromC(entry->pw_dir);
118
1
  return s;
119
1
}
120
121
1
Str* GetHomeDir(Str* user_name) {
122
  // Don't free this.  (May return a pointer to a static area)
123
1
  struct passwd* entry = getpwnam(user_name->data_);
124
1
  if (entry == nullptr) {
125
0
    return nullptr;
126
0
  }
127
1
  Str* s = StrFromC(entry->pw_dir);
128
1
  return s;
129
1
}
130
131
1
List<PasswdEntry*>* GetAllUsers() {
132
1
  auto* ret = NewList<PasswdEntry*>();
133
1
  struct passwd* entry = nullptr;
134
135
1
  setpwent();
136
76
  while (true) {
137
76
    errno = 0;
138
76
    entry = getpwent();
139
76
    if (entry == nullptr) {
140
1
      if (errno == EINTR) {
141
0
        continue;  // try again
142
1
      } else if (errno != 0) {
143
0
        throw Alloc<OSError>(errno);
144
0
      }
145
1
      break;
146
1
    }
147
75
    ret->append(Alloc<PasswdEntry>(entry));
148
75
  }
149
1
  endpwent();
150
151
1
  return ret;
152
1
}
153
154
2
Str* GetUserName(int uid) {
155
2
  Str* result = kEmptyString;
156
157
2
  if (passwd* pw = getpwuid(uid)) {
158
2
    result = StrFromC(pw->pw_name);
159
2
  } else {
160
0
    throw Alloc<IOError>(errno);
161
0
  }
162
163
2
  return result;
164
2
}
165
166
1
Str* OsType() {
167
1
  Str* result = kEmptyString;
168
169
1
  utsname un = {};
170
1
  if (::uname(&un) == 0) {
171
1
    result = StrFromC(un.sysname);
172
1
  } else {
173
0
    throw Alloc<IOError>(errno);
174
0
  }
175
176
1
  return result;
177
1
}
178
179
1
Tuple3<double, double, double> Time() {
180
1
  struct rusage ru;
181
1
  if (::getrusage(RUSAGE_SELF, &ru) == -1) {
182
0
    throw Alloc<IOError>(errno);
183
0
  }
184
185
1
  time_t t = ::time(nullptr);
186
1
  auto result = Tuple3<double, double, double>(
187
1
      static_cast<double>(t), static_cast<double>(ru.ru_utime.tv_sec),
188
1
      static_cast<double>(ru.ru_stime.tv_sec));
189
1
  return result;
190
1
}
191
192
4
static void PrintClock(clock_t ticks, long ticks_per_sec) {
193
4
  double seconds = static_cast<double>(ticks) / ticks_per_sec;
194
4
  printf("%ldm%.3fs", static_cast<long>(seconds) / 60, std::fmod(seconds, 60));
195
4
}
196
197
// bash source: builtins/times.def
198
1
void PrintTimes() {
199
1
  struct tms t;
200
1
  if (times(&t) == -1) {
201
0
    throw Alloc<IOError>(errno);
202
0
  }
203
1
  long ticks_per_sec = sysconf(_SC_CLK_TCK);
204
205
1
  PrintClock(t.tms_utime, ticks_per_sec);
206
1
  putc(' ', stdout);
207
1
  PrintClock(t.tms_stime, ticks_per_sec);
208
1
  putc('\n', stdout);
209
1
  PrintClock(t.tms_cutime, ticks_per_sec);
210
1
  putc(' ', stdout);
211
1
  PrintClock(t.tms_cstime, ticks_per_sec);
212
1
  putc('\n', stdout);
213
1
}
214
215
0
bool InputAvailable(int fd) {
216
0
  fd_set fds;
217
0
  FD_ZERO(&fds);
218
0
  struct timeval timeout = {0};  // return immediately
219
0
  FD_SET(fd, &fds);
220
0
  return select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0;
221
0
}
222
223
1
SignalSafe* InitSignalSafe() {
224
1
  gSignalSafe = Alloc<SignalSafe>();
225
1
  gHeap.RootGlobalVar(gSignalSafe);
226
227
1
  RegisterSignalInterest(SIGINT);  // for KeyboardInterrupt checks
228
229
1
  return gSignalSafe;
230
1
}
231
232
2
void Sigaction(int sig_num, sighandler_t handler) {
233
2
  struct sigaction act = {};
234
2
  act.sa_handler = handler;
235
2
  if (sigaction(sig_num, &act, nullptr) != 0) {
236
0
    throw Alloc<OSError>(errno);
237
0
  }
238
2
}
239
240
4
static void signal_handler(int sig_num) {
241
4
  assert(gSignalSafe != nullptr);
242
0
  gSignalSafe->UpdateFromSignalHandler(sig_num);
243
4
}
244
245
4
void RegisterSignalInterest(int sig_num) {
246
4
  struct sigaction act = {};
247
4
  act.sa_handler = signal_handler;
248
4
  assert(sigaction(sig_num, &act, nullptr) == 0);
249
4
}
250
251
2
Tuple2<Str*, int>* MakeDirCacheKey(Str* path) {
252
2
  struct stat st;
253
2
  if (::stat(path->data(), &st) == -1) {
254
1
    throw Alloc<OSError>(errno);
255
1
  }
256
257
1
  return Alloc<Tuple2<Str*, int>>(path, st.st_mtime);
258
2
}
259
260
}  // namespace pyos
261
262
namespace pyutil {
263
264
// TODO: SHARE with pyext
265
2
bool IsValidCharEscape(Str* c) {
266
2
  DCHECK(len(c) == 1);
267
268
0
  int ch = c->data_[0];
269
270
2
  if (ch == '/' || ch == '.' || ch == '-') {
271
0
    return false;
272
0
  }
273
2
  if (ch == ' ') {  // foo\ bar is idiomatic
274
0
    return true;
275
0
  }
276
2
  return ispunct(ch);
277
2
}
278
279
3
Str* ChArrayToString(List<int>* ch_array) {
280
3
  int n = len(ch_array);
281
3
  Str* result = NewStr(n);
282
11
  for (int i = 0; i < n; ++i) {
283
8
    result->data_[i] = ch_array->index_(i);
284
8
  }
285
3
  result->data_[n] = '\0';
286
3
  return result;
287
3
}
288
289
// TODO: Should eliminate _ResourceLoader
290
0
Str* _ResourceLoader::Get(Str* path) {
291
0
  return StrFromC("TODO");
292
0
}
293
294
1
_ResourceLoader* GetResourceLoader() {
295
1
  return Alloc<_ResourceLoader>();
296
1
}
297
298
1
Str* GetVersion(_ResourceLoader* loader) {
299
1
  return consts::gVersion;
300
1
}
301
302
1
void ShowAppVersion(_ResourceLoader* loader) {
303
  // Simple --version text.
304
  // osh --version is more elaborate, with compiler and so forth.
305
  // python -V is similarly simple.
306
307
1
  printf("Oils for Unix %s\n", consts::gVersion->data_);
308
1
  printf("\n");
309
1
  printf("    https://oils-for-unix.org/\n");
310
1
  printf("\n");
311
1
}
312
313
2
Str* BackslashEscape(Str* s, Str* meta_chars) {
314
2
  int upper_bound = len(s) * 2;
315
2
  Str* buf = OverAllocatedStr(upper_bound);
316
2
  char* p = buf->data_;
317
318
11
  for (int i = 0; i < len(s); ++i) {
319
9
    char c = s->data_[i];
320
9
    if (memchr(meta_chars->data_, c, len(meta_chars))) {
321
3
      *p++ = '\\';
322
3
    }
323
9
    *p++ = c;
324
9
  }
325
2
  buf->MaybeShrink(p - buf->data_);
326
2
  return buf;
327
2
}
328
329
1
Str* strerror(IOError_OSError* e) {
330
1
  Str* s = StrFromC(::strerror(e->errno_));
331
1
  return s;
332
1
}
333
334
0
grammar::Grammar* LoadOilGrammar(_ResourceLoader*) {
335
0
  return nullptr;
336
0
}
337
338
}  // namespace pyutil