diff options
author | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-12 02:25:17 +0200 |
---|---|---|
committer | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-15 20:46:01 +0200 |
commit | da51dc9cf202772f60bd2da975dbef257bd9237c (patch) | |
tree | 5c16b93238a153f55634e9323077f30c8133970c /src/os | |
parent | ffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff) | |
download | rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.gz rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.bz2 rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.zip |
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/.
- src/nvim/ becomes the new root dir for nvim executable sources.
- src/libnvim/ is planned to become root dir of the neovim library.
Diffstat (limited to 'src/os')
35 files changed, 0 insertions, 3962 deletions
diff --git a/src/os/channel.c b/src/os/channel.c deleted file mode 100644 index 2427db2a14..0000000000 --- a/src/os/channel.c +++ /dev/null @@ -1,175 +0,0 @@ -#include <string.h> - -#include <uv.h> -#include <msgpack.h> - -#include "lib/klist.h" -#include "os/channel.h" -#include "os/channel_defs.h" -#include "os/rstream.h" -#include "os/rstream_defs.h" -#include "os/wstream.h" -#include "os/wstream_defs.h" -#include "os/job.h" -#include "os/job_defs.h" -#include "os/msgpack_rpc.h" -#include "vim.h" -#include "memory.h" - -typedef struct { - ChannelProtocol protocol; - bool is_job; - union { - struct { - msgpack_unpacker *unpacker; - msgpack_sbuffer *sbuffer; - } msgpack; - } proto; - union { - int job_id; - struct { - RStream *read; - WStream *write; - } streams; - } data; -} Channel; - -#define _destroy_channel(x) - -KLIST_INIT(Channel, Channel *, _destroy_channel) - -static klist_t(Channel) *channels = NULL; -static void on_job_stdout(RStream *rstream, void *data, bool eof); -static void on_job_stderr(RStream *rstream, void *data, bool eof); -static void parse_msgpack(RStream *rstream, void *data, bool eof); - -void channel_init() -{ - channels = kl_init(Channel); -} - -void channel_teardown() -{ - if (!channels) { - return; - } - - Channel *channel; - - while (kl_shift(Channel, channels, &channel) == 0) { - - switch (channel->protocol) { - case kChannelProtocolMsgpack: - msgpack_sbuffer_free(channel->proto.msgpack.sbuffer); - msgpack_unpacker_free(channel->proto.msgpack.unpacker); - break; - default: - abort(); - } - - if (channel->is_job) { - job_stop(channel->data.job_id); - } else { - rstream_free(channel->data.streams.read); - wstream_free(channel->data.streams.write); - } - } -} - -void channel_from_job(char **argv, ChannelProtocol prot) -{ - Channel *channel = xmalloc(sizeof(Channel)); - rstream_cb rcb = NULL; - - switch (prot) { - case kChannelProtocolMsgpack: - rcb = on_job_stdout; - channel->proto.msgpack.unpacker = - msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - channel->proto.msgpack.sbuffer = msgpack_sbuffer_new(); - break; - default: - abort(); - } - - channel->protocol = prot; - channel->is_job = true; - channel->data.job_id = job_start(argv, channel, rcb, on_job_stderr, NULL); - *kl_pushp(Channel, channels) = channel; -} - -void channel_from_stream(uv_stream_t *stream, ChannelProtocol prot) -{ - Channel *channel = xmalloc(sizeof(Channel)); - rstream_cb rcb = NULL; - - switch (prot) { - case kChannelProtocolMsgpack: - rcb = parse_msgpack; - channel->proto.msgpack.unpacker = - msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - channel->proto.msgpack.sbuffer = msgpack_sbuffer_new(); - break; - default: - abort(); - } - - stream->data = NULL; - channel->protocol = prot; - channel->is_job = false; - // read stream - channel->data.streams.read = rstream_new(rcb, 1024, channel, true); - rstream_set_stream(channel->data.streams.read, stream); - rstream_start(channel->data.streams.read); - // write stream - channel->data.streams.write = wstream_new(1024 * 1024); - wstream_set_stream(channel->data.streams.write, stream); - // push to channel list - *kl_pushp(Channel, channels) = channel; -} - -static void on_job_stdout(RStream *rstream, void *data, bool eof) -{ - Job *job = data; - parse_msgpack(rstream, job_data(job), eof); -} - -static void on_job_stderr(RStream *rstream, void *data, bool eof) -{ - // TODO(tarruda): plugin error messages should be sent to the error buffer -} - -static void parse_msgpack(RStream *rstream, void *data, bool eof) -{ - msgpack_unpacked unpacked; - Channel *channel = data; - uint32_t count = rstream_available(rstream); - - // Feed the unpacker with data - msgpack_unpacker_reserve_buffer(channel->proto.msgpack.unpacker, count); - rstream_read(rstream, - msgpack_unpacker_buffer(channel->proto.msgpack.unpacker), - count); - msgpack_unpacker_buffer_consumed(channel->proto.msgpack.unpacker, count); - - msgpack_unpacked_init(&unpacked); - - // Deserialize everything we can. - while (msgpack_unpacker_next(channel->proto.msgpack.unpacker, &unpacked)) { - // Each object is a new msgpack-rpc request and requires an empty response - msgpack_packer response; - msgpack_packer_init(&response, - channel->proto.msgpack.sbuffer, - msgpack_sbuffer_write); - // Perform the call - msgpack_rpc_call(&unpacked.data, &response); - wstream_write(channel->data.streams.write, - xmemdup(channel->proto.msgpack.sbuffer->data, - channel->proto.msgpack.sbuffer->size), - channel->proto.msgpack.sbuffer->size, - true); - - // Clear the buffer for future calls - msgpack_sbuffer_clear(channel->proto.msgpack.sbuffer); - } -} diff --git a/src/os/channel.h b/src/os/channel.h deleted file mode 100644 index c58e91a0e5..0000000000 --- a/src/os/channel.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef NEOVIM_OS_CHANNEL_H -#define NEOVIM_OS_CHANNEL_H - -#include <uv.h> - -#include "os/channel_defs.h" - -/// Initializes the module -void channel_init(void); - -/// Teardown the module -void channel_teardown(void); - -/// Creates an API channel from a libuv stream representing a tcp or -/// pipe/socket client connection -/// -/// @param stream The established connection -/// @param prot The rpc protocol used -void channel_from_stream(uv_stream_t *stream, ChannelProtocol prot); - -/// Creates an API channel by starting a job and connecting to its -/// stdin/stdout. stderr is forwarded to the editor error stream. -/// -/// @param argv The argument vector for the process -/// @param prot The rpc protocol used -void channel_from_job(char **argv, ChannelProtocol prot); - -#endif // NEOVIM_OS_CHANNEL_H - diff --git a/src/os/channel_defs.h b/src/os/channel_defs.h deleted file mode 100644 index 27ba103fbe..0000000000 --- a/src/os/channel_defs.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef NEOVIM_OS_CHANNEL_DEFS_H -#define NEOVIM_OS_CHANNEL_DEFS_H - -typedef enum { - kChannelProtocolMsgpack -} ChannelProtocol; - -#endif // NEOVIM_OS_CHANNEL_DEFS_H diff --git a/src/os/env.c b/src/os/env.c deleted file mode 100644 index e6cdb92ea8..0000000000 --- a/src/os/env.c +++ /dev/null @@ -1,80 +0,0 @@ -// env.c -- environment variable access - -#include <uv.h> - -#include "os/os.h" -#include "misc2.h" - -#ifdef HAVE__NSGETENVIRON -#include <crt_externs.h> -#endif - -#ifdef HAVE_SYS_UTSNAME_H -#include <sys/utsname.h> -#endif - -const char *os_getenv(const char *name) -{ - return getenv(name); -} - -int os_setenv(const char *name, const char *value, int overwrite) -{ - return setenv(name, value, overwrite); -} - -char *os_getenvname_at_index(size_t index) -{ -# if defined(HAVE__NSGETENVIRON) - char **environ = *_NSGetEnviron(); -# elif !defined(__WIN32__) - // Borland C++ 5.2 has this in a header file. - extern char **environ; -# endif - // check if index is inside the environ array - for (size_t i = 0; i < index; i++) { - if (environ[i] == NULL) { - return NULL; - } - } - char *str = environ[index]; - if (str == NULL) { - return NULL; - } - int namesize = 0; - while (str[namesize] != '=' && str[namesize] != NUL) { - namesize++; - } - char *name = (char *)vim_strnsave((char_u *)str, namesize); - return name; -} - - -int64_t os_get_pid() -{ -#ifdef _WIN32 - return (int64_t)GetCurrentProcessId(); -#else - return (int64_t)getpid(); -#endif -} - -void os_get_hostname(char *hostname, size_t len) -{ -#ifdef HAVE_SYS_UTSNAME_H - struct utsname vutsname; - - if (uname(&vutsname) < 0) { - *hostname = '\0'; - } else { - strncpy(hostname, vutsname.nodename, len - 1); - hostname[len - 1] = '\0'; - } -#else - // TODO(unknown): Implement this for windows. - // See the implementation used in vim: - // https://code.google.com/p/vim/source/browse/src/os_win32.c?r=6b69d8dde19e32909f4ee3a6337e6a2ecfbb6f72#2899 - *hostname = '\0'; -#endif -} - diff --git a/src/os/event.c b/src/os/event.c deleted file mode 100644 index 2c25852778..0000000000 --- a/src/os/event.c +++ /dev/null @@ -1,148 +0,0 @@ -#include <stdint.h> -#include <stdbool.h> -#include <stdlib.h> - -#include <uv.h> - -#include "lib/klist.h" -#include "os/event.h" -#include "os/input.h" -#include "os/channel.h" -#include "os/server.h" -#include "os/signal.h" -#include "os/rstream.h" -#include "os/job.h" -#include "vim.h" -#include "memory.h" -#include "misc2.h" - -// event will be cleaned up after it gets processed -#define _destroy_event(x) // do nothing -KLIST_INIT(Event, Event, _destroy_event) - -static klist_t(Event) *event_queue; -static uv_timer_t timer; -static uv_prepare_t timer_prepare; -static void timer_cb(uv_timer_t *handle); -static void timer_prepare_cb(uv_prepare_t *); - -void event_init() -{ - // Initialize the event queue - event_queue = kl_init(Event); - // Initialize input events - input_init(); - // Timer to wake the event loop if a timeout argument is passed to - // `event_poll` - // Signals - signal_init(); - // Jobs - job_init(); - // Channels - channel_init(); - // Servers - server_init(); - uv_timer_init(uv_default_loop(), &timer); - // This prepare handle that actually starts the timer - uv_prepare_init(uv_default_loop(), &timer_prepare); -} - -void event_teardown() -{ - channel_teardown(); - job_teardown(); - server_teardown(); -} - -// Wait for some event -bool event_poll(int32_t ms) -{ - bool timed_out; - uv_run_mode run_mode = UV_RUN_ONCE; - - if (input_ready()) { - // If there's a pending input event to be consumed, do it now - return true; - } - - input_start(); - timed_out = false; - - if (ms > 0) { - // Timeout passed as argument to the timer - timer.data = &timed_out; - // We only start the timer after the loop is running, for that we - // use an prepare handle(pass the interval as data to it) - timer_prepare.data = &ms; - uv_prepare_start(&timer_prepare, timer_prepare_cb); - } else if (ms == 0) { - // For ms == 0, we need to do a non-blocking event poll by - // setting the run mode to UV_RUN_NOWAIT. - run_mode = UV_RUN_NOWAIT; - } - - do { - // Run one event loop iteration, blocking for events if run_mode is - // UV_RUN_ONCE - uv_run(uv_default_loop(), run_mode); - } while ( - // Continue running if ... - !input_ready() && // we have no input - kl_empty(event_queue) && // no events are waiting to be processed - run_mode != UV_RUN_NOWAIT && // ms != 0 - !timed_out); // we didn't get a timeout - - input_stop(); - - if (ms > 0) { - // Stop the timer - uv_timer_stop(&timer); - } - - return input_ready() || event_is_pending(); -} - -bool event_is_pending() -{ - return !kl_empty(event_queue); -} - -// Push an event to the queue -void event_push(Event event) -{ - *kl_pushp(Event, event_queue) = event; -} - -// Runs the appropriate action for each queued event -void event_process() -{ - Event event; - - while (kl_shift(Event, event_queue, &event) == 0) { - switch (event.type) { - case kEventSignal: - signal_handle(event); - break; - case kEventRStreamData: - rstream_read_event(event); - break; - case kEventJobExit: - job_exit_event(event); - break; - default: - abort(); - } - } -} - -// Set a flag in the `event_poll` loop for signaling of a timeout -static void timer_cb(uv_timer_t *handle) -{ - *((bool *)handle->data) = true; -} - -static void timer_prepare_cb(uv_prepare_t *handle) -{ - uv_timer_start(&timer, timer_cb, *(uint32_t *)timer_prepare.data, 0); - uv_prepare_stop(&timer_prepare); -} diff --git a/src/os/event.h b/src/os/event.h deleted file mode 100644 index 5ddca46875..0000000000 --- a/src/os/event.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef NEOVIM_OS_EVENT_H -#define NEOVIM_OS_EVENT_H - -#include <stdint.h> -#include <stdbool.h> - -#include "os/event_defs.h" -#include "os/job_defs.h" - -void event_init(void); -void event_teardown(void); -bool event_poll(int32_t ms); -bool event_is_pending(void); -void event_push(Event event); -void event_process(void); - -#endif // NEOVIM_OS_EVENT_H - diff --git a/src/os/event_defs.h b/src/os/event_defs.h deleted file mode 100644 index 428ae50f3e..0000000000 --- a/src/os/event_defs.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef NEOVIM_OS_EVENT_DEFS_H -#define NEOVIM_OS_EVENT_DEFS_H - -#include "os/job_defs.h" -#include "os/rstream_defs.h" - -typedef enum { - kEventSignal, - kEventRStreamData, - kEventJobExit -} EventType; - -typedef struct { - EventType type; - union { - int signum; - struct { - RStream *ptr; - bool eof; - } rstream; - Job *job; - } data; -} Event; - -#endif // NEOVIM_OS_EVENT_DEFS_H diff --git a/src/os/fs.c b/src/os/fs.c deleted file mode 100644 index 89eb4c8691..0000000000 --- a/src/os/fs.c +++ /dev/null @@ -1,277 +0,0 @@ -// fs.c -- filesystem access - -#include "os/os.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "path.h" - -static bool is_executable(const char_u *name); -static bool is_executable_in_path(const char_u *name); - -// Many fs functions from libuv return that value on success. -static const int kLibuvSuccess = 0; - -int os_chdir(const char *path) { - if (p_verbose >= 5) { - verbose_enter(); - smsg((char_u *)"chdir(%s)", path); - verbose_leave(); - } - return uv_chdir(path); -} - -int os_dirname(char_u *buf, size_t len) -{ - assert(buf && len); - - int errno; - if ((errno = uv_cwd((char *)buf, &len)) != kLibuvSuccess) { - vim_strncpy(buf, (char_u *)uv_strerror(errno), len - 1); - return FAIL; - } - return OK; -} - -bool os_isdir(const char_u *name) -{ - int32_t mode = os_getperm(name); - if (mode < 0) { - return false; - } - - if (!S_ISDIR(mode)) { - return false; - } - - return true; -} - -bool os_can_exe(const char_u *name) -{ - // If it's an absolute or relative path don't need to use $PATH. - if (path_is_absolute_path(name) || - (name[0] == '.' && (name[1] == '/' || - (name[1] == '.' && name[2] == '/')))) { - return is_executable(name); - } - - return is_executable_in_path(name); -} - -// Return true if "name" is an executable file, false if not or it doesn't -// exist. -static bool is_executable(const char_u *name) -{ - int32_t mode = os_getperm(name); - - if (mode < 0) { - return false; - } - - if (S_ISREG(mode) && (S_IEXEC & mode)) { - return true; - } - - return false; -} - -/// Check if a file is inside the $PATH and is executable. -/// -/// @return `true` if `name` is an executable inside $PATH. -static bool is_executable_in_path(const char_u *name) -{ - const char *path = getenv("PATH"); - // PATH environment variable does not exist or is empty. - if (path == NULL || *path == NUL) { - return false; - } - - int buf_len = STRLEN(name) + STRLEN(path) + 2; - char_u *buf = alloc((unsigned)(buf_len)); - - // Walk through all entries in $PATH to check if "name" exists there and - // is an executable file. - for (;; ) { - const char *e = strchr(path, ':'); - if (e == NULL) { - e = path + STRLEN(path); - } - - // Glue together the given directory from $PATH with name and save into - // buf. - vim_strncpy(buf, (char_u *) path, e - path); - append_path((char *) buf, (const char *) name, buf_len); - - if (is_executable(buf)) { - // Found our executable. Free buf and return. - free(buf); - return true; - } - - if (*e != ':') { - // End of $PATH without finding any executable called name. - free(buf); - return false; - } - - path = e + 1; - } - - // We should never get to this point. - assert(false); - return false; -} - -int os_stat(const char_u *name, uv_stat_t *statbuf) -{ - uv_fs_t request; - int result = uv_fs_stat(uv_default_loop(), &request, - (const char *)name, NULL); - *statbuf = request.statbuf; - uv_fs_req_cleanup(&request); - - if (result == kLibuvSuccess) { - return OK; - } - - return FAIL; -} - -int32_t os_getperm(const char_u *name) -{ - uv_stat_t statbuf; - if (os_stat(name, &statbuf) == FAIL) { - return -1; - } else { - return (int32_t)statbuf.st_mode; - } -} - -int os_setperm(const char_u *name, int perm) -{ - uv_fs_t request; - int result = uv_fs_chmod(uv_default_loop(), &request, - (const char*)name, perm, NULL); - uv_fs_req_cleanup(&request); - - if (result == kLibuvSuccess) { - return OK; - } - - return FAIL; -} - -bool os_file_exists(const char_u *name) -{ - uv_stat_t statbuf; - if (os_stat(name, &statbuf) == OK) { - return true; - } - - return false; -} - -bool os_file_is_readonly(const char *name) -{ - return access(name, W_OK) != 0; -} - -int os_file_is_writable(const char *name) -{ - if (access(name, W_OK) == 0) { - if (os_isdir((char_u *)name)) { - return 2; - } - return 1; - } - return 0; -} - -bool os_get_file_size(const char *name, off_t *size) -{ - uv_stat_t statbuf; - if (os_stat((char_u *)name, &statbuf) == OK) { - *size = statbuf.st_size; - return true; - } - return false; -} - -int os_rename(const char_u *path, const char_u *new_path) -{ - uv_fs_t request; - int result = uv_fs_rename(uv_default_loop(), &request, - (const char *)path, (const char *)new_path, NULL); - uv_fs_req_cleanup(&request); - - if (result == kLibuvSuccess) { - return OK; - } - - return FAIL; -} - -int os_mkdir(const char *path, int32_t mode) -{ - uv_fs_t request; - int result = uv_fs_mkdir(uv_default_loop(), &request, path, mode, NULL); - uv_fs_req_cleanup(&request); - return result; -} - -int os_rmdir(const char *path) -{ - uv_fs_t request; - int result = uv_fs_rmdir(uv_default_loop(), &request, path, NULL); - uv_fs_req_cleanup(&request); - return result; -} - -int os_remove(const char *path) -{ - uv_fs_t request; - int result = uv_fs_unlink(uv_default_loop(), &request, path, NULL); - uv_fs_req_cleanup(&request); - return result; -} - -bool os_get_file_info(const char *path, FileInfo *file_info) -{ - if (os_stat((char_u *)path, &(file_info->stat)) == OK) { - return true; - } - return false; -} - -bool os_get_file_info_link(const char *path, FileInfo *file_info) -{ - uv_fs_t request; - int result = uv_fs_lstat(uv_default_loop(), &request, path, NULL); - file_info->stat = request.statbuf; - uv_fs_req_cleanup(&request); - if (result == kLibuvSuccess) { - return true; - } - return false; -} - -bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info) -{ - uv_fs_t request; - int result = uv_fs_fstat(uv_default_loop(), &request, file_descriptor, NULL); - file_info->stat = request.statbuf; - uv_fs_req_cleanup(&request); - if (result == kLibuvSuccess) { - return true; - } - return false; -} - -bool os_file_info_id_equal(FileInfo *file_info_1, FileInfo *file_info_2) -{ - return file_info_1->stat.st_ino == file_info_2->stat.st_ino - && file_info_1->stat.st_dev == file_info_2->stat.st_dev; -} - diff --git a/src/os/input.c b/src/os/input.c deleted file mode 100644 index 4310edd514..0000000000 --- a/src/os/input.c +++ /dev/null @@ -1,195 +0,0 @@ -#include <string.h> -#include <stdint.h> -#include <stdbool.h> - -#include <uv.h> - -#include "os/input.h" -#include "os/event.h" -#include "os/rstream_defs.h" -#include "os/rstream.h" -#include "vim.h" -#include "ui.h" -#include "fileio.h" -#include "getchar.h" -#include "term.h" - -#define READ_BUFFER_SIZE 256 - -typedef enum { - kInputNone, - kInputAvail, - kInputEof -} InbufPollResult; - -static RStream *read_stream; -static bool eof = false, started_reading = false; - -static InbufPollResult inbuf_poll(int32_t ms); -static void stderr_switch(void); -static void read_cb(RStream *rstream, void *data, bool eof); -// Helper function used to push bytes from the 'event' key sequence partially -// between calls to os_inchar when maxlen < 3 -static int push_event_key(uint8_t *buf, int maxlen); - -void input_init() -{ - read_stream = rstream_new(read_cb, READ_BUFFER_SIZE, NULL, false); - rstream_set_file(read_stream, read_cmd_fd); -} - -// Check if there's pending input -bool input_ready() -{ - return rstream_available(read_stream) > 0 || eof; -} - -// Listen for input -void input_start() -{ - rstream_start(read_stream); -} - -// Stop listening for input -void input_stop() -{ - 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) -{ - 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) -{ - InbufPollResult result; - - if (event_is_pending()) { - // Return pending event bytes - return push_event_key(buf, maxlen); - } - - if (ms >= 0) { - if ((result = inbuf_poll(ms)) == kInputNone) { - return 0; - } - } else { - if ((result = inbuf_poll(p_ut)) == kInputNone) { - if (trigger_cursorhold() && maxlen >= 3 - && !typebuf_changed(tb_change_cnt)) { - buf[0] = K_SPECIAL; - buf[1] = KS_EXTRA; - buf[2] = KE_CURSORHOLD; - return 3; - } - - before_blocking(); - result = inbuf_poll(-1); - } - } - - // If there are pending events, return the keys directly - if (event_is_pending()) { - return push_event_key(buf, maxlen); - } - - // If input was put directly in typeahead buffer bail out here. - if (typebuf_changed(tb_change_cnt)) { - return 0; - } - - if (result == kInputEof) { - read_error_exit(); - return 0; - } - - return read_from_input_buf(buf, (int64_t)maxlen); -} - -// Check if a character is available for reading -bool os_char_avail() -{ - return inbuf_poll(0) == kInputAvail; -} - -// Check for CTRL-C typed by reading all available characters. -// In cooked mode we should get SIGINT, no need to check. -void os_breakcheck() -{ - if (curr_tmode == TMODE_RAW && event_poll(0)) - fill_input_buf(false); -} - -bool os_isatty(int fd) -{ - return uv_guess_handle(fd) == UV_TTY; -} - -// This is a replacement for the old `WaitForChar` function in os_unix.c -static InbufPollResult inbuf_poll(int32_t ms) -{ - if (input_available()) { - return kInputAvail; - } - - if (event_poll(ms)) { - return eof && rstream_available(read_stream) == 0 ? - kInputEof : - kInputAvail; - } - - return kInputNone; -} - -static void stderr_switch() -{ - int mode = cur_tmode; - // We probably set the wrong file descriptor to raw mode. Switch back to - // cooked mode - settmode(TMODE_COOK); - // Stop the idle handle - rstream_stop(read_stream); - // Use stderr for stdin, also works for shell commands. - read_cmd_fd = 2; - // Initialize and start the input stream - rstream_set_file(read_stream, read_cmd_fd); - rstream_start(read_stream); - // Set the mode back to what it was - settmode(mode); -} - -static void read_cb(RStream *rstream, void *data, bool at_eof) -{ - if (at_eof) { - if (!started_reading - && rstream_is_regular_file(rstream) - && os_isatty(STDERR_FILENO)) { - // Read error. Since stderr is a tty we switch to reading from it. This - // is for handling for cases like "foo | xargs vim" because xargs - // redirects stdin from /dev/null. Previously, this was done in ui.c - stderr_switch(); - } else { - eof = true; - } - } - - started_reading = true; -} - -static int push_event_key(uint8_t *buf, int maxlen) -{ - static const uint8_t key[3] = { K_SPECIAL, KS_EXTRA, KE_EVENT }; - static int key_idx = 0; - int buf_idx = 0; - - do { - buf[buf_idx++] = key[key_idx++]; - key_idx %= 3; - } while (key_idx > 0 && buf_idx < maxlen); - - return buf_idx; -} diff --git a/src/os/input.h b/src/os/input.h deleted file mode 100644 index 9ffd50fd3f..0000000000 --- a/src/os/input.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef NEOVIM_OS_INPUT_H -#define NEOVIM_OS_INPUT_H - -#include <stdint.h> -#include <stdbool.h> - -void input_init(void); -bool input_ready(void); -void input_start(void); -void input_stop(void); -uint32_t input_read(char *buf, uint32_t count); -int os_inchar(uint8_t *, int, int32_t, int); -bool os_char_avail(void); -void os_breakcheck(void); - -/// Test whether a file descriptor refers to a terminal. -/// -/// @param fd File descriptor. -/// @return `true` if file descriptor refers to a terminal. -bool os_isatty(int fd); - -#endif // NEOVIM_OS_INPUT_H - diff --git a/src/os/job.c b/src/os/job.c deleted file mode 100644 index 7940338b70..0000000000 --- a/src/os/job.c +++ /dev/null @@ -1,370 +0,0 @@ -#include <stdint.h> -#include <stdbool.h> - -#include <uv.h> - -#include "os/uv_helpers.h" -#include "os/job.h" -#include "os/job_defs.h" -#include "os/rstream.h" -#include "os/rstream_defs.h" -#include "os/wstream.h" -#include "os/wstream_defs.h" -#include "os/event.h" -#include "os/event_defs.h" -#include "os/time.h" -#include "os/shell.h" -#include "vim.h" -#include "memory.h" -#include "term.h" - -#define EXIT_TIMEOUT 25 -#define MAX_RUNNING_JOBS 100 -#define JOB_BUFFER_SIZE 1024 -#define JOB_WRITE_MAXMEM 1024 * 1024 - -struct job { - // Job id the index in the job table plus one. - int id; - // 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. - // We use this reference count to ensure the JobExit event is only emitted - // when stdout/stderr are drained - int pending_refs; - // Same as above, but for freeing the job memory which contains - // libuv handles. Only after all are closed the job can be safely freed. - int pending_closes; - // If the job was already stopped - bool stopped; - // Data associated with the job - void *data; - // Callbacks - job_exit_cb exit_cb; - rstream_cb stdout_cb, stderr_cb; - // Readable streams(std{out,err}) - RStream *out, *err; - // Writable stream(stdin) - WStream *in; - // Structures for process spawning/management used by libuv - uv_process_t proc; - uv_process_options_t proc_opts; - uv_stdio_container_t stdio[3]; - uv_pipe_t proc_stdin, proc_stdout, proc_stderr; -}; - -static Job *table[MAX_RUNNING_JOBS] = {NULL}; -static uint32_t job_count = 0; -static uv_prepare_t job_prepare; - -// Some helpers shared in this module -static bool is_alive(Job *job); -static Job * find_job(int id); -static void free_job(Job *job); - -// Callbacks for libuv -static void job_prepare_cb(uv_prepare_t *handle); -static void read_cb(RStream *rstream, void *data, bool eof); -static void exit_cb(uv_process_t *proc, int64_t status, int term_signal); -static void close_cb(uv_handle_t *handle); -static void emit_exit_event(Job *job); - -void job_init() -{ - uv_disable_stdio_inheritance(); - uv_prepare_init(uv_default_loop(), &job_prepare); -} - -void job_teardown() -{ - // 20 tries will give processes about 1 sec to exit cleanly - uint32_t remaining_tries = 20; - bool all_dead = true; - int i; - Job *job; - - // Politely ask each job to terminate - for (i = 0; i < MAX_RUNNING_JOBS; i++) { - if ((job = table[i]) != NULL) { - all_dead = false; - uv_process_kill(&job->proc, SIGTERM); - } - } - - if (all_dead) { - return; - } - - os_delay(10, 0); - // Right now any exited process are zombies waiting for us to acknowledge - // their status with `wait` or handling SIGCHLD. libuv does that - // automatically (and then calls `exit_cb`) but we have to give it a chance - // by running the loop one more time - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - - // Prepare to start shooting - for (i = 0; i < MAX_RUNNING_JOBS; i++) { - if ((job = table[i]) == NULL) { - continue; - } - - // Still alive - while (is_alive(job) && remaining_tries--) { - os_delay(50, 0); - // Acknowledge child exits - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } - - if (is_alive(job)) { - uv_process_kill(&job->proc, SIGKILL); - } - } -} - -int job_start(char **argv, - void *data, - rstream_cb stdout_cb, - rstream_cb stderr_cb, - job_exit_cb job_exit_cb) -{ - int i; - Job *job; - - // Search for a free slot in the table - for (i = 0; i < MAX_RUNNING_JOBS; i++) { - if (table[i] == NULL) { - break; - } - } - - if (i == MAX_RUNNING_JOBS) { - // No free slots - return 0; - } - - job = xmalloc(sizeof(Job)); - // Initialize - job->id = i + 1; - job->pending_refs = 3; - job->pending_closes = 4; - job->data = data; - job->stdout_cb = stdout_cb; - job->stderr_cb = stderr_cb; - job->exit_cb = job_exit_cb; - job->stopped = false; - job->exit_timeout = EXIT_TIMEOUT; - job->proc_opts.file = argv[0]; - job->proc_opts.args = argv; - job->proc_opts.stdio = job->stdio; - job->proc_opts.stdio_count = 3; - job->proc_opts.flags = UV_PROCESS_WINDOWS_HIDE; - job->proc_opts.exit_cb = exit_cb; - job->proc_opts.cwd = NULL; - job->proc_opts.env = NULL; - job->proc.data = NULL; - job->proc_stdin.data = NULL; - job->proc_stdout.data = NULL; - job->proc_stderr.data = NULL; - - // Initialize the job std{in,out,err} - uv_pipe_init(uv_default_loop(), &job->proc_stdin, 0); - job->stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - job->stdio[0].data.stream = (uv_stream_t *)&job->proc_stdin; - - uv_pipe_init(uv_default_loop(), &job->proc_stdout, 0); - job->stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - job->stdio[1].data.stream = (uv_stream_t *)&job->proc_stdout; - - uv_pipe_init(uv_default_loop(), &job->proc_stderr, 0); - job->stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - job->stdio[2].data.stream = (uv_stream_t *)&job->proc_stderr; - - // Spawn the job - if (uv_spawn(uv_default_loop(), &job->proc, &job->proc_opts) != 0) { - free_job(job); - return -1; - } - - // Give all handles a reference to the job - handle_set_job((uv_handle_t *)&job->proc, job); - handle_set_job((uv_handle_t *)&job->proc_stdin, job); - handle_set_job((uv_handle_t *)&job->proc_stdout, job); - handle_set_job((uv_handle_t *)&job->proc_stderr, job); - - job->in = wstream_new(JOB_WRITE_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, true); - job->err = rstream_new(read_cb, JOB_BUFFER_SIZE, job, true); - 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); - rstream_start(job->err); - // Save the job to the table - table[i] = job; - - // Start polling job status if this is the first - if (job_count == 0) { - uv_prepare_start(&job_prepare, job_prepare_cb); - } - job_count++; - - return job->id; -} - -bool job_stop(int id) -{ - Job *job = find_job(id); - - if (job == NULL || job->stopped) { - return false; - } - - job->stopped = true; - - return true; -} - -bool job_write(int id, char *data, uint32_t len) -{ - Job *job = find_job(id); - - if (job == NULL || job->stopped) { - free(data); - return false; - } - - if (!wstream_write(job->in, data, len, true)) { - job_stop(job->id); - return false; - } - - return true; -} - -void job_exit_event(Event event) -{ - Job *job = event.data.job; - - // Free the slot now, 'exit_cb' may want to start another job to replace - // this one - table[job->id - 1] = NULL; - - if (job->exit_cb) { - // Invoke the exit callback - job->exit_cb(job, job->data); - } - - // Free the job resources - free_job(job); - - // Stop polling job status if this was the last - job_count--; - if (job_count == 0) { - uv_prepare_stop(&job_prepare); - } -} - -int job_id(Job *job) -{ - return job->id; -} - -void *job_data(Job *job) -{ - return job->data; -} - -static bool is_alive(Job *job) -{ - return uv_process_kill(&job->proc, 0) == 0; -} - -static Job * find_job(int id) -{ - if (id <= 0 || id > MAX_RUNNING_JOBS) { - return NULL; - } - - return table[id - 1]; -} - -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); - uv_close((uv_handle_t *)&job->proc_stderr, close_cb); - uv_close((uv_handle_t *)&job->proc, close_cb); -} - -/// Iterates the table, sending SIGTERM to stopped jobs and SIGKILL to those -/// that didn't die from SIGTERM after a while(exit_timeout is 0). -static void job_prepare_cb(uv_prepare_t *handle) -{ - Job *job; - int i; - - for (i = 0; i < MAX_RUNNING_JOBS; i++) { - if ((job = table[i]) == NULL || !job->stopped) { - continue; - } - - if ((job->exit_timeout--) == EXIT_TIMEOUT) { - // Job was just stopped, close all stdio handles and send SIGTERM - uv_process_kill(&job->proc, SIGTERM); - } else if (job->exit_timeout == 0) { - // We've waited long enough, send SIGKILL - uv_process_kill(&job->proc, SIGKILL); - } - } -} - -// Wraps the call to std{out,err}_cb and emits a JobExit event if necessary. -static void read_cb(RStream *rstream, void *data, bool eof) -{ - Job *job = data; - - if (rstream == job->out) { - job->stdout_cb(rstream, data, eof); - } else { - job->stderr_cb(rstream, data, eof); - } - - if (eof && --job->pending_refs == 0) { - emit_exit_event(job); - } -} - -// Emits a JobExit event if both rstreams are closed -static void exit_cb(uv_process_t *proc, int64_t status, int term_signal) -{ - Job *job = handle_get_job((uv_handle_t *)proc); - - if (--job->pending_refs == 0) { - emit_exit_event(job); - } -} - -static void emit_exit_event(Job *job) -{ - Event event; - event.type = kEventJobExit; - event.data.job = job; - event_push(event); -} - -static void close_cb(uv_handle_t *handle) -{ - Job *job = handle_get_job(handle); - - if (--job->pending_closes == 0) { - // Only free the job memory after all the associated handles are properly - // closed by libuv - rstream_free(job->out); - rstream_free(job->err); - wstream_free(job->in); - shell_free_argv(job->proc_opts.args); - free(job->data); - free(job); - } -} diff --git a/src/os/job.h b/src/os/job.h deleted file mode 100644 index db147f2c24..0000000000 --- a/src/os/job.h +++ /dev/null @@ -1,78 +0,0 @@ -// Job is a short name we use to refer to child processes that run in parallel -// with the editor, probably executing long-running tasks and sending updates -// asynchronously. Communication happens through anonymous pipes connected to -// the job's std{in,out,err}. They are more like bash/zsh co-processes than the -// usual shell background job. The name 'Job' was chosen because it applies to -// the concept while being significantly shorter. -#ifndef NEOVIM_OS_JOB_H -#define NEOVIM_OS_JOB_H - -#include <stdint.h> -#include <stdbool.h> - -#include "os/event_defs.h" -#include "os/rstream_defs.h" - -/// Initializes job control resources -void job_init(void); - -/// Releases job control resources and terminates running jobs -void job_teardown(void); - -/// Tries to start a new job. -/// -/// @param argv Argument vector for the process. The first item is the -/// executable to run. -/// @param data Caller data that will be associated with the job -/// @param stdout_cb Callback that will be invoked when data is available -/// 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. This is -/// optional. -/// @return The job id if the job started successfully. If the the first item / -/// of `argv`(the program) could not be executed, -1 will be returned. -// 0 will be returned if the job table is full. -int job_start(char **argv, - void *data, - rstream_cb stdout_cb, - rstream_cb stderr_cb, - job_exit_cb exit_cb); - -/// Terminates a job. This is a non-blocking operation, but if the job exists -/// it's guaranteed to succeed(SIGKILL will eventually be sent) -/// -/// @param id The job id -/// @return true if the stop request was successfully sent, false if the job -/// id is invalid(probably because it has already stopped) -bool job_stop(int id); - -/// Writes data to the job's stdin. This is a non-blocking operation, it -/// returns when the write request was sent. -/// -/// @param id The job id -/// @param data Buffer containing the data to be written -/// @param len Size of the data -/// @return true if the write request was successfully sent, false if the job -/// id is invalid(probably because it has already stopped) -bool job_write(int id, char *data, uint32_t len); - -/// Runs the read callback associated with the job exit event -/// -/// @param event Object containing data necessary to invoke the callback -void job_exit_event(Event event); - -/// Get the job id -/// -/// @param job A pointer to the job -/// @return The job id -int job_id(Job *job); - -/// Get data associated with a job -/// -/// @param job A pointer to the job -/// @return The job data -void *job_data(Job *job); - -#endif // NEOVIM_OS_JOB_H - diff --git a/src/os/job_defs.h b/src/os/job_defs.h deleted file mode 100644 index e98b639154..0000000000 --- a/src/os/job_defs.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef NEOVIM_OS_JOB_DEFS_H -#define NEOVIM_OS_JOB_DEFS_H - -#include "os/rstream_defs.h" - -typedef struct job Job; - -/// Function called when the job reads data -/// -/// @param id The job id -/// @param data Some data associated with the job by the caller -typedef void (*job_exit_cb)(Job *job, void *data); - -#endif // NEOVIM_OS_JOB_DEFS_H - diff --git a/src/os/mem.c b/src/os/mem.c deleted file mode 100644 index e5549340ff..0000000000 --- a/src/os/mem.c +++ /dev/null @@ -1,10 +0,0 @@ -/// Functions for accessing system memory information. - -#include <uv.h> - -#include "os/os.h" - -uint64_t os_get_total_mem_kib(void) { - // Convert bytes to KiB. - return uv_get_total_memory() >> 10; -} diff --git a/src/os/msgpack_rpc.c b/src/os/msgpack_rpc.c deleted file mode 100644 index e429bf0519..0000000000 --- a/src/os/msgpack_rpc.c +++ /dev/null @@ -1,408 +0,0 @@ -#include <msgpack.h> - -#include "msgpack_rpc.h" -#include "vim.h" -#include "memory.h" - -static bool msgpack_rpc_to_uint16_t(msgpack_object *obj, uint16_t *arg); - -void msgpack_rpc_call(msgpack_object *req, msgpack_packer *res) -{ - // The initial response structure is the same no matter what happens, - // we set it up here - // Array of size 4 - msgpack_pack_array(res, 4); - // Response type is 1 - msgpack_pack_int(res, 1); - - // Validate the basic structure of the msgpack-rpc payload - if (req->type != MSGPACK_OBJECT_ARRAY) { - msgpack_pack_int(res, 0); // no message id yet - msgpack_rpc_error("Request is not an array", res); - return; - } - - if (req->via.array.size != 4) { - msgpack_pack_int(res, 0); // no message id yet - char error_msg[256]; - snprintf(error_msg, - sizeof(error_msg), - "Request array size is %u, it should be 4", - req->via.array.size); - msgpack_rpc_error(error_msg, res); - } - - if (req->via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_pack_int(res, 0); // no message id yet - msgpack_rpc_error("Id must be a positive integer", res); - } - - // Set the response id, which is the same as the request - msgpack_pack_int(res, req->via.array.ptr[1].via.u64); - - if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_rpc_error("Message type must be an integer", res); - return; - } - - if (req->via.array.ptr[0].via.u64 != 0) { - msgpack_rpc_error("Message type must be 0", res); - return; - } - - if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_rpc_error("Method id must be a positive integer", res); - return; - } - - if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { - msgpack_rpc_error("Paremeters must be an array", res); - return; - } - - // dispatch the message - msgpack_rpc_dispatch(req, res); -} - -void msgpack_rpc_error(char *msg, msgpack_packer *res) -{ - size_t len = strlen(msg); - - // error message - msgpack_pack_raw(res, len); - msgpack_pack_raw_body(res, msg, len); - // Nil result - msgpack_pack_nil(res); -} - -bool msgpack_rpc_to_bool(msgpack_object *obj, bool *arg) -{ - *arg = obj->via.boolean; - return obj->type == MSGPACK_OBJECT_BOOLEAN; -} - -bool msgpack_rpc_to_int64_t(msgpack_object *obj, int64_t *arg) -{ - *arg = obj->via.i64; - return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER - || obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER; -} - -bool msgpack_rpc_to_double(msgpack_object *obj, double *arg) -{ - *arg = obj->via.dec; - return obj->type == MSGPACK_OBJECT_DOUBLE; -} - -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) -{ - arg->data = (char *)obj->via.raw.ptr; - arg->size = obj->via.raw.size; - return obj->type == MSGPACK_OBJECT_RAW; -} - -bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg) -{ - return msgpack_rpc_to_uint16_t(obj, arg); -} - -bool msgpack_rpc_to_window(msgpack_object *obj, Window *arg) -{ - return msgpack_rpc_to_uint16_t(obj, arg); -} - -bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg) -{ - return msgpack_rpc_to_uint16_t(obj, arg); -} - -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) -{ - switch (obj->type) { - case MSGPACK_OBJECT_NIL: - arg->type = kObjectTypeNil; - return true; - - case MSGPACK_OBJECT_BOOLEAN: - arg->type = kObjectTypeBool; - return msgpack_rpc_to_bool(obj, &arg->data.boolean); - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - arg->type = kObjectTypeInt; - return msgpack_rpc_to_int64_t(obj, &arg->data.integer); - - case MSGPACK_OBJECT_DOUBLE: - arg->type = kObjectTypeFloat; - return msgpack_rpc_to_double(obj, &arg->data.floating_point); - - case MSGPACK_OBJECT_RAW: - arg->type = kObjectTypeString; - return msgpack_rpc_to_string(obj, &arg->data.string); - - case MSGPACK_OBJECT_ARRAY: - arg->type = kObjectTypeArray; - return msgpack_rpc_to_array(obj, &arg->data.array); - - case MSGPACK_OBJECT_MAP: - arg->type = kObjectTypeDictionary; - return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); - - default: - return false; - } -} - -bool msgpack_rpc_to_stringarray(msgpack_object *obj, StringArray *arg) -{ - if (obj->type != MSGPACK_OBJECT_ARRAY) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.array.size, sizeof(String)); - - for (uint32_t i = 0; i < obj->via.array.size; i++) { - if (!msgpack_rpc_to_string(obj->via.array.ptr + i, &arg->items[i])) { - return false; - } - } - - return true; -} - -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) -{ - // positions are represented by integer arrays of size 2 - if (obj->type != MSGPACK_OBJECT_ARRAY - || obj->via.array.size != 2 - || obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER - || obj->via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - return false; - } - - arg->row = obj->via.array.ptr[0].via.u64; - arg->col = obj->via.array.ptr[1].via.u64; - - return true; -} - - -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) -{ - if (obj->type != MSGPACK_OBJECT_ARRAY) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.array.size, sizeof(Object)); - - for (uint32_t i = 0; i < obj->via.array.size; i++) { - if (!msgpack_rpc_to_object(obj->via.array.ptr + i, &arg->items[i])) { - return false; - } - } - - return true; -} - -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) -{ - if (obj->type != MSGPACK_OBJECT_MAP) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.map.size, sizeof(KeyValuePair)); - - - for (uint32_t i = 0; i < obj->via.map.size; i++) { - if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key, - &arg->items[i].key)) { - return false; - } - - if (!msgpack_rpc_to_object(&obj->via.map.ptr[i].val, - &arg->items[i].value)) { - return false; - } - } - - return true; -} - -void msgpack_rpc_from_bool(bool result, msgpack_packer *res) -{ - if (result) { - msgpack_pack_true(res); - } else { - msgpack_pack_false(res); - } -} - -void msgpack_rpc_from_int64_t(int64_t result, msgpack_packer *res) -{ - msgpack_pack_int64(res, result); -} - -void msgpack_rpc_from_uint64_t(uint64_t result, msgpack_packer *res) -{ - msgpack_pack_uint64(res, result); -} - -void msgpack_rpc_from_double(double result, msgpack_packer *res) -{ - msgpack_pack_double(res, result); -} - -void msgpack_rpc_from_string(String result, msgpack_packer *res) -{ - msgpack_pack_raw(res, result.size); - msgpack_pack_raw_body(res, result.data, result.size); -} - -void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res) -{ - msgpack_rpc_from_uint64_t(result, res); -} - -void msgpack_rpc_from_window(Window result, msgpack_packer *res) -{ - msgpack_rpc_from_uint64_t(result, res); -} - -void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res) -{ - msgpack_rpc_from_uint64_t(result, res); -} - -void msgpack_rpc_from_object(Object result, msgpack_packer *res) -{ - switch (result.type) { - case kObjectTypeNil: - msgpack_pack_nil(res); - break; - - case kObjectTypeBool: - msgpack_rpc_from_bool(result.data.boolean, res); - break; - - case kObjectTypeInt: - msgpack_rpc_from_int64_t(result.data.integer, res); - break; - - case kObjectTypeFloat: - msgpack_rpc_from_double(result.data.floating_point, res); - break; - - case kObjectTypeString: - msgpack_rpc_from_string(result.data.string, res); - break; - - case kObjectTypeArray: - msgpack_rpc_from_array(result.data.array, res); - break; - - case kObjectTypeDictionary: - msgpack_rpc_from_dictionary(result.data.dictionary, res); - break; - - default: - abort(); - } -} - -void msgpack_rpc_from_stringarray(StringArray result, msgpack_packer *res) -{ - msgpack_pack_array(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_string(result.items[i], res); - } -} - -void msgpack_rpc_from_position(Position result, msgpack_packer *res) -{ - msgpack_pack_array(res, 2);; - msgpack_pack_uint16(res, result.row); - msgpack_pack_uint16(res, result.col); -} - -void msgpack_rpc_from_array(Array result, msgpack_packer *res) -{ - msgpack_pack_array(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_object(result.items[i], res); - } -} - -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) -{ - msgpack_pack_map(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_string(result.items[i].key, res); - msgpack_rpc_from_object(result.items[i].value, res); - } -} - -void msgpack_rpc_free_object(Object value) -{ - switch (value.type) { - case kObjectTypeNil: - case kObjectTypeBool: - case kObjectTypeInt: - case kObjectTypeFloat: - break; - - case kObjectTypeString: - msgpack_rpc_free_string(value.data.string); - break; - - case kObjectTypeArray: - msgpack_rpc_free_array(value.data.array); - break; - - case kObjectTypeDictionary: - msgpack_rpc_free_dictionary(value.data.dictionary); - break; - - default: - abort(); - } -} - -void msgpack_rpc_free_stringarray(StringArray value) { - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_string(value.items[i]); - } - - free(value.items); -} - -void msgpack_rpc_free_array(Array value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_object(value.items[i]); - } - - free(value.items); -} - -void msgpack_rpc_free_dictionary(Dictionary value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_string(value.items[i].key); - msgpack_rpc_free_object(value.items[i].value); - } - - free(value.items); -} - -static bool msgpack_rpc_to_uint16_t(msgpack_object *obj, uint16_t *arg) -{ - *arg = obj->via.u64; - return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER; -} - diff --git a/src/os/msgpack_rpc.h b/src/os/msgpack_rpc.h deleted file mode 100644 index 7f754bfca1..0000000000 --- a/src/os/msgpack_rpc.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef NEOVIM_OS_MSGPACK_RPC_H -#define NEOVIM_OS_MSGPACK_RPC_H - -#include <stdint.h> -#include <stdbool.h> - -#include <msgpack.h> - -#include "api/defs.h" - -/// Validates the basic structure of the msgpack-rpc call and fills `res` -/// with the basic response structure. -/// -/// @param req The parsed request object -/// @param res A packer that contains the response -void msgpack_rpc_call(msgpack_object *req, msgpack_packer *res); - -/// Dispatches to the actual API function after basic payload validation by -/// `msgpack_rpc_call`. It is responsible for validating/converting arguments -/// to C types, and converting the return value back to msgpack types. -/// The implementation is generated at compile time with metadata extracted -/// from the api/*.h headers, -/// -/// @param req The parsed request object -/// @param res A packer that contains the response -void msgpack_rpc_dispatch(msgpack_object *req, msgpack_packer *res); - -/// Finishes the msgpack-rpc call with an error message. -/// -/// @param msg The error message -/// @param res A packer that contains the response -void msgpack_rpc_error(char *msg, msgpack_packer *res); - -/// Functions for validating and converting from msgpack types to C types. -/// These are used by `msgpack_rpc_dispatch` to validate and convert each -/// argument. -/// -/// @param obj The object to convert -/// @param[out] arg A pointer to the avalue -/// @return true if the convertion succeeded, false otherwise -bool msgpack_rpc_to_bool(msgpack_object *obj, bool *arg); -bool msgpack_rpc_to_int64_t(msgpack_object *obj, int64_t *arg); -bool msgpack_rpc_to_double(msgpack_object *obj, double *arg); -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg); -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg); -bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg); -bool msgpack_rpc_to_window(msgpack_object *obj, Window *arg); -bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg); -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg); -bool msgpack_rpc_to_stringarray(msgpack_object *obj, StringArray *arg); -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg); -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg); - -/// Functions for converting from C types to msgpack types. -/// These are used by `msgpack_rpc_dispatch` to convert return values -/// from the API -/// -/// @param result A pointer to the result -/// @param res A packer that contains the response -void msgpack_rpc_from_bool(bool result, msgpack_packer *res); -void msgpack_rpc_from_int64_t(int64_t result, msgpack_packer *res); -void msgpack_rpc_from_double(double result, msgpack_packer *res); -void msgpack_rpc_from_position(Position result, msgpack_packer *res); -void msgpack_rpc_from_string(String result, msgpack_packer *res); -void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res); -void msgpack_rpc_from_window(Window result, msgpack_packer *res); -void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res); -void msgpack_rpc_from_object(Object result, msgpack_packer *res); -void msgpack_rpc_from_stringarray(StringArray result, msgpack_packer *res); -void msgpack_rpc_from_array(Array result, msgpack_packer *res); -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res); - -/// Helpers for initializing types that may be freed later -#define msgpack_rpc_init_bool -#define msgpack_rpc_init_int64_t -#define msgpack_rpc_init_double -#define msgpack_rpc_init_position -#define msgpack_rpc_init_string -#define msgpack_rpc_init_buffer -#define msgpack_rpc_init_window -#define msgpack_rpc_init_tabpage -#define msgpack_rpc_init_object = {.type = kObjectTypeNil} -#define msgpack_rpc_init_stringarray = {.items = NULL, .size = 0} -#define msgpack_rpc_init_array = {.items = NULL, .size = 0} -#define msgpack_rpc_init_dictionary = {.items = NULL, .size = 0} - -/// Helpers for freeing arguments/return value -/// -/// @param value The value to be freed -#define msgpack_rpc_free_bool(value) -#define msgpack_rpc_free_int64_t(value) -#define msgpack_rpc_free_double(value) -#define msgpack_rpc_free_position(value) -// Strings are not copied from msgpack and so don't need to be freed(they -// probably "live" in the msgpack streaming buffer) -#define msgpack_rpc_free_string(value) -#define msgpack_rpc_free_buffer(value) -#define msgpack_rpc_free_window(value) -#define msgpack_rpc_free_tabpage(value) -void msgpack_rpc_free_object(Object value); -void msgpack_rpc_free_stringarray(StringArray value); -void msgpack_rpc_free_array(Array value); -void msgpack_rpc_free_dictionary(Dictionary value); - - -#endif // NEOVIM_OS_MSGPACK_RPC_H - diff --git a/src/os/os.h b/src/os/os.h deleted file mode 100644 index b30872f06d..0000000000 --- a/src/os/os.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef NEOVIM_OS_OS_H -#define NEOVIM_OS_OS_H -#include <uv.h> - -#include "vim.h" - -/// Change to the given directory. -/// -/// @return `0` on success, a libuv error code on failure. -int os_chdir(const char *path); - -/// Get the name of current directory. -/// -/// @param buf Buffer to store the directory name. -/// @param len Length of `buf`. -/// @return `OK` for success, `FAIL` for failure. -int os_dirname(char_u *buf, size_t len); - -/// Check if the given path is a directory or not. -/// -/// @return `true` if `fname` is a directory. -bool os_isdir(const char_u *name); - -/// Check if the given path represents an executable file. -/// -/// @return `true` if `name` is executable and -/// - can be found in $PATH, -/// - is relative to current dir or -/// - is absolute. -/// -/// @return `false` otherwise. -bool os_can_exe(const char_u *name); - -/// Get the file permissions for a given file. -/// -/// @return `-1` when `name` doesn't exist. -int32_t os_getperm(const char_u *name); - -/// Set the permission of a file. -/// -/// @return `OK` for success, `FAIL` for failure. -int os_setperm(const char_u *name, int perm); - -/// Check if a file exists. -/// -/// @return `true` if `name` exists. -bool os_file_exists(const char_u *name); - -/// Check if a file is readonly. -/// -/// @return `true` if `name` is readonly. -bool os_file_is_readonly(const char *name); - -/// Check if a file is writable. -/// -/// @return `0` if `name` is not writable, -/// @return `1` if `name` is writable, -/// @return `2` for a directory which we have rights to write into. -int os_file_is_writable(const char *name); - -/// Get the size of a file in bytes. -/// -/// @param[out] size pointer to an off_t to put the size into. -/// @return `true` for success, `false` for failure. -bool os_get_file_size(const char *name, off_t *size); - -/// Rename a file or directory. -/// -/// @return `OK` for success, `FAIL` for failure. -int os_rename(const char_u *path, const char_u *new_path); - -/// Make a directory. -/// -/// @return `0` for success, non-zero for failure. -int os_mkdir(const char *path, int32_t mode); - -/// Remove a directory. -/// -/// @return `0` for success, non-zero for failure. -int os_rmdir(const char *path); - -/// Remove a file. -/// -/// @return `0` for success, non-zero for failure. -int os_remove(const char *path); - -/// Get the total system physical memory in KiB. -uint64_t os_get_total_mem_kib(void); -const char *os_getenv(const char *name); -int os_setenv(const char *name, const char *value, int overwrite); -char *os_getenvname_at_index(size_t index); - -/// Get the process ID of the Neovim process. -/// -/// @return the process ID. -int64_t os_get_pid(void); - -/// Get the hostname of the machine runing Neovim. -/// -/// @param hostname Buffer to store the hostname. -/// @param len Length of `hostname`. -void os_get_hostname(char *hostname, size_t len); - -int os_get_usernames(garray_T *usernames); -int os_get_user_name(char *s, size_t len); -int os_get_uname(uid_t uid, char *s, size_t len); -char *os_get_user_directory(const char *name); - -/// Get stat information for a file. -/// -/// @return OK on success, FAIL if an failure occured. -int os_stat(const char_u *name, uv_stat_t *statbuf); - -/// Struct which encapsulates stat information. -typedef struct { - // TODO(stefan991): make stat private - uv_stat_t stat; -} FileInfo; - -/// Get the file information for a given path -/// -/// @param file_descriptor File descriptor of the file. -/// @param[out] file_info Pointer to a FileInfo to put the information in. -/// @return `true` on sucess, `false` for failure. -bool os_get_file_info(const char *path, FileInfo *file_info); - -/// Get the file information for a given path without following links -/// -/// @param path Path to the file. -/// @param[out] file_info Pointer to a FileInfo to put the information in. -/// @return `true` on sucess, `false` for failure. -bool os_get_file_info_link(const char *path, FileInfo *file_info); - -/// Get the file information for a given file descriptor -/// -/// @param file_descriptor File descriptor of the file. -/// @param[out] file_info Pointer to a FileInfo to put the information in. -/// @return `true` on sucess, `false` for failure. -bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info); - -/// Compare the inodes of two FileInfos -/// -/// @return `true` if the two FileInfos represent the same file. -bool os_file_info_id_equal(FileInfo *file_info_1, FileInfo *file_info_2); - -#endif // NEOVIM_OS_OS_H diff --git a/src/os/rstream.c b/src/os/rstream.c deleted file mode 100644 index e14075c8db..0000000000 --- a/src/os/rstream.c +++ /dev/null @@ -1,297 +0,0 @@ -#include <stdint.h> -#include <stdbool.h> -#include <stdlib.h> - -#include <uv.h> - -#include "os/uv_helpers.h" -#include "os/rstream_defs.h" -#include "os/rstream.h" -#include "os/event_defs.h" -#include "os/event.h" -#include "vim.h" -#include "memory.h" - -struct rstream { - uv_buf_t uvbuf; - void *data; - char *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 reading, free_handle, async; -}; - -// Callbacks used by libuv -static void alloc_cb(uv_handle_t *, size_t, uv_buf_t *); -static void read_cb(uv_stream_t *, ssize_t, const uv_buf_t *); -static void fread_idle_cb(uv_idle_t *); -static void close_cb(uv_handle_t *handle); -static void emit_read_event(RStream *rstream, bool eof); - -RStream * rstream_new(rstream_cb cb, - uint32_t buffer_size, - void *data, - bool async) -{ - RStream *rv = xmalloc(sizeof(RStream)); - rv->buffer = xmalloc(buffer_size); - rv->buffer_size = buffer_size; - rv->data = data; - rv->async = async; - rv->cb = cb; - rv->rpos = rv->wpos = rv->fpos = 0; - rv->stream = NULL; - rv->fread_idle = NULL; - rv->free_handle = false; - rv->file_type = UV_UNKNOWN_HANDLE; - - return rv; -} - -void rstream_free(RStream *rstream) -{ - if (rstream->free_handle) { - if (rstream->fread_idle != NULL) { - uv_close((uv_handle_t *)rstream->fread_idle, close_cb); - } else { - uv_close((uv_handle_t *)rstream->stream, close_cb); - } - } - - free(rstream->buffer); - free(rstream); -} - -void rstream_set_stream(RStream *rstream, uv_stream_t *stream) -{ - handle_set_rstream((uv_handle_t *)stream, rstream); - rstream->stream = stream; -} - -void rstream_set_file(RStream *rstream, uv_file file) -{ - rstream->file_type = uv_guess_handle(file); - - if (rstream->free_handle) { - // If this is the second time we're calling this function, free the - // previously allocated memory - if (rstream->fread_idle != NULL) { - uv_close((uv_handle_t *)rstream->fread_idle, close_cb); - } else { - uv_close((uv_handle_t *)rstream->stream, close_cb); - } - } - - if (rstream->file_type == UV_FILE) { - // Non-blocking file reads are simulated with a idle handle that reads - // in chunks of rstream->buffer_size, giving time for other events to - // be processed between reads. - rstream->fread_idle = xmalloc(sizeof(uv_idle_t)); - uv_idle_init(uv_default_loop(), rstream->fread_idle); - rstream->fread_idle->data = NULL; - handle_set_rstream((uv_handle_t *)rstream->fread_idle, rstream); - } else { - // Only pipes are supported for now - assert(rstream->file_type == UV_NAMED_PIPE - || rstream->file_type == UV_TTY); - rstream->stream = xmalloc(sizeof(uv_pipe_t)); - uv_pipe_init(uv_default_loop(), (uv_pipe_t *)rstream->stream, 0); - uv_pipe_open((uv_pipe_t *)rstream->stream, file); - rstream->stream->data = NULL; - handle_set_rstream((uv_handle_t *)rstream->stream, rstream); - } - - rstream->fd = file; - rstream->free_handle = true; -} - -bool rstream_is_regular_file(RStream *rstream) -{ - return rstream->file_type == UV_FILE; -} - -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); - } -} - -void rstream_stop(RStream *rstream) -{ - if (rstream->file_type == UV_FILE) { - uv_idle_stop(rstream->fread_idle); - } else { - uv_read_stop(rstream->stream); - } -} - -size_t rstream_read(RStream *rstream, char *buf, uint32_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; -} - -size_t rstream_available(RStream *rstream) -{ - return rstream->wpos - rstream->rpos; -} - -void rstream_read_event(Event event) -{ - RStream *rstream = event.data.rstream.ptr; - - rstream->cb(rstream, rstream->data, event.data.rstream.eof); -} - -// Called by libuv to allocate memory for reading. -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 -// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a -// 0-length buffer. -static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) -{ - RStream *rstream = handle_get_rstream((uv_handle_t *)stream); - - if (cnt <= 0) { - if (cnt != UV_ENOBUFS) { - // Read error or EOF, either way stop the stream and invoke the callback - // with eof == true - uv_read_stop(stream); - emit_read_event(rstream, true); - } - return; - } - - // at this point we're sure that cnt is positive, no error occurred - size_t nread = (size_t) cnt; - - // Data was already written, so all we need is to update 'wpos' to reflect - // the space actually used in the buffer. - rstream->wpos += nread; - - if (rstream->wpos == rstream->buffer_size) { - // The last read filled the buffer, stop reading for now - rstream_stop(rstream); - } - - rstream->reading = false; - emit_read_event(rstream, false); -} - -// Called by the by the 'idle' handle to emulate a reading event -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; - - // the offset argument to uv_fs_read is int64_t, could someone really try - // to read more than 9 quintillion (9e18) bytes? - // upcast is meant to avoid tautological condition warning on 32 bits - uintmax_t fpos_intmax = rstream->fpos; - assert(fpos_intmax <= INT64_MAX); - - // Synchronous read - uv_fs_read( - uv_default_loop(), - &req, - rstream->fd, - &rstream->uvbuf, - 1, - (int64_t) rstream->fpos, - NULL); - - uv_fs_req_cleanup(&req); - - if (req.result <= 0) { - uv_idle_stop(rstream->fread_idle); - emit_read_event(rstream, true); - return; - } - - // no errors (req.result (ssize_t) is positive), it's safe to cast. - size_t nread = (size_t) req.result; - - rstream->wpos += 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); -} - -static void close_cb(uv_handle_t *handle) -{ - free(handle->data); - free(handle); -} - -static void emit_read_event(RStream *rstream, bool eof) -{ - if (rstream->async) { - Event event; - - event.type = kEventRStreamData; - event.data.rstream.ptr = rstream; - event.data.rstream.eof = eof; - event_push(event); - } else { - // Invoke the callback passing in the number of bytes available and data - // associated with the stream - rstream->cb(rstream, rstream->data, eof); - } -} diff --git a/src/os/rstream.h b/src/os/rstream.h deleted file mode 100644 index 5eb3e97f55..0000000000 --- a/src/os/rstream.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef NEOVIM_OS_RSTREAM_H -#define NEOVIM_OS_RSTREAM_H - -#include <stdbool.h> -#include <stdint.h> -#include <uv.h> - -#include "os/event_defs.h" -#include "os/rstream_defs.h" - -/// 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 data Some state to associate with the `RStream` instance -/// @param async Flag that specifies if the callback should only be called -/// outside libuv event loop(When processing async events with -/// KE_EVENT). Only the RStream instance reading user input should set -/// this to false -/// @return The newly-allocated `RStream` instance -RStream * rstream_new(rstream_cb cb, - uint32_t buffer_size, - void *data, - bool async); - -/// Frees all memory allocated for a RStream instance -/// -/// @param rstream The `RStream` instance -void rstream_free(RStream *rstream); - -/// Sets the underlying `uv_stream_t` instance -/// -/// @param rstream The `RStream` instance -/// @param stream The new `uv_stream_t` instance -void rstream_set_stream(RStream *rstream, uv_stream_t *stream); - -/// Sets the underlying file descriptor that will be read from. Only pipes -/// and regular files are supported for now. -/// -/// @param rstream The `RStream` instance -/// @param file The file descriptor -void rstream_set_file(RStream *rstream, uv_file file); - -/// Tests if the stream is backed by a regular file -/// -/// @param rstream The `RStream` instance -/// @return True if the underlying file descriptor represents a regular file -bool rstream_is_regular_file(RStream *rstream); - -/// Starts watching for events from a `RStream` instance. -/// -/// @param rstream The `RStream` instance -void rstream_start(RStream *rstream); - -/// Stops watching for events from a `RStream` instance. -/// -/// @param rstream The `RStream` instance -void rstream_stop(RStream *rstream); - -/// 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 *buffer, uint32_t 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); - -/// Runs the read callback associated with the rstream -/// -/// @param event Object containing data necessary to invoke the callback -void rstream_read_event(Event event); - -#endif // NEOVIM_OS_RSTREAM_H - diff --git a/src/os/rstream_defs.h b/src/os/rstream_defs.h deleted file mode 100644 index 3d1dbec34f..0000000000 --- a/src/os/rstream_defs.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef NEOVIM_OS_RSTREAM_DEFS_H -#define NEOVIM_OS_RSTREAM_DEFS_H - -#include <stdbool.h> - -typedef struct rstream RStream; - -/// Type of function called when the RStream receives data -/// -/// @param rstream The RStream instance -/// @param data State associated with the RStream instance -/// @param eof If the stream reached EOF. -typedef void (*rstream_cb)(RStream *rstream, void *data, bool eof); - -#endif // NEOVIM_OS_RSTREAM_DEFS_H - diff --git a/src/os/server.c b/src/os/server.c deleted file mode 100644 index bda9602936..0000000000 --- a/src/os/server.c +++ /dev/null @@ -1,243 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <stdbool.h> - -#include <uv.h> - -#include "os/channel_defs.h" -#include "os/channel.h" -#include "os/server.h" -#include "os/os.h" -#include "vim.h" -#include "memory.h" -#include "message.h" -#include "fileio.h" -#include "map.h" - -#define MAX_CONNECTIONS 32 -#define ADDRESS_MAX_SIZE 256 -#define NEOVIM_DEFAULT_TCP_PORT 7450 - -typedef enum { - kServerTypeTcp, - kServerTypePipe -} ServerType; - -typedef struct { - // Protocol for channels established through this server - ChannelProtocol protocol; - // Type of the union below - ServerType type; - - // This is either a tcp server or unix socket(named pipe on windows) - union { - struct { - uv_tcp_t handle; - struct sockaddr_in addr; - } tcp; - struct { - uv_pipe_t handle; - char addr[ADDRESS_MAX_SIZE]; - } pipe; - } socket; -} Server; - -static Map *servers = NULL; - -static void close_server(Map *map, const char *endpoint, void *server); -static void connection_cb(uv_stream_t *server, int status); -static void free_client(uv_handle_t *handle); -static void free_server(uv_handle_t *handle); - -void server_init() -{ - servers = map_new(); - - if (!os_getenv("NEOVIM_LISTEN_ADDRESS")) { - char *listen_address = (char *)vim_tempname('s'); - os_setenv("NEOVIM_LISTEN_ADDRESS", listen_address, 1); - free(listen_address); - } - - server_start((char *)os_getenv("NEOVIM_LISTEN_ADDRESS"), - kChannelProtocolMsgpack); -} - -void server_teardown() -{ - if (!servers) { - return; - } - - map_foreach(servers, close_server); -} - -void server_start(char *endpoint, ChannelProtocol prot) -{ - char addr[ADDRESS_MAX_SIZE]; - - // Trim to `ADDRESS_MAX_SIZE` - strncpy(addr, endpoint, sizeof(addr)); - - // Check if the server already exists - if (map_has(servers, addr)) { - EMSG2("Already listening on %s", addr); - return; - } - - ServerType server_type = kServerTypeTcp; - Server *server = xmalloc(sizeof(Server)); - char ip[16], *ip_end = strrchr(addr, ':'); - - server->protocol = prot; - - if (!ip_end) { - ip_end = strchr(addr, NUL); - } - - uint32_t addr_len = ip_end - addr; - - if (addr_len > sizeof(ip) - 1) { - // Maximum length of a ip address buffer is 15(eg: 255.255.255.255) - addr_len = sizeof(ip); - } - - // Extract the address part - strncpy(ip, addr, addr_len); - - int port = NEOVIM_DEFAULT_TCP_PORT; - - if (*ip_end == ':') { - char *port_end; - // Extract the port - port = strtol(ip_end + 1, &port_end, 10); - - errno = 0; - if (errno != 0 || port == 0 || port > 0xffff) { - // Invalid port, treat as named pipe or unix socket - server_type = kServerTypePipe; - } - } - - if (server_type == kServerTypeTcp) { - // Try to parse ip address - if (uv_ip4_addr(ip, port, &server->socket.tcp.addr)) { - // Invalid address, treat as named pipe or unix socket - server_type = kServerTypePipe; - } - } - - int result; - - if (server_type == kServerTypeTcp) { - // Listen on tcp address/port - uv_tcp_init(uv_default_loop(), &server->socket.tcp.handle); - server->socket.tcp.handle.data = server; - uv_tcp_bind(&server->socket.tcp.handle, - (const struct sockaddr *)&server->socket.tcp.addr, - 0); - result = uv_listen((uv_stream_t *)&server->socket.tcp.handle, - MAX_CONNECTIONS, - connection_cb); - if (result) { - uv_close((uv_handle_t *)&server->socket.tcp.handle, free_server); - } - } else { - // Listen on named pipe or unix socket - strcpy(server->socket.pipe.addr, addr); - uv_pipe_init(uv_default_loop(), &server->socket.pipe.handle, 0); - server->socket.pipe.handle.data = server; - uv_pipe_bind(&server->socket.pipe.handle, server->socket.pipe.addr); - result = uv_listen((uv_stream_t *)&server->socket.pipe.handle, - MAX_CONNECTIONS, - connection_cb); - - if (result) { - uv_close((uv_handle_t *)&server->socket.pipe.handle, free_server); - } - } - - if (result) { - EMSG2("Failed to start server: %s", uv_strerror(result)); - return; - } - - - server->type = server_type; - - // Add the server to the hash table - map_put(servers, addr, server); -} - -void server_stop(char *endpoint) -{ - Server *server; - char addr[ADDRESS_MAX_SIZE]; - - // Trim to `ADDRESS_MAX_SIZE` - strncpy(addr, endpoint, sizeof(addr)); - - if ((server = map_get(servers, addr)) == NULL) { - EMSG2("Not listening on %s", addr); - return; - } - - if (server->type == kServerTypeTcp) { - uv_close((uv_handle_t *)&server->socket.tcp.handle, free_server); - } else { - uv_close((uv_handle_t *)&server->socket.pipe.handle, free_server); - } - - map_del(servers, addr); -} - -static void connection_cb(uv_stream_t *server, int status) -{ - int result; - uv_stream_t *client; - Server *srv = server->data; - - if (status < 0) { - abort(); - } - - if (srv->type == kServerTypeTcp) { - client = xmalloc(sizeof(uv_tcp_t)); - uv_tcp_init(uv_default_loop(), (uv_tcp_t *)client); - } else { - client = xmalloc(sizeof(uv_pipe_t)); - uv_pipe_init(uv_default_loop(), (uv_pipe_t *)client, 0); - } - - result = uv_accept(server, client); - - if (result) { - EMSG2("Failed to accept connection: %s", uv_strerror(result)); - uv_close((uv_handle_t *)client, free_client); - return; - } - - channel_from_stream(client, srv->protocol); -} - -static void close_server(Map *map, const char *endpoint, void *srv) -{ - Server *server = srv; - - if (server->type == kServerTypeTcp) { - uv_close((uv_handle_t *)&server->socket.tcp.handle, free_server); - } else { - uv_close((uv_handle_t *)&server->socket.pipe.handle, free_server); - } -} - -static void free_client(uv_handle_t *handle) -{ - free(handle); -} - -static void free_server(uv_handle_t *handle) -{ - free(handle->data); -} diff --git a/src/os/server.h b/src/os/server.h deleted file mode 100644 index b9459a81af..0000000000 --- a/src/os/server.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef NEOVIM_OS_SERVER_H -#define NEOVIM_OS_SERVER_H - -#include "os/channel_defs.h" - -/// Initializes the module -void server_init(); - -/// Teardown the server module -void server_teardown(); - -/// Starts listening on arbitrary tcp/unix addresses specified by -/// `endpoint` for API calls. The type of socket used(tcp or unix/pipe) will -/// be determined by parsing `endpoint`: If it's a valid tcp address in the -/// 'ip:port' format, then it will be tcp socket, else it will be a unix -/// socket or named pipe. -/// -/// @param endpoint Address of the server. Either a 'ip:port' string or an -/// arbitrary identifier(trimmed to 256 bytes) for the unix socket or -/// named pipe. -/// @param prot The rpc protocol to be used -void server_start(char *endpoint, ChannelProtocol prot); - -/// Stops listening on the address specified by `endpoint`. -/// -/// @param endpoint Address of the server. -void server_stop(char *endpoint); - -#endif // NEOVIM_OS_SERVER_H - diff --git a/src/os/server_defs.h b/src/os/server_defs.h deleted file mode 100644 index cbaefe3949..0000000000 --- a/src/os/server_defs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef NEOVIM_OS_SERVER_DEFS_H -#define NEOVIM_OS_SERVER_DEFS_H - -typedef struct server Server; - -#endif // NEOVIM_OS_SERVER_DEFS_H - diff --git a/src/os/shell.c b/src/os/shell.c deleted file mode 100644 index d14e355d19..0000000000 --- a/src/os/shell.c +++ /dev/null @@ -1,465 +0,0 @@ -#include <string.h> -#include <stdbool.h> -#include <stdlib.h> - -#include <uv.h> - -#include "os/shell.h" -#include "os/signal.h" -#include "types.h" -#include "vim.h" -#include "message.h" -#include "ascii.h" -#include "memory.h" -#include "term.h" -#include "misc2.h" -#include "screen.h" -#include "memline.h" -#include "option_defs.h" -#include "charset.h" - -#define BUFFER_LENGTH 1024 - -typedef struct { - bool reading; - int old_state, old_mode, exit_status, exited; - char *wbuffer; - char rbuffer[BUFFER_LENGTH]; - uv_buf_t bufs[2]; - uv_stream_t *shell_stdin; - garray_T ga; -} ProcessData; - -/// Parses a command string into a sequence of words, taking quotes into -/// consideration. -/// -/// @param str The command string to be parsed -/// @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); - -/// Calculates the length of a shell word. -/// -/// @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 *command); - -/// Queues selected range for writing to the child process stdin. -/// -/// @param req The structure containing information to peform the write -static void write_selection(uv_write_t *req); - -/// Cleanup memory and restore state modified by `os_call_shell`. -/// -/// @param data State shared by all functions collaborating with -/// `os_call_shell`. -/// @param opts Process spawning options, containing some allocated memory -/// @param shellopts Options passed to `os_call_shell`. Used for deciding -/// if/which messages are displayed. -static int proc_cleanup_exit(ProcessData *data, - uv_process_options_t *opts, - int shellopts); -// Callbacks for libuv -static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf); -static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf); -static void write_cb(uv_write_t *req, int status); -static void exit_cb(uv_process_t *proc, int64_t status, int term_signal); - -char ** shell_build_argv(char_u *cmd, 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 *))); - - // Split 'shell' - i = tokenize(p_sh, rv); - - if (extra_shell_opt != NULL) { - // Push a copy of `extra_shell_opt` - rv[i++] = xstrdup((char *)extra_shell_opt); - } - - if (cmd != NULL) { - // Split 'shellcmdflag' - i += tokenize(p_shcf, rv + i); - rv[i++] = xstrdup((char *)cmd); - } - - rv[i] = NULL; - - return rv; -} - -void shell_free_argv(char **argv) -{ - char **p = argv; - - if (p == NULL) { - // Nothing was allocated, return - return; - } - - while (*p != NULL) { - // Free each argument - free(*p); - p++; - } - - free(argv); -} - -int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) -{ - uv_stdio_container_t proc_stdio[3]; - uv_process_options_t proc_opts; - uv_process_t proc; - uv_pipe_t proc_stdin, proc_stdout; - uv_write_t write_req; - int expected_exits = 1; - ProcessData pdata = { - .reading = false, - .exited = 0, - .old_mode = cur_tmode, - .old_state = State, - .shell_stdin = (uv_stream_t *)&proc_stdin, - .wbuffer = NULL, - }; - - out_flush(); - if (opts & kShellOptCooked) { - // set to normal mode - settmode(TMODE_COOK); - } - - // While the child is running, ignore terminating signals - signal_reject_deadly(); - - // Create argv for `uv_spawn` - // TODO(tarruda): we can use a static buffer for small argument vectors. 1024 - // bytes should be enough for most of the commands and if more is necessary - // we can allocate a another buffer - proc_opts.args = shell_build_argv(cmd, extra_shell_arg); - proc_opts.file = proc_opts.args[0]; - proc_opts.exit_cb = exit_cb; - // Initialize libuv structures - proc_opts.stdio = proc_stdio; - proc_opts.stdio_count = 3; - // Hide window on Windows :) - proc_opts.flags = UV_PROCESS_WINDOWS_HIDE; - proc_opts.cwd = NULL; - proc_opts.env = NULL; - - // The default is to inherit all standard file descriptors(this will change - // when the UI is moved to an external process) - proc_stdio[0].flags = UV_INHERIT_FD; - proc_stdio[0].data.fd = 0; - proc_stdio[1].flags = UV_INHERIT_FD; - proc_stdio[1].data.fd = 1; - proc_stdio[2].flags = UV_INHERIT_FD; - proc_stdio[2].data.fd = 2; - - if (opts & (kShellOptHideMess | kShellOptExpand)) { - // Ignore the shell stdio(redirects to /dev/null on unixes) - proc_stdio[0].flags = UV_IGNORE; - proc_stdio[1].flags = UV_IGNORE; - proc_stdio[2].flags = UV_IGNORE; - } else { - State = EXTERNCMD; - - if (opts & kShellOptWrite) { - // Write from the current buffer into the process stdin - uv_pipe_init(uv_default_loop(), &proc_stdin, 0); - write_req.data = &pdata; - proc_stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - proc_stdio[0].data.stream = (uv_stream_t *)&proc_stdin; - } - - if (opts & kShellOptRead) { - // Read from the process stdout into the current buffer - uv_pipe_init(uv_default_loop(), &proc_stdout, 0); - proc_stdout.data = &pdata; - proc_stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - proc_stdio[1].data.stream = (uv_stream_t *)&proc_stdout; - ga_init(&pdata.ga, 1, BUFFER_LENGTH); - } - } - - if (uv_spawn(uv_default_loop(), &proc, &proc_opts)) { - // Failed, probably due to `sh` not being executable - if (!emsg_silent) { - MSG_PUTS(_("\nCannot execute shell ")); - msg_outtrans(p_sh); - msg_putchar('\n'); - } - - return proc_cleanup_exit(&pdata, &proc_opts, opts); - } - - // Assign the flag address after `proc` is initialized by `uv_spawn` - proc.data = &pdata; - - if (opts & kShellOptWrite) { - // Queue everything for writing to the shell stdin - write_selection(&write_req); - expected_exits++; - } - - if (opts & kShellOptRead) { - // Start the read stream for the shell stdout - uv_read_start((uv_stream_t *)&proc_stdout, alloc_cb, read_cb); - expected_exits++; - } - - // Keep running the loop until all three handles are completely closed - while (pdata.exited < expected_exits) { - uv_run(uv_default_loop(), UV_RUN_ONCE); - - if (got_int) { - // Forward SIGINT to the shell - // TODO(tarruda): for now this is only needed if the terminal is in raw - // mode, but when the UI is externalized we'll also need it, so leave it - // here - uv_process_kill(&proc, SIGINT); - got_int = false; - } - } - - if (opts & kShellOptRead) { - if (pdata.ga.ga_len > 0) { - // If there's an unfinished line in the growable array, append it now. - append_ga_line(&pdata.ga); - // remember that the NL was missing - curbuf->b_no_eol_lnum = curwin->w_cursor.lnum; - } else { - curbuf->b_no_eol_lnum = 0; - } - ga_clear(&pdata.ga); - } - - if (opts & kShellOptWrite) { - free(pdata.wbuffer); - } - - return proc_cleanup_exit(&pdata, &proc_opts, opts); -} - -static int tokenize(char_u *str, char **argv) -{ - int argc = 0, len; - char_u *p = str; - - while (*p != NUL) { - len = word_length(p); - - if (argv != NULL) { - // Fill the slot - argv[argc] = xmalloc(len + 1); - memcpy(argv[argc], p, len); - argv[argc][len] = NUL; - } - - argc++; - p += len; - p = skipwhite(p); - } - - return argc; -} - -static int word_length(char_u *str) -{ - char_u *p = str; - bool inquote = false; - int length = 0; - - // Move `p` to the end of shell word by advancing the pointer while it's - // inside a quote or it's a non-whitespace character - while (*p && (inquote || (*p != ' ' && *p != TAB))) { - if (*p == '"') { - // Found a quote character, switch the `inquote` flag - inquote = !inquote; - } - - p++; - length++; - } - - return length; -} - -/// To remain compatible with the old implementation(which forked a process -/// for writing) the entire text is copied to a temporary buffer before the -/// event loop starts. If we don't(by writing in chunks returned by `ml_get`) -/// the buffer being modified might get modified by reading from the process -/// before we finish writing. -static void write_selection(uv_write_t *req) -{ - ProcessData *pdata = (ProcessData *)req->data; - // TODO(tarruda): use a static buffer for up to a limit(BUFFER_LENGTH) and - // only after filled we should start allocating memory(skip unnecessary - // allocations for small writes) - int buflen = BUFFER_LENGTH; - pdata->wbuffer = (char *)xmalloc(buflen); - uv_buf_t uvbuf; - linenr_T lnum = curbuf->b_op_start.lnum; - int off = 0; - int written = 0; - char_u *lp = ml_get(lnum); - int l; - int len; - - for (;;) { - l = strlen((char *)lp + written); - if (l == 0) { - len = 0; - } else if (lp[written] == NL) { - // NL -> NUL translation - len = 1; - if (off + len >= buflen) { - // Resize the buffer - buflen *= 2; - pdata->wbuffer = xrealloc(pdata->wbuffer, buflen); - } - pdata->wbuffer[off++] = NUL; - } else { - char_u *s = vim_strchr(lp + written, NL); - len = s == NULL ? l : s - (lp + written); - while (off + len >= buflen) { - // Resize the buffer - buflen *= 2; - pdata->wbuffer = xrealloc(pdata->wbuffer, buflen); - } - memcpy(pdata->wbuffer + off, lp + written, len); - off += len; - } - if (len == l) { - // Finished a line, add a NL, unless this line - // should not have one. - // FIXME need to make this more readable - if (lnum != curbuf->b_op_end.lnum - || !curbuf->b_p_bin - || (lnum != curbuf->b_no_eol_lnum - && (lnum != - curbuf->b_ml.ml_line_count - || curbuf->b_p_eol))) { - if (off + 1 >= buflen) { - // Resize the buffer - buflen *= 2; - pdata->wbuffer = xrealloc(pdata->wbuffer, buflen); - } - pdata->wbuffer[off++] = NL; - } - ++lnum; - if (lnum > curbuf->b_op_end.lnum) { - break; - } - lp = ml_get(lnum); - written = 0; - } else if (len > 0) { - written += len; - } - } - - uvbuf.base = pdata->wbuffer; - uvbuf.len = off; - - uv_write(req, pdata->shell_stdin, &uvbuf, 1, write_cb); -} - -// "Allocates" a buffer for reading from the shell stdout. -static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) -{ - ProcessData *pdata = (ProcessData *)handle->data; - - if (pdata->reading) { - buf->len = 0; - return; - } - - buf->base = pdata->rbuffer; - buf->len = BUFFER_LENGTH; - // Avoid `alloc_cb`, `alloc_cb` sequences on windows - pdata->reading = true; -} - -static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) -{ - // TODO(tarruda): avoid using a growable array for this, refactor the - // algorithm to call `ml_append` directly(skip unecessary copies/resizes) - int i; - ProcessData *pdata = (ProcessData *)stream->data; - - if (cnt <= 0) { - if (cnt != UV_ENOBUFS) { - uv_read_stop(stream); - uv_close((uv_handle_t *)stream, NULL); - pdata->exited++; - } - return; - } - - for (i = 0; i < cnt; ++i) { - if (pdata->rbuffer[i] == NL) { - // Insert the line - append_ga_line(&pdata->ga); - } else if (pdata->rbuffer[i] == NUL) { - // Translate NUL to NL - ga_append(&pdata->ga, NL); - } else { - // buffer data into the grow array - ga_append(&pdata->ga, pdata->rbuffer[i]); - } - } - - windgoto(msg_row, msg_col); - cursor_on(); - out_flush(); - - pdata->reading = false; -} - -static void write_cb(uv_write_t *req, int status) -{ - ProcessData *pdata = (ProcessData *)req->data; - uv_close((uv_handle_t *)pdata->shell_stdin, NULL); - pdata->exited++; -} - -static int proc_cleanup_exit(ProcessData *proc_data, - uv_process_options_t *proc_opts, - int shellopts) -{ - if (proc_data->exited) { - if (!emsg_silent && proc_data->exit_status != 0 && - !(shellopts & kShellOptSilent)) { - MSG_PUTS(_("\nshell returned ")); - msg_outnum((int64_t)proc_data->exit_status); - msg_putchar('\n'); - } - } - - State = proc_data->old_state; - - if (proc_data->old_mode == TMODE_RAW) { - // restore mode - settmode(TMODE_RAW); - } - - signal_accept_deadly(); - - // Release argv memory - shell_free_argv(proc_opts->args); - - return proc_data->exit_status; -} - -static void exit_cb(uv_process_t *proc, int64_t status, int term_signal) -{ - ProcessData *data = (ProcessData *)proc->data; - data->exited++; - data->exit_status = status; - uv_close((uv_handle_t *)proc, NULL); -} diff --git a/src/os/shell.h b/src/os/shell.h deleted file mode 100644 index 776c36d384..0000000000 --- a/src/os/shell.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef NEOVIM_OS_SHELL_H -#define NEOVIM_OS_SHELL_H - -#include <stdbool.h> - -#include "types.h" - -// Flags for mch_call_shell() second argument -typedef enum { - kShellOptFilter = 1, ///< filtering text - kShellOptExpand = 2, ///< expanding wildcards - kShellOptCooked = 4, ///< set term to cooked mode - kShellOptDoOut = 8, ///< redirecting output - kShellOptSilent = 16, ///< don't print error returned by command - kShellOptRead = 32, ///< read lines and insert into buffer - kShellOptWrite = 64, ///< write lines from buffer - kShellOptHideMess = 128, ///< previously a global variable from os_unix.c -} ShellOpts; - -/// Builds the argument vector for running the shell configured in `sh` -/// ('shell' option), optionally with a command that will be passed with `shcf` -/// ('shellcmdflag'). -/// -/// @param cmd Command string. If NULL it will run an interactive shell. -/// @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_arg); - -/// Releases the memory allocated by `shell_build_argv`. -/// -/// @param argv The argument vector. -void shell_free_argv(char **argv); - -/// Calls the user shell for running a command, interactive session or -/// wildcard expansion. It uses the shell set in the `sh` option. -/// -/// @param cmd The command to be executed. If NULL it will run an interactive -/// shell -/// @param opts Various options that control how the shell will work -/// @param extra_shell_arg Extra argument to be passed to the shell -int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg); - -#endif // NEOVIM_OS_SHELL_H - diff --git a/src/os/signal.c b/src/os/signal.c deleted file mode 100644 index d3bedad09c..0000000000 --- a/src/os/signal.c +++ /dev/null @@ -1,163 +0,0 @@ -#include <stdbool.h> - -#include <uv.h> - -#include "types.h" -#include "vim.h" -#include "globals.h" -#include "memline.h" -#include "eval.h" -#include "term.h" -#include "memory.h" -#include "misc1.h" -#include "misc2.h" -#include "os/event_defs.h" -#include "os/event.h" -#include "os/signal.h" - -static uv_signal_t sint, spipe, shup, squit, sterm, swinch; -#ifdef SIGPWR -static uv_signal_t spwr; -#endif - -static bool rejecting_deadly; -static char * signal_name(int signum); -static void deadly_signal(int signum); -static void signal_cb(uv_signal_t *, int signum); - -void signal_init() -{ - uv_signal_init(uv_default_loop(), &sint); - uv_signal_init(uv_default_loop(), &spipe); - uv_signal_init(uv_default_loop(), &shup); - uv_signal_init(uv_default_loop(), &squit); - uv_signal_init(uv_default_loop(), &sterm); - uv_signal_init(uv_default_loop(), &swinch); - uv_signal_start(&sint, signal_cb, SIGINT); - uv_signal_start(&spipe, signal_cb, SIGPIPE); - uv_signal_start(&shup, signal_cb, SIGHUP); - uv_signal_start(&squit, signal_cb, SIGQUIT); - uv_signal_start(&sterm, signal_cb, SIGTERM); - uv_signal_start(&swinch, signal_cb, SIGWINCH); -#ifdef SIGPWR - uv_signal_init(uv_default_loop(), &spwr); - uv_signal_start(&spwr, signal_cb, SIGPWR); -#endif -} - -void signal_stop() -{ - uv_signal_stop(&sint); - uv_signal_stop(&spipe); - uv_signal_stop(&shup); - uv_signal_stop(&squit); - uv_signal_stop(&sterm); - uv_signal_stop(&swinch); -#ifdef SIGPWR - uv_signal_stop(&spwr); -#endif -} - -void signal_reject_deadly() -{ - rejecting_deadly = true; -} - -void signal_accept_deadly() -{ - rejecting_deadly = false; -} - -void signal_handle(Event event) -{ - int signum = event.data.signum; - - switch (signum) { - case SIGINT: - got_int = true; - break; -#ifdef SIGPWR - case SIGPWR: - // Signal of a power failure(eg batteries low), flush the swap files to - // be safe - ml_sync_all(false, false); - break; -#endif - case SIGPIPE: - // Ignore - break; - case SIGWINCH: - shell_resized(); - break; - case SIGTERM: - case SIGQUIT: - case SIGHUP: - if (!rejecting_deadly) { - deadly_signal(signum); - } - break; - default: - fprintf(stderr, "Invalid signal %d", signum); - break; - } -} - -static char * signal_name(int signum) -{ - switch (signum) { - case SIGINT: - return "SIGINT"; -#ifdef SIGPWR - case SIGPWR: - return "SIGPWR"; -#endif - case SIGPIPE: - return "SIGPIPE"; - case SIGWINCH: - return "SIGWINCH"; - case SIGTERM: - return "SIGTERM"; - case SIGQUIT: - return "SIGQUIT"; - case SIGHUP: - return "SIGHUP"; - default: - return "Unknown"; - } -} - -// This function handles deadly signals. -// It tries to preserve any swap files and exit properly. -// (partly from Elvis). -// NOTE: Avoid unsafe functions, such as allocating memory, they can result in -// a deadlock. -static void deadly_signal(int signum) -{ - // Set the v:dying variable. - set_vim_var_nr(VV_DYING, 1); - - snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\n", - signal_name(signum)); - - // Preserve files and exit. - preserve_exit(); -} - -static void signal_cb(uv_signal_t *handle, int signum) -{ - if (rejecting_deadly) { - if (signum == SIGINT) { - got_int = true; - } - - return; - } - - Event event = { - .type = kEventSignal, - .data = { - .signum = signum - } - }; - event_push(event); -} diff --git a/src/os/signal.h b/src/os/signal.h deleted file mode 100644 index 79fdc8d79c..0000000000 --- a/src/os/signal.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef NEOVIM_OS_SIGNAL_H -#define NEOVIM_OS_SIGNAL_H - -#include "os/event_defs.h" - -void signal_init(void); -void signal_stop(void); -void signal_accept_deadly(void); -void signal_reject_deadly(void); -void signal_handle(Event event); - -#endif // NEOVIM_OS_SIGNAL_H - diff --git a/src/os/time.c b/src/os/time.c deleted file mode 100644 index 1dc7ca68d4..0000000000 --- a/src/os/time.c +++ /dev/null @@ -1,86 +0,0 @@ -#include <stdint.h> -#include <stdbool.h> -#include <sys/time.h> - -#include <uv.h> - -#include "os/time.h" -#include "vim.h" -#include "term.h" - -static uv_mutex_t delay_mutex; -static uv_cond_t delay_cond; - -static void microdelay(uint64_t ms); - -void time_init() -{ - uv_mutex_init(&delay_mutex); - uv_cond_init(&delay_cond); -} - -void os_delay(uint64_t milliseconds, bool ignoreinput) -{ - os_microdelay(milliseconds * 1000, ignoreinput); -} - -void os_microdelay(uint64_t microseconds, bool ignoreinput) -{ - int old_tmode; - - if (ignoreinput) { - // Go to cooked mode without echo, to allow SIGINT interrupting us - // here - old_tmode = curr_tmode; - - if (curr_tmode == TMODE_RAW) - settmode(TMODE_SLEEP); - - microdelay(microseconds); - - settmode(old_tmode); - } else { - microdelay(microseconds); - } -} - -static void microdelay(uint64_t microseconds) -{ - uint64_t hrtime; - int64_t ns = microseconds * 1000; // convert to nanoseconds - - uv_mutex_lock(&delay_mutex); - - while (ns > 0) { - hrtime = uv_hrtime(); - if (uv_cond_timedwait(&delay_cond, &delay_mutex, ns) == UV_ETIMEDOUT) - break; - ns -= uv_hrtime() - hrtime; - } - - uv_mutex_unlock(&delay_mutex); -} - -struct tm *os_localtime_r(const time_t *clock, struct tm *result) -{ -#ifdef UNIX - // POSIX provides localtime_r() as a thread-safe version of localtime(). - return localtime_r(clock, result); -#else - // Windows version of localtime() is thread-safe. - // See http://msdn.microsoft.com/en-us/library/bf12f0hc%28VS.80%29.aspx - struct tm *local_time = localtime(clock); // NOLINT - *result = *local_time; -return result; -#endif -} - -struct tm *os_get_localtime(struct tm *result) -{ - struct timeval tv; - if (gettimeofday(&tv, NULL) < 0) { - return NULL; - } - - return os_localtime_r(&tv.tv_sec, result); -} diff --git a/src/os/time.h b/src/os/time.h deleted file mode 100644 index ef795d03be..0000000000 --- a/src/os/time.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef NEOVIM_OS_TIME_H -#define NEOVIM_OS_TIME_H - -#include <stdint.h> -#include <stdbool.h> - -/// Initializes the time module -void time_init(void); - -/// Sleeps for a certain amount of milliseconds -/// -/// @param milliseconds Number of milliseconds to sleep -/// @param ignoreinput If true, allow a SIGINT to interrupt us -void os_delay(uint64_t milliseconds, bool ignoreinput); - -/// Sleeps for a certain amount of microseconds -/// -/// @param microseconds Number of microseconds to sleep -/// @param ignoreinput If true, allow a SIGINT to interrupt us -void os_microdelay(uint64_t microseconds, bool ignoreinput); - -/// Portable version of POSIX localtime_r() -/// -/// @return NULL in case of error -struct tm *os_localtime_r(const time_t *clock, struct tm *result); - -/// Obtains the current UNIX timestamp and adjusts it to local time -/// -/// @param result Pointer to a 'struct tm' where the result should be placed -/// @return A pointer to a 'struct tm' in the current time zone (the 'result' -/// argument) or NULL in case of error -struct tm *os_get_localtime(struct tm *result); - -#endif // NEOVIM_OS_TIME_H - diff --git a/src/os/users.c b/src/os/users.c deleted file mode 100644 index e7b362637b..0000000000 --- a/src/os/users.c +++ /dev/null @@ -1,87 +0,0 @@ -// users.c -- operating system user information - -#include <uv.h> - -#include "os/os.h" -#include "garray.h" -#include "misc2.h" -#ifdef HAVE_PWD_H -# include <pwd.h> -#endif - -// Initialize users garray and fill it with os usernames. -// Return Ok for success, FAIL for failure. -int os_get_usernames(garray_T *users) -{ - if (users == NULL) { - return FAIL; - } - ga_init(users, sizeof(char *), 20); - -# if defined(HAVE_GETPWENT) && defined(HAVE_PWD_H) - char *user; - struct passwd *pw; - - setpwent(); - while ((pw = getpwent()) != NULL) { - // pw->pw_name shouldn't be NULL but just in case... - if (pw->pw_name != NULL) { - ga_grow(users, 1); - user = (char *)vim_strsave((char_u*)pw->pw_name); - if (user == NULL) { - return FAIL; - } - ((char **)(users->ga_data))[users->ga_len++] = user; - } - } - endpwent(); -# endif - - return OK; -} - -// Insert user name in s[len]. -// Return OK if a name found. -int os_get_user_name(char *s, size_t len) -{ - return os_get_uname(getuid(), s, len); -} - -// Insert user name for "uid" in s[len]. -// Return OK if a name found. -// If the name is not found, write the uid into s[len] and return FAIL. -int os_get_uname(uid_t uid, char *s, size_t len) -{ -#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) - struct passwd *pw; - - if ((pw = getpwuid(uid)) != NULL - && pw->pw_name != NULL && *(pw->pw_name) != NUL) { - vim_strncpy((char_u *)s, (char_u *)pw->pw_name, len - 1); - return OK; - } -#endif - snprintf(s, len, "%d", (int)uid); - return FAIL; // a number is not a name -} - -// Returns the user directory for the given username. -// The caller has to free() the returned string. -// If the username is not found, NULL is returned. -char *os_get_user_directory(const char *name) -{ -#if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H) - struct passwd *pw; - if (name == NULL) { - return NULL; - } - pw = getpwnam(name); - if (pw != NULL) { - // save the string from the static passwd entry into malloced memory - char *user_directory = (char *)vim_strsave((char_u *)pw->pw_dir); - return user_directory; - } -#endif - return NULL; -} - diff --git a/src/os/uv_helpers.c b/src/os/uv_helpers.c deleted file mode 100644 index 62b021de5e..0000000000 --- a/src/os/uv_helpers.c +++ /dev/null @@ -1,70 +0,0 @@ -#include <uv.h> - -#include "os/uv_helpers.h" -#include "vim.h" -#include "memory.h" - -/// Common structure that will always be assigned to the `data` field of -/// libuv handles. It has fields for many types of pointers, and allow a single -/// handle to contain data from many sources -typedef struct { - WStream *wstream; - RStream *rstream; - Job *job; -} HandleData; - -static HandleData *init(uv_handle_t *handle); - -RStream *handle_get_rstream(uv_handle_t *handle) -{ - RStream *rv = init(handle)->rstream; - assert(rv != NULL); - return rv; -} - -void handle_set_rstream(uv_handle_t *handle, RStream *rstream) -{ - init(handle)->rstream = rstream; -} - -WStream *handle_get_wstream(uv_handle_t *handle) -{ - WStream *rv = init(handle)->wstream; - assert(rv != NULL); - return rv; -} - -void handle_set_wstream(uv_handle_t *handle, WStream *wstream) -{ - HandleData *data = init(handle); - data->wstream = wstream; -} - -Job *handle_get_job(uv_handle_t *handle) -{ - Job *rv = init(handle)->job; - assert(rv != NULL); - return rv; -} - -void handle_set_job(uv_handle_t *handle, Job *job) -{ - init(handle)->job = job; -} - -static HandleData *init(uv_handle_t *handle) -{ - HandleData *rv; - - if (handle->data == NULL) { - rv = xmalloc(sizeof(HandleData)); - rv->rstream = NULL; - rv->wstream = NULL; - rv->job = NULL; - handle->data = rv; - } else { - rv = handle->data; - } - - return rv; -} diff --git a/src/os/uv_helpers.h b/src/os/uv_helpers.h deleted file mode 100644 index 9d4cea30b2..0000000000 --- a/src/os/uv_helpers.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef NEOVIM_OS_UV_HELPERS_H -#define NEOVIM_OS_UV_HELPERS_H - -#include <uv.h> - -#include "os/wstream_defs.h" -#include "os/rstream_defs.h" -#include "os/job_defs.h" - -/// Gets the RStream instance associated with a libuv handle -/// -/// @param handle libuv handle -/// @return the RStream pointer -RStream *handle_get_rstream(uv_handle_t *handle); - -/// Associates a RStream instance with a libuv handle -/// -/// @param handle libuv handle -/// @param rstream the RStream pointer -void handle_set_rstream(uv_handle_t *handle, RStream *rstream); - -/// Gets the WStream instance associated with a libuv handle -/// -/// @param handle libuv handle -/// @return the WStream pointer -WStream *handle_get_wstream(uv_handle_t *handle); - -/// Associates a WStream instance with a libuv handle -/// -/// @param handle libuv handle -/// @param wstream the WStream pointer -void handle_set_wstream(uv_handle_t *handle, WStream *wstream); - -/// Gets the Job instance associated with a libuv handle -/// -/// @param handle libuv handle -/// @return the Job pointer -Job *handle_get_job(uv_handle_t *handle); - -/// Associates a Job instance with a libuv handle -/// -/// @param handle libuv handle -/// @param job the Job pointer -void handle_set_job(uv_handle_t *handle, Job *job); - -#endif // NEOVIM_OS_UV_HELPERS_H - diff --git a/src/os/wstream.c b/src/os/wstream.c deleted file mode 100644 index 0b289e80f5..0000000000 --- a/src/os/wstream.c +++ /dev/null @@ -1,117 +0,0 @@ -#include <stdint.h> -#include <stdbool.h> - -#include <uv.h> - -#include "os/uv_helpers.h" -#include "os/wstream.h" -#include "os/wstream_defs.h" -#include "vim.h" -#include "memory.h" - -struct wstream { - uv_stream_t *stream; - // Memory currently used by pending buffers - uint32_t curmem; - // Maximum memory used by this instance - uint32_t maxmem; - // Number of pending requests - uint32_t pending_reqs; - bool freed; -}; - -typedef struct { - WStream *wstream; - // Buffer containing data to be written - char *buffer; - // Size of the buffer - uint32_t length; - // If it's our responsibility to free the buffer - bool free; -} WriteData; - -static void write_cb(uv_write_t *req, int status); - -WStream * wstream_new(uint32_t maxmem) -{ - WStream *rv = xmalloc(sizeof(WStream)); - rv->maxmem = maxmem; - rv->stream = NULL; - rv->curmem = 0; - rv->pending_reqs = 0; - rv->freed = false; - - return rv; -} - -void wstream_free(WStream *wstream) -{ - if (!wstream->pending_reqs) { - free(wstream); - } else { - wstream->freed = true; - } -} - -void wstream_set_stream(WStream *wstream, uv_stream_t *stream) -{ - handle_set_wstream((uv_handle_t *)stream, wstream); - wstream->stream = stream; -} - -bool wstream_write(WStream *wstream, char *buffer, uint32_t length, bool free) -{ - WriteData *data; - uv_buf_t uvbuf; - uv_write_t *req; - - if (wstream->freed) { - // Don't accept write requests after the WStream instance was freed - return false; - } - - if (wstream->curmem + length > wstream->maxmem) { - return false; - } - - if (free) { - // We should only account for buffers that are ours to free - wstream->curmem += length; - } - - data = xmalloc(sizeof(WriteData)); - data->wstream = wstream; - data->buffer = buffer; - data->length = length; - data->free = free; - req = xmalloc(sizeof(uv_write_t)); - req->data = data; - uvbuf.base = buffer; - uvbuf.len = length; - wstream->pending_reqs++; - uv_write(req, wstream->stream, &uvbuf, 1, write_cb); - - return true; -} - -static void write_cb(uv_write_t *req, int status) -{ - WriteData *data = req->data; - - free(req); - - if (data->free) { - // Free the data written to the stream - free(data->buffer); - data->wstream->curmem -= data->length; - } - - data->wstream->pending_reqs--; - if (data->wstream->freed && data->wstream->pending_reqs == 0) { - // Last pending write, free the wstream; - free(data->wstream); - } - - free(data); -} - diff --git a/src/os/wstream.h b/src/os/wstream.h deleted file mode 100644 index 4a557ffd9f..0000000000 --- a/src/os/wstream.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef NEOVIM_OS_WSTREAM_H -#define NEOVIM_OS_WSTREAM_H - -#include <stdint.h> -#include <stdbool.h> -#include <uv.h> - -#include "os/wstream_defs.h" - -/// Creates a new WStream instance. A WStream encapsulates all the boilerplate -/// necessary for writing to a libuv stream. -/// -/// @param maxmem Maximum amount memory used by this `WStream` instance. -/// @return The newly-allocated `WStream` instance -WStream * wstream_new(uint32_t maxmem); - -/// Frees all memory allocated for a WStream instance -/// -/// @param wstream The `WStream` instance -void wstream_free(WStream *wstream); - -/// Sets the underlying `uv_stream_t` instance -/// -/// @param wstream The `WStream` instance -/// @param stream The new `uv_stream_t` instance -void wstream_set_stream(WStream *wstream, uv_stream_t *stream); - -/// 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`. -/// -/// @param wstream The `WStream` instance -/// @param buffer The buffer which contains data to be written -/// @param length Number of bytes that should be written from `buffer` -/// @param free If true, `buffer` will be freed after the write is complete -/// @return true if the data was successfully queued, false otherwise. -bool wstream_write(WStream *wstream, char *buffer, uint32_t length, bool free); - -#endif // NEOVIM_OS_WSTREAM_H - diff --git a/src/os/wstream_defs.h b/src/os/wstream_defs.h deleted file mode 100644 index 59d57365fa..0000000000 --- a/src/os/wstream_defs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef NEOVIM_OS_WSTREAM_DEFS_H -#define NEOVIM_OS_WSTREAM_DEFS_H - -typedef struct wstream WStream; - -#endif // NEOVIM_OS_WSTREAM_DEFS_H - |