OILS / cpp / core.cc View on Github | oils.pub

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