cpp

Coverage Report

Created: 2025-05-19 06:06

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