cpp

Coverage Report

Created: 2023-09-13 01:07

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