From 5ab9e9f617934fae8f85ceb6db398dbf1e93471d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 17:03:07 +0300 Subject: os/fileio: Add msgpack_file_write function --- src/nvim/os/fileio.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 4309ac723c..d16746b7bf 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -26,6 +26,7 @@ #include "nvim/globals.h" #include "nvim/rbuffer.h" #include "nvim/macros.h" +#include "nvim/message.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/fileio.c.generated.h" @@ -345,3 +346,23 @@ ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size) return (ptrdiff_t)read_bytes; } + +/// Msgpack callback for writing to a file +/// +/// @param data File to write to. +/// @param[in] buf Data to write. +/// @param[in] len Length of the data to write. +/// +/// @return 0 in case of success, -1 in case of error. +int msgpack_file_write(void *data, const char *buf, size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(len < PTRDIFF_MAX); + const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len); + if (written_bytes < 0) { + emsgf(_("E5420: Failed to write to file: %s"), + os_strerror((int)written_bytes)); + return -1; + } + return 0; +} -- cgit From 72b3fd96642e7b2c268e17953de3b2ed995eb3b4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 17:08:43 +0300 Subject: os/fileio: Add ability to use os/fileio.c for file descriptors Code imported from #6299 --- src/nvim/os/fileio.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index d16746b7bf..0ed0ae87e3 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -49,7 +49,6 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int os_open_flags = 0; - int fd; TriState wr = kNone; // -V:FLAG:501 #define FLAG(flags, flag, fcntl_flags, wrval, cond) \ @@ -74,14 +73,35 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true); #endif #undef FLAG + // wr is used for kFileReadOnly flag, but on + // QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with + // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]` + (void)wr; - fd = os_open(fname, os_open_flags, mode); + const int fd = os_open(fname, os_open_flags, mode); if (fd < 0) { return fd; } + return file_open_fd(ret_fp, fd, (wr == kTrue)); +} - ret_fp->wr = (wr == kTrue); +/// Wrap file descriptor with FileDescriptor structure +/// +/// @warning File descriptor wrapped like this must not be accessed by other +/// means. +/// +/// @param[out] ret_fp Address where information needed for reading from or +/// writing to a file is saved +/// @param[in] fd File descriptor to wrap. +/// @param[in] wr True if fd is opened for writing only, false if it is read +/// only. +/// +/// @return Error code (@see os_strerror()) or 0. Currently always returns 0. +int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + ret_fp->wr = wr; ret_fp->fd = fd; ret_fp->eof = false; ret_fp->rv = rbuffer_new(kRWBufferSize); @@ -115,6 +135,26 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, return fp; } +/// 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[in] fd File descriptor to wrap. +/// @param[in] wr True if fd is opened for writing only, false if it is read +/// only. +/// +/// @return [allocated] Opened file or NULL in case of error. +FileDescriptor *file_open_fd_new(int *const error, const int fd, const bool wr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + FileDescriptor *const fp = xmalloc(sizeof(*fp)); + if ((*error = file_open_fd(fp, fd, wr)) != 0) { + xfree(fp); + return NULL; + } + return fp; +} + /// Close file and free its buffer /// /// @param[in,out] fp File to close. -- cgit From f0b3029ad33cbba284c4e677a08b0e2488bf062b Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 17:12:00 +0300 Subject: os: Add OS_STD*_FILENO constants --- src/nvim/os/os_defs.h | 7 +++++++ src/nvim/os/win_defs.h | 10 ++++++++++ 2 files changed, 17 insertions(+) (limited to 'src/nvim/os') diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index f81785675e..923a362b41 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -13,6 +13,13 @@ # 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 7c980c3768..7ed70f6092 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -91,4 +91,14 @@ typedef SSIZE_T ssize_t; # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #endif +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + #endif // NVIM_OS_WIN_DEFS_H -- cgit From 7109f63e3cf70ffc4581f3dfb6132632413e4817 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 4 Jul 2017 19:48:26 +0300 Subject: main: Flush file in place of closing it, also do error reporting Apparently on travis OS X systems it crashes when cleaning up streams with stdout closed: (lldb) bt all * thread #1: tid = 0x0000, 0x00007fff8703df06 libsystem_kernel.dylib`__pthread_kill + 10, stop reason = signal SIGSTOP * frame #0: 0x00007fff8703df06 libsystem_kernel.dylib`__pthread_kill + 10 frame #1: 0x00007fff93a764ec libsystem_pthread.dylib`pthread_kill + 90 frame #2: 0x00007fff97c056df libsystem_c.dylib`abort + 129 frame #3: 0x00007fff97bccdd8 libsystem_c.dylib`__assert_rtn + 321 frame #4: 0x0000000107a4e106 nvim`uv__close(fd=) + 102 at core.c:521 frame #5: 0x0000000107a5307d nvim`uv__loop_close(loop=0x00007fff5847c018) + 77 at loop.c:118 frame #6: 0x0000000107a4d149 nvim`uv_loop_close(loop=0x00007fff5847c018) + 57 at uv-common.c:626 frame #7: 0x000000010783e5bc nvim`stream_set_blocking(fd=0, blocking=true) + 204 at stream.c:34 frame #8: 0x000000010795d66b nvim`mch_exit(r=0) + 91 at os_unix.c:147 frame #9: 0x00000001078d5663 nvim`command_line_scan(parmp=0x00007fff5847c760) + 1779 at main.c:787 frame #10: 0x00000001078d4393 nvim`main(argc=2, argv=0x00007fff5847c898) + 163 at main.c:249 frame #11: 0x00007fff8cdd65ad libdyld.dylib`start + 1 frame #12: 0x00007fff8cdd65ad libdyld.dylib`start + 1 --- src/nvim/os/fileio.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 0ed0ae87e3..5d68473982 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -400,9 +400,18 @@ int msgpack_file_write(void *data, const char *buf, size_t len) assert(len < PTRDIFF_MAX); const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len); if (written_bytes < 0) { - emsgf(_("E5420: Failed to write to file: %s"), - os_strerror((int)written_bytes)); - return -1; + return msgpack_file_write_error((int)written_bytes); } return 0; } + +/// Print error which occurs when failing to write msgpack data +/// +/// @param[in] error Error code of the error to print. +/// +/// @return -1 (error return for msgpack_packer callbacks). +int msgpack_file_write_error(const int error) +{ + emsgf(_("E5420: Failed to write to file: %s"), os_strerror(error)); + return -1; +} -- cgit From 5acda12419858da202ce7ea962fae75102a75b2b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 29 Jul 2017 04:53:40 +0200 Subject: coverity/155506: null dereference (#7089) Coverity warning is a false positive: if rbuffer_read_ptr() returns NULL then `cnt` is zero. Revert 76ea97c809e50fccc5ca6615943ac6da1db1e030 (which caused the TSan build to hang often--possibly because of the missing ui_flush()). Instead, modify out_data_append_to_screen() to check for NULL. ref #6862 --- src/nvim/os/shell.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 9d80a43718..32e9a70e57 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -447,7 +447,7 @@ static void out_data_append_to_screen(char *output, size_t remaining, size_t off = 0; int last_row = (int)Rows - 1; - while (off < remaining) { + 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? @@ -473,7 +473,7 @@ static void out_data_append_to_screen(char *output, size_t remaining, off++; } - if (remaining) { + if (output != NULL && remaining) { if (last_col == 0) { screen_del_lines(0, 0, 1, (int)Rows, NULL); } @@ -496,12 +496,8 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, size_t cnt; char *ptr = rbuffer_read_ptr(buf, &cnt); - if (ptr == NULL || cnt == 0) { - // Nothing to read; - return; - } - - if (out_data_decide_throttle(cnt)) { // Skip output above a threshold. + if (ptr != NULL && cnt > 0 + && out_data_decide_throttle(cnt)) { // Skip output above a threshold. // Save the skipped output. If it is the final chunk, we display it later. out_data_ring(ptr, cnt); } else { -- cgit From d801ce70c1fb8dd8b3735ca9e06158aaf48203ba Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 5 Aug 2017 23:29:02 +0200 Subject: win_defs.h: redefine RGB macro after undefining Before this change, if os_defs.h was included after macros.h then win_defs.h would undefine our own RGB macro. vim-patch:8.0.0146 --- src/nvim/os/win_defs.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 7ed70f6092..8fd2e51f8b 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -30,8 +30,13 @@ #define USE_CRNL -// We have our own RGB macro in macros.h. -#undef RGB +// 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 #ifdef _MSC_VER # ifndef inline -- cgit From 9506ee037083ff047a3601b3d97acf91521a58fb Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 8 Aug 2017 20:50:41 +0200 Subject: buf_write(): wrong argument to os_fileinfo_hardlinks This was broken in ye olde refactor from 2014: e85fe0957d40080f43cbfcbe9eb8864475325b09 References #4370 --- src/nvim/os/fs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 14dacd97c4..9e3025cf89 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -131,6 +131,7 @@ bool os_isdir(const char_u *name) /// NODE_WRITABLE: writable device, socket, fifo, etc. /// NODE_OTHER: non-writable things int os_nodetype(const char *name) + FUNC_ATTR_NONNULL_ALL { #ifdef WIN32 // Edge case from Vim os_win32.c: -- cgit From ac055d677aa9eff9fca11cecb5ac7f7a4edb0265 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Tue, 8 Aug 2017 22:18:50 +0200 Subject: os_stat: return ENOENT on NULL filename arg Closes #4370 Explication: In the backtrace in #4370, we see that `buf_write()` was called with non-NULL `fname` and `sfname` arguments, but they've since _become_ NULL. #7 0x00000000004de09d in buf_write (buf=0x1dee040, fname=0x0, fname@entry=0x1e985b0 "/home/sean/src/github.com/snczl/virta/pkg/meld/segment.go", sfname=0x0, sfname@entry=0x1ddfa60 "segment.go", start=1, end=72, eap=eap@entry=0x7ffc6b032e60, append=0, forceit=0, reset_changed=1, filtering=0) at /home/travis/build/neovim/bot-ci/build/neovim/src/nvim/fileio.c:2576 This is most likely due to the code that restores those values from `buf`, which happens just before the fatal call to `os_fileinfo` ```c /* * The autocommands may have changed the name of the buffer, which may * be kept in fname, ffname and sfname. */ if (buf_ffname) ffname = buf->b_ffname; if (buf_sfname) sfname = buf->b_sfname; if (buf_fname_f) fname = buf->b_ffname; if (buf_fname_s) fname = buf->b_sfname; ``` It's worth noting that at this point `ffname` is still non-NULL, so it _could_ be used. However, our current code is purely more strict than Vim in this area, which has caused us problems before (e.g., `getdigits()`). The commentary for `struct file_buffer` clearly indicate that all of `b_ffname`, `b_sfname`, and `b_fname` may be NULL: ```c /* * b_ffname has the full path of the file (NULL for no name). * b_sfname is the name as the user typed it (or NULL). * b_fname is the same as b_sfname, unless ":cd" has been done, * then it is the same as b_ffname (NULL for no name). */ char_u *b_ffname; /* full path file name */ char_u *b_sfname; /* short file name */ char_u *b_fname; /* current file name */ ``` Vim directly calls `stat(2)` which, although it is annotated to tell the compiler that the path argument is non-NULL, does handle a NULL pointer by returning a `-1` value and setting `errno` to `EFAULT`. This satisfies Vim's check, since it treats any `-1` return from `stat(2)` to mean the file doesn't exist (at least in this code path). Note that Vim's mch_stat() implementations on win32 and solaris clearly cannot accept NULL `name`. But the codepaths that call mch_stat will NULL `name` tend to be unix-only (eg: u_read_undo)! --- src/nvim/os/fs.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 9e3025cf89..6ac9d682d7 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -605,8 +605,11 @@ int os_fsync(int fd) /// /// @return libuv return code. static int os_stat(const char *name, uv_stat_t *statbuf) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { + if (!name) { + return UV_ENOENT; + } uv_fs_t request; int result = uv_fs_stat(&fs_loop, &request, name, NULL); *statbuf = request.statbuf; @@ -618,7 +621,6 @@ static int os_stat(const char *name, uv_stat_t *statbuf) /// /// @return libuv error code on error. int32_t os_getperm(const char *name) - FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; int stat_result = os_stat(name, &statbuf); @@ -657,7 +659,6 @@ int os_fchown(int fd, uv_uid_t owner, uv_gid_t group) /// /// @return `true` if `path` exists bool os_path_exists(const char_u *path) - FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; return os_stat((char *)path, &statbuf) == kLibuvSuccess; @@ -847,7 +848,7 @@ int os_remove(const char *path) /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. bool os_fileinfo(const char *path, FileInfo *file_info) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { return os_stat(path, &(file_info->stat)) == kLibuvSuccess; } -- cgit From d258ac8ed21b3a202212792688b237d0741f6844 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 13 Aug 2017 18:46:09 +0200 Subject: io: more guards against NULL filename (#7159) References ac055d677aa9 References #4370 --- src/nvim/os/fs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 6ac9d682d7..78627f8703 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -859,8 +859,11 @@ bool os_fileinfo(const char *path, FileInfo *file_info) /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. bool os_fileinfo_link(const char *path, FileInfo *file_info) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { + if (path == NULL) { + return false; + } uv_fs_t request; int result = uv_fs_lstat(&fs_loop, &request, path, NULL); file_info->stat = request.statbuf; -- cgit From 7f22a27a10655dc40528f35034cbdf8c9543241c Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Wed, 24 Feb 2016 02:14:19 -0600 Subject: win: integrate winpty (WIP) Handling of process exit is still broken. It detects the moment when the child process exits, then quickly stops polling for process output. It should continue polling for output until the agent has scraped all of the process' output. This problem is easy to notice by running a command like "dir && exit", but even typing "exit" can manifest the problem -- the "t" might not appear. winpty's Cygwin adapter handles shutdown by waiting for the agent to close the CONOUT pipe, which it does after it has scraped the child's last output. AFAIK, neovim doesn't do anything interesting when winpty closes the CONOUT pipe. --- src/nvim/os/pty_process_win.c | 189 ++++++++++++++++++++++++++++++++++++++++++ src/nvim/os/pty_process_win.h | 25 ++++-- 2 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 src/nvim/os/pty_process_win.c (limited to 'src/nvim/os') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c new file mode 100644 index 0000000000..e75c92e7fb --- /dev/null +++ b/src/nvim/os/pty_process_win.c @@ -0,0 +1,189 @@ +#include +#include +#include + +#include "nvim/memory.h" +#include "nvim/os/pty_process_win.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_win.c.generated.h" +#endif + +static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) +{ + uv_async_t *finish_async = (uv_async_t *)context; + uv_async_send(finish_async); +} + +bool pty_process_spawn(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + bool success = false; + winpty_error_ptr_t err = NULL; + winpty_config_t *cfg = NULL; + winpty_spawn_config_t *spawncfg = NULL; + winpty_t *wp = NULL; + char *in_name = NULL, *out_name = NULL; + HANDLE process_handle = NULL; + + assert(proc->in && proc->out && !proc->err); + + if (!(cfg = winpty_config_new( + WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { + goto cleanup; + } + winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + + if (!(wp = winpty_open(cfg, &err))) { + goto cleanup; + } + + in_name = utf16_to_utf8(winpty_conin_name(wp)); + out_name = utf16_to_utf8(winpty_conout_name(wp)); + uv_pipe_connect( + xmalloc(sizeof(uv_connect_t)), + &proc->in->uv.pipe, + in_name, + pty_process_connect_cb); + uv_pipe_connect( + xmalloc(sizeof(uv_connect_t)), + &proc->out->uv.pipe, + out_name, + pty_process_connect_cb); + + // XXX: Provide the correct ptyprocess parameters (at least, the cmdline... + // probably cwd too? what about environ?) + if (!(spawncfg = winpty_spawn_config_new( + WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, + L"C:\\Windows\\System32\\cmd.exe", + L"C:\\Windows\\System32\\cmd.exe", + NULL, NULL, + &err))) { + goto cleanup; + } + if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { + goto cleanup; + } + + uv_async_init(&proc->loop->uv, &ptyproc->finish_async, pty_process_finish2); + if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, + pty_process_finish1, &ptyproc->finish_async, INFINITE, 0)) { + abort(); + } + + ptyproc->wp = wp; + ptyproc->process_handle = process_handle; + wp = NULL; + process_handle = NULL; + success = true; + +cleanup: + winpty_error_free(err); + winpty_config_free(cfg); + winpty_spawn_config_free(spawncfg); + winpty_free(wp); + xfree(in_name); + xfree(out_name); + if (process_handle != NULL) { + CloseHandle(process_handle); + } + return success; +} + +void pty_process_resize(PtyProcess *ptyproc, uint16_t width, + uint16_t height) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->wp != NULL) { + winpty_set_size(ptyproc->wp, width, height, NULL); + } +} + +void pty_process_close(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + + ptyproc->is_closing = true; + pty_process_close_master(ptyproc); + + uv_handle_t *finish_async_handle = (uv_handle_t *)&ptyproc->finish_async; + if (ptyproc->finish_wait != NULL) { + // Use INVALID_HANDLE_VALUE to block until either the wait is cancelled + // or the callback has signalled the uv_async_t. + UnregisterWaitEx(ptyproc->finish_wait, INVALID_HANDLE_VALUE); + uv_close(finish_async_handle, pty_process_finish_closing); + } else { + pty_process_finish_closing(finish_async_handle); + } +} + +void pty_process_close_master(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->wp != NULL) { + winpty_free(ptyproc->wp); + ptyproc->wp = NULL; + } +} + +void pty_process_teardown(Loop *loop) + FUNC_ATTR_NONNULL_ALL +{ +} + +// Returns a string freeable with xfree. Never returns NULL (OOM is a fatal +// error). Windows appears to replace invalid UTF-16 code points (i.e. +// unpaired surrogates) using U+FFFD (the replacement character). +static char *utf16_to_utf8(LPCWSTR str) + FUNC_ATTR_NONNULL_ALL +{ + int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + assert(len >= 1); // Even L"" has a non-zero length due to NUL terminator. + char *ret = xmalloc(len); + int len2 = WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL); + assert(len == len2); + return ret; +} + +static void pty_process_connect_cb(uv_connect_t *req, int status) +{ + assert(status == 0); + xfree(req); +} + +static void pty_process_finish2(uv_async_t *finish_async) +{ + PtyProcess *ptyproc = + (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); + Process *proc = (Process *)ptyproc; + + if (!ptyproc->is_closing) { + // If pty_process_close has already been called, be consistent and never + // call the internal_exit callback. + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = exit_code; + + if (proc->internal_exit_cb) { + proc->internal_exit_cb(proc); + } + } +} + +static void pty_process_finish_closing(uv_handle_t *finish_async) +{ + PtyProcess *ptyproc = + (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); + Process *proc = (Process *)ptyproc; + + if (ptyproc->process_handle != NULL) { + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; + } + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); + } +} diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 8e2b37a1c1..87b2b6545d 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -1,21 +1,23 @@ #ifndef NVIM_OS_PTY_PROCESS_WIN_H #define NVIM_OS_PTY_PROCESS_WIN_H +#include + +#include + #include "nvim/event/libuv_process.h" typedef struct pty_process { Process process; char *term_name; uint16_t width, height; + winpty_t *wp; + uv_async_t finish_async; + HANDLE finish_wait; + HANDLE process_handle; + bool is_closing; } PtyProcess; -#define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) -#define pty_process_close(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_resize(job, width, height) ( \ - (void)job, (void)width, (void)height, 0) -#define pty_process_teardown(loop) ((void)loop, 0) - static inline PtyProcess pty_process_init(Loop *loop, void *data) { PtyProcess rv; @@ -23,7 +25,16 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.term_name = NULL; rv.width = 80; rv.height = 24; + rv.wp = NULL; + // XXX: Zero rv.finish_async somehow? + rv.finish_wait = NULL; + rv.process_handle = NULL; + rv.is_closing = false; return rv; } +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_win.h.generated.h" +#endif + #endif // NVIM_OS_PTY_PROCESS_WIN_H -- cgit From 4b1f21de75f9981007d80aca8355239e8615d6bd Mon Sep 17 00:00:00 2001 From: erw7 Date: Tue, 28 Mar 2017 18:07:58 +0900 Subject: win: support :terminal --- src/nvim/os/pty_process_win.c | 232 ++++++++++++++++++++++++++++++------------ src/nvim/os/pty_process_win.h | 13 ++- 2 files changed, 176 insertions(+), 69 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 e75c92e7fb..ea95c1bb09 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -2,30 +2,53 @@ #include #include +#include "nvim/vim.h" +#include "nvim/ascii.h" #include "nvim/memory.h" +#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 #include "nvim/os/pty_process_win.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/pty_process_win.c.generated.h" #endif +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = + (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + Process *proc = (Process *)ptyproc; + + if (!uv_is_readable(proc->out->uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } +} + static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) + FUNC_ATTR_NONNULL_ALL { - uv_async_t *finish_async = (uv_async_t *)context; - uv_async_send(finish_async); + PtyProcess *ptyproc = (PtyProcess *)context; + Process *proc = (Process *)ptyproc; + + uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); + ptyproc->wait_eof_timer.data = (void *)ptyproc; + uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } -bool pty_process_spawn(PtyProcess *ptyproc) +int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { Process *proc = (Process *)ptyproc; - bool success = false; + int status = 0; winpty_error_ptr_t err = NULL; winpty_config_t *cfg = NULL; winpty_spawn_config_t *spawncfg = NULL; winpty_t *wp = NULL; char *in_name = NULL, *out_name = NULL; HANDLE process_handle = NULL; + uv_connect_t *in_req = NULL, *out_req = NULL; + wchar_t *cmdline = NULL, *cwd = NULL; assert(proc->in && proc->out && !proc->err); @@ -33,52 +56,70 @@ bool pty_process_spawn(PtyProcess *ptyproc) WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { goto cleanup; } - winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_config_set_initial_size( + cfg, + ptyproc->width, + ptyproc->height); if (!(wp = winpty_open(cfg, &err))) { goto cleanup; } - in_name = utf16_to_utf8(winpty_conin_name(wp)); - out_name = utf16_to_utf8(winpty_conout_name(wp)); + if ((status = utf16_to_utf8(winpty_conin_name(wp), &in_name))) { + goto cleanup; + } + if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { + goto cleanup; + } + in_req = xmalloc(sizeof(uv_connect_t)); + out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + in_req, &proc->in->uv.pipe, in_name, pty_process_connect_cb); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + out_req, &proc->out->uv.pipe, out_name, pty_process_connect_cb); - // XXX: Provide the correct ptyprocess parameters (at least, the cmdline... - // probably cwd too? what about environ?) + if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { + goto cleanup; + } + if ((status = build_cmdline(proc->argv, &cmdline))) { + goto cleanup; + } if (!(spawncfg = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, - L"C:\\Windows\\System32\\cmd.exe", - L"C:\\Windows\\System32\\cmd.exe", - NULL, NULL, - &err))) { + NULL, cmdline, cwd, NULL, &err))) { goto cleanup; } if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { goto cleanup; } + proc->pid = GetProcessId(process_handle); - uv_async_init(&proc->loop->uv, &ptyproc->finish_async, pty_process_finish2); - if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, - pty_process_finish1, &ptyproc->finish_async, INFINITE, 0)) { + if (!RegisterWaitForSingleObject( + &ptyproc->finish_wait, + process_handle, pty_process_finish1, ptyproc, + INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { abort(); } + while (in_req->handle || out_req->handle) { + uv_run(&proc->loop->uv, UV_RUN_ONCE); + } + ptyproc->wp = wp; ptyproc->process_handle = process_handle; wp = NULL; process_handle = NULL; - success = true; cleanup: + if (err != NULL) { + status = (int)winpty_error_code(err); + } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); @@ -88,7 +129,11 @@ cleanup: if (process_handle != NULL) { CloseHandle(process_handle); } - return success; + xfree(in_req); + xfree(out_req); + xfree(cmdline); + xfree(cwd); + return status; } void pty_process_resize(PtyProcess *ptyproc, uint16_t width, @@ -105,17 +150,10 @@ void pty_process_close(PtyProcess *ptyproc) { Process *proc = (Process *)ptyproc; - ptyproc->is_closing = true; pty_process_close_master(ptyproc); - uv_handle_t *finish_async_handle = (uv_handle_t *)&ptyproc->finish_async; - if (ptyproc->finish_wait != NULL) { - // Use INVALID_HANDLE_VALUE to block until either the wait is cancelled - // or the callback has signalled the uv_async_t. - UnregisterWaitEx(ptyproc->finish_wait, INVALID_HANDLE_VALUE); - uv_close(finish_async_handle, pty_process_finish_closing); - } else { - pty_process_finish_closing(finish_async_handle); + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); } } @@ -133,57 +171,123 @@ void pty_process_teardown(Loop *loop) { } -// Returns a string freeable with xfree. Never returns NULL (OOM is a fatal -// error). Windows appears to replace invalid UTF-16 code points (i.e. -// unpaired surrogates) using U+FFFD (the replacement character). -static char *utf16_to_utf8(LPCWSTR str) - FUNC_ATTR_NONNULL_ALL -{ - int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); - assert(len >= 1); // Even L"" has a non-zero length due to NUL terminator. - char *ret = xmalloc(len); - int len2 = WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL); - assert(len == len2); - return ret; -} - static void pty_process_connect_cb(uv_connect_t *req, int status) + FUNC_ATTR_NONNULL_ALL { assert(status == 0); - xfree(req); + req->handle = NULL; } -static void pty_process_finish2(uv_async_t *finish_async) +static void pty_process_finish2(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); Process *proc = (Process *)ptyproc; - if (!ptyproc->is_closing) { - // If pty_process_close has already been called, be consistent and never - // call the internal_exit callback. + UnregisterWaitEx(ptyproc->finish_wait, NULL); + uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = (int)exit_code; - DWORD exit_code = 0; - GetExitCodeProcess(ptyproc->process_handle, &exit_code); - proc->status = exit_code; + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; - if (proc->internal_exit_cb) { - proc->internal_exit_cb(proc); + proc->internal_exit_cb(proc); +} + +static int build_cmdline(char **argv, wchar_t **cmdline) + FUNC_ATTR_NONNULL_ALL +{ + char *args = NULL; + size_t args_len = 0, argc = 0; + int ret; + QUEUE q; + QUEUE_INIT(&q); + + while (*argv) { + arg_T *arg = xmalloc(sizeof(arg_T)); + arg->arg = (char *)xmalloc(strlen(*argv) * 2 + 3); + quote_cmd_arg(arg->arg, *argv); + args_len += strlen(arg->arg); + QUEUE_INIT(&arg->node); + QUEUE_INSERT_TAIL(&q, &arg->node); + argc++; + argv++; + } + args_len += argc; + args = xmalloc(args_len); + *args = NUL; + while (1) { + QUEUE *head = QUEUE_HEAD(&q); + QUEUE_REMOVE(head); + arg_T *arg = QUEUE_DATA(head, arg_T, node); + xstrlcat(args, arg->arg, args_len); + xfree(arg->arg); + xfree(arg); + if (QUEUE_EMPTY(&q)) { + break; + } else { + xstrlcat(args, " ", args_len); } } + ret = utf8_to_utf16(args, cmdline); + xfree(args); + return ret; } -static void pty_process_finish_closing(uv_handle_t *finish_async) +// Emulate quote_cmd_arg of libuv and quotes command line arguments +static void quote_cmd_arg(char *target, const char *source) + FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); - Process *proc = (Process *)ptyproc; + size_t len = strlen(source); + size_t i; + bool quote_hit = true; + char *start = target; + char tmp; - if (ptyproc->process_handle != NULL) { - CloseHandle(ptyproc->process_handle); - ptyproc->process_handle = NULL; + if (len == 0) { + *(target++) = '"'; + *(target++) = '"'; + *target = NUL; + return; } - if (proc->internal_close_cb) { - proc->internal_close_cb(proc); + + if (NULL == strpbrk(source, " \t\"")) { + strcpy(target, source); + return; + } + + if (NULL == strpbrk(source, "\"\\")) { + *(target++) = '"'; + strncpy(target, source, len); + target += len; + *(target++) = '"'; + *target = NUL; + return; + } + + *(target++) = NUL; + *(target++) = '"'; + for (i = len; i > 0; --i) { + *(target++) = source[i - 1]; + + if (quote_hit && source[i - 1] == '\\') { + *(target++) = '\\'; + } else if (source[i - 1] == '"') { + quote_hit = true; + *(target++) = '\\'; + } else { + quote_hit = false; + } + } + *target = '"'; + while (start < target) { + tmp = *start; + *start = *target; + *target = tmp; + start++; + target--; } + return; } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 87b2b6545d..806857f130 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -5,19 +5,24 @@ #include -#include "nvim/event/libuv_process.h" +#include "nvim/event/process.h" +#include "nvim/lib/queue.h" typedef struct pty_process { Process process; char *term_name; uint16_t width, height; winpty_t *wp; - uv_async_t finish_async; HANDLE finish_wait; HANDLE process_handle; - bool is_closing; + uv_timer_t wait_eof_timer; } PtyProcess; +typedef struct arg_S { + char *arg; + QUEUE node; +} arg_T; + static inline PtyProcess pty_process_init(Loop *loop, void *data) { PtyProcess rv; @@ -26,10 +31,8 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.width = 80; rv.height = 24; rv.wp = NULL; - // XXX: Zero rv.finish_async somehow? rv.finish_wait = NULL; rv.process_handle = NULL; - rv.is_closing = false; return rv; } -- cgit From e635754e8e9a92d1537ccc980c63eaa2e9d732bb Mon Sep 17 00:00:00 2001 From: erw7 Date: Wed, 29 Mar 2017 21:39:58 +0900 Subject: win/pty: jobstart, jobstop: fix null-pointer dereference - Make sure that proc->in is not NULL, because nvim crashed when starting a job with pty. - Make sure that proc->out is not NULL, because nvim crashed when stopping a job opened with pty. --- src/nvim/os/pty_process_win.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 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 ea95c1bb09..101f468005 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -19,7 +19,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); Process *proc = (Process *)ptyproc; - if (!uv_is_readable(proc->out->uvstream)) { + if (!proc->out || !uv_is_readable(proc->out->uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); } @@ -50,7 +50,7 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *in_req = NULL, *out_req = NULL; wchar_t *cmdline = NULL, *cwd = NULL; - assert(proc->in && proc->out && !proc->err); + assert(!proc->err); if (!(cfg = winpty_config_new( WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { @@ -71,18 +71,22 @@ int pty_process_spawn(PtyProcess *ptyproc) if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { goto cleanup; } - in_req = xmalloc(sizeof(uv_connect_t)); - out_req = xmalloc(sizeof(uv_connect_t)); - uv_pipe_connect( - in_req, - &proc->in->uv.pipe, - in_name, - pty_process_connect_cb); - uv_pipe_connect( - out_req, - &proc->out->uv.pipe, - out_name, - pty_process_connect_cb); + if (proc->in) { + in_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + in_req, + &proc->in->uv.pipe, + in_name, + pty_process_connect_cb); + } + if (proc->out) { + out_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + out_req, + &proc->out->uv.pipe, + out_name, + pty_process_connect_cb); + } if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { goto cleanup; @@ -107,7 +111,7 @@ int pty_process_spawn(PtyProcess *ptyproc) abort(); } - while (in_req->handle || out_req->handle) { + while ((in_req && in_req->handle) || (out_req && out_req->handle)) { uv_run(&proc->loop->uv, UV_RUN_ONCE); } -- cgit From 3b992f16889b45215ab6f867edaec5201776d579 Mon Sep 17 00:00:00 2001 From: erw7 Date: Thu, 30 Mar 2017 18:30:40 +0900 Subject: win/pty: quote_cmd_arg(): check bounds --- src/nvim/os/pty_process_win.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 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 101f468005..f3efe87d78 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -210,9 +210,10 @@ static int build_cmdline(char **argv, wchar_t **cmdline) QUEUE_INIT(&q); while (*argv) { + size_t buf_len = strlen(*argv) * 2 + 3; arg_T *arg = xmalloc(sizeof(arg_T)); - arg->arg = (char *)xmalloc(strlen(*argv) * 2 + 3); - quote_cmd_arg(arg->arg, *argv); + arg->arg = (char *)xmalloc(buf_len); + quote_cmd_arg(arg->arg, buf_len, *argv); args_len += strlen(arg->arg); QUEUE_INIT(&arg->node); QUEUE_INSERT_TAIL(&q, &arg->node); @@ -241,50 +242,50 @@ static int build_cmdline(char **argv, wchar_t **cmdline) } // Emulate quote_cmd_arg of libuv and quotes command line arguments -static void quote_cmd_arg(char *target, const char *source) +static void quote_cmd_arg(char *target, size_t remain, const char *source) FUNC_ATTR_NONNULL_ALL { - size_t len = strlen(source); + size_t src_len = strlen(source); size_t i; bool quote_hit = true; char *start = target; char tmp; - if (len == 0) { - *(target++) = '"'; - *(target++) = '"'; - *target = NUL; + if (src_len == 0) { + snprintf(target, remain, "\"\""); return; } if (NULL == strpbrk(source, " \t\"")) { - strcpy(target, source); + xstrlcpy(target, source, remain); return; } if (NULL == strpbrk(source, "\"\\")) { - *(target++) = '"'; - strncpy(target, source, len); - target += len; - *(target++) = '"'; - *target = NUL; + snprintf(target, remain, "\"%s\"", source); return; } + assert(remain--); *(target++) = NUL; + assert(remain--); *(target++) = '"'; - for (i = len; i > 0; --i) { + for (i = src_len; i > 0; i--) { + assert(remain--); *(target++) = source[i - 1]; if (quote_hit && source[i - 1] == '\\') { + assert(remain--); *(target++) = '\\'; } else if (source[i - 1] == '"') { quote_hit = true; + assert(remain--); *(target++) = '\\'; } else { quote_hit = false; } } + assert(remain); *target = '"'; while (start < target) { tmp = *start; -- cgit From 84fb794da653fc3bcc2832816ca0cab01b4e0400 Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 2 Apr 2017 18:16:39 +0900 Subject: win/pyt: cleanup --- src/nvim/os/pty_process_win.c | 273 +++++++++++++++++++++++++----------------- src/nvim/os/pty_process_win.h | 13 +- 2 files changed, 173 insertions(+), 113 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 f3efe87d78..6a08e2868b 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -12,19 +12,6 @@ # include "os/pty_process_win.c.generated.h" #endif -static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) - FUNC_ATTR_NONNULL_ALL -{ - PtyProcess *ptyproc = - (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); - Process *proc = (Process *)ptyproc; - - if (!proc->out || !uv_is_readable(proc->out->uvstream)) { - uv_timer_stop(&ptyproc->wait_eof_timer); - pty_process_finish2(ptyproc); - } -} - static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) FUNC_ATTR_NONNULL_ALL { @@ -36,6 +23,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } +/// @returns zero on sucess, or error code of winpty or MultiByteToWideChar. int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -44,34 +32,39 @@ int pty_process_spawn(PtyProcess *ptyproc) winpty_error_ptr_t err = NULL; winpty_config_t *cfg = NULL; winpty_spawn_config_t *spawncfg = NULL; - winpty_t *wp = NULL; - char *in_name = NULL, *out_name = NULL; + winpty_t *winpty_object = NULL; + char *in_name = NULL; + char *out_name = NULL; HANDLE process_handle = NULL; - uv_connect_t *in_req = NULL, *out_req = NULL; - wchar_t *cmdline = NULL, *cwd = NULL; + uv_connect_t *in_req = NULL; + uv_connect_t *out_req = NULL; + wchar_t *cmd_line = NULL; + wchar_t *cwd = NULL; assert(!proc->err); - if (!(cfg = winpty_config_new( - WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { + cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); + if (cfg == NULL) { goto cleanup; } - winpty_config_set_initial_size( - cfg, - ptyproc->width, - ptyproc->height); - if (!(wp = winpty_open(cfg, &err))) { + winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_object = winpty_open(cfg, &err); + if (winpty_object == NULL) { goto cleanup; } - if ((status = utf16_to_utf8(winpty_conin_name(wp), &in_name))) { + status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); + if (status != 0) { goto cleanup; } - if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { + + status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); + if (status != 0) { goto cleanup; } - if (proc->in) { + + if (proc->in != NULL) { in_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( in_req, @@ -79,7 +72,8 @@ int pty_process_spawn(PtyProcess *ptyproc) in_name, pty_process_connect_cb); } - if (proc->out) { + + if (proc->out != NULL) { out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( out_req, @@ -88,46 +82,65 @@ int pty_process_spawn(PtyProcess *ptyproc) pty_process_connect_cb); } - if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { - goto cleanup; + if (proc->cwd != NULL) { + status = utf8_to_utf16(proc->cwd, &cwd); + if (status != 0) { + goto cleanup; + } } - if ((status = build_cmdline(proc->argv, &cmdline))) { + + status = build_cmd_line(proc->argv, &cmd_line); + if (status != 0) { goto cleanup; } - if (!(spawncfg = winpty_spawn_config_new( + + spawncfg = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, - NULL, cmdline, cwd, NULL, &err))) { + NULL, // Optional application name + cmd_line, + cwd, + NULL, // Optional environment variables + &err); + if (spawncfg == NULL) { goto cleanup; } - if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { + + if (!winpty_spawn(winpty_object, + spawncfg, + &process_handle, + NULL, // Optional thread handle + NULL, // Optional create process error + &err)) { goto cleanup; } proc->pid = GetProcessId(process_handle); if (!RegisterWaitForSingleObject( &ptyproc->finish_wait, - process_handle, pty_process_finish1, ptyproc, - INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { + process_handle, + pty_process_finish1, + ptyproc, + INFINITE, + WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { abort(); } - while ((in_req && in_req->handle) || (out_req && out_req->handle)) { + // Wait until pty_process_connect_cb is called. + while ((in_req != NULL && in_req->handle != NULL) + || (out_req != NULL && out_req->handle != NULL)) { uv_run(&proc->loop->uv, UV_RUN_ONCE); } - ptyproc->wp = wp; + ptyproc->winpty_object = winpty_object; ptyproc->process_handle = process_handle; - wp = NULL; + winpty_object = NULL; process_handle = NULL; cleanup: - if (err != NULL) { - status = (int)winpty_error_code(err); - } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); - winpty_free(wp); + winpty_free(winpty_object); xfree(in_name); xfree(out_name); if (process_handle != NULL) { @@ -135,7 +148,7 @@ cleanup: } xfree(in_req); xfree(out_req); - xfree(cmdline); + xfree(cmd_line); xfree(cwd); return status; } @@ -144,8 +157,8 @@ void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) FUNC_ATTR_NONNULL_ALL { - if (ptyproc->wp != NULL) { - winpty_set_size(ptyproc->wp, width, height, NULL); + if (ptyproc->winpty_object != NULL) { + winpty_set_size(ptyproc->winpty_object, width, height, NULL); } } @@ -164,9 +177,9 @@ void pty_process_close(PtyProcess *ptyproc) void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { - if (ptyproc->wp != NULL) { - winpty_free(ptyproc->wp); - ptyproc->wp = NULL; + if (ptyproc->winpty_object != NULL) { + winpty_free(ptyproc->winpty_object); + ptyproc->winpty_object = NULL; } } @@ -182,6 +195,19 @@ static void pty_process_connect_cb(uv_connect_t *req, int status) req->handle = NULL; } +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = + (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + Process *proc = (Process *)ptyproc; + + if (!proc->out || !uv_is_readable(proc->out->uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } +} + static void pty_process_finish2(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -200,99 +226,132 @@ static void pty_process_finish2(PtyProcess *ptyproc) proc->internal_exit_cb(proc); } -static int build_cmdline(char **argv, wchar_t **cmdline) +/// Build the command line to pass to CreateProcessW. +/// +/// @param[in] argv Array with string arguments. +/// @param[out] cmd_line Location where saved bulded cmd line. +/// +/// @returns zero on sucess, or error code of MultiByteToWideChar function. +/// +static int build_cmd_line(char **argv, wchar_t **cmd_line) FUNC_ATTR_NONNULL_ALL { - char *args = NULL; - size_t args_len = 0, argc = 0; - int ret; - QUEUE q; - QUEUE_INIT(&q); + size_t utf8_cmd_line_len = 0; + size_t argc = 0; + QUEUE args_q; + QUEUE_INIT(&args_q); while (*argv) { size_t buf_len = strlen(*argv) * 2 + 3; - arg_T *arg = xmalloc(sizeof(arg_T)); - arg->arg = (char *)xmalloc(buf_len); - quote_cmd_arg(arg->arg, buf_len, *argv); - args_len += strlen(arg->arg); - QUEUE_INIT(&arg->node); - QUEUE_INSERT_TAIL(&q, &arg->node); + ArgNode *arg_node = xmalloc(sizeof(*arg_node)); + arg_node->arg = xmalloc(buf_len); + quote_cmd_arg(arg_node->arg, buf_len, *argv); + utf8_cmd_line_len += strlen(arg_node->arg); + QUEUE_INIT(&arg_node->node); + QUEUE_INSERT_TAIL(&args_q, &arg_node->node); argc++; argv++; } - args_len += argc; - args = xmalloc(args_len); - *args = NUL; + + utf8_cmd_line_len += argc; + char *utf8_cmd_line = xmalloc(utf8_cmd_line_len); + *utf8_cmd_line = NUL; while (1) { - QUEUE *head = QUEUE_HEAD(&q); + QUEUE *head = QUEUE_HEAD(&args_q); QUEUE_REMOVE(head); - arg_T *arg = QUEUE_DATA(head, arg_T, node); - xstrlcat(args, arg->arg, args_len); - xfree(arg->arg); - xfree(arg); - if (QUEUE_EMPTY(&q)) { + ArgNode *arg_node = QUEUE_DATA(head, ArgNode, node); + xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len); + xfree(arg_node->arg); + xfree(arg_node); + if (QUEUE_EMPTY(&args_q)) { break; } else { - xstrlcat(args, " ", args_len); + xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len); } } - ret = utf8_to_utf16(args, cmdline); - xfree(args); - return ret; + + int result = utf8_to_utf16(utf8_cmd_line, cmd_line); + if (result != 0) { + } + xfree(utf8_cmd_line); + return result; } -// Emulate quote_cmd_arg of libuv and quotes command line arguments -static void quote_cmd_arg(char *target, size_t remain, const char *source) +/// Emulate quote_cmd_arg of libuv and quotes command line argument. +/// Most of the code came from libuv. +/// +/// @param[out] dist Location where saved quotes argument. +/// @param dist_remaining Deistnation buffer size. +/// @param[in] src Pointer to argument. +/// +static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) FUNC_ATTR_NONNULL_ALL { - size_t src_len = strlen(source); - size_t i; + size_t src_len = strlen(src); bool quote_hit = true; - char *start = target; - char tmp; + char *start = dist; if (src_len == 0) { - snprintf(target, remain, "\"\""); + // Need double quotation for empty argument. + snprintf(dist, dist_remaining, "\"\""); return; } - if (NULL == strpbrk(source, " \t\"")) { - xstrlcpy(target, source, remain); + if (NULL == strpbrk(src, " \t\"")) { + // No quotation needed. + xstrlcpy(dist, src, dist_remaining); return; } - if (NULL == strpbrk(source, "\"\\")) { - snprintf(target, remain, "\"%s\"", source); + if (NULL == strpbrk(src, "\"\\")) { + // No embedded double quotes or backlashes, so I can just wrap quote marks. + // around the whole thing. + snprintf(dist, dist_remaining, "\"%s\"", src); return; } - assert(remain--); - *(target++) = NUL; - assert(remain--); - *(target++) = '"'; - for (i = src_len; i > 0; i--) { - assert(remain--); - *(target++) = source[i - 1]; - - if (quote_hit && source[i - 1] == '\\') { - assert(remain--); - *(target++) = '\\'; - } else if (source[i - 1] == '"') { + // 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\\" + + assert(dist_remaining--); + *(dist++) = NUL; + assert(dist_remaining--); + *(dist++) = '"'; + for (size_t i = src_len; i > 0; i--) { + assert(dist_remaining--); + *(dist++) = src[i - 1]; + if (quote_hit && src[i - 1] == '\\') { + assert(dist_remaining--); + *(dist++) = '\\'; + } else if (src[i - 1] == '"') { quote_hit = true; - assert(remain--); - *(target++) = '\\'; + assert(dist_remaining--); + *(dist++) = '\\'; } else { quote_hit = false; } } - assert(remain); - *target = '"'; - while (start < target) { - tmp = *start; - *start = *target; - *target = tmp; + assert(dist_remaining); + *dist = '"'; + + while (start < dist) { + char tmp = *start; + *start = *dist; + *dist = tmp; start++; - target--; + dist--; } - return; } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 806857f130..59e0fad7f7 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -12,16 +12,17 @@ typedef struct pty_process { Process process; char *term_name; uint16_t width, height; - winpty_t *wp; + winpty_t *winpty_object; HANDLE finish_wait; HANDLE process_handle; uv_timer_t wait_eof_timer; } PtyProcess; -typedef struct arg_S { - char *arg; - QUEUE node; -} arg_T; +// Structure used by build_cmd_line() +typedef struct arg_node { + char *arg; // pointer to argument. + QUEUE node; // QUEUE structure. +} ArgNode; static inline PtyProcess pty_process_init(Loop *loop, void *data) { @@ -30,7 +31,7 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.term_name = NULL; rv.width = 80; rv.height = 24; - rv.wp = NULL; + rv.winpty_object = NULL; rv.finish_wait = NULL; rv.process_handle = NULL; return rv; -- cgit From d3a8c4f99289f7b65a68bf9ed5eeab34aa688e0e Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 2 Apr 2017 18:32:23 +0900 Subject: win/pty: log errors --- src/nvim/os/pty_process_win.c | 112 ++++++++++++++++++++++++++++++------------ src/nvim/os/pty_process_win.h | 1 - 2 files changed, 80 insertions(+), 33 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 6a08e2868b..4bb5f64455 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -2,7 +2,9 @@ #include #include -#include "nvim/vim.h" +#include + +#include "nvim/os/os.h" #include "nvim/ascii.h" #include "nvim/memory.h" #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 @@ -23,7 +25,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } -/// @returns zero on sucess, or error code of winpty or MultiByteToWideChar. +/// @returns zero on success, or negative error code. int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -40,27 +42,32 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *out_req = NULL; wchar_t *cmd_line = NULL; wchar_t *cwd = NULL; + const char *emsg = NULL; assert(!proc->err); cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); if (cfg == NULL) { + emsg = "Failed, winpty_config_new."; goto cleanup; } winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); winpty_object = winpty_open(cfg, &err); if (winpty_object == NULL) { + emsg = "Failed, winpty_open."; goto cleanup; } status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); if (status != 0) { + emsg = "Failed to convert in_name from utf16 to utf8."; goto cleanup; } status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); if (status != 0) { + emsg = "Failed to convert out_name from utf16 to utf8."; goto cleanup; } @@ -85,12 +92,14 @@ int pty_process_spawn(PtyProcess *ptyproc) if (proc->cwd != NULL) { status = utf8_to_utf16(proc->cwd, &cwd); if (status != 0) { + emsg = "Failed to convert pwd form utf8 to utf16."; goto cleanup; } } status = build_cmd_line(proc->argv, &cmd_line); if (status != 0) { + emsg = "Failed to convert cmd line form utf8 to utf16."; goto cleanup; } @@ -102,15 +111,23 @@ int pty_process_spawn(PtyProcess *ptyproc) NULL, // Optional environment variables &err); if (spawncfg == NULL) { + emsg = "Failed winpty_spawn_config_new."; goto cleanup; } + DWORD win_err = 0; if (!winpty_spawn(winpty_object, spawncfg, &process_handle, NULL, // Optional thread handle - NULL, // Optional create process error + &win_err, &err)) { + if (win_err) { + status = (int)win_err; + emsg = "Failed spawn process."; + } else { + emsg = "Failed winpty_spawn."; + } goto cleanup; } proc->pid = GetProcessId(process_handle); @@ -137,6 +154,15 @@ int pty_process_spawn(PtyProcess *ptyproc) process_handle = NULL; cleanup: + if (status) { + // In the case of an error of MultiByteToWideChar or CreateProcessW. + ELOG("%s error code: %d", emsg, status); + status = os_translate_sys_error(status); + } else if (err != NULL) { + status = (int)winpty_error_code(err); + ELOG("%s error code: %d", emsg, status); + status = translate_winpty_error(status); + } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); @@ -198,8 +224,7 @@ static void pty_process_connect_cb(uv_connect_t *req, int status) static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + PtyProcess *ptyproc = wait_eof_timer->data; Process *proc = (Process *)ptyproc; if (!proc->out || !uv_is_readable(proc->out->uvstream)) { @@ -229,9 +254,9 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// Build the command line to pass to CreateProcessW. /// /// @param[in] argv Array with string arguments. -/// @param[out] cmd_line Location where saved bulded cmd line. +/// @param[out] cmd_line Location where saved builded cmd line. /// -/// @returns zero on sucess, or error code of MultiByteToWideChar function. +/// @returns zero on success, or error code of MultiByteToWideChar function. /// static int build_cmd_line(char **argv, wchar_t **cmd_line) FUNC_ATTR_NONNULL_ALL @@ -271,8 +296,6 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) } int result = utf8_to_utf16(utf8_cmd_line, cmd_line); - if (result != 0) { - } xfree(utf8_cmd_line); return result; } @@ -280,33 +303,33 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) /// Emulate quote_cmd_arg of libuv and quotes command line argument. /// Most of the code came from libuv. /// -/// @param[out] dist Location where saved quotes argument. -/// @param dist_remaining Deistnation buffer size. +/// @param[out] dest Location where saved quotes argument. +/// @param dest_remaining Destination buffer size. /// @param[in] src Pointer to argument. /// -static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) +static void quote_cmd_arg(char *dest, size_t dest_remaining, const char *src) FUNC_ATTR_NONNULL_ALL { size_t src_len = strlen(src); bool quote_hit = true; - char *start = dist; + char *start = dest; if (src_len == 0) { // Need double quotation for empty argument. - snprintf(dist, dist_remaining, "\"\""); + snprintf(dest, dest_remaining, "\"\""); return; } if (NULL == strpbrk(src, " \t\"")) { // No quotation needed. - xstrlcpy(dist, src, dist_remaining); + xstrlcpy(dest, src, dest_remaining); return; } if (NULL == strpbrk(src, "\"\\")) { // No embedded double quotes or backlashes, so I can just wrap quote marks. // around the whole thing. - snprintf(dist, dist_remaining, "\"%s\"", src); + snprintf(dest, dest_remaining, "\"%s\"", src); return; } @@ -326,32 +349,57 @@ static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) // input : hello world\ // output: "hello world\\" - assert(dist_remaining--); - *(dist++) = NUL; - assert(dist_remaining--); - *(dist++) = '"'; + assert(dest_remaining--); + *(dest++) = NUL; + assert(dest_remaining--); + *(dest++) = '"'; for (size_t i = src_len; i > 0; i--) { - assert(dist_remaining--); - *(dist++) = src[i - 1]; + assert(dest_remaining--); + *(dest++) = src[i - 1]; if (quote_hit && src[i - 1] == '\\') { - assert(dist_remaining--); - *(dist++) = '\\'; + assert(dest_remaining--); + *(dest++) = '\\'; } else if (src[i - 1] == '"') { quote_hit = true; - assert(dist_remaining--); - *(dist++) = '\\'; + assert(dest_remaining--); + *(dest++) = '\\'; } else { quote_hit = false; } } - assert(dist_remaining); - *dist = '"'; + assert(dest_remaining); + *dest = '"'; - while (start < dist) { + while (start < dest) { char tmp = *start; - *start = *dist; - *dist = tmp; + *start = *dest; + *dest = tmp; start++; - dist--; + dest--; + } +} + +/// Translate winpty error code to libuv error. +/// +/// @param[in] winpty_errno Winpty error code returned by winpty_error_code +/// function. +/// +/// @returns Error code of libuv error. +int translate_winpty_error(int winpty_errno) +{ + if (winpty_errno <= 0) { + return winpty_errno; // If < 0 then it's already a libuv error. + } + + switch (winpty_errno) { + case WINPTY_ERROR_OUT_OF_MEMORY: return UV_ENOMEM; + case WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED: return UV_EAI_FAIL; + case WINPTY_ERROR_LOST_CONNECTION: return UV_ENOTCONN; + case WINPTY_ERROR_AGENT_EXE_MISSING: return UV_ENOENT; + case WINPTY_ERROR_UNSPECIFIED: return UV_UNKNOWN; + case WINPTY_ERROR_AGENT_DIED: return UV_ESRCH; + case WINPTY_ERROR_AGENT_TIMEOUT: return UV_ETIMEDOUT; + case WINPTY_ERROR_AGENT_CREATION_FAILED: return UV_EAI_FAIL; + default: return UV_UNKNOWN; } } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 59e0fad7f7..1a4019e654 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -2,7 +2,6 @@ #define NVIM_OS_PTY_PROCESS_WIN_H #include - #include #include "nvim/event/process.h" -- cgit From 8c1782b84070d786cc1a7dfd6242a9ebc0923ad3 Mon Sep 17 00:00:00 2001 From: erw7 Date: Fri, 14 Apr 2017 22:35:56 +0900 Subject: pty_process_win: avoid quoting for cmd.exe --- src/nvim/os/pty_process_win.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 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 4bb5f64455..ef8a699c56 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -97,7 +97,8 @@ int pty_process_spawn(PtyProcess *ptyproc) } } - status = build_cmd_line(proc->argv, &cmd_line); + status = build_cmd_line(proc->argv, &cmd_line, + os_shell_is_cmdexe(proc->argv[0])); if (status != 0) { emsg = "Failed to convert cmd line form utf8 to utf16."; goto cleanup; @@ -258,7 +259,7 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// /// @returns zero on success, or error code of MultiByteToWideChar function. /// -static int build_cmd_line(char **argv, wchar_t **cmd_line) +static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe) FUNC_ATTR_NONNULL_ALL { size_t utf8_cmd_line_len = 0; @@ -267,10 +268,14 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) QUEUE_INIT(&args_q); while (*argv) { - size_t buf_len = strlen(*argv) * 2 + 3; + size_t buf_len = is_cmdexe ? (strlen(*argv) + 1) : (strlen(*argv) * 2 + 3); ArgNode *arg_node = xmalloc(sizeof(*arg_node)); arg_node->arg = xmalloc(buf_len); - quote_cmd_arg(arg_node->arg, buf_len, *argv); + if (is_cmdexe) { + xstrlcpy(arg_node->arg, *argv, buf_len); + } else { + quote_cmd_arg(arg_node->arg, buf_len, *argv); + } utf8_cmd_line_len += strlen(arg_node->arg); QUEUE_INIT(&arg_node->node); QUEUE_INSERT_TAIL(&args_q, &arg_node->node); -- cgit From d135ba99b2945ca18fe3246cbb89a0920868ccf6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Nov 2017 22:59:58 +0100 Subject: os_open, os_stat: UV_EINVAL on NULL filename EINVAL (instead of EFAULT) because that's what glibc does: https://github.com/bminor/glibc/blob/master/io/open.c#L35 os_nodetype: check for UV_EINVAL explicitly. ref #4370 ref https://github.com/neovim/neovim/issues/4370#issuecomment-344366571 ref ac055d677aa9eff9fca11cecb5ac7f7a4edb0265 ref #4772 --- src/nvim/os/fs.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 78627f8703..c5049acda9 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -172,7 +172,7 @@ int os_nodetype(const char *name) | O_NONBLOCK #endif , 0); - if (fd == -1) { + if (fd == UV_EINVAL) { return NODE_OTHER; // open() failed. } @@ -394,9 +394,11 @@ end: /// @param mode Permissions for the newly-created file (IGNORED if 'flags' is /// not `O_CREAT` or `O_TMPFILE`), subject to the current umask /// @return file descriptor, or libuv error code on failure -int os_open(const char* path, int flags, int mode) - FUNC_ATTR_NONNULL_ALL +int os_open(const char *path, int flags, int mode) { + if (path == NULL) { // uv_fs_open asserts on NULL. #7561 + return UV_EINVAL; + } int r; RUN_UV_FS_FUNC(r, uv_fs_open, path, flags, mode, NULL); return r; @@ -603,12 +605,12 @@ int os_fsync(int fd) /// Get stat information for a file. /// -/// @return libuv return code. +/// @return libuv return code, or -errno static int os_stat(const char *name, uv_stat_t *statbuf) FUNC_ATTR_NONNULL_ARG(2) { if (!name) { - return UV_ENOENT; + return UV_EINVAL; } uv_fs_t request; int result = uv_fs_stat(&fs_loop, &request, name, NULL); -- cgit From bf3f0efb3a98afbcc15020cc3399c71db8f831e7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 16 Nov 2017 01:00:47 +0100 Subject: os_nodetype: rework Make the Windows impl closer to Vim os_win32.c, and the Unix impl closer to Vim os_unix.c. Outcomes: - Do not send negative fd to close(). ref #4806 #4772 #6860 - Fallback return-value is now correct in (hopefully) all cases. - unix: check S_ISXXX instead of relying on os_open (which can fail for irrelevant reasons). buf_write() expects NODE_WRITABLE for character devices such as /dev/stderr. 96f834a8424e --- src/nvim/os/fs.c | 84 ++++++++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 45 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index c5049acda9..aa28b95c30 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -133,22 +133,12 @@ bool os_isdir(const char_u *name) int os_nodetype(const char *name) FUNC_ATTR_NONNULL_ALL { -#ifdef WIN32 - // Edge case from Vim os_win32.c: - // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read - // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. - if (STRNCMP(name, "\\\\.\\", 4) == 0) { - return NODE_WRITABLE; - } -#endif - +#ifndef WIN32 // Unix uv_stat_t statbuf; if (0 != os_stat(name, &statbuf)) { return NODE_NORMAL; // File doesn't exist. } - -#ifndef WIN32 - // libuv does not handle BLK and DIR in uv_handle_type. + // uv_handle_type does not distinguish BLK and DIR. // Related: https://github.com/joyent/libuv/pull/1421 if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) { return NODE_NORMAL; @@ -156,48 +146,51 @@ int os_nodetype(const char *name) if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable return NODE_OTHER; } -#endif + // Everything else is writable? + // buf_write() expects NODE_WRITABLE for char device /dev/stderr. + return NODE_WRITABLE; +#else // Windows + // Edge case from Vim os_win32.c: + // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read + // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. + if (STRNCMP(name, "\\\\.\\", 4) == 0) { + return NODE_WRITABLE; + } - // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015): - // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { - // wn = enc_to_utf16(name, NULL); - // hFile = CreatFile(wn, ...) - // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we - // give it. uv_fs_open calls fs__capture_path which does a similar dance and - // saves us the hassle. + // Vim os_win32.c:mch_nodetype does (since 7.4.015): + // wn = enc_to_utf16(name, NULL); + // hFile = CreatFile(wn, ...) + // to get a HANDLE. Whereas libuv just calls _get_osfhandle() on the fd we + // give it. But uv_fs_open later calls fs__capture_path which does a similar + // utf8-to-utf16 dance and saves us the hassle. - int nodetype = NODE_WRITABLE; + // macOS: os_open(/dev/stderr) would return UV_EACCES. int fd = os_open(name, O_RDONLY -#ifdef O_NONBLOCK +# ifdef O_NONBLOCK | O_NONBLOCK -#endif +# endif , 0); - if (fd == UV_EINVAL) { - return NODE_OTHER; // open() failed. + if (fd < 0) { // open() failed. + return NODE_NORMAL; + } + int guess = uv_guess_handle(fd); + if (close(fd) == -1) { + ELOG("close(%d) failed. name='%s'", fd, name); } - switch (uv_guess_handle(fd)) { - case UV_TTY: // FILE_TYPE_CHAR - nodetype = NODE_WRITABLE; - break; - case UV_FILE: // FILE_TYPE_DISK - nodetype = NODE_NORMAL; - break; - case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c - case UV_UDP: // unix only - case UV_TCP: // unix only + switch (guess) { + case UV_TTY: // FILE_TYPE_CHAR + return NODE_WRITABLE; + case UV_FILE: // FILE_TYPE_DISK + return NODE_NORMAL; + case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c + case UV_UDP: // unix only + case UV_TCP: // unix only case UV_UNKNOWN_HANDLE: default: -#ifdef WIN32 - nodetype = NODE_NORMAL; -#else - nodetype = NODE_WRITABLE; // Everything else is writable? -#endif - break; + return NODE_OTHER; // Vim os_win32.c default } - - close(fd); - return nodetype; +#endif } /// Gets the absolute path of the currently running executable. @@ -1080,7 +1073,8 @@ shortcut_end: #endif -int os_translate_sys_error(int sys_errno) { +int os_translate_sys_error(int sys_errno) +{ #ifdef HAVE_UV_TRANSLATE_SYS_ERROR return uv_translate_sys_error(sys_errno); #elif defined(WIN32) -- cgit From 5215e3205a07b85e4e4cf1f8a8ca6be2b9556459 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 27 Aug 2017 11:59:33 +0200 Subject: channels: refactor --- src/nvim/os/pty_process_unix.c | 10 +++++----- src/nvim/os/pty_process_win.c | 12 ++++++------ src/nvim/os/shell.c | 26 +++++++++++--------------- 3 files changed, 22 insertions(+), 26 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 ee3ab96a83..e39f837c1a 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -47,7 +47,7 @@ int pty_process_spawn(PtyProcess *ptyproc) int status = 0; // zero or negative error code (libuv convention) Process *proc = (Process *)ptyproc; - assert(!proc->err); + assert(proc->err.closed); uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 }; uv_disable_stdio_inheritance(); @@ -83,12 +83,12 @@ int pty_process_spawn(PtyProcess *ptyproc) goto error; } - if (proc->in - && (status = set_duplicating_descriptor(master, &proc->in->uv.pipe))) { + if (!proc->in.closed + && (status = set_duplicating_descriptor(master, &proc->in.uv.pipe))) { goto error; } - if (proc->out - && (status = set_duplicating_descriptor(master, &proc->out->uv.pipe))) { + if (!proc->out.closed + && (status = set_duplicating_descriptor(master, &proc->out.uv.pipe))) { goto error; } diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index ef8a699c56..3c4839a076 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -44,7 +44,7 @@ int pty_process_spawn(PtyProcess *ptyproc) wchar_t *cwd = NULL; const char *emsg = NULL; - assert(!proc->err); + assert(proc->err.closed); cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); if (cfg == NULL) { @@ -71,20 +71,20 @@ int pty_process_spawn(PtyProcess *ptyproc) goto cleanup; } - if (proc->in != NULL) { + if (!proc->in.closed) { in_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( in_req, - &proc->in->uv.pipe, + &proc->in.uv.pipe, in_name, pty_process_connect_cb); } - if (proc->out != NULL) { + if (!proc->out.closed) { out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( out_req, - &proc->out->uv.pipe, + &proc->out.uv.pipe, out_name, pty_process_connect_cb); } @@ -228,7 +228,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) PtyProcess *ptyproc = wait_eof_timer->data; Process *proc = (Process *)ptyproc; - if (!proc->out || !uv_is_readable(proc->out->uvstream)) { + if (proc->out.closed || !uv_is_readable(proc->out.uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); } diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 32e9a70e57..f54fe412ba 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -207,16 +207,12 @@ static int do_os_system(char **argv, char prog[MAXPATHL]; xstrlcpy(prog, argv[0], MAXPATHL); - Stream in, out, err; LibuvProcess uvproc = libuv_process_init(&main_loop, &buf); Process *proc = &uvproc.process; MultiQueue *events = multiqueue_new_child(main_loop.events); proc->events = events; proc->argv = argv; - proc->in = input != NULL ? &in : NULL; - proc->out = &out; - proc->err = &err; - int status = process_spawn(proc); + int status = process_spawn(proc, input != NULL, true, true); if (status) { loop_poll_events(&main_loop, 0); // Failed, probably 'shell' is not executable. @@ -236,27 +232,27 @@ static int do_os_system(char **argv, // streams while there's still data in the OS buffer (due to the process // exiting before all data is read). if (input != NULL) { - proc->in->events = NULL; - wstream_init(proc->in, 0); + proc->in.events = NULL; + wstream_init(&proc->in, 0); } - proc->out->events = NULL; - rstream_init(proc->out, 0); - rstream_start(proc->out, data_cb, &buf); - proc->err->events = NULL; - rstream_init(proc->err, 0); - rstream_start(proc->err, data_cb, &buf); + proc->out.events = NULL; + rstream_init(&proc->out, 0); + rstream_start(&proc->out, data_cb, &buf); + proc->err.events = NULL; + rstream_init(&proc->err, 0); + 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 (!wstream_write(&in, input_buffer)) { + if (!wstream_write(&proc->in, input_buffer)) { // couldn't write, stop the process and tell the user about it process_stop(proc); return -1; } // close the input stream after everything is written - wstream_set_write_cb(&in, shell_write_cb, NULL); + wstream_set_write_cb(&proc->in, shell_write_cb, NULL); } // Invoke busy_start here so LOOP_PROCESS_EVENTS_UNTIL will not change the -- cgit From 1ebc96fe10fbdbec22caa26d5d52a9f095da9687 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 5 Jun 2017 08:29:10 +0200 Subject: channels: allow bytes sockets and stdio, and buffered bytes output --- src/nvim/os/shell.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index f54fe412ba..ec2ebb9d19 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -227,18 +227,15 @@ static int do_os_system(char **argv, return -1; } - // We want to deal with stream events as fast a possible while queueing - // process events, so reset everything to NULL. It prevents closing the + // Note: unlike process events, stream events are not queued, as we want to + // 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) { - proc->in.events = NULL; wstream_init(&proc->in, 0); } - proc->out.events = NULL; rstream_init(&proc->out, 0); rstream_start(&proc->out, data_cb, &buf); - proc->err.events = NULL; rstream_init(&proc->err, 0); rstream_start(&proc->err, data_cb, &buf); -- cgit From 90e5cc5484ceeb410ae2a2706e09ed475cade4a5 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 8 Jun 2017 17:15:53 +0200 Subject: channels: generalize jobclose() --- src/nvim/os/shell.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/nvim/os') diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index ec2ebb9d19..e32c6e05d2 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -677,10 +677,6 @@ static void shell_write_cb(Stream *stream, void *data, int status) msg_schedule_emsgf(_("E5677: Error writing input to shell-command: %s"), uv_err_name(status)); } - if (stream->closed) { // Process may have exited before this write. - WLOG("stream was already closed"); - return; - } stream_close(stream, NULL, NULL); } -- cgit From 9acd7bfe25b5ea2b31ffbbdbd201f5f09afc4237 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 10 Jul 2017 01:58:54 +0200 Subject: tui: job-control: use saved termios for pty jobs On startup, if running in a terminal, save the termios properties. Use the saved termios for `:terminal` and `jobstart()` pty jobs. This won't affect nvim spawned outside of a terminal. questions: - This affects `:terminal` and `jobstart({'pty':v:true})`. Should we be more conservative for `jobstart({'pty':v:true})` (e.g. pass NULL to forkpty() and let the OS defaults prevail)? - Note: `iutf8` would not be set in that case. --- src/nvim/os/pty_process_unix.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 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 e39f837c1a..53301e4b53 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -36,13 +36,26 @@ # include "os/pty_process_unix.c.generated.h" #endif +/// termios saved at startup (for TUI) or initialized by pty_process_spawn(). +static struct termios termios_default; + +/// Saves the termios properties associated with `tty_fd`. +/// +/// @param tty_fd TTY file descriptor, or -1 if not in a terminal. +void pty_process_save_termios(int tty_fd) +{ + if (tty_fd == -1 || tcgetattr(tty_fd, &termios_default) != 0) { + return; + } +} + /// @returns zero on success, or negative error code int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { - static struct termios termios; - if (!termios.c_cflag) { - init_termios(&termios); + if (!termios_default.c_cflag) { + // TODO(jkeyes): We could pass NULL to forkpty() instead ... + init_termios(&termios_default); } int status = 0; // zero or negative error code (libuv convention) @@ -52,7 +65,7 @@ int pty_process_spawn(PtyProcess *ptyproc) ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 }; uv_disable_stdio_inheritance(); int master; - int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize); + int pid = forkpty(&master, NULL, &termios_default, &ptyproc->winsize); if (pid < 0) { status = -errno; -- cgit From 85bc6630c0a8259c713383c8787e65f92e24e600 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 6 Sep 2017 08:16:59 +0200 Subject: input: only change mode of input fd if there is an input fd --- 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 31e06ce404..7d6f2abd7f 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -37,7 +37,7 @@ typedef enum { static Stream read_stream = {.closed = true}; static RBuffer *input_buffer = NULL; static bool input_eof = false; -static int global_fd = 0; +static int global_fd = -1; static int events_enabled = 0; static bool blocking = false; -- cgit From 8f91f2c9b47e6705e29da2fdbc9519092c757ba5 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 26 Nov 2017 21:26:56 -0500 Subject: Use defined(BSD) check when defining OPEN_CHR_FILES Rather than enumerate predefines for all BSD systems, just rely on the fact that they all "#define BSD" in sys/param.h. Debian's GNU/kFreeBSD still requires its own check, since it isn't using the BSD userspace. References: OpenBSD - https://github.com/openbsd/src/blob/210ebf9df0460bbdad02da9bbd5d859b61f57462/sys/sys/param.h#L40 FreeBSD - https://github.com/freebsd/freebsd/blob/f5d95e1f8d32db4ccccfd5ad9cecb21ed07a695d/sys/sys/param.h#L43 NetBSD - https://github.com/NetBSD/src/blob/ea620980793cf2011e5424f4a537b0488e3ffb4d/sys/sys/param.h#L49 DragonFlyBSD - https://github.com/DragonFlyBSD/DragonFlyBSD/blob/94ecf1295bb42b59772448d58ff40dd75c4a3ef8/sys/sys/param.h#L41 vim-patch:8.0.1357 --- src/nvim/os/os_defs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/os') diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 923a362b41..87f8d214bd 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include -- cgit 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