11 #include <sys/utsname.h>
12 #include <sys/ioctl.h>
24 extern char ** environ;
29 #define DO_KEY(key) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)krk_copyString(buf. key, strlen(buf .key)))
30 #define S_KEY(key,val) krk_attachNamedObject(AS_DICT(result), #key, (KrkObj*)val);
35 if (uname(&buf) < 0)
return NONE_VAL();
53 TCHAR buffer[256] = TEXT(
"");
54 DWORD dwSize =
sizeof(buffer);
55 GetComputerName(buffer, &dwSize);
57 OSVERSIONINFOA versionInfo = {0};
58 versionInfo.dwOSVersionInfoSize =
sizeof(OSVERSIONINFO);
59 GetVersionExA(&versionInfo);
61 if (versionInfo.dwMajorVersion == 10) {
62 S_KEY(release,S(
"10"));
63 }
else if (versionInfo.dwMajorVersion == 6) {
64 if (versionInfo.dwMinorVersion == 3) {
65 S_KEY(release,S(
"8.1"));
66 }
else if (versionInfo.dwMinorVersion == 2) {
67 S_KEY(release,S(
"8.0"));
68 }
else if (versionInfo.dwMinorVersion == 1) {
69 S_KEY(release,S(
"7"));
70 }
else if (versionInfo.dwMinorVersion == 0) {
71 S_KEY(release,S(
"Vista"));
74 S_KEY(release,S(
"XP or earlier"));
78 size_t len = snprintf(tmp, 256,
"%ld", versionInfo.dwBuildNumber);
81 if (
sizeof(
void *) == 8) {
82 S_KEY(machine,S(
"x64"));
84 S_KEY(machine,S(
"x86"));
87 S_KEY(sysname,S(
"Windows"));
94 #define AS_Environ(o) (AS_INSTANCE(o))
95 #define IS_Environ(o) (krk_isInstanceOf(o,os_Environ))
96 #define CURRENT_CTYPE KrkInstance*
103 char * tmp = malloc(len);
104 snprintf(tmp, len,
"%s=%s", key->
chars, val->
chars);
109 KRK_Method(Environ,__setitem__) {
110 METHOD_TAKES_EXACTLY(2);
113 int r = _setVar(key,val);
126 unsetenv(str->
chars);
128 size_t len = str->
length + 2;
129 char * tmp = malloc(len);
130 snprintf(tmp, len,
"%s", str->
chars);
136 KRK_Method(Environ,__delitem__) {
137 METHOD_TAKES_EXACTLY(1);
151 BIND_METHOD(Environ,__setitem__);
152 BIND_METHOD(Environ,__delitem__);
160 environObj->
_class = Environ;
167 if (!environ)
return;
169 char ** env = environ;
170 for (; *env; env++) {
171 const char * equals = strchr(*env,
'=');
172 if (!equals)
continue;
174 size_t len = strlen(*env);
175 size_t keyLen = equals - *env;
176 size_t valLen = len - keyLen - 1;
183 krk_tableSet(AS_DICT(OBJECT_VAL(environObj)), key, val);
190 KRK_Function(system) {
192 if (!
krk_parseArgs(
"s",(
const char*[]){
"command"},&cmd))
return NONE_VAL();
193 return INTEGER_VAL(system(cmd));
196 KRK_Function(getcwd) {
197 FUNCTION_TAKES_NONE();
199 if (!getcwd(buf, 4096))
return krk_runtimeError(KRK_EXC(OSError),
"%s", strerror(errno));
203 KRK_Function(chdir) {
205 if (!
krk_parseArgs(
"s",(
const char*[]){
"path"}, &path))
return NONE_VAL();
206 if (chdir(path))
return krk_runtimeError(KRK_EXC(OSError),
"%s", strerror(errno));
210 KRK_Function(getpid) {
211 FUNCTION_TAKES_NONE();
212 return INTEGER_VAL(getpid());
215 KRK_Function(strerror) {
217 if (!
krk_parseArgs(
"i",(
const char*[]){
"errnum"},&errnum))
return NONE_VAL();
218 char *s = strerror(errnum);
219 if (!s)
return NONE_VAL();
223 KRK_Function(access) {
226 if (!
krk_parseArgs(
"si",(
const char*[]){
"pathname",
"mode"},&path,&mask))
return NONE_VAL();
227 if (access(path, mask) == 0)
return BOOLEAN_VAL(1);
228 return BOOLEAN_VAL(0);
231 KRK_Function(abort) {
237 if (!
krk_parseArgs(
"i",(
const char*[]){
"status"},&status))
return NONE_VAL();
241 KRK_Function(remove) {
243 if (!
krk_parseArgs(
"s",(
const char*[]){
"path"}, &path))
return NONE_VAL();
244 if (remove(path) != 0) {
250 KRK_Function(truncate) {
253 if (!
krk_parseArgs(
"sn",(
const char*[]){
"path",
"length"}, &path, &length))
return NONE_VAL();
254 if (truncate(path, length) != 0) {
262 if (!
krk_parseArgs(
"i",(
const char*[]){
"fd"}, &fd))
return NONE_VAL();
263 int result = dup(fd);
267 return INTEGER_VAL(result);
272 if (!
krk_parseArgs(
"ii",(
const char*[]){
"fd",
"fd2"}, &fd, &fd2))
return NONE_VAL();
273 int result = dup2(fd,fd2);
277 return INTEGER_VAL(result);
280 KRK_Function(isatty) {
282 if (!
krk_parseArgs(
"i",(
const char*[]){
"fd"}, &fd))
return NONE_VAL();
283 return BOOLEAN_VAL(isatty(fd));
286 KRK_Function(lseek) {
289 if (!
krk_parseArgs(
"ini",(
const char*[]){
"fd",
"offset",
"how"}, &fd, &offset, &how))
return NONE_VAL();
290 off_t result = lseek(fd,offset,how);
294 return INTEGER_VAL(result);
301 if (!
krk_parseArgs(
"si|i",(
const char*[]){
"path",
"flags",
"mode"}, &path, &flags, &mode))
return NONE_VAL();
302 int result = open(path, flags, mode);
306 return INTEGER_VAL(result);
309 KRK_Function(close) {
311 if (!
krk_parseArgs(
"i",(
const char*[]){
"fd"}, &fd))
return NONE_VAL();
312 if (close(fd) == -1) {
319 #define mkdir(p,m) mkdir(p); (void)m
321 KRK_Function(mkdir) {
324 if (!
krk_parseArgs(
"s|i",(
const char*[]){
"path",
"mode"}, &path, &mode))
return NONE_VAL();
325 int result = mkdir(path, mode);
335 if (!
krk_parseArgs(
"in",(
const char*[]){
"fd",
"count"}, &fd, &count))
return NONE_VAL();
337 uint8_t * tmp = malloc(count);
338 ssize_t result = read(fd,tmp,count);
350 #define IS_bytes(o) IS_BYTES(o)
351 #define AS_bytes(o) AS_BYTES(o)
354 KRK_Function(write) {
357 if (!
krk_parseArgs(
"iO!",(
const char*[]){
"fd",
"buf"}, &fd, KRK_BASE_CLASS(bytes), &buf))
return NONE_VAL();
363 return INTEGER_VAL(result);
368 FUNCTION_TAKES_NONE();
370 if (pipe(fds) == -1) {
374 AS_TUPLE(
krk_peek(0))->values.values[0] = INTEGER_VAL(fds[0]);
375 AS_TUPLE(
krk_peek(0))->values.values[1] = INTEGER_VAL(fds[1]);
376 AS_TUPLE(
krk_peek(0))->values.count = 2;
381 FUNCTION_TAKES_EXACTLY(2);
384 if (!
krk_parseArgs(
"ni",(
const char*[]){
"pid",
"sig"}, &pid, &sig))
return NONE_VAL();
386 int result = kill(pid, sig);
390 return INTEGER_VAL(result);
394 FUNCTION_TAKES_NONE();
395 return INTEGER_VAL(fork());
398 KRK_Function(symlink) {
401 if (!
krk_parseArgs(
"ss",(
const char*[]){
"target",
"linkpath"}, &src, &dst))
return NONE_VAL();
403 if (symlink(src, dst) != 0) {
409 KRK_Function(tcgetpgrp) {
411 if (!
krk_parseArgs(
"i",(
const char*[]){
"fd"}, &fd))
return NONE_VAL();
412 int result = tcgetpgrp(fd);
416 return INTEGER_VAL(result);
419 KRK_Function(tcsetpgrp) {
422 if (!
krk_parseArgs(
"in",(
const char*[]){
"fd",
"pgrp"}, &fd, &pgrp))
return NONE_VAL();
423 int result = tcsetpgrp(fd,pgrp);
430 KRK_Function(ttyname) {
432 if (!
krk_parseArgs(
"i",(
const char*[]){
"fd"}, &fd))
return NONE_VAL();
433 char * result = ttyname(fd);
440 KRK_Function(get_terminal_size) {
442 if (!
krk_parseArgs(
"|i",(
const char*[]){
"fd"}, &fd))
return NONE_VAL();
445 int res = ioctl(fd, TIOCGWINSZ, &wsz);
452 AS_TUPLE(
krk_peek(0))->values.values[0] = INTEGER_VAL(wsz.ws_col);
453 AS_TUPLE(
krk_peek(0))->values.values[1] = INTEGER_VAL(wsz.ws_row);
454 AS_TUPLE(
krk_peek(0))->values.count = 2;
459 static int makeArgs(
int count,
const KrkValue * values,
char *** argsOut,
const char * _method_name) {
460 char ** out = malloc(
sizeof(
char*)*(count+1));
461 for (
int i = 0; i < count; ++i) {
462 if (!IS_STRING(values[i])) {
464 TYPE_ERROR(str,values[i]);
467 out[i] = AS_CSTRING(values[i]);
474 KRK_Function(execl) {
475 FUNCTION_TAKES_AT_LEAST(1);
478 if (makeArgs(argc-1,&argv[1],&args,_method_name))
return NONE_VAL();
479 if (execv(path->chars, args) == -1) {
483 return krk_runtimeError(KRK_EXC(OSError),
"Expected to not return from exec, but did.");
486 KRK_Function(execlp) {
487 FUNCTION_TAKES_AT_LEAST(1);
490 if (makeArgs(argc-1,&argv[1],&args,_method_name))
return NONE_VAL();
491 if (execvp(filename->chars, args) == -1) {
495 return krk_runtimeError(KRK_EXC(OSError),
"Expected to not return from exec, but did.");
498 KRK_Function(execle) {
499 FUNCTION_TAKES_AT_LEAST(1);
501 CHECK_ARG((argc-1),list,
KrkList*,envp);
504 if (makeArgs(argc-2,&argv[1],&args,_method_name))
return NONE_VAL();
505 if (makeArgs(envp->values.count, envp->values.values,&env,_method_name)) {
509 if (execve(path->chars, args, env) == -1) {
514 return krk_runtimeError(KRK_EXC(OSError),
"Expected to not return from exec, but did.");
517 KRK_Function(execv) {
518 FUNCTION_TAKES_EXACTLY(2);
520 CHECK_ARG(1,list,
KrkList*,args);
522 if (makeArgs(args->values.count, args->values.values, &argp,_method_name))
return NONE_VAL();
523 if (execv(filename->chars, argp) == -1) {
527 return krk_runtimeError(KRK_EXC(OSError),
"Expected to not return from exec, but did.");
530 KRK_Function(execvp) {
531 FUNCTION_TAKES_EXACTLY(2);
533 CHECK_ARG(1,list,
KrkList*,args);
535 if (makeArgs(args->values.count, args->values.values, &argp,_method_name))
return NONE_VAL();
536 if (execvp(path->chars, argp) == -1) {
540 return krk_runtimeError(KRK_EXC(OSError),
"Expected to not return from exec, but did.");
543 #define SET(thing) krk_attachNamedValue(&out->fields, #thing, INTEGER_VAL(buf. thing))
545 #define STAT_STRUCT struct __stat64
548 #define STAT_STRUCT struct stat
552 if (!
krk_parseArgs(
"s",(
const char*[]){
"path"}, &path))
return NONE_VAL();
555 int result = stat(path, &buf);
577 #define IS_stat_result(o) (krk_isInstanceOf(o,os_stat_result))
578 #define AS_stat_result(o) AS_INSTANCE(o)
579 #define CURRENT_NAME self
581 #define getProp(name) \
582 KrkValue name = NONE_VAL(); \
583 krk_tableGet(&self->fields, OBJECT_VAL(S(#name)), &name); \
584 if (!IS_INTEGER(name)) return krk_runtimeError(vm.exceptions->valueError, "stat_result is invalid")
586 KRK_Method(stat_result,__repr__) {
596 char * buf = malloc(1024);
597 size_t len = snprintf(buf,1024,
606 (
int)AS_INTEGER(st_dev),
607 (
int)AS_INTEGER(st_ino),
608 (
int)AS_INTEGER(st_mode),
609 (
int)AS_INTEGER(st_nlink),
610 (
int)AS_INTEGER(st_uid),
611 (
int)AS_INTEGER(st_gid),
612 (
int)AS_INTEGER(st_size));
614 if (len > 1023) len = 1023;
621 KRK_DOC(module,
"@brief Provides access to low-level system operations.");
643 #define DO_INT(name) krk_attachNamedValue(&module->fields, #name, INTEGER_VAL(name))
680 KRK_DOC(BIND_FUNC(module,uname),
681 "@brief Returns a @ref dict of attributes describing the current platform.\n\n"
682 "On POSIX platforms, the result should match the contents and layout of a standard @c uname() call. "
683 "On Windows, values are synthesized from available information.");
684 KRK_DOC(BIND_FUNC(module,system),
685 "@brief Call the system shell.\n"
687 "Runs @p cmd using the system shell and returns the platform-dependent return value.");
688 KRK_DOC(BIND_FUNC(module,getcwd),
689 "@brief Get the name of the current working directory.");
690 KRK_DOC(BIND_FUNC(module,chdir),
691 "@brief Change the current working directory.\n"
692 "@arguments newcwd\n\n"
693 "Attempts to change the working directory to @p newcwd. Raises @ref OSError on failure.");
694 KRK_DOC(BIND_FUNC(module,getpid),
695 "@brief Obtain the system process identifier.");
696 KRK_DOC(BIND_FUNC(module,strerror),
697 "@brief Convert an integer error code to a string.\n"
698 "@arguments errorno\n\n"
699 "Provides the string description for the error code specified by @p errorno.");
700 KRK_DOC(BIND_FUNC(module,abort),
701 "@brief Abort the current process.\n\n"
702 "@bsnote{This will exit the interpreter without calling cleanup routines.}");
703 KRK_DOC(BIND_FUNC(module,exit),
704 "@brief Exit the current process.\n\n"
705 "@bsnote{This will exit the interpreter without calling cleanup routines.}");
706 KRK_DOC(BIND_FUNC(module,remove),
707 "@brief Delete a file.\n"
708 "@arguments path\n\n"
709 "Attempts to delete the file at @p path.");
710 KRK_DOC(BIND_FUNC(module,truncate),
711 "@brief Resize a file.\n"
712 "@arguments path,length\n\n"
713 "Attempts to resize the file at @p path to @p length bytes.");
715 "@brief Duplicate a file descriptor.\n"
717 "Returns a new file descriptor pointing to the same file as @p fd.");
718 KRK_DOC(BIND_FUNC(module,dup2),
719 "@brief Duplicate a file descriptor.\n"
720 "@arguments oldfd,newfd\n\n"
721 "Like @ref dup but the new file descriptor is placed at @p newfd.\n");
722 KRK_DOC(BIND_FUNC(module,isatty),
723 "@brief Determine if a file descriptor is a terminal.\n"
725 "Returns a @ref bool indicating whether the open file descriptor @p fd refers to a terminal.");
726 KRK_DOC(BIND_FUNC(module,lseek),
727 "@brief Seek an open file descriptor.\n"
728 "@arguments fd,pos,how\n\n"
729 "Seeks the open file descriptor @p fd by @p pos bytes as specified in @p how. "
730 "Use the values @c SEEK_SET, @c SEEK_CUR, and @c SEEK_END for @p how.");
731 KRK_DOC(BIND_FUNC(module,open),
732 "@brief Open a file.\n"
733 "@arguments path,flags,mode=0o777\n\n"
734 "Opens the file at @p path with the specified @p flags and @p mode. Returns a file descriptor.\n\n"
735 "@bsnote{Not to be confused with <a class=\"el\" href=\"mod_fileio.html#open\">fileio.open</a>}");
736 KRK_DOC(BIND_FUNC(module,close),
737 "@brief Close an open file descriptor.\n"
739 KRK_DOC(BIND_FUNC(module,read),
740 "@brief Read from an open file descriptor.\n"
741 "@arguments fd,n\n\n"
742 "Reads at most @p n bytes from the open file descriptor @p fd.");
743 KRK_DOC(BIND_FUNC(module,write),
744 "@brief Write to an open file descriptor.\n"
745 "@arguments fd,data\n\n"
746 "Writes the @ref bytes object @p data to the open file descriptor @p fd.");
747 KRK_DOC(BIND_FUNC(module,mkdir),
748 "@brief Create a directory.\n"
749 "@arguments path,mode=0o777\n\n"
750 "Creates a directory at @p path.");
752 KRK_DOC(BIND_FUNC(module,execl),
753 "@brief Replace the current process.\n"
754 "@arguments path,[args...]\n\n"
755 "The @c exec* family of functions replaces the calling process's image with a new one. "
756 "@c execl takes a @p path to a binary and an arbitrary number of @ref str arguments to "
757 "pass to the new executable.");
758 KRK_DOC(BIND_FUNC(module,execle),
759 "@brief Replace the current process.\n"
760 "@arguments path,[args...],env\n\n"
761 "The @c exec* family of functions replaces the calling process's image with a new one. "
762 "@c execle takes a @p path to a binary, an arbitrary number of @ref str arguments to "
763 "pass to the new executable, and @ref list of @c 'KEY=VALUE' pairs to set as the new "
765 KRK_DOC(BIND_FUNC(module,execlp),
766 "@brief Replace the current process.\n"
767 "@arguments filename,[args...]\n\n"
768 "The @c exec* family of functions replaces the calling process's image with a new one. "
769 "@c execlp takes a @p filename of a binary and an arbitrary number of @ref str arguments to "
770 "pass to the new executable. @p filename will be searched for in @c $PATH.");
771 KRK_DOC(BIND_FUNC(module,execv),
772 "@brief Replace the current process.\n"
773 "@arguments path,args\n\n"
774 "The @c exec* family of functions replaces the calling process's image with a new one. "
775 "@c execv takes a @p path to a binary and a @ref list @p args of @ref str arguments to "
776 "pass to the new executable.");
777 KRK_DOC(BIND_FUNC(module,execvp),
778 "@brief Replace the current process.\n"
779 "@arguments filename,args\n\n"
780 "The @c exec* family of functions replaces the calling process's image with a new one. "
781 "@c execvp takes a @p filename of a binary and a @ref list @p args of @ref str arguments to "
782 "pass to the new executable. @p filename will be searched for in @c $PATH.");
788 KRK_DOC(BIND_FUNC(module,access),
789 "@brief Determine if a file can be accessed.\n"
790 "@arguments path,mask\n\n"
791 "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 "
792 "process has sufficient access rights to perform the requested operations on the file "
796 KRK_DOC(BIND_FUNC(module,pipe),
797 "@brief Create a pipe.\n\n"
798 "Creates a _pipe_, returning a two-tuple of file descriptors for the read and write ends respectively.");
799 KRK_DOC(BIND_FUNC(module,kill),
800 "@brief Send a signal to a process.\n"
801 "@arguments pid,signum\n\n"
802 "Send the signal @p signum to the process at @p pid.\n");
803 KRK_DOC(BIND_FUNC(module,fork),
804 "@brief Fork the current process.\n\n"
805 "Returns the PID of the new child process in the original process and @c 0 in the child.");
806 KRK_DOC(BIND_FUNC(module,symlink),
807 "@brief Create a symbolic link.\n"
808 "@arguments src,dst\n\n"
809 "Creates a symbolic link at @p src pointing to @p dst.");
811 KRK_DOC(BIND_FUNC(module,tcgetpgrp),
812 "@brief Get the terminal foreground process group.\n"
814 "Return the PID representing the foreground process group of the terminal specified by the file descriptor @p fd.");
815 KRK_DOC(BIND_FUNC(module,tcsetpgrp),
816 "@brief %Set the terminal foreground process group.\n"
817 "@arguments fd,pgrp\n\n"
818 "%Set the PID representing the foreground process group of the terminal specified by the file descriptor @p fd to @p pgrp.");
819 KRK_DOC(BIND_FUNC(module,ttyname),
820 "@brief Get the path to a terminal device.\n"
822 "Returns a @ref str representing the path to the terminal device provided by the file descriptor @p fd.");
824 KRK_DOC(BIND_FUNC(module,get_terminal_size),
825 "@brief Obtain the size of the terminal window.\n"
827 "Obtain the size of the host terminal as a tuple of columns and lines.");
830 _loadEnviron(module);
834 BIND_METHOD(stat_result,__repr__);
837 KRK_DOC(BIND_FUNC(module,stat),
838 "@brief Get the status of a file\n"
839 "@arguments path\n\n"
840 "Runs the @c stat system call on @p path. Returns a @ref stat_result.\n");
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Struct definitions for core object types.
Immutable sequence of bytes.
size_t length
Length of data in bytes.
uint8_t * bytes
Pointer to separately-stored bytes data.
KrkBytes * krk_newBytes(size_t length, uint8_t *source)
Create a new byte array.
KrkClass * krk_makeClass(KrkInstance *module, KrkClass **_class, const char *name, KrkClass *base)
Convenience function for creating new types.
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.
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
KrkTable fields
Attributes table.
The most basic object type.
Immutable sequence of Unicode codepoints.
KrkString * krk_copyString(const char *chars, size_t length)
Obtain a string object representation of the given C string.
char * chars
UTF8 canonical data.
size_t length
String length in bytes.
int krk_tableSet(KrkTable *table, KrkValue key, KrkValue value)
Assign a value to a key in a table.
void krk_attachNamedObject(KrkTable *table, const char name[], KrkObj *obj)
Attach an object to an attribute table.
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
Stack reference or primative value.
Utilities for creating native bindings.
#define krk_parseArgs(f, n,...)
Parse arguments to a function while accepting keyword arguments.
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
KrkValue krk_pop(void)
Pop the top of the stack.
void krk_push(KrkValue value)
Push a stack value.
KrkValue krk_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
KrkValue krk_peek(int distance)
Peek down from the top of the stack.