]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0512: clientserver uses binary protocol v9.2.0512
authorFoxe Chen <chen.foxe@gmail.com>
Fri, 22 May 2026 18:30:52 +0000 (18:30 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 22 May 2026 20:12:01 +0000 (20:12 +0000)
Problem:  clientserver feature uses binary protocol and is hard
          to understand
Solution: Rewrite the code based on channels and JSON messages
          (Foxe Chen).

closes: #19782

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
40 files changed:
Filelist
runtime/doc/remote.txt
runtime/doc/tags
runtime/doc/version9.txt
runtime/doc/vim.1
runtime/doc/vim.man
src/Make_cyg_ming.mak
src/Make_mvc.mak
src/Makefile
src/auto/configure
src/channel.c
src/clientserver.c
src/configure.ac
src/errors.h
src/feature.h
src/gc.c
src/getchar.c
src/globals.h
src/gui.c
src/gui_gtk_x11.c
src/json.c
src/main.c
src/os_unix.c
src/po/vim.pot
src/proto.h
src/proto/channel.pro
src/proto/gui_gtk_x11.pro
src/proto/os_unix.pro
src/proto/socketserver.pro [new file with mode: 0644]
src/socketserver.c [new file with mode: 0644]
src/structs.h
src/testdir/dumps/Test_clientserver_1.dump [new file with mode: 0644]
src/testdir/test_clientserver.vim
src/testdir/test_remote.vim
src/testdir/test_usercommands.vim
src/testdir/test_vim9_builtin.vim
src/testdir/util/check.vim
src/testdir/util/socketserver.vim [deleted file]
src/ui.c
src/version.c

index dfc828a759fe561220beecb7d8d2cb38abdc4c84..4ea946c84126a9c7be5c7d45711b1f771265fdf8 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -145,6 +145,7 @@ SRC_ALL =   \
                src/session.c \
                src/sha256.c \
                src/sign.c \
+               src/socketserver.c \
                src/sound.c \
                src/spell.c \
                src/spell.h \
@@ -344,6 +345,7 @@ SRC_ALL =   \
                src/proto/session.pro \
                src/proto/sha256.pro \
                src/proto/sign.pro \
+               src/proto/socketserver.pro \
                src/proto/sound.pro \
                src/proto/spell.pro \
                src/proto/spellfile.pro \
index 2f77fcaa578c2252bc5ac341daeb4b2798aa323d..260fc864f63974138f12782d73b2672c9f45b28e 100644 (file)
@@ -79,9 +79,10 @@ The following command line arguments are available:
                                                                *--clientserver*
    --clientserver {method}     Use the specified method {method} as the
                                backend for clientserver functionality.  Can
-                               either be "socket" or "x11".
+                               either be "socket", "x11", or "mswin".
                                {only available when compiled with both |+X11|
-                               and |+socketserver| features}
+                               and |+socketserver| features, or
+                               |+socketserver| on MS-Windows}
 
 
 Examples ~
@@ -220,12 +221,16 @@ When using gvim, the --remote-wait only works properly this way: >
 <
 ==============================================================================
 4. Socket server specific items                            *socketserver-clientserver*
-                                   *E1563* *E1564* *E1565* *E1566* *E1567*
+                                                   *E1564* *E1565* *E1566*
 
-The communication between client and server is done using Unix domain sockets.
-These sockets are either placed in these directories in the following order of
+NOTE: Vim version before patch 9.2.511 use a different socketserver backend
+which is incompatible with the new one, which is based on channels and JSON.
+
+The communication between client and server is done using channels internally.
+When using the traditional/generic clientserver naming (only available on
+Unix), sockets are placed in these directories in the following order of
 availability:
-    1. "$XDG_RUTIME_DIR/vim" if $XDG_RUNTIME_DIR is set in the environment.
+    1. "$XDG_RUNTIME_DIR/vim" if $XDG_RUNTIME_DIR is set in the environment.
     2. "$TMPDIR/vim-[uid]", where "[uid]" is the uid of the user.  This
        directory will have the access permissions set to 700 so only the user
        can read or write from/to it.  If $TMPDIR is not set, "/tmp" is used.
@@ -237,45 +242,55 @@ absolute or relative path.  If the server id starts with either a "/"
 Otherwise the server id will be the filename of the socket which will be
 placed in the above common directories.  Note that a server id/name can only
 contain slashes "/" if it is taken as a path, so names such as "abc/dir" will
-be invalid.
+be invalid.  This feature is only available on Unix.
+
+                                                   *socketserver-address*
+In addition, the socketserver can also be created as a |channel-address|. To
+do this, prefix the address with "channel:" (which will be ignored).  This is
+available on both Unix and MS-Windows, and is the only available naming for the
+socketserver backend on Windows.  However, note that |ch_listen()|
+restrictions apply, meaning only port numbers can be used for TCP sockets.
+
+If the name is prefixed with "name:", then the legacy servername behaviour is
+used, so "name:VIM" is the same as "VIM".
 
-Socket server functionality is available in both GTK GUI and terminal versions
-of Vim.  Unless Vim is compiled with |+autoservername| feature, the socket
-server will have to started explicitly, just like X11, even in the GUI.
+Unless Vim is compiled with |+autoservername| feature, the socket server will
+have to started explicitly, similar to X11. This is unless a X11 GUI Vim is
+being used.
 
 If Vim crashes or does not exit cleanly, the socket server will not remove the
 socket file and it will be left around.  This is generally not a problem,
 because if a socket name is taken, Vim checks if the socket in its place is
 dead (not attached to any process), and can replace it instead of finding a
-new name.
-
-To send commands to a Vim socket server from another application, read the
-source file src/os_unix.c, there is detailed description of the protocol used.
+new name.  This is only relevant for Unix.
 
                                                    *socketserver-differences*
-Most of the functionality is the same as X11, however unlike X11, where the
-client does not need to be a server in order to communicate with another
-server, the socket server requires the server to be running even as a client.
-The exception is |serverlist()| or the |--serverlist| argument, which does not
-require the server to be running.
+Most of the functionality is the same as the other clientserver backends.
 
-Additionally, the server id or client id will not be a number like X11 or
+However, the server id or client id will not be a number like X11 or
 MS-Windows (shown in hex representation), instead it is the absolute path to
-the socket.  This can be seen via the |v:servername| variable.
+the socket.  This can be seen via the |v:servername| variable.  If the name is
+a channel address, then it will be the address with the "a/" prefix as well.
+
+Note when using |--remote-wait|, the client id will look like "t/<some
+number>". This naming is specifically for this command, and attempting to use
+it as an address will fail.
 
-The |--serverlist| argument will act just like X11, however it only checks the
-given common directories above.  If a custom path is used for a socket, it
-will not be detected, such as a path either not in $XDG_RUNTIME_DIR or
-<$TMPDIR or /tmp>/vim of the |--serverlist| Vim process.
+If on Unix, the |--serverlist| argument will act just like X11, however it
+only checks the given common directories above.  If a custom path is used for
+a socket, it will not be detected, such as a path either not in
+$XDG_RUNTIME_DIR or <$TMPDIR or /tmp>/vim of the |--serverlist| Vim process.
+If on MS-Windows, then the |--serverlist| argument will output nothing.
 
-If you have both |+socketserver| and |+X11| compiled, you will need to add
-|--clientserver| set to "socket" in combination with |--serverlist| to list
-the available servers.  You cannot list both types of backends in one command.
+The |+socketserver| feature is automatically enabled when on Unix or
+MS-Windows, and the |+channel| feature is enabled
 
                                                    *socketserver-x11*
-If Vim is compiled with both |+X11| and |+socketserver|, then deciding which
-backend to use is done at startup time, via the |--clientserver| argument.  By
-default if it is not specified, then X11 will be used.  A Vim instance using a
-socket server cannot communicate with one using X11.
+If Vim is compiled with |+socketserver| and another clientserver backend, then
+deciding which backend to use is done at startup time, via the
+|--clientserver| argument.  By default if it is not specified, then the native
+backend (X11 or MS-Window messages) will be used, if available.  A Vim
+instance using a socket server cannot communicate with one using another
+clientserver backend.
 
  vim:tw=78:sw=4:ts=8:noet:ft=help:norl:
index 7a4c09031b1568fccb0272e2f133ab753eed620e..9c0298aedd73717a435683b7db54de19f38834cc 100644 (file)
@@ -4762,11 +4762,9 @@ E156     sign.txt        /*E156*
 E1560  vim9.txt        /*E1560*
 E1561  vim9.txt        /*E1561*
 E1562  options.txt     /*E1562*
-E1563  remote.txt      /*E1563*
 E1564  remote.txt      /*E1564*
 E1565  remote.txt      /*E1565*
 E1566  remote.txt      /*E1566*
-E1567  remote.txt      /*E1567*
 E1568  options.txt     /*E1568*
 E1569  builtin.txt     /*E1569*
 E157   sign.txt        /*E157*
@@ -10377,6 +10375,7 @@ slow-fast-terminal      term.txt        /*slow-fast-terminal*
 slow-start     starting.txt    /*slow-start*
 slow-terminal  term.txt        /*slow-terminal*
 socket-interface       channel.txt     /*socket-interface*
+socketserver-address   remote.txt      /*socketserver-address*
 socketserver-clientserver      remote.txt      /*socketserver-clientserver*
 socketserver-differences       remote.txt      /*socketserver-differences*
 socketserver-name      remote.txt      /*socketserver-name*
index 1e7d1c71356ca07de919e05d1fd944afcdb07e23..4f7b9b62da6b1877fe98bc92bac7ba473a0e167a 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.2.  Last change: 2026 May 19
+*version9.txt* For Vim version 9.2.  Last change: 2026 May 22
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -52665,6 +52665,7 @@ Changed ~
   if needed, see |wayland-primary-selection| for an example.
 - On Unix, filename completion for single-file Ex commands now treats embedded
   whitespace as part of the filename, like on other platforms.
+- Rewrite the clientserver socketserver backend to use channels and JSON.
 
 
                                                        *added-9.3*
index cf9a080482375f45c69c5cc8119f5caf8f982f9b..cb1d3fc7623da0455bb8bf712d5e895dcd114ba9 100644 (file)
@@ -504,9 +504,10 @@ socketserver backend is being used, if the name starts with "/", "./", or "../",
 it is taken as either an absolute, relative or relative path to the socket.
 .TP
 \-\-clientserver {backend}
-Use {backend} as the backend for clientserver functionality, either "socket" or
-"x11" respectively.  Only available when compiled with both socketserver and X11
-features present
+Use {backend} as the backend for clientserver functionality, either "socket",
+"x11", or "mswin" respectively.  Only available when compiled with both
+socketserver and X11 features present, or if compiled with socketserver on
+MS-Windows.
 .TP
 \-\-socketid {id}
 GTK GUI only: Use the GtkPlug mechanism to run gVim in another window.
index 9a5881b3db42078e881b2568920deffe60235220..ec4058f9138581f0ddb30b56288124a58bda7eb0 100644 (file)
@@ -385,9 +385,10 @@ OPTIONS
 
        --clientserver {backend}
                    Use {backend} as the backend for  clientserver  functional‐
-                   ity, either "socket" or "x11" respectively.  Only available
-                   when  compiled  with  both  socketserver  and  X11 features
-                   present
+                   ity, either "socket", "x11", or "mswin" respectively.  Only
+                   available when compiled with both socketserver and X11 fea‐
+                   tures  present, or if compiled with socketserver on MS-Win‐
+                   dows.
 
        --socketid {id}
                    GTK GUI only: Use the GtkPlug mechanism to run gVim in  an‐
index 7f3d519bf1030f73bbaa47387dc7da34a0ca6e5e..24db4ccfff6ca5cae2faa8368639da7c75b5783d 100644 (file)
@@ -877,6 +877,7 @@ OBJ = \
        $(OUTDIR)/session.o \
        $(OUTDIR)/sha256.o \
        $(OUTDIR)/sign.o \
+       $(OUTDIR)/socketserver.o \
        $(OUTDIR)/spell.o \
        $(OUTDIR)/spellfile.o \
        $(OUTDIR)/spellsuggest.o \
index 8994143c92773c0106931841ec95d12466b49167..3b9ccb98f8df59c78f136308fc8e69b646969c67 100644 (file)
@@ -772,6 +772,7 @@ OBJ = \
        $(OUTDIR)\session.obj \
        $(OUTDIR)\sha256.obj \
        $(OUTDIR)\sign.obj \
+       $(OUTDIR)\socketserver.obj \
        $(OUTDIR)\spell.obj \
        $(OUTDIR)\spellfile.obj \
        $(OUTDIR)\spellsuggest.obj \
@@ -1767,6 +1768,8 @@ $(OUTDIR)/sha256.obj: $(OUTDIR) sha256.c $(INCL)
 
 $(OUTDIR)/sign.obj: $(OUTDIR) sign.c $(INCL)
 
+$(OUTDIR)/socketserver.obj: $(OUTDIR) socketserver.c $(INCL)
+
 $(OUTDIR)/spell.obj: $(OUTDIR) spell.c $(INCL)
 
 $(OUTDIR)/spellfile.obj: $(OUTDIR) spellfile.c $(INCL)
@@ -2017,6 +2020,7 @@ proto.h: \
        proto/session.pro \
        proto/sha256.pro \
        proto/sign.pro \
+       proto/socketserver.pro \
        proto/spell.pro \
        proto/spellfile.pro \
        proto/spellsuggest.pro \
index 4e00a2eb8816f011a0004f9a7be2557f4020322c..dd94e7418025624ab2e6e2e54462cd7207abb0a2 100644 (file)
@@ -1571,6 +1571,7 @@ BASIC_SRC = \
        session.c \
        sha256.c \
        sign.c \
+       socketserver.c \
        sound.c \
        spell.c \
        spellfile.c \
@@ -1749,6 +1750,7 @@ OBJ_COMMON = \
        objects/session.o \
        objects/sha256.o \
        objects/sign.o \
+       objects/socketserver.o \
        objects/sound.o \
        objects/spell.o \
        objects/spellfile.o \
@@ -3592,6 +3594,9 @@ objects/sha256.o: sha256.c
 objects/sign.o: sign.c
        $(CCC) -o $@ sign.c
 
+objects/socketserver.o: socketserver.c
+       $(CCC) -o $@ socketserver.c
+
 objects/sound.o: sound.c
        $(CCC) -o $@ sound.c
 
@@ -4267,6 +4272,11 @@ objects/sign.o: auto/osdef.h sign.c vim.h protodef.h auto/config.h feature.h os_
  structs.h regexp.h gui.h libvterm/include/vterm.h \
  libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
  globals.h errors.h
+objects/socketserver.o: socketserver.c vim.h protodef.h auto/config.h feature.h \
+ os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \
+ beval.h structs.h regexp.h gui.h libvterm/include/vterm.h \
+ libvterm/include/vterm_keycodes.h xdiff/xdiff.h xdiff/../vim.h alloc.h \
+ ex_cmds.h spell.h proto.h globals.h errors.h
 objects/sound.o: auto/osdef.h sound.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  structs.h regexp.h gui.h libvterm/include/vterm.h \
@@ -4793,6 +4803,7 @@ proto/search.pro: search.c
 proto/session.pro: session.c
 proto/sha256.pro: sha256.c
 proto/sign.pro: sign.c
+proto/socketserver.pro: socketserver.c
 proto/sound.pro: sound.c
 proto/spell.pro: spell.c
 proto/spellfile.pro: spellfile.c
index 90110a812bc6384726d63407b98a73151e42d396..a65e2182fac601d2e4dcd5076feacb758106c1cf 100755 (executable)
@@ -850,7 +850,6 @@ enable_netbeans
 enable_channel
 enable_terminal
 enable_autoservername
-enable_socketserver
 enable_multibyte
 enable_rightleft
 enable_arabic
@@ -1532,7 +1531,6 @@ Optional Features:
   --disable-channel       Disable process communication support.
   --enable-terminal       Enable terminal emulation support.
   --enable-autoservername Automatically define servername at vim startup.
-  --enable-socketserver Use sockets for clientserver communication.
   --enable-multibyte      Include multibyte editing support.
   --disable-rightleft     Do not include Right-to-Left language support.
   --disable-arabic        Do not include Arabic language support.
@@ -9077,36 +9075,6 @@ if test "$enable_autoservername" = "yes"; then
 
 fi
 
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-socketserver argument" >&5
-printf %s "checking --enable-socketserver argument... " >&6; }
-# Check whether --enable-socketserver was given.
-if test ${enable_socketserver+y}
-then :
-  enableval=$enable_socketserver; enable_socketserver=$enableval
-else case e in #(
-  e) if test "x$features" = xtiny
-then :
-  enable_socketserver=no_msg
-                        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cannot use socketserver with tiny features" >&5
-printf "%s\n" "cannot use socketserver with tiny features" >&6; }
-else case e in #(
-  e) enable_socketserver=yes ;;
-esac
-fi ;;
-esac
-fi
-
-
-if test "$enable_socketserver" = "yes"; then
-  printf "%s\n" "#define WANT_SOCKETSERVER 1" >>confdefs.h
-
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
-elif test "$enable_socketserver" = "no"; then
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
-fi
-
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-multibyte argument" >&5
 printf %s "checking --enable-multibyte argument... " >&6; }
 # Check whether --enable-multibyte was given.
index 579087b374fed57822dece3ab30ab2205723e625..27aa259e2b69751f0bc823a536c7810090cc9289 100644 (file)
@@ -820,7 +820,7 @@ channel_connect(
  * Returns the channel for success.
  * Returns NULL for failure.
  */
-    static channel_T *
+    channel_T *
 channel_open_unix(
        const char *path,
        void (*nb_close_cb)(void))
@@ -1290,6 +1290,46 @@ channel_set_options(channel_T *channel, jobopt_T *opt)
     channel->ch_part[PART_IN].ch_io = opt->jo_io[PART_IN];
 }
 
+/*
+ * Parse the address for socketserver and store port in "port", or the path in
+ * "unix_path" if it is a unix domain socket. Returns OK on success and FAIL on
+ * failure.
+ */
+    int
+channel_parse_socketserver_address(
+       char_u  *address,
+       int     *port,
+       char_u  **unix_path,
+       bool    quiet)
+{
+    char    *rest;
+    long    val;
+
+    if (*address == NUL)
+       goto fail;
+
+    if (!STRNCMP(address, "unix:", 5))
+    {
+       *unix_path = vim_strsave(address + 5);
+       return OK;
+    }
+
+    val = strtol((char *)(address), &rest, 10);
+    if (val < 0 || val >= 65536 || *rest != NUL)
+    {
+       if (!quiet)
+           semsg(_(e_invalid_argument_str), address);
+       return FAIL;
+    }
+    *port = (int)val;
+
+    return OK;
+fail:
+    if (!quiet)
+       semsg(_(e_invalid_argument_str), address);
+    return FAIL;
+}
+
 /*
  * Implements ch_open().
  */
@@ -1449,7 +1489,7 @@ channel_listen_func(typval_T *argvars)
     }
 
     if (is_unix)
-       channel = channel_listen_unix((char *)arg, NULL);
+       channel = channel_listen_unix((char *)arg, NULL, true);
     else
        channel = channel_listen(port, NULL);
     if (channel != NULL)
