diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 106 | ||||
-rw-r--r-- | src/nvim/func_attr.h | 4 | ||||
-rw-r--r-- | src/nvim/keymap.c | 3 | ||||
-rw-r--r-- | src/nvim/keymap.h | 4 | ||||
-rw-r--r-- | src/nvim/message.h | 1 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/os/channel.c | 1 | ||||
-rw-r--r-- | src/nvim/os/job.c | 129 | ||||
-rw-r--r-- | src/nvim/os/provider.c | 1 | ||||
-rw-r--r-- | src/nvim/os/rstream.c | 12 | ||||
-rw-r--r-- | src/nvim/os/shell.c | 147 | ||||
-rw-r--r-- | src/nvim/os/wstream.c | 36 | ||||
-rw-r--r-- | src/nvim/os/wstream_defs.h | 12 | ||||
-rw-r--r-- | src/nvim/sha256.c | 21 | ||||
-rw-r--r-- | src/nvim/term.c | 164 | ||||
-rw-r--r-- | src/nvim/term.h | 2 | ||||
-rw-r--r-- | src/nvim/version.c | 5 |
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) |