mirror of
https://github.com/janet-lang/janet
synced 2024-11-25 01:37:19 +00:00
Add :pipe to os/spawn for piping to subprocess.
Similar to Python's subprocess.PIPE, this creates and manages pipes automatically for the caller.
This commit is contained in:
parent
8763df1cd0
commit
d3147b661b
@ -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));
|
||||
}
|
||||
|
@ -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.")
|
||||
},
|
||||
{
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user