Merge PR #1300 'Refactor input buffer'

This commit is contained in:
Thiago de Arruda 2014-10-18 13:57:58 -03:00
commit fb34028c1e
13 changed files with 284 additions and 413 deletions

View File

@ -21,6 +21,8 @@
#include "nvim/eval_defs.h"
// for proftime_T
#include "nvim/profile.h"
// for String
#include "nvim/api/private/defs.h"
/*
* Flags for w_valid.
@ -311,9 +313,7 @@ typedef struct {
int old_mod_mask;
buffheader_T save_readbuf1;
buffheader_T save_readbuf2;
#ifdef USE_INPUT_BUF
char_u *save_inputbuf;
#endif
String save_inputbuf;
} tasave_T;
/*

View File

@ -19531,7 +19531,7 @@ static void on_job_exit(Job *job, void *data)
static void on_job_data(RStream *rstream, void *data, bool eof, char *type)
{
Job *job = data;
uint32_t read_count = rstream_available(rstream);
uint32_t read_count = rstream_pending(rstream);
char *str = xmalloc(read_count + 1);
rstream_read(rstream, str, read_count);

View File

@ -50,6 +50,7 @@
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/os/event.h"
#include "nvim/os/input.h"
/*
* These buffers are used for storing:
@ -1201,9 +1202,7 @@ void save_typeahead(tasave_T *tp)
readbuf1.bh_first.b_next = NULL;
tp->save_readbuf2 = readbuf2;
readbuf2.bh_first.b_next = NULL;
# ifdef USE_INPUT_BUF
tp->save_inputbuf = get_input_buf();
# endif
tp->save_inputbuf = input_buffer_save();
}
/*
@ -1224,9 +1223,7 @@ void restore_typeahead(tasave_T *tp)
readbuf1 = tp->save_readbuf1;
free_buff(&readbuf2);
readbuf2 = tp->save_readbuf2;
# ifdef USE_INPUT_BUF
set_input_buf(tp->save_inputbuf);
# endif
input_buffer_restore(tp->save_inputbuf);
}
/*
@ -2551,21 +2548,6 @@ fix_input_buffer (
return len;
}
#if defined(USE_INPUT_BUF) || defined(PROTO)
/*
* Return TRUE when bytes are in the input buffer or in the typeahead buffer.
* Normally the input buffer would be sufficient, but feedkeys() may insert
* characters in the typeahead buffer while we are waiting for input to arrive.
*/
int input_available(void)
{
return !vim_is_input_buf_empty()
|| typebuf_was_filled
;
}
#endif
/*
* map[!] : show all key mappings
* map[!] {lhs} : show key mapping for {lhs}

View File

@ -3803,50 +3803,6 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8,
return OK;
}
#if defined(FEAT_GUI) || defined(WIN3264) || defined(PROTO)
/*
* Do conversion on typed input characters in-place.
* The input and output are not NUL terminated!
* Returns the length after conversion.
*/
int convert_input(char_u *ptr, int len, int maxlen)
{
return convert_input_safe(ptr, len, maxlen, NULL, NULL);
}
#endif
/*
* Like convert_input(), but when there is an incomplete byte sequence at the
* end return that as an allocated string in "restp" and set "*restlenp" to
* the length. If "restp" is NULL it is not used.
*/
int convert_input_safe(char_u *ptr, int len, int maxlen, char_u **restp,
int *restlenp)
{
char_u *d;
int dlen = len;
int unconvertlen = 0;
d = string_convert_ext(&input_conv, ptr, &dlen,
restp == NULL ? NULL : &unconvertlen);
if (d != NULL) {
if (dlen <= maxlen) {
if (unconvertlen > 0) {
/* Move the unconverted characters to allocated memory. */
*restp = xmalloc(unconvertlen);
memmove(*restp, ptr + len - unconvertlen, unconvertlen);
*restlenp = unconvertlen;
}
memmove(ptr, d, dlen);
} else
/* result is too long, keep the unconverted text (the caller must
* have done something wrong!) */
dlen = len;
free(d);
}
return dlen;
}
/*
* Convert text "ptr[*lenp]" according to "vcp".
* Returns the result in allocated memory and sets "*lenp".

View File

@ -127,7 +127,7 @@ void channel_from_stream(uv_stream_t *stream)
channel->is_job = false;
// read stream
channel->data.streams.read = rstream_new(parse_msgpack,
CHANNEL_BUFFER_SIZE,
rbuffer_new(CHANNEL_BUFFER_SIZE),
channel,
NULL);
rstream_set_stream(channel->data.streams.read, stream);
@ -290,7 +290,7 @@ static void channel_from_stdio(void)
channel->is_job = false;
// read stream
channel->data.streams.read = rstream_new(parse_msgpack,
CHANNEL_BUFFER_SIZE,
rbuffer_new(CHANNEL_BUFFER_SIZE),
channel,
NULL);
rstream_set_file(channel->data.streams.read, 0);
@ -313,7 +313,7 @@ static void job_err(RStream *rstream, void *data, bool eof)
char buf[256];
Channel *channel = job_data(data);
while ((count = rstream_available(rstream))) {
while ((count = rstream_pending(rstream))) {
size_t read = rstream_read(rstream, buf, sizeof(buf) - 1);
buf[read] = NUL;
ELOG("Channel %" PRIu64 " stderr: %s", channel->id, buf);
@ -336,7 +336,7 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof)
goto end;
}
uint32_t count = rstream_available(rstream);
uint32_t count = rstream_pending(rstream);
DLOG("Feeding the msgpack parser with %u bytes of data from RStream(%p)",
count,
rstream);

View File

@ -4,6 +4,7 @@
#include <uv.h>
#include "nvim/api/private/defs.h"
#include "nvim/os/input.h"
#include "nvim/os/event.h"
#include "nvim/os/signal.h"
@ -12,11 +13,15 @@
#include "nvim/ascii.h"
#include "nvim/vim.h"
#include "nvim/ui.h"
#include "nvim/memory.h"
#include "nvim/keymap.h"
#include "nvim/mbyte.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/term.h"
#define READ_BUFFER_SIZE 256
#define READ_BUFFER_SIZE 0xffff
#define INPUT_BUFFER_SIZE 4096
typedef enum {
kInputNone,
@ -25,6 +30,7 @@ typedef enum {
} InbufPollResult;
static RStream *read_stream;
static RBuffer *read_buffer, *input_buffer;
static bool eof = false, started_reading = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@ -35,11 +41,17 @@ static bool eof = false, started_reading = false;
void input_init(void)
{
input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN);
if (embedded_mode) {
return;
}
read_stream = rstream_new(read_cb, READ_BUFFER_SIZE, NULL, NULL);
read_buffer = rbuffer_new(READ_BUFFER_SIZE);
read_stream = rstream_new(read_cb,
read_buffer,
NULL,
NULL);
rstream_set_file(read_stream, read_cmd_fd);
}
@ -63,17 +75,6 @@ void input_stop(void)
rstream_stop(read_stream);
}
// Copies (at most `count`) of was read from `read_cmd_fd` into `buf`
uint32_t input_read(char *buf, uint32_t count)
{
if (embedded_mode) {
return 0;
}
return rstream_read(read_stream, buf, count);
}
// Low level input function.
int os_inchar(uint8_t *buf, int maxlen, int32_t ms, int tb_change_cnt)
{
@ -118,7 +119,8 @@ int os_inchar(uint8_t *buf, int maxlen, int32_t ms, int tb_change_cnt)
return 0;
}
return read_from_input_buf(buf, (int64_t)maxlen);
convert_input();
return rbuffer_read(input_buffer, (char *)buf, maxlen);
}
// Check if a character is available for reading
@ -132,7 +134,7 @@ bool os_char_avail(void)
void os_breakcheck(void)
{
if (curr_tmode == TMODE_RAW && input_poll(0))
fill_input_buf(false);
convert_input();
}
/// Test whether a file descriptor refers to a terminal.
@ -144,6 +146,27 @@ bool os_isatty(int fd)
return uv_guess_handle(fd) == UV_TTY;
}
/// Return the contents of the input buffer and make it empty. The returned
/// pointer must be passed to `input_buffer_restore()` later.
String input_buffer_save(void)
{
size_t inbuf_size = rbuffer_pending(input_buffer);
String rv = {
.data = xmemdup(rbuffer_data(input_buffer), inbuf_size),
.size = inbuf_size
};
rbuffer_consumed(input_buffer, inbuf_size);
return rv;
}
/// Restore the contents of the input buffer and free `str`
void input_buffer_restore(String str)
{
rbuffer_consumed(input_buffer, rbuffer_pending(input_buffer));
rbuffer_write(input_buffer, str.data, str.size);
free(str.data);
}
static bool input_poll(int32_t ms)
{
if (embedded_mode) {
@ -162,12 +185,12 @@ static bool input_poll(int32_t ms)
// This is a replacement for the old `WaitForChar` function in os_unix.c
static InbufPollResult inbuf_poll(int32_t ms)
{
if (input_available()) {
if (typebuf_was_filled || rbuffer_pending(input_buffer)) {
return kInputAvail;
}
if (input_poll(ms)) {
return eof && rstream_available(read_stream) == 0 ?
return eof && rstream_pending(read_stream) == 0 ?
kInputEof :
kInputAvail;
}
@ -210,6 +233,60 @@ static void read_cb(RStream *rstream, void *data, bool at_eof)
started_reading = true;
}
static void convert_input(void)
{
if (!rbuffer_available(input_buffer)) {
// No input buffer space
return;
}
bool convert = input_conv.vc_type != CONV_NONE;
// Set unconverted data/length
char *data = rbuffer_data(read_buffer);
size_t data_length = rbuffer_pending(read_buffer);
size_t converted_length = data_length;
if (convert) {
// Perform input conversion according to `input_conv`
size_t unconverted_length;
data = (char *)string_convert_ext(&input_conv,
(uint8_t *)data,
(int *)&converted_length,
(int *)&unconverted_length);
data_length = rbuffer_pending(read_buffer) - unconverted_length;
}
// Write processed data to input buffer
size_t consumed = rbuffer_write(input_buffer, data, data_length);
// Adjust raw buffer pointers
rbuffer_consumed(read_buffer, consumed);
if (convert) {
// data points to memory allocated by `string_convert_ext`, free it.
free(data);
}
if (!ctrl_c_interrupts) {
return;
}
char *inbuf = rbuffer_data(input_buffer);
size_t count = rbuffer_pending(input_buffer), consume_count = 0;
for (int i = count - 1; i >= 0; i--) {
if (inbuf[i] == 3) {
consume_count = i + 1;
break;
}
}
if (consume_count) {
// Remove everything typed before the CTRL-C
rbuffer_consumed(input_buffer, consume_count);
got_int = true;
}
}
static int push_event_key(uint8_t *buf, int maxlen)
{
static const uint8_t key[3] = { K_SPECIAL, KS_EXTRA, KE_EVENT };
@ -225,8 +302,8 @@ static int push_event_key(uint8_t *buf, int maxlen)
}
// Check if there's pending input
bool input_ready(void)
static bool input_ready(void)
{
return rstream_available(read_stream) > 0 || eof;
return rstream_pending(read_stream) > 0 || eof;
}

View File

@ -4,6 +4,8 @@
#include <stdint.h>
#include <stdbool.h>
#include "nvim/api/private/defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/input.h.generated.h"
#endif

View File

@ -213,8 +213,14 @@ Job *job_start(char **argv,
job->in = wstream_new(maxmem);
wstream_set_stream(job->in, (uv_stream_t *)&job->proc_stdin);
// Start the readable streams
job->out = rstream_new(read_cb, JOB_BUFFER_SIZE, job, job_event_source(job));
job->err = rstream_new(read_cb, JOB_BUFFER_SIZE, job, job_event_source(job));
job->out = rstream_new(read_cb,
rbuffer_new(JOB_BUFFER_SIZE),
job,
job_event_source(job));
job->err = rstream_new(read_cb,
rbuffer_new(JOB_BUFFER_SIZE),
job,
job_event_source(job));
rstream_set_stream(job->out, (uv_stream_t *)&job->proc_stdout);
rstream_set_stream(job->err, (uv_stream_t *)&job->proc_stderr);
rstream_start(job->out);

View File

@ -16,16 +16,22 @@
#include "nvim/log.h"
#include "nvim/misc1.h"
struct rbuffer {
char *data;
size_t capacity, rpos, wpos;
RStream *rstream;
};
struct rstream {
uv_buf_t uvbuf;
void *data;
char *buffer;
uv_buf_t uvbuf;
size_t fpos;
RBuffer *buffer;
uv_stream_t *stream;
uv_idle_t *fread_idle;
uv_handle_type file_type;
uv_file fd;
rstream_cb cb;
size_t buffer_size, rpos, wpos, fpos;
bool free_handle;
EventSource source_override;
};
@ -34,27 +40,151 @@ struct rstream {
# include "os/rstream.c.generated.h"
#endif
/// Creates a new `RBuffer` instance.
RBuffer *rbuffer_new(size_t capacity)
{
RBuffer *rv = xmalloc(sizeof(RBuffer));
rv->data = xmalloc(capacity);
rv->capacity = capacity;
rv->rpos = rv->wpos = 0;
return rv;
}
/// Advances `rbuffer` read pointers to consume data. If the associated
/// RStream had stopped because the buffer was full, this will restart it.
///
/// This is called automatically by rbuffer_read, but when using `rbuffer_data`
/// directly, this needs to called after the data was consumed.
void rbuffer_consumed(RBuffer *rbuffer, size_t count)
{
rbuffer->rpos += count;
if (count && rbuffer->wpos == rbuffer->capacity) {
// `wpos` is at the end of the buffer, so free some space by moving unread
// data...
rbuffer_relocate(rbuffer);
if (rbuffer->rstream) {
// restart the associated RStream
rstream_start(rbuffer->rstream);
}
}
}
/// Advances `rbuffer` write pointers. If the internal buffer becomes full,
/// this will stop the associated RStream instance.
void rbuffer_produced(RBuffer *rbuffer, size_t count)
{
rbuffer->wpos += count;
DLOG("Received %u bytes from RStream(address: %p, source: %p)",
(size_t)cnt,
rbuffer->rstream,
rstream_event_source(rbuffer->rstream));
rbuffer_relocate(rbuffer);
if (rbuffer->rstream && rbuffer->wpos == rbuffer->capacity) {
// The last read filled the buffer, stop reading for now
rstream_stop(rbuffer->rstream);
DLOG("Buffer for RStream(address: %p, source: %p) is full, stopping it",
rstream,
rstream_event_source(rstream));
}
}
/// Reads data from a `RBuffer` instance into a raw buffer.
///
/// @param rbuffer The `RBuffer` instance
/// @param buffer The buffer which will receive the data
/// @param count Number of bytes that `buffer` can accept
/// @return The number of bytes copied into `buffer`
size_t rbuffer_read(RBuffer *rbuffer, char *buffer, size_t count)
{
size_t read_count = rbuffer_pending(rbuffer);
if (count < read_count) {
read_count = count;
}
if (read_count > 0) {
memcpy(buffer, rbuffer_data(rbuffer), read_count);
rbuffer_consumed(rbuffer, read_count);
}
return read_count;
}
/// Copies data to `rbuffer` read queue.
///
/// @param rbuffer the `RBuffer` instance
/// @param buffer The buffer containing data to be copied
/// @param count Number of bytes that should be copied
/// @return The number of bytes actually copied
size_t rbuffer_write(RBuffer *rbuffer, char *buffer, size_t count)
{
size_t write_count = rbuffer_available(rbuffer);
if (count < write_count) {
write_count = count;
}
if (write_count > 0) {
memcpy(rbuffer_data(rbuffer), buffer, write_count);
rbuffer_produced(rbuffer, write_count);
}
return write_count;
}
/// Returns a pointer to a raw buffer containing the first byte available for
/// reading.
char *rbuffer_data(RBuffer *rbuffer)
{
return rbuffer->data + rbuffer->rpos;
}
/// Returns the number of bytes ready for consumption in `rbuffer`
///
/// @param rbuffer The `RBuffer` instance
/// @return The number of bytes ready for consumption
size_t rbuffer_pending(RBuffer *rbuffer)
{
return rbuffer->wpos - rbuffer->rpos;
}
/// Returns available space in `rbuffer`
///
/// @param rbuffer The `RBuffer` instance
/// @return The space available in number of bytes
size_t rbuffer_available(RBuffer *rbuffer)
{
return rbuffer->capacity - rbuffer->wpos;
}
void rbuffer_free(RBuffer *rbuffer)
{
free(rbuffer->data);
free(rbuffer);
}
/// Creates a new RStream instance. A RStream encapsulates all the boilerplate
/// necessary for reading from a libuv stream.
///
/// @param cb A function that will be called whenever some data is available
/// for reading with `rstream_read`
/// @param buffer_size Size in bytes of the internal buffer.
/// @param buffer RBuffer instance to associate with the RStream
/// @param data Some state to associate with the `RStream` instance
/// @param source_override Replacement for the default source used in events
/// emitted by this RStream. If NULL, the default is used.
/// @return The newly-allocated `RStream` instance
RStream * rstream_new(rstream_cb cb,
size_t buffer_size,
RBuffer *buffer,
void *data,
EventSource source_override)
{
RStream *rv = xmalloc(sizeof(RStream));
rv->buffer = xmalloc(buffer_size);
rv->buffer_size = buffer_size;
rv->buffer = buffer;
rv->buffer->rstream = rv;
rv->fpos = 0;
rv->data = data;
rv->cb = cb;
rv->rpos = rv->wpos = rv->fpos = 0;
rv->stream = NULL;
rv->fread_idle = NULL;
rv->free_handle = false;
@ -77,7 +207,7 @@ void rstream_free(RStream *rstream)
}
}
free(rstream->buffer);
rbuffer_free(rstream->buffer);
free(rstream);
}
@ -166,51 +296,21 @@ void rstream_stop(RStream *rstream)
}
}
/// Returns the number of bytes ready for consumption in `rstream`
size_t rstream_pending(RStream *rstream)
{
return rbuffer_pending(rstream->buffer);
}
/// Reads data from a `RStream` instance into a buffer.
///
/// @param rstream The `RStream` instance
/// @param buffer The buffer which will receive the data
/// @param count Number of bytes that `buffer` can accept
/// @return The number of bytes copied into `buffer`
size_t rstream_read(RStream *rstream, char *buf, size_t count)
size_t rstream_read(RStream *rstream, char *buffer, size_t count)
{
size_t read_count = rstream->wpos - rstream->rpos;
if (count < read_count) {
read_count = count;
}
if (read_count > 0) {
memcpy(buf, rstream->buffer + rstream->rpos, read_count);
rstream->rpos += read_count;
}
if (rstream->wpos == rstream->buffer_size) {
// `wpos` is at the end of the buffer, so free some space by moving unread
// data...
memmove(
rstream->buffer, // ...To the beginning of the buffer(rpos 0)
rstream->buffer + rstream->rpos, // ...From the first unread position
rstream->wpos - rstream->rpos); // ...By the number of unread bytes
rstream->wpos -= rstream->rpos;
rstream->rpos = 0;
if (rstream->wpos < rstream->buffer_size) {
// Restart reading since we have freed some space
rstream_start(rstream);
}
}
return read_count;
}
/// Returns the number of bytes available for reading from `rstream`
///
/// @param rstream The `RStream` instance
/// @return The number of bytes available
size_t rstream_available(RStream *rstream)
{
return rstream->wpos - rstream->rpos;
return rbuffer_read(rstream->buffer, buffer, count);
}
/// Runs the read callback associated with the rstream
@ -235,8 +335,8 @@ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
RStream *rstream = handle_get_rstream(handle);
buf->len = rstream->buffer_size - rstream->wpos;
buf->base = rstream->buffer + rstream->wpos;
buf->len = rbuffer_available(rstream->buffer);
buf->base = rbuffer_data(rstream->buffer);
}
// Callback invoked by libuv after it copies the data into the buffer provided
@ -264,20 +364,7 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
// Data was already written, so all we need is to update 'wpos' to reflect
// the space actually used in the buffer.
rstream->wpos += nread;
DLOG("Received %u bytes from RStream(address: %p, source: %p)",
(size_t)cnt,
rstream,
rstream_event_source(rstream));
if (rstream->wpos == rstream->buffer_size) {
// The last read filled the buffer, stop reading for now
rstream_stop(rstream);
DLOG("Buffer for RStream(address: %p, source: %p) is full, stopping it",
rstream,
rstream_event_source(rstream));
}
rbuffer_produced(rstream->buffer, nread);
emit_read_event(rstream, false);
}
@ -287,8 +374,8 @@ static void fread_idle_cb(uv_idle_t *handle)
uv_fs_t req;
RStream *rstream = handle_get_rstream((uv_handle_t *)handle);
rstream->uvbuf.base = rstream->buffer + rstream->wpos;
rstream->uvbuf.len = rstream->buffer_size - rstream->wpos;
rstream->uvbuf.len = rstream->buffer->capacity - rstream->buffer->wpos;
rstream->uvbuf.base = rstream->buffer->data + rstream->buffer->wpos;
// the offset argument to uv_fs_read is int64_t, could someone really try
// to read more than 9 quintillion (9e18) bytes?
@ -319,15 +406,8 @@ static void fread_idle_cb(uv_idle_t *handle)
// no errors (req.result (ssize_t) is positive), it's safe to cast.
size_t nread = (size_t) req.result;
rstream->wpos += nread;
rbuffer_produced(rstream->buffer, nread);
rstream->fpos += nread;
if (rstream->wpos == rstream->buffer_size) {
// The last read filled the buffer, stop reading for now
rstream_stop(rstream);
}
emit_read_event(rstream, false);
}
@ -349,3 +429,14 @@ static void emit_read_event(RStream *rstream, bool eof)
};
event_push(event);
}
static void rbuffer_relocate(RBuffer *rbuffer)
{
// Move data ...
memmove(
rbuffer->data, // ...to the beginning of the buffer(rpos 0)
rbuffer->data + rbuffer->rpos, // ...From the first unread position
rbuffer->wpos - rbuffer->rpos); // ...By the number of unread bytes
rbuffer->wpos -= rbuffer->rpos;
rbuffer->rpos = 0;
}

View File

@ -3,6 +3,7 @@
#include <stdbool.h>
typedef struct rbuffer RBuffer;
typedef struct rstream RStream;
/// Type of function called when the RStream receives data

View File

@ -341,7 +341,7 @@ 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);
size_t nread = rstream_pending(rstream);
dyn_buf_ensure(buf, buf->len + nread + 1);
rstream_read(rstream, buf->data + buf->len, nread);

View File

@ -228,247 +228,6 @@ void ui_breakcheck(void)
* for them.
*/
/*****************************************************************************
* Functions that handle the input buffer.
* This is used for any GUI version, and the unix terminal version.
*
* For Unix, the input characters are buffered to be able to check for a
* CTRL-C. This should be done with signals, but I don't know how to do that
* in a portable way for a tty in RAW mode.
*
* For the client-server code in the console the received keys are put in the
* input buffer.
*/
#if defined(USE_INPUT_BUF) || defined(PROTO)
/*
* Internal typeahead buffer. Includes extra space for long key code
* descriptions which would otherwise overflow. The buffer is considered full
* when only this extra space (or part of it) remains.
*/
# define INBUFLEN 4096
static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN];
static int inbufcount = 0; /* number of chars in inbuf[] */
/*
* vim_is_input_buf_full(), vim_is_input_buf_empty(), add_to_input_buf(), and
* trash_input_buf() are functions for manipulating the input buffer. These
* are used by the gui_* calls when a GUI is used to handle keyboard input.
*/
int vim_is_input_buf_full(void)
{
return inbufcount >= INBUFLEN;
}
int vim_is_input_buf_empty(void)
{
return inbufcount == 0;
}
/*
* Return the current contents of the input buffer and make it empty.
* The returned pointer must be passed to set_input_buf() later.
*/
char_u *get_input_buf(void)
{
/* We use a growarray to store the data pointer and the length. */
garray_T *gap = xmalloc(sizeof(garray_T));
/* Add one to avoid a zero size. */
gap->ga_data = xmalloc(inbufcount + 1);
if (gap->ga_data != NULL)
memmove(gap->ga_data, inbuf, (size_t)inbufcount);
gap->ga_len = inbufcount;
trash_input_buf();
return (char_u *)gap;
}
/*
* Restore the input buffer with a pointer returned from get_input_buf().
* The allocated memory is freed, this only works once!
*/
void set_input_buf(char_u *p)
{
garray_T *gap = (garray_T *)p;
if (gap != NULL) {
if (gap->ga_data != NULL) {
memmove(inbuf, gap->ga_data, gap->ga_len);
inbufcount = gap->ga_len;
free(gap->ga_data);
}
free(gap);
}
}
#if defined(FEAT_GUI) \
|| defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE) \
|| defined(FEAT_XCLIPBOARD) || defined(PROTO)
/*
* Add the given bytes to the input buffer
* Special keys start with CSI. A real CSI must have been translated to
* CSI KS_EXTRA KE_CSI. K_SPECIAL doesn't require translation.
*/
void add_to_input_buf(char_u *s, int len)
{
if (inbufcount + len > INBUFLEN + MAX_KEY_CODE_LEN)
return; /* Shouldn't ever happen! */
if ((State & (INSERT|CMDLINE)) && hangul_input_state_get())
if ((len = hangul_input_process(s, len)) == 0)
return;
while (len--)
inbuf[inbufcount++] = *s++;
}
#endif
#if ((defined(FEAT_XIM) || defined(FEAT_DND)) && defined(FEAT_GUI_GTK)) \
|| defined(FEAT_GUI_MSWIN) \
|| defined(FEAT_GUI_MAC) \
|| defined(FEAT_MBYTE_IME) \
|| defined(FEAT_GUI) \
|| defined(PROTO)
/*
* Add "str[len]" to the input buffer while escaping CSI bytes.
*/
void add_to_input_buf_csi(char_u *str, int len) {
int i;
char_u buf[2];
for (i = 0; i < len; ++i) {
add_to_input_buf(str + i, 1);
if (str[i] == CSI) {
/* Turn CSI into K_CSI. */
buf[0] = KS_EXTRA;
buf[1] = (int)KE_CSI;
add_to_input_buf(buf, 2);
}
}
}
#endif
/* Remove everything from the input buffer. Called when ^C is found */
void trash_input_buf(void)
{
inbufcount = 0;
}
/*
* Read as much data from the input buffer as possible up to maxlen, and store
* it in buf.
* Note: this function used to be Read() in unix.c
*/
int read_from_input_buf(char_u *buf, long maxlen)
{
if (inbufcount == 0) /* if the buffer is empty, fill it */
fill_input_buf(true);
if (maxlen > inbufcount)
maxlen = inbufcount;
memmove(buf, inbuf, (size_t)maxlen);
inbufcount -= maxlen;
if (inbufcount)
memmove(inbuf, inbuf + maxlen, (size_t)inbufcount);
return (int)maxlen;
}
void fill_input_buf(bool exit_on_error)
{
#if defined(UNIX) || defined(MACOS_X_UNIX)
int len;
int try;
static char_u *rest = NULL; /* unconverted rest of previous read */
static int restlen = 0;
int unconverted;
#endif
#if defined(UNIX) || defined(MACOS_X_UNIX)
if (vim_is_input_buf_full())
return;
/*
* Fill_input_buf() is only called when we really need a character.
* If we can't get any, but there is some in the buffer, just return.
* If we can't get any, and there isn't any in the buffer, we give up and
* exit Vim.
*/
if (rest != NULL) {
/* Use remainder of previous call, starts with an invalid character
* that may become valid when reading more. */
if (restlen > INBUFLEN - inbufcount)
unconverted = INBUFLEN - inbufcount;
else
unconverted = restlen;
memmove(inbuf + inbufcount, rest, unconverted);
if (unconverted == restlen) {
free(rest);
rest = NULL;
} else {
restlen -= unconverted;
memmove(rest, rest + unconverted, restlen);
}
inbufcount += unconverted;
} else
unconverted = 0;
len = 0; /* to avoid gcc warning */
for (try = 0; try < 100; ++try) {
len = input_read(
(char *)inbuf + inbufcount,
(size_t)((INBUFLEN - inbufcount) / input_conv.vc_factor));
if (len > 0 || got_int)
break;
if (!exit_on_error)
return;
}
if (len <= 0 && !got_int)
read_error_exit();
if (got_int) {
/* Interrupted, pretend a CTRL-C was typed. */
inbuf[0] = 3;
inbufcount = 1;
} else {
/*
* May perform conversion on the input characters.
* Include the unconverted rest of the previous call.
* If there is an incomplete char at the end it is kept for the next
* time, reading more bytes should make conversion possible.
* Don't do this in the unlikely event that the input buffer is too
* small ("rest" still contains more bytes).
*/
if (input_conv.vc_type != CONV_NONE) {
inbufcount -= unconverted;
len = convert_input_safe(inbuf + inbufcount,
len + unconverted, INBUFLEN - inbufcount,
rest == NULL ? &rest : NULL, &restlen);
}
while (len-- > 0) {
/*
* if a CTRL-C was typed, remove it from the buffer and set got_int
*/
if (inbuf[inbufcount] == 3 && ctrl_c_interrupts) {
/* remove everything typed before the CTRL-C */
memmove(inbuf, inbuf + inbufcount, (size_t)(len + 1));
inbufcount = 0;
got_int = TRUE;
}
++inbufcount;
}
}
#endif /* UNIX */
}
#endif /* defined(UNIX) || defined(FEAT_GUI) */
/*
* Exit because of an input read error.
*/
@ -954,3 +713,4 @@ void im_save_status(long *psave)
}
}
#endif

View File

@ -339,10 +339,6 @@ enum {
#define fnamencmp(x, y, n) vim_fnamencmp((char_u *)(x), (char_u *)(y), \
(size_t)(n))
#if defined(UNIX) || defined(FEAT_GUI)
# define USE_INPUT_BUF
#endif
#ifndef EINTR
# define read_eintr(fd, buf, count) vim_read((fd), (buf), (count))
# define write_eintr(fd, buf, count) vim_write((fd), (buf), (count))