@@ -1571,13 +1611,15 @@ channel_listen(
 
 /*
  * Listen to a Unix domain socket channel.
+ * If "replace" is true, then unlink the path first beforehand.
  * Returns the channel for success.
  * Returns NULL for failure.
  */
     channel_T *
 channel_listen_unix(
        char *path,
-       void (*nb_close_cb)(void))
+       void (*nb_close_cb)(void),
+       bool replace)
 {
     int                        sd = -1;
     struct sockaddr_un server;
@@ -1619,8 +1661,9 @@ channel_listen_unix(
        return NULL;
     }
 
-    // Unlink the socket in case it already exists
-    unlink(server.sun_path);
+    if (replace)
+       // Unlink the socket in case it already exists
+       unlink(server.sun_path);
 
     // Bind the socket to the path
     server_len = offsetof(struct sockaddr_un, sun_path) + path_len + 1;
@@ -1983,7 +2026,7 @@ channel_buffer_free(buf_T *buf)
 /*
  * Write any lines waiting to be written to "channel".
  */
-    static void
+    void
 channel_write_input(channel_T *channel)
 {
     chanpart_T *in_part = &channel->ch_part[PART_IN];
@@ -2364,7 +2407,7 @@ channel_save(channel_T *channel, ch_part_T part, char_u *buf, int len,
  * Try to fill the buffer of "reader".
  * Returns FALSE when nothing was added.
  */
-    static int
+    int
 channel_fill(js_read_T *reader)
 {
     channel_T  *channel = (channel_T *)reader->js_cookie;
@@ -2484,11 +2527,12 @@ channel_process_lspdap_http_hdr(js_read_T *reader)
 
 /*
  * Use the read buffer of "channel"/"part" and parse a JSON message that is
- * complete.  The messages are added to the queue.
+ * complete.  The messages are added to the queue. If "socketserver" is true,
+ * then ignore the channel mode.
  * Return TRUE if there is more to read.
  */
-    static int
-channel_parse_json(channel_T *channel, ch_part_T part)
+    int
+channel_parse_json(channel_T *channel, ch_part_T part, bool socketserver)
 {
     js_read_T  reader;
     typval_T   listtv;
@@ -2507,8 +2551,8 @@ channel_parse_json(channel_T *channel, ch_part_T part)
     reader.js_cookie = channel;
     reader.js_cookie_arg = part;
 
-    if (chanpart->ch_mode == CH_MODE_LSP
-           || chanpart->ch_mode == CH_MODE_DAP)
+    if (!socketserver && (chanpart->ch_mode == CH_MODE_LSP
+           || chanpart->ch_mode == CH_MODE_DAP))
        status = channel_process_lspdap_http_hdr(&reader);
 
     // When a message is incomplete we wait for a short while for more to
@@ -2526,14 +2570,16 @@ channel_parse_json(channel_T *channel, ch_part_T part)
     {
        // Only accept the response when it is a list with at least two
        // items.
-       if ((chanpart->ch_mode == CH_MODE_LSP || chanpart->ch_mode == CH_MODE_DAP)
+       if (!socketserver && (chanpart->ch_mode == CH_MODE_LSP
+                   || chanpart->ch_mode == CH_MODE_DAP)
                && listtv.v_type != VAR_DICT)
        {
            ch_error(channel, "Did not receive a LSP dict, discarding");
            clear_tv(&listtv);
        }
-       else if (chanpart->ch_mode != CH_MODE_LSP && chanpart->ch_mode != CH_MODE_DAP
-             && (listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2))
+       else if (!socketserver && chanpart->ch_mode != CH_MODE_LSP
+               && chanpart->ch_mode != CH_MODE_DAP
+               && (listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2))
        {
            if (listtv.v_type != VAR_LIST)
                ch_error(channel, "Did not receive a list, discarding");
@@ -2668,7 +2714,7 @@ remove_cb_node(cbq_T *head, cbq_T *node)
  * Remove "node" from the queue that it is in and free it.
  * Caller should have freed or used node->jq_value.
  */
-    static void
+    void
 remove_json_node(jsonq_T *head, jsonq_T *node)
 {
     if (node->jq_prev == NULL)
@@ -3227,8 +3273,12 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
     int                called_otc;             // one time callbackup
     int                raw_len = 0;
 
-    if (channel->ch_nb_close_cb != NULL)
-       // this channel is handled elsewhere (netbeans)
+    if (channel->ch_nb_close_cb != NULL
+#ifdef FEAT_SOCKETSERVER
+           || channel->ch_socketserver
+#endif
+           )
+       // this channel is handled elsewhere (netbeans or socketserver)
        return FALSE;
 
     // Use a message-specific callback, part callback or channel callback
@@ -3267,7 +3317,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
                (void)channel_collapse(channel, part, FALSE);
 
            // Parse readahead, return when there is still no message.
-           channel_parse_json(channel, part);
+           channel_parse_json(channel, part, false);
            if (channel_get_json(channel, part, -1, FALSE, &listtv) == FAIL)
                return FALSE;
        }
@@ -3595,7 +3645,7 @@ channel_readahead_pointer(channel_T *channel, ch_part_T part)
        if (head->jq_next == NULL)
            // Parse json from readahead, there might be a complete message to
            // process.
-           channel_parse_json(channel, part);
+           channel_parse_json(channel, part, false);
 
        return head->jq_next;
     }
@@ -3842,6 +3892,11 @@ channel_close(channel_T *channel, int invoke_close_cb)
     }
 
     channel->ch_nb_close_cb = NULL;
+#ifdef FEAT_SOCKETSERVER
+    channel->ch_socketserver = false;
+    channel->ch_ss_accept_cb = NULL;
+    channel->ch_ss_close_cb = NULL;
+#endif
 
 #ifdef FEAT_TERMINAL
     term_channel_closed(channel);
@@ -4181,6 +4236,10 @@ channel_close_now(channel_T *channel)
     ch_log(channel, "Closing channel because all readable fds are closed");
     if (channel->ch_nb_close_cb != NULL)
        (*channel->ch_nb_close_cb)();
+#ifdef FEAT_SOCKETSERVER
+    if (channel->ch_ss_close_cb != NULL)
+       channel->ch_ss_close_cb(channel);
+#endif
     channel_close(channel, TRUE);
 }
 
@@ -4252,6 +4311,14 @@ channel_read(channel_T *channel, ch_part_T part, char *func)
            channel_gui_register_one(newchannel, PART_SOCK);
 #endif
 
+#ifdef FEAT_SOCKETSERVER
+           if (channel->ch_ss_accept_cb != NULL)
+           {
+               channel->ch_ss_accept_cb(newchannel);
+               return;
+           }
+#endif
+
            if (client.ss_family == AF_INET)
            {
 #ifdef HAVE_INET_NTOP
@@ -4323,6 +4390,16 @@ channel_read(channel_T *channel, ch_part_T part, char *func)
 #endif
 }
 
+#ifdef FEAT_SOCKETSERVER
+
+    void
+channel_check(channel_T *channel, ch_part_T part)
+{
+    channel_read(channel, part, "channel_check");
+}
+
+#endif
+
 /*
  * Read from RAW or NL "channel"/"part".  Blocks until there is something to
  * read or the timeout expires.
@@ -4465,7 +4542,7 @@ channel_read_json_block(
            // received messages.
            (void)channel_collapse(channel, part, FALSE);
 
-       more = channel_parse_json(channel, part);
+       more = channel_parse_json(channel, part, false);
 
        // search for message "id"
        if (channel_get_json(channel, part, id, TRUE, rettv) == OK)
@@ -5915,4 +5992,17 @@ channel_to_string_buf(typval_T *varp, char_u *buf)
     return buf;
 }
 
+/*
+ * Return the channel with the given ID. Returns NULL if not found.
+ */
+    channel_T *
+channel_find(int ch_id)
+{
+    channel_T *ch;
+    FOR_ALL_CHANNELS(ch)
+       if (ch->ch_id == ch_id)
+           return ch;
+    return NULL;
+}
+
 #endif // FEAT_JOB_CHANNEL
index 9186f4db16d35da5f1a3ca5737ed6962fed4f251..ae72a694f10e0254687b35bcc4f9899b5a09e584 100644 (file)
 
 #if defined(FEAT_CLIENTSERVER)
 
-# ifdef FEAT_SOCKETSERVER
-#  include <sys/socket.h>
-#  include "sys/un.h"
-# endif
-
 static void cmdsrv_main(int *argc, char **argv, char_u *serverName_arg, char_u **serverStr);
 static char_u *serverMakeName(char_u *arg, char *cmd);
 
@@ -206,23 +201,6 @@ exec_on_server(mparm_T *parmp)
     serverInitMessaging();
 # endif
 
-# ifdef FEAT_SOCKETSERVER
-    // If servername is specified and we are using sockets, always init the
-    // sockt server. We may need to receive replies back to us. If --serverlist
-    // is passed, the socket server will be uninitialized before listing
-    // sockets then initialized after. This is so we don't add our own socket
-    // in the list. This does not happen in serverlist().
-    if ((parmp->serverArg || parmp->serverName_arg != NULL) &&
-           clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-    {
-       parmp->servername = serverMakeName(parmp->serverName_arg,
-               parmp->argv[0]);
-       if (socket_server_init(parmp->servername) == OK)
-           TIME_MSG("initialize socket server");
-       made_name = TRUE;
-    }
-# endif
-
     /*
      * When a command server argument was found, execute it.  This may
      * exit Vim when it was successful.  Otherwise it's executed further
@@ -242,10 +220,12 @@ exec_on_server(mparm_T *parmp)
        parmp->servername = serverMakeName(parmp->serverName_arg,
                parmp->argv[0]);
 # ifdef MSWIN
-    if (parmp->servername != NULL)
+    if (parmp->servername != NULL && clientserver_method == CLIENTSERVER_METHOD_MSWIN)
     {
        serverSetName(parmp->servername);
+#  ifndef FEAT_SOCKETSERVER
        vim_free(parmp->servername);
+#  endif
     }
 # endif
 }
@@ -262,16 +242,31 @@ prepare_server(mparm_T *parmp)
      * Only register nongui-vim's with an explicit --servername argument,
      * or when compiling with autoservername.
      * When running as root --servername is also required.
+     *
+     * If we are Windows and socketserver is being used, then don't use
+     * autoservername, since traditional naming isn't supported.
      */
 
     if (
-#  ifdef FEAT_X11
-           X_DISPLAY != NULL &&
+#  if defined(FEAT_X11) || (defined(FEAT_SOCKETSERVER) && !defined(MSWIN))
+           (
+#   ifdef FEAT_X11
+           X_DISPLAY != NULL
+#   endif
+#   if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) && !defined(MSWIN)
+           ||
+#   endif
+#   if defined(FEAT_SOCKETSERVER) && !defined(MSWIN)
+           clientserver_method == CLIENTSERVER_METHOD_SOCKET
+#   endif
+           ) &&
 #  endif
-
            parmp->servername != NULL && (
 #  if defined(FEAT_AUTOSERVERNAME) || defined(FEAT_GUI)
                (
+#   if defined(FEAT_SOCKETSERVER) && defined(MSWIN)
+                clientserver_method != CLIENTSERVER_METHOD_SOCKET &&
+#   endif
 #   if defined(FEAT_AUTOSERVERNAME)
                    1
 #   else
@@ -284,12 +279,10 @@ prepare_server(mparm_T *parmp)
 #  endif
                parmp->serverName_arg != NULL))
     {
+
 #  ifdef FEAT_SOCKETSERVER
        if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-       {
-           if (socket_server_init(parmp->servername) == OK)
-               TIME_MSG("initialize socket server");
-       }
+           socketserver_start(parmp->servername, false);
 #  endif
 #  ifdef FEAT_X11
        if (clientserver_method == CLIENTSERVER_METHOD_X11)
@@ -300,10 +293,15 @@ prepare_server(mparm_T *parmp)
 #  endif
        vim_free(parmp->servername);
     }
-#  ifdef FEAT_X11
     else
-       serverDelayedStartName = parmp->servername;
+    {
+#  ifdef FEAT_X11
+       if (clientserver_method == CLIENTSERVER_METHOD_X11)
+           serverDelayedStartName = parmp->servername;
+       else
 #  endif
+           vim_free(parmp->servername);
+    }
 # endif
 
     /*
@@ -343,9 +341,6 @@ cmdsrv_main(
 # define ARGTYPE_SEND          3
     int                silent = FALSE;
     int                tabs = FALSE;
-# ifdef FEAT_SOCKETSERVER
-    char_u     *receiver;
-# endif
 # ifdef MSWIN
     HWND       srv;
 # elif defined(FEAT_X11)
@@ -353,6 +348,9 @@ cmdsrv_main(
 
     setup_term_clip();
 # endif
+# ifdef FEAT_SOCKETSERVER
+    channel_T  *ch = NULL;
+# endif
 
     sname = serverMakeName(serverName_arg, argv[0]);
     if (sname == NULL)
@@ -434,9 +432,8 @@ cmdsrv_main(
 
 # ifdef FEAT_SOCKETSERVER
            if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-               ret = socket_server_send(
-                       sname, *serverStr, NULL, &receiver,
-                       0, -1, silent);
+               ret = socketserver_send(sname, *serverStr, NULL, false, -1,
+                       silent, &ch);
 # endif
 # ifdef FEAT_X11
            if (clientserver_method == CLIENTSERVER_METHOD_X11)
@@ -452,8 +449,10 @@ cmdsrv_main(
            }
 # endif
 # ifdef MSWIN
-           // Win32 always works?
-           ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent);
+           if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
+               // Win32 always works?
+               ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0,
+                       silent);
 # endif
            if (ret < 0)
            {
@@ -513,23 +512,23 @@ cmdsrv_main(
                    char_u  *p = NULL;
                    int     j;
 # ifdef MSWIN
-                   p = serverGetReply(srv, NULL, TRUE, TRUE, 0);
-                   if (p == NULL)
-                       break;
-# else
-#  ifdef FEAT_SOCKETSERVER
+                   if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
+                       p = serverGetReply(srv, NULL, TRUE, TRUE, 0);
+# endif
+# ifdef FEAT_SOCKETSERVER
                    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET
-                           && socket_server_read_reply(receiver, &p, -1) == FAIL)
+                           && (ch == NULL
+                               || socketserver_read_reply(sname, &p, -1, true)
+                               == FAIL))
                            break;
-#  endif
-#  ifdef FEAT_X11
+# endif
+# ifdef FEAT_X11
                    if (clientserver_method == CLIENTSERVER_METHOD_X11
                            && serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0)
                            break;
-#  endif
+# endif
                    if (p == NULL)
                        break;
-# endif
                    j = atoi((char *)p);
                    vim_free(p);
                    if (j >= 0 && j < numFiles)
@@ -543,6 +542,13 @@ cmdsrv_main(
                        done[j] = 1;
                    }
                }
+# ifdef FEAT_SOCKETSERVER
+               if (ch != NULL)
+               {
+                   channel_close(ch, false);
+                   channel_clear(ch);
+               }
+# endif
 # ifdef FEAT_GUI_MSWIN
                Shell_NotifyIcon(NIM_DELETE, &ni);
 # endif
@@ -551,25 +557,24 @@ cmdsrv_main(
        }
        else if (STRICMP(argv[i], "--remote-expr") == 0)
        {
+           int status = OK;
+
            if (i == *argc - 1)
                mainerr_arg_missing((char_u *)argv[i]);
 # ifdef MSWIN
            // Win32 always works?
-           if (serverSendToVim(sname, (char_u *)argv[i + 1],
+           if (clientserver_method == CLIENTSERVER_METHOD_MSWIN
+                   && serverSendToVim(sname, (char_u *)argv[i + 1],
                                                  &res, NULL, 1, 0, FALSE) < 0)
-# else
-#  ifdef FEAT_SOCKETSERVER
-           if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-           {
-               if (!socket_server_valid())
-                   mch_errmsg(_("Socket server not online:"
-                               "Send expression failed"));
-               else if (socket_server_send(sname, (char_u *)argv[i + 1],
-                           &res, NULL, 1, 0, FALSE) < 0)
-                   goto expr_fail;
-           }
-#  endif
-#  ifdef FEAT_X11
+               status = FAIL;
+# endif
+# ifdef FEAT_SOCKETSERVER
+           if (clientserver_method == CLIENTSERVER_METHOD_SOCKET
+               && socketserver_send(sname, (char_u *)argv[i + 1],
+                           &res, 1, 0, false, NULL) < 0)
+               status = FAIL;
+# endif
+# ifdef FEAT_X11
            if (clientserver_method == CLIENTSERVER_METHOD_X11)
            {
                if (xterm_dpy == NULL)
@@ -577,15 +582,11 @@ cmdsrv_main(
                else if (serverSendToVim(xterm_dpy, sname,
                            (char_u *)argv[i + 1], &res,
                            NULL, 1, 0, 1, FALSE) < 0)
-                   goto expr_fail;
+                   status = FAIL;
            }
-#  endif
-           if (FALSE)
 # endif
+           if (status == FAIL)
            {
-# if !defined(MSWIN)
-expr_fail:
-# endif
                if (res != NULL && *res != NUL)
                {
                    // Output error from remote
@@ -598,28 +599,22 @@ expr_fail:
        else if (STRICMP(argv[i], "--serverlist") == 0)
        {
 # ifdef MSWIN
-           // Win32 always works?
-           res = serverGetVimNames();
-# else
-#  ifdef FEAT_SOCKETSERVER
+           if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
+               // Win32 always works?
+               res = serverGetVimNames();
+# endif
+# ifdef FEAT_SOCKETSERVER
            if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-           {
-               int was_init = socket_server_valid();
-
-               // Don't want to add ourselves to the list. So shutdown the
-               // server before listing then startup back again.
-               socket_server_uninit();
-               res = socket_server_list_sockets();
-
-               if (was_init)
-                   socket_server_init(NULL);
-           }
+#  ifdef MSWIN
+               res = vim_strsave((char_u *)"");
+#  else
+               res = socketserver_list();
 #  endif
-#  ifdef FEAT_X11
+# endif
+# ifdef FEAT_X11
            if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
                    xterm_dpy != NULL)
                res = serverGetVimNames(xterm_dpy);
-#  endif
 # endif
            if (did_emsg)
                mch_errmsg("\n");
@@ -648,9 +643,6 @@ expr_fail:
 
     if (didone)
     {
-# ifdef FEAT_SOCKETSERVER
-       socket_server_uninit();
-# endif
        display_errors();       // display any collected messages
        exit(exiterr);  // Mission accomplished - get out
     }
@@ -807,12 +799,12 @@ serverMakeName(char_u *arg, char *cmd)
     if (arg != NULL && *arg != NUL)
     {
 # ifdef FEAT_SOCKETSERVER
-       // If we are using a socket server, we want to preserve the original
-       // name if it is a path, else uppercase it if its just a generic name.
+       // When using socketserver backend, do not change the name if path or
+       // channel address.
        if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
        {
-           if (arg[0] == '/' || STRNCMP(arg, "./", 2) == 0 ||
-                   STRNCMP(arg, "../", 3) == 0)
+           if (STRNICMP(arg, "channel:", 8) == 0 || arg[0] == '/' ||
+                   STRNCMP(arg, "./", 2) == 0 || STRNCMP(arg, "../", 3) == 0)
                p = vim_strsave(arg);
            else
                p = vim_strsave_up(arg);
@@ -853,6 +845,8 @@ make_connection(void)
     static int
 check_connection(void)
 {
+    if (clientserver_method != CLIENTSERVER_METHOD_X11)
+       return OK;
     make_connection();
     if (X_DISPLAY == NULL)
     {
@@ -878,10 +872,8 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
 #  ifdef FEAT_X11
     Window     w;
 #  endif
-#  ifdef FEAT_SOCKETSERVER
-    char_u     *client = NULL;
-#  endif
 # endif
+    int                ret = OK;
 
     if (check_restricted() || check_secure())
        return;
@@ -899,35 +891,29 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
        return;         // type error; errmsg already given
     keys = tv_get_string_buf(&argvars[1], buf);
 # ifdef MSWIN
-    if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0)
-# else
-#  ifdef FEAT_SOCKETSERVER
-    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-       if (socket_server_send(server_name, keys, &r, &client, expr,
-                   timeout * 1000, TRUE) < 0)
-           goto fail;
-#  endif
-#  ifdef FEAT_X11
+    if (clientserver_method == CLIENTSERVER_METHOD_MSWIN
+           && serverSendToVim(server_name, keys, &r, &w, expr,
+               timeout, TRUE) < 0)
+       ret = FAIL;
+# endif
+# ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET
+       && socketserver_send(server_name, keys, &r, expr,
+           timeout * 1000, true, NULL) < 0)
+       ret = FAIL;
+# endif
+# ifdef FEAT_X11
     if (clientserver_method == CLIENTSERVER_METHOD_X11)
        if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
                    0, TRUE) < 0)
-           goto fail;
-#  endif
+       ret = FAIL;
 # endif
-# if !defined(MSWIN)
-    if (FALSE)
-    {
-fail:
-# else
+    if (ret == FAIL)
     {
-# endif
        if (r != NULL)
        {
            emsg((char *)r);    // sending worked but evaluation failed
            vim_free(r);
-# ifdef FEAT_SOCKETSERVER
-           vim_free(client);
-# endif
        }
        else
            semsg(_(e_unable_to_send_to_str), server_name);
@@ -939,40 +925,42 @@ fail:
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
        dictitem_T      v;
-# if defined(FEAT_SOCKETSERVER)
-       struct sockaddr_un addr;
-       char_u          str[sizeof(addr.sun_path)];
-# else
-       char_u          str[30];
+# if defined(MSWIN) || defined(FEAT_X11)
+       char_u          sbuf[30];
 # endif
+       char_u          *str = NULL;
        char_u          *idvar;
 
        idvar = tv_get_string_chk(&argvars[2]);
        if (idvar != NULL && *idvar != NUL)
        {
-           str[0] = NUL;
 # ifdef MSWIN
-           sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
-# else
-#  ifdef FEAT_X11
+           if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
+           {
+               sprintf((char *)sbuf, PRINTF_HEX_LONG_U, (long_u)w);
+               str = sbuf;
+           }
+# endif
+# ifdef FEAT_X11
            if (clientserver_method == CLIENTSERVER_METHOD_X11)
-               sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
-#  endif
-#  ifdef FEAT_SOCKETSERVER
+           {
+               sprintf((char *)sbuf, PRINTF_HEX_LONG_U, (long_u)w);
+               str = sbuf;
+           }
+# endif
+# ifdef FEAT_SOCKETSERVER
            if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-               vim_snprintf((char *)str, sizeof(addr.sun_path),
-                       "%s", client);
-#  endif
+               str = server_name;
 # endif
+           if (str == NULL)
+               str = (char_u *)"";
+
            v.di_tv.v_type = VAR_STRING;
            v.di_tv.vval.v_string = vim_strsave(str);
            set_var(idvar, &v.di_tv, FALSE);
            vim_free(v.di_tv.vval.v_string);
        }
     }
-# ifdef FEAT_SOCKETSERVER
-    vim_free(client);
-# endif
 }
 #endif
 
@@ -1054,20 +1042,23 @@ f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv)
     if (serverid == NULL)
        return;         // type error; errmsg already given
 #  ifdef MSWIN
-    sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n);
-    if (n == 0)
-       rettv->vval.v_number = -1;
-    else
+    if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
     {
-       s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0);
-       rettv->vval.v_number = (s != NULL);
+       sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n);
+       if (n == 0)
+           rettv->vval.v_number = -1;
+       else
+       {
+           s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0);
+           rettv->vval.v_number = (s != NULL);
+       }
     }
-#  else
-#   ifdef FEAT_SOCKETSERVER
+#  endif
+#  ifdef FEAT_SOCKETSERVER
     if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-       rettv->vval.v_number = socket_server_peek_reply(serverid, &s);
-#   endif
-#   ifdef FEAT_X11
+       rettv->vval.v_number = socketserver_peek_reply(serverid, &s);
+#  endif
+#  ifdef FEAT_X11
     if (clientserver_method == CLIENTSERVER_METHOD_X11)
     {
        if (check_connection() == FAIL)
@@ -1076,7 +1067,6 @@ f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv)
        rettv->vval.v_number = serverPeekReply(X_DISPLAY,
                serverStrToWin(serverid), &s);
     }
-#   endif
 #  endif
 
     if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0)
@@ -1121,24 +1111,27 @@ f_remote_read(typval_T *argvars UNUSED, typval_T *rettv)
            timeout = tv_get_number(&argvars[1]);
 
 #  ifdef MSWIN
-       sscanf((char *)serverid, SCANF_HEX_LONG_U, &n);
-       if (n != 0)
-           r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout);
-       if (r == NULL)
-           emsg(_(e_unable_to_read_server_reply));
-#  else
-#   ifdef FEAT_SOCKETSERVER
+       if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
+       {
+           sscanf((char *)serverid, SCANF_HEX_LONG_U, &n);
+           if (n != 0)
+               r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout);
+           if (r == NULL)
+               emsg(_(e_unable_to_read_server_reply));
+       }
+#  endif
+#  ifdef FEAT_SOCKETSERVER
        if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
-               socket_server_read_reply(serverid, &r, timeout * 1000) == FAIL)
+               socketserver_read_reply(serverid, &r, timeout * 1000, false)
+               == FAIL)
            emsg(_(e_unable_to_read_server_reply));
-#   endif
-#   ifdef FEAT_X11
+#  endif
+#  ifdef FEAT_X11
        if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
                (check_connection() == FAIL
                || serverReadReply(X_DISPLAY, serverStrToWin(serverid),
                                                       &r, FALSE, timeout) < 0))
            emsg(_(e_unable_to_read_server_reply));
-#   endif
 #  endif
     }
 # endif
@@ -1184,17 +1177,17 @@ f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
 
     char_u *server = tv_get_string_chk(&argvars[0]);
 #  ifdef MSWIN
-    serverSetName(server);
-#  else
-#   ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
+       serverSetName(server);
+#  endif
+#  ifdef FEAT_SOCKETSERVER
     if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-       socket_server_init(server);
-#   endif
-#   ifdef FEAT_X11
+       socketserver_start(server, true);
+#  endif
+#  ifdef FEAT_X11
     if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
            check_connection() == OK)
        serverRegisterName(X_DISPLAY, server);
-#   endif
 #  endif
 
 # else
@@ -1209,6 +1202,7 @@ f_server2client(typval_T *argvars UNUSED, typval_T *rettv)
     char_u     buf[NUMBUFLEN];
     char_u     *server;
     char_u     *reply;
+    int                ret = OK;
 
     rettv->vval.v_number = -1;
     if (check_restricted() || check_secure())
@@ -1226,28 +1220,22 @@ f_server2client(typval_T *argvars UNUSED, typval_T *rettv)
 
 #  ifdef FEAT_SOCKETSERVER
     if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
-           socket_server_send_reply(server, reply) == FAIL)
-       goto fail;
+           socketserver_send_reply(server, reply) == FAIL)
+       ret = FAIL;
 #  endif
-
 #  ifdef FEAT_X11
-    if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
-           check_connection() == FAIL)
-       return;
-
     if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
            serverSendReply(server, reply) < 0)
+       ret = FAIL;
 #  endif
 #  ifdef MSWIN
-    if (serverSendReply(server, reply) < 0)
-#  endif
-#  if defined(FEAT_SOCKETSERVER) && !defined(FEAT_X11) && !defined(MSWIN)
-    if (FALSE)
+    if (clientserver_method == CLIENTSERVER_METHOD_MSWIN
+           && serverSendReply(server, reply) < 0)
+       ret = FAIL;
 #  endif
+
+    if (ret == FAIL)
     {
-#  ifdef FEAT_SOCKETSERVER
-fail:
-#  endif
        emsg(_(e_unable_to_send_to_client));
        return;
     }
@@ -1264,20 +1252,24 @@ f_serverlist(typval_T *argvars UNUSED, typval_T *rettv)
 
 # ifdef FEAT_CLIENTSERVER
 #  ifdef MSWIN
-    r = serverGetVimNames();
-#  else
-#   ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_MSWIN)
+       r = serverGetVimNames();
+#  endif
+#  ifdef FEAT_SOCKETSERVER
     if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
-       r = socket_server_list_sockets();
+#   ifdef MSWIN
+       r = vim_strsave((char_u *)"");
+#   else
+       r = socketserver_list();
 #   endif
-#   ifdef FEAT_X11
+#  endif
+#  ifdef FEAT_X11
     if (clientserver_method == CLIENTSERVER_METHOD_X11)
     {
     make_connection();
     if (X_DISPLAY != NULL)
        r = serverGetVimNames(X_DISPLAY);
     }
-#   endif
 #  endif
 # endif
     rettv->v_type = VAR_STRING;
index 6f6590f6daa9bbab6cb338543ea95cd3ba2e81f9..27a8943bda73eeed6a529bc6bcaf812d43074678 100644 (file)
@@ -2363,22 +2363,6 @@ if test "$enable_autoservername" = "yes"; then
   AC_DEFINE(FEAT_AUTOSERVERNAME)
 fi
 
-AC_MSG_CHECKING(--enable-socketserver argument)
-AC_ARG_ENABLE(socketserver,
-       [  --enable-socketserver Use sockets for clientserver communication.],
-       [enable_socketserver=$enableval],
-       AS_IF([test "x$features" = xtiny],
-                       [enable_socketserver=no_msg
-                        AC_MSG_RESULT([cannot use socketserver with tiny features])],
-                       [enable_socketserver=yes]))
-
-if test "$enable_socketserver" = "yes"; then
-  AC_DEFINE(WANT_SOCKETSERVER)
-  AC_MSG_RESULT([yes])
-elif test "$enable_socketserver" = "no"; then
-  AC_MSG_RESULT([no])
-fi
-
 AC_MSG_CHECKING(--enable-multibyte argument)
 AC_ARG_ENABLE(multibyte,
        [  --enable-multibyte      Include multibyte editing support.], ,
index 4e261c9232e73ee9b6e92789877ea83ab66cb0ee..0013a43ee87f88c34e232c12cfe4f40b370721fa 100644 (file)
@@ -1139,7 +1139,7 @@ EXTERN char e_cant_read_postscript_resource_file_str[]
        INIT(= N_("E457: Can't read PostScript resource file \"%s\""));
 #endif
 // E458 unused
-#if defined(UNIX) || defined(FEAT_SESSION)
+#if defined(UNIX) || defined(FEAT_SESSION) || defined(FEAT_SOCKETSERVER)
 EXTERN char e_cannot_go_back_to_previous_directory[]
        INIT(= N_("E459: Cannot go back to previous directory"));
 #endif
@@ -3777,16 +3777,14 @@ EXTERN char e_diff_anchors_with_hidden_windows[]
        INIT(= N_("E1562: Diff anchors cannot be used with hidden diff windows"));
 #endif
 #ifdef FEAT_SOCKETSERVER
-EXTERN char e_socket_path_too_big[]
-       INIT(= N_("E1563: Socket path is too big"));
 EXTERN char e_socket_name_no_slashes[]
-       INIT(= N_("E1564: Socket name cannot have slashes in it without being a path"));
+       INIT(= N_("E1564: Socket name '%s' cannot have slashes in it without being a path"));
 EXTERN char e_socket_server_not_online[]
        INIT(= N_("E1565: Socket server is not online, call remote_startserver() first"));
 EXTERN char e_socket_server_failed_connecting[]
-       INIT(= N_("E1566: Failed connecting to socket %s: %s"));
-EXTERN char e_socket_server_unavailable[]
-       INIT(= N_("E1567: Cannot start socket server, socket path is unavailable"));
+       INIT(= N_("E1566: Failed connecting to socket '%s'"));
+EXTERN char e_socket_server_version_mismatch[]
+       INIT(= N_("E1567: Socket server protocol version mismatch, check what Vim version you are using"));
 #endif
 EXTERN char e_osc_response_timed_out[]
        INIT(= N_("E1568: OSC command response timed out: %.*s"));
index 115db703d2d59eed63b18d1861f5487cbc41f2c2..46508f74bbbcf53c9feb04ab21ebe2e1883997ca 100644 (file)
 #endif
 
 /*
- * +socketserver        Use UNIX domain sockets for clientserver communication
+ * The +channel feature requires +eval.
+ */
+#if !defined(FEAT_EVAL) && defined(FEAT_JOB_CHANNEL)
+# undef FEAT_JOB_CHANNEL
+#endif
+
+/*
+ * +socketserver        Use channels for clientserver communication
  */
-#if defined(UNIX) && defined(WANT_SOCKETSERVER)
+#if (defined(UNIX) || defined(MSWIN)) && defined(FEAT_JOB_CHANNEL)
 # define FEAT_SOCKETSERVER
 #endif
 
 #if (defined(MSWIN) || defined(FEAT_XCLIPBOARD) || defined(FEAT_SOCKETSERVER)) \
     && defined(FEAT_EVAL)
 # define FEAT_CLIENTSERVER
+# if defined(FEAT_SOCKETSERVER) && (defined(FEAT_XCLIPBOARD) || defined(MSWIN))
+#  define FEAT_CLIENTSERVER_BACKENDS
+# endif
 #endif
 
 /*
  * +tgetent
  */
 
-
-/*
- * The +channel feature requires +eval.
- */
-#if !defined(FEAT_EVAL) && defined(FEAT_JOB_CHANNEL)
-# undef FEAT_JOB_CHANNEL
-#endif
-
 /*
  * The Netbeans feature requires +eval and +job_channel
  */
index f15d1dffe0c284ca15bfcff83c149908ac89d5ba..f1bd6b7245cc164afc87e7d8e0137e8fe348be11 100644 (file)
--- a/src/gc.c
+++ b/src/gc.c
@@ -208,6 +208,9 @@ garbage_collect(int testing)
 #ifdef FEAT_NETBEANS_INTG
     abort = abort || set_ref_in_nb_channel(copyID);
 #endif
+#ifdef FEAT_SOCKETSERVER
+    abort = abort || set_ref_in_socketserver_channel(copyID);
+#endif
 
 #ifdef FEAT_TIMERS
     abort = abort || set_ref_in_timer(copyID);
index b995698128eb579ffd83571a1708b258d0a55617..47ba62ad6c83233bd0ae8324b7a00e94ba37385a 100644 (file)
@@ -2650,6 +2650,9 @@ parse_queued_messages(void)
        // Process the queued netbeans messages.
        netbeans_parse_messages();
 # endif
+# ifdef FEAT_SOCKETSERVER
+       socketserver_parse_messages();
+# endif
 # ifdef FEAT_JOB_CHANNEL
        // Write any buffer lines still to be written.
        channel_write_any_lines();
index 2fdc9446480cff353f50e449f4290861e0fa56ea..0a4a09d65d743ba0a84dfe11b0ebaa9040bd4c15 100644 (file)
@@ -2122,25 +2122,28 @@ EXTERN int wayland_no_connect INIT(= FALSE);
 
 #endif
 
-#if defined(FEAT_CLIENTSERVER) && !defined(MSWIN)
+#if defined(FEAT_CLIENTSERVER)
 
 // Backend for clientserver functionality
 typedef enum {
     CLIENTSERVER_METHOD_NONE,
+# ifdef FEAT_X11
     CLIENTSERVER_METHOD_X11,
+# endif
+# ifdef MSWIN
+    CLIENTSERVER_METHOD_MSWIN,
+# endif
+# ifdef FEAT_SOCKETSERVER
     CLIENTSERVER_METHOD_SOCKET
+# endif
 } clientserver_method_T;
 
-// Default to X11 if compiled with support for it, else use socket server.
-# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
 EXTERN clientserver_method_T clientserver_method
-# else
-// Since we aren't going to be changing clientserver_method, make it constant to
-// allow compiler optimizations.
-EXTERN const clientserver_method_T clientserver_method
-# endif
+
 # ifdef FEAT_X11
 INIT(= CLIENTSERVER_METHOD_X11);
+# elif defined(MSWIN)
+INIT(= CLIENTSERVER_METHOD_MSWIN);
 # elif defined(FEAT_SOCKETSERVER)
 INIT(= CLIENTSERVER_METHOD_SOCKET);
 # else
index 1448c86bdb71bc7e3deec82551139bb17533b374..b04f132163f353a25b4bb6ff640d7443d98b33cb 100644 (file)
--- a/src/gui.c
+++ b/src/gui.c
@@ -158,12 +158,6 @@ gui_start(char_u *arg UNUSED)
        choose_clipmethod();
 #endif
 
-#if defined(FEAT_SOCKETSERVER) && defined(FEAT_GUI_GTK)
-    // Install socket server listening socket if we are running it
-    if (socket_server_valid())
-       gui_gtk_init_socket_server();
-#endif
-
 #ifdef FEAT_GUI_MSWIN
     // Enable fullscreen mode
     if (vim_strchr(p_go, GO_FULLSCREEN) != NULL)
index 2dab62939452ec708fe5f7b32578604f3bb91373..af452c84683e1003dca7542fce6becad0cd9b71e 100644 (file)
@@ -80,13 +80,6 @@ extern void bonobo_dock_item_set_behavior(BonoboDockItem *dock_item, BonoboDockI
 # include <X11/Sunkeysym.h>
 #endif
 
-#ifdef FEAT_SOCKETSERVER
-# include <glib-unix.h>
-
-// Used to track the source for the listening socket
-static uint socket_server_source_id = 0;
-#endif
-
 /*
  * Easy-to-use macro for multihead support.
  */
@@ -2690,54 +2683,6 @@ global_event_filter(GdkXEvent *xev,
 }
 #endif // !USE_GNOME_SESSION
 
-#if defined(FEAT_SOCKETSERVER)
-
-/*
- * Callback for new events from the socket server listening socket
- */
-    static int
-socket_server_poll_in(int fd UNUSED, GIOCondition cond, void *user_data UNUSED)
-{
-    if (cond & G_IO_IN)
-       socket_server_accept_client();
-    else if (cond & (G_IO_ERR | G_IO_HUP))
-    {
-       socket_server_uninit();
-       return FALSE;
-    }
-
-    return TRUE;
-}
-
-/*
- * Initialize socket server for use in the GUI (does not actually initialize the
- * socket server, only attaches a source).
- */
-    void
-gui_gtk_init_socket_server(void)
-{
-    if (socket_server_source_id > 0)
-       return;
-    // Register source for file descriptor to global default context
-    socket_server_source_id = g_unix_fd_add(socket_server_get_fd(),
-           G_IO_IN | G_IO_ERR | G_IO_HUP, socket_server_poll_in, NULL);
-}
-
-/*
- * Remove the source for the socket server listening socket.
- */
-    void
-gui_gtk_uninit_socket_server(void)
-{
-    if (socket_server_source_id > 0)
-    {
-       g_source_remove(socket_server_source_id);
-       socket_server_source_id = 0;
-    }
-}
-
-#endif
-
     static GdkPixbuf *
 pixbuf_new_from_png_data(const unsigned char *data, unsigned int len)
 {
index a3e58043d8891aef4d1e74d8ccc003f4f4dfe174..b9cbc0d594a63e733b5cc06f9793dfa0b93f5e6e 100644 (file)
@@ -51,6 +51,8 @@ json_encode(typval_T *val, int options)
     // Store bytes in the growarray.
     ga_init2(&ga, 1, 4000);
     json_encode_gap(&ga, val, options);
+    if (options & JSON_NL)
+       ga_append(&ga, NL);
     ga_append(&ga, NUL);
     return ga.ga_data;
 }
index cac5db145ef3564db187f1dce5530d1703441024..d2fe22ebe555942ba27a00ab4d0cb050307b0d54 100644 (file)
@@ -1844,6 +1844,9 @@ getout(int exitval)
 #ifdef FEAT_NETBEANS_INTG
     netbeans_end();
 #endif
+#ifdef FEAT_SOCKETSERVER
+    socketserver_stop();
+#endif
 #ifdef FEAT_CSCOPE
     cs_end();
 #endif
@@ -1910,7 +1913,7 @@ early_arg_scan(mparm_T *parmp UNUSED)
                gui.dofork = false;
 #  endif
        }
-#  if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
+#  ifdef FEAT_CLIENTSERVER_BACKENDS
        else if (STRNICMP(argv[i], "--clientserver", 14) == 0)
        {
            char_u *arg;
@@ -1920,8 +1923,14 @@ early_arg_scan(mparm_T *parmp UNUSED)
 
            if (STRICMP(arg, "socket") == 0)
                clientserver_method = CLIENTSERVER_METHOD_SOCKET;
+#   ifdef FEAT_X11
            else if (STRICMP(arg, "x11") == 0)
                clientserver_method = CLIENTSERVER_METHOD_X11;
+#   endif
+#   ifdef MSWIN
+           else if (STRICMP(arg, "mswin") == 0)
+               clientserver_method = CLIENTSERVER_METHOD_MSWIN;
+#   endif
            else
                mainerr(ME_UNKNOWN_OPTION, arg);
        }
@@ -2247,9 +2256,9 @@ command_line_scan(mparm_T *parmp)
                    ; // already processed -- no arg
                else if (STRNICMP(argv[0] + argv_idx, "servername", 10) == 0
                       || STRNICMP(argv[0] + argv_idx, "serversend", 10) == 0
-#  if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
+                      // Don't put this under FEAT_CLIENTSERVER_BACKENDS, just
+                      // let it be ignored. Makes tests less complicated
                       || STRNICMP(argv[0] + argv_idx, "clientserver", 12) == 0
-#  endif
                       )
                {
                    // already processed -- snatch the following arg
@@ -3747,8 +3756,8 @@ usage(void)
     main_msg(_("-Y\t\t\tDo not connect to Wayland compositor"));
 # endif
 # ifdef FEAT_CLIENTSERVER
-#  if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
-    main_msg(_("--clientserver <socket|x11> Backend for clientserver communication"));
+#  ifdef FEAT_CLIENTSERVER_BACKENDS
+    main_msg(_("--clientserver <socket|x11|mswin> Backend for clientserver communication"));
 #  endif
     main_msg(_("--remote <files>\tEdit <files> in a Vim server if possible"));
     main_msg(_("--remote-silent <files>  Same, don't complain if there is no server"));
index 5cfc422b7a18c2a40934a00f04c667e9ed8b9fc8..ab58dda7eb4b98f98755f9afb93b07235be8623a 100644 (file)
@@ -147,123 +147,6 @@ Window        x11_window = 0;
 Display            *x11_display = NULL;
 #endif
 
-#if defined(FEAT_SOCKETSERVER)
-# include <sys/socket.h>
-# include <sys/un.h>
-
-# define SOCKET_SERVER_MAX_BACKLOG 5
-# define SOCKET_SERVER_MAX_CMD_SIZE 16384
-# define SOCKET_SERVER_MAX_MSG 6
-
-static int     socket_server_fd = -1;
-static char_u  *socket_server_path = NULL;
-
-typedef enum {
-    SS_MSG_TYPE_ENCODING    = 'e',  // Encoding of message.
-    SS_MSG_TYPE_STRING     = 'c',  // Script to execute or reply string.
-    SS_MSG_TYPE_SERIAL     = 's',  // Serial of pending command
-    SS_MSG_TYPE_CODE       = 'r',  // Result code for an expression sent
-    SS_MSG_TYPE_SENDER     = 'd'   // Location of socket for the client that
-                                   // sent the command.
-} ss_msg_type_T;
-
-typedef enum {
-    SS_CMD_TYPE_EXPR       = 'E',  // An expression
-    SS_CMD_TYPE_KEYSTROKES  = 'K',  // Series of keystrokes
-    SS_CMD_TYPE_REPLY      = 'R',  // Reply from an expression
-    SS_CMD_TYPE_NOTIFY     = 'N',  // A notification
-    SS_CMD_TYPE_ALIVE      = 'A',  // Check if server is still responsive
-} ss_cmd_type_T;
-
-// Represents a message in a command. A command can contain multiple messages.
-// Each message starts with a single byte representing the type, then a uint32
-// representing the length of the contents, and then the actual contents.
-// Everything is in native byte order.
-//
-// While contents may contain NULL characters, such as when it is a number, it
-// is always NULL terminated. Note that the NULL terminator does not count in
-// the length.
-typedef struct {
-    char_u     msg_type;           // Type of message
-    uint32_t   msg_len;            // Total length of contents
-    char_u     *msg_contents;      // Actual contents of message
-} ss_msg_T;
-
-// Represents a command sent over a socket. Each socket starts with a byte
-// representing the type, then a uint32 representing the number of messages,
-// then a uint32 representing the total size of the messages in bytes, and then
-// the actual messages. Everything is in native byte order.
-typedef struct {
-    char_u     cmd_type;                           // Type of command
-    uint32_t   cmd_num;                            // Number of messages
-    uint32_t   cmd_len;                            // Combined size of all
-                                                   // messages
-    ss_msg_T   cmd_msgs[SOCKET_SERVER_MAX_MSG];    // Array of messages
-} ss_cmd_T;
-
-# define SS_CMD_INFO_SIZE (sizeof(char_u) + (sizeof(uint32_t) * 2))
-# define SS_MSG_INFO_SIZE (sizeof(char_u) + sizeof(uint32_t))
-
-// Represents a pending reply from a command sent to a Vim server. When a
-// command is sent out, we generate unique serial number with it. When we
-// receive any reply, we check which pending command has a matching serial
-// number, and is therefore the reply for that pending command.
-//
-// The reason we just don't use the existing fd created by the connect() call,
-// and communicate using that, is that it can't handle recursive calls, ex:
-// call remote_expr('B', 'remote_expr("A", "<expr>")')
-//
-// This idea is taken from the existing X server functionality
-typedef struct ss_pending_cmd_S {
-    uint32_t   serial;             // Serial number expected in result
-    char_u     code;               // Result code, can be 0 or -1.
-    char_u     *result;            // Result of command
-
-    struct ss_pending_cmd_S *next;  // Next in list
-} ss_pending_cmd_T;
-
-static ss_pending_cmd_T *ss_pending_cmds;
-
-// Serial is always greater than zero
-static uint32_t ss_serial = 0;
-
-// Represents a reply from a server2client call. Each client that calls a
-// server2client call to us has its own ss_reply_T. Each time a client sends
-// data using server2client, Vim creates a ss_reply_T if it doesn't exist and
-// adds the string to the array. When remote_read is called, the server id is
-// used to find the specific ss_reply_T, and a single string is popped from the
-// array.
-//
-// This idea is taken from the existing X server functionality
-typedef struct {
-    char_u *sender;
-    garray_T strings;
-} ss_reply_T;
-
-static garray_T ss_replies;
-
-static char_u *socket_server_get_path_from_name(char_u *name);
-static int socket_server_connect(char_u *name, char_u **path, int silent);
-static void socket_server_init_pending_cmd(ss_pending_cmd_T *pending);
-static void socket_server_pop_pending_cmd(ss_pending_cmd_T *pending);
-static void socket_server_init_cmd(ss_cmd_T *cmd, ss_cmd_type_T type);
-static int socket_server_append_msg(ss_cmd_T *cmd, char_u type,
-       char_u *contents, int len);
-static void socket_server_free_cmd(ss_cmd_T *cmd);
-static char_u *socket_server_encode_cmd(ss_cmd_T *cmd, size_t *sz);
-static int socket_server_decode_cmd(ss_cmd_T *cmd, int socket_fd, int timeout);
-static int socket_server_write(int sock_fd, char_u *data, size_t sz,
-       int timeout);
-static ss_reply_T *socket_server_get_reply(char_u *sender, int *index);
-static ss_reply_T *socket_server_add_reply(char_u *sender);
-static void socket_server_remove_reply(char_u *sender);
-static void socket_server_exec_cmd(ss_cmd_T *cmd, int fd);
-static int socket_server_dispatch(int timeout);
-static int socket_server_check_alive(char_u *name);
-static int socket_server_name_is_valid(char_u *name);
-
-#endif // FEAT_SOCKETSERVER
-
 static int ignore_sigtstp = FALSE;
 
 static int get_x11_title(int);
@@ -3782,10 +3665,6 @@ mch_exit(int r)
     x11_export_final_selection();
 #endif
 
-#ifdef FEAT_SOCKETSERVER
-    socket_server_uninit();
-#endif
-
 #ifdef FEAT_GUI
     if (!gui.in_use)
 #endif
@@ -6792,9 +6671,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
                        // each channel may use in, out and err
        struct pollfd   fds[7 + 3 * MAX_OPEN_CHANNELS];
        int             nfd;
-#  ifdef FEAT_SOCKETSERVER
-       int             socket_server_idx = -1;
-#  endif
 #  ifdef FEAT_WAYLAND_CLIPBOARD
        int             wayland_idx = -1;
 #  endif
@@ -6821,16 +6697,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
        fds[0].events = POLLIN;
        nfd = 1;
 
-#  ifdef FEAT_SOCKETSERVER
-       if (socket_server_fd != -1)
-       {
-           socket_server_idx = nfd;
-           fds[nfd].fd = socket_server_fd;
-           fds[nfd].events = POLLIN;
-           nfd++;
-       }
-#  endif
-
 #  ifdef FEAT_WAYLAND
        if ((wayland_fd = wayland_prepare_read()) >= 0)
        {
@@ -6886,19 +6752,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
            finished = FALSE;
 #  endif
 
-#  ifdef FEAT_SOCKETSERVER
-       if (socket_server_idx >= 0)
-       {
-           if (fds[socket_server_idx].revents & POLLIN)
-           {
-               if (socket_server_accept_client() == FAIL)
-                   socket_server_uninit();
-           }
-           else if (fds[socket_server_idx].revents & (POLLHUP | POLLERR))
-               socket_server_uninit();
-       }
-#  endif
-
 #  ifdef FEAT_WAYLAND
        if (wayland_idx >= 0)
            wayland_poll_check(fds[wayland_idx].revents);
@@ -6984,16 +6837,6 @@ select_eintr:
 #  endif
        maxfd = fd;
 
-#  ifdef FEAT_SOCKETSERVER
-       if (socket_server_fd != -1)
-       {
-           FD_SET(socket_server_fd, &rfds);
-
-           if (maxfd < socket_server_fd)
-               maxfd = socket_server_fd;
-       }
-#  endif
-
 #  ifdef FEAT_WAYLAND
        if ((wayland_fd = wayland_prepare_read()) >= 0)
        {
@@ -7093,13 +6936,6 @@ select_eintr:
            finished = FALSE;
 #  endif
 
-#  ifdef FEAT_SOCKETSERVER
-       if (ret > 0 && socket_server_fd != -1
-               && FD_ISSET(socket_server_fd, &rfds)
-               && socket_server_accept_client() == FAIL)
-           socket_server_uninit();
-#  endif
-
 #  ifdef FEAT_WAYLAND
        if (wayland_fd != -1)
            wayland_select_check(ret > 0 && FD_ISSET(wayland_fd, &rfds));
@@ -7161,16 +6997,9 @@ select_eintr:
        if (finished || msec == 0)
            break;
 
-#  if defined(FEAT_CLIENTSERVER)
-#   ifdef FEAT_X11
+#  if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
        if (clientserver_method == CLIENTSERVER_METHOD_X11 && server_waiting())
            break;
-#   endif
-#   ifdef FEAT_SOCKETSERVER
-       if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
-               socket_server_waiting_accept())
-           break;
-#   endif
 #  endif
 
        // We're going to loop around again, find out for how long
@@ -9251,1565 +9080,3 @@ mch_create_anon_file(void)
     }
     return fd;
 }
-
-#if defined(FEAT_SOCKETSERVER)
-
-/*
- * Initialize socket server called "name" (the socket filename). If "name" is a
- * path (starts with a '/', './', or '../'), it is assumed to be the path to
- * the desired socket. If the socket path is already taken, append an
- * incrementing number to the path until we find a socket filename that can be
- * used. If NULL is passed as the name, the previous socket path is used (only
- * if not NULL). Returns OK on success and FAIL on failure.
- */
-    int
-socket_server_init(char_u *name)
-{
-    struct sockaddr_un addr;
-    char_u             *path;
-    int                        num_printed;
-    int                        fd;
-    int                        i = 1;
-
-    if (socket_server_valid() || (name == NULL && socket_server_path == NULL))
-       return FAIL;
-    if (name == NULL)
-       name = socket_server_path;
-
-    path = alloc(sizeof(addr.sun_path));
-
-    if (path == NULL)
-       return FAIL;
-
-    fd = socket(AF_UNIX, SOCK_STREAM, 0);
-
-    if (fd == -1)
-    {
-       vim_free(path);
-       return FAIL;
-    }
-
-    addr.sun_family = AF_UNIX;
-
-    // If name is not a path, find a common directory to place the
-    // socket.
-    if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 ||
-           STRNCMP(name, "../", 3) == 0)
-       num_printed =
-           vim_snprintf((char *)path, sizeof(addr.sun_path), "%s", name);
-    else
-    {
-       const char_u    *dir;
-       char_u          *buf;
-
-       // Check if there are slashes in the name
-       if (vim_strchr(name, '/') != NULL)
-       {
-           emsg(_(e_socket_name_no_slashes));
-           goto fail;
-       }
-
-       dir = mch_getenv("XDG_RUNTIME_DIR");
-
-       if (dir == NULL)
-       {
-           // Use $TMPDIR or /tmp if $XDG_RUNTIME_DIR is not set.
-           const char_u    *tmpdir = mch_getenv("TMPDIR");
-           size_t          sz;
-
-           if (tmpdir != NULL)
-               dir = tmpdir;
-           else
-               dir = (char_u *)"/tmp";
-
-           sz = STRLEN(dir) + 25;
-           buf = alloc(sz);
-
-           if (buf == NULL)
-               goto fail;
-
-           vim_snprintf((char *)buf, sz, "%s/vim-%lu", dir,
-                   (unsigned long int)getuid());
-       }
-       else
-       {
-           buf = alloc(STRLEN(dir) + STRLEN("vim") + 2);
-
-           if (buf == NULL)
-               goto fail;
-
-           sprintf((char *)buf, "%s/vim", dir);
-       }
-
-       // Always set directory permissions to 0700 for security
-       if (vim_mkdir(buf, 0700) == -1 && errno != EEXIST)
-       {
-           semsg(_("Failed creating socket directory: %s"), strerror(errno));
-           vim_free(buf);
-           goto fail;
-       }
-
-       num_printed = vim_snprintf((char *)path, sizeof(addr.sun_path),
-               "%s/%s", buf, name);
-
-       vim_free(buf);
-    }
-
-    // Check if path was too big
-    if ((size_t)num_printed >= sizeof(addr.sun_path))
-    {
-       emsg(_(e_socket_path_too_big));
-       goto fail;
-    }
-
-    vim_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
-
-    // Bind to a suitable path/address
-    while (i < 1000)
-    {
-       if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))
-               == -1)
-       {
-           int fd2;
-
-           if (errno != EADDRINUSE)
-           {
-               emsg(_(e_socket_server_unavailable));
-               goto fail;
-           }
-
-           // If the socket is dead, remove it and try again
-           fd2 = socket_server_connect((char_u *)addr.sun_path, NULL, TRUE);
-
-           if (fd2 == -1)
-           {
-               mch_remove(addr.sun_path);
-               continue;
-           }
-           else
-               close(fd2);
-       }
-       else
-           break;
-
-       num_printed = vim_snprintf(addr.sun_path, sizeof(addr.sun_path),
-               "%s%d", path, i);
-
-       if ((size_t)num_printed >= sizeof(addr.sun_path))
-       {
-           // Address too big
-           emsg(_(e_socket_path_too_big));
-           goto fail;
-       }
-
-       i++;
-    }
-
-    if (i >= 1000)
-    {
-       emsg(_(e_socket_server_unavailable));
-       goto fail;
-    }
-
-    // Start listening for connections
-    if (listen(fd, SOCKET_SERVER_MAX_BACKLOG) == -1)
-       goto fail;
-
-    // Set global path and vvar to the absolute path
-    if ((socket_server_path = alloc(MAXPATHL)) == NULL)
-       goto fail;
-
-    socket_server_path[0] = NUL;
-
-    if (mch_FullName((char_u *)addr.sun_path, socket_server_path,
-               MAXPATHL, FALSE) == FAIL)
-    {
-       vim_free(socket_server_path);
-       goto fail;
-    }
-
-    serverName = vim_strsave(socket_server_path);
-# ifdef FEAT_EVAL
-    set_vim_var_string(VV_SEND_SERVER, serverName, -1);
-# endif
-
-    socket_server_fd = fd;
-
-# ifdef FEAT_GUI_GTK
-    if (gui.in_use)
-       // Initialize source for GUI if we are using it
-       gui_gtk_init_socket_server();
-# endif
-
-    vim_free(path);
-    return OK;
-fail:
-    close(fd);
-    vim_free(path);
-    socket_server_uninit();
-    return FAIL;
-}
-
-    void
-socket_server_uninit(void)
-{
-    if (socket_server_fd != -1)
-    {
-       close(socket_server_fd);
-       socket_server_fd = -1;
-    }
-
-    if (socket_server_path != NULL)
-    {
-       mch_remove(socket_server_path);
-       vim_free(socket_server_path);
-       socket_server_path = NULL;
-    }
-# ifdef FEAT_GUI_GTK
-    if (gui.in_use)
-       gui_gtk_uninit_socket_server();
-# endif
-}
-
-/*
- * List available sockets that can be connected to, only in common directories
- * that Vim knows about. Vim instances with custom socket paths will not be
- * detected. Returns a newline separated string on success and NULL on failure.
- */
-    char_u *
-socket_server_list_sockets(void)
-{
-    garray_T           str;
-    string_T           buf;
-    string_T           path;
-    DIR                        *dirp;
-    struct dirent      *dp;
-    struct sockaddr_un addr;
-    char_u             *known_dirs[] = {
-       mch_getenv("XDG_RUNTIME_DIR"),
-       mch_getenv("TMPDIR"),
-       (char_u *)"/tmp"
-    };
-
-    if ((buf.string = alloc(sizeof(addr.sun_path))) == NULL)
-       return NULL;
-    if ((path.string = alloc(sizeof(addr.sun_path))) == NULL)
-    {
-       vim_free(buf.string);
-       return NULL;
-    }
-    buf.length = 0;
-    path.length = 0;
-
-    ga_init2(&str, 1, 100);
-
-    for (size_t i = 0 ; i < ARRAY_LENGTH(known_dirs); i++)
-    {
-       char_u *dir = known_dirs[i];
-
-       if (dir == NULL)
-           continue;
-
-       if (STRCMP(dir, "/tmp") == 0 ||
-               (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0))
-           path.length = vim_snprintf_safelen((char *)path.string, sizeof(addr.sun_path),
-               "%s/vim-%lu", dir, (unsigned long int)getuid());
-       else
-           path.length = vim_snprintf_safelen((char *)path.string, sizeof(addr.sun_path),
-               "%s/vim", dir);
-
-       dirp = opendir((char *)path.string);
-       if (dirp == NULL)
-           continue;
-
-       // Loop through directory
-       while ((dp = readdir(dirp)) != NULL)
-       {
-           if (STRCMP(dp->d_name, ".") == 0 || STRCMP(dp->d_name, "..") == 0)
-               continue;
-
-           buf.length = vim_snprintf_safelen((char *)buf.string, sizeof(addr.sun_path),
-               "%s/%s", path.string, dp->d_name);
-
-           // Don't want to send to ourselves, but we do want to list our
-           // server name (if we are a server).
-           if (socket_server_path == NULL
-                   || STRCMP(socket_server_path, buf.string) != 0)
-           {
-               // Try sending an ALIVE command. This is more assuring than a
-               // simple connect, and *also seems to make tests less flaky*.
-               //
-               // We could also use a lock file which may be better, but this
-               // has worked fine so far... - 64bitman
-               if (!socket_server_check_alive(buf.string))
-                   continue;
-           }
-
-           ga_concat_len(&str, (char_u *)dp->d_name, buf.length - (path.length + 1));
-           ga_append(&str, '\n');
-       }
-
-       closedir(dirp);
-
-       break;
-    }
-
-    vim_free(path.string);
-    vim_free(buf.string);
-
-    ga_append(&str, NUL);
-
-    return str.ga_data;
-}
-
-/*
- * Called when the server has received a new command. If so, parse it and do the
- * stuff it says, and possibly send back a reply. Returns OK if client was
- * accepted, else FAIL.
- */
-    int
-socket_server_accept_client(void)
-{
-    int        fd = accept(socket_server_fd, NULL, NULL);
-    ss_cmd_T cmd;
-
-    if (fd == -1)
-       return FAIL;
-
-    if (socket_server_decode_cmd(&cmd, fd, 1000) == FAIL)
-       goto exit;
-
-# ifdef FEAT_EVAL
-    ch_log(NULL, "accepted new client on socket %s", socket_server_path);
-# endif
-
-    socket_server_exec_cmd(&cmd, fd);
-    socket_server_free_cmd(&cmd);
-
-exit:
-    close(fd);
-    return OK;
-}
-
-/*
- * Check if socket server is able to be used
- */
-    int
-socket_server_valid(void)
-{
-    return socket_server_fd != -1 && socket_server_path != NULL;
-}
-
-/*
- * If "name" is a pathless name such as "VIM", search known directories for the
- * socket named "name", and return the alloc'ed path to it. If "name" starts
- * with a '/', './' or '../', then a copy of "name" is returned. Returns NULL
- * on failure or if no socket was found.
- */
-    static char_u *
-socket_server_get_path_from_name(char_u *name)
-{
-    char_u         *buf;
-    stat_T         s;
-    const char_u    *known_dirs[] = {
-       mch_getenv("XDG_RUNTIME_DIR"),
-       mch_getenv("TMPDIR"),
-       (char_u *)"/tmp"
-    };
-
-    if (name == NULL)
-       return NULL;
-
-    // Ignore if name is a path
-    if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 ||
-           STRNCMP(name, "../", 3) == 0)
-       return vim_strsave(name);
-
-    buf = alloc(MAXPATHL);
-
-    if (buf == NULL)
-       return NULL;
-
-    for (size_t i = 0; i < ARRAY_LENGTH(known_dirs); i++)
-    {
-       const char_u *dir = known_dirs[i];
-
-       if (dir == NULL)
-           continue;
-       else if (STRCMP(dir, "/tmp") == 0 ||
-               (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0))
-           vim_snprintf((char *)buf, MAXPATHL, "%s/vim-%lu/%s", dir,
-                   (unsigned long int)getuid(), name);
-       else
-           vim_snprintf((char *)buf, MAXPATHL, "%s/vim/%s", dir, name);
-
-       if (mch_stat((char *)buf,&s) == 0 && S_ISSOCK(s.st_mode))
-       {
-           if (STRCMP(buf, socket_server_path) == 0)
-               // Can't connect to itself
-               break;
-           return buf;
-       }
-    }
-
-    vim_free(buf);
-    return NULL;
-}
-
-/*
- * Send command to socket named "name". Returns 0 for OK, -1 on error.
- */
-    int
-socket_server_send(
-       char_u *name,       // Socket path or a general name
-       char_u *str,        // What to send
-       char_u **result,    // Set to result of expr
-       char_u **receiver,  // Full path of "name"
-       int is_expr,        // Is it an expression or keystrokes?
-       int timeout,        // In milliseconds
-       int silent)         // Don't complain if socket doesn't exist
-{
-    ss_cmd_T       cmd;
-    int                    socket_fd;
-    size_t         sz;
-    char_u         *final;
-    char_u         *path;
-# ifdef ELAPSED_FUNC
-    elapsed_T      start_tv;
-# endif
-
-    if (!socket_server_valid())
-    {
-       emsg(_(e_socket_server_not_online));
-       return -1;
-    }
-
-    socket_fd = socket_server_connect(name, &path, silent);
-
-    if (socket_fd == -1)
-       return -1;
-
-# ifdef FEAT_EVAL
-    ch_log(NULL, "socket_server_send(%s, %s)", path, str);
-# endif
-
-    // Execute locally if target is ourselves
-    if (serverName != NULL && STRICMP(path, serverName) == 0)
-    {
-       vim_free(path);
-       close(socket_fd);
-       return sendToLocalVim(str, is_expr, result);
-    }
-
-    socket_server_init_cmd(&cmd,
-           is_expr ? SS_CMD_TYPE_EXPR : SS_CMD_TYPE_KEYSTROKES);
-
-    socket_server_append_msg(&cmd, SS_MSG_TYPE_ENCODING, p_enc, STRLEN(p_enc));
-
-    // Add +1 in case of empty string
-    socket_server_append_msg(&cmd, SS_MSG_TYPE_STRING, str, STRLEN(str) + 1);
-
-    // Tell server who we are so it can save our socket path internally for
-    // later use with server2client
-    socket_server_append_msg(&cmd, SS_MSG_TYPE_SENDER, socket_server_path,
-           STRLEN(socket_server_path));
-
-    if (is_expr)
-    {
-       ss_serial++;
-       socket_server_append_msg(&cmd, SS_MSG_TYPE_SERIAL,
-               (char_u *)&ss_serial, sizeof(ss_serial));
-    }
-
-    final = socket_server_encode_cmd(&cmd, &sz);
-
-    if (final == NULL ||
-           socket_server_write(socket_fd, final, sz, 1000) == FAIL)
-    {
-       if (final != NULL)
-           emsg(_(e_failed_to_send_command_to_destination_program));
-
-       vim_free(path);
-       socket_server_free_cmd(&cmd);
-       close(socket_fd);
-       vim_free(final);
-       return -1;
-    }
-    socket_server_free_cmd(&cmd);
-    vim_free(final);
-
-
-    close(socket_fd);
-    if (!is_expr)
-    {
-       if (receiver != NULL)
-           *receiver = path;
-       else
-           vim_free(path);
-
-       // Exit, we aren't waiting for a response
-       return 0;
-    }
-
-    ss_pending_cmd_T pending;
-
-    socket_server_init_pending_cmd(&pending);
-
-# ifdef ELAPSED_FUNC
-    ELAPSED_INIT(start_tv);
-# endif
-
-    // Wait for server to send back result
-    while (socket_server_dispatch(500) >= 0)
-    {
-       if (pending.result != NULL)
-           break;
-
-# ifdef ELAPSED_FUNC
-       if (ELAPSED_FUNC(start_tv) >= (timeout > 0 ? timeout : 1000))
-           break;
-# endif
-    }
-
-    if (pending.result == NULL)
-    {
-       socket_server_pop_pending_cmd(&pending);
-       vim_free(path);
-       return -1;
-    }
-
-    if (result != NULL)
-       *result = pending.result;
-    else
-       vim_free(pending.result);
-
-    if (receiver != NULL)
-       *receiver = path;
-    else
-       vim_free(path);
-
-    socket_server_pop_pending_cmd(&pending);
-
-    return pending.code == 0 ? 0 : -1;
-}
-
-/*
- * Wait for replies from "client" and place result in "str". Returns OK on
- * success and FAIL on failure. Timeout is in milliseconds
- */
-    int
-socket_server_read_reply(char_u *client, char_u **str, int timeout UNUSED)
-{
-    ss_reply_T *reply = NULL;
-# ifdef ELAPSED_FUNC
-    elapsed_T  start_tv;
-# endif
-
-    if (!socket_server_name_is_valid(client))
-       return -1;
-
-    if (!socket_server_valid())
-       return -1;
-
-# ifdef ELAPSED_FUNC
-    if (timeout > 0)
-       ELAPSED_INIT(start_tv);
-# endif
-
-    // Try seeing if there already is a reply in the queue
-    goto get_reply;
-
-    while (socket_server_dispatch(500) >= 0)
-    {
-       int fd;
-
-# ifdef ELAPSED_FUNC
-       if (timeout > 0 && ELAPSED_FUNC(start_tv) >= timeout)
-           break;
-# endif
-
-get_reply:
-       reply = socket_server_get_reply(client, NULL);
-
-       if (reply != NULL)
-           break;
-
-       // Check if sender is down by connecting to it as a test. A simple
-       // connect will do.
-       fd = socket_server_connect(client, NULL, TRUE);
-
-       if (fd == -1)
-           return FAIL;
-       else
-           close(fd);
-    }
-
-    if (reply == NULL || reply->strings.ga_data == NULL ||
-           reply->strings.ga_len <= 0)
-    {
-       return FAIL;
-    }
-
-    // Consume the string
-    *str = ((char_u **)reply->strings.ga_data)[0];
-
-    for (int i = 1; i < reply->strings.ga_len; i++)
-    {
-       ((char_u **)reply->strings.ga_data)[i - 1] =
-           ((char_u **)reply->strings.ga_data)[i];
-    }
-    reply->strings.ga_len--;
-
-    if (reply->strings.ga_len < 1)
-       // Last string removed, remove the reply
-       socket_server_remove_reply(client);
-
-
-    return OK;
-}
-
-/*
- * Check for any replies for "sender". Returns 1 if there is and places the
- * reply in "str" without consuming it. Returns 0 if otherwise and -1 on
- * error.
- */
-    int
-socket_server_peek_reply(char_u *sender, char_u **str)
-{
-    ss_reply_T *reply;
-
-    if (!socket_server_name_is_valid(sender))
-       return -1;
-
-    if (!socket_server_valid())
-       return 0;
-
-    reply = socket_server_get_reply(sender, NULL);
-
-    if (reply != NULL && reply->strings.ga_len > 0)
-    {
-       if (str != NULL)
-           *str = ((char_u **)reply->strings.ga_data)[0];
-       return 1;
-    }
-
-    return 0;
-}
-
-/*
- * Send a string to "client" as a reply (notification). Returns OK on success
- * and FAIL on failure.
- */
-    int
-socket_server_send_reply(char_u *client, char_u *str)
-{
-    int socket_fd;
-    ss_cmd_T   cmd;
-    size_t     sz;
-    char_u     *final;
-
-    if (!socket_server_name_is_valid(client))
-       return FAIL;
-
-    if (!socket_server_valid())
-    {
-       emsg(_(e_socket_server_not_online));
-       return FAIL;
-    }
-
-    socket_fd = socket_server_connect(client, NULL, TRUE);
-
-    if (socket_fd == -1)
-       return FAIL;
-
-    socket_server_init_cmd(&cmd, SS_CMD_TYPE_NOTIFY);
-
-    socket_server_append_msg(&cmd, SS_MSG_TYPE_ENCODING, p_enc, STRLEN(p_enc));
-    socket_server_append_msg(&cmd, SS_MSG_TYPE_STRING, str, STRLEN(str));
-    socket_server_append_msg(&cmd, SS_MSG_TYPE_SENDER,
-           socket_server_path, STRLEN(socket_server_path));
-
-    final = socket_server_encode_cmd(&cmd, &sz);
-
-    if (final == NULL ||
-           socket_server_write(socket_fd, final, sz, 1000) == FAIL)
-    {
-       socket_server_free_cmd(&cmd);
-       vim_free(final);
-       close(socket_fd);
-       return FAIL;
-    }
-
-    socket_server_free_cmd(&cmd);
-    vim_free(final);
-    close(socket_fd);
-
-    return OK;
-}
-
-/*
- * Connect to a socket using "name". "path" is set to the full path of "name"
- * used to create the socket, only if its not NULL. Returns fd on success and -1
- * on failure.
- */
-    static int
-socket_server_connect(char_u *name, char_u **path, int silent)
-{
-    int                        socket_fd;
-    int                        res;
-    struct sockaddr_un addr;
-
-    char_u *socket_path = socket_server_get_path_from_name(name);
-
-    if (socket_path == NULL)
-    {
-       if (!silent)
-           semsg(_(e_no_registered_server_named_str), name);
-       return -1;
-    }
-    if (STRLEN(socket_path) >= sizeof(addr.sun_path))
-    {
-       // Path too big
-       vim_free(socket_path);
-       return -1;
-    }
-
-    socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-
-    if (socket_fd == -1)
-    {
-       vim_free(socket_path);
-       return -1;
-    }
-
-    addr.sun_family = AF_UNIX;
-    vim_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
-
-    res = connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr));
-
-    if (res == -1)
-    {
-       if (!silent)
-           semsg(_(e_socket_server_failed_connecting), socket_path,
-                   strerror(errno));
-       close(socket_fd);
-       vim_free(socket_path);
-       return -1;
-    }
-
-    if (path != NULL)
-       *path = socket_path;
-    else
-       vim_free(socket_path);
-
-    return socket_fd;
-
-}
-
-/*
- * Add a new pending command to the list of pending commands. Returns OK on
- * success and FAIL on failure
- */
-    static void
-socket_server_init_pending_cmd(ss_pending_cmd_T *pending)
-{
-    pending->code = 0;
-    pending->result = NULL;
-    pending->serial = ss_serial;
-    pending->next = ss_pending_cmds;
-    ss_pending_cmds = pending;
-}
-
-/*
- * Remove pending command from the list, does not free the result string.
- */
-    static void
-socket_server_pop_pending_cmd(ss_pending_cmd_T *pending)
-{
-    if (ss_pending_cmds == pending)
-    {
-       ss_pending_cmds = pending->next;
-       return;
-    }
-
-    for (ss_pending_cmd_T *cmd = ss_pending_cmds; cmd != NULL; cmd = cmd->next)
-    {
-       if (cmd->next == pending)
-       {
-           cmd->next = pending->next;
-           return;
-       }
-    }
-}
-
-/*
- * Initialize command structure to empty state
- */
-    static void
-socket_server_init_cmd(ss_cmd_T *cmd, ss_cmd_type_T type)
-{
-    cmd->cmd_len = 0;
-    cmd->cmd_num = 0;
-    cmd->cmd_type = type;
-}
-
-/*
- * Append a message to a command. Note that "len" is the length of contents.
- * Returns OK on success and FAIL on failure
- */
-    static int
-socket_server_append_msg(ss_cmd_T *cmd, char_u type, char_u *contents, int len)
-{
-    ss_msg_T *msg = cmd->cmd_msgs + cmd->cmd_num;
-
-    if (cmd->cmd_num >= SOCKET_SERVER_MAX_MSG)
-       return FAIL;
-
-    // Check if command will be too big.
-    if (SS_CMD_INFO_SIZE + cmd->cmd_len + SS_MSG_INFO_SIZE + len
-           > SOCKET_SERVER_MAX_CMD_SIZE)
-       return FAIL;
-
-    msg->msg_contents = alloc(len);
-
-    if (msg->msg_contents == NULL)
-       return FAIL;
-
-    msg->msg_type = type;
-    msg->msg_len = len;
-    memcpy(msg->msg_contents, contents, len);
-
-    cmd->cmd_len += SS_MSG_INFO_SIZE + len;
-    cmd->cmd_num++;
-
-    return OK;
-}
-
-/*
- * Free all resources associated with a command object.
- */
-    static void
-socket_server_free_cmd(ss_cmd_T *cmd)
-{
-    for (uint32_t i = 0; i < cmd->cmd_num; i++)
-    {
-       ss_msg_T *msg = cmd->cmd_msgs + i;
-
-       vim_free(msg->msg_contents);
-    }
-}
-
-/*
- * Encode command struct and return the final message to send. Returns NULL on
- * failure.
- */
-    static char_u *
-socket_server_encode_cmd(ss_cmd_T *cmd, size_t *sz)
-{
-    size_t size;
-    char_u *buf;
-    char_u *start;
-
-    size = SS_CMD_INFO_SIZE + cmd->cmd_len;
-    buf = alloc(size);
-
-    if (buf == NULL)
-       return NULL;
-
-    start = buf;
-    memcpy(start, &cmd->cmd_type, sizeof(cmd->cmd_type));
-    start += sizeof(cmd->cmd_type);
-    memcpy(start, &cmd->cmd_num, sizeof(cmd->cmd_num));
-    start += sizeof(cmd->cmd_num);
-    memcpy(start, &cmd->cmd_len, sizeof(cmd->cmd_len));
-    start += sizeof(cmd->cmd_len);
-
-    // Append messages to buffer
-    for (uint32_t i = 0; i < cmd->cmd_num; i++)
-    {
-       ss_msg_T *msg = cmd->cmd_msgs + i;
-
-       memcpy(start, &msg->msg_type, sizeof(msg->msg_type));
-       start += sizeof(msg->msg_type);
-       memcpy(start, &msg->msg_len, sizeof(msg->msg_len));
-       start += sizeof(msg->msg_len);
-
-       memcpy(start, msg->msg_contents, msg->msg_len);
-       start += msg->msg_len;
-    }
-
-    *sz = size;
-
-    return buf;
-}
-
-/*
- * Read from "socket_fd" an entire command and return the result in "cmd". The
- * socket fd should be at the start of the command. Returns OK on success and
- * FAIL on failure.
- */
-    static int
-socket_server_decode_cmd(ss_cmd_T *cmd, int socket_fd, int timeout)
-{
-    int                got_cmd_info    = FALSE; // Consists of type, num, and len
-    size_t     total_r         = 0;
-    char_u     *buf;
-    char_u     *cur;
-# ifdef ELAPSED_FUNC
-    elapsed_T  start_tv;
-# endif
-
-    // We also poll the socket server listening file descriptor to handle
-    // recursive remote calls between Vim instances, such as when one Vim
-    // instance calls remote_expr for an expression that calls remote_expr to
-    // itself again.
-# ifndef HAVE_SELECT
-    struct pollfd pfd;
-
-    pfd.fd = socket_fd;
-    pfd.events = POLLIN;
-# else
-    fd_set         rfds;
-    struct timeval  tv;
-
-    FD_ZERO(&rfds);
-    FD_SET(socket_fd, &rfds);
-# endif
-
-    buf = alloc(SS_CMD_INFO_SIZE);
-
-    if (buf == NULL)
-       return FAIL;
-
-    // We may exit in the middle of the loop and free the messages, we don't
-    // want to free an uninitialized pointer.
-    memset(cmd, 0, sizeof(*cmd));
-
-# ifdef ELAPSED_FUNC
-    ELAPSED_INIT(start_tv);
-# endif
-
-    while (TRUE)
-    {
-       int ret;
-       ssize_t r = 0;
-
-# ifndef HAVE_SELECT
-       ret = poll(&pfd, 1, timeout);
-# else
-       tv.tv_sec = 0;
-       tv.tv_usec = 500 * 1000;
-       ret = select(socket_fd + 1, &rfds, NULL, NULL, &tv);
-# endif
-       if (ret < 0)
-           goto fail;
-       if (ret == 0)
-           goto continue_loop;
-
-       // Get cmd info first so we know the total size of all messages, and
-       // can read it all in one go.
-       if (!got_cmd_info)
-       {
-           r = read(socket_fd, buf + total_r, SS_CMD_INFO_SIZE - total_r);
-
-           if ((size_t)r >= SS_CMD_INFO_SIZE - total_r)
-           {
-               char_u *tmp;
-
-               got_cmd_info = TRUE;
-
-               memcpy(&cmd->cmd_type, buf, sizeof(cmd->cmd_type));
-               memcpy(&cmd->cmd_num, buf + sizeof(cmd->cmd_type),
-                       sizeof(cmd->cmd_num));
-               memcpy(&cmd->cmd_len,
-                       buf + sizeof(cmd->cmd_type) + sizeof(cmd->cmd_num),
-                       sizeof(cmd->cmd_len));
-
-               if (cmd->cmd_num > SOCKET_SERVER_MAX_MSG)
-                   // Too many messages to handle or invalid number
-                   goto fail;
-
-               if (cmd->cmd_num == 0)
-                   // No messages to read
-                   goto exit;
-
-               // Now that we now the total size of messages, we can realloc
-               // the buffer to contain all data
-               tmp = vim_realloc(buf, SS_CMD_INFO_SIZE + cmd->cmd_len);
-
-               if (tmp == NULL)
-                   goto fail;
-
-               buf = tmp;
-               cur = buf + SS_CMD_INFO_SIZE;
-
-               continue;
-           }
-       }
-       else
-       {
-           // Read message data
-           r = read(socket_fd, cur + total_r, cmd->cmd_len - total_r);
-
-           if ((size_t)r >= cmd->cmd_len - total_r)
-               break;
-       }
-
-       if (r == -1 || r == 0)
-           goto fail;
-
-       total_r += r;
-
-continue_loop:
-# ifdef ELAPSED_FUNC
-       if (ELAPSED_FUNC(start_tv) >= timeout)
-           goto fail;
-# endif
-    }
-
-    // Parse message data
-    for (uint32_t i = 0; i <  cmd->cmd_num; i++)
-    {
-       ss_msg_T *msg = cmd->cmd_msgs + i;
-
-       memcpy(&msg->msg_type, cur, sizeof(msg->msg_type));
-       cur += sizeof(msg->msg_type);
-       memcpy(&msg->msg_len, cur, sizeof(msg->msg_len));
-       cur += sizeof(msg->msg_len);
-
-       msg->msg_contents = alloc(msg->msg_len + 1);
-
-       if (msg->msg_contents == NULL)
-           goto fail;
-
-       memcpy(msg->msg_contents, cur, msg->msg_len);
-       msg->msg_contents[msg->msg_len] = 0; // NULL terminate it
-
-       // Move pointer to start of next message
-       cur += msg->msg_len;
-    }
-
-exit:
-    vim_free(buf);
-    return OK;
-fail:
-    socket_server_free_cmd(cmd);
-    vim_free(buf);
-    return FAIL;
-}
-
-/*
- * Low level function that writes to a socket with a timeout in milliseconds.
- * Returns OK on success and FAIL on failure.
- */
-    static int
-socket_server_write(int socket_fd, char_u *data, size_t sz, int timeout)
-{
-    char_u *cur = data;
-    size_t total_w = 0;
-# ifdef ELAPSED_FUNC
-    elapsed_T start_tv;
-# endif
-# ifndef HAVE_SELECT
-    struct pollfd pfd;
-
-    pfd.fd = socket_fd;
-    pfd.events = POLLOUT;
-# else
-    fd_set         wfds;
-    struct timeval  tv;
-
-    FD_ZERO(&wfds);
-    FD_SET(socket_fd, &wfds);
-# endif
-
-# ifdef ELAPSED_FUNC
-    ELAPSED_INIT(start_tv);
-# endif
-
-    while (total_w < sz)
-    {
-       int ret;
-       ssize_t written;
-
-       errno = 0;
-# ifndef HAVE_SELECT
-       ret = poll(&pfd, 1, timeout);
-# else
-       tv.tv_sec = 0;
-       tv.tv_usec = 500 * 1000;
-       ret = select(socket_fd + 1, NULL, &wfds, NULL, &tv);
-# endif
-       if (ret < 0)
-           return FAIL;
-       else if (ret == 0)
-           goto continue_loop;
-
-       written = write(socket_fd, cur, sz - total_w);
-
-       if (written == -1)
-           return FAIL;
-
-       total_w += written;
-
-continue_loop:
-# ifdef ELAPSED_FUNC
-       if (ELAPSED_FUNC(start_tv) >= timeout)
-           return FAIL;
-# endif
-    }
-
-    return OK;
-}
-
-    static ss_reply_T *
-socket_server_get_reply(char_u *sender, int *index)
-{
-    for (int i = 0; i < ss_replies.ga_len; i++)
-    {
-       ss_reply_T *reply = ((ss_reply_T *)ss_replies.ga_data) + i;
-
-       if (STRCMP(reply->sender, sender) == 0)
-       {
-           if (index != NULL)
-               *index = i;
-           return reply;
-       }
-    }
-    return NULL;
-}
-
-/*
- * Add reply to list of replies. Returns a pointer to the ss_reply_T that was
- * initialized or was found.
- */
-    static ss_reply_T *
-socket_server_add_reply(char_u *sender)
-{
-    ss_reply_T *reply;
-
-    if (ss_replies.ga_growsize == 0)
-       ga_init2(&ss_replies, sizeof(ss_reply_T), 1);
-
-    reply = socket_server_get_reply(sender, NULL);
-
-    if (reply == NULL && ga_grow(&ss_replies, 1) == OK)
-    {
-       reply = ((ss_reply_T *)ss_replies.ga_data) + ss_replies.ga_len++;
-
-       reply->sender = vim_strsave(sender);
-
-       if (reply->sender == NULL)
-           return NULL;
-
-       ga_init2(&reply->strings, sizeof(char_u *), 5);
-    }
-
-    return reply;
-}
-
-    static void
-socket_server_remove_reply(char_u *sender)
-{
-    int index;
-    ss_reply_T *reply = socket_server_get_reply(sender, &index);
-
-    if (reply != NULL)
-    {
-       ss_reply_T *arr = ss_replies.ga_data;
-
-       // Free strings
-       vim_free(reply->sender);
-       ga_clear_strings(&reply->strings);
-
-       // Move all elements after the removed reply forward by one
-       for (int i = index + 1; i < ss_replies.ga_len; i++)
-           arr[i - 1] = arr[i];
-       ss_replies.ga_len--;
-    }
-}
-
-/*
- * Execute the actions given by command. "fd" is the socket of the client that
- * sent the command.
- */
-    static void
-socket_server_exec_cmd(ss_cmd_T *cmd, int fd)
-{
-    char_u         *str = NULL;
-    char_u         *enc = NULL;
-    char_u         *sender = NULL;
-    uint32_t       serial = 0;
-    char_u         rcode = 0;
-    char_u         *to_free;
-    char_u         *to_free2;
-
-    for (uint32_t i = 0; i < cmd->cmd_num; i++)
-    {
-       ss_msg_T *msg = cmd->cmd_msgs + i;
-
-       if (msg->msg_type == SS_MSG_TYPE_STRING)
-           str = msg->msg_contents;
-       if (msg->msg_type == SS_MSG_TYPE_ENCODING)
-           enc = msg->msg_contents;
-       if (msg->msg_type == SS_MSG_TYPE_SERIAL)
-           memcpy(&serial, msg->msg_contents, sizeof(serial));
-       if (msg->msg_type == SS_MSG_TYPE_CODE)
-           memcpy(&rcode, msg->msg_contents, sizeof(rcode));
-       else if (msg->msg_type == SS_MSG_TYPE_SENDER)
-       {
-           sender = msg->msg_contents;
-
-           // Save in global
-           vim_free(client_socket);
-           client_socket = vim_strsave(sender);
-       }
-    }
-
-# ifdef FEAT_EVAL
-    ch_log(NULL, "socket_server_exec_cmd(): encoding: %s, result: %s",
-           enc == NULL ? (char_u *)"(null)" : enc,
-           str == NULL ? (char_u *)"(null)" : str);
-# endif
-
-    if (cmd->cmd_type == SS_CMD_TYPE_EXPR ||
-           cmd->cmd_type == SS_CMD_TYPE_KEYSTROKES)
-    {
-       // Either an expression or keystrokes.
-       if (socket_server_valid() && enc != NULL && str != NULL)
-       {
-           str = serverConvert(enc, str, &to_free);
-
-           if (cmd->cmd_type == SS_CMD_TYPE_KEYSTROKES)
-               server_to_input_buf(str);
-           else if (sender != NULL)
-           {
-               // Evaluate expression and send reply containing result
-               char_u      *result;
-               size_t      sz;
-               char_u      *buf;
-               char_u      code;
-
-               result = eval_client_expr_to_string(str);
-
-               code = result == NULL ? -1 : 0;
-
-               // Send reply
-               ss_cmd_T rcmd;
-
-               socket_server_init_cmd(&rcmd, SS_CMD_TYPE_REPLY);
-
-               // Don't care about errors, server will just ignore command if
-               // its missing something.
-               if (result != NULL)
-                   socket_server_append_msg(&rcmd, SS_MSG_TYPE_STRING, result,
-                           STRLEN(result) + 1); // We add +1 in case "result"
-                                                // is an empty string.
-               else
-                   // An error occurred, return an error msg instead
-                   socket_server_append_msg(&rcmd, SS_MSG_TYPE_STRING,
-                           (char_u *)_(e_invalid_expression_received),
-                           STRLEN(e_invalid_expression_received));
-
-               socket_server_append_msg(&rcmd, SS_MSG_TYPE_CODE,
-                       &code, sizeof(code));
-
-               socket_server_append_msg(&rcmd, SS_MSG_TYPE_ENCODING, p_enc,
-                       STRLEN(p_enc));
-
-               socket_server_append_msg(&rcmd, SS_MSG_TYPE_SERIAL,
-                       (char_u *)&serial, sizeof(serial));
-
-               buf = socket_server_encode_cmd(&rcmd, &sz);
-
-               if (buf != NULL)
-               {
-                   int fd2 = socket_server_connect(sender, NULL, TRUE);
-
-                   if (fd2 >= 0)
-                       socket_server_write(fd2, buf, sz, 1000);
-                   vim_free(buf);
-                   close(fd2);
-               }
-
-               socket_server_free_cmd(&rcmd);
-               vim_free(result);
-           }
-           vim_free(to_free);
-       }
-       return;
-    }
-    else if (cmd->cmd_type == SS_CMD_TYPE_REPLY)
-    {
-       // A reply from a previous command we set up, update the corresponding
-       // pending command.
-       if (serial > 0 && str != NULL)
-       {
-           for (ss_pending_cmd_T *pending = ss_pending_cmds; pending != NULL;
-                   pending = pending->next)
-           {
-               if (serial == pending->serial && pending->result == NULL)
-               {
-                   str = serverConvert(enc, str, &to_free);
-
-                   pending->code = rcode;
-
-                   if (to_free == NULL)
-                       pending->result = vim_strsave(str);
-                   else
-                       pending->result = str;
-                   break;
-               }
-           }
-       }
-       return;
-    }
-    else if (cmd->cmd_type == SS_CMD_TYPE_NOTIFY)
-    {
-       // Notification, execute autocommands and save the reply for later use
-       if (sender != NULL && str != NULL && enc != NULL)
-       {
-           ss_reply_T *reply;
-
-           str = serverConvert(enc, str, &to_free);
-           sender = serverConvert(enc, sender, &to_free2);
-
-           reply = socket_server_add_reply(sender);
-
-           if (reply != NULL)
-               ga_copy_string(&reply->strings, str);
-
-           apply_autocmds(EVENT_REMOTEREPLY, sender, str, TRUE, curbuf);
-
-           vim_free(to_free);
-           vim_free(to_free2);
-       }
-       return;
-    }
-    else if (cmd->cmd_type == SS_CMD_TYPE_ALIVE)
-    {
-       // Client wants to check if we are still responsive, send back a single
-       // byte as a YES.
-       char_u buf[1] = {1};
-# ifndef HAVE_SELECT
-       struct pollfd pfd;
-
-       pfd.fd = fd;
-       pfd.events = POLLIN;
-# else
-       fd_set          rfds;
-       struct timeval  tv;
-
-       FD_ZERO(&rfds);
-       FD_SET(fd, &rfds);
-# endif
-
-       if (write(fd, buf, 1) == -1)
-           return;
-
-       // Poll until client closes their end
-
-# ifndef HAVE_SELECT
-       poll(&pfd, 1, 1000);
-# else
-       tv.tv_sec = 1;
-       tv.tv_usec = 0;
-       select(fd + 1, &rfds, NULL, NULL, &tv);
-# endif
-       return;
-    }
-
-    // Command type is invalid, do nothing
-    return;
-}
-
-/*
- * Poll the socket server fd until a new connection is accepted. Returns 0 on
- * success, 1 if it timed out or if poll returned empty, and -1 on error.
- */
-    static int
-socket_server_dispatch(int timeout)
-{
-    int ret;
-# ifndef HAVE_SELECT
-    struct pollfd pfd;
-
-    pfd.fd = socket_server_fd;
-    pfd.events = POLLIN;
-# else
-    fd_set         rfds;
-    fd_set         efds;
-    struct timeval  tv;
-
-    FD_ZERO(&rfds);
-    FD_ZERO(&efds);
-    FD_SET(socket_server_fd, &rfds);
-    FD_SET(socket_server_fd, &efds);
-# endif
-
-# ifndef HAVE_SELECT
-    ret = poll(&pfd, 1, timeout);
-# else
-    tv.tv_sec = timeout / 1000;
-    tv.tv_usec = (timeout % 1000) * 1000;
-    ret = select(socket_server_fd + 1, &rfds, NULL, &efds, &tv);
-# endif
-
-    if (ret < 0)
-       return -1;
-    else if (ret == 0)
-       return 1;
-
-# ifndef HAVE_SELECT
-    if (pfd.revents & POLLIN)
-# else
-    if (FD_ISSET(socket_server_fd, &rfds))
-# endif
-       {
-           socket_server_accept_client();
-           return 0;
-       }
-# ifndef HAVE_SELECT
-    else if (pfd.revents & (POLLHUP | POLLERR))
-# else
-    else if (FD_ISSET(socket_server_fd, &efds))
-# endif
-       // Connection was closed
-       return -1;
-    else
-       return 1;
-
-    return -1;
-}
-
-/*
- * Check if socket "name" is responsive by sending an ALIVE command. This does
- * not require the socket server to be active.
- */
-    static int
-socket_server_check_alive(char_u *name)
-{
-    int            socket_fd;
-    int            ret;
-    size_t  sz;
-    char_u  *final;
-    char_u  buf[1] = {0};
-# ifndef HAVE_SELECT
-    struct pollfd pfd;
-# else
-    fd_set         rfds;
-    struct timeval  tv;
-# endif
-
-    socket_fd = socket_server_connect(name, NULL, TRUE);
-
-    if (socket_fd == -1)
-       return FALSE;
-
-# ifndef HAVE_SELECT
-    pfd.fd = socket_fd;
-    pfd.events = POLLIN;
-# else
-    FD_ZERO(&rfds);
-    FD_SET(socket_fd, &rfds);
-# endif
-
-    ss_cmd_T cmd;
-
-    socket_server_init_cmd(&cmd, SS_CMD_TYPE_ALIVE);
-
-    final = socket_server_encode_cmd(&cmd, &sz);
-
-    if (final == NULL ||
-           socket_server_write(socket_fd, final, sz, 1000) == FAIL)
-    {
-       vim_free(final);
-       close(socket_fd);
-       return FALSE;
-    }
-    vim_free(final);
-
-    // Poll for response
-# ifndef HAVE_SELECT
-    ret = poll(&pfd, 1, 1000);
-# else
-    tv.tv_sec = 1;
-    tv.tv_usec = 0;
-    ret = select(socket_fd + 1, &rfds, NULL, NULL, &tv);
-# endif
-
-    if (ret > 0)
-       if (read(socket_fd, buf, 1) == -1)
-       {
-           close(socket_fd);
-           return FALSE;
-       }
-
-    close(socket_fd);
-    return buf[0] == 1;
-}
-
-/*
- * Get file descriptor of listening socket
- */
-    int
-socket_server_get_fd(void)
-{
-    return socket_server_fd;
-}
-
-
-/*
- * Check if socket name is a valid name
- */
-    static int
-socket_server_name_is_valid(char_u *name)
-{
-    if (STRLEN(name) == 0 || (name[0] != '/' && vim_strchr(name, '/') != NULL))
-    {
-       semsg(_(e_invalid_server_id_used_str), name);
-       return FALSE;
-    }
-    return TRUE;
-}
-
-/*
- * Returns TRUE if there are clients queued in the listening socket waiting to
- * be accepted
- */
-    int
-socket_server_waiting_accept(void)
-{
-    int ret;
-# ifndef HAVE_SELECT
-    struct pollfd pfd;
-
-    pfd.fd = socket_server_fd;
-    pfd.events = POLLIN;
-
-    ret = poll(&pfd, 1, 0);
-
-    if (ret > 0 && pfd.revents & POLLIN)
-       return TRUE;
-# else
-    fd_set         rfds;
-    struct timeval  tv;
-
-    if (socket_server_fd == -1)
-       return FALSE;
-
-    FD_ZERO(&rfds);
-    FD_SET(socket_server_fd, &rfds);
-
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-    ret = select(socket_server_fd + 1, &rfds, NULL, NULL, &tv);
-
-    if (ret > 0 && FD_ISSET(socket_server_fd, &rfds))
-       return TRUE;
-# endif
-
-    return FALSE;
-}
-
-#endif // FEAT_SOCKETSERVER
index ad805b30b7d1a2e42a55dc8cf90ec0de8a90b9c5..946ce08831008f1d981cae6b46fa3c6e7a4a53af 100644 (file)
@@ -254,9 +254,6 @@ msgstr ""
 msgid "%d of %d edited"
 msgstr ""
 
