/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 |