aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2017-06-09 08:40:24 +0200
committerBjörn Linse <bjorn.linse@gmail.com>2017-11-25 09:37:00 +0100
commit5af47031773fc647de867444693d1598d0da458d (patch)
tree5ffc630accb7945c69c0b051768283fac664cf1b /src
parent90e5cc5484ceeb410ae2a2706e09ed475cade4a5 (diff)
downloadrneovim-5af47031773fc647de867444693d1598d0da458d.tar.gz
rneovim-5af47031773fc647de867444693d1598d0da458d.tar.bz2
rneovim-5af47031773fc647de867444693d1598d0da458d.zip
channels: stderr channel
Diffstat (limited to 'src')
-rw-r--r--src/nvim/channel.c71
-rw-r--r--src/nvim/channel.h11
-rw-r--r--src/nvim/eval.c79
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua3
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/main.c7
7 files changed, 120 insertions, 56 deletions
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 416e0a1fb6..4d9304472b 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -14,6 +14,12 @@
static bool did_stdio = false;
PMap(uint64_t) *channels = NULL;
+/// next free id for a job or rpc channel
+/// 1 is reserved for stdio channel
+/// 2 is reserved for stderr channel
+static uint64_t next_chan_id = CHAN_STDERR+1;
+
+
typedef struct {
Channel *data;
Callback *callback;
@@ -73,7 +79,6 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
}
} else if ((part == kChannelPartStdin || part == kChannelPartStdout)
&& chan->is_rpc) {
- // EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')"));
*error = (const char *)e_invstreamrpc;
return false;
}
@@ -117,6 +122,21 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
}
break;
+ case kChannelStreamStderr:
+ if (part != kChannelPartAll && part != kChannelPartStderr) {
+ *error = (const char *)e_invstream;
+ return false;
+ }
+ if (!chan->stream.err.closed) {
+ chan->stream.err.closed = true;
+ // Don't close on exit, in case late error messages
+ if (!exiting) {
+ fclose(stderr);
+ }
+ channel_decref(chan);
+ }
+ break;
+
case kChannelStreamInternal:
if (!close_main) {
*error = (const char *)e_invstream;
@@ -132,6 +152,7 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
void channel_init(void)
{
channels = pmap_new(uint64_t)();
+ channel_alloc(kChannelStreamStderr);
rpc_init();
remote_ui_init();
}
@@ -143,7 +164,13 @@ void channel_init(void)
static Channel *channel_alloc(ChannelStreamType type)
{
Channel *chan = xcalloc(1, sizeof(*chan));
- chan->id = type == kChannelStreamStdio ? 1 : next_chan_id++;
+ if (type == kChannelStreamStdio) {
+ chan->id = CHAN_STDIO;
+ } else if (type == kChannelStreamStderr) {
+ chan->id = CHAN_STDERR;
+ } else {
+ chan->id = next_chan_id++;
+ }
chan->events = multiqueue_new_child(main_loop.events);
chan->refcount = 1;
chan->streamtype = type;
@@ -403,6 +430,46 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output,
return channel->id;
}
+/// @param data will be consumed
+size_t channel_send(uint64_t id, char *data, size_t len, const char **error)
+{
+ Channel *chan = find_channel(id);
+ if (!chan) {
+ EMSG(_(e_invchan));
+ goto err;
+ }
+
+ if (chan->streamtype == kChannelStreamStderr) {
+ if (chan->stream.err.closed) {
+ *error = _("Can't send data to closed stream");
+ goto err;
+ }
+ // unbuffered write
+ size_t written = fwrite(data, len, 1, stderr);
+ xfree(data);
+ return len * written;
+ }
+
+
+ Stream *in = channel_instream(chan);
+ if (in->closed) {
+ *error = _("Can't send data to closed stream");
+ goto err;
+ }
+
+ if (chan->is_rpc) {
+ *error = _("Can't send raw data to rpc channel");
+ goto err;
+ }
+
+ WBuffer *buf = wstream_new_buffer(data, len, 1, xfree);
+ return wstream_write(in, buf) ? len : 0;
+
+err:
+ xfree(data);
+ return 0;
+}
+
// vimscript job callbacks must be executed on Nvim main loop
static inline void process_channel_event(Channel *chan, Callback *callback,
const char *type, char *buf,
diff --git a/src/nvim/channel.h b/src/nvim/channel.h
index eaf0fd92d0..ee119756c0 100644
--- a/src/nvim/channel.h
+++ b/src/nvim/channel.h
@@ -9,10 +9,14 @@
#include "nvim/eval/typval.h"
#include "nvim/msgpack_rpc/channel_defs.h"
+#define CHAN_STDIO 1
+#define CHAN_STDERR 2
+
typedef enum {
kChannelStreamProc,
kChannelStreamSocket,
kChannelStreamStdio,
+ kChannelStreamStderr,
kChannelStreamInternal
} ChannelStreamType;
@@ -31,6 +35,10 @@ typedef struct {
} StdioPair;
typedef struct {
+ bool closed;
+} StderrState;
+
+typedef struct {
Callback cb;
garray_T buffer;
bool buffered;
@@ -56,6 +64,7 @@ struct Channel {
PtyProcess pty;
Stream socket;
StdioPair stdio;
+ StderrState err;
} stream;
bool is_rpc;
@@ -95,6 +104,7 @@ static inline Stream *channel_instream(Channel *chan)
return &chan->stream.stdio.out;
case kChannelStreamInternal:
+ case kChannelStreamStderr:
abort();
}
abort();
@@ -114,6 +124,7 @@ static inline Stream *channel_outstream(Channel *chan)
return &chan->stream.stdio.in;
case kChannelStreamInternal:
+ case kChannelStreamStderr:
abort();
}
abort();
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ba356f28b9..f92e2d8d65 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -366,6 +366,7 @@ static struct vimvar {
VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
+ VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_REG, "register", VAR_STRING, VV_RO),
VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
@@ -586,6 +587,7 @@ void eval_init(void)
v_event->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_EVENT, v_event);
set_vim_var_list(VV_ERRORS, tv_list_alloc());
+ set_vim_var_nr(VV_STDERR, CHAN_STDERR);
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
set_vim_var_nr(VV_COUNT1, 1);
@@ -7361,6 +7363,37 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+// "chansend(id, data)" function
+static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type == VAR_UNKNOWN) {
+ // First argument is the channel id and second is the data to write
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ ptrdiff_t input_len = 0;
+ char *input = save_tv_as_string(&argvars[1], &input_len, false);
+ if (!input) {
+ // Either the error has been handled by save_tv_as_string(),
+ // or there is no input to send.
+ return;
+ }
+ uint64_t id = argvars[0].vval.v_number;
+ const char *error = NULL;
+ rettv->vval.v_number = channel_send(id, input, input_len, &error);
+ if (error) {
+ EMSG(error);
+ }
+}
+
/*
* "char2nr(string)" function
*/
@@ -11454,52 +11487,6 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = proc->pid;
}
-// "jobsend()" function
-static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
-
- if (check_restricted() || check_secure()) {
- return;
- }
-
- if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type == VAR_UNKNOWN) {
- // First argument is the job id and second is the string or list to write
- // to the job's stdin
- EMSG(_(e_invarg));
- return;
- }
-
- Channel *data = find_channel(argvars[0].vval.v_number);
- if (!data) {
- EMSG(_(e_invchan));
- return;
- }
-
- Stream *in = channel_instream(data);
- if (in->closed) {
- EMSG(_("Can't send data to the job: stdin is closed"));
- return;
- }
-
- if (data->is_rpc) {
- EMSG(_("Can't send raw data to rpc channel"));
- return;
- }
-
- ptrdiff_t input_len = 0;
- char *input = save_tv_as_string(&argvars[1], &input_len, false);
- if (!input) {
- // Either the error has been handled by save_tv_as_string(), or there is no
- // input to send.
- return;
- }
-
- WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree);
- rettv->vval.v_number = wstream_write(in, buf);
-}
-
// "jobresize(job, width, height)" function
static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 7814a086fa..0c0a6881f6 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -56,6 +56,7 @@ typedef enum {
VV_DYING,
VV_EXCEPTION,
VV_THROWPOINT,
+ VV_STDERR,
VV_REG,
VV_CMDBANG,
VV_INSERTMODE,
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index bb03691fd4..54cbc54d78 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -56,6 +56,7 @@ return {
ceil={args=1, func="float_op_wrapper", data="&ceil"},
changenr={},
chanclose={args={1, 2}},
+ chansend={args=2},
char2nr={args={1, 2}},
cindent={args=1},
clearmatches={},
@@ -177,7 +178,7 @@ return {
jobclose={args={1, 2}, func="f_chanclose"},
jobpid={args=1},
jobresize={args=3},
- jobsend={args=2},
+ jobsend={args=2, func="f_chansend"},
jobstart={args={1, 2}},
jobstop={args=1},
jobwait={args={1, 2}},
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index d1b0ad0ed3..dcb8b40973 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1199,10 +1199,6 @@ EXTERN bool embedded_mode INIT(= false);
// or read/write to stdio (unless embedding)
EXTERN bool headless_mode INIT(= false);
-/// next free id for a job or rpc channel
-/// 1 is reserved for stdio channel
-EXTERN uint64_t next_chan_id INIT(= 2);
-
/// Used to track the status of external functions.
/// Currently only used for iconv().
typedef enum {
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 9059644fc0..aa57913f7c 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -297,7 +297,7 @@ int main(int argc, char **argv)
assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX);
cmdline_row = (int)(Rows - p_ch);
msg_row = cmdline_row;
- screenalloc(false); /* allocate screen buffers */
+ screenalloc(false); // allocate screen buffers
set_init_2(headless_mode);
TIME_MSG("inits 2");
@@ -310,8 +310,9 @@ int main(int argc, char **argv)
/* Set the break level after the terminal is initialized. */
debug_break_level = params.use_debug_break_level;
- bool reading_input = !headless_mode && (params.input_isatty
- || params.output_isatty || params.err_isatty);
+ bool reading_input = !headless_mode
+ && (params.input_isatty || params.output_isatty
+ || params.err_isatty);
if (reading_input) {
// One of the startup commands (arguments, sourced scripts or plugins) may