-msgid "Socket server not online:Send expression failed"
-msgstr ""
-
 msgid "No display: Send expression failed.\n"
 msgstr ""
 
@@ -1722,7 +1719,8 @@ msgstr ""
 msgid "-Y\t\t\tDo not connect to Wayland compositor"
 msgstr ""
 
-msgid "--clientserver <socket|x11> Backend for clientserver communication"
+msgid ""
+"--clientserver <socket|x11|mswin> Backend for clientserver communication"
 msgstr ""
 
 msgid "--remote <files>\tEdit <files> in a Vim server if possible"
@@ -2531,10 +2529,6 @@ msgstr ""
 msgid "XSMP SmcOpenConnection failed: %s"
 msgstr ""
 
-#, c-format
-msgid "Failed creating socket directory: %s"
-msgstr ""
-
 msgid "At line"
 msgstr ""
 
@@ -2806,6 +2800,10 @@ msgstr ""
 msgid " (not supported)"
 msgstr ""
 
+#, c-format
+msgid "Failed creating socket directory: %s"
+msgstr ""
+
 #, c-format
 msgid "Warning: Cannot find word list \"%s_%s.spl\" or \"%s_ascii.spl\""
 msgstr ""
@@ -8834,20 +8832,20 @@ msgstr ""
 msgid "E1562: Diff anchors cannot be used with hidden diff windows"
 msgstr ""
 
