os.c
1 #include <assert.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <signal.h>
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #ifndef _WIN32
11 #include <sys/utsname.h>
12 #include <sys/ioctl.h>
13 #include <termios.h>
14 #else
15 #include <windows.h>
16 #endif
17 
18 #include <kuroko/vm.h>
19 #include <kuroko/value.h>
20 #include <kuroko/object.h>
21 #include <kuroko/util.h>
22 
23 /* Did you know this is actually specified to not exist in a header? */
24 extern char ** environ;
25 
26 #define DO_KEY(key) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)krk_copyString(buf. key, strlen(buf .key)))
27 #define S_KEY(key,val) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)val);
28 
29 #ifndef _WIN32
30 KRK_Function(uname) {
31  struct utsname buf;
32  if (uname(&buf) < 0) return NONE_VAL();
33 
34  KrkValue result = krk_dict_of(0, NULL, 0);
35  krk_push(result);
36 
37  DO_KEY(sysname);
38  DO_KEY(nodename);
39  DO_KEY(release);
40  DO_KEY(version);
41  DO_KEY(machine);
42 
43  return krk_pop();;
44 }
45 #else
46 KRK_Function(uname) {
47  KrkValue result = krk_dict_of(0, NULL, 0);
48  krk_push(result);
49 
50  TCHAR buffer[256] = TEXT("");
51  DWORD dwSize = sizeof(buffer);
52  GetComputerName(buffer, &dwSize);
53 
54  OSVERSIONINFOA versionInfo = {0};
55  versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
56  GetVersionExA(&versionInfo);
57 
58  if (versionInfo.dwMajorVersion == 10) {
59  S_KEY(release,S("10"));
60  } else if (versionInfo.dwMajorVersion == 6) {
61  if (versionInfo.dwMinorVersion == 3) {
62  S_KEY(release,S("8.1"));
63  } else if (versionInfo.dwMinorVersion == 2) {
64  S_KEY(release,S("8.0"));
65  } else if (versionInfo.dwMinorVersion == 1) {
66  S_KEY(release,S("7"));
67  } else if (versionInfo.dwMinorVersion == 0) {
68  S_KEY(release,S("Vista"));
69  }
70  } else {
71  S_KEY(release,S("XP or earlier"));
72  }
73 
74  char tmp[256];
75  size_t len = snprintf(tmp, 256, "%ld", versionInfo.dwBuildNumber);
76 
77  S_KEY(version, krk_copyString(tmp,len));
78  if (sizeof(void *) == 8) {
79  S_KEY(machine,S("x64"));
80  } else {
81  S_KEY(machine,S("x86"));
82  }
83 
84  S_KEY(sysname,S("Windows"));
85  S_KEY(nodename,krk_copyString(buffer,dwSize));
86 
87  return krk_pop();
88 }
89 #endif
90 
91 #define AS_Environ(o) (AS_INSTANCE(o))
92 #define IS_Environ(o) (krk_isInstanceOf(o,KRK_BASE_CLASS(Environ)))
93 #define CURRENT_CTYPE KrkInstance*
94 
95 static int _setVar(KrkString * key, KrkString * val) {
96 #ifndef _WIN32
97  return setenv(key->chars, val->chars, 1);
98 #else
99  size_t len = key->length + val->length + 3;
100  char * tmp = malloc(len);
101  snprintf(tmp, len, "%s=%s", key->chars, val->chars);
102  return putenv(tmp);
103 #endif
104 }
105 
106 KRK_Method(Environ,__setitem__) {
107  METHOD_TAKES_EXACTLY(2);
108  CHECK_ARG(1,str,KrkString*,key);
109  CHECK_ARG(2,str,KrkString*,val);
110  int r = _setVar(key,val);
111  if (r == 0) {
112  krk_push(argv[0]);
113  krk_push(argv[1]);
114  krk_push(argv[2]);
115  return krk_callDirect(vm.baseClasses->dictClass->_setter, 3);
116  }
117 
118  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
119 }
120 
121 static void _unsetVar(KrkString * str) {
122 #ifndef _WIN32
123  unsetenv(str->chars);
124 #else
125  size_t len = str->length + 2;
126  char * tmp = malloc(len);
127  snprintf(tmp, len, "%s", str->chars);
128  putenv(tmp);
129  free(tmp);
130 #endif
131 }
132 
133 KRK_Method(Environ,__delitem__) {
134  METHOD_TAKES_EXACTLY(1);
135  CHECK_ARG(1,str,KrkString*,key);
136  _unsetVar(key);
137  krk_push(argv[0]);
138  krk_push(argv[1]);
139  return krk_callDirect(vm.baseClasses->dictClass->_delitem, 2);
140 }
141 
142 static void _loadEnviron(KrkInstance * module) {
143  /* Create a new class to subclass `dict` */
144  KrkClass * Environ = krk_makeClass(module, &KRK_BASE_CLASS(Environ), "_Environ", vm.baseClasses->dictClass);
145  krk_attachNamedObject(&module->fields, "_Environ", (KrkObj*)Environ);
146 
147  /* Add our set method that should also call dict's set method */
148  BIND_METHOD(Environ,__setitem__);
149  BIND_METHOD(Environ,__delitem__);
150  krk_finalizeClass(Environ);
151 
152  /* Start with an empty dictionary */
153  KrkInstance * environObj = AS_INSTANCE(krk_dict_of(0,NULL,0));
154  krk_push(OBJECT_VAL(environObj));
155 
156  /* Transform it into an _Environ */
157  environObj->_class = Environ;
158 
159  /* And attach it to the module */
160  krk_attachNamedObject(&module->fields, "environ", (KrkObj*)environObj);
161  krk_pop();
162 
163  /* Now load the environment into it */
164  if (!environ) return; /* Empty environment */
165 
166  char ** env = environ;
167  for (; *env; env++) {
168  const char * equals = strchr(*env, '=');
169  if (!equals) continue;
170 
171  size_t len = strlen(*env);
172  size_t keyLen = equals - *env;
173  size_t valLen = len - keyLen - 1;
174 
175  KrkValue key = OBJECT_VAL(krk_copyString(*env, keyLen));
176  krk_push(key);
177  KrkValue val = OBJECT_VAL(krk_copyString(equals+1, valLen));
178  krk_push(val);
179 
180  krk_tableSet(AS_DICT(OBJECT_VAL(environObj)), key, val);
181  krk_pop(); /* val */
182  krk_pop(); /* key */
183  }
184 
185 }
186 
187 KRK_Function(system) {
188  const char * cmd;
189  if (!krk_parseArgs("s",(const char*[]){"command"},&cmd)) return NONE_VAL();
190  return INTEGER_VAL(system(cmd));
191 }
192 
193 KRK_Function(getcwd) {
194  FUNCTION_TAKES_NONE();
195  char buf[4096]; /* TODO PATH_MAX? */
196  if (!getcwd(buf, 4096)) return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
197  return OBJECT_VAL(krk_copyString(buf, strlen(buf)));
198 }
199 
200 KRK_Function(chdir) {
201  const char * path;
202  if (!krk_parseArgs("s",(const char*[]){"path"}, &path)) return NONE_VAL();
203  if (chdir(path)) return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
204  return NONE_VAL();
205 }
206 
207 KRK_Function(getpid) {
208  FUNCTION_TAKES_NONE();
209  return INTEGER_VAL(getpid());
210 }
211 
212 KRK_Function(strerror) {
213  int errnum;
214  if (!krk_parseArgs("i",(const char*[]){"errnum"},&errnum)) return NONE_VAL();
215  char *s = strerror(errnum);
216  if (!s) return NONE_VAL();
217  return OBJECT_VAL(krk_copyString(s,strlen(s)));
218 }
219 
220 KRK_Function(access) {
221  const char * path;
222  int mask;
223  if (!krk_parseArgs("si",(const char*[]){"pathname","mode"},&path,&mask)) return NONE_VAL();
224  if (access(path, mask) == 0) return BOOLEAN_VAL(1);
225  return BOOLEAN_VAL(0);
226 }
227 
228 KRK_Function(abort) {
229  abort();
230 }
231 
232 KRK_Function(exit) {
233  int status;
234  if (!krk_parseArgs("i",(const char*[]){"status"},&status)) return NONE_VAL();
235  exit(status);
236 }
237 
238 KRK_Function(remove) {
239  const char * path;
240  if (!krk_parseArgs("s",(const char*[]){"path"}, &path)) return NONE_VAL();
241  if (remove(path) != 0) {
242  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
243  }
244  return NONE_VAL();
245 }
246 
247 KRK_Function(truncate) {
248  const char * path;
249  size_t length;
250  if (!krk_parseArgs("sn",(const char*[]){"path","length"}, &path, &length)) return NONE_VAL();
251  if (truncate(path, length) != 0) {
252  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
253  }
254  return NONE_VAL();
255 }
256 
257 KRK_Function(dup) {
258  int fd;
259  if (!krk_parseArgs("i",(const char*[]){"fd"}, &fd)) return NONE_VAL();
260  int result = dup(fd);
261  if (result < 0) {
262  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
263  }
264  return INTEGER_VAL(result);
265 }
266 
267 KRK_Function(dup2) {
268  int fd, fd2;
269  if (!krk_parseArgs("ii",(const char*[]){"fd","fd2"}, &fd, &fd2)) return NONE_VAL();
270  int result = dup2(fd,fd2);
271  if (result < 0) {
272  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
273  }
274  return INTEGER_VAL(result);
275 }
276 
277 KRK_Function(isatty) {
278  int fd;
279  if (!krk_parseArgs("i",(const char*[]){"fd"}, &fd)) return NONE_VAL();
280  return BOOLEAN_VAL(isatty(fd));
281 }
282 
283 KRK_Function(lseek) {
284  int fd, how;
285  ssize_t offset;
286  if (!krk_parseArgs("ini",(const char*[]){"fd","offset","how"}, &fd, &offset, &how)) return NONE_VAL();
287  off_t result = lseek(fd,offset,how);
288  if (result == -1) {
289  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
290  }
291  return INTEGER_VAL(result);
292 }
293 
294 KRK_Function(open) {
295  const char * path;
296  int flags;
297  int mode = 0777;
298  if (!krk_parseArgs("si|i",(const char*[]){"path","flags","mode"}, &path, &flags, &mode)) return NONE_VAL();
299  int result = open(path, flags, mode);
300  if (result == -1) {
301  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
302  }
303  return INTEGER_VAL(result);
304 }
305 
306 KRK_Function(close) {
307  int fd;
308  if (!krk_parseArgs("i",(const char*[]){"fd"}, &fd)) return NONE_VAL();
309  if (close(fd) == -1) {
310  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
311  }
312  return NONE_VAL();
313 }
314 
315 #ifdef _WIN32
316 #define mkdir(p,m) mkdir(p); (void)m
317 #endif
318 KRK_Function(mkdir) {
319  const char * path;
320  int mode = 0777;
321  if (!krk_parseArgs("s|i",(const char*[]){"path","mode"}, &path, &mode)) return NONE_VAL();
322  int result = mkdir(path, mode);
323  if (result == -1) {
324  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
325  }
326  return NONE_VAL();
327 }
328 
329 KRK_Function(read) {
330  int fd;
331  ssize_t count;
332  if (!krk_parseArgs("in",(const char*[]){"fd","count"}, &fd, &count)) return NONE_VAL();
333 
334  uint8_t * tmp = malloc(count);
335  ssize_t result = read(fd,tmp,count);
336  if (result == -1) {
337  free(tmp);
338  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
339  } else {
340  krk_push(OBJECT_VAL(krk_newBytes(result,tmp)));
341  free(tmp);
342  return krk_pop();
343  }
344 }
345 
346 #ifndef IS_bytes
347 #define IS_bytes(o) IS_BYTES(o)
348 #define AS_bytes(o) AS_BYTES(o)
349 #endif
350 
351 KRK_Function(write) {
352  int fd;
353  KrkBytes * buf;
354  if (!krk_parseArgs("iO!",(const char*[]){"fd","buf"}, &fd, KRK_BASE_CLASS(bytes), &buf)) return NONE_VAL();
355 
356  ssize_t result = write(fd,buf->bytes,buf->length);
357  if (result == -1) {
358  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
359  }
360  return INTEGER_VAL(result);
361 }
362 
363 #ifndef _WIN32
364 KRK_Function(pipe) {
365  FUNCTION_TAKES_NONE();
366  int fds[2];
367  if (pipe(fds) == -1) {
368  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
369  }
370  krk_push(OBJECT_VAL(krk_newTuple(2)));
371  AS_TUPLE(krk_peek(0))->values.values[0] = INTEGER_VAL(fds[0]);
372  AS_TUPLE(krk_peek(0))->values.values[1] = INTEGER_VAL(fds[1]);
373  AS_TUPLE(krk_peek(0))->values.count = 2;
374  return krk_pop();
375 }
376 
377 KRK_Function(kill) {
378  FUNCTION_TAKES_EXACTLY(2);
379  ssize_t pid;
380  int sig;
381  if (!krk_parseArgs("ni",(const char*[]){"pid","sig"}, &pid, &sig)) return NONE_VAL();
382 
383  int result = kill(pid, sig);
384  if (result == -1) {
385  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
386  }
387  return INTEGER_VAL(result);
388 }
389 
390 KRK_Function(fork) {
391  FUNCTION_TAKES_NONE();
392  return INTEGER_VAL(fork());
393 }
394 
395 KRK_Function(symlink) {
396  const char * src;
397  const char * dst;
398  if (!krk_parseArgs("ss",(const char*[]){"target","linkpath"}, &src, &dst)) return NONE_VAL();
399 
400  if (symlink(src, dst) != 0) {
401  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
402  }
403  return NONE_VAL();
404 }
405 
406 KRK_Function(tcgetpgrp) {
407  int fd;
408  if (!krk_parseArgs("i",(const char*[]){"fd"}, &fd)) return NONE_VAL();
409  int result = tcgetpgrp(fd);
410  if (result == -1) {
411  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
412  }
413  return INTEGER_VAL(result);
414 }
415 
416 KRK_Function(tcsetpgrp) {
417  int fd;
418  ssize_t pgrp;
419  if (!krk_parseArgs("in",(const char*[]){"fd","pgrp"}, &fd, &pgrp)) return NONE_VAL();
420  int result = tcsetpgrp(fd,pgrp);
421  if (result == -1) {
422  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
423  }
424  return NONE_VAL();
425 }
426 
427 KRK_Function(ttyname) {
428  int fd;
429  if (!krk_parseArgs("i",(const char*[]){"fd"}, &fd)) return NONE_VAL();
430  char * result = ttyname(fd);
431  if (!result) {
432  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
433  }
434  return OBJECT_VAL(krk_copyString(result,strlen(result)));
435 }
436 
437 KRK_Function(get_terminal_size) {
438  int fd = 1;
439  if (!krk_parseArgs("|i",(const char*[]){"fd"}, &fd)) return NONE_VAL();
440 
441  struct winsize wsz;
442  int res = ioctl(fd, TIOCGWINSZ, &wsz);
443 
444  if (res < 0) {
445  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
446  }
447 
448  krk_push(OBJECT_VAL(krk_newTuple(2)));
449  AS_TUPLE(krk_peek(0))->values.values[0] = INTEGER_VAL(wsz.ws_col);
450  AS_TUPLE(krk_peek(0))->values.values[1] = INTEGER_VAL(wsz.ws_row);
451  AS_TUPLE(krk_peek(0))->values.count = 2;
452  return krk_pop();
453 }
454 #endif
455 
456 static int makeArgs(int count, const KrkValue * values, char *** argsOut, const char * _method_name) {
457  char ** out = malloc(sizeof(char*)*(count+1));
458  for (int i = 0; i < count; ++i) {
459  if (!IS_STRING(values[i])) {
460  free(out);
461  TYPE_ERROR(str,values[i]);
462  return 1;
463  }
464  out[i] = AS_CSTRING(values[i]);
465  }
466  out[count] = NULL;
467  *argsOut = out;
468  return 0;
469 }
470 
471 KRK_Function(execl) {
472  FUNCTION_TAKES_AT_LEAST(1);
473  CHECK_ARG(0,str,KrkString*,path);
474  char ** args;
475  if (makeArgs(argc-1,&argv[1],&args,_method_name)) return NONE_VAL();
476  if (execv(path->chars, args) == -1) {
477  free(args);
478  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
479  }
480  return krk_runtimeError(KRK_EXC(OSError), "Expected to not return from exec, but did.");
481 }
482 
483 KRK_Function(execlp) {
484  FUNCTION_TAKES_AT_LEAST(1);
485  CHECK_ARG(0,str,KrkString*,filename);
486  char ** args;
487  if (makeArgs(argc-1,&argv[1],&args,_method_name)) return NONE_VAL();
488  if (execvp(filename->chars, args) == -1) {
489  free(args);
490  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
491  }
492  return krk_runtimeError(KRK_EXC(OSError), "Expected to not return from exec, but did.");
493 }
494 
495 KRK_Function(execle) {
496  FUNCTION_TAKES_AT_LEAST(1);
497  CHECK_ARG(0,str,KrkString*,path);
498  CHECK_ARG((argc-1),list,KrkList*,envp);
499  char ** args;
500  char ** env;
501  if (makeArgs(argc-2,&argv[1],&args,_method_name)) return NONE_VAL();
502  if (makeArgs(envp->values.count, envp->values.values,&env,_method_name)) {
503  free(args);
504  return NONE_VAL();
505  }
506  if (execve(path->chars, args, env) == -1) {
507  free(args);
508  free(env);
509  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
510  }
511  return krk_runtimeError(KRK_EXC(OSError), "Expected to not return from exec, but did.");
512 }
513 
514 KRK_Function(execv) {
515  FUNCTION_TAKES_EXACTLY(2);
516  CHECK_ARG(0,str,KrkString*,filename);
517  CHECK_ARG(1,list,KrkList*,args);
518  char ** argp;
519  if (makeArgs(args->values.count, args->values.values, &argp,_method_name)) return NONE_VAL();
520  if (execv(filename->chars, argp) == -1) {
521  free(argp);
522  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
523  }
524  return krk_runtimeError(KRK_EXC(OSError), "Expected to not return from exec, but did.");
525 }
526 
527 KRK_Function(execvp) {
528  FUNCTION_TAKES_EXACTLY(2);
529  CHECK_ARG(0,str,KrkString*,path);
530  CHECK_ARG(1,list,KrkList*,args);
531  char ** argp;
532  if (makeArgs(args->values.count, args->values.values, &argp,_method_name)) return NONE_VAL();
533  if (execvp(path->chars, argp) == -1) {
534  free(argp);
535  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
536  }
537  return krk_runtimeError(KRK_EXC(OSError), "Expected to not return from exec, but did.");
538 }
539 
540 #define SET(thing) krk_attachNamedValue(&out->fields, #thing, INTEGER_VAL(buf. thing))
541 #ifdef _WIN32
542 #define STAT_STRUCT struct __stat64
543 #define stat _stat64
544 #else
545 #define STAT_STRUCT struct stat
546 #endif
547 KRK_Function(stat) {
548  const char * path;
549  if (!krk_parseArgs("s",(const char*[]){"path"}, &path)) return NONE_VAL();
550 
551  STAT_STRUCT buf;
552  int result = stat(path, &buf);
553  if (result == -1) {
554  return krk_runtimeError(KRK_EXC(OSError), "%s", strerror(errno));
555  }
556  KrkInstance * out = krk_newInstance(KRK_BASE_CLASS(stat_result));
557  krk_push(OBJECT_VAL(out));
558 
559  SET(st_dev);
560  SET(st_ino);
561  SET(st_mode);
562  SET(st_nlink);
563  SET(st_uid);
564  SET(st_gid);
565  SET(st_size);
566 
567  /* TODO times */
568  /* TODO block sizes */
569 
570  return krk_pop();
571 }
572 #undef SET
573 
574 #define IS_stat_result(o) (krk_isInstanceOf(o,KRK_BASE_CLASS(stat_result)))
575 #define AS_stat_result(o) AS_INSTANCE(o)
576 #define CURRENT_NAME self
577 
578 #define getProp(name) \
579  KrkValue name = NONE_VAL(); \
580  krk_tableGet(&self->fields, OBJECT_VAL(S(#name)), &name); \
581  if (!IS_INTEGER(name)) return krk_runtimeError(vm.exceptions->valueError, "stat_result is invalid")
582 
583 KRK_Method(stat_result,__repr__) {
584  METHOD_TAKES_NONE();
585  getProp(st_dev);
586  getProp(st_ino);
587  getProp(st_mode);
588  getProp(st_nlink);
589  getProp(st_uid);
590  getProp(st_gid);
591  getProp(st_size);
592 
593  char * buf = malloc(1024);
594  size_t len = snprintf(buf,1024,
595  "os.stat_result("
596  "st_dev=%d,"
597  "st_ino=%d,"
598  "st_mode=%d,"
599  "st_nlink=%d,"
600  "st_uid=%d,"
601  "st_gid=%d,"
602  "st_size=%d)",
603  (int)AS_INTEGER(st_dev),
604  (int)AS_INTEGER(st_ino),
605  (int)AS_INTEGER(st_mode),
606  (int)AS_INTEGER(st_nlink),
607  (int)AS_INTEGER(st_uid),
608  (int)AS_INTEGER(st_gid),
609  (int)AS_INTEGER(st_size));
610 
611  if (len > 1023) len = 1023;
612  krk_push(OBJECT_VAL(krk_copyString(buf,len)));
613  free(buf);
614  return krk_pop();
615 }
616 
617 KRK_Function(S_ISBLK) {
618  int mode;
619  if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
620  return INTEGER_VAL(S_ISBLK(mode));
621 }
622 KRK_Function(S_ISCHR) {
623  int mode;
624  if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
625  return INTEGER_VAL(S_ISCHR(mode));
626 }
627 KRK_Function(S_ISDIR) {
628  int mode;
629  if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
630  return INTEGER_VAL(S_ISDIR(mode));
631 }
632 KRK_Function(S_ISFIFO) {
633  int mode;
634  if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
635  return INTEGER_VAL(S_ISFIFO(mode));
636 }
637 KRK_Function(S_ISREG) {
638  int mode;
639  if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
640  return INTEGER_VAL(S_ISREG(mode));
641 }
642 #ifndef _WIN32
643 KRK_Function(S_ISLNK) {
644  int mode;
645  if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
646  return INTEGER_VAL(S_ISLNK(mode));
647 }
648 KRK_Function(S_ISSOCK) {
649  int mode;
650  if (!krk_parseArgs("i",(const char*[]){"mode"},&mode)) return NONE_VAL();
651  return INTEGER_VAL(S_ISSOCK(mode));
652 }
653 #endif
654 
655 void krk_module_init_os(void) {
656  KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
657  krk_attachNamedObject(&vm.modules, "os", (KrkObj*)module);
658  krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("os"));
659  krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
660  KRK_DOC(module, "@brief Provides access to low-level system operations.");
661 
662 #ifdef _WIN32
663  krk_attachNamedObject(&module->fields, "name", (KrkObj*)S("nt"));
664  krk_attachNamedObject(&module->fields, "sep", (KrkObj*)S("\\"));
665  krk_attachNamedObject(&module->fields, "altsep", (KrkObj*)S("/"));
666  krk_attachNamedObject(&module->fields, "pathsep", (KrkObj*)S(";"));
667  krk_attachNamedObject(&module->fields, "linesep", (KrkObj*)S("\r\n"));
668  krk_attachNamedObject(&module->fields, "devnull", (KrkObj*)S("nul"));
669 #else
670  krk_attachNamedObject(&module->fields, "name", (KrkObj*)S("posix"));
671  krk_attachNamedObject(&module->fields, "sep", (KrkObj*)S("/"));
672  krk_attachNamedValue(&module->fields, "altsep", NONE_VAL());
673  krk_attachNamedObject(&module->fields, "pathsep", (KrkObj*)S(":"));
674  krk_attachNamedObject(&module->fields, "linesep", (KrkObj*)S("\n"));
675  krk_attachNamedObject(&module->fields, "devnull", (KrkObj*)S("/dev/null"));
676 #endif
677 
678  krk_attachNamedObject(&module->fields, "curdir", (KrkObj*)S("."));
679  krk_attachNamedObject(&module->fields, "pardir", (KrkObj*)S(".."));
680  krk_attachNamedObject(&module->fields, "extsep", (KrkObj*)S("."));
681 
682 #define DO_INT(name) krk_attachNamedValue(&module->fields, #name, INTEGER_VAL(name))
683 
684  DO_INT(O_RDONLY);
685  DO_INT(O_WRONLY);
686  DO_INT(O_RDWR);
687  DO_INT(O_APPEND);
688  DO_INT(O_CREAT);
689  DO_INT(O_EXCL);
690  DO_INT(O_TRUNC);
691 
692 #ifdef O_CLOEXEC
693  DO_INT(O_CLOEXEC);
694 #endif
695 #ifdef O_DIRECTORY
696  DO_INT(O_DIRECTORY);
697 #endif
698 #ifdef O_PATH
699  DO_INT(O_PATH);
700 #endif
701 #ifdef O_NOFOLLOW
702  DO_INT(O_NOFOLLOW);
703 #endif
704 #ifdef O_NONBLOCK
705  DO_INT(O_NONBLOCK);
706 #endif
707 
708  DO_INT(SEEK_SET);
709  DO_INT(SEEK_CUR);
710  DO_INT(SEEK_END);
711 
712 #ifdef SEEK_HOLE
713  DO_INT(SEEK_HOLE);
714 #endif
715 #ifdef SEEK_DATA
716  DO_INT(SEEK_DATA);
717 #endif
718 
719  KRK_DOC(BIND_FUNC(module,uname),
720  "@brief Returns a @ref dict of attributes describing the current platform.\n\n"
721  "On POSIX platforms, the result should match the contents and layout of a standard @c uname() call. "
722  "On Windows, values are synthesized from available information.");
723  KRK_DOC(BIND_FUNC(module,system),
724  "@brief Call the system shell.\n"
725  "@arguments cmd\n\n"
726  "Runs @p cmd using the system shell and returns the platform-dependent return value.");
727  KRK_DOC(BIND_FUNC(module,getcwd),
728  "@brief Get the name of the current working directory.");
729  KRK_DOC(BIND_FUNC(module,chdir),
730  "@brief Change the current working directory.\n"
731  "@arguments newcwd\n\n"
732  "Attempts to change the working directory to @p newcwd. Raises @ref OSError on failure.");
733  KRK_DOC(BIND_FUNC(module,getpid),
734  "@brief Obtain the system process identifier.");
735  KRK_DOC(BIND_FUNC(module,strerror),
736  "@brief Convert an integer error code to a string.\n"
737  "@arguments errorno\n\n"
738  "Provides the string description for the error code specified by @p errorno.");
739  KRK_DOC(BIND_FUNC(module,abort),
740  "@brief Abort the current process.\n\n"
741  "@bsnote{This will exit the interpreter without calling cleanup routines.}");
742  KRK_DOC(BIND_FUNC(module,exit),
743  "@brief Exit the current process.\n\n"
744  "@bsnote{This will exit the interpreter without calling cleanup routines.}");
745  KRK_DOC(BIND_FUNC(module,remove),
746  "@brief Delete a file.\n"
747  "@arguments path\n\n"
748  "Attempts to delete the file at @p path.");
749  KRK_DOC(BIND_FUNC(module,truncate),
750  "@brief Resize a file.\n"
751  "@arguments path,length\n\n"
752  "Attempts to resize the file at @p path to @p length bytes.");
753  KRK_DOC(BIND_FUNC(module,dup),
754  "@brief Duplicate a file descriptor.\n"
755  "@arguments fd\n\n"
756  "Returns a new file descriptor pointing to the same file as @p fd.");
757  KRK_DOC(BIND_FUNC(module,dup2),
758  "@brief Duplicate a file descriptor.\n"
759  "@arguments oldfd,newfd\n\n"
760  "Like @ref dup but the new file descriptor is placed at @p newfd.\n");
761  KRK_DOC(BIND_FUNC(module,isatty),
762  "@brief Determine if a file descriptor is a terminal.\n"
763  "@arguments fd\n\n"
764  "Returns a @ref bool indicating whether the open file descriptor @p fd refers to a terminal.");
765  KRK_DOC(BIND_FUNC(module,lseek),
766  "@brief Seek an open file descriptor.\n"
767  "@arguments fd,pos,how\n\n"
768  "Seeks the open file descriptor @p fd by @p pos bytes as specified in @p how. "
769  "Use the values @c SEEK_SET, @c SEEK_CUR, and @c SEEK_END for @p how.");
770  KRK_DOC(BIND_FUNC(module,open),
771  "@brief Open a file.\n"
772  "@arguments path,flags,mode=0o777\n\n"
773  "Opens the file at @p path with the specified @p flags and @p mode. Returns a file descriptor.\n\n"
774  "@bsnote{Not to be confused with <a class=\"el\" href=\"mod_fileio.html#open\">fileio.open</a>}");
775  KRK_DOC(BIND_FUNC(module,close),
776  "@brief Close an open file descriptor.\n"
777  "@arguments fd");
778  KRK_DOC(BIND_FUNC(module,read),
779  "@brief Read from an open file descriptor.\n"
780  "@arguments fd,n\n\n"
781  "Reads at most @p n bytes from the open file descriptor @p fd.");
782  KRK_DOC(BIND_FUNC(module,write),
783  "@brief Write to an open file descriptor.\n"
784  "@arguments fd,data\n\n"
785  "Writes the @ref bytes object @p data to the open file descriptor @p fd.");
786  KRK_DOC(BIND_FUNC(module,mkdir),
787  "@brief Create a directory.\n"
788  "@arguments path,mode=0o777\n\n"
789  "Creates a directory at @p path.");
790 
791  KRK_DOC(BIND_FUNC(module,execl),
792  "@brief Replace the current process.\n"
793  "@arguments path,[args...]\n\n"
794  "The @c exec* family of functions replaces the calling process's image with a new one. "
795  "@c execl takes a @p path to a binary and an arbitrary number of @ref str arguments to "
796  "pass to the new executable.");
797  KRK_DOC(BIND_FUNC(module,execle),
798  "@brief Replace the current process.\n"
799  "@arguments path,[args...],env\n\n"
800  "The @c exec* family of functions replaces the calling process's image with a new one. "
801  "@c execle takes a @p path to a binary, an arbitrary number of @ref str arguments to "
802  "pass to the new executable, and @ref list of @c 'KEY=VALUE' pairs to set as the new "
803  "environment.");
804  KRK_DOC(BIND_FUNC(module,execlp),
805  "@brief Replace the current process.\n"
806  "@arguments filename,[args...]\n\n"
807  "The @c exec* family of functions replaces the calling process's image with a new one. "
808  "@c execlp takes a @p filename of a binary and an arbitrary number of @ref str arguments to "
809  "pass to the new executable. @p filename will be searched for in @c $PATH.");
810  KRK_DOC(BIND_FUNC(module,execv),
811  "@brief Replace the current process.\n"
812  "@arguments path,args\n\n"
813  "The @c exec* family of functions replaces the calling process's image with a new one. "
814  "@c execv takes a @p path to a binary and a @ref list @p args of @ref str arguments to "
815  "pass to the new executable.");
816  KRK_DOC(BIND_FUNC(module,execvp),
817  "@brief Replace the current process.\n"
818  "@arguments filename,args\n\n"
819  "The @c exec* family of functions replaces the calling process's image with a new one. "
820  "@c execvp takes a @p filename of a binary and a @ref list @p args of @ref str arguments to "
821  "pass to the new executable. @p filename will be searched for in @c $PATH.");
822 
823  DO_INT(F_OK);
824  DO_INT(R_OK);
825  DO_INT(W_OK);
826  DO_INT(X_OK);
827  KRK_DOC(BIND_FUNC(module,access),
828  "@brief Determine if a file can be accessed.\n"
829  "@arguments path,mask\n\n"
830  "Use the values @c F_OK, @c R_OK, @c W_OK, and @c X_OK to construct @p mask and check if the current "
831  "process has sufficient access rights to perform the requested operations on the file "
832  "at @p path.");
833 
834 #ifndef _WIN32
835  KRK_DOC(BIND_FUNC(module,pipe),
836  "@brief Create a pipe.\n\n"
837  "Creates a _pipe_, returning a two-tuple of file descriptors for the read and write ends respectively.");
838  KRK_DOC(BIND_FUNC(module,kill),
839  "@brief Send a signal to a process.\n"
840  "@arguments pid,signum\n\n"
841  "Send the signal @p signum to the process at @p pid.\n");
842  KRK_DOC(BIND_FUNC(module,fork),
843  "@brief Fork the current process.\n\n"
844  "Returns the PID of the new child process in the original process and @c 0 in the child.");
845  KRK_DOC(BIND_FUNC(module,symlink),
846  "@brief Create a symbolic link.\n"
847  "@arguments src,dst\n\n"
848  "Creates a symbolic link at @p src pointing to @p dst.");
849 
850  KRK_DOC(BIND_FUNC(module,tcgetpgrp),
851  "@brief Get the terminal foreground process group.\n"
852  "@arguments fd\n\n"
853  "Return the PID representing the foreground process group of the terminal specified by the file descriptor @p fd.");
854  KRK_DOC(BIND_FUNC(module,tcsetpgrp),
855  "@brief %Set the terminal foreground process group.\n"
856  "@arguments fd,pgrp\n\n"
857  "%Set the PID representing the foreground process group of the terminal specified by the file descriptor @p fd to @p pgrp.");
858  KRK_DOC(BIND_FUNC(module,ttyname),
859  "@brief Get the path to a terminal device.\n"
860  "@arguments fd\n\n"
861  "Returns a @ref str representing the path to the terminal device provided by the file descriptor @p fd.");
862 
863  KRK_DOC(BIND_FUNC(module,get_terminal_size),
864  "@brief Obtain the size of the terminal window.\n"
865  "@arguments fd=1\n"
866  "Obtain the size of the host terminal as a tuple of columns and lines.");
867 #endif
868 
869  _loadEnviron(module);
870 
871  /* Nothing special */
872  KrkClass * stat_result = krk_makeClass(module, &KRK_BASE_CLASS(stat_result), "stat_result", vm.baseClasses->objectClass);
873  BIND_METHOD(stat_result,__repr__);
874  krk_finalizeClass(stat_result);
875 
876  KRK_DOC(BIND_FUNC(module,stat),
877  "@brief Get the status of a file\n"
878  "@arguments path\n\n"
879  "Runs the @c stat system call on @p path. Returns a @ref stat_result.\n");
880 
881  module = krk_newInstance(vm.baseClasses->moduleClass);
882  krk_attachNamedObject(&vm.modules, "stat", (KrkObj*)module);
883  krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("stat"));
884  krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
885  KRK_DOC(module,
886  "@brief Functions to check results from @ref stat calls.");
887 
888  BIND_FUNC(module,S_ISBLK);
889  BIND_FUNC(module,S_ISCHR);
890  BIND_FUNC(module,S_ISDIR);
891  BIND_FUNC(module,S_ISFIFO);
892  BIND_FUNC(module,S_ISREG);
893 #ifndef _WIN32
894  BIND_FUNC(module,S_ISLNK);
895  BIND_FUNC(module,S_ISSOCK);
896 #endif
897 }
898 
899 
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:445
Struct definitions for core object types.
Immutable sequence of bytes.
Definition: object.h:105
size_t length
Length of data in bytes.
Definition: object.h:107
uint8_t * bytes
Pointer to separately-stored bytes data.
Definition: object.h:108
KrkBytes * krk_newBytes(size_t length, uint8_t *source)
Create a new byte array.
Definition: object.c:363
Type object.
Definition: object.h:189
KrkClass * krk_makeClass(KrkInstance *module, KrkClass **_class, const char *name, KrkClass *base)
Convenience function for creating new types.
Definition: vm.c:203
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
KrkValue krk_dict_of(int argc, const KrkValue argv[], int hasKw)
Create a dict object.
Definition: obj_dict.c:11
An object of a class.
Definition: object.h:255
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:339
KrkClass * _class
Type.
Definition: object.h:257
KrkTable fields
Attributes table.
Definition: object.h:258
Mutable array of values.
Definition: object.h:309
The most basic object type.
Definition: object.h:41
Immutable sequence of Unicode codepoints.
Definition: object.h:93
KrkString * krk_copyString(const char *chars, size_t length)
Obtain a string object representation of the given C string.
Definition: object.c:221
char * chars
UTF8 canonical data.
Definition: object.h:97
size_t length
String length in bytes.
Definition: object.h:95
int krk_tableSet(KrkTable *table, KrkValue key, KrkValue value)
Assign a value to a key in a table.
Definition: table.c:145
void krk_attachNamedObject(KrkTable *table, const char name[], KrkObj *obj)
Attach an object to an attribute table.
Definition: vm.c:839
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:825
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
Definition: object.c:353
Stack reference or primative value.
Utilities for creating native bindings.
#define krk_parseArgs(f, n,...)
Parse arguments to a function while accepting keyword arguments.
Definition: util.h:348
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:292
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:267
void krk_module_init_os(void)
Initialize the built-in 'os' module.
Definition: os.c:655
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:170
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:157
KrkValue krk_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
Definition: vm.c:771
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:178