diff --git a/src/core/io.c b/src/core/io.c index 3283dd98..d9636db0 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -799,6 +799,10 @@ FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) { return iof->file; } +JanetFile *janet_makejfile(FILE *f, int flags) { + return makef(f, flags); +} + Janet janet_makefile(FILE *f, int flags) { return janet_wrap_abstract(makef(f, flags)); } diff --git a/src/core/os.c b/src/core/os.c index 76dacc05..6d3ed18b 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -414,6 +414,30 @@ static Janet os_proc_kill(int32_t argc, Janet *argv) { } } +/* Create piped file for os/execute and os/spawn. */ +static JanetFile *make_pipes(JanetHandle *handle, int reverse) { + JanetHandle handles[2]; +#ifdef JANET_WINDOWS + bool result = CreatePipe(handles, handles + 1, NULL, 0); + if (result) { + + } else { + + } +#else + if (pipe(handles)) janet_panic(strerror(errno)); + if (reverse) { + JanetHandle temp = handles[0]; + handles[0] = handles[1]; + handles[1] = temp; + } + *handle = handles[1]; + FILE *f = fdopen(handles[0], reverse ? "w" : "r"); + if (NULL == f) janet_panic(strerror(errno)); + return janet_makejfile(f, reverse ? JANET_FILE_WRITE : JANET_FILE_READ); +#endif +} + static const JanetMethod proc_methods[] = { {"wait", os_proc_wait}, {"kill", os_proc_kill}, @@ -470,6 +494,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { /* Optional stdio redirections */ JanetFile *new_in = NULL, *new_out = NULL, *new_err = NULL; + JanetHandle pipe_in = JANET_HANDLE_NONE, pipe_out = JANET_HANDLE_NONE, pipe_err = JANET_HANDLE_NONE; /* Get optional redirections */ if (argc > 2) { @@ -477,9 +502,21 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in")); Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out")); Janet maybe_stderr = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("err")); - if (!janet_checktype(maybe_stdin, JANET_NIL)) new_in = janet_getjfile(&maybe_stdin, 0); - if (!janet_checktype(maybe_stdout, JANET_NIL)) new_out = janet_getjfile(&maybe_stdout, 0); - if (!janet_checktype(maybe_stderr, JANET_NIL)) new_err = janet_getjfile(&maybe_stderr, 0); + if (janet_keyeq(maybe_stdin, "pipe")) { + new_in = make_pipes(&pipe_in, 1); + } else if (!janet_checktype(maybe_stdin, JANET_NIL)) { + new_in = janet_getjfile(&maybe_stdin, 0); + } + if (janet_keyeq(maybe_stdout, "pipe")) { + new_out = make_pipes(&pipe_out, 0); + } else if (!janet_checktype(maybe_stdout, JANET_NIL)) { + new_out = janet_getjfile(&maybe_stdout, 0); + } + if (janet_keyeq(maybe_stderr, "err")) { + new_err = make_pipes(&pipe_err, 0); + } else if (!janet_checktype(maybe_stderr, JANET_NIL)) { + new_err = janet_getjfile(&maybe_stderr, 0); + } } /* Result */ @@ -502,9 +539,24 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { const char *path = (const char *) janet_unwrap_string(exargs.items[0]); /* Do IO redirection */ - startupInfo.hStdInput = (HANDLE) _get_osfhandle((new_in == NULL) ? 0 : _fileno(new_in->file)); - startupInfo.hStdOutput = (HANDLE) _get_osfhandle((new_out == NULL) ? 1 : _fileno(new_out->file)); - startupInfo.hStdError = (HANDLE) _get_osfhandle((new_err == NULL) ? 2 : _fileno(new_err->file)); + + if (pipe_in != JANET_HANDLE_NONE) { + startupInfo.hStdInput = pipe_in; + } else if (new_in != NULL) { + startupInfo.hStdInput = (HANDLE) _get_osfhandle(_fileno(new_in->file)); + } + + if (pipe_out != JANET_HANDLE_NONE) { + startupInfo.hStdInput = pipe_out; + } else if (new_out != NULL) { + startupInfo.hStdOutput = (HANDLE) _get_osfhandle(_fileno(new_out->file)); + } + + if (pipe_err != JANET_HANDLE_NONE) { + startupInfo.hStdInput = pipe_err; + } else if (new_err != NULL) { + startupInfo.hStdError = (HANDLE) _get_osfhandle(_fileno(new_err->file)); + } /* Use _spawn family of functions. */ /* Windows docs say do this before any spawns. */ @@ -524,6 +576,10 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { janet_panic("failed to create process"); } + if (pipe_in != JANET_HANDLE_NONE) CloseHandle(pipe_in); + if (pipe_out != JANET_HANDLE_NONE) CloseHandle(pipe_out); + if (pipe_err != JANET_HANDLE_NONE) CloseHandle(pipe_err); + pHandle = processInfo.hProcess; tHandle = processInfo.hThread; @@ -559,13 +615,19 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { /* Posix spawn setup */ posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); - if (new_in != NULL) { + if (pipe_in != JANET_HANDLE_NONE) { + posix_spawn_file_actions_adddup2(&actions, pipe_in, 0); + } else if (new_in != NULL) { posix_spawn_file_actions_adddup2(&actions, fileno(new_in->file), 0); } - if (new_out != NULL) { + if (pipe_out != JANET_HANDLE_NONE) { + posix_spawn_file_actions_adddup2(&actions, pipe_out, 1); + } else if (new_out != NULL) { posix_spawn_file_actions_adddup2(&actions, fileno(new_out->file), 1); } - if (new_err != NULL) { + if (pipe_err != JANET_HANDLE_NONE) { + posix_spawn_file_actions_adddup2(&actions, pipe_err, 2); + } else if (new_err != NULL) { posix_spawn_file_actions_adddup2(&actions, fileno(new_err->file), 2); } @@ -582,6 +644,10 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) { posix_spawn_file_actions_destroy(&actions); + if (pipe_in != JANET_HANDLE_NONE) close(pipe_in); + if (pipe_out != JANET_HANDLE_NONE) close(pipe_out); + if (pipe_err != JANET_HANDLE_NONE) close(pipe_err); + if (use_environ) { janet_unlock_environ(); } @@ -1554,6 +1620,11 @@ static const JanetReg os_cfuns[] = { "env is a table or struct mapping environment variables to values. It can also " "contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. " "These arguments should be core/file values. " + "One can also pass in the :pipe keyword " + "for these arguments to create files that will read (for :err and :out) or write (for :in) " + "to the file descriptor of the subprocess. This is only useful in os/spawn, which takes " + "the same parameters as os/execute, but will return an object that contains references to these " + "files via (return-value :in), (return-value :out), and (return-value :err). " "Returns the exit status of the program.") }, { diff --git a/src/include/janet.h b/src/include/janet.h index 53aa406d..a3bf38cf 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -310,6 +310,15 @@ JANET_API extern const char *const janet_type_names[16]; JANET_API extern const char *const janet_signal_names[14]; JANET_API extern const char *const janet_status_names[16]; +/* For various IO routines, we want to use an int on posix and HANDLE on windows */ +#ifdef JANET_WINDOWS +typedef void *JanetHandle; +#define JANET_HANDLE_NONE NULL +#else +typedef int JanetHandle; +#define JANET_HANDLE_NONE (-1) +#endif + /* Fiber signals */ typedef enum { JANET_SIGNAL_OK, @@ -1560,6 +1569,7 @@ extern JANET_API const JanetAbstractType janet_file_type; #define JANET_FILE_NONIL 512 JANET_API Janet janet_makefile(FILE *f, int32_t flags); +JANET_API JanetFile *janet_makejfile(FILE *f, int32_t flags); JANET_API FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags); JANET_API FILE *janet_dynfile(const char *name, FILE *def); JANET_API JanetFile *janet_getjfile(const Janet *argv, int32_t n);