-msgid "E1563: Socket path is too big"
-msgstr ""
-
-msgid "E1564: Socket name cannot have slashes in it without being a path"
+#, c-format
+msgid "E1564: Socket name '%s' cannot have slashes in it without being a path"
 msgstr ""
 
 msgid "E1565: Socket server is not online, call remote_startserver() first"
 msgstr ""
 
 #, c-format
-msgid "E1566: Failed connecting to socket %s: %s"
+msgid "E1566: Failed connecting to socket '%s'"
 msgstr ""
 
-msgid "E1567: Cannot start socket server, socket path is unavailable"
+msgid ""
+"E1567: Socket server protocol version mismatch, check what Vim version you "
+"are using"
 msgstr ""
 
 #, c-format
index ed7a5a054e990b618fa96cf982f409fcf49b0769..8dd9c30cbe6fa9090b33e1be5118a9159905d023 100644 (file)
@@ -277,6 +277,9 @@ void mbyte_im_set_active(int active_arg);
 #  include "job.pro"
 #  include "channel.pro"
 # endif
+# ifdef FEAT_SOCKETSERVER
+#  include "socketserver.pro"
+# endif
 
 # ifdef FEAT_EVAL
 // Not generated automatically so that we can add an extra attribute.
index 0b5d17a137421b42ca28a014bd1f5d11d915ad6e..cd58f2c3171f69e855876b75ebe4aa6b38b3e3ed 100644 (file)
@@ -7,15 +7,18 @@ int channel_unref(channel_T *channel);
 int free_unused_channels_contents(int copyID, int mask);
 void free_unused_channels(int copyID, int mask);
 void channel_gui_register_all(void);
