cpp

Coverage Report

Created: 2024-08-25 11:48

/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 <float.h>
8
#include <math.h>  // fmod()
9
#include <pwd.h>   // passwd
10
#include <signal.h>
11
#include <sys/resource.h>  // getrusage
12
#include <sys/select.h>    // select(), FD_ISSET, FD_SET, FD_ZERO
13
#include <sys/stat.h>      // stat
14
#include <sys/time.h>      // gettimeofday
15
#include <sys/times.h>     // tms / times()
16
#include <sys/utsname.h>   // uname
17
#include <sys/wait.h>      // waitpid()
18
#include <termios.h>       // tcgetattr(), tcsetattr()
19
#include <time.h>          // time()
20
#include <unistd.h>        // getuid(), environ
21
22
#include "_build/detected-cpp-config.h"  // HAVE_PWENT
23
#include "_gen/cpp/build_stamp.h"        // gCommitHash
24
#include "_gen/frontend/consts.h"        // gVersion
25
#include "cpp/embedded_file.h"
26
27
extern char** environ;
28
29
namespace pyos {
30
31
SignalSafe* gSignalSafe = nullptr;
32
33
1
Tuple2<int, int> WaitPid(int waitpid_options) {
34
1
  int status;
35
1
  int result = ::waitpid(-1, &status, WUNTRACED | waitpid_options);
36
1
  if (result < 0) {
37
1
    if (errno == EINTR && gSignalSafe->PollUntrappedSigInt()) {
38
0
      throw Alloc<KeyboardInterrupt>();
39
0
    }
40
1
    return Tuple2<int, int>(-1, errno);
41
1
  }
42
0
  return Tuple2<int, int>(result, status);
43
1
}
44
45
2
Tuple2<int, int> Read(int fd, int n, List<BigStr*>* chunks) {
46
2
  BigStr* s = OverAllocatedStr(n);  // Allocate enough for the result
47
48
2
  int length = ::read(fd, s->data(), n);
49
2
  if (length < 0) {
50
0
    if (errno == EINTR && gSignalSafe->PollUntrappedSigInt()) {
51
0
      throw Alloc<KeyboardInterrupt>();
52
0
    }
53
0
    return Tuple2<int, int>(-1, errno);
54
0
  }
55
2
  if (length == 0) {
56
1
    return Tuple2<int, int>(length, 0);
57
1
  }
58
59
  // Now we know how much data we got back
60
1
  s->MaybeShrink(length);
61
1
  chunks->append(s);
62
63
1
  return Tuple2<int, int>(length, 0);
64
2
}
65
66
3
Tuple2<int, int> ReadByte(int fd) {
67
3
  unsigned char buf[1];
68
3
  ssize_t n = read(fd, &buf, 1);
69
3
  if (n < 0) {  // read error
70
0
    if (errno == EINTR && gSignalSafe->PollUntrappedSigInt()) {
71
0
      throw Alloc<KeyboardInterrupt>();
72
0
    }
73
0
    return Tuple2<int, int>(-1, errno);
74
3
  } else if (n == 0) {  // EOF
75
1
    return Tuple2<int, int>(EOF_SENTINEL, 0);
76
2
  } else {  // return character
77
2
    return Tuple2<int, int>(buf[0], 0);
78
2
  }
79
3
}
80
81
1
Dict<BigStr*, BigStr*>* Environ() {
82
1
  auto d = Alloc<Dict<BigStr*, BigStr*>>();
83
84
56
  for (char** env = environ; *env; ++env) {
85
55
    char* pair = *env;
86
87
55
    char* eq = strchr(pair, '=');
88
55
    assert(eq != nullptr);  // must look like KEY=value
89
90
0
    int len = strlen(pair);
91
92
55
    int key_len = eq - pair;
93
55
    BigStr* key = StrFromC(pair, key_len);
94
95
55
    int val_len = len - key_len - 1;
96
55
    BigStr* val = StrFromC(eq + 1, val_len);
97
98
55
    d->set(key, val);
99
55
  }
100
101
1
  return d;
102
1
}
103
104
3
int Chdir(BigStr* dest_dir) {
105
3
  if (chdir(dest_dir->data_) == 0) {
106
2
    return 0;  // success
107
2
  } else {
108
1
    return errno;
109
1
  }
110
3
}
111
112
1
BigStr* GetMyHomeDir() {
113
1
  uid_t uid = getuid();  // always succeeds
114
115
  // Don't free this.  (May return a pointer to a static area)
116
1
  struct passwd* entry = getpwuid(uid);
117
1
  if (entry == nullptr) {
118
0
    return nullptr;
119
0
  }
120
1
  BigStr* s = StrFromC(entry->pw_dir);
121
1
  return s;
122
1
}
123
124
1
BigStr* GetHomeDir(BigStr* user_name) {
125
  // Don't free this.  (May return a pointer to a static area)
126
1
  struct passwd* entry = getpwnam(user_name->data_);
127
1
  if (entry == nullptr) {
128
0
    return nullptr;
129
0
  }
130
1
  BigStr* s = StrFromC(entry->pw_dir);
131
1
  return s;
132
1
}
133
134
1
List<PasswdEntry*>* GetAllUsers() {
135
1
#ifdef HAVE_PWENT
136
1
  auto* ret = NewList<PasswdEntry*>();
137
1
  struct passwd* entry = nullptr;
138
139
1
  setpwent();
140
37
  while (true) {
141
37
    errno = 0;
142
37
    entry = getpwent();
143
37
    if (entry == nullptr) {
144
1
      if (errno == EINTR) {
145
0
        continue;  // try again
146
1
      } else if (errno != 0) {
147
0
        throw Alloc<OSError>(errno);
148
0
      }
149
1
      break;
150
1
    }
151
36
    ret->append(Alloc<PasswdEntry>(entry));
152
36
  }
153
1
  endpwent();
154
155
1
  return ret;
156
#else
157
  fprintf(
158
      stderr,
159
      "Oils compiled without libc *pwent() functions.  Can't list users.\n");
160
  return NewList<PasswdEntry*>();
161
#endif
162
1
}
163
164
2
BigStr* GetUserName(int uid) {
165
2
  BigStr* result = kEmptyString;
166
167
2
  if (passwd* pw = getpwuid(uid)) {
168
2
    result = StrFromC(pw->pw_name);
169
2
  } else {
170
0
    throw Alloc<IOError>(errno);
171
0
  }
172
173
2
  return result;
174
2
}
175
176
1
BigStr* OsType() {
177
1
  BigStr* result = kEmptyString;
178
179
1
  utsname un = {};
180
1
  if (::uname(&un) == 0) {
181
1
    result = StrFromC(un.sysname);
182
1
  } else {
183
0
    throw Alloc<IOError>(errno);
184
0
  }
185
186
1
  return result;
187
1
}
188
189
0
Tuple2<mops::BigInt, mops::BigInt> GetRLimit(int resource) {
190
0
  struct rlimit lim;
191
0
  if (::getrlimit(resource, &lim) < 0) {
192
0
    throw Alloc<IOError>(errno);
193
0
  }
194
0
  return Tuple2<mops::BigInt, mops::BigInt>(lim.rlim_cur, lim.rlim_max);
195
0
}
196
197
0
void SetRLimit(int resource, mops::BigInt soft, mops::BigInt hard) {
198
0
  struct rlimit lim;
199
0
  lim.rlim_cur = soft;
200
0
  lim.rlim_max = hard;
201
202
0
  if (::setrlimit(resource, &lim) < 0) {
203
0
    throw Alloc<IOError>(errno);
204
0
  }
205
0
}
206
207
1
Tuple3<double, double, double> Time() {
208
1
  struct timeval now;
209
1
  if (gettimeofday(&now, nullptr) < 0) {
210
0
    throw Alloc<IOError>(errno);  // could be a permission error
211
0
  }
212
1
  double real = now.tv_sec + static_cast<double>(now.tv_usec) / 1e6;
213
214
1
  struct rusage ru;
215
1
  if (::getrusage(RUSAGE_SELF, &ru) == -1) {
216
0
    throw Alloc<IOError>(errno);
217
0
  }
218
1
  struct timeval* u = &(ru.ru_utime);
219
1
  struct timeval* s = &(ru.ru_stime);
220
221
1
  double user = u->tv_sec + static_cast<double>(u->tv_usec) / 1e6;
222
1
  double sys = s->tv_sec + static_cast<double>(s->tv_usec) / 1e6;
223
224
1
  return Tuple3<double, double, double>(real, user, sys);
225
1
}
226
227
4
static void PrintClock(clock_t ticks, long ticks_per_sec) {
228
4
  double seconds = static_cast<double>(ticks) / ticks_per_sec;
229
4
  printf("%ldm%.3fs", static_cast<long>(seconds) / 60, fmod(seconds, 60));
230
4
}
231
232
// bash source: builtins/times.def
233
1
void PrintTimes() {
234
1
  struct tms t;
235
1
  if (times(&t) == -1) {
236
0
    throw Alloc<IOError>(errno);
237
0
  }
238
1
  long ticks_per_sec = sysconf(_SC_CLK_TCK);
239
240
1
  PrintClock(t.tms_utime, ticks_per_sec);
241
1
  putc(' ', stdout);
242
1
  PrintClock(t.tms_stime, ticks_per_sec);
243
1
  putc('\n', stdout);
244
1
  PrintClock(t.tms_cutime, ticks_per_sec);
245
1
  putc(' ', stdout);
246
1
  PrintClock(t.tms_cstime, ticks_per_sec);
247
1
  putc('\n', stdout);
248
1
}
249
250
0
bool InputAvailable(int fd) {
251
0
  fd_set fds;
252
0
  FD_ZERO(&fds);
253
0
  struct timeval timeout = {0};  // return immediately
254
0
  FD_SET(fd, &fds);
255
0
  return select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0;
256
0
}
257
258
1
IOError_OSError* FlushStdout() {
259
  // Flush libc buffers
260
1
  if (::fflush(stdout) != 0) {
261
0
    return Alloc<IOError>(errno);
262
0
  }
263
1
  return nullptr;
264
1
}
265
266
1
SignalSafe* InitSignalSafe() {
267
1
  gSignalSafe = Alloc<SignalSafe>();
268
1
  gHeap.RootGlobalVar(gSignalSafe);
269
270
1
  RegisterSignalInterest(SIGINT);  // for KeyboardInterrupt checks
271
272
1
  return gSignalSafe;
273
1
}
274
275
// Note that the Python implementation of pyos.sigaction() calls
276
// signal.signal(), which calls PyOS_setsig(), which calls sigaction() #ifdef
277
// HAVE_SIGACTION.
278
2
void sigaction(int sig_num, void (*handler)(int)) {
279
  // SIGINT and SIGWINCH must be registered through SignalSafe
280
2
  DCHECK(sig_num != SIGINT);
281
2
  DCHECK(sig_num != SIGWINCH);
282
283
0
  struct sigaction act = {};
284
2
  act.sa_handler = handler;
285
2
  if (sigaction(sig_num, &act, nullptr) != 0) {
286
0
    throw Alloc<OSError>(errno);
287
0
  }
288
2
}
289
290
4
static void OurSignalHandler(int sig_num) {
291
4
  assert(gSignalSafe != nullptr);
292
0
  gSignalSafe->UpdateFromSignalHandler(sig_num);
293
4
}
294
295
4
void RegisterSignalInterest(int sig_num) {
296
4
  struct sigaction act = {};
297
4
  act.sa_handler = OurSignalHandler;
298
4
  if (sigaction(sig_num, &act, nullptr) != 0) {
299
0
    throw Alloc<OSError>(errno);
300
0
  }
301
4
}
302
303
2
Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path) {
304
2
  struct stat st;
305
2
  if (::stat(path->data(), &st) == -1) {
306
1
    throw Alloc<OSError>(errno);
307
1
  }
308
309
1
  return Alloc<Tuple2<BigStr*, int>>(path, st.st_mtime);
310
2
}
311
312
0
Tuple2<int, void*> PushTermAttrs(int fd, int mask) {
313
0
  struct termios* term_attrs =
314
0
      static_cast<struct termios*>(malloc(sizeof(struct termios)));
315
316
0
  if (tcgetattr(fd, term_attrs) < 0) {
317
0
    throw Alloc<OSError>(errno);
318
0
  }
319
  // Flip the bits in one field
320
0
  int orig_local_modes = term_attrs->c_lflag;
321
0
  term_attrs->c_lflag = orig_local_modes & mask;
322
323
0
  if (tcsetattr(fd, TCSANOW, term_attrs) < 0) {
324
0
    throw Alloc<OSError>(errno);
325
0
  }
326
327
0
  return Tuple2<int, void*>(orig_local_modes, term_attrs);
328
0
}
329
330
0
void PopTermAttrs(int fd, int orig_local_modes, void* term_attrs) {
331
0
  struct termios* t = static_cast<struct termios*>(term_attrs);
332
0
  t->c_lflag = orig_local_modes;
333
0
  if (tcsetattr(fd, TCSANOW, t) < 0) {
334
0
    ;  // Like Python, ignore error because of issue #1001
335
0
  }
336
0
}
337
338
}  // namespace pyos
339
340
namespace pyutil {
341
342
0
double infinity() {
343
0
  return INFINITY;  // float.h
344
0
}
345
346
0
double nan() {
347
0
  return NAN;  // float.h
348
0
}
349
350
// TODO: SHARE with pyext
351
2
bool IsValidCharEscape(BigStr* c) {
352
2
  DCHECK(len(c) == 1);
353
354
0
  int ch = c->data_[0];
355
356
2
  if (ch == '/' || ch == '.' || ch == '-') {
357
0
    return false;
358
0
  }
359
2
  if (ch == ' ') {  // foo\ bar is idiomatic
360
0
    return true;
361
0
  }
362
2
  return ispunct(ch);
363
2
}
364
365
3
BigStr* ChArrayToString(List<int>* ch_array) {
366
3
  int n = len(ch_array);
367
3
  BigStr* result = NewStr(n);
368
11
  for (int i = 0; i < n; ++i) {
369
8
    result->data_[i] = ch_array->at(i);
370
8
  }
371
3
  result->data_[n] = '\0';
372
3
  return result;
373
3
}
374
375
3
BigStr* _ResourceLoader::Get(BigStr* path) {
376
3
  TextFile* t = gEmbeddedFiles;  // start of generated data
377
6
  while (t->rel_path != nullptr) {
378
5
    if (strcmp(t->rel_path, path->data_) == 0) {
379
2
      return t->contents;
380
2
    }
381
3
    t++;
382
3
  }
383
  // Emulate Python
384
1
  throw Alloc<IOError>(ENOENT);
385
3
}
386
387
1
_ResourceLoader* GetResourceLoader() {
388
1
  return Alloc<_ResourceLoader>();
389
1
}
390
391
1
BigStr* GetVersion(_ResourceLoader* loader) {
392
1
  return consts::gVersion;
393
1
}
394
395
1
void PrintVersionDetails(_ResourceLoader* loader) {
396
  // Invoked by core/util.py VersionFlag()
397
1
  printf("git commit = %s\n", gCommitHash);
398
399
  // TODO: I would like the CPU, OS, compiler
400
  // How do we get those?  Look at CPython
401
1
}
402
403
2
BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars) {
404
2
  int upper_bound = len(s) * 2;
405
2
  BigStr* buf = OverAllocatedStr(upper_bound);
406
2
  char* p = buf->data_;
407
408
11
  for (int i = 0; i < len(s); ++i) {
409
9
    char c = s->data_[i];
410
9
    if (memchr(meta_chars->data_, c, len(meta_chars))) {
411
3
      *p++ = '\\';
412
3
    }
413
9
    *p++ = c;
414
9
  }
415
2
  buf->MaybeShrink(p - buf->data_);
416
2
  return buf;
417
2
}
418
419
1
BigStr* strerror(IOError_OSError* e) {
420
1
  BigStr* s = StrFromC(::strerror(e->errno_));
421
1
  return s;
422
1
}
423
424
static grammar::Grammar* gOilGrammar = nullptr;
425
426
0
grammar::Grammar* LoadYshGrammar(_ResourceLoader*) {
427
0
  if (gOilGrammar != nullptr) {
428
0
    return gOilGrammar;
429
0
  }
430
431
0
  gOilGrammar = Alloc<grammar::Grammar>();
432
0
  gHeap.RootGlobalVar(gOilGrammar);
433
0
  return gOilGrammar;
434
0
}
435
436
}  // namespace pyutil