aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c106
-rw-r--r--src/nvim/func_attr.h4
-rw-r--r--src/nvim/keymap.c3
-rw-r--r--src/nvim/keymap.h4
-rw-r--r--src/nvim/message.h1
-rw-r--r--src/nvim/option_defs.h9
-rw-r--r--src/nvim/os/channel.c1
-rw-r--r--src/nvim/os/job.c129
-rw-r--r--src/nvim/os/provider.c1
-rw-r--r--src/nvim/os/rstream.c12
-rw-r--r--src/nvim/os/shell.c147
-rw-r--r--src/nvim/os/wstream.c36
-rw-r--r--src/nvim/os/wstream_defs.h12
-rw-r--r--src/nvim/sha256.c21
-rw-r--r--src/nvim/term.c164
-rw-r--r--src/nvim/term.h2
-rw-r--r--src/nvim/version.c5
17 files changed, 376 insertions, 281 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 77f327c124..e2fee7af49 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6490,6 +6490,7 @@ static struct fst {
{"settabvar", 3, 3, f_settabvar},
{"settabwinvar", 4, 4, f_settabwinvar},
{"setwinvar", 3, 3, f_setwinvar},
+ {"sha256", 1, 1, f_sha256},
{"shellescape", 1, 2, f_shellescape},
{"shiftwidth", 0, 0, f_shiftwidth},
{"simplify", 1, 1, f_simplify},
@@ -9708,9 +9709,6 @@ static void f_has(typval_T *argvars, typval_T *rettv)
"mouse",
#if defined(UNIX)
"mouse_dec",
-# ifdef FEAT_MOUSE_JSB
- "mouse_jsbterm",
-# endif
"mouse_netterm",
"mouse_sgr",
"mouse_urxvt",
@@ -10497,7 +10495,6 @@ static void f_job_start(typval_T *argvars, typval_T *rettv)
on_job_stdout,
on_job_stderr,
on_job_exit,
- true,
0,
&rettv->vval.v_number);
@@ -13102,6 +13099,17 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off)
}
}
+/// f_sha256 - sha256({string}) function
+static void f_sha256(typval_T *argvars, typval_T *rettv)
+{
+ char_u *p = get_tv_string(&argvars[0]);
+ const char_u *hash = sha256_bytes(p, (int) STRLEN(p) , NULL, 0);
+
+ // make a copy of the hash (sha256_bytes returns a static buffer)
+ rettv->vval.v_string = (char_u *) xstrdup((char *) hash);
+ rettv->v_type = VAR_STRING;
+}
+
/*
* "shellescape({string})" function
*/
@@ -14065,76 +14073,58 @@ static void f_synstack(typval_T *argvars, typval_T *rettv)
}
}
-/*
- * "system()" function
- */
+/// f_system - the VimL system() function
static void f_system(typval_T *argvars, typval_T *rettv)
{
- char_u *res = NULL;
- char_u *p;
- char_u *infile = NULL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ // get input to the shell command (if any), and its length
char_u buf[NUMBUFLEN];
- int err = FALSE;
- FILE *fd;
+ const char *input = (argvars[1].v_type != VAR_UNKNOWN)
+ ? (char *) get_tv_string_buf_chk(&argvars[1], buf): NULL;
+ size_t input_len = input ? strlen(input) : 0;
- if (check_restricted() || check_secure())
- goto done;
+ // get shell command to execute
+ const char *cmd = (char *) get_tv_string(&argvars[0]);
- if (argvars[1].v_type != VAR_UNKNOWN) {
- /*
- * Write the string to a temp file, to be used for input of the shell
- * command.
- */
- if ((infile = vim_tempname()) == NULL) {
- EMSG(_(e_notmp));
- goto done;
- }
+ // execute the command
+ size_t nread = 0;
+ char *res = NULL;
+ int status = os_system(cmd, input, input_len, &res, &nread);
- fd = mch_fopen((char *)infile, WRITEBIN);
- if (fd == NULL) {
- EMSG2(_(e_notopen), infile);
- goto done;
- }
- p = get_tv_string_buf_chk(&argvars[1], buf);
- if (p == NULL) {
- fclose(fd);
- goto done; /* type error; errmsg already given */
- }
- if (fwrite(p, STRLEN(p), 1, fd) != 1)
- err = TRUE;
- if (fclose(fd) != 0)
- err = TRUE;
- if (err) {
- EMSG(_("E677: Error writing temp file"));
- goto done;
+ set_vim_var_nr(VV_SHELL_ERROR, (long) status);
+
+#if defined(USE_CR)
+ // translate <CR> into <NL>
+ if (res != NULL) {
+ for (char *s = res; *s; ++s) {
+ if (*s == CAR) {
+ *s = NL;
+ }
}
}
-
- res = get_cmd_output(get_tv_string(&argvars[0]), infile,
- kShellOptSilent | kShellOptCooked);
-
-#ifdef USE_CRNL
- /* translate <CR><NL> into <NL> */
+#elif defined(USE_CRNL)
+ // translate <CR><NL> into <NL>
if (res != NULL) {
- char_u *s, *d;
-
- d = res;
- for (s = res; *s; ++s) {
- if (s[0] == CAR && s[1] == NL)
+ char *d = res;
+ for (char *s = res; *s; ++s) {
+ if (s[0] == CAR && s[1] == NL) {
++s;
+ }
+
*d++ = *s;
}
+
*d = NUL;
}
#endif
-done:
- if (infile != NULL) {
- os_remove((char *)infile);
- free(infile);
- }
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = res;
+ rettv->vval.v_string = (char_u *) res;
}
/*
diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h
index 6c29f0fed4..c75d0ab312 100644
--- a/src/nvim/func_attr.h
+++ b/src/nvim/func_attr.h
@@ -105,6 +105,8 @@
#define REAL_FATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#define REAL_FATTR_ALWAYS_INLINE __attribute__((always_inline))
#define REAL_FATTR_UNUSED __attribute__((unused))
+ #define REAL_FATTR_NONNULL_ALL __attribute__((nonnull))
+ #define REAL_FATTR_NONNULL_ARG(...) __attribute__((nonnull(__VA_ARGS__)))
#ifdef __clang__
// clang only
@@ -118,8 +120,6 @@
// gcc only
#define REAL_FATTR_ALLOC_SIZE(x) __attribute__((alloc_size(x)))
#define REAL_FATTR_ALLOC_SIZE_PROD(x,y) __attribute__((alloc_size(x,y)))
- #define REAL_FATTR_NONNULL_ALL __attribute__((nonnull))
- #define REAL_FATTR_NONNULL_ARG(...) __attribute__((nonnull(__VA_ARGS__)))
#if GCC_VERSION >= 40900
#define REAL_FATTR_NONNULL_RET __attribute__((returns_nonnull))
#endif
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index 4b3b0dd5af..963729215b 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -252,9 +252,6 @@ static struct key_name_entry {
{K_MOUSE, (char_u *)"Mouse"},
{K_NETTERM_MOUSE, (char_u *)"NetMouse"},
{K_DEC_MOUSE, (char_u *)"DecMouse"},
-#ifdef FEAT_MOUSE_JSB
- {K_JSBTERM_MOUSE, (char_u *)"JsbMouse"},
-#endif
{K_URXVT_MOUSE, (char_u *)"UrxvtMouse"},
{K_SGR_MOUSE, (char_u *)"SgrMouse"},
{K_LEFTMOUSE, (char_u *)"LeftMouse"},
diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h
index 490efdd7d9..94ea095ace 100644
--- a/src/nvim/keymap.h
+++ b/src/nvim/keymap.h
@@ -94,9 +94,6 @@
*/
#define KS_TEAROFF 244
-/* Used for JSB term mouse. */
-#define KS_JSBTERM_MOUSE 243
-
/* Used a termcap entry that produces a normal character. */
#define KS_KEY 242
@@ -415,7 +412,6 @@ enum key_extra {
#define K_NETTERM_MOUSE TERMCAP2KEY(KS_NETTERM_MOUSE, KE_FILLER)
#define K_DEC_MOUSE TERMCAP2KEY(KS_DEC_MOUSE, KE_FILLER)
-#define K_JSBTERM_MOUSE TERMCAP2KEY(KS_JSBTERM_MOUSE, KE_FILLER)
#define K_PTERM_MOUSE TERMCAP2KEY(KS_PTERM_MOUSE, KE_FILLER)
#define K_URXVT_MOUSE TERMCAP2KEY(KS_URXVT_MOUSE, KE_FILLER)
#define K_SGR_MOUSE TERMCAP2KEY(KS_SGR_MOUSE, KE_FILLER)
diff --git a/src/nvim/message.h b/src/nvim/message.h
index c620597f33..019c7bfb73 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -2,6 +2,7 @@
#define NVIM_MESSAGE_H
#include <stdbool.h>
+#include <stdarg.h>
#include "nvim/eval_defs.h" // for typval_T
/*
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index f75824ec03..a22eec4136 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -572,16 +572,15 @@ EXTERN char_u *p_ttym; /* 'ttymouse' */
EXTERN unsigned ttym_flags;
# ifdef IN_OPTION_C
static char *(p_ttym_values[]) =
-{"xterm", "xterm2", "dec", "netterm", "jsbterm", "pterm", "urxvt", "sgr", NULL};
+{"xterm", "xterm2", "dec", "netterm", "pterm", "urxvt", "sgr", NULL};
# endif
# define TTYM_XTERM 0x01
# define TTYM_XTERM2 0x02
# define TTYM_DEC 0x04
# define TTYM_NETTERM 0x08
-# define TTYM_JSBTERM 0x10
-# define TTYM_PTERM 0x20
-# define TTYM_URXVT 0x40
-# define TTYM_SGR 0x80
+# define TTYM_PTERM 0x10
+# define TTYM_URXVT 0x20
+# define TTYM_SGR 0x40
#endif
EXTERN char_u *p_udir; /* 'undodir' */
EXTERN long p_ul; /* 'undolevels' */
diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c
index d5f29aa667..1a2efca513 100644
--- a/src/nvim/os/channel.c
+++ b/src/nvim/os/channel.c
@@ -96,7 +96,6 @@ uint64_t channel_from_job(char **argv)
job_out,
job_err,
NULL,
- true,
0,
&status);
diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c
index 203aa2c990..9deca9de74 100644
--- a/src/nvim/os/job.c
+++ b/src/nvim/os/job.c
@@ -14,17 +14,20 @@
#include "nvim/os/event_defs.h"
#include "nvim/os/time.h"
#include "nvim/os/shell.h"
+#include "nvim/os/signal.h"
#include "nvim/vim.h"
#include "nvim/memory.h"
#include "nvim/term.h"
#define EXIT_TIMEOUT 25
#define MAX_RUNNING_JOBS 100
-#define JOB_BUFFER_SIZE 1024
+#define JOB_BUFFER_SIZE 0xFFFF
struct job {
// Job id the index in the job table plus one.
int id;
+ // Exit status code of the job process
+ int64_t status;
// Number of polls after a SIGTERM that will trigger a SIGKILL
int exit_timeout;
// exit_cb may be called while there's still pending data from stdout/stderr.
@@ -36,7 +39,6 @@ struct job {
int pending_closes;
// If the job was already stopped
bool stopped;
- bool defer;
// Data associated with the job
void *data;
// Callbacks
@@ -127,19 +129,16 @@ void job_teardown(void)
/// on stdout
/// @param stderr_cb Callback that will be invoked when data is available
/// on stderr
-/// @param exit_cb Callback that will be invoked when the job exits
-/// @param defer If the job callbacks invocation should be deferred to vim
-/// main loop
+/// @param job_exit_cb Callback that will be invoked when the job exits
/// @param maxmem Maximum amount of memory used by the job WStream
-/// @param[out] The job id if the job started successfully, 0 if the job table
-/// is full, -1 if the program could not be executed.
+/// @param[out] status The job id if the job started successfully, 0 if the job
+/// table is full, -1 if the program could not be executed.
/// @return The job pointer if the job started successfully, NULL otherwise
Job *job_start(char **argv,
void *data,
rstream_cb stdout_cb,
rstream_cb stderr_cb,
job_exit_cb job_exit_cb,
- bool defer,
size_t maxmem,
int *status)
{
@@ -163,6 +162,7 @@ Job *job_start(char **argv,
// Initialize
job->id = i + 1;
*status = job->id;
+ job->status = -1;
job->pending_refs = 3;
job->pending_closes = 4;
job->data = data;
@@ -183,7 +183,6 @@ Job *job_start(char **argv,
job->proc_stdin.data = NULL;
job->proc_stdout.data = NULL;
job->proc_stderr.data = NULL;
- job->defer = defer;
// Initialize the job std{in,out,err}
uv_pipe_init(uv_default_loop(), &job->proc_stdin, 0);
@@ -257,6 +256,101 @@ void job_stop(Job *job)
job->stopped = true;
}
+/// job_wait - synchronously wait for a job to finish
+///
+/// @param job The job instance
+/// @param ms Number of milliseconds to wait, 0 for not waiting, -1 for
+/// waiting until the job quits.
+/// @return returns the status code of the exited job. -1 if the job is
+/// still running and the `timeout` has expired. Note that this is
+/// indistinguishable from the process returning -1 by itself. Which
+/// is possible on some OS.
+int job_wait(Job *job, int ms) FUNC_ATTR_NONNULL_ALL
+{
+ // switch to cooked so `got_int` will be set if the user interrupts
+ int old_mode = cur_tmode;
+ settmode(TMODE_COOK);
+
+ EventSource sources[] = {job_event_source(job), signal_event_source(), NULL};
+
+ // keep track of the elapsed time if ms > 0
+ uint64_t before = (ms > 0) ? os_hrtime() : 0;
+
+ while (1) {
+ // check if the job has exited (and the status is available).
+ if (job->pending_refs == 0) {
+ break;
+ }
+
+ event_poll(ms, sources);
+
+ // we'll assume that a user frantically hitting interrupt doesn't like
+ // the current job. Signal that it has to be killed.
+ if (got_int) {
+ job_stop(job);
+ }
+
+ if (ms == 0) {
+ break;
+ }
+
+ // check if the poll timed out, if not, decrease the ms to wait for the
+ // next run
+ if (ms > 0) {
+ uint64_t now = os_hrtime();
+ ms -= (int) ((now - before) / 1000000);
+ before = now;
+
+ // if the time elapsed is greater than the `ms` wait time, break
+ if (ms <= 0) {
+ break;
+ }
+ }
+ }
+
+ settmode(old_mode);
+
+ // return -1 for a timeout, the job status otherwise
+ return (job->pending_refs) ? -1 : (int) job->status;
+}
+
+/// Close the pipe used to write to the job.
+///
+/// This can be used for example to indicate to the job process that no more
+/// input is coming, and that it should shut down cleanly.
+///
+/// It has no effect when the input pipe doesn't exist or was already
+/// closed.
+///
+/// @param job The job instance
+void job_close_in(Job *job) FUNC_ATTR_NONNULL_ALL
+{
+ if (!job->in) {
+ return;
+ }
+
+ // let other functions in the job module know that the in pipe is no more
+ wstream_free(job->in);
+ job->in = NULL;
+
+ uv_close((uv_handle_t *)&job->proc_stdin, close_cb);
+}
+
+/// All writes that complete after calling this function will be reported
+/// to `cb`.
+///
+/// Use this function to be notified about the status of an in-flight write.
+///
+/// @see {wstream_set_write_cb}
+///
+/// @param job The job instance
+/// @param cb The function that will be called on write completion or
+/// failure. It will be called with the job as the `data` argument.
+void job_write_cb(Job *job, wstream_cb cb) FUNC_ATTR_NONNULL_ALL
+{
+ wstream_set_write_cb(job->in, cb, job);
+}
+
/// Writes data to the job's stdin. This is a non-blocking operation, it
/// returns when the write request was sent.
///
@@ -329,7 +423,9 @@ static bool is_alive(Job *job)
static void free_job(Job *job)
{
uv_close((uv_handle_t *)&job->proc_stdout, close_cb);
- uv_close((uv_handle_t *)&job->proc_stdin, close_cb);
+ if (job->in) {
+ uv_close((uv_handle_t *)&job->proc_stdin, close_cb);
+ }
uv_close((uv_handle_t *)&job->proc_stderr, close_cb);
uv_close((uv_handle_t *)&job->proc, close_cb);
}
@@ -377,6 +473,7 @@ static void exit_cb(uv_process_t *proc, int64_t status, int term_signal)
{
Job *job = handle_get_job((uv_handle_t *)proc);
+ job->status = status;
if (--job->pending_refs == 0) {
emit_exit_event(job);
}
@@ -401,7 +498,17 @@ static void close_cb(uv_handle_t *handle)
// closed by libuv
rstream_free(job->out);
rstream_free(job->err);
- wstream_free(job->in);
+ if (job->in) {
+ wstream_free(job->in);
+ }
+
+ // Free data memory of process and pipe handles, that was allocated
+ // by handle_set_job in job_start.
+ free(job->proc.data);
+ free(job->proc_stdin.data);
+ free(job->proc_stdout.data);
+ free(job->proc_stderr.data);
+
shell_free_argv(job->proc_opts.args);
free(job);
}
diff --git a/src/nvim/os/provider.c b/src/nvim/os/provider.c
index 2e42cbb8f2..99cc078e94 100644
--- a/src/nvim/os/provider.c
+++ b/src/nvim/os/provider.c
@@ -109,6 +109,7 @@ Object provider_call(char *method, Object arg)
"Provider for \"%s\" is not available",
method);
report_error(buf);
+ msgpack_rpc_free_object(arg);
return NIL;
}
diff --git a/src/nvim/os/rstream.c b/src/nvim/os/rstream.c
index d7ab5b8a64..b3a5196351 100644
--- a/src/nvim/os/rstream.c
+++ b/src/nvim/os/rstream.c
@@ -26,7 +26,7 @@ struct rstream {
uv_file fd;
rstream_cb cb;
size_t buffer_size, rpos, wpos, fpos;
- bool reading, free_handle;
+ bool free_handle;
EventSource source_override;
};
@@ -150,7 +150,6 @@ void rstream_start(RStream *rstream)
if (rstream->file_type == UV_FILE) {
uv_idle_start(rstream->fread_idle, fread_idle_cb);
} else {
- rstream->reading = false;
uv_read_start(rstream->stream, alloc_cb, read_cb);
}
}
@@ -236,16 +235,8 @@ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
RStream *rstream = handle_get_rstream(handle);
- if (rstream->reading) {
- buf->len = 0;
- return;
- }
-
buf->len = rstream->buffer_size - rstream->wpos;
buf->base = rstream->buffer + rstream->wpos;
-
- // Avoid `alloc_cb`, `alloc_cb` sequences on windows
- rstream->reading = true;
}
// Callback invoked by libuv after it copies the data into the buffer provided
@@ -287,7 +278,6 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
rstream_event_source(rstream));
}
- rstream->reading = false;
emit_read_event(rstream, false);
}
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 68cd2ad527..398d94b606 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -5,6 +5,10 @@
#include <uv.h>
#include "nvim/ascii.h"
+#include "nvim/lib/kvec.h"
+#include "nvim/log.h"
+#include "nvim/os/job.h"
+#include "nvim/os/rstream.h"
#include "nvim/os/shell.h"
#include "nvim/os/signal.h"
#include "nvim/types.h"
@@ -31,6 +35,11 @@ typedef struct {
garray_T ga;
} ProcessData;
+typedef struct {
+ char *data;
+ size_t cap;
+ size_t len;
+} dyn_buffer_t;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/shell.c.generated.h"
@@ -47,16 +56,13 @@ typedef struct {
/// @param extra_shell_opt Extra argument to the shell. If NULL it is ignored
/// @return A newly allocated argument vector. It must be freed with
/// `shell_free_argv` when no longer needed.
-char ** shell_build_argv(char_u *cmd, char_u *extra_shell_opt)
+char **shell_build_argv(const char_u *cmd, const char_u *extra_shell_opt)
{
- int i;
- char **rv;
int argc = tokenize(p_sh, NULL) + tokenize(p_shcf, NULL);
-
- rv = (char **)xmalloc((unsigned)((argc + 4) * sizeof(char *)));
+ char **rv = xmalloc((unsigned)((argc + 4) * sizeof(char *)));
// Split 'shell'
- i = tokenize(p_sh, rv);
+ int i = tokenize(p_sh, rv);
if (extra_shell_opt != NULL) {
// Push a copy of `extra_shell_opt`
@@ -237,6 +243,127 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
return proc_cleanup_exit(&pdata, &proc_opts, opts);
}
+/// os_system - synchronously execute a command in the shell
+///
+/// example:
+/// char *output = NULL;
+/// size_t nread = 0;
+/// int status = os_sytem("ls -la", NULL, 0, &output, &nread);
+///
+/// @param cmd The full commandline to be passed to the shell
+/// @param input The input to the shell (NULL for no input), passed to the
+/// stdin of the resulting process.
+/// @param len The length of the input buffer (not used if `input` == NULL)
+/// @param[out] output A pointer to to a location where the output will be
+/// allocated and stored. Will point to NULL if the shell
+/// command did not output anything. NOTE: it's not
+/// allowed to pass NULL yet
+/// @param[out] nread the number of bytes in the returned buffer (if the
+/// returned buffer is not NULL)
+/// @return the return code of the process, -1 if the process couldn't be
+/// started properly
+int os_system(const char *cmd,
+ const char *input,
+ size_t len,
+ char **output,
+ size_t *nread) FUNC_ATTR_NONNULL_ARG(1, 4)
+{
+ // the output buffer
+ dyn_buffer_t buf;
+ memset(&buf, 0, sizeof(buf));
+
+ char **argv = shell_build_argv((char_u *) cmd, NULL);
+
+ int i;
+ Job *job = job_start(argv,
+ &buf,
+ system_data_cb,
+ system_data_cb,
+ NULL,
+ 0,
+ &i);
+
+ if (i <= 0) {
+ // couldn't even start the job
+ ELOG("Couldn't start job, error code: '%d'", i);
+ return -1;
+ }
+
+ // write the input, if any
+ if (input) {
+ WBuffer *input_buffer = wstream_new_buffer((char *) input, len, 1, NULL);
+
+ // we want to be notified when the write completes
+ job_write_cb(job, system_write_cb);
+
+ if (!job_write(job, input_buffer)) {
+ // couldn't write, stop the job and tell the user about it
+ job_stop(job);
+ return -1;
+ }
+ } else {
+ // close the input stream, let the process know that no input is coming
+ job_close_in(job);
+ }
+
+ int status = job_wait(job, -1);
+
+ // prepare the out parameters if requested
+ if (buf.len == 0) {
+ // no data received from the process, return NULL
+ *output = NULL;
+ free(buf.data);
+ } else {
+ // NUL-terminate to make the output directly usable as a C string
+ buf.data[buf.len] = NUL;
+ *output = buf.data;
+ }
+
+ if (nread) {
+ *nread = buf.len;
+ }
+
+ return status;
+}
+
+/// dyn_buf_ensure - ensures at least `desired` bytes in buffer
+///
+/// TODO(aktau): fold with kvec/garray
+static void dyn_buf_ensure(dyn_buffer_t *buf, size_t desired)
+{
+ if (buf->cap >= desired) {
+ return;
+ }
+
+ buf->cap = desired;
+ kv_roundup32(buf->cap);
+ buf->data = xrealloc(buf->data, buf->cap);
+}
+
+static void system_data_cb(RStream *rstream, void *data, bool eof)
+{
+ Job *job = data;
+ dyn_buffer_t *buf = job_data(job);
+
+ size_t nread = rstream_available(rstream);
+
+ dyn_buf_ensure(buf, buf->len + nread + 1);
+ rstream_read(rstream, buf->data + buf->len, nread);
+
+ buf->len += nread;
+}
+
+static void system_write_cb(WStream *wstream,
+ void *data,
+ size_t pending,
+ int status)
+{
+ if (pending == 0) {
+ Job *job = data;
+ job_close_in(job);
+ }
+}
+
/// Parses a command string into a sequence of words, taking quotes into
/// consideration.
///
@@ -244,10 +371,10 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg)
/// @param argv The vector that will be filled with copies of the parsed
/// words. It can be NULL if the caller only needs to count words.
/// @return The number of words parsed.
-static int tokenize(char_u *str, char **argv)
+static int tokenize(const char_u *str, char **argv)
{
int argc = 0, len;
- char_u *p = str;
+ char_u *p = (char_u *) str;
while (*p != NUL) {
len = word_length(p);
@@ -271,9 +398,9 @@ static int tokenize(char_u *str, char **argv)
///
/// @param str A pointer to the first character of the word
/// @return The offset from `str` at which the word ends.
-static int word_length(char_u *str)
+static int word_length(const char_u *str)
{
- char_u *p = str;
+ const char_u *p = str;
bool inquote = false;
int length = 0;
diff --git a/src/nvim/os/wstream.c b/src/nvim/os/wstream.c
index 0978d33a10..194bf757e4 100644
--- a/src/nvim/os/wstream.c
+++ b/src/nvim/os/wstream.c
@@ -21,6 +21,9 @@ struct wstream {
// Number of pending requests
size_t pending_reqs;
bool freed;
+ // (optional) Write callback and data
+ wstream_cb cb;
+ void *data;
};
struct wbuffer {
@@ -57,6 +60,7 @@ WStream * wstream_new(size_t maxmem)
rv->curmem = 0;
rv->pending_reqs = 0;
rv->freed = false;
+ rv->cb = NULL;
return rv;
}
@@ -83,6 +87,25 @@ void wstream_set_stream(WStream *wstream, uv_stream_t *stream)
wstream->stream = stream;
}
+/// Sets a callback that will be called on completion of a write request,
+/// indicating failure/success.
+///
+/// This affects all requests currently in-flight as well. Overwrites any
+/// possible earlier callback.
+///
+/// @note This callback will not fire if the write request couldn't even be
+/// queued properly (i.e.: when `wstream_write() returns an error`).
+///
+/// @param wstream The `WStream` instance
+/// @param cb The callback
+/// @param data User-provided data that will be passed to `cb`
+void wstream_set_write_cb(WStream *wstream, wstream_cb cb, void *data)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ wstream->cb = cb;
+ wstream->data = data;
+}
+
/// Queues data for writing to the backing file descriptor of a `WStream`
/// instance. This will fail if the write would cause the WStream use more
/// memory than specified by `maxmem`.
@@ -162,6 +185,14 @@ static void write_cb(uv_write_t *req, int status)
release_wbuffer(data->buffer);
data->wstream->pending_reqs--;
+
+ if (data->wstream->cb) {
+ data->wstream->cb(data->wstream,
+ data->wstream->data,
+ data->wstream->pending_reqs,
+ status);
+ }
+
if (data->wstream->freed && data->wstream->pending_reqs == 0) {
// Last pending write, free the wstream;
free(data->wstream);
@@ -173,7 +204,10 @@ static void write_cb(uv_write_t *req, int status)
static void release_wbuffer(WBuffer *buffer)
{
if (!--buffer->refcount) {
- buffer->cb(buffer->data);
+ if (buffer->cb) {
+ buffer->cb(buffer->data);
+ }
+
free(buffer);
}
}
diff --git a/src/nvim/os/wstream_defs.h b/src/nvim/os/wstream_defs.h
index 1bf61ffce1..e42481f283 100644
--- a/src/nvim/os/wstream_defs.h
+++ b/src/nvim/os/wstream_defs.h
@@ -5,5 +5,17 @@ typedef struct wbuffer WBuffer;
typedef struct wstream WStream;
typedef void (*wbuffer_data_finalizer)(void *data);
+/// Type of function called when the WStream has information about a write
+/// request.
+///
+/// @param wstream The `WStream` instance
+/// @param data User-defined data
+/// @param pending The number of write requests that are still pending
+/// @param status 0 on success, anything else indicates failure
+typedef void (*wstream_cb)(WStream *wstream,
+ void *data,
+ size_t pending,
+ int status);
+
#endif // NVIM_OS_WSTREAM_DEFS_H
diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c
index 9877a82fa1..b11ee2293c 100644
--- a/src/nvim/sha256.c
+++ b/src/nvim/sha256.c
@@ -15,10 +15,10 @@
#include <inttypes.h>
#include <string.h>
+#include "nvim/os/time.h"
#include "nvim/vim.h"
#include "nvim/sha256.h"
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "sha256.c.generated.h"
#endif
@@ -350,21 +350,6 @@ int sha256_self_test(void)
return failures > 0 ? FAIL : OK;
}
-static unsigned int get_some_time(void)
-{
-#ifdef HAVE_GETTIMEOFDAY
- struct timeval tv;
-
- // Using usec makes it less predictable.
- gettimeofday(&tv, NULL);
- return (unsigned int) (tv.tv_sec + tv.tv_usec);
-
-#else // ifdef HAVE_GETTIMEOFDAY
- return (unsigned int) time(NULL);
-
-#endif // ifdef HAVE_GETTIMEOFDAY
-}
-
/// Fill "header[header_len]" with random_data.
/// Also "salt[salt_len]" when "salt" is not NULL.
///
@@ -378,11 +363,11 @@ void sha2_seed(char_u *header, int header_len, char_u *salt, int salt_len)
char_u sha256sum[32];
context_sha256_T ctx;
- srand(get_some_time());
+ srand((unsigned int) os_hrtime());
int i;
for (i = 0; i < (int) sizeof(random_data) - 1; i++) {
- random_data[i] = (char_u) ((get_some_time() ^ rand()) & 0xff);
+ random_data[i] = (char_u) ((os_hrtime() ^ rand()) & 0xff);
}
sha256_start(&ctx);
sha256_update(&ctx, (char_u *) random_data, sizeof(random_data));
diff --git a/src/nvim/term.c b/src/nvim/term.c
index 4ed438b282..b1d751e00a 100644
--- a/src/nvim/term.c
+++ b/src/nvim/term.c
@@ -1636,10 +1636,9 @@ int set_termname(char_u *term)
# define HMT_NORMAL 1
# define HMT_NETTERM 2
# define HMT_DEC 4
-# define HMT_JSBTERM 8
-# define HMT_PTERM 16
-# define HMT_URXVT 32
-# define HMT_SGR 64
+# define HMT_PTERM 8
+# define HMT_URXVT 16
+# define HMT_SGR 32
static int has_mouse_termcode = 0;
void
@@ -1653,11 +1652,6 @@ set_mouse_termcode (
name[0] = n;
name[1] = KE_FILLER;
add_termcode(name, s, FALSE);
-# ifdef FEAT_MOUSE_JSB
- if (n == KS_JSBTERM_MOUSE)
- has_mouse_termcode |= HMT_JSBTERM;
- else
-# endif
if (n == KS_NETTERM_MOUSE)
has_mouse_termcode |= HMT_NETTERM;
else if (n == KS_DEC_MOUSE)
@@ -1681,11 +1675,6 @@ del_mouse_termcode (
name[0] = n;
name[1] = KE_FILLER;
del_termcode(name);
-# ifdef FEAT_MOUSE_JSB
- if (n == KS_JSBTERM_MOUSE)
- has_mouse_termcode &= ~HMT_JSBTERM;
- else
-# endif
if (n == KS_NETTERM_MOUSE)
has_mouse_termcode &= ~HMT_NETTERM;
else if (n == KS_DEC_MOUSE)
@@ -3179,14 +3168,6 @@ int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen)
static int held_button = MOUSE_RELEASE;
static int orig_num_clicks = 1;
static int orig_mouse_code = 0x0;
-# ifdef CHECK_DOUBLE_CLICK
- static int orig_mouse_col = 0;
- static int orig_mouse_row = 0;
- static struct timeval orig_mouse_time = {0, 0};
- /* time of previous mouse click */
- struct timeval mouse_time; /* time of current mouse click */
- long timediff; /* elapsed time in msec */
-# endif
int cpo_koffset;
cpo_koffset = (vim_strchr(p_cpo, CPO_KOFFSET) != NULL);
@@ -3506,9 +3487,6 @@ int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen)
* If it is a mouse click, get the coordinates.
*/
if (key_name[0] == KS_MOUSE
-# ifdef FEAT_MOUSE_JSB
- || key_name[0] == KS_JSBTERM_MOUSE
-# endif
|| key_name[0] == KS_NETTERM_MOUSE
|| key_name[0] == KS_DEC_MOUSE
|| key_name[0] == KS_URXVT_MOUSE
@@ -3690,122 +3668,6 @@ int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen)
mouse_code = MOUSE_LEFT;
slen += (int)(p - (tp + slen));
}
-# ifdef FEAT_MOUSE_JSB
- if (key_name[0] == (int)KS_JSBTERM_MOUSE) {
- int mult, val, iter, button, status;
-
- /* JSBTERM Input Model
- * \033[0~zw uniq escape sequence
- * (L-x) Left button pressed - not pressed x not reporting
- * (M-x) Middle button pressed - not pressed x not reporting
- * (R-x) Right button pressed - not pressed x not reporting
- * (SDmdu) Single , Double click, m mouse move d button down
- * u button up
- * ### X cursor position padded to 3 digits
- * ### Y cursor position padded to 3 digits
- * (s-x) SHIFT key pressed - not pressed x not reporting
- * (c-x) CTRL key pressed - not pressed x not reporting
- * \033\\ terminating sequence
- */
-
- p = tp + slen;
- button = mouse_code = 0;
- switch (*p++) {
- case 'L': button = 1; break;
- case '-': break;
- case 'x': break; /* ignore sequence */
- default: return -1; /* Unknown Result */
- }
- switch (*p++) {
- case 'M': button |= 2; break;
- case '-': break;
- case 'x': break; /* ignore sequence */
- default: return -1; /* Unknown Result */
- }
- switch (*p++) {
- case 'R': button |= 4; break;
- case '-': break;
- case 'x': break; /* ignore sequence */
- default: return -1; /* Unknown Result */
- }
- status = *p++;
- for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
- mult /= 10, p++)
- if (*p >= '0' && *p <= '9')
- val += (*p - '0') * mult;
- else
- return -1;
- mouse_col = val;
- for (val = 0, mult = 100, iter = 0; iter < 3; iter++,
- mult /= 10, p++)
- if (*p >= '0' && *p <= '9')
- val += (*p - '0') * mult;
- else
- return -1;
- mouse_row = val;
- switch (*p++) {
- case 's': button |= 8; break; /* SHIFT key Pressed */
- case '-': break; /* Not Pressed */
- case 'x': break; /* Not Reporting */
- default: return -1; /* Unknown Result */
- }
- switch (*p++) {
- case 'c': button |= 16; break; /* CTRL key Pressed */
- case '-': break; /* Not Pressed */
- case 'x': break; /* Not Reporting */
- default: return -1; /* Unknown Result */
- }
- if (*p++ != '\033')
- return -1;
- if (*p++ != '\\')
- return -1;
- switch (status) {
- case 'D': /* Double Click */
- case 'S': /* Single Click */
- if (button & 1) mouse_code |= MOUSE_LEFT;
- if (button & 2) mouse_code |= MOUSE_MIDDLE;
- if (button & 4) mouse_code |= MOUSE_RIGHT;
- if (button & 8) mouse_code |= MOUSE_SHIFT;
- if (button & 16) mouse_code |= MOUSE_CTRL;
- break;
- case 'm': /* Mouse move */
- if (button & 1) mouse_code |= MOUSE_LEFT;
- if (button & 2) mouse_code |= MOUSE_MIDDLE;
- if (button & 4) mouse_code |= MOUSE_RIGHT;
- if (button & 8) mouse_code |= MOUSE_SHIFT;
- if (button & 16) mouse_code |= MOUSE_CTRL;
- if ((button & 7) != 0) {
- held_button = mouse_code;
- mouse_code |= MOUSE_DRAG;
- }
- is_drag = TRUE;
- showmode();
- break;
- case 'd': /* Button Down */
- if (button & 1) mouse_code |= MOUSE_LEFT;
- if (button & 2) mouse_code |= MOUSE_MIDDLE;
- if (button & 4) mouse_code |= MOUSE_RIGHT;
- if (button & 8) mouse_code |= MOUSE_SHIFT;
- if (button & 16) mouse_code |= MOUSE_CTRL;
- break;
- case 'u': /* Button Up */
- if (button & 1)
- mouse_code |= MOUSE_LEFT | MOUSE_RELEASE;
- if (button & 2)
- mouse_code |= MOUSE_MIDDLE | MOUSE_RELEASE;
- if (button & 4)
- mouse_code |= MOUSE_RIGHT | MOUSE_RELEASE;
- if (button & 8)
- mouse_code |= MOUSE_SHIFT;
- if (button & 16)
- mouse_code |= MOUSE_CTRL;
- break;
- default: return -1; /* Unknown Result */
- }
-
- slen += (p - (tp + slen));
- }
-# endif /* FEAT_MOUSE_JSB */
if (key_name[0] == (int)KS_DEC_MOUSE) {
/* The DEC Locator Input Model
* Netterm delivers the code sequence:
@@ -3955,17 +3817,17 @@ int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen)
} else if (wheel_code == 0) {
# ifdef CHECK_DOUBLE_CLICK
{
- /*
- * Compute the time elapsed since the previous mouse click.
- */
- gettimeofday(&mouse_time, NULL);
- timediff = (mouse_time.tv_usec
- - orig_mouse_time.tv_usec) / 1000;
- if (timediff < 0)
- --orig_mouse_time.tv_sec;
- timediff += (mouse_time.tv_sec
- - orig_mouse_time.tv_sec) * 1000;
+ static int orig_mouse_col = 0;
+ static int orig_mouse_row = 0;
+
+ static uint64_t orig_mouse_time = 0; // time of previous mouse click
+ uint64_t mouse_time = os_hrtime(); // time of current mouse click
+
+ // compute the time elapsed since the previous mouse click and
+ // convert it from ns to ms because p_mouset is stored as ms
+ long timediff = (long) (mouse_time - orig_mouse_time) / 1E6;
orig_mouse_time = mouse_time;
+
if (mouse_code == orig_mouse_code
&& timediff < p_mouset
&& orig_num_clicks != 4
diff --git a/src/nvim/term.h b/src/nvim/term.h
index 52c3efcac5..17154b8c26 100644
--- a/src/nvim/term.h
+++ b/src/nvim/term.h
@@ -52,7 +52,7 @@
* 128 = 16384 columns, now it's reduced to 10000. */
#define MOUSE_COLOFF 10000
-#if defined(UNIX) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H)
+#if defined(UNIX)
# define CHECK_DOUBLE_CLICK 1 /* Checking for double clicks ourselves. */
#endif
diff --git a/src/nvim/version.c b/src/nvim/version.c
index c10fc970ab..fc1966f959 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -116,11 +116,6 @@ static char *(features[]) = {
#if defined(UNIX)
"+mouse_dec",
"-mouse_gpm",
-# ifdef FEAT_MOUSE_JSB
- "+mouse_jsbterm",
-# else // ifdef FEAT_MOUSE_JSB
- "-mouse_jsbterm",
-# endif // ifdef FEAT_MOUSE_JSB
"+mouse_netterm",
#endif // if defined(UNIX)