+channel_T *channel_open_unix(const char *path, void (*nb_close_cb)(void));
 channel_T *channel_open(const char *hostname, int port, int waittime, void (*nb_close_cb)(void));
+int channel_parse_socketserver_address(char_u *address, int *port, char_u **unix_path, bool quiet);
 channel_T *channel_listen_func(typval_T *argvars);
 channel_T *channel_listen(int port_in, void (*nb_close_cb)(void));
-channel_T *channel_listen_unix(char *path, void (*nb_close_cb)(void));
+channel_T *channel_listen_unix(char *path, void (*nb_close_cb)(void), bool replace);
 void ch_close_part(channel_T *channel, ch_part_T part);
 void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err);
 void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options);
 void channel_write_in(channel_T *channel);
 void channel_buffer_free(buf_T *buf);
+void channel_write_input(channel_T *channel);
 void channel_write_any_lines(void);
 void channel_write_new_lines(buf_T *buf);
 readq_T *channel_peek(channel_T *channel, ch_part_T part);
@@ -23,11 +26,15 @@ char_u *channel_first_nl(readq_T *node);
 char_u *channel_get(channel_T *channel, ch_part_T part, int *outlen);
 void channel_consume(channel_T *channel, ch_part_T part, int len);
 int channel_collapse(channel_T *channel, ch_part_T part, int want_nl);
