mirror of
https://github.com/neovim/neovim.git
synced 2025-09-29 14:38:32 +00:00
job/shell: Refactor os_call_shell/os_system to share code
This commit is contained in:
@@ -24,19 +24,9 @@
|
|||||||
#include "nvim/option_defs.h"
|
#include "nvim/option_defs.h"
|
||||||
#include "nvim/charset.h"
|
#include "nvim/charset.h"
|
||||||
#include "nvim/strings.h"
|
#include "nvim/strings.h"
|
||||||
|
#include "nvim/ui.h"
|
||||||
|
|
||||||
#define DYNAMIC_BUFFER_INIT {NULL, 0, 0}
|
#define DYNAMIC_BUFFER_INIT {NULL, 0, 0}
|
||||||
#define BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool reading;
|
|
||||||
int old_state, old_mode, exit_status, exited;
|
|
||||||
char *wbuffer;
|
|
||||||
char rbuffer[BUFFER_LENGTH];
|
|
||||||
uv_buf_t bufs[2];
|
|
||||||
uv_stream_t *shell_stdin;
|
|
||||||
garray_T ga;
|
|
||||||
} ProcessData;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *data;
|
char *data;
|
||||||
@@ -109,140 +99,70 @@ void shell_free_argv(char **argv)
|
|||||||
/// @param cmd The command to be executed. If NULL it will run an interactive
|
/// @param cmd The command to be executed. If NULL it will run an interactive
|
||||||
/// shell
|
/// shell
|
||||||
/// @param opts Various options that control how the shell will work
|
/// @param opts Various options that control how the shell will work
|
||||||
/// @param extra_shell_arg Extra argument to be passed to the shell
|
/// @param extra_arg Extra argument to be passed to the shell
|
||||||
int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
|
int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_arg)
|
||||||
{
|
{
|
||||||
uv_stdio_container_t proc_stdio[3];
|
DynamicBuffer input = DYNAMIC_BUFFER_INIT;
|
||||||
uv_process_options_t proc_opts;
|
char *output = NULL, **output_ptr = NULL;
|
||||||
uv_process_t proc;
|
int current_state = State, old_mode = cur_tmode;
|
||||||
uv_pipe_t proc_stdin, proc_stdout;
|
bool forward_output = true;
|
||||||
uv_write_t write_req;
|
|
||||||
int expected_exits = 1;
|
|
||||||
ProcessData pdata = {
|
|
||||||
.reading = false,
|
|
||||||
.exited = 0,
|
|
||||||
.old_mode = cur_tmode,
|
|
||||||
.old_state = State,
|
|
||||||
.shell_stdin = (uv_stream_t *)&proc_stdin,
|
|
||||||
.wbuffer = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
out_flush();
|
out_flush();
|
||||||
|
|
||||||
if (opts & kShellOptCooked) {
|
if (opts & kShellOptCooked) {
|
||||||
// set to normal mode
|
|
||||||
settmode(TMODE_COOK);
|
settmode(TMODE_COOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// While the child is running, ignore terminating signals
|
// While the child is running, ignore terminating signals
|
||||||
signal_reject_deadly();
|
signal_reject_deadly();
|
||||||
|
|
||||||
// Create argv for `uv_spawn`
|
|
||||||
// TODO(tarruda): we can use a static buffer for small argument vectors. 1024
|
|
||||||
// bytes should be enough for most of the commands and if more is necessary
|
|
||||||
// we can allocate a another buffer
|
|
||||||
proc_opts.args = shell_build_argv(cmd, extra_shell_arg);
|
|
||||||
proc_opts.file = proc_opts.args[0];
|
|
||||||
proc_opts.exit_cb = exit_cb;
|
|
||||||
// Initialize libuv structures
|
|
||||||
proc_opts.stdio = proc_stdio;
|
|
||||||
proc_opts.stdio_count = 3;
|
|
||||||
// Hide window on Windows :)
|
|
||||||
proc_opts.flags = UV_PROCESS_WINDOWS_HIDE;
|
|
||||||
proc_opts.cwd = NULL;
|
|
||||||
proc_opts.env = NULL;
|
|
||||||
|
|
||||||
// The default is to inherit all standard file descriptors(this will change
|
|
||||||
// when the UI is moved to an external process)
|
|
||||||
proc_stdio[0].flags = UV_INHERIT_FD;
|
|
||||||
proc_stdio[0].data.fd = 0;
|
|
||||||
proc_stdio[1].flags = UV_INHERIT_FD;
|
|
||||||
proc_stdio[1].data.fd = 1;
|
|
||||||
proc_stdio[2].flags = UV_INHERIT_FD;
|
|
||||||
proc_stdio[2].data.fd = 2;
|
|
||||||
|
|
||||||
if (opts & (kShellOptHideMess | kShellOptExpand)) {
|
if (opts & (kShellOptHideMess | kShellOptExpand)) {
|
||||||
// Ignore the shell stdio(redirects to /dev/null on unixes)
|
forward_output = false;
|
||||||
proc_stdio[0].flags = UV_IGNORE;
|
|
||||||
proc_stdio[1].flags = UV_IGNORE;
|
|
||||||
proc_stdio[2].flags = UV_IGNORE;
|
|
||||||
} else {
|
} else {
|
||||||
State = EXTERNCMD;
|
State = EXTERNCMD;
|
||||||
|
|
||||||
if (opts & kShellOptWrite) {
|
if (opts & kShellOptWrite) {
|
||||||
// Write from the current buffer into the process stdin
|
read_input(&input);
|
||||||
uv_pipe_init(uv_default_loop(), &proc_stdin, 0);
|
|
||||||
write_req.data = &pdata;
|
|
||||||
proc_stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
|
|
||||||
proc_stdio[0].data.stream = (uv_stream_t *)&proc_stdin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts & kShellOptRead) {
|
if (opts & kShellOptRead) {
|
||||||
// Read from the process stdout into the current buffer
|
output_ptr = &output;
|
||||||
uv_pipe_init(uv_default_loop(), &proc_stdout, 0);
|
forward_output = false;
|
||||||
proc_stdout.data = &pdata;
|
|
||||||
proc_stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE;
|
|
||||||
proc_stdio[1].data.stream = (uv_stream_t *)&proc_stdout;
|
|
||||||
ga_init(&pdata.ga, 1, BUFFER_LENGTH);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uv_spawn(uv_default_loop(), &proc, &proc_opts)) {
|
size_t nread;
|
||||||
// Failed, probably due to `sh` not being executable
|
int status = shell((const char *)cmd,
|
||||||
if (!emsg_silent) {
|
(const char *)extra_arg,
|
||||||
MSG_PUTS(_("\nCannot execute shell "));
|
input.data,
|
||||||
msg_outtrans(p_sh);
|
input.len,
|
||||||
|
output_ptr,
|
||||||
|
&nread,
|
||||||
|
emsg_silent,
|
||||||
|
forward_output);
|
||||||
|
|
||||||
|
if (input.data) {
|
||||||
|
free(input.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
write_output(output, nread);
|
||||||
|
free(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emsg_silent && status != 0 && !(opts & kShellOptSilent)) {
|
||||||
|
MSG_PUTS(_("\nshell returned "));
|
||||||
|
msg_outnum(status);
|
||||||
msg_putchar('\n');
|
msg_putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
return proc_cleanup_exit(&pdata, &proc_opts, opts);
|
if (old_mode == TMODE_RAW) {
|
||||||
|
// restore mode
|
||||||
|
settmode(TMODE_RAW);
|
||||||
}
|
}
|
||||||
|
State = current_state;
|
||||||
|
signal_accept_deadly();
|
||||||
|
|
||||||
// Assign the flag address after `proc` is initialized by `uv_spawn`
|
return status;
|
||||||
proc.data = &pdata;
|
|
||||||
|
|
||||||
if (opts & kShellOptWrite) {
|
|
||||||
// Queue everything for writing to the shell stdin
|
|
||||||
write_selection(&write_req);
|
|
||||||
expected_exits++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts & kShellOptRead) {
|
|
||||||
// Start the read stream for the shell stdout
|
|
||||||
uv_read_start((uv_stream_t *)&proc_stdout, alloc_cb, read_cb);
|
|
||||||
expected_exits++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep running the loop until all three handles are completely closed
|
|
||||||
while (pdata.exited < expected_exits) {
|
|
||||||
event_poll(0);
|
|
||||||
|
|
||||||
if (got_int) {
|
|
||||||
// Forward SIGINT to the shell
|
|
||||||
// TODO(tarruda): for now this is only needed if the terminal is in raw
|
|
||||||
// mode, but when the UI is externalized we'll also need it, so leave it
|
|
||||||
// here
|
|
||||||
uv_process_kill(&proc, SIGINT);
|
|
||||||
got_int = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts & kShellOptRead) {
|
|
||||||
if (!GA_EMPTY(&pdata.ga)) {
|
|
||||||
// If there's an unfinished line in the growable array, append it now.
|
|
||||||
append_ga_line(&pdata.ga);
|
|
||||||
// remember that the NL was missing
|
|
||||||
curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
|
|
||||||
} else {
|
|
||||||
curbuf->b_no_eol_lnum = 0;
|
|
||||||
}
|
|
||||||
ga_clear(&pdata.ga);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts & kShellOptWrite) {
|
|
||||||
free(pdata.wbuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return proc_cleanup_exit(&pdata, &proc_opts, opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// os_system - synchronously execute a command in the shell
|
/// os_system - synchronously execute a command in the shell
|
||||||
@@ -269,25 +189,49 @@ int os_system(const char *cmd,
|
|||||||
size_t len,
|
size_t len,
|
||||||
char **output,
|
char **output,
|
||||||
size_t *nread) FUNC_ATTR_NONNULL_ARG(1)
|
size_t *nread) FUNC_ATTR_NONNULL_ARG(1)
|
||||||
|
{
|
||||||
|
return shell(cmd, NULL, input, len, output, nread, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shell(const char *cmd,
|
||||||
|
const char *extra_arg,
|
||||||
|
const char *input,
|
||||||
|
size_t len,
|
||||||
|
char **output,
|
||||||
|
size_t *nread,
|
||||||
|
bool silent,
|
||||||
|
bool forward_output) FUNC_ATTR_NONNULL_ARG(1)
|
||||||
{
|
{
|
||||||
// the output buffer
|
// the output buffer
|
||||||
DynamicBuffer buf = DYNAMIC_BUFFER_INIT;
|
DynamicBuffer buf = DYNAMIC_BUFFER_INIT;
|
||||||
|
rstream_cb data_cb = system_data_cb;
|
||||||
|
|
||||||
char **argv = shell_build_argv((char_u *) cmd, NULL);
|
if (forward_output) {
|
||||||
|
data_cb = out_data_cb;
|
||||||
|
} else if (!output) {
|
||||||
|
data_cb = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int i;
|
char **argv = shell_build_argv((char_u *) cmd, (char_u *)extra_arg);
|
||||||
|
|
||||||
|
int status;
|
||||||
Job *job = job_start(argv,
|
Job *job = job_start(argv,
|
||||||
&buf,
|
&buf,
|
||||||
input != NULL,
|
input != NULL,
|
||||||
output ? system_data_cb : NULL,
|
data_cb,
|
||||||
output ? system_data_cb : NULL,
|
data_cb,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
&i);
|
&status);
|
||||||
|
|
||||||
if (i <= 0) {
|
if (status <= 0) {
|
||||||
// couldn't even start the job
|
// Failed, probably due to `sh` not being executable
|
||||||
ELOG("Couldn't start job, error code: '%d'", i);
|
ELOG("Couldn't start job, command: '%s', error code: '%d'", cmd, status);
|
||||||
|
if (!silent) {
|
||||||
|
MSG_PUTS(_("\nCannot execute shell "));
|
||||||
|
msg_outtrans(p_sh);
|
||||||
|
msg_putchar('\n');
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +249,7 @@ int os_system(const char *cmd,
|
|||||||
job_close_in(job);
|
job_close_in(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = job_wait(job, -1);
|
status = job_wait(job, -1);
|
||||||
|
|
||||||
// prepare the out parameters if requested
|
// prepare the out parameters if requested
|
||||||
if (output) {
|
if (output) {
|
||||||
@@ -354,6 +298,14 @@ static void system_data_cb(RStream *rstream, void *data, bool eof)
|
|||||||
buf->len += nread;
|
buf->len += nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void out_data_cb(RStream *rstream, void *data, bool eof)
|
||||||
|
{
|
||||||
|
RBuffer *rbuffer = rstream_buffer(rstream);
|
||||||
|
size_t len = rbuffer_pending(rbuffer);
|
||||||
|
ui_write((char_u *)rbuffer_read_ptr(rbuffer), (int)len);
|
||||||
|
rbuffer_consumed(rbuffer, len);
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a command string into a sequence of words, taking quotes into
|
/// Parses a command string into a sequence of words, taking quotes into
|
||||||
/// consideration.
|
/// consideration.
|
||||||
///
|
///
|
||||||
@@ -414,24 +366,11 @@ static size_t word_length(const char_u *str)
|
|||||||
/// event loop starts. If we don't(by writing in chunks returned by `ml_get`)
|
/// event loop starts. If we don't(by writing in chunks returned by `ml_get`)
|
||||||
/// the buffer being modified might get modified by reading from the process
|
/// the buffer being modified might get modified by reading from the process
|
||||||
/// before we finish writing.
|
/// before we finish writing.
|
||||||
/// Queues selected range for writing to the child process stdin.
|
static void read_input(DynamicBuffer *buf)
|
||||||
///
|
|
||||||
/// @param req The structure containing information to peform the write
|
|
||||||
static void write_selection(uv_write_t *req)
|
|
||||||
{
|
{
|
||||||
ProcessData *pdata = (ProcessData *)req->data;
|
size_t written = 0, l = 0, len = 0;
|
||||||
// TODO(tarruda): use a static buffer for up to a limit(BUFFER_LENGTH) and
|
|
||||||
// only after filled we should start allocating memory(skip unnecessary
|
|
||||||
// allocations for small writes)
|
|
||||||
size_t buflen = BUFFER_LENGTH;
|
|
||||||
pdata->wbuffer = (char *)xmalloc(buflen);
|
|
||||||
uv_buf_t uvbuf;
|
|
||||||
linenr_T lnum = curbuf->b_op_start.lnum;
|
linenr_T lnum = curbuf->b_op_start.lnum;
|
||||||
size_t off = 0;
|
|
||||||
size_t written = 0;
|
|
||||||
char_u *lp = ml_get(lnum);
|
char_u *lp = ml_get(lnum);
|
||||||
size_t l;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
l = strlen((char *)lp + written);
|
l = strlen((char *)lp + written);
|
||||||
@@ -440,26 +379,17 @@ static void write_selection(uv_write_t *req)
|
|||||||
} else if (lp[written] == NL) {
|
} else if (lp[written] == NL) {
|
||||||
// NL -> NUL translation
|
// NL -> NUL translation
|
||||||
len = 1;
|
len = 1;
|
||||||
if (off + len >= buflen) {
|
dynamic_buffer_ensure(buf, buf->len + len);
|
||||||
// Resize the buffer
|
buf->data[buf->len++] = NUL;
|
||||||
buflen *= 2;
|
|
||||||
pdata->wbuffer = xrealloc(pdata->wbuffer, buflen);
|
|
||||||
}
|
|
||||||
pdata->wbuffer[off++] = NUL;
|
|
||||||
} else {
|
} else {
|
||||||
char_u *s = vim_strchr(lp + written, NL);
|
char_u *s = vim_strchr(lp + written, NL);
|
||||||
len = s == NULL ? l : (size_t)(s - (lp + written));
|
len = s == NULL ? l : (size_t)(s - (lp + written));
|
||||||
while (off + len >= buflen) {
|
dynamic_buffer_ensure(buf, buf->len + len);
|
||||||
// Resize the buffer
|
memcpy(buf->data + buf->len, lp + written, len);
|
||||||
buflen *= 2;
|
buf->len += len;
|
||||||
pdata->wbuffer = xrealloc(pdata->wbuffer, buflen);
|
|
||||||
}
|
|
||||||
memcpy(pdata->wbuffer + off, lp + written, len);
|
|
||||||
off += len;
|
|
||||||
}
|
}
|
||||||
if (len == l) {
|
if (len == l) {
|
||||||
// Finished a line, add a NL, unless this line
|
// Finished a line, add a NL, unless this line should not have one.
|
||||||
// should not have one.
|
|
||||||
// FIXME need to make this more readable
|
// FIXME need to make this more readable
|
||||||
if (lnum != curbuf->b_op_end.lnum
|
if (lnum != curbuf->b_op_end.lnum
|
||||||
|| !curbuf->b_p_bin
|
|| !curbuf->b_p_bin
|
||||||
@@ -467,12 +397,8 @@ static void write_selection(uv_write_t *req)
|
|||||||
&& (lnum !=
|
&& (lnum !=
|
||||||
curbuf->b_ml.ml_line_count
|
curbuf->b_ml.ml_line_count
|
||||||
|| curbuf->b_p_eol))) {
|
|| curbuf->b_p_eol))) {
|
||||||
if (off + 1 >= buflen) {
|
dynamic_buffer_ensure(buf, buf->len + 1);
|
||||||
// Resize the buffer
|
buf->data[buf->len++] = NL;
|
||||||
buflen *= 2;
|
|
||||||
pdata->wbuffer = xrealloc(pdata->wbuffer, buflen);
|
|
||||||
}
|
|
||||||
pdata->wbuffer[off++] = NL;
|
|
||||||
}
|
}
|
||||||
++lnum;
|
++lnum;
|
||||||
if (lnum > curbuf->b_op_end.lnum) {
|
if (lnum > curbuf->b_op_end.lnum) {
|
||||||
@@ -484,112 +410,40 @@ static void write_selection(uv_write_t *req)
|
|||||||
written += len;
|
written += len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uvbuf.base = pdata->wbuffer;
|
|
||||||
uvbuf.len = off;
|
|
||||||
|
|
||||||
uv_write(req, pdata->shell_stdin, &uvbuf, 1, write_cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Allocates" a buffer for reading from the shell stdout.
|
static void write_output(char *output, size_t remaining)
|
||||||
static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
|
|
||||||
{
|
{
|
||||||
ProcessData *pdata = (ProcessData *)handle->data;
|
if (!output) {
|
||||||
|
|
||||||
if (pdata->reading) {
|
|
||||||
buf->len = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf->base = pdata->rbuffer;
|
size_t off = 0;
|
||||||
buf->len = BUFFER_LENGTH;
|
while (off < remaining) {
|
||||||
// Avoid `alloc_cb`, `alloc_cb` sequences on windows
|
if (output[off] == NL) {
|
||||||
pdata->reading = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
|
|
||||||
{
|
|
||||||
// TODO(tarruda): avoid using a growable array for this, refactor the
|
|
||||||
// algorithm to call `ml_append` directly(skip unnecessary copies/resizes)
|
|
||||||
int i;
|
|
||||||
ProcessData *pdata = (ProcessData *)stream->data;
|
|
||||||
|
|
||||||
if (cnt <= 0) {
|
|
||||||
if (cnt != UV_ENOBUFS) {
|
|
||||||
uv_read_stop(stream);
|
|
||||||
uv_close((uv_handle_t *)stream, NULL);
|
|
||||||
pdata->exited++;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < cnt; ++i) {
|
|
||||||
if (pdata->rbuffer[i] == NL) {
|
|
||||||
// Insert the line
|
// Insert the line
|
||||||
append_ga_line(&pdata->ga);
|
output[off] = NUL;
|
||||||
} else if (pdata->rbuffer[i] == NUL) {
|
ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false);
|
||||||
|
size_t skip = off + 1;
|
||||||
|
output += skip;
|
||||||
|
remaining -= skip;
|
||||||
|
off = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output[off] == NUL) {
|
||||||
// Translate NUL to NL
|
// Translate NUL to NL
|
||||||
ga_append(&pdata->ga, NL);
|
output[off] = NL;
|
||||||
|
}
|
||||||
|
off++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining) {
|
||||||
|
// append unfinished line
|
||||||
|
ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false);
|
||||||
|
// remember that the NL was missing
|
||||||
|
curbuf->b_no_eol_lnum = curwin->w_cursor.lnum;
|
||||||
} else {
|
} else {
|
||||||
// buffer data into the grow array
|
curbuf->b_no_eol_lnum = 0;
|
||||||
ga_append(&pdata->ga, pdata->rbuffer[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
windgoto(msg_row, msg_col);
|
|
||||||
cursor_on();
|
|
||||||
out_flush();
|
|
||||||
|
|
||||||
pdata->reading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void write_cb(uv_write_t *req, int status)
|
|
||||||
{
|
|
||||||
ProcessData *pdata = (ProcessData *)req->data;
|
|
||||||
uv_close((uv_handle_t *)pdata->shell_stdin, NULL);
|
|
||||||
pdata->exited++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cleanup memory and restore state modified by `os_call_shell`.
|
|
||||||
///
|
|
||||||
/// @param data State shared by all functions collaborating with
|
|
||||||
/// `os_call_shell`.
|
|
||||||
/// @param opts Process spawning options, containing some allocated memory
|
|
||||||
/// @param shellopts Options passed to `os_call_shell`. Used for deciding
|
|
||||||
/// if/which messages are displayed.
|
|
||||||
static int proc_cleanup_exit(ProcessData *proc_data,
|
|
||||||
uv_process_options_t *proc_opts,
|
|
||||||
int shellopts)
|
|
||||||
{
|
|
||||||
if (proc_data->exited) {
|
|
||||||
if (!emsg_silent && proc_data->exit_status != 0 &&
|
|
||||||
!(shellopts & kShellOptSilent)) {
|
|
||||||
MSG_PUTS(_("\nshell returned "));
|
|
||||||
msg_outnum((int64_t)proc_data->exit_status);
|
|
||||||
msg_putchar('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
State = proc_data->old_state;
|
|
||||||
|
|
||||||
if (proc_data->old_mode == TMODE_RAW) {
|
|
||||||
// restore mode
|
|
||||||
settmode(TMODE_RAW);
|
|
||||||
}
|
|
||||||
|
|
||||||
signal_accept_deadly();
|
|
||||||
|
|
||||||
// Release argv memory
|
|
||||||
shell_free_argv(proc_opts->args);
|
|
||||||
|
|
||||||
return proc_data->exit_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exit_cb(uv_process_t *proc, int64_t status, int term_signal)
|
|
||||||
{
|
|
||||||
ProcessData *data = (ProcessData *)proc->data;
|
|
||||||
data->exited++;
|
|
||||||
assert(status <= INT_MAX);
|
|
||||||
data->exit_status = (int)status;
|
|
||||||
uv_close((uv_handle_t *)proc, NULL);
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user