aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c122
-rw-r--r--src/nvim/memory.c19
-rw-r--r--src/nvim/os/rstream.c12
3 files changed, 112 insertions, 41 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index d75807800c..59363a3608 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -453,6 +453,7 @@ static dictitem_T vimvars_var; /* variable used for v: */
typedef struct {
int id;
char *name, *type, *received;
+ size_t received_len;
} JobEvent;
#define JobEventFreer(x)
KMEMPOOL_INIT(JobEventPool, JobEvent, JobEventFreer)
@@ -10592,9 +10593,9 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv)
return;
}
- if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) {
- // First argument is the job id and second is the string to write to
- // the job's stdin
+ 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;
}
@@ -10607,10 +10608,15 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv)
return;
}
- WBuffer *buf = wstream_new_buffer(xstrdup((char *)argvars[1].vval.v_string),
- strlen((char *)argvars[1].vval.v_string),
- 1,
- free);
+ ssize_t input_len;
+ char *input = (char *) save_tv_as_string(&argvars[1], &input_len, true);
+ if (input_len < 0) {
+ return; // Error handled by save_tv_as_string().
+ } else if (input_len == 0) {
+ return; // Not an error, but nothing to do.
+ }
+
+ WBuffer *buf = wstream_new_buffer(input, input_len, 1, free);
rettv->vval.v_number = job_write(job, buf);
}
@@ -14433,6 +14439,29 @@ static void f_synstack(typval_T *argvars, typval_T *rettv)
}
}
+static list_T* string_to_list(char_u *str, size_t len)
+{
+ list_T *list = list_alloc();
+
+ // Copy each line to a list element using NL as the delimiter.
+ for (size_t i = 0; i < len; i++) {
+ char_u *start = str + i;
+ size_t line_len = (char_u *) xmemscan(start, NL, len - i) - start;
+ i += line_len;
+
+ // Don't use a str function to copy res as it may contains NULs.
+ char_u *s = xmemdupz(start, line_len);
+ memchrsub(s, NUL, NL, line_len); // Replace NUL with NL to avoid truncation
+
+ listitem_T *li = listitem_alloc();
+ li->li_tv.v_type = VAR_STRING;
+ li->li_tv.vval.v_string = s;
+ list_append(list, li);
+ }
+
+ return list;
+}
+
static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
bool retlist)
{
@@ -14445,7 +14474,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
// get input to the shell command (if any), and its length
ssize_t input_len;
- char *input = (char *) save_tv_as_string(&argvars[1], &input_len);
+ char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false);
if (input_len == -1) {
return;
}
@@ -14467,23 +14496,9 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
}
if (retlist) {
- list_T *list = rettv_list_alloc(rettv);
-
- // Copy each line to a list element using NL as the delimiter.
- for (size_t i = 0; i < nread; i++) {
- char_u *start = (char_u *) res + i;
- size_t len = (char_u *) xmemscan(start, NL, nread - i) - start;
- i += len;
-
- // Don't use a str function to copy res as it may contains NULs.
- char_u *s = xmemdupz(start, len);
- memchrsub(s, NUL, NL, len); // Replace NUL with NL to avoid truncation.
-
- listitem_T *li = listitem_alloc();
- li->li_tv.v_type = VAR_STRING;
- li->li_tv.vval.v_string = s;
- list_append(list, li);
- }
+ rettv->vval.v_list = string_to_list((char_u *) res, nread);
+ rettv->vval.v_list->lv_refcount++;
+ rettv->v_type = VAR_LIST;
free(res);
} else {
@@ -15148,9 +15163,10 @@ static bool write_list(FILE *fd, list_T *list, bool binary)
///
/// @param[in] tv A value to store as a string.
/// @param[out] len The length of the resulting string or -1 on error.
+/// @param[in] endnl If true, the output will end in a newline (if a list).
/// @returns an allocated string if `tv` represents a VimL string, list, or
/// number; NULL otherwise.
-static char_u *save_tv_as_string(typval_T *tv, ssize_t *len)
+static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
if (tv->v_type == VAR_UNKNOWN) {
@@ -15182,13 +15198,13 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len)
return NULL;
}
- char_u *ret = xmalloc(*len);
+ char_u *ret = xmalloc(*len + endnl);
char_u *end = ret;
for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; s++) {
*end++ = (*s == '\n') ? NUL : *s;
}
- if (li->li_next != NULL) {
+ if (endnl || li->li_next != NULL) {
*end++ = '\n';
}
}
@@ -19528,15 +19544,33 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
// JobActivity autocommands will execute vimscript code, so it must be executed
// on Nvim main loop
-#define push_job_event(j, r, t) \
+#define push_job_event(j, r, t, eof) \
do { \
JobEvent *event_data = kmp_alloc(JobEventPool, job_event_pool); \
event_data->received = NULL; \
+ size_t read_count = 0; \
if (r) { \
- size_t read_count = rstream_pending(r); \
- event_data->received = xmalloc(read_count + 1); \
+ if (eof) { \
+ read_count = rstream_pending(r); \
+ } else { \
+ char *read = rstream_read_ptr(r); \
+ char *lastnl = xmemrchr(read, NL, rstream_pending(r)); \
+ if (lastnl) { \
+ read_count = (size_t) (lastnl - read) + 1; \
+ } else if (rstream_available(r) == 0) { \
+ /* No newline or room to grow; flush everything. */ \
+ read_count = rstream_pending(r); \
+ } \
+ } \
+ if (read_count == 0) { \
+ /* Either we're at EOF or we need to wait until next time */ \
+ /* to receive a '\n. */ \
+ kmp_free(JobEventPool, job_event_pool, event_data); \
+ return; \
+ } \
+ event_data->received_len = read_count; \
+ event_data->received = xmallocz(read_count); \
rstream_read(r, event_data->received, read_count); \
- event_data->received[read_count] = NUL; \
} \
event_data->id = job_id(j); \
event_data->name = job_data(j); \
@@ -19549,31 +19583,33 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
static void on_job_stdout(RStream *rstream, void *data, bool eof)
{
- if (!eof) {
- push_job_event(data, rstream, "stdout");
+ if (rstream_pending(rstream)) {
+ push_job_event(data, rstream, "stdout", eof);
}
}
static void on_job_stderr(RStream *rstream, void *data, bool eof)
{
- if (!eof) {
- push_job_event(data, rstream, "stderr");
+ if (rstream_pending(rstream)) {
+ push_job_event(data, rstream, "stderr", eof);
}
}
static void on_job_exit(Job *job, void *data)
{
- push_job_event(job, NULL, "exit");
+ push_job_event(job, NULL, "exit", true);
}
static void on_job_event(Event event)
{
JobEvent *data = event.data;
- apply_job_autocmds(data->id, data->name, data->type, data->received);
+ apply_job_autocmds(data->id, data->name, data->type,
+ data->received, data->received_len);
kmp_free(JobEventPool, job_event_pool, data);
}
-static void apply_job_autocmds(int id, char *name, char *type, char *received)
+static void apply_job_autocmds(int id, char *name, char *type,
+ char *received, size_t received_len)
{
// Create the list which will be set to v:job_data
list_T *list = list_alloc();
@@ -19582,10 +19618,14 @@ static void apply_job_autocmds(int id, char *name, char *type, char *received)
if (received) {
listitem_T *str_slot = listitem_alloc();
- str_slot->li_tv.v_type = VAR_STRING;
+ str_slot->li_tv.v_type = VAR_LIST;
str_slot->li_tv.v_lock = 0;
- str_slot->li_tv.vval.v_string = (uint8_t *)received;
+ str_slot->li_tv.vval.v_list =
+ string_to_list((char_u *) received, received_len);
+ str_slot->li_tv.vval.v_list->lv_refcount++;
list_append(list, str_slot);
+
+ free(received);
}
// Update v:job_data for the autocommands
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 59edefec4a..4b24213ecd 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -389,6 +389,25 @@ char *xstrdup(const char *str)
return ret;
}
+/// A version of memchr that starts the search at `src + len`.
+///
+/// Based on glibc's memrchr.
+///
+/// @param src The source memory object.
+/// @param c The byte to search for.
+/// @param len The length of the memory object.
+/// @returns a pointer to the found byte in src[len], or NULL.
+void *xmemrchr(void *src, uint8_t c, size_t len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
+{
+ while (len--) {
+ if (((uint8_t *)src)[len] == c) {
+ return (uint8_t *) src + len;
+ }
+ }
+ return NULL;
+}
+
/// strndup() wrapper
///
/// @see {xmalloc}
diff --git a/src/nvim/os/rstream.c b/src/nvim/os/rstream.c
index f16226cdd1..e36a0213c8 100644
--- a/src/nvim/os/rstream.c
+++ b/src/nvim/os/rstream.c
@@ -190,6 +190,18 @@ RStream * rstream_new(rstream_cb cb, RBuffer *buffer, void *data)
return rv;
}
+/// Returns the read pointer used by the rstream.
+char *rstream_read_ptr(RStream *rstream)
+{
+ return rbuffer_read_ptr(rstream->buffer);
+}
+
+/// Returns the number of bytes before the rstream is full.
+size_t rstream_available(RStream *rstream)
+{
+ return rbuffer_available(rstream->buffer);
+}
+
/// Frees all memory allocated for a RStream instance
///
/// @param rstream The `RStream` instance