+int channel_fill(js_read_T *reader);
+int channel_parse_json(channel_T *channel, ch_part_T part, bool socketserver);
+void remove_json_node(jsonq_T *head, jsonq_T *node);
 int channel_can_write_to(channel_T *channel);
 int channel_is_open(channel_T *channel);
 void channel_close(channel_T *channel, int invoke_close_cb);
 void channel_clear(channel_T *channel);
 void channel_free_all(void);
+void channel_check(channel_T *channel, ch_part_T part);
 int channel_in_blocking_wait(void);
 channel_T *get_channel_arg(typval_T *tv, int check_open, int reading, ch_part_T part);
 void channel_handle_events(int only_keep_open);
@@ -59,4 +66,5 @@ void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
 void f_ch_setoptions(typval_T *argvars, typval_T *rettv);
 void f_ch_status(typval_T *argvars, typval_T *rettv);
 char_u *channel_to_string_buf(typval_T *varp, char_u *buf);
+channel_T *channel_find(int ch_id);
 /* vim: set ft=c : */
index 5a572401355d3acac28945928460ce0d2f6d5023..696f6a07af1a1fe22be8f93dab5b26867c4daa72 100644 (file)
@@ -8,8 +8,6 @@ void gui_mch_stop_blink(int may_call_gui_update_cursor);
 void gui_mch_start_blink(void);
 int gui_mch_early_init_check(int give_message);
 int gui_mch_init_check(void);
-void gui_gtk_init_socket_server(void);
-void gui_gtk_uninit_socket_server(void);
 void gui_mch_set_dark_theme(int dark);
 void gui_mch_show_tabline(int showit);
 int gui_mch_showing_tabline(void);
index 7513e400f046002ff7597a3f39154b055c05f9b1..aca289d1f75aa2c34364297e93556f66e229e8c2 100644 (file)
@@ -96,15 +96,4 @@ void stop_timeout(void);
 volatile sig_atomic_t *start_timeout(long msec);
 void delete_timer(void);
 int mch_create_anon_file(void);
-int socket_server_init(char_u *name);
-void socket_server_uninit(void);
-char_u *socket_server_list_sockets(void);
-int socket_server_accept_client(void);
-int socket_server_valid(void);
-int socket_server_send(char_u *name, char_u *str, char_u **result, char_u **receiver, int is_expr, int timeout, int silent);
-int socket_server_read_reply(char_u *client, char_u **str, int timeout);
-int socket_server_peek_reply(char_u *sender, char_u **str);
-int socket_server_send_reply(char_u *client, char_u *str);
-int socket_server_get_fd(void);
-int socket_server_waiting_accept(void);
 /* vim: set ft=c : */
