From 1f2b35860f755513a58c1d010f082d946c889b47 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 7 Dec 2017 20:53:02 -0500 Subject: mac: Set $LANG based on the system locale Unix's typical locale-related environment variables aren't always set appropriately on a Mac. Instead of relying on them, query the locale information using Mac specific APIs and then set $LANG appropriately for the rest of nvim. Closes #5873 --- src/nvim/os/lang.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/nvim/os/lang.h | 7 +++++++ 2 files changed, 47 insertions(+) create mode 100644 src/nvim/os/lang.c create mode 100644 src/nvim/os/lang.h (limited to 'src/nvim/os') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c new file mode 100644 index 0000000000..f0bbf4b1cb --- /dev/null +++ b/src/nvim/os/lang.c @@ -0,0 +1,40 @@ +#ifdef __APPLE__ +# define Boolean CFBoolean // Avoid conflict with API's Boolean +# include +# include +# undef Boolean +#endif + +#ifdef HAVE_LOCALE_H +# include +#endif +#include "nvim/os/os.h" + +void lang_init(void) +{ +#ifdef __APPLE__ + if (os_getenv("LANG") == NULL) { + CFLocaleRef cf_locale = CFLocaleCopyCurrent(); + CFTypeRef cf_lang_region = CFLocaleGetValue(cf_locale, + kCFLocaleIdentifier); + CFRetain(cf_lang_region); + CFRelease(cf_locale); + + const char *lang_region = CFStringGetCStringPtr(cf_lang_region, + kCFStringEncodingUTF8); + if (lang_region) { + os_setenv("LANG", lang_region, true); + } else { + char buf[20] = { 0 }; + if (CFStringGetCString(cf_lang_region, buf, 20, + kCFStringEncodingUTF8)) { + os_setenv("LANG", lang_region, true); + } + } + CFRelease(cf_lang_region); +# ifdef HAVE_LOCALE_H + setlocale(LC_ALL, ""); +# endif + } +#endif +} diff --git a/src/nvim/os/lang.h b/src/nvim/os/lang.h new file mode 100644 index 0000000000..f60e064f57 --- /dev/null +++ b/src/nvim/os/lang.h @@ -0,0 +1,7 @@ +#ifndef NVIM_OS_LANG_H +#define NVIM_OS_LANG_H + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/lang.h.generated.h" +#endif +#endif // NVIM_OS_LANG_H -- cgit From 6203c23449cfdf8fb09c33d3ab267703d57123fa Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 12 Dec 2017 01:31:15 +0100 Subject: pty_process_unix: _exit() on execvp() failure Mostly cargo-culting based on a reading of the manpages, interwebs, and the Vim source. --- src/nvim/os/pty_process_unix.c | 8 ++++---- src/nvim/os/signal.c | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 53301e4b53..855ca2ae47 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -72,8 +72,7 @@ int pty_process_spawn(PtyProcess *ptyproc) ELOG("forkpty failed: %s", strerror(errno)); return status; } else if (pid == 0) { - init_child(ptyproc); - abort(); + init_child(ptyproc); // never returns } // make sure the master file descriptor is non blocking @@ -163,14 +162,15 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL Process *proc = (Process *)ptyproc; if (proc->cwd && os_chdir(proc->cwd) != 0) { - fprintf(stderr, "chdir failed: %s\n", strerror(errno)); + ELOG("chdir failed: %s", strerror(errno)); return; } char *prog = ptyproc->process.argv[0]; setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); execvp(prog, ptyproc->process.argv); - fprintf(stderr, "execvp failed: %s: %s\n", strerror(errno), prog); + ELOG("execvp failed: %s: %s", strerror(errno), prog); + _exit(122); // 122 is EXEC_FAILED in the Vim source. } static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index fd6d3b32e4..732be072e1 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -10,6 +10,7 @@ #endif #include "nvim/ascii.h" +#include "nvim/log.h" #include "nvim/vim.h" #include "nvim/globals.h" #include "nvim/memline.h" @@ -162,7 +163,7 @@ static void on_signal(SignalWatcher *handle, int signum, void *data) } break; default: - fprintf(stderr, "Invalid signal %d", signum); + ELOG("invalid signal: %d", signum); break; } } -- cgit From d5bce42b524708a54243658e87b1e3bd9c7acdf3 Mon Sep 17 00:00:00 2001 From: Michael Schupikov Date: Sat, 23 Sep 2017 09:56:44 +0200 Subject: vim-patch:8.0.0074 Problem: Cannot make Vim fail on an internal error. Solution: Add IEMSG() and IEMSG2(). (Domenique Pelle) Avoid reporting an internal error without mentioning where. https://github.com/vim/vim/commit/95f096030ed1a8afea028f2ea295d6f6a70f466f Signed-off-by: Michael Schupikov --- src/nvim/os/env.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index de0cd10d9c..999fcd434a 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -887,7 +887,7 @@ bool os_setenv_append_path(const char *fname) # define MAX_ENVPATHLEN INT_MAX #endif if (!path_is_absolute_path((char_u *)fname)) { - EMSG2(_(e_intern2), "os_setenv_append_path()"); + internal_error("os_setenv_append_path()"); return false; } const char *tail = (char *)path_tail_with_sep((char_u *)fname); -- cgit From c10ae4bc8548ca170876e00489fb5d907801e553 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 3 Jan 2018 03:31:10 +0300 Subject: os/fileio: Fix some flag names in file_* functions documentation --- src/nvim/os/fileio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 5d68473982..d294f9139b 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -39,9 +39,9 @@ /// @param[in] fname File name to open. /// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and /// writing to the file at once is not supported, so either -/// FILE_WRITE_ONLY or FILE_READ_ONLY is required. +/// kFileWriteOnly or kFileReadOnly is required. /// @param[in] mode Permissions for the newly created file (ignored if flags -/// does not have FILE_CREATE\*). +/// does not have kFileCreate\*). /// /// @return Error code (@see os_strerror()) or 0. int file_open(FileDescriptor *const ret_fp, const char *const fname, @@ -120,7 +120,7 @@ int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr) /// @param[in] fname File name to open. /// @param[in] flags Flags, @see FileOpenFlags. /// @param[in] mode Permissions for the newly created file (ignored if flags -/// does not have FILE_CREATE\*). +/// does not have kFileCreate\*). /// /// @return [allocated] Opened file or NULL in case of error. FileDescriptor *file_open_new(int *const error, const char *const fname, -- cgit From bc17ad31dccb446fc24e0f6bb3cb2149ce951ae5 Mon Sep 17 00:00:00 2001 From: lePerdu Date: Wed, 18 Oct 2017 18:27:31 -0400 Subject: os/input.c: parse keycodes in non-string context #7411 cb02137dfac7 had two mistakes, of the same nature: trans_special() must be invoked with in_string=false unless the parsing context is a VimL string. replace_termcodes() and input_enqueue() are low-level mechanisms where VimL strings do not exist. keymap.c: adjust double-quote case to satisfy keymap_spec.lua closes #7410 --- src/nvim/os/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 7d6f2abd7f..405500767d 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -188,7 +188,7 @@ size_t input_enqueue(String keys) uint8_t buf[6] = { 0 }; unsigned int new_size = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true, - true); + false); if (new_size) { new_size = handle_mouse_event(&ptr, buf, new_size); -- cgit From 06994e0e21eb5545aef7c2234f5d1a271865366e Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 17 Jan 2018 19:35:57 +0800 Subject: Fix warning about conversion on mingw64 --- src/nvim/os/pty_process_win.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 3c4839a076..c249536536 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -131,7 +131,7 @@ int pty_process_spawn(PtyProcess *ptyproc) } goto cleanup; } - proc->pid = GetProcessId(process_handle); + proc->pid = (int)GetProcessId(process_handle); if (!RegisterWaitForSingleObject( &ptyproc->finish_wait, -- cgit From 421f2605c003c51ce25d131d444489f63a2ad928 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 17 Jan 2018 19:48:03 +0800 Subject: Fix warning about NULL compare --- src/nvim/os/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index aa28b95c30..5f29a45263 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -990,7 +990,7 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, /// to and return that name in allocated memory. /// Otherwise NULL is returned. char *os_resolve_shortcut(const char *fname) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { HRESULT hr; IPersistFile *ppf = NULL; -- cgit From 2408a05151542da9432bd406c36f92c8bdeef1ec Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 17 Jan 2018 19:54:21 +0800 Subject: Fix warning, read/write have unsigned int count on windows. --- src/nvim/os/fs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 5f29a45263..f3b3871aac 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -461,7 +461,7 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, while (read_bytes != size) { assert(size >= read_bytes); const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes, - size - read_bytes); + IO_SIZE(size - read_bytes)); if (cur_read_bytes > 0) { read_bytes += (size_t)cur_read_bytes; } @@ -564,7 +564,7 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size) while (written_bytes != size) { assert(size >= written_bytes); const ptrdiff_t cur_written_bytes = write(fd, buf + written_bytes, - size - written_bytes); + IO_SIZE(size - written_bytes)); if (cur_written_bytes > 0) { written_bytes += (size_t)cur_written_bytes; } -- cgit From e76c6e2ee8b8a064f1f2a48e3638be6ca07f2f2a Mon Sep 17 00:00:00 2001 From: George Zhao Date: Wed, 17 Jan 2018 20:03:01 +0800 Subject: Fix warning: multi-line comment [-Wcomment] use `:341,355s/: \zs.*/\=string(submatch(0))` --- src/nvim/os/pty_process_win.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index c249536536..b90578edb7 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -339,20 +339,20 @@ static void quote_cmd_arg(char *dest, size_t dest_remaining, const char *src) } // Expected input/output: - // input : hello"world - // output: "hello\"world" - // input : hello""world - // output: "hello\"\"world" - // input : hello\world - // output: hello\world - // input : hello\\world - // output: hello\\world - // input : hello\"world - // output: "hello\\\"world" - // input : hello\\"world - // output: "hello\\\\\"world" - // input : hello world\ - // output: "hello world\\" + // input : 'hello"world' + // output: '"hello\"world"' + // input : 'hello""world' + // output: '"hello\"\"world"' + // input : 'hello\world' + // output: 'hello\world' + // input : 'hello\\world' + // output: 'hello\\world' + // input : 'hello\"world' + // output: '"hello\\\"world"' + // input : 'hello\\"world' + // output: '"hello\\\\\"world"' + // input : 'hello world\' + // output: '"hello world\\"' assert(dest_remaining--); *(dest++) = NUL; -- cgit From 648fed975eb8ddde9c5cbc0f859d06deebf80dd9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 31 Jan 2018 10:25:51 +0100 Subject: os_system(): do not set up input stream for empty string #7951 Test failure: test/functional/eval/system_spec.lua: "works with an empty string" E5677: Error writing input to shell-command: EPIPE ref https://github.com/neovim/neovim/pull/6558#issuecomment-361061035 ref #6554 --- src/nvim/os/shell.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index e32c6e05d2..c205e4b3af 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -189,6 +189,7 @@ static int do_os_system(char **argv, { out_data_decide_throttle(0); // Initialize throttle decider. out_data_ring(NULL, 0); // Initialize output ring-buffer. + bool has_input = (input != NULL && input[0] != '\0'); // the output buffer DynamicBuffer buf = DYNAMIC_BUFFER_INIT; @@ -212,7 +213,7 @@ static int do_os_system(char **argv, MultiQueue *events = multiqueue_new_child(main_loop.events); proc->events = events; proc->argv = argv; - int status = process_spawn(proc, input != NULL, true, true); + int status = process_spawn(proc, has_input, true, true); if (status) { loop_poll_events(&main_loop, 0); // Failed, probably 'shell' is not executable. @@ -231,7 +232,7 @@ static int do_os_system(char **argv, // deal with stream events as fast a possible. It prevents closing the // streams while there's still data in the OS buffer (due to the process // exiting before all data is read). - if (input != NULL) { + if (has_input) { wstream_init(&proc->in, 0); } rstream_init(&proc->out, 0); @@ -240,8 +241,8 @@ static int do_os_system(char **argv, rstream_start(&proc->err, data_cb, &buf); // write the input, if any - if (input) { - WBuffer *input_buffer = wstream_new_buffer((char *) input, len, 1, NULL); + if (has_input) { + WBuffer *input_buffer = wstream_new_buffer((char *)input, len, 1, NULL); if (!wstream_write(&proc->in, input_buffer)) { // couldn't write, stop the process and tell the user about it -- cgit From 2d99b81ab5b7481e20c03376f0f40924daff491f Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 12 Jan 2018 08:32:28 +0100 Subject: shell: use msg_outtrans_len_attr for :!cmd fixes #7830 and #7788 --- src/nvim/os/shell.c | 65 +++++++++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 39 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index c205e4b3af..11e6a76939 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -123,6 +123,9 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args) if (opts & kShellOptRead) { output_ptr = &output; forward_output = false; + } else if (opts & kShellOptDoOut) { + // Caller has already redirected output + forward_output = false; } } @@ -257,11 +260,25 @@ static int do_os_system(char **argv, // busy state. ui_busy_start(); ui_flush(); + if (forward_output) { + msg_sb_eol(); + msg_start(); + msg_no_more = true; + lines_left = -1; + } int exitcode = process_wait(proc, -1, NULL); if (!got_int && out_data_decide_throttle(0)) { // Last chunk of output was skipped; display it now. out_data_ring(NULL, SIZE_MAX); } + if (forward_output) { + // caller should decide if wait_return is invoked + no_wait_return++; + msg_end(); + no_wait_return--; + msg_no_more = false; + } + ui_busy_stop(); // prepare the out parameters if requested @@ -436,47 +453,17 @@ static void out_data_ring(char *output, size_t size) static void out_data_append_to_screen(char *output, size_t remaining, bool new_line) { - static colnr_T last_col = 0; // Column of last row to append to. - - size_t off = 0; - int last_row = (int)Rows - 1; - - while (output != NULL && off < remaining) { - // Found end of line? - if (output[off] == NL) { - // Can we start a new line or do we need to continue the last one? - if (last_col == 0) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - } - screen_puts_len((char_u *)output, (int)off, last_row, last_col, 0); - last_col = 0; - - size_t skip = off + 1; - output += skip; - remaining -= skip; - off = 0; - continue; - } - - // TODO(bfredl): using msg_puts would be better until - // terminal emulation is implemented. - if (output[off] < 0x20) { - output[off] = ' '; - } - - off++; - } + char *p = output, *end = output + remaining; + while (p < end) { + if (*p == '\n' || *p == '\r' || *p == TAB) { + msg_putchar_attr((uint8_t)(*p), 0); + p++; + } else { + int i = *p ? mb_ptr2len_len((char_u *)p, (int)(end-p)) : 1; - if (output != NULL && remaining) { - if (last_col == 0) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); + (void)msg_outtrans_len_attr((char_u *)p, i, 0); + p += i; } - screen_puts_len((char_u *)output, (int)remaining, last_row, last_col, 0); - last_col += (colnr_T)remaining; - } - - if (new_line) { - last_col = 0; } ui_flush(); -- cgit From 35a789278128bfe5d28e4c61ac7fa727042fd30a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 7 Feb 2018 01:13:51 +0100 Subject: lint, minor cleanup --- src/nvim/os/shell.c | 8 ++------ src/nvim/os/time.c | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 11e6a76939..5b3cb64a4d 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -576,14 +576,10 @@ static void read_input(DynamicBuffer *buf) 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 - && curbuf->b_p_fixeol) + || (!curbuf->b_p_bin && curbuf->b_p_fixeol) || (lnum != curbuf->b_no_eol_lnum - && (lnum != - curbuf->b_ml.ml_line_count - || curbuf->b_p_eol))) { + && (lnum != curbuf->b_ml.ml_line_count || curbuf->b_p_eol))) { dynamic_buffer_ensure(buf, buf->len + 1); buf->data[buf->len++] = NL; } diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index c471352c02..290d421acc 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -38,33 +38,33 @@ uint64_t os_hrtime(void) return uv_hrtime(); } -/// Sleeps for a certain amount of milliseconds. +/// Sleeps for `ms` milliseconds. /// -/// @param milliseconds Number of milliseconds to sleep +/// @param ms Number of milliseconds to sleep /// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt. -void os_delay(uint64_t milliseconds, bool ignoreinput) +void os_delay(uint64_t ms, bool ignoreinput) { if (ignoreinput) { - if (milliseconds > INT_MAX) { - milliseconds = INT_MAX; + if (ms > INT_MAX) { + ms = INT_MAX; } - LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)ms, got_int); } else { - os_microdelay(milliseconds * 1000u, ignoreinput); + os_microdelay(ms * 1000u, ignoreinput); } } -/// Sleeps for a certain amount of microseconds. +/// Sleeps for `us` microseconds. /// -/// @param ms Number of microseconds to sleep. +/// @param us Number of microseconds to sleep. /// @param ignoreinput If true, ignore all input (including SIGINT/CTRL-C). /// If false, waiting is aborted on any input. -void os_microdelay(uint64_t ms, bool ignoreinput) +void os_microdelay(uint64_t us, bool ignoreinput) { uint64_t elapsed = 0u; uint64_t base = uv_hrtime(); // Convert microseconds to nanoseconds, or UINT64_MAX on overflow. - const uint64_t ns = (ms < UINT64_MAX / 1000u) ? ms * 1000u : UINT64_MAX; + const uint64_t ns = (us < UINT64_MAX / 1000u) ? us * 1000u : UINT64_MAX; uv_mutex_lock(&delay_mutex); -- cgit From 60ce7d9e0a0606e20a17f90d78e9d8319114273b Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 7 Feb 2018 10:23:19 +0100 Subject: shell: support bell --- src/nvim/os/shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 5b3cb64a4d..166e06b1a4 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -455,7 +455,7 @@ static void out_data_append_to_screen(char *output, size_t remaining, { char *p = output, *end = output + remaining; while (p < end) { - if (*p == '\n' || *p == '\r' || *p == TAB) { + if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) { msg_putchar_attr((uint8_t)(*p), 0); p++; } else { -- cgit From f75c4b39ece7b5f760892c1e18af449c5bb270c7 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 8 Feb 2018 15:11:56 +0100 Subject: shell: handle split-up UTF-8 sequences --- src/nvim/os/shell.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 166e06b1a4..f650a51fe7 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -422,7 +422,7 @@ static void out_data_ring(char *output, size_t size) } if (output == NULL && size == SIZE_MAX) { // Print mode - out_data_append_to_screen(last_skipped, last_skipped_len, true); + out_data_append_to_screen(last_skipped, &last_skipped_len, true); return; } @@ -450,30 +450,40 @@ static void out_data_ring(char *output, size_t size) /// @param output Data to append to screen lines. /// @param remaining Size of data. /// @param new_line If true, next data output will be on a new line. -static void out_data_append_to_screen(char *output, size_t remaining, - bool new_line) +static void out_data_append_to_screen(char *output, size_t *count, + bool eof) { - char *p = output, *end = output + remaining; + char *p = output, *end = output + *count; while (p < end) { if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) { msg_putchar_attr((uint8_t)(*p), 0); p++; } else { + // Note: this is not 100% precise: + // 1. we don't check if received continuation bytes are already invalid + // and we thus do some buffering that could be avoided + // 2. we don't compose chars over buffer boundaries, even if we see an + // incomplete UTF-8 sequence that could be composing with the last + // complete sequence. + // This will be corrected when we switch to vterm based implementation int i = *p ? mb_ptr2len_len((char_u *)p, (int)(end-p)) : 1; + if (!eof && i == 1 && utf8len_tab_zero[*(uint8_t *)p] > (end-p)) { + *count = (size_t)(p - output); + goto end; + } (void)msg_outtrans_len_attr((char_u *)p, i, 0); p += i; } } +end: ui_flush(); } static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { - // We always output the whole buffer, so the buffer can never - // wrap around. size_t cnt; char *ptr = rbuffer_read_ptr(buf, &cnt); @@ -482,12 +492,16 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, // Save the skipped output. If it is the final chunk, we display it later. out_data_ring(ptr, cnt); } else { - out_data_append_to_screen(ptr, cnt, eof); + out_data_append_to_screen(ptr, &cnt, eof); } if (cnt) { rbuffer_consumed(buf, cnt); } + + // Move remaining data to start of buffer, so the buffer can never + // wrap around. + rbuffer_reset(buf); } /// Parses a command string into a sequence of words, taking quotes into -- cgit From d520e2590af5e8d22416e6acbcd161ba1601a532 Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Wed, 28 Feb 2018 19:14:27 -0600 Subject: build/msvc: Add mode_t typedef to win_defs.h --- src/nvim/os/win_defs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/os') diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 8fd2e51f8b..48607845cc 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -60,6 +60,7 @@ #ifdef _MSC_VER typedef SSIZE_T ssize_t; +typedef int mode_t; #endif #ifndef SSIZE_MAX -- cgit From f04b53aa24f7b6f1f4cdcdf08fcdd600472b0bab Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Wed, 28 Feb 2018 19:14:27 -0600 Subject: build/msvc: Move include into unix_defs.h --- src/nvim/os/os_defs.h | 1 - src/nvim/os/unix_defs.h | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 87f8d214bd..923a362b41 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index 5c9daca476..27e64f20cf 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -1,9 +1,10 @@ #ifndef NVIM_OS_UNIX_DEFS_H #define NVIM_OS_UNIX_DEFS_H -// Windows doesn't have unistd.h, so we include it here to avoid numerous -// instances of `#ifdef WIN32'. +// Windows doesn't have the following headers, +// so we include them here to avoid numerous instances of `#ifdef WIN32'. #include +#include // POSIX.1-2008 says that NAME_MAX should be in here #include -- cgit From 60a341a05f2d2a351a172b36e7f01e579e49ab02 Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Wed, 28 Feb 2018 19:14:27 -0600 Subject: build/msvc: Fix standard IO file number definitions With MSVC, STDOUT_FILENO and STDERR_FILENO are defined as function calls instead of constants, meaning they can't be assigned to enum values. The enum was only used in one file, so it has been removed. A definition for STDIN_FILENO has been added that is consistent with the other two definitions. --- src/nvim/os/os_defs.h | 7 ------- src/nvim/os/win_defs.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 923a362b41..f81785675e 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -13,13 +13,6 @@ # include "nvim/os/unix_defs.h" #endif -/// File descriptor number used for standard IO streams -enum { - OS_STDIN_FILENO = STDIN_FILENO, - OS_STDOUT_FILENO = STDOUT_FILENO, - OS_STDERR_FILENO = STDERR_FILENO, -}; - #define BASENAMELEN (NAME_MAX - 5) // Use the system path length if it makes sense. diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 48607845cc..faee06304c 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -45,6 +45,9 @@ # ifndef restrict # define restrict __restrict # endif +# ifndef STDIN_FILENO +# define STDIN_FILENO _fileno(stdin) +# endif # ifndef STDOUT_FILENO # define STDOUT_FILENO _fileno(stdout) # endif -- cgit From fb3667cd16adfb1564ccbe36973f8874afc1524f Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Sun, 4 Mar 2018 17:44:23 -0600 Subject: build/msvc: Remove confusing comment --- src/nvim/os/unix_defs.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index 27e64f20cf..60a2dfa882 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -1,8 +1,6 @@ #ifndef NVIM_OS_UNIX_DEFS_H #define NVIM_OS_UNIX_DEFS_H -// Windows doesn't have the following headers, -// so we include them here to avoid numerous instances of `#ifdef WIN32'. #include #include -- cgit From 8bd1bbcec817443b20870d5220063c363ce7edb8 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 11 Mar 2018 17:23:27 -0400 Subject: Add missing PVS headers to new files --- src/nvim/os/lang.c | 3 +++ src/nvim/os/pty_process_win.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'src/nvim/os') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index f0bbf4b1cb..47c278ee97 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #ifdef __APPLE__ # define Boolean CFBoolean // Avoid conflict with API's Boolean # include diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index b90578edb7..a6774a6275 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #include #include #include -- cgit From 8d90171f8be6b92d7186ca84c42a0e07c2c71908 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 16 Feb 2018 19:00:02 +0100 Subject: jobs: child proc must have a separate process-group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UV_PROCESS_DETACHED compels libuv:uv__process_child_init() to call setsid() in the child just after fork(). That ensures the process and its descendants are grouped in a separate session (and process group). The following jobstart() call correctly groups `sh` and `sleep` in a new session (and process-group), where `sh` is the "session leader" (and process-group leader): :call jobstart(['sh','-c','sleep 60']) SESN PGRP PID PPID Command 30383 30383 30383 3620 │ ├─ -bash 30383 31432 31432 30383 │ │ └─ nvim -u NORC 30383 31432 31433 30383 │ │ ├─ nvim -u NORC 8105 8105 8105 31432 │ │ └─ sh -c sleep 60 8105 8105 8106 8105 │ │ └─ sleep 60 closes #6530 ref: https://stackoverflow.com/q/1046933 ref: https://unix.stackexchange.com/a/404065 Helped-by: Marco Hinz Discussion ------------------------------------------------------------------------ On my linux box before this patch, the termclose_spec.lua:'kills job trapping SIGTERM' test indirectly causes cmake/busted to wait for 60s. That's because the test spawns a `sleep 60` descendant process which hangs around even after nvim exits: nvim killed the parent PID, but not PGID (process-group), so the grandchild "reparented" to init (PID 1). Session contains processes (and process-groups) which are logically part of the same "login session". Process-group is a set of logically/informally-related processes within a session; for example, shells assign a process group to each "job". Session IDs and PGIDs both have type pid_t (like PIDs). These OS-level mechanisms are, as usual, legacy accidents whose purpose is upheld by convention and folklore. We can use session-level grouping (setsid), or we could use process-group-level grouping (setpgid). Vim uses setsid() if available, otherwise setpgid(0,0). Windows ------------------------------------------------------------------------ UV_PROCESS_DETACHED on win32 sets CREATE_NEW_PROCESS_GROUP flag. But uv_kill() does not kill the process-group: https://github.com/nodejs/node/issues/3617 Ideas: - Set UV_PROCESS_DETACHED (CREATE_NEW_PROCESS_GROUP), then call GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid) - Maybe won't work because MSDN says "Only processes that share the same console as the calling process receive the signal." https://docs.microsoft.com/en-us/windows/console/generateconsolectrlevent But CREATE_NEW_PROCESS_GROUP creates a new console ... ref https://stackoverflow.com/q/1453520 - Group processes within a "job". libuv does that *globally* for non-detached processes: uv__init_global_job_handle. - Iterate through CreateToolhelp32Snapshot(). - https://stackoverflow.com/q/1173342 - Vim does this, see terminate_all() --- src/nvim/os/pty_process_unix.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 855ca2ae47..dfe2cfbb8d 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -145,8 +145,12 @@ void pty_process_teardown(Loop *loop) uv_signal_stop(&loop->children_watcher); } -static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL +static void init_child(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL { + // New session/process-group. #6530 + setsid(); + unsetenv("COLUMNS"); unsetenv("LINES"); unsetenv("TERMCAP"); -- cgit From de86f824835b1556cc0070dd5720cdae484a0296 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 11 Mar 2018 21:46:14 +0100 Subject: win: os_proc_tree_kill() XXX: comment at https://stackoverflow.com/q/1173342 : > Windows recycles PIDs quite fast, you have to be extra careful not > to kill unrelated processes. These APIs will report PPIDs for long > dead processes whose PIDs may have been recycled. Check the parent > start date to make sure it is related to the processes you spawned. --- src/nvim/os/process.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/os/process.h | 8 +++++ 2 files changed, 95 insertions(+) create mode 100644 src/nvim/os/process.c create mode 100644 src/nvim/os/process.h (limited to 'src/nvim/os') diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c new file mode 100644 index 0000000000..eefc94faf6 --- /dev/null +++ b/src/nvim/os/process.c @@ -0,0 +1,87 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include // for HANDLE (win32) +#ifdef WIN32 +# include // for CreateToolhelp32Snapshot +#endif + +#include "nvim/log.h" +#include "nvim/os/process.h" +#include "nvim/os/os.h" +#include "nvim/os/os_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/process.c.generated.h" +#endif + +#ifdef WIN32 +/// Kills process `pid` and its descendants recursively. +bool os_proc_tree_kill_rec(HANDLE process, int sig) +{ + if (process == NULL) { + return false; + } + PROCESSENTRY32 pe; + DWORD pid = GetProcessId(process); + + if (pid != 0) { + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h != INVALID_HANDLE_VALUE) { + pe.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(h, &pe)) { + goto theend; + } + + do { + if (pe.th32ParentProcessID == pid) { + HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); + if (ph != NULL) { + os_proc_tree_kill_rec(ph, sig); + CloseHandle(ph); + } + } + } while (Process32Next(h, &pe)); + + CloseHandle(h); + } + } + +theend: + return (bool)TerminateProcess(process, (unsigned int)sig); +} +bool os_proc_tree_kill(int pid, int sig) +{ + assert(sig >= 0); + assert(sig == SIGTERM || sig == SIGKILL); + if (pid > 0) { + ILOG("terminating process tree: %d", pid); + HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); + return os_proc_tree_kill_rec(h, sig); + } else { + ELOG("invalid pid: %d", pid); + } + return false; +} +#else +/// Kills process group where `pid` is the process group leader. +bool os_proc_tree_kill(int pid, int sig) +{ + assert(sig == SIGTERM || sig == SIGKILL); + int pgid = getpgid(pid); + if (pgid > 0) { // Ignore error. Never kill self (pid=0). + if (pgid == pid) { + ILOG("sending %s to process group: -%d", + sig == SIGTERM ? "SIGTERM" : "SIGKILL", pgid); + int rv = uv_kill(-pgid, sig); + return rv == 0; + } else { + // Should never happen, because process_spawn() did setsid() in the child. + ELOG("pgid %d != pid %d", pgid, pid); + } + } else { + ELOG("getpgid(%d) returned %d", pid, pgid); + } + return false; +} +#endif diff --git a/src/nvim/os/process.h b/src/nvim/os/process.h new file mode 100644 index 0000000000..9549ae9081 --- /dev/null +++ b/src/nvim/os/process.h @@ -0,0 +1,8 @@ +#ifndef NVIM_OS_PROCESS_H +#define NVIM_OS_PROCESS_H + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/process.h.generated.h" +#endif + +#endif // NVIM_OS_PROCESS_H -- cgit From dbad797edd4636f830abd7ade1138a1a27ac30d2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 11 Mar 2018 21:47:12 +0100 Subject: API: nvim_get_proc_children() ref https://github.com/libuv/libuv/pull/836 --- src/nvim/os/process.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/nvim/os/process.h | 2 + 2 files changed, 101 insertions(+), 4 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index eefc94faf6..da66d78e0d 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -6,6 +6,17 @@ # include // for CreateToolhelp32Snapshot #endif +#if defined(__FreeBSD__) // XXX: OpenBSD, NetBSD ? +# include +# include +# include +#endif + +#if defined(__APPLE__) || defined(BSD) +# include +# include +#endif + #include "nvim/log.h" #include "nvim/os/process.h" #include "nvim/os/os.h" @@ -16,8 +27,7 @@ #endif #ifdef WIN32 -/// Kills process `pid` and its descendants recursively. -bool os_proc_tree_kill_rec(HANDLE process, int sig) +static bool os_proc_tree_kill_rec(HANDLE process, int sig) { if (process == NULL) { return false; @@ -35,7 +45,7 @@ bool os_proc_tree_kill_rec(HANDLE process, int sig) do { if (pe.th32ParentProcessID == pid) { - HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); + HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, false, pe.th32ProcessID); if (ph != NULL) { os_proc_tree_kill_rec(ph, sig); CloseHandle(ph); @@ -50,13 +60,14 @@ bool os_proc_tree_kill_rec(HANDLE process, int sig) theend: return (bool)TerminateProcess(process, (unsigned int)sig); } +/// Kills process `pid` and its descendants recursively. bool os_proc_tree_kill(int pid, int sig) { assert(sig >= 0); assert(sig == SIGTERM || sig == SIGKILL); if (pid > 0) { ILOG("terminating process tree: %d", pid); - HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); + HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD)pid); return os_proc_tree_kill_rec(h, sig); } else { ELOG("invalid pid: %d", pid); @@ -85,3 +96,87 @@ bool os_proc_tree_kill(int pid, int sig) return false; } #endif + +/// Gets the process ids of the immediate children of process `ppid`. +/// +/// @param ppid Process to inspect. +/// @param[out,allocated] proc_list Child process ids. +/// @param[out] proc_count Number of child processes. +/// @return 0 on success, 1 if process not found, 2 on other error. +int os_proc_children(int ppid, int **proc_list, size_t *proc_count) +{ + // + // psutil is a good reference for cross-platform syscall voodoo: + // https://github.com/giampaolo/psutil/tree/master/psutil/arch + // + + int *temp = NULL; + *proc_list = NULL; + *proc_count = 0; + +#if defined(__APPLE__) || defined(BSD) +# if defined(__APPLE__) +# define KP_PID(o) o.kp_proc.p_pid +# define KP_PPID(o) o.kp_eproc.e_ppid +# elif defined(__FreeBSD__) +# define KP_PID(o) o.ki_pid +# define KP_PPID(o) o.ki_ppid +# else +# define KP_PID(o) o.p_pid +# define KP_PPID(o) o.p_ppid +# endif + static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + + // Get total process count. + size_t len = 0; + int rv = sysctl(name, ARRAY_SIZE(name) - 1, NULL, &len, NULL, 0); + if (rv) { + return 2; + } + + // Get ALL processes. + struct kinfo_proc *p_list = xmalloc(len); + rv = sysctl(name, ARRAY_SIZE(name) - 1, p_list, &len, NULL, 0); + if (rv) { + xfree(p_list); + return 2; + } + + // Collect processes whose parent matches `ppid`. + bool exists = false; + size_t p_count = len / sizeof(*p_list); + for (size_t i = 0; i < p_count; i++) { + exists = exists || KP_PID(p_list[i]) == ppid; + if (KP_PPID(p_list[i]) == ppid) { + temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp)); + temp[*proc_count] = KP_PID(p_list[i]); + (*proc_count)++; + } + } + xfree(p_list); + if (!exists) { + return 1; // Process not found. + } + +#elif defined(__linux__) + char proc_p[256] = { 0 }; + // Collect processes whose parent matches `ppid`. + // Rationale: children are defined in thread with same ID of process. + snprintf(proc_p, sizeof(proc_p), "/proc/%d/task/%d/children", ppid, ppid); + FILE *fp = fopen(proc_p, "r"); + if (fp == NULL) { + return 1; // Process not found. + } + int match_pid; + while (fscanf(fp, "%d", &match_pid) > 0) { + temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp)); + temp[*proc_count] = match_pid; + (*proc_count)++; + } + fclose(fp); +#endif + + *proc_list = temp; + return 0; +} + diff --git a/src/nvim/os/process.h b/src/nvim/os/process.h index 9549ae9081..9a83942169 100644 --- a/src/nvim/os/process.h +++ b/src/nvim/os/process.h @@ -1,6 +1,8 @@ #ifndef NVIM_OS_PROCESS_H #define NVIM_OS_PROCESS_H +#include + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/process.h.generated.h" #endif -- cgit From 12af7016e23e7b7f507dc99a1b73e64d0bb5ccf3 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 14 Mar 2018 23:26:37 +0100 Subject: nvim_get_proc_children: fallback to shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /proc/…/children may be unavailable because of an unset kernel option. Fallback to `pgrep` invoked in a shell. --- src/nvim/os/process.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index da66d78e0d..80c2dad64d 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -2,6 +2,7 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include // for HANDLE (win32) + #ifdef WIN32 # include // for CreateToolhelp32Snapshot #endif @@ -165,7 +166,7 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) snprintf(proc_p, sizeof(proc_p), "/proc/%d/task/%d/children", ppid, ppid); FILE *fp = fopen(proc_p, "r"); if (fp == NULL) { - return 1; // Process not found. + return 2; // Process not found, or /proc/…/children not supported. } int match_pid; while (fscanf(fp, "%d", &match_pid) > 0) { -- cgit From 330e5acbcec00d1bc43a9c1110c4325fa62ba9ad Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 15 Mar 2018 02:33:20 +0100 Subject: win: nvim_get_proc_children() TODO: Raymond Chen explains[1] racy behavior of the CreateToolhelp32Snapshot approach. Better approach: > create a job object and put process P in it. Then call > QueryInformationJobObject with JobObjectBasicProcessIdList to get the > list of child processes. [1] "Why is CreateToolhelp32Snapshot returning incorrect parent process IDs all of a sudden?" https://blogs.msdn.microsoft.com/oldnewthing/20150403-00/?p=44313 --- src/nvim/os/process.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 80c2dad64d..09769925ac 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -43,7 +43,6 @@ static bool os_proc_tree_kill_rec(HANDLE process, int sig) if (!Process32First(h, &pe)) { goto theend; } - do { if (pe.th32ParentProcessID == pid) { HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, false, pe.th32ProcessID); @@ -53,7 +52,6 @@ static bool os_proc_tree_kill_rec(HANDLE process, int sig) } } } while (Process32Next(h, &pe)); - CloseHandle(h); } } @@ -111,11 +109,40 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) // https://github.com/giampaolo/psutil/tree/master/psutil/arch // + if (ppid < 0) { + return 2; + } + int *temp = NULL; *proc_list = NULL; *proc_count = 0; -#if defined(__APPLE__) || defined(BSD) +#ifdef WIN32 + PROCESSENTRY32 pe; + + // Snapshot of all processes. + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if(h == INVALID_HANDLE_VALUE) { + return 2; + } + + pe.dwSize = sizeof(PROCESSENTRY32); + // Get root process. + if(!Process32First(h, &pe)) { + CloseHandle(h); + return 2; + } + // Collect processes whose parent matches `ppid`. + do { + if (pe.th32ParentProcessID == (DWORD)ppid) { + temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp)); + temp[*proc_count] = (int)pe.th32ProcessID; + (*proc_count)++; + } + } while (Process32Next(h, &pe)); + CloseHandle(h); + +#elif defined(__APPLE__) || defined(BSD) # if defined(__APPLE__) # define KP_PID(o) o.kp_proc.p_pid # define KP_PPID(o) o.kp_eproc.e_ppid -- cgit From a034d4b69d6032b3431c10b8a11c998551700fc2 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 16 Mar 2018 05:13:38 +0100 Subject: API: nvim_get_proc() TODO: "exepath" field (win32: QueryFullProcessImageName()) On unix-likes `ps` is used because the platform-specific APIs are a nightmare. For reference, below is a (incomplete) attempt: diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 09769925aca5..99afbbf290c1 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -208,3 +210,60 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) return 0; } +/// Gets various properties of the process identified by `pid`. +/// +/// @param pid Process to inspect. +/// @return Map of process properties, empty on error. +Dictionary os_proc_info(int pid) +{ + Dictionary pinfo = ARRAY_DICT_INIT; +#ifdef WIN32 + +#elif defined(__APPLE__) + char buf[PROC_PIDPATHINFO_MAXSIZE]; + if (proc_pidpath(pid, buf, sizeof(buf))) { + name = getName(buf); + PUT(pinfo, "exepath", STRING_OBJ(cstr_to_string(buf))); + return name; + } else { + ILOG("proc_pidpath() failed for pid: %d", pid); + } +#elif defined(BSD) +# if defined(__FreeBSD__) +# define KP_COMM(o) o.ki_comm +# else +# define KP_COMM(o) o.p_comm +# endif + struct kinfo_proc *proc = kinfo_getproc(pid); + if (proc) { + PUT(pinfo, "name", cstr_to_string(KP_COMM(proc))); + xfree(proc); + } else { + ILOG("kinfo_getproc() failed for pid: %d", pid); + } + +#elif defined(__linux__) + char fname[256] = { 0 }; + char buf[MAXPATHL]; + snprintf(fname, sizeof(fname), "/proc/%d/comm", pid); + FILE *fp = fopen(fname, "r"); + // FileDescriptor *f = file_open_new(&error, fname, kFileReadOnly, 0); + // ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, + // const size_t size) + if (fp == NULL) { + ILOG("fopen() of /proc/%d/comm failed", pid); + } else { + size_t n = fread(buf, sizeof(char), sizeof(buf) - 1, fp); + if (n == 0) { + WLOG("fread() of /proc/%d/comm failed", pid); + } else { + size_t end = MIN(sizeof(buf) - 1, n); + end = (end > 0 && buf[end - 1] == '\n') ? end - 1 : end; + buf[end] = '\0'; + PUT(pinfo, "name", STRING_OBJ(cstr_to_string(buf))); + } + } + fclose(fp); +#endif + return pinfo; +} --- src/nvim/os/fileio.c | 10 ++++----- src/nvim/os/process.c | 57 ++++++++++++++++++++++++++++++++++++++++++++------- src/nvim/os/process.h | 1 + 3 files changed, 55 insertions(+), 13 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index d294f9139b..a95adc86b6 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -4,7 +4,7 @@ /// @file fileio.c /// /// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with -/// Neovim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite +/// Nvim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite /// replacement. #include @@ -43,7 +43,7 @@ /// @param[in] mode Permissions for the newly created file (ignored if flags /// does not have kFileCreate\*). /// -/// @return Error code (@see os_strerror()) or 0. +/// @return Error code, or 0 on success. @see os_strerror() int file_open(FileDescriptor *const ret_fp, const char *const fname, const int flags, const int mode) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT @@ -115,8 +115,7 @@ int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr) /// Like file_open(), but allocate and return ret_fp /// -/// @param[out] error Error code, @see os_strerror(). Is set to zero on -/// success. +/// @param[out] error Error code, or 0 on success. @see os_strerror() /// @param[in] fname File name to open. /// @param[in] flags Flags, @see FileOpenFlags. /// @param[in] mode Permissions for the newly created file (ignored if flags @@ -137,8 +136,7 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, /// Like file_open_fd(), but allocate and return ret_fp /// -/// @param[out] error Error code, @see os_strerror(). Is set to zero on -/// success. +/// @param[out] error Error code, or 0 on success. @see os_strerror() /// @param[in] fd File descriptor to wrap. /// @param[in] wr True if fd is opened for writing only, false if it is read /// only. diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 09769925ac..e23ba8a4ee 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -1,6 +1,11 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +/// OS process functions +/// +/// psutil is a good reference for cross-platform syscall voodoo: +/// https://github.com/giampaolo/psutil/tree/master/psutil/arch + #include // for HANDLE (win32) #ifdef WIN32 @@ -18,10 +23,12 @@ # include #endif +#include "nvim/globals.h" #include "nvim/log.h" #include "nvim/os/process.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" +#include "nvim/api/private/helpers.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/process.c.generated.h" @@ -104,11 +111,6 @@ bool os_proc_tree_kill(int pid, int sig) /// @return 0 on success, 1 if process not found, 2 on other error. int os_proc_children(int ppid, int **proc_list, size_t *proc_count) { - // - // psutil is a good reference for cross-platform syscall voodoo: - // https://github.com/giampaolo/psutil/tree/master/psutil/arch - // - if (ppid < 0) { return 2; } @@ -122,13 +124,13 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) // Snapshot of all processes. HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if(h == INVALID_HANDLE_VALUE) { + if (h == INVALID_HANDLE_VALUE) { return 2; } pe.dwSize = sizeof(PROCESSENTRY32); // Get root process. - if(!Process32First(h, &pe)) { + if (!Process32First(h, &pe)) { CloseHandle(h); return 2; } @@ -208,3 +210,44 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) return 0; } +#ifdef WIN32 +/// Gets various properties of the process identified by `pid`. +/// +/// @param pid Process to inspect. +/// @return Map of process properties, empty on error. +Dictionary os_proc_info(int pid) +{ + Dictionary pinfo = ARRAY_DICT_INIT; + PROCESSENTRY32 pe; + + // Snapshot of all processes. This is used instead of: + // OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, …) + // to avoid ERROR_PARTIAL_COPY. https://stackoverflow.com/a/29942376 + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h == INVALID_HANDLE_VALUE) { + return pinfo; // Return empty. + } + + pe.dwSize = sizeof(PROCESSENTRY32); + // Get root process. + if (!Process32First(h, &pe)) { + CloseHandle(h); + return pinfo; // Return empty. + } + // Find the process. + do { + if (pe.th32ProcessID == (DWORD)pid) { + break; + } + } while (Process32Next(h, &pe)); + CloseHandle(h); + + if (pe.th32ProcessID == (DWORD)pid) { + PUT(pinfo, "pid", INTEGER_OBJ(pid)); + PUT(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); + PUT(pinfo, "name", STRING_OBJ(cstr_to_string(pe.szExeFile))); + } + + return pinfo; +} +#endif diff --git a/src/nvim/os/process.h b/src/nvim/os/process.h index 9a83942169..1722d56bd3 100644 --- a/src/nvim/os/process.h +++ b/src/nvim/os/process.h @@ -2,6 +2,7 @@ #define NVIM_OS_PROCESS_H #include +#include "nvim/api/private/defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/process.h.generated.h" -- cgit From 65b66bc332c56c04d4687f11b601a59ddd5d09bd Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 18 Mar 2018 14:07:34 +0100 Subject: build/MSVC: fix "C4005: RGB: macro redefinition" --- src/nvim/os/win_defs.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index faee06304c..db93f016bf 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -32,11 +32,8 @@ // Windows defines a RGB macro that produces 0x00bbggrr color values for use // with GDI. Our macro is different, and we don't use GDI. -#if defined(RGB) -# undef RGB - // Duplicated from macros.h to avoid include-order sensitivity. -# define RGB(r, g, b) ((r << 16) | (g << 8) | b) -#endif +// Duplicated from macros.h to avoid include-order sensitivity. +#define RGB_(r, g, b) ((r << 16) | (g << 8) | b) #ifdef _MSC_VER # ifndef inline -- cgit From 998a16c926623a667ecb0228f4a6cd8ba1e90201 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Mar 2018 11:21:20 +0100 Subject: refactor/rename: path_is_absolute() --- src/nvim/os/env.c | 2 +- src/nvim/os/fs.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 999fcd434a..3fcb9415c7 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -886,7 +886,7 @@ bool os_setenv_append_path(const char *fname) // No prescribed maximum on unix. # define MAX_ENVPATHLEN INT_MAX #endif - if (!path_is_absolute_path((char_u *)fname)) { + if (!path_is_absolute((char_u *)fname)) { internal_error("os_setenv_append_path()"); return false; } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index c0a97aeb34..b7c2714296 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -222,7 +222,7 @@ int os_exepath(char *buffer, size_t *size) bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) FUNC_ATTR_NONNULL_ARG(1) { - bool no_path = !use_path || path_is_absolute_path(name); + bool no_path = !use_path || path_is_absolute(name); #ifndef WIN32 // If the filename is "qualified" (relative or absolute) do not check $PATH. no_path |= (name[0] == '.' @@ -244,7 +244,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) #endif if (ok) { if (abspath != NULL) { - *abspath = save_absolute_path(name); + *abspath = save_abs_path(name); } return true; } @@ -357,7 +357,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) #endif if (ok) { if (abspath != NULL) { // Caller asked for a copy of the path. - *abspath = save_absolute_path((char_u *)buf); + *abspath = save_abs_path((char_u *)buf); } rv = true; -- cgit From e9cf40f2b658970d37e0b1a8f70b64073464c648 Mon Sep 17 00:00:00 2001 From: Utkarsh Anand Date: Thu, 29 Mar 2018 14:07:49 +0530 Subject: build/NetBSD: use kinfo_proc2; undef uint64_t (#8197) closes #8196 For historical reasons, uint64_t and friends are defined both as typedefs and macros. Some platforms that do that define the macros as identity (#define uint64_t uint64_t), others like NetBSD define to the backing type (#define uint64_t __uint64_t). This is normally transparent, except when multiple levels of macro expansions are used inconsistently. --- src/nvim/os/process.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index e23ba8a4ee..60ca890e0e 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -12,12 +12,16 @@ # include // for CreateToolhelp32Snapshot #endif -#if defined(__FreeBSD__) // XXX: OpenBSD, NetBSD ? +#if defined(__FreeBSD__) // XXX: OpenBSD ? # include # include # include #endif +#if defined(__NetBSD__) +# include +#endif + #if defined(__APPLE__) || defined(BSD) # include # include @@ -155,7 +159,11 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) # define KP_PID(o) o.p_pid # define KP_PPID(o) o.p_ppid # endif +# ifdef __NetBSD__ + static int name[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, 0, (int)(sizeof(struct kinfo_proc2)), 0 }; +# else static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; +# endif // Get total process count. size_t len = 0; @@ -165,7 +173,11 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) } // Get ALL processes. +# ifdef __NetBSD__ + struct kinfo_proc2 *p_list = xmalloc(len); +# else struct kinfo_proc *p_list = xmalloc(len); +# endif rv = sysctl(name, ARRAY_SIZE(name) - 1, p_list, &len, NULL, 0); if (rv) { xfree(p_list); -- cgit From e54ff10d44a18b59350503accc68811e4a5be29f Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 29 Mar 2018 10:32:12 -0400 Subject: lint --- src/nvim/os/process.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 60ca890e0e..63eea9022b 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -160,7 +160,9 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) # define KP_PPID(o) o.p_ppid # endif # ifdef __NetBSD__ - static int name[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, 0, (int)(sizeof(struct kinfo_proc2)), 0 }; + static int name[] = { + CTL_KERN, KERN_PROC2, KERN_PROC_ALL, 0, (int)(sizeof(struct kinfo_proc2)), 0 + }; # else static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; # endif -- cgit From 9b7ce004867e0ac3692afbef0cc15e2474242057 Mon Sep 17 00:00:00 2001 From: Utkarsh Anand Date: Mon, 2 Apr 2018 14:38:11 +0530 Subject: build/OpenBSD: need -lpthread -lc++abi for LuaJIT (#8215) --- src/nvim/os/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 63eea9022b..a67e7487eb 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -18,7 +18,7 @@ # include #endif -#if defined(__NetBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) # include #endif -- cgit From c28dbede27c7bc270aee55931ad6d42391da73fe Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 9 Apr 2018 10:10:06 +0200 Subject: os/shell: remove dead calls to screen functions --- src/nvim/os/shell.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index f650a51fe7..236840a91b 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -136,7 +136,7 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args) xfree(input.data); if (output) { - (void)write_output(output, nread, true, true); + (void)write_output(output, nread, true); xfree(output); } @@ -609,28 +609,20 @@ static void read_input(DynamicBuffer *buf) } } -static size_t write_output(char *output, size_t remaining, bool to_buffer, - bool eof) +static size_t write_output(char *output, size_t remaining, bool eof) { if (!output) { return 0; } - char replacement_NUL = to_buffer ? NL : 1; char *start = output; size_t off = 0; - int lastrow = (int)Rows - 1; while (off < remaining) { if (output[off] == NL) { // Insert the line - if (to_buffer) { - output[off] = NUL; - ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1, - false); - } else { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - screen_puts_len((char_u *)output, (int)off, lastrow, 0, 0); - } + output[off] = NUL; + ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1, + false); size_t skip = off + 1; output += skip; remaining -= skip; @@ -640,24 +632,19 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, if (output[off] == NUL) { // Translate NUL to NL - output[off] = replacement_NUL; + output[off] = NL; } off++; } if (eof) { if (remaining) { - if (to_buffer) { - // append unfinished line - ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false); - // remember that the NL was missing - curbuf->b_no_eol_lnum = curwin->w_cursor.lnum; - } else { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - screen_puts_len((char_u *)output, (int)remaining, lastrow, 0, 0); - } + // append unfinished line + ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false); + // remember that the NL was missing + curbuf->b_no_eol_lnum = curwin->w_cursor.lnum; output += remaining; - } else if (to_buffer) { + } else { curbuf->b_no_eol_lnum = 0; } } -- cgit From d4688add960c22f9a379f3cddbe795c45d41603a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 9 Apr 2018 10:32:32 +0200 Subject: os/shell: use msg functions instead of screen when throttling --- src/nvim/os/shell.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 236840a91b..04f59d7522 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -388,10 +388,10 @@ static bool out_data_decide_throttle(size_t size) pulse_msg[1] = (tick == 0 || 1 == tick) ? ' ' : '.'; pulse_msg[2] = (tick == 0 || 1 == tick || 2 == tick) ? ' ' : '.'; if (visit == 1) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); + msg_putchar('\n'); } - int lastrow = (int)Rows - 1; - screen_puts_len((char_u *)pulse_msg, ARRAY_SIZE(pulse_msg), lastrow, 0, 0); + msg_putchar('\r'); // put cursor at start of line + msg_puts(pulse_msg); ui_flush(); return true; } -- cgit