diff --git a/src/proto/socketserver.pro b/src/proto/socketserver.pro
new file mode 100644 (file)
index 0000000..69d292c
--- /dev/null
@@ -0,0 +1,11 @@
+/* socketserver.c */
+int socketserver_start(char_u *name, bool quiet);
+void socketserver_stop(void);
+char_u *socketserver_list(void);
+int set_ref_in_socketserver_channel(int copyID);
+void socketserver_parse_messages(void);
+int socketserver_send(char_u *name, char_u *str, char_u **result, bool is_expr, int timeout, bool silent, channel_T **ch);
+int socketserver_send_reply(char_u *client, char_u *str);
+int socketserver_read_reply(char_u *client, char_u **str, int timeout, bool remotewait);
+int socketserver_peek_reply(char_u *sender, char_u **str);
+/* vim: set ft=c : */
diff --git a/src/socketserver.c b/src/socketserver.c
new file mode 100644 (file)
index 0000000..7c4e19b
--- /dev/null
@@ -0,0 +1,1404 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved   by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * socketserver.c: Socketserver clientserver functionality
+ */
+
+#include "vim.h"
+
+#ifdef FEAT_SOCKETSERVER
+
+// Always greater than one.
+# define PROTOCOL_VER 1
+
+/*
+ * Any message sent to a Vim server or received from a Vim server is a simple
+ * JSON object with the following fields:
+ * {
+ *   "version": PROTOCOL_VER -- Protocol version, if different, then ignore.
+ *
+ *   "type": "expr"|"keystrokes"|"notify"|"reply"|"ver" -- The type of message
+ *
+ *   "str": string -- What to execute for the command or contents of result
+ *
+ *   ?"code": number -- Return code for expression
+ *
+ *   ?"sender": string -- Address of client that sent command if it is a server.
+ *
+ *   ?"wait": bool -- Used by --remote-wait since the client is not a server.
+ * }
+ */
+
+/*
+ * Represents a reply from a server2client call. Each client that calls a
+ * server2client call to us has its own ss_reply_T. Each time a client sends
+ * data using server2client, Vim creates a ss_reply_T if it doesn't exist and
+ * adds the string to the array. When remote_read is called, the server id is
+ * used to find the specific ss_reply_T, and a single string is popped from the
+ * array.
+ */
+typedef struct
+{
+    char_u *sender; // Includes "type:" prefix if any
+    garray_T strings;
+} ss_reply_T;
+
+static channel_T    *server_channel = NULL;
+static char_u      *server_address = NULL; // Includes "type:" prefix if any
+static bool        server_is_unix = false;
+static char_u      *server_addr_cwd = NULL; // CWD when server was started, used
+                                         // to handle relative file paths.
+static garray_T            server_replies;
+
+static channel_T    *client_channels = NULL;
+
+# define FOR_ALL_CLIENTS(ch) \
+    for (ch = client_channels; ch != NULL; ch = ch->ch_ss_next)
+
+static void socketserver_cleanup(void);
+static char_u *socketserver_create_path(char_u *name, bool quiet);
+static char_u *socketserver_get_path(char_u *name, bool new, bool quiet, bool *fatal);
+static void socketserver_accept(channel_T *channel);
+static void socketserver_close(channel_T *channel);
+static ss_reply_T *socketserver_add_reply(char_u *sender);
+
+/*
+ * Start the socketserver using the given name. Returns OK on success and FAIL
+ * on failure.
+ */
+    int
+socketserver_start(char_u *name, bool quiet)
+{
+    char_u     *address = NULL;
+    int                port;
+    bool       is_unix = false;
+    channel_T  *channel;
+    char_u     *buf;
+    char_u     dirbuf[MAXPATHL];
+
+    if (server_channel != NULL)
+       return OK;
+
+    if (STRNICMP(name, "channel:", 8) == 0)
+    {
+       if (channel_parse_socketserver_address(name + 8, &port, &address, quiet)
+               == FAIL)
+           return FAIL;
+       if (address != NULL)
+           is_unix = true;
+    }
+    else
+    {
+       address = socketserver_create_path(name, quiet);
+       if (address == NULL)
+           return FAIL;
+       is_unix = true;
+    }
+
+    if (is_unix)
+       channel = channel_listen_unix((char *)address, NULL, false);
+    else
+       channel = channel_listen(port, NULL);
+
+    if (channel == NULL)
+    {
+       vim_free(address);
+       return FAIL;
+    }
+
+    channel->ch_socketserver = true;
+    channel->ch_ss_accept_cb = socketserver_accept;
+    channel->ch_ss_close_cb = socketserver_close;
+
+    server_channel = channel;
+    server_is_unix = is_unix;
+
+    VIM_CLEAR(serverName);
+
+    if (STRNICMP(name, "channel:", 8) == 0)
+       buf = vim_strsave(name);
+    else
+    {
+       buf = alloc(MAXPATHL + 1);
+       if (buf != NULL)
+       {
+           buf[0] = NUL;
+           mch_FullName(address, buf, MAXPATHL, false);
+       }
+    }
+    vim_free(address);
+
+    if (buf != NULL)
+    {
+       server_address = vim_strsave(buf);
+       serverName = buf;
+       set_vim_var_string(VV_SEND_SERVER, serverName, -1);
+    }
+
+    vim_free(server_addr_cwd);
+    if (mch_dirname(dirbuf, sizeof(dirbuf)) == OK)
+       server_addr_cwd = vim_strsave(dirbuf);
+    else
+       server_addr_cwd = NULL;
+
+    ga_init2(&server_replies, sizeof(ss_reply_T), 2);
+
+    ch_log(NULL, "socketserver: started server at %s", name);
+
+    return OK;
+}
+
+/*
+ * Stop running the socketserver if it is. Note that this does not stop Vim from
+ * becoming a client.
+ */
+    void
+socketserver_stop(void)
+{
+    if (server_channel == NULL)
+       return;
+
+    channel_close(server_channel, false);
+    channel_clear(server_channel);
+
+    socketserver_cleanup();
+
+    ch_log(NULL, "socketserver: shutting down server");
+}
+
+/*
+ * Cleanup server stuff. Do not close client channels, as those are not part of
+ * the actual server.
+ */
+    static void
+socketserver_cleanup(void)
+{
+# ifdef UNIX
+    if (server_is_unix && server_address != NULL)
+    {
+       char_u *path = server_address;
+       char_u dirbuf[MAXPATHL];
+
+       if (STRNICMP(path, "channel:unix:", 13) == 0)
+           path += 13;
+       else if (STRNICMP(path, "name:", 5) == 0)
+           path += 5;
+
+       if (*path == '/')
+           mch_remove(path);
+       // Go to the directory where the server was started. This is to handle
+       // when Vim changes directories and the servername is a relative path.
+       else if (server_addr_cwd != NULL
+               && mch_dirname(dirbuf, sizeof(dirbuf)) == OK)
+       {
+           if (mch_chdir((char *)server_addr_cwd) == 0)
+           {
+               mch_remove(path);
+               if (mch_chdir((char *)dirbuf) < 0)
+                   emsg(_(e_cannot_go_back_to_previous_directory));
+           }
+       }
+    }
+# endif
+
+    server_channel = NULL;
+    VIM_CLEAR(server_address);
+    VIM_CLEAR(server_addr_cwd);
+
+    // Free all replies
+    for (int i = 0; i < server_replies.ga_len; i++)
+    {
+       ss_reply_T *reply = (ss_reply_T *)server_replies.ga_data + i;
+
+       vim_free(reply->sender);
+       ga_clear_strings(&reply->strings);
+    }
+    ga_clear(&server_replies);
+}
+
+/*
+ * List available sockets that can be connected to, only in common directories
+ * that Vim knows about. Vim instances with custom socket paths will not be
+ * detected. Returns a newline separated string on success and NULL on failure.
+ */
+    char_u *
+socketserver_list(void)
+{
+# ifdef MSWIN
+    // Only support addresses on Windows
+    return vim_strsave((char_u *)"");
+# else
+    garray_T       str;
+    string_T       buf;
+    string_T       path;
+    DIR                    *dirp;
+    struct dirent   *dp;
+    const char_u    *known_dirs[] = {
+       mch_getenv("XDG_RUNTIME_DIR"),
+       mch_getenv("TMPDIR"),
+       (char_u *)"/tmp"
+    };
+
+    if ((buf.string = alloc(MAXPATHL)) == NULL)
+       return NULL;
+    if ((path.string = alloc(MAXPATHL)) == NULL)
+    {
+       vim_free(buf.string);
+       return NULL;
+    }
+    buf.length = 0;
+    path.length = 0;
+
+    ga_init2(&str, 1, 100);
+
+    for (size_t i = 0 ; i < ARRAY_LENGTH(known_dirs); i++)
+    {
+       const char_u *dir = known_dirs[i];
+
+       if (dir == NULL)
+           continue;
+
+       if (STRCMP(dir, "/tmp") == 0 ||
+               (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0))
+           path.length = vim_snprintf_safelen((char *)path.string, MAXPATHL,
+                   "%s/vim-%lu", dir, (unsigned long int)getuid());
+       else
+           path.length = vim_snprintf_safelen((char *)path.string, MAXPATHL,
+                   "%s/vim", dir);
+
+       dirp = opendir((char *)path.string);
+       if (dirp == NULL)
+           continue;
+
+       // Loop through directory
+       while ((dp = readdir(dirp)) != NULL)
+       {
+           if (STRCMP(dp->d_name, ".") == 0 || STRCMP(dp->d_name, "..") == 0)
+               continue;
+
+           buf.length = vim_snprintf_safelen((char *)buf.string, MAXPATHL,
+                   "%s/%s", path.string, dp->d_name);
+
+           ga_concat_len(&str, (char_u *)dp->d_name,
+                   buf.length - (path.length + 1));
+           ga_append(&str, '\n');
+       }
+
+       closedir(dirp);
+
+       break;
+    }
+
+    vim_free(path.string);
+    vim_free(buf.string);
+
+    ga_append(&str, NUL);
+
+    return str.ga_data;
+# endif
+}
+
+/*
+ * If "name" is a path (starts with a '/', './', or '../'), it is assumed to be
+ * the path to the desired socket. If the socket path is already taken, append
+ * an incrementing number to the path until we find a socket filename that can
+ * be used. Returns alloced string or NULL on failure.
+ */
+    static char_u *
+socketserver_create_path(char_u *name, bool quiet)
+{
+# ifdef MSWIN
+    // Only support channel addresses on Windows
+    if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8)
+       return vim_strsave(name + 8);
+    else
+    {
+       if (!quiet)
+           semsg(_(e_invalid_argument_str), name);
+       return NULL;
+    }
+# else
+    char_u  *buf = NULL;
+    int            buflen = STRLEN(name) + NUMBUFLEN;
+    char_u  *path = NULL;
+    bool    fatal = false;
+
+    if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8)
+       return vim_strsave(name + 8);
+    if (STRNICMP(name, "name:", 5) == 0)
+       name += 5;
+
+    for (int i = 0; i < 1000; i++)
+    {
+       if (buf != NULL)
+       {
+           vim_snprintf((char *)buf, buflen, "%s%d", name, i);
+           path = socketserver_get_path(buf, true, quiet, &fatal);
+       }
+       else
+           path = socketserver_get_path(name, true, quiet, &fatal);
+
+       if (fatal)
+           break;
+
+       if (path == NULL)
+       {
+           if (buf == NULL)
+           {
+               buf = alloc(buflen);
+               if (buf == NULL)
+               {
+                   semsg(_(e_out_of_memory_allocating_nr_bytes), buflen);
+                   return NULL;
+               }
+           }
+           continue;
+       }
+       break;
+    }
+    vim_free(buf);
+
+    return path;
+# endif
+}
+
+/*
+ * If "name" is a pathless name such as "VIM", search known directories for the
+ * socket named "name", and return the alloc'ed path to it. If "name" starts
+ * with a '/', './' or '../', then a copy of "name" is returned.
+ *
+ * If "name" starts with "channel:", then return the address part
+ *
+ * If "new" is true, then return a path if the name does not exist in the known
+ * location.
+ *
+ * If "fatal" is not NULL, then it is set to true if error is fatal.
+ *
+ * Returns path on success and NULL on failure.
+ */
+    static char_u *
+socketserver_get_path(char_u *name, bool new UNUSED, bool quiet, bool *fatal)
+{
+# ifdef MSWIN
+    // Only support channel addresses on Windows
+    if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8)
+       return vim_strsave(name + 8);
+    else
+    {
+       if (!quiet)
+           semsg(_(e_invalid_argument_str), name);
+       if (fatal != NULL)
+           *fatal = true;
+       return NULL;
+    }
+# else
+    char_u         *buf;
+    bool           res = false;
+    channel_T      *channel;
+    const char_u    *known_dirs[] = {
+       mch_getenv("XDG_RUNTIME_DIR"),
+       mch_getenv("TMPDIR"),
+       (char_u *)"/tmp"
+    };
+
+    if (name == NULL)
+    {
+       if (fatal != NULL)
+           *fatal = true;
+       return NULL;
+    }
+
+    // Ignore if name is a path
+    if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 ||
+           STRNCMP(name, "../", 3) == 0)
+       return vim_strsave(name);
+
+    if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8)
+       return vim_strsave(name + 8);
+
+    if (STRNICMP(name, "name:", 5) == 0)
+       name += 5;
+
+    if (vim_strchr(name, '/') != NULL)
+    {
+       if (!quiet)
+           semsg(_(e_socket_name_no_slashes), name);
+       if (fatal != NULL)
+           *fatal = true;
+       return NULL;
+    }
+
+    buf = alloc(MAXPATHL);
+
+    if (buf == NULL)
+    {
+       if (fatal != NULL)
+           *fatal = true;
+       semsg(_(e_out_of_memory_allocating_nr_bytes), MAXPATHL);
+       return NULL;
+    }
+
+    for (size_t i = 0; i < ARRAY_LENGTH(known_dirs); i++)
+    {
+       const char_u    *dir = known_dirs[i];
+       bool            got = false;
+
+       if (dir == NULL)
+           continue;
+       else if (STRCMP(dir, "/tmp") == 0 ||
+               (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0))
+       {
+           // "/tmp" or $TMPDIR, must suffix dir with uid
+           vim_snprintf((char *)buf, MAXPATHL, "%s/vim-%lu", dir,
+                   (unsigned long int)getuid());
+           if (vim_mkdir(buf, 0700) == -1 && errno != EEXIST)
+               continue;
+
+           vim_snprintf((char *)buf, MAXPATHL, "%s/vim-%lu/%s", dir,
+                   (unsigned long int)getuid(), name);
+       }
+       else
+       {
+           vim_snprintf((char *)buf, MAXPATHL, "%s/vim", dir);
+           if (vim_mkdir(buf, 0700) == -1 && errno != EEXIST)
+               continue;
+
+           vim_snprintf((char *)buf, MAXPATHL, "%s/vim/%s", dir, name);
+       }
+
+       // If looking for a new socket path, and "buf" currently exists, check
+       // if it is a dead socket, if it is then remove it.
+       if (new)
+       {
+           emsg_silent++;
+           channel = channel_open_unix((char *)buf, NULL);
+           emsg_silent--;
+
+           if (channel != NULL)
+           {
+               channel_close(channel, false);
+               channel_clear(channel);
+           }
+           else
+           {
+               mch_remove(buf);
+               got = true;
+           }
+       }
+
+       res = true;
+       if (got || (!new && mch_access((char *)buf, F_OK) == 0))
+       {
+           if (server_address != NULL && STRCMP(buf, server_address) == 0)
+               // Can't connect to itself
+               break;
+           return buf;
+       }
+       break;
+    }
+
+    if (!quiet)
+    {
+       if (!res)
+           semsg(_("Failed creating socket directory: %s"), strerror(errno));
+       else
+           semsg(_(e_invalid_server_id_used_str), name);
+    }
+    if (!res && fatal != NULL)
+       *fatal = true;
+
+    vim_free(buf);
+    return NULL;
+# endif
+}
+
+/*
+ * Callback for when client channel is closed
+ */
+    static void
+socketserver_client_close(channel_T *channel)
+{
+    if (channel == client_channels)
+       client_channels = channel->ch_ss_next;
+    if (channel->ch_ss_prev != NULL)
+       channel->ch_ss_prev->ch_ss_next = channel->ch_ss_next;
+    if (channel->ch_ss_next != NULL)
+       channel->ch_ss_next->ch_ss_prev = channel->ch_ss_prev;
+
+    ch_log(NULL, "socketserver: client channel closed");
+}
+
+/*
+ * Callback for when server channel accepted new client.
+ */
+    static void
+socketserver_accept(channel_T *channel)
+{
+    channel->ch_socketserver = true;
+    channel->ch_ss_close_cb = socketserver_client_close;
+
+    channel->ch_ss_next = client_channels;
+    channel->ch_ss_prev = NULL;
+    if (client_channels != NULL)
+       client_channels->ch_ss_prev = channel;
+    client_channels = channel;
+
+    // We will read the command from the client later in the input loop.
+    ch_log(NULL, "socketserver: accepted new client");
+}
+
+/*
+ * Callback for when server channel is closed
+ */
+    static void
+socketserver_close(channel_T *channel UNUSED)
+{
+    socketserver_cleanup();
+
+    ch_log(NULL, "socketserver: server channel closed");
+}
+
+/*
+ * Mark references to socketserver channels
+ */
+    int
+set_ref_in_socketserver_channel(int copyID)
+{
+    bool       abort = false;
+    channel_T  *channel;
+    typval_T   tv;
+
+    tv.v_type = VAR_CHANNEL;
+    tv.vval.v_channel = server_channel;
+    abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
+
+    FOR_ALL_CLIENTS(channel)
+    {
+       if (abort)
+           break;
+
+       tv.v_type = VAR_CHANNEL;
+       tv.vval.v_channel = channel;
+       abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
+    }
+    return abort;
+}
+
+/*
+ * Serialize "msg" into a string and send it over the given channel "ch". Also
+ * adds version header to "msg", if "ver" is true. Returns OK on success and
+ * FAIL on failure.
+ */
+    static int
+socketserver_send_message(channel_T *ch, dict_T *msg, char *func, bool ver)
+{
+    typval_T   tv;
+    char_u     *buf;
+    int                ret;
+
+    if (ver)
+       dict_add_number(msg, "version", PROTOCOL_VER);
+
+    tv.v_type = VAR_DICT;
+    tv.vval.v_dict = msg;
+    buf = json_encode(&tv, JSON_NL);
+
+    if (buf != NULL)
+    {
+       emsg_silent++;
+       ret = channel_send(ch, PART_SOCK, buf, (int)STRLEN(buf), func);
+       emsg_silent--;
+    }
+    else
+       return FAIL;
+    vim_free(buf);
+    return ret;
+}
+
+/*
+ * Execute the JSON message represented by "dict".
+ */
+    static void
+socketserver_exec(channel_T *channel, dict_T *message)
+{
+    dictitem_T *di;
+    char_u     *type;
+    char_u     *str = NULL;
+    char_u     *sender = NULL;
+    char_u     idbuf[11 + NUMBUFLEN];
+
+    di = dict_find(message, (char_u *)"type", -1);
+    if (di == NULL || di->di_tv.v_type != VAR_STRING)
+       return;
+    else
+       type = di->di_tv.vval.v_string;
+
+    di = dict_find(message, (char_u *)"str", -1);
+    if (di != NULL && di->di_tv.v_type == VAR_STRING)
+       str = di->di_tv.vval.v_string;
+
+    di = dict_find(message, (char_u *)"sender", -1);
+    if (di != NULL && di->di_tv.v_type == VAR_STRING)
+    {
+       sender = di->di_tv.vval.v_string;
+
+       // Save in global
+       vim_free(client_socket);
+       client_socket = vim_strsave(sender);
+    }
+
+    di = dict_find(message, (char_u *)"wait", -1);
+    if (di != NULL && di->di_tv.v_type == VAR_BOOL && di->di_tv.vval.v_number)
+    {
+       // Client is not a server, but still wants a response later. Save the
+       // ID of the channel connection that we will use to send back a response,
+       vim_snprintf((char *)idbuf, sizeof(idbuf), "remotewait:%d",
+               channel->ch_id);
+
+       sender = idbuf;
+       vim_free(client_socket);
+       client_socket = vim_strsave(sender);
+    }
+
+    ch_log(NULL, "socketserver_exec(): result: %s",
+           str == NULL ? (char_u *)"(null)" : str);
+
+    if (STRCMP(type, "expr") == 0 && str != NULL)
+    {
+       // Evaluate expression and send back reply
+       dict_T      *dict;
+       char_u      *result;
+       int         code;
+
+       dict = dict_alloc();
+       if (dict == NULL)
+           return;
+
+       result = eval_client_expr_to_string(str);
+       code = result == NULL ? -1 : 0;
+
+       dict_add_string(dict, "type", (char_u *)"reply");
+       if (result != NULL)
+       {
+           dict_add_string(dict, "str", result);
+           vim_free(result);
+       }
+       else
+           // Error occured, return error message
+           dict_add_string(dict, "str",
+                   (char_u *)_(e_invalid_expression_received));
+
+       dict_add_number(dict, "code", code);
+
+       socketserver_send_message(channel, dict, "socketserver_exec", true);
+       dict_unref(dict);
+    }
+    else if (STRCMP(type, "keystrokes") == 0 && str != NULL)
+    {
+       // Execute keystrokes
+       server_to_input_buf(str);
+    }
+    else if (STRCMP(type, "notify") == 0)
+    {
+       // Notification, execute autocommands and save the reply for later use
+       if (sender != NULL && str != NULL)
+       {
+           ss_reply_T *reply;
+
+           reply = socketserver_add_reply(sender);
+
+           if (reply != NULL)
+               ga_copy_string(&reply->strings, str);
+
+           apply_autocmds(EVENT_REMOTEREPLY, sender, str, TRUE, curbuf);
+       }
+    }
+    else if (STRCMP(type, "ver") == 0)
+    {
+       // A message we sent previously had the wrong version. Emit an error
+       // message to alert the user.
+       emsg(_(e_socket_server_version_mismatch));
+    }
+    else
+       ch_error(NULL, "socketserver: unknown command type '%s'", type);
+}
+
+    static int
+socketserver_get_message(channel_T *channel, typval_T **tv)
+{
+    jsonq_T *head = &channel->ch_part[PART_SOCK].ch_json_head;
+    jsonq_T *json_msg = head->jq_next;
+    int            ver;
+
+    if (json_msg == NULL)
+    {
+       // Check the readahead buffer
+       channel_parse_json(channel, PART_SOCK, true);
+       json_msg = head->jq_next;
+    }
+    if (json_msg == NULL)
+       return FAIL;
+    *tv = json_msg->jq_value;
+    remove_json_node(head, json_msg);
+
+    if ((*tv)->v_type != VAR_DICT)
+    {
+       ch_error(NULL, "socketserver: message is not a JSON object");
+       free_tv(*tv);
+       return FAIL;
+    }
+
+    ver = dict_get_number((*tv)->vval.v_dict, "version");
+
+    if (ver != 0 && ver != PROTOCOL_VER)
+    {
+       dict_T *dict;
+
+       ch_error(NULL, "socketserver: message has different version %d", ver);
+       free_tv(*tv);
+
+       // Send back a special error
+       dict = dict_alloc();
+       if (dict != NULL)
+       {
+           dict_add_string(dict, "type", (char_u *)"ver");
+           socketserver_send_message(channel, dict,
+                   "socketserver_get_message", false);
+           dict_unref(dict);
+       }
+       return FAIL;
+    }
+
+    return OK;
+}
+
+/*
+ * Parse any commands in the queue and execute them.
+ */
+    void
+socketserver_parse_messages(void)
+{
+    typval_T   *tv;
+
+    for (channel_T *ch = client_channels; ch != NULL;)
+    {
+       // Make sure to save next channel in case "ch" is freed. Not sure if
+       // this can actually happen but be safe.
+       channel_T *next = ch->ch_ss_next;
+
+       // Get the JSON message if there is any from the queue for this channel.
+       if (socketserver_get_message(ch, &tv) == FAIL)
+       {
+           ch = next;
+           continue;
+       }
+
+       socketserver_exec(ch, tv->vval.v_dict);
+       free_tv(tv);
+       ch = next;
+    }
+}
+
+
+/*
+ * Poll until there is something to read on "channel". Also handle other
+ * socketserver channels in the meantime. If "channel" is NULL, then poll all
+ * channels once then exit. If "timeout" is -1, then wait forever unless
+ * interrupted.
+ *
+ * Return OK on success and FAIL on failure or timeout.
+ */
+    static int
+socketserver_wait(channel_T *channel, int timeout)
+{
+    while (true)
+    {
+       int             ret;
+       channel_T       *ch;
+# ifdef HAVE_SELECT
+       fd_set          rfds;
+       struct timeval  tv;
+       int             maxfd = -1;
+
+       if (timeout != -1)
+       {
+           tv.tv_sec = timeout / 1000;
+           tv.tv_usec = (timeout % 1000) * 1000;
+       }
+
+       FD_ZERO(&rfds);
+
+       if (channel != NULL)
+       {
+           if (channel->CH_SOCK_FD == INVALID_FD)
+               // Shouldn't happen
+               return FAIL;
+
+           maxfd = channel->CH_SOCK_FD;
+           FD_SET(channel->CH_SOCK_FD, &rfds);
+       }
+       if (server_channel != NULL && server_channel->CH_SOCK_FD != INVALID_FD)
+       {
+           FD_SET(server_channel->CH_SOCK_FD, &rfds);
+           if (server_channel->CH_SOCK_FD > maxfd)
+               maxfd = server_channel->CH_SOCK_FD;
+       }
+
+       FOR_ALL_CLIENTS(ch)
+       {
+           if (ch->CH_SOCK_FD != INVALID_FD)
+           {
+               FD_SET(ch->CH_SOCK_FD, &rfds);
+               if (maxfd < (int)ch->CH_SOCK_FD)
+                   maxfd = (int)ch->CH_SOCK_FD;
+           }
+       }
+
+       if (maxfd == -1)
+           return FAIL;
+
+       ret = select(maxfd + 1, &rfds, NULL, NULL, timeout == -1 ? NULL : &tv);
+
+#  ifdef EINTR
+       if (ret == -1 && errno == EINTR)
+       {
+           if (got_int)
+               break;
+           continue;
+       }
+#  endif
+
+       if (ret > 0)
+       {
+           if (server_channel != NULL
+                   && server_channel->CH_SOCK_FD != INVALID_FD
+                   && FD_ISSET(server_channel->CH_SOCK_FD, &rfds))
+               channel_check(server_channel, PART_SOCK);
+
+           FOR_ALL_CLIENTS(ch)
+               if (ch->CH_SOCK_FD != INVALID_FD
+                       && FD_ISSET(ch->CH_SOCK_FD, &rfds))
+                   channel_check(ch, PART_SOCK);
+
+           socketserver_parse_messages();
+
+           if (channel == NULL)
+               return OK;
+
+           if (channel->CH_SOCK_FD != INVALID_FD
+                   && FD_ISSET(channel->CH_SOCK_FD, &rfds))
+           {
+               channel_check(channel, PART_SOCK);
+               return OK;
+           }
+           continue;
+       }
+# else
+       struct pollfd   fds[MAX_OPEN_CHANNELS + 1];
+       int             nfd = 0;
+       int             channel_idx = -1;
+       int             server_idx = -1;
+
+       if (channel != NULL)
+       {
+           if (channel->CH_SOCK_FD == INVALID_FD)
+               // Shouldn't happen
+               return FAIL;
+
+           channel_idx = nfd;
+           fds[nfd].fd = channel->CH_SOCK_FD;
+           fds[nfd++].events = POLLIN;
+       }
+       if (server_channel != NULL && server_channel->CH_SOCK_FD != INVALID_FD)
+       {
+           server_idx = nfd;
+           fds[nfd].fd = server_channel->CH_SOCK_FD;
+           fds[nfd++].events = POLLIN;
+       }
+
+       FOR_ALL_CLIENTS(ch)
+           if (ch->CH_SOCK_FD != INVALID_FD)
+           {
+               fds[nfd].fd = ch->CH_SOCK_FD;
+               fds[nfd].events = POLLIN;
+               ch->ch_part[PART_SOCK].ch_poll_idx = nfd;
+               nfd++;
+           }
+
+       ret = poll(fds, nfd, timeout);
+
+#  ifdef EINTR
+       if (ret == -1 && errno == EINTR)
+       {
+           if (got_int)
+               break;
+           continue;
+       }
+#  endif
+
+       if (ret > 0)
+       {
+           if (server_channel != NULL
+                   && server_channel->CH_SOCK_FD != INVALID_FD
+                   && fds[server_idx].revents & POLLIN)
+               channel_check(server_channel, PART_SOCK);
+
+           FOR_ALL_CLIENTS(ch)
+               if (ch->CH_SOCK_FD != INVALID_FD
+                       && fds[ch->ch_part[PART_SOCK].ch_poll_idx]
+                       .revents & POLLIN)
+                   channel_check(ch, PART_SOCK);
+
+           socketserver_parse_messages();
+
+           if (channel == NULL)
+               return OK;
+
+           if (channel->CH_SOCK_FD != INVALID_FD
+                   && fds[channel_idx].revents & POLLIN)
+           {
+               channel_check(channel, PART_SOCK);
+               return OK;
+           }
+           continue;
+       }
+# endif
+       break;
+    }
+    return FAIL;
+}
+
+/*
+ * Parse "name" and create or get the channel connection for it. If "wait" is
+ * not NULL, then if the client name is a channel ID, then it will be set to
+ * true. Returns NULL on
+ * failure.
+ */
+    static channel_T *
+socketserver_get_channel(char_u *name, bool quiet, bool *wait)
+{
+    char_u     *address = NULL;
+    int                port;
+    bool       is_unix = false;
+    channel_T  *channel;
+
+    if (STRNICMP(name, "channel:", 8) == 0)
+    {
+       if (channel_parse_socketserver_address(name + 8, &port, &address, true)
+               == FAIL)
+           return NULL;
+       if (address != NULL)
+           is_unix = true;
+    }
+    else if (STRNICMP(name, "remotewait:", 11) == 0)
+    {
+       // Channel ID name, find channel with that ID.
+       int id;
+
+       if (name[11] == NUL)
+           return NULL;
+       id = strtol((char *)name + 11, NULL, 10);
+       if (wait != NULL)
+           *wait = true;
+
+       return channel_find(id);
+    }
+    else
+    {
+       address = socketserver_get_path(name, false, quiet, NULL);
+       if (address == NULL)
+           return NULL;
+       is_unix = true;
+    }
+
+    if (is_unix)
+       channel = channel_open_unix((char *)address, NULL);
+    else
+       channel =  channel_open("localhost", port, 1000, NULL);
+
+    if (channel == NULL && !quiet)
+       semsg(_(e_socket_server_failed_connecting), name);
+
+    vim_free(address);
+
+    return channel;
+}
+
+/*
+ * Send command to address "name". If "ch" is not NULL, it is set to the channel
+ * for the connection between us and the server, and the channel will not just
+ * be closed immediately, this is used for --remote-wait. Returns 0 for OK, -1
+ * on error.
+ */
+    int
+socketserver_send(
+       char_u *name,
+       char_u *str,
+       char_u **result,
+       bool is_expr,
+       int timeout,
+       bool silent,
+       channel_T **ch)
+{
+    int                rcode = -1;
+    channel_T  *channel;
+    dict_T     *dict;
+    dictitem_T *di;
+    typval_T   *resp_tv = NULL;
+
+    if (*name == NUL)
+    {
+       semsg(_(e_unable_to_send_to_str), name);
+       return -1;
+    }
+
+    // Execute locally if target is ourselves
+    if (serverName != NULL && STRICMP(name, serverName) == 0)
+       return sendToLocalVim(str, is_expr, result);
+
+    channel = socketserver_get_channel(name, silent, NULL);
+    if (channel == NULL)
+       return -1;
+
+    dict = dict_alloc();
+    if (dict == NULL)
+       goto exit;
+
+    dict_add_string(dict, "type", (char_u *)(is_expr ? "expr" : "keystrokes"));
+    dict_add_string(dict, "str", str);
+
+    // Tell server who we are so it can save our socket path internally for
+    // later use with server2client. Only do this if we are actually a server.
+    //
+    // If we are not a server, then --remote-wait will not work. To handle this
+    // case, we add "wait" to the JSON message set to true, so that the server
+    // will create an internal address for our connection to it.  This is
+    // only used for --remote-wait, not exposed to user.
+    if (server_address != NULL)
+       dict_add_string(dict, "sender", server_address);
+    else if (ch != NULL)
+       dict_add_bool(dict, "wait", true);
+
+    if (socketserver_send_message(channel, dict, "socketserver_send", true)
+           == FAIL)
+    {
+       semsg(_(e_unable_to_send_to_str), name);
+       dict_unref(dict);
+       goto exit;
+    }
+    dict_unref(dict);
+
+    if (!is_expr)
+    {
+       // Exit, we aren't waiting for a response
+       rcode = 0;
+
+       if (ch != NULL)
+       {
+           *ch = channel;
+
+           channel->ch_ss_next = client_channels;
+           channel->ch_ss_prev = NULL;
+           channel->ch_ss_close_cb = socketserver_client_close;
+           client_channels = channel;
+
+           return rcode;
+       }
+       goto exit;
+    }
+
+    if (timeout == 0)
+       timeout = 1000;
+
+    // To handle recursive calls, we must handle any socketserver channels as
+    // well.
+    while (socketserver_wait(channel, timeout) == OK)
+       if (socketserver_get_message(channel, &resp_tv) == OK)
+           break;
+
+    if (resp_tv == NULL)
+    {
+       // Channel closed, don't make this an error, because this may be the
+       // result of the expression (e.g. --remote-expr 'execute("qa!")')
+       rcode = 0;
+       goto exit;
+    }
+
+    dict = resp_tv->vval.v_dict;
+
+    di = dict_find(dict, (char_u *)"type", -1);
+    if (di == NULL || di->di_tv.v_type != VAR_STRING ||
+           (STRCMP(di->di_tv.vval.v_string, "reply") != 0
+            && STRCMP(di->di_tv.vval.v_string, "ver") != 0))
+    {
+       ch_error(NULL, "socketserver: unknown reply type");
+       free_tv(resp_tv);
+       goto exit;
+    }
+
+    if (STRCMP(di->di_tv.vval.v_string, "ver") == 0)
+    {
+       emsg(_(e_socket_server_version_mismatch));
+       free_tv(resp_tv);
+       goto exit;
+    }
+
+    if (result != NULL)
+    {
+       di = dict_find(dict, (char_u *)"str", -1);
+       if (di != NULL && di->di_tv.v_type == VAR_STRING)
+           *result = vim_strsave(di->di_tv.vval.v_string);
+       else
+       {
+           free_tv(resp_tv);
+           goto exit;
+       }
+    }
+
+    di = dict_find(dict, (char_u *)"code", -1);
+    if (di != NULL && di->di_tv.v_type == VAR_NUMBER)
+       rcode = di->di_tv.vval.v_number;
+
+    free_tv(resp_tv);
+
+exit:
+    channel_close(channel, false);
+    channel_clear(channel);
+    return rcode;
+}
+
+   static ss_reply_T *
+socketserver_get_reply(char_u *sender, int *index)
+{
+    for (int i = 0; i < server_replies.ga_len; i++)
+    {
+       ss_reply_T *reply = ((ss_reply_T *)server_replies.ga_data) + i;
+
+       if (STRCMP(reply->sender, sender) == 0)
+       {
+           if (index != NULL)
+               *index = i;
+           return reply;
+       }
+    }
+    return NULL;
+}
+
+/*
+ * Add reply to list of replies. Returns a pointer to the ss_reply_T that was
+ * initialized or was found.
+ */
+    static ss_reply_T *
+socketserver_add_reply(char_u *sender)
+{
+    ss_reply_T *reply;
+
+    if (server_replies.ga_growsize == 0)
+       ga_init2(&server_replies, sizeof(ss_reply_T), 1);
+
+    reply = socketserver_get_reply(sender, NULL);
+
+    if (reply == NULL && ga_grow(&server_replies, 1) == OK)
+    {
+       reply = ((ss_reply_T *)server_replies.ga_data) + server_replies.ga_len++;
+
+       reply->sender = vim_strsave(sender);
+
+       if (reply->sender == NULL)
+           return NULL;
+
+       ga_init2(&reply->strings, sizeof(char_u *), 5);
+    }
+
+    return reply;
+}
+
+    static void
+socketserver_remove_reply(char_u *sender)
+{
+    int index;
+    ss_reply_T *reply = socketserver_get_reply(sender, &index);
+
+    if (reply != NULL)
+    {
+       ss_reply_T  *arr = server_replies.ga_data;
+       int         len  = server_replies.ga_len;
+
+       // Free strings
+       vim_free(reply->sender);
+       ga_clear_strings(&reply->strings);
+
+       // Move all elements after the removed reply forward by one
+       if (len > 1)
+           mch_memmove(arr + index, arr + index + 1,
+                   sizeof(ss_reply_T) * (len - index - 1));
+       server_replies.ga_len--;
+    }
+}
+
+/*
+ * Send a string to "client" as a reply (notification). Returns OK on success
+ * and FAIL on failure.
+ */
+    int
+socketserver_send_reply(char_u *client, char_u *str)
+{
+    dict_T     *dict;
+    channel_T  *channel;
+    int                ret = OK;
+    bool       wait = false;
+
+    if (*client == NUL)
+    {
+       semsg(_(e_invalid_server_id_used_str), client);
+       return FAIL;
+    }
+
+    if (server_channel == NULL || server_address == NULL)
+    {
+       emsg(_(e_socket_server_not_online));
+       return FAIL;
+    }
+
+    channel = socketserver_get_channel(client, false, &wait);
+    if (channel == NULL)
+       return FAIL;
+
+    dict = dict_alloc();
+    if (dict == NULL)
+    {
+       ret = FAIL;
+       goto exit;
+    }
+
+    dict_add_string(dict, "type", (char_u *)"notify");
+    dict_add_string(dict, "str", str);
+    if (server_address != NULL)
+       dict_add_string(dict, "sender", server_address);
+
+    ret = socketserver_send_message(channel, dict,
+           "socketserver_send_reply", true);
+    dict_unref(dict);
+
+exit:
+    // Don't want to close the channel if client is referenced by channel ID.
+    // This allows --remote-wait to work with multiple files.
+    if (!wait)
+    {
+       channel_close(channel, false);
+       channel_clear(channel);
+    }
+
+    return ret;
+}
+
+/*
+ * Wait for reply from "client" and place result in "str". Returns OK on success
+ * and FAIL on failure. Timeout is in milliseconds
+ */
+    int
+socketserver_read_reply(
+       char_u *client,
+       char_u **str,
+       int timeout,
+       bool remotewait)
+{
+    ss_reply_T *reply = NULL;
+    char_u     *actual;
+
+    if (*client == NUL)
+    {
+       semsg(_(e_invalid_server_id_used_str), client);
+       return FAIL;
+    }
+
+    if (!remotewait && (server_channel == NULL || server_address == NULL))
+    {
+       emsg(_(e_socket_server_not_online));
+       return FAIL;
+    }
+
+    actual = socketserver_get_path(client, false, false, NULL);
+    if (actual == NULL)
+       return FAIL;
+
+    while (true)
+    {
+       reply = socketserver_get_reply(actual, NULL);
+       if (reply != NULL)
+           break;
+       if (socketserver_wait(NULL, timeout) == FAIL)
+           break;
+    }
+
+    if (reply == NULL || reply->strings.ga_len == 0)
+    {
+       vim_free(actual);
+       return FAIL;
+    }
+
+    // Consume the string
+    *str = ((char_u **)reply->strings.ga_data)[0];
+
+    if (reply->strings.ga_len > 1)
+       mch_memmove((char_u **)reply->strings.ga_data,
+               ((char_u **)reply->strings.ga_data) + 1,
+               sizeof(char_u *) * (reply->strings.ga_len - 1));
+    reply->strings.ga_len--;
+
+    if (reply->strings.ga_len < 1)
+       // Last string removed, remove the reply
+       socketserver_remove_reply(actual);
+
+    vim_free(actual);
+
+    return OK;
+}
+
+/*
+ * Check for any replies for "sender". Returns 1 if there is and places the
+ * reply in "str" without consuming it (note that a copy is not created).
+ * Returns 0 if otherwise and -1 on
+ * error.
+ */
+    int
+socketserver_peek_reply(char_u *sender, char_u **str)
+{
+    ss_reply_T *reply;
+    char_u     *actual;
+
+    if (*sender == NUL)
+    {
+       semsg(_(e_invalid_server_id_used_str), sender);
+       return FAIL;
+    }
+
+    if (server_channel == NULL || server_address == NULL)
+    {
+       emsg(_(e_socket_server_not_online));
+       return FAIL;
+    }
+
+    actual = socketserver_get_path(sender, false, false, NULL);
+    if (actual == NULL)
+       return FAIL;
+
+    reply = socketserver_get_reply(actual, NULL);
+    vim_free(actual);
+
+    if (reply != NULL && reply->strings.ga_len > 0)
+    {
+       if (str != NULL)
+           *str = ((char_u **)reply->strings.ga_data)[0];
+       return 1;
+    }
+    return 0;
+}
+
+#endif // FEAT_SOCKETSERVER
index 6c6ea8c0e48183c431ec28546d76a3c694f3c66a..8cb9b3f91d46c9e66482cf0472c5f1476e7b77ae 100644 (file)
@@ -2795,6 +2795,13 @@ struct channel_S {
     void       (*ch_nb_close_cb)(void);
                                // callback for Netbeans when channel is
                                // closed
+#ifdef FEAT_SOCKETSERVER
+    bool       ch_socketserver; // If channel is used by socketserver
+    void       (*ch_ss_close_cb)(channel_T *);
+    void       (*ch_ss_accept_cb)(channel_T *);
+    channel_T  *ch_ss_next;
+    channel_T  *ch_ss_prev;
+#endif
 
 #ifdef MSWIN
     int                ch_named_pipe;  // using named pipe instead of pty
diff --git a/src/testdir/dumps/Test_clientserver_1.dump b/src/testdir/dumps/Test_clientserver_1.dump
new file mode 100644 (file)
index 0000000..a4a0b42
--- /dev/null
@@ -0,0 +1,8 @@
+|~+0#4040ff13#ffffff0| @73
+|~| @73
+|~| @73
+|E+0#ffffff16#e000002|1|5|6|7|:| |S|o|c|k|e|t| |s|e|r|v|e|r| |p|r|o|t|o|c|o|l| |v|e|r|s|i|o|n| |m|i|s|m|a|t|c|h|,| |c|h|e|c|k| |w|h|a|t| |V|i|m| |v|e|r|s|i|o|n| |y|o|u| 
+|a|r|e| |u|s|i|n|g| +0#0000000#ffffff0@65
+|E+0#ffffff16#e000002|2|4|1|:| |U|n|a|b|l|e| |t|o| |s|e|n|d| |t|o| |c|h|a|n@1|e|l|:|2|0@2| +0#0000000#ffffff0@38
+@75
+|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35
index 0ced9cbc41cde01db17aa2cfd6c33128dc35697a..15918f726795aeec1e1d0c01061391fce1b9d2ba 100644 (file)
@@ -1,5 +1,6 @@
 " Tests for the +clientserver feature.
 
+source util/screendump.vim
 CheckFeature job
 
 if !has('clientserver')
@@ -10,10 +11,6 @@ CheckFeature clientserver
 
 source util/shared.vim
 
-" Unlike X11, we need the socket server running if we want to send commands to
-" a server via sockets.
-RunSocketServer
-
 func Check_X11_Connection()
   if has('x11')
     CheckX11
@@ -192,9 +189,9 @@ func Test_client_server()
 
   " When using socket server, server id is not a number, but the path to the
   " socket.
-  if has('socketserver') && !has('X11')
-    call assert_fails("let x = remote_read('vim/10')", ['E573:.*vim/10'])
-    call assert_fails("call server2client('a/b/c', 'xyz')", ['E573:.*a/b/c'])
+  if (has('socketserver') && !has('X11') && !has('win32')) || index(v:argv, "socket") != -1
+    call assert_fails("let x = remote_read('vim/10')", ['E1564:'])
+    call assert_fails("call server2client('x/b/c', 'xyz')", ['E1564:'])
   else
     call assert_fails("let x = remote_read('vim10')",
           \ has('unix') ? ['E573:.*vim10'] : 'E277:')
@@ -246,11 +243,65 @@ func Test_client_server_stopinsert()
   endtry
 endfunc
 
-" Test if socket server and X11 backends can be chosen and work properly.
-func Test_client_server_x11_and_socket_server()
-  CheckNotMSWindows
+" Test if socket server, X11, and mswin backends can be chosen and work properly.
+func Test_client_server_multiple_backends()
+    CheckFeature socketserver
+
+    let g:test_is_flaky = 1
+    let cmd = GetVimCommand()
+
+    if cmd == ''
+      throw 'GetVimCommand() failed'
+    endif
+    call Check_X11_Connection()
+
+    let types = [
+          \ ['socket', "channel:2000"],
+          \ ['x11', "TEST"],
+          \ ['mswin', "TEST"],
+          \ ]
+
+    for [type, expected] in types
+      if (type == 'x11' && (!has('x11') || !empty($WAYLAND_DISPLAY) || empty($DISPLAY))
+            \ || (type == 'mswin' && !has('win32')))
+        continue
+      endif
+      if has('win32') && has('gui_running')
+        " Windows gVim --remote-expr shows a dialog window, which blocks tests
+        " from running. Using --gui-dialog-file does not seem to work either.
+        continue
+      endif
+
+      let actual_cmd = cmd .. ' --clientserver ' .. type
+      let actual_cmd ..= ' --servername ' .. expected
+      let job = job_start(actual_cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+
+      call WaitForAssert({-> assert_equal("run", job_status(job))})
+      call assert_match(expected, system(actual_cmd .. ' --remote-expr "v:servername"'))
+
+      " On Windows using --remote-expr causes E282, possibly due to some shell
+      " escaping quirk? When gtk gui is running, using system() seems to cause a
+      " deadlock when using the x11 backend only... don't use it for now...
+      if has('win32') || has('gui_running')
+        call job_stop(job, 'kill')
+      else
+        call system(actual_cmd .. " --remote-expr 'execute(\"qa!\")'")
+      endif
+      try
+        call WaitForAssert({-> assert_equal("dead", job_status(job))})
+      finally
+        if job_status(job) != 'dead'
+          call assert_report('Server did not exit')
+          call job_stop(job, 'kill')
+        endif
+      endtry
+    endfor
+  endfunc
+
+" Test if custom paths work for socketserver
+func Test_client_server_socketserver_custom_path()
   CheckFeature socketserver
-  CheckFeature x11
+  CheckNotMSWindows
 
   let g:test_is_flaky = 1
   let cmd = GetVimCommand()
@@ -258,22 +309,20 @@ func Test_client_server_x11_and_socket_server()
   if cmd == ''
     throw 'GetVimCommand() failed'
   endif
-  call Check_X11_Connection()
 
-  let types = ['socket', 'x11']
+  let name = 'VIMTESTSOCKET2'
+
+  let paths = ['./' .. name, '../testdir/' .. name, getcwd(-1) .. '/' .. name]
 
-  for type in types
-    let name = 'VIMTEST_' .. toupper(type)
-    let actual_cmd = cmd .. ' --clientserver ' .. type
-    let actual_cmd .= ' --servername ' .. name
-    let job = job_start(actual_cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+  for path in paths
+    let actual = cmd .. ' --clientserver socket --servername ' .. path
 
-    call WaitForAssert({-> assert_equal("run", job_status(job))})
-    call WaitForAssert({-> assert_match(name, system(cmd .. ' --clientserver ' .. type .. ' --serverlist'))})
+    let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'})
 
-    call assert_match(name, system(actual_cmd .. ' --remote-expr "v:servername"'))
+    call WaitForAssert({-> assert_equal("run", job_status(job))})
+    call WaitForAssert({-> assert_equal(path, glob(path))})
 
-    call system(actual_cmd .. " --remote-expr 'execute(\"qa!\")'")
+    call system(actual .. " --remote-expr 'execute(\"qa!\")'")
     try
       call WaitForAssert({-> assert_equal("dead", job_status(job))})
     finally
@@ -285,11 +334,10 @@ func Test_client_server_x11_and_socket_server()
   endfor
 endfunc
 
-" Test if socket server works in the GUI
-func Test_client_server_socket_server_gui()
-  CheckNotMSWindows
+" Test if "channel:" prefix works correctly to use channel address for
+" socketserver.
+func Test_client_server_socketserver_address()
   CheckFeature socketserver
-  CheckFeature gui_gtk
 
   let g:test_is_flaky = 1
   let cmd = GetVimCommand()
@@ -297,23 +345,24 @@ func Test_client_server_socket_server_gui()
   if cmd == ''
     throw 'GetVimCommand() failed'
   endif
-  call Check_X11_Connection()
 
-  let name = 'VIMTESTSOCKET'
-  let cmd .= ' --clientserver socket'
-  let cmd .= ' --servername ' .. name
+  let actual = cmd .. ' --clientserver socket --servername channel:2000'
 
-  let job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+  let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'})
 
   call WaitForAssert({-> assert_equal("run", job_status(job))})
-  call WaitForAssert({-> assert_match(name, system(cmd .. ' --serverlist'))})
-
-  call system(cmd .. " --remote-expr 'execute(\"gui\")'")
 
-  call assert_match('1', system(cmd .. " --remote-expr 'has(\"gui_running\")'"))
-  call assert_match(name, system(cmd .. ' --remote-expr "v:servername"'))
+  if !has('win32') || !has('gui_running')
+    " Does not work with gVim on Windows because it shows an OK dialog box which
+    " blocks tests from running
+      call assert_match('channel:2000', system(actual .. ' --remote-expr "v:servername"'))
+  endif
 
-  call system(cmd .. " --remote-expr 'execute(\"qa!\")'")
+  if has('win32')
+    call job_stop(job, 'kill')
+  else
+    call system(actual .. " --remote-expr 'execute(\"qa!\")'")
+  endif
   try
     call WaitForAssert({-> assert_equal("dead", job_status(job))})
   finally
@@ -324,41 +373,129 @@ func Test_client_server_socket_server_gui()
   endtry
 endfunc
 
-" Test if custom paths work for socketserver
-func Test_client_socket_server_custom_path()
+" Test if --remote-wait works properly with multiple files
+func Test_client_server_multiple_remote_wait()
+  CheckRunVimInTerminal
+
+  call Check_X11_Connection()
+
+  let buf = RunVimInTerminal('--servername TEST', {'rows': 8})
+  call TermWait(buf)
+
+  call writefile(["1"], 'XRemoteOne', 'D')
+  call writefile(["2"], 'XRemoteTwo', 'D')
+
+  let cmd = GetVimCommand()
+
+  if cmd == ''
+    throw 'GetVimCommand() failed'
+  endif
+
+  let actual = cmd .. ' -n --servername TEST  --remote-wait ./XRemoteOne ./XRemoteTwo'
+  let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'})
+
+  sleep 500m " Wait for server to receive request
+  call assert_equal("run", job_status(job))
+
+  call term_sendkeys(buf, "\<Esc>:next\<CR>")
+  call TermWait(buf)
+  call assert_equal("run", job_status(job))
+
+  call term_sendkeys(buf, "\<Esc>:q!\<CR>") " Don't use qa! because we only want to quit one file
+  call WaitForAssert({-> assert_equal("dead", job_status(job))})
+endfunc
+
+" Check if socket is removed even if Vim changes directory
+func Test_client_server_socketserver_chdir()
+  CheckFeature socketserver
+  CheckRunVimInTerminal
   CheckNotMSWindows
+
+  let buf = RunVimInTerminal('--clientserver socket --servername ./TEST',
+        \ {'rows': 8})
+  call TermWait(buf)
+
+  call term_sendkeys(buf, "\<Esc>:cd ../\<CR>")
+  call StopVimInTerminal(buf)
+
+  call assert_equal("", glob("./TEST"))
+endfunc
+
+func DummyServerCallback(ch, addr)
+  let msg = json_encode(#{type: "ver"}) .. "\n"
+  call ch_sendraw(a:ch, msg)
+endfunc
+
+" Test if commands with different version are ignored and handled properly.
+func Test_client_server_socketserver_version_mismatch()
   CheckFeature socketserver
-  CheckNotFeature x11
+  CheckRunVimInTerminal
+  CheckScreendump
 
-  let g:test_is_flaky = 1
   let cmd = GetVimCommand()
 
   if cmd == ''
     throw 'GetVimCommand() failed'
   endif
 
-  let name = 'VIMTESTSOCKET2'
+  let buf = RunVimInTerminal('--clientserver socket --servername
+        \ channel:2000', {'rows': 8})
+  call TermWait(buf)
 
-  let paths = ['./' .. name, '../testdir/' .. name, getcwd(-1) .. '/' .. name]
+  let ch = ch_open('localhost:2000', #{mode: 'nl'})
+  let msg = json_encode(#{
+        \ type: "expr",
+        \ str: "v:servername",
+        \ version: 999999999
+        \ }) .. "\n"
 
-  for path in paths
-    let actual = cmd .. ' --servername ' .. path
+  call ch_sendraw(ch, msg)
+  let resp = ch_readraw(ch)
 
-    let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'})
+  call assert_equal(#{type: "ver"}, json_decode(resp))
 
-    call WaitForAssert({-> assert_equal("run", job_status(job))})
-    call WaitForAssert({-> assert_equal(path, glob(path))})
+  call StopVimInTerminal(buf)
 
-    call system(actual .. " --remote-expr 'execute(\"qa!\")'")
-    try
-      call WaitForAssert({-> assert_equal("dead", job_status(job))})
-    finally
-      if job_status(job) != 'dead'
-        call assert_report('Server did not exit')
-        call job_stop(job, 'kill')
-      endif
-    endtry
-  endfor
+  let ch = ch_listen("2000", #{
+        \ mode: 'nl',
+        \ callback: function('DummyServerCallback')
+        \ })
+
+  let buf = RunVimInTerminal('--clientserver socket --servername channel:3000', {'rows': 8})
+  call TermWait(buf)
+
+  call term_sendkeys(buf, "\<Esc>:echo remote_expr('channel:2000', 'v:servername', '', 1)\<CR>")
+  call TermWait(buf)
+
+  call VerifyScreenDump(buf, 'Test_clientserver_1', #{wait: 3000})
+
+  call StopVimInTerminal(buf)
+endfunc
+
+" Test if invalid messages do not crash Vim socketserver.
+func Test_clientserver_socketserver_invalid_msg()
+  CheckFeature socketserver
+  CheckRunVimInTerminal
+
+  let cmd = GetVimCommand()
+
+  if cmd == ''
+    throw 'GetVimCommand() failed'
+  endif
+
+  let buf = RunVimInTerminal('--clientserver socket --servername
+        \ channel:3000', {'rows': 8})
+  call TermWait(buf)
+
+  let ch = ch_open('localhost:3000', #{mode: 'nl'})
+
+  call ch_sendraw(ch, "wjdaljdsjalsj\n")
+  call ch_sendraw(ch, "{\"type\": \"unknown\"}\n")
+
+  call assert_match("channel:3000", system(cmd .. " --clientserver socket --servername channel:3000 --remote-expr 'v:servername'"))
+  call assert_equal("running", term_getstatus(buf))
+
+  call StopVimInTerminal(buf)
 endfunc
 
 " Uncomment this line to get a debugging log
index ad959e629cbe552f8a7a6aa69ef60e692366c117..9f23bb0ce1775895102d30417f13705fae93f424 100644 (file)
@@ -16,18 +16,6 @@ func Verify_remote_feature_works()
   let buf = RunVimInTerminal('--servername XVIMTEST', {'rows': 8})
   call TermWait(buf)
 
-  " For some reason when the socket server is being used, the terminal Vim never
-  " receives the `:w! XVimRemoteTest.txt` command from term_sendkeys.
-  if has('socketserver') && !has('X11')
-    if match(serverlist(), "XVIMTEST") == -1
-      call StopVimInTerminal(buf)
-      throw s:skip
-    endif
-
-    let s:remote = 1
-    return
-  endif
-
   let cmd = GetVimCommandCleanTerm() .. '--serverlist'
   call term_sendkeys(buf, ":r! " .. cmd .. "\<CR>")
   call TermWait(buf)
@@ -71,6 +59,14 @@ func Test_remote_servername()
   " open XTEST.txt, if wildignore setting is not ignored, the server
   " will continue with the Xdummy.log file
   let buf2 = RunVimInTerminal('--servername XVIMTEST --remote-silent XTEST.txt', {'rows': 5, 'wait_for_ruler': 0})
+
+  " It may take a while for the command to be sent to XVIMTEST and be executed.
+  " Do a blocking --remote-expr so that is assured the command was executed.
+  botright new
+  let buf3 = RunVimInTerminal('--servername XVIMTEST --remote-expr "v:servername"', {'rows': 5, 'wait_for_ruler': 0})
+  call WaitForAssert({-> assert_equal("finished", term_getstatus(buf3))})
+  exe buf3 .. 'bw!'
+
   " job should be no-longer running, so we can just close it
   exe buf2 .. 'bw!'
   call term_sendkeys(buf, ":sil :3,$d\<CR>")
index 6ad39787e3c1ac967a5de57fdeeceb0813aa319c..8b90a826e48bf2c4db7f36ba22971e64d06ae9ae 100644 (file)
@@ -402,7 +402,6 @@ func Test_CmdCompletion()
     exe 'delcommand ' .. cmd
   endfor
   delcommand MissingFeature
-  delcommand RunSocketServer
 
   command! DoCmd1 :
   command! DoCmd2 :
index b74f324aef36568a922617075cbc7036237baecd..3f95ee8b251c293fbdbcf929f24f4404cdacc719 100644 (file)
@@ -1,7 +1,6 @@
 " Test using builtin functions in the Vim9 script language.
 
 source util/screendump.vim
-source util/socketserver.vim
 import './util/vim9.vim' as v9
 
 " Test for passing too many or too few arguments to builtin functions
@@ -3525,11 +3524,12 @@ enddef
 
 def Test_remote_expr()
   CheckFeature clientserver
-  TrySocketServer
+  CheckNotMSWindows
 
-  if !g:socketserver_only
+  if has("x11")
     CheckEnv DISPLAY
   endif
+
   v9.CheckSourceDefAndScriptFailure(['remote_expr(1, "b")'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1'])
   v9.CheckSourceDefAndScriptFailure(['remote_expr("a", 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2'])
   v9.CheckSourceDefAndScriptFailure(['remote_expr("a", "b", 3)'], ['E1013: Argument 3: type mismatch, expected string but got number', 'E1174: String required for argument 3'])
@@ -3551,10 +3551,12 @@ enddef
 
 def Test_remote_peek()
   CheckFeature clientserver
-  TrySocketServer
-  if !g:socketserver_only
+  CheckNotMSWindows
+
+  if has("x11")
     CheckEnv DISPLAY
   endif
+
   v9.CheckSourceDefAndScriptFailure(['remote_peek(0z10)'], ['E1013: Argument 1: type mismatch, expected string but got blob', 'E1174: String required for argument 1'])
   v9.CheckSourceDefAndScriptFailure(['remote_peek("a5b6c7", [1])'], ['E1013: Argument 2: type mismatch, expected string but got list<number>', 'E1174: String required for argument 2'])
   v9.CheckSourceDefExecAndScriptFailure(['remote_peek("")'], 'E573: Invalid server id used')
@@ -3562,7 +3564,12 @@ enddef
 
 def Test_remote_read()
   CheckFeature clientserver
-  CheckEnv DISPLAY
+  CheckNotMSWindows
+
+  if has("x11")
+    CheckEnv DISPLAY
+  endif
+
   v9.CheckSourceDefAndScriptFailure(['remote_read(1)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1'])
   v9.CheckSourceDefAndScriptFailure(['remote_read("a", "x")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
   v9.CheckSourceDefExecAndScriptFailure(['remote_read("")'], 'E573: Invalid server id used')
@@ -3570,10 +3577,12 @@ enddef
 
 def Test_remote_send()
   CheckFeature clientserver
-  TrySocketServer
-  if !g:socketserver_only
+  CheckNotMSWindows
+
+  if has("x11")
     CheckEnv DISPLAY
   endif
+
   v9.CheckSourceDefAndScriptFailure(['remote_send(1, "b")'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1'])
   v9.CheckSourceDefAndScriptFailure(['remote_send("a", 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2'])
   v9.CheckSourceDefAndScriptFailure(['remote_send("a", "b", 3)'], ['E1013: Argument 3: type mismatch, expected string but got number', 'E1174: String required for argument 3'])
@@ -3582,10 +3591,12 @@ enddef
 
 def Test_remote_startserver()
   CheckFeature clientserver
-  TrySocketServer
-  if !g:socketserver_only
+  CheckNotMSWindows
+
+  if has("x11")
     CheckEnv DISPLAY
   endif
+
   v9.CheckSourceDefAndScriptFailure(['remote_startserver({})'], ['E1013: Argument 1: type mismatch, expected string but got dict<any>', 'E1174: String required for argument 1'])
 enddef
 
index 8df985091cb10bee228a2ae7a5b65289dcaccdd3..c69d39944176536876372ba7f7f28d30d3b117e2 100644 (file)
@@ -348,17 +348,6 @@ func CheckGithubActions()
   endif
 endfunc
 
-command RunSocketServer call RunSocketServer()
-func RunSocketServer()
-  if has("socketserver") && v:servername == ""
-    try
-      call remote_startserver('VIMSOCKETSERVERTEST')
-    catch " not possible to start a remote server
-      throw 'Skipped: Cannot start remote server'
-    endtry
-  endif
-endfunc
-
 let &cpo = s:cpo_save
 unlet s:cpo_save
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/util/socketserver.vim b/src/testdir/util/socketserver.vim
deleted file mode 100644 (file)
index 5492db6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-" Check if only the socketserver backend is available for clientserver (only on
-" Unix), and set g:socketserver_only to v:true along with starting the
-" socketserver.
-command TrySocketServer call TrySocketServer()
-func TrySocketServer()
-  if has("socketserver") && !has("x11")
-    let g:socketserver_only = v:true
-
-    if v:servername == ""
-      call remote_startserver('VIMSOCKETSERVERTEST')
-    endif
-  else
-      let g:socketserver_only = v:false
-    endif
-endfunc
-
-" vim: shiftwidth=2 sts=2 expandtab
index aa985e4ee86d99601afe5128f4c398392bb01378..f277ba4075ad9ca7d8ea210bd597e151c1fe3e66 100644 (file)
--- a/src/ui.c
+++ b/src/ui.c
@@ -406,20 +406,10 @@ inchar_loop(
 # endif
 
        if ((resize_func != NULL && resize_func(TRUE))
-# if defined(FEAT_CLIENTSERVER) && defined(UNIX)
-               || (
-#  ifdef FEAT_X11
-                   (clientserver_method == CLIENTSERVER_METHOD_X11 &&
-                   server_waiting())
-#  endif
-#  if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
-                   ||
-#  endif
-#  ifdef FEAT_SOCKETSERVER
-                   (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
-                    socket_server_waiting_accept())
-#  endif
-               )
+# if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
+               ||
+               (clientserver_method == CLIENTSERVER_METHOD_X11 &&
+                server_waiting())
 # endif
 # ifdef MESSAGE_QUEUE
                || interrupted
index 7be1f150baab787cbe9199f7f058445cb4f33884..ce8c620a928670e9bea4a2292612c18267010198 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    512,
 /**/
     511,
 /**/