diff options
author | Bram Moolenaar <Bram@vim.org> | 2020-04-05 20:21:03 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2020-04-05 20:21:03 +0200 |
commit | f87a0400fd81862c33d6ad2291a56e178db7dddd (patch) | |
tree | 2a1ef72193db1883d8aa81c158d99148b5412fb6 | |
parent | b8ed3aa9e708ec0af4e9ee8921ad198f0e949c0d (diff) | |
download | vim-git-f87a0400fd81862c33d6ad2291a56e178db7dddd.tar.gz |
patch 8.2.0516: client-server code is spread outv8.2.0516
Problem: Client-server code is spread out.
Solution: Move client-server code to a new file. (Yegappan Lakshmanan,
closes #5885)
-rw-r--r-- | Filelist | 2 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 1 | ||||
-rw-r--r-- | src/Make_morph.mak | 1 | ||||
-rw-r--r-- | src/Make_mvc.mak | 4 | ||||
-rw-r--r-- | src/Make_vms.mms | 6 | ||||
-rw-r--r-- | src/Makefile | 10 | ||||
-rw-r--r-- | src/README.md | 1 | ||||
-rw-r--r-- | src/clientserver.c | 1003 | ||||
-rw-r--r-- | src/evalfunc.c | 324 | ||||
-rw-r--r-- | src/main.c | 673 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/proto/clientserver.pro | 16 | ||||
-rw-r--r-- | src/proto/main.pro | 4 | ||||
-rw-r--r-- | src/version.c | 2 |
14 files changed, 1047 insertions, 1001 deletions
@@ -30,6 +30,7 @@ SRC_ALL = \ src/channel.c \ src/charset.c \ src/cindent.c \ + src/clientserver.c \ src/clipboard.c \ src/cmdexpand.c \ src/cmdhist.c \ @@ -203,6 +204,7 @@ SRC_ALL = \ src/proto/channel.pro \ src/proto/charset.pro \ src/proto/cindent.pro \ + src/proto/clientserver.pro \ src/proto/clipboard.pro \ src/proto/cmdexpand.pro \ src/proto/cmdhist.pro \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index c51225b35..86986482a 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -710,6 +710,7 @@ OBJ = \ $(OUTDIR)/change.o \ $(OUTDIR)/charset.o \ $(OUTDIR)/cindent.o \ + $(OUTDIR)/clientserver.o \ $(OUTDIR)/clipboard.o \ $(OUTDIR)/cmdexpand.o \ $(OUTDIR)/cmdhist.o \ diff --git a/src/Make_morph.mak b/src/Make_morph.mak index 72731825e..1830d85b5 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -33,6 +33,7 @@ SRC = arabic.c \ change.c \ charset.c \ cindent.c \ + clientserver.c \ clipboard.c \ cmdexpand.c \ cmdhist.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 86c319fd5..1d7a3ed11 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -730,6 +730,7 @@ OBJ = \ $(OUTDIR)\change.obj \ $(OUTDIR)\charset.obj \ $(OUTDIR)\cindent.obj \ + $(OUTDIR)\clientserver.obj \ $(OUTDIR)\clipboard.obj \ $(OUTDIR)\cmdexpand.obj \ $(OUTDIR)\cmdhist.obj \ @@ -1516,6 +1517,8 @@ $(OUTDIR)/charset.obj: $(OUTDIR) charset.c $(INCL) $(OUTDIR)/cindent.obj: $(OUTDIR) cindent.c $(INCL) +$(OUTDIR)/clientserver.obj: $(OUTDIR) clientserver.c $(INCL) + $(OUTDIR)/clipboard.obj: $(OUTDIR) clipboard.c $(INCL) $(OUTDIR)/cmdexpand.obj: $(OUTDIR) cmdexpand.c $(INCL) @@ -1865,6 +1868,7 @@ proto.h: \ proto/change.pro \ proto/charset.pro \ proto/cindent.pro \ + proto/clientserver.pro \ proto/clipboard.pro \ proto/cmdexpand.pro \ proto/cmdhist.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index ea6a8d76f..fdd7ba7c1 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -306,6 +306,7 @@ SRC = \ change.c \ charset.c \ cindent.c \ + clientserver.c \ clipboard.c \ cmdexpand.c \ cmdhist.c \ @@ -413,6 +414,7 @@ OBJ = \ change.obj \ charset.obj \ cindent.obj \ + clientserver.obj \ clipboard.obj \ cmdexpand.obj \ cmdhist.obj \ @@ -702,6 +704,10 @@ cindent.obj : cindent.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ globals.h +clientserver.obj : clientserver.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h clipboard.obj : clipboard.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ diff --git a/src/Makefile b/src/Makefile index 221b4f0de..704721b5f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1602,6 +1602,7 @@ BASIC_SRC = \ change.c \ charset.c \ cindent.c \ + clientserver.c \ clipboard.c \ cmdexpand.c \ cmdhist.c \ @@ -1747,6 +1748,7 @@ OBJ_COMMON = \ objects/blob.o \ objects/blowfish.o \ objects/cindent.o \ + objects/clientserver.o \ objects/clipboard.o \ objects/cmdexpand.o \ objects/cmdhist.o \ @@ -1908,6 +1910,7 @@ PRO_AUTO = \ channel.pro \ charset.pro \ cindent.pro \ + clientserver.pro \ clipboard.pro \ cmdexpand.pro \ cmdhist.pro \ @@ -3123,6 +3126,9 @@ objects/charset.o: charset.c objects/cindent.o: cindent.c $(CCC) -o $@ cindent.c +objects/clientserver.o: clientserver.c + $(CCC) -o $@ clientserver.c + objects/clipboard.o: clipboard.c $(CCC) -o $@ clipboard.c @@ -3743,6 +3749,10 @@ objects/cindent.o: cindent.c vim.h protodef.h auto/config.h feature.h os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h +objects/clientserver.o: clientserver.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/clipboard.o: clipboard.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.md b/src/README.md index f28788c84..54be08284 100644 --- a/src/README.md +++ b/src/README.md @@ -30,6 +30,7 @@ buffer.c | manipulating buffers (loaded files) bufwrite.c | writing a buffer to file change.c | handling changes to text cindent.c | C and Lisp indentation +clientserver.c | client server functionality clipboard.c | handling the clipboard cmdexpand.c | command-line completion cmdhist.c | command-line history diff --git a/src/clientserver.c b/src/clientserver.c new file mode 100644 index 000000000..524542580 --- /dev/null +++ b/src/clientserver.c @@ -0,0 +1,1003 @@ +/* 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. + * See README.txt for an overview of the Vim source code. + */ + +/* + * clientserver.c: functions for Client Server functionality + */ + +#include "vim.h" + +#if defined(FEAT_CLIENTSERVER) || defined(PROTO) + +static void cmdsrv_main(int *argc, char **argv, char_u *serverName_arg, char_u **serverStr); +static char_u *serverMakeName(char_u *arg, char *cmd); + +/* + * Replace termcodes such as <CR> and insert as key presses if there is room. + */ + void +server_to_input_buf(char_u *str) +{ + char_u *ptr = NULL; + char_u *cpo_save = p_cpo; + + // Set 'cpoptions' the way we want it. + // B set - backslashes are *not* treated specially + // k set - keycodes are *not* reverse-engineered + // < unset - <Key> sequences *are* interpreted + // The last but one parameter of replace_termcodes() is TRUE so that the + // <lt> sequence is recognised - needed for a real backslash. + p_cpo = (char_u *)"Bk"; + str = replace_termcodes((char_u *)str, &ptr, REPTERM_DO_LT, NULL); + p_cpo = cpo_save; + + if (*ptr != NUL) // trailing CTRL-V results in nothing + { + /* + * Add the string to the input stream. + * Can't use add_to_input_buf() here, we now have K_SPECIAL bytes. + * + * First clear typed characters from the typeahead buffer, there could + * be half a mapping there. Then append to the existing string, so + * that multiple commands from a client are concatenated. + */ + if (typebuf.tb_maplen < typebuf.tb_len) + del_typebuf(typebuf.tb_len - typebuf.tb_maplen, typebuf.tb_maplen); + (void)ins_typebuf(str, REMAP_NONE, typebuf.tb_len, TRUE, FALSE); + + // Let input_available() know we inserted text in the typeahead + // buffer. + typebuf_was_filled = TRUE; + } + vim_free((char_u *)ptr); +} + +/* + * Evaluate an expression that the client sent to a string. + */ + char_u * +eval_client_expr_to_string(char_u *expr) +{ + char_u *res; + int save_dbl = debug_break_level; + int save_ro = redir_off; + funccal_entry_T funccal_entry; + int did_save_funccal = FALSE; + + // Evaluate the expression at the toplevel, don't use variables local to + // the calling function. Except when in debug mode. + if (!debug_mode) + { + save_funccal(&funccal_entry); + did_save_funccal = TRUE; + } + + // Disable debugging, otherwise Vim hangs, waiting for "cont" to be + // typed. + debug_break_level = -1; + redir_off = 0; + // Do not display error message, otherwise Vim hangs, waiting for "cont" + // to be typed. Do generate errors so that try/catch works. + ++emsg_silent; + + res = eval_to_string(expr, NULL, TRUE); + + debug_break_level = save_dbl; + redir_off = save_ro; + --emsg_silent; + if (emsg_silent < 0) + emsg_silent = 0; + if (did_save_funccal) + restore_funccal(); + + // A client can tell us to redraw, but not to display the cursor, so do + // that here. + setcursor(); + out_flush_cursor(FALSE, FALSE); + + return res; +} + +/* + * Evaluate a command or expression sent to ourselves. + */ + int +sendToLocalVim(char_u *cmd, int asExpr, char_u **result) +{ + if (asExpr) + { + char_u *ret; + + ret = eval_client_expr_to_string(cmd); + if (result != NULL) + { + if (ret == NULL) + { + char *err = _(e_invexprmsg); + size_t len = STRLEN(cmd) + STRLEN(err) + 5; + char_u *msg; + + msg = alloc(len); + if (msg != NULL) + vim_snprintf((char *)msg, len, "%s: \"%s\"", err, cmd); + *result = msg; + } + else + *result = ret; + } + else + vim_free(ret); + return ret == NULL ? -1 : 0; + } + server_to_input_buf(cmd); + return 0; +} + +/* + * If conversion is needed, convert "data" from "client_enc" to 'encoding' and + * return an allocated string. Otherwise return "data". + * "*tofree" is set to the result when it needs to be freed later. + */ + char_u * +serverConvert( + char_u *client_enc UNUSED, + char_u *data, + char_u **tofree) +{ + char_u *res = data; + + *tofree = NULL; + if (client_enc != NULL && p_enc != NULL) + { + vimconv_T vimconv; + + vimconv.vc_type = CONV_NONE; + if (convert_setup(&vimconv, client_enc, p_enc) != FAIL + && vimconv.vc_type != CONV_NONE) + { + res = string_convert(&vimconv, data, NULL); + if (res == NULL) + res = data; + else + *tofree = res; + } + convert_setup(&vimconv, NULL, NULL); + } + return res; +} +#endif + +#if (defined(FEAT_CLIENTSERVER) && !defined(NO_VIM_MAIN)) || defined(PROTO) + +/* + * Common code for the X command server and the Win32 command server. + */ + +static char_u *build_drop_cmd(int filec, char **filev, int tabs, int sendReply); + +/* + * Do the client-server stuff, unless "--servername ''" was used. + */ + void +exec_on_server(mparm_T *parmp) +{ + if (parmp->serverName_arg == NULL || *parmp->serverName_arg != NUL) + { +# ifdef MSWIN + // Initialise the client/server messaging infrastructure. + serverInitMessaging(); +# endif + + /* + * When a command server argument was found, execute it. This may + * exit Vim when it was successful. Otherwise it's executed further + * on. Remember the encoding used here in "serverStrEnc". + */ + if (parmp->serverArg) + { + cmdsrv_main(&parmp->argc, parmp->argv, + parmp->serverName_arg, &parmp->serverStr); + parmp->serverStrEnc = vim_strsave(p_enc); + } + + // If we're still running, get the name to register ourselves. + // On Win32 can register right now, for X11 need to setup the + // clipboard first, it's further down. + parmp->servername = serverMakeName(parmp->serverName_arg, + parmp->argv[0]); +# ifdef MSWIN + if (parmp->servername != NULL) + { + serverSetName(parmp->servername); + vim_free(parmp->servername); + } +# endif + } +} + +/* + * Prepare for running as a Vim server. + */ + void +prepare_server(mparm_T *parmp) +{ +# if defined(FEAT_X11) + /* + * Register for remote command execution with :serversend and --remote + * unless there was a -X or a --servername '' on the command line. + * Only register nongui-vim's with an explicit --servername argument, + * or when compiling with autoservername. + * When running as root --servername is also required. + */ + if (X_DISPLAY != NULL && parmp->servername != NULL && ( +# if defined(FEAT_AUTOSERVERNAME) || defined(FEAT_GUI) + ( +# if defined(FEAT_AUTOSERVERNAME) + 1 +# else + gui.in_use +# endif +# ifdef UNIX + && getuid() != ROOT_UID +# endif + ) || +# endif + parmp->serverName_arg != NULL)) + { + (void)serverRegisterName(X_DISPLAY, parmp->servername); + vim_free(parmp->servername); + TIME_MSG("register server name"); + } + else + serverDelayedStartName = parmp->servername; +# endif + + /* + * Execute command ourselves if we're here because the send failed (or + * else we would have exited above). + */ + if (parmp->serverStr != NULL) + { + char_u *p; + + server_to_input_buf(serverConvert(parmp->serverStrEnc, + parmp->serverStr, &p)); + vim_free(p); + } +} + + static void +cmdsrv_main( + int *argc, + char **argv, + char_u *serverName_arg, + char_u **serverStr) +{ + char_u *res; + int i; + char_u *sname; + int ret; + int didone = FALSE; + int exiterr = 0; + char **newArgV = argv + 1; + int newArgC = 1, + Argc = *argc; + int argtype; +#define ARGTYPE_OTHER 0 +#define ARGTYPE_EDIT 1 +#define ARGTYPE_EDIT_WAIT 2 +#define ARGTYPE_SEND 3 + int silent = FALSE; + int tabs = FALSE; +# ifndef FEAT_X11 + HWND srv; +# else + Window srv; + + setup_term_clip(); +# endif + + sname = serverMakeName(serverName_arg, argv[0]); + if (sname == NULL) + return; + + /* + * Execute the command server related arguments and remove them + * from the argc/argv array; We may have to return into main() + */ + for (i = 1; i < Argc; i++) + { + res = NULL; + if (STRCMP(argv[i], "--") == 0) // end of option arguments + { + for (; i < *argc; i++) + { + *newArgV++ = argv[i]; + newArgC++; + } + break; + } + + if (STRICMP(argv[i], "--remote-send") == 0) + argtype = ARGTYPE_SEND; + else if (STRNICMP(argv[i], "--remote", 8) == 0) + { + char *p = argv[i] + 8; + + argtype = ARGTYPE_EDIT; + while (*p != NUL) + { + if (STRNICMP(p, "-wait", 5) == 0) + { + argtype = ARGTYPE_EDIT_WAIT; + p += 5; + } + else if (STRNICMP(p, "-silent", 7) == 0) + { + silent = TRUE; + p += 7; + } + else if (STRNICMP(p, "-tab", 4) == 0) + { + tabs = TRUE; + p += 4; + } + else + { + argtype = ARGTYPE_OTHER; + break; + } + } + } + else + argtype = ARGTYPE_OTHER; + + if (argtype != ARGTYPE_OTHER) + { + if (i == *argc - 1) + mainerr_arg_missing((char_u *)argv[i]); + if (argtype == ARGTYPE_SEND) + { + *serverStr = (char_u *)argv[i + 1]; + i++; + } + else + { + *serverStr = build_drop_cmd(*argc - i - 1, argv + i + 1, + tabs, argtype == ARGTYPE_EDIT_WAIT); + if (*serverStr == NULL) + { + // Probably out of memory, exit. + didone = TRUE; + exiterr = 1; + break; + } + Argc = i; + } +# ifdef FEAT_X11 + if (xterm_dpy == NULL) + { + mch_errmsg(_("No display")); + ret = -1; + } + else + ret = serverSendToVim(xterm_dpy, sname, *serverStr, + NULL, &srv, 0, 0, 0, silent); +# else + // Win32 always works? + ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent); +# endif + if (ret < 0) + { + if (argtype == ARGTYPE_SEND) + { + // Failed to send, abort. + mch_errmsg(_(": Send failed.\n")); + didone = TRUE; + exiterr = 1; + } + else if (!silent) + // Let vim start normally. + mch_errmsg(_(": Send failed. Trying to execute locally\n")); + break; + } + +# ifdef FEAT_GUI_MSWIN + // Guess that when the server name starts with "g" it's a GUI + // server, which we can bring to the foreground here. + // Foreground() in the server doesn't work very well. + if (argtype != ARGTYPE_SEND && TOUPPER_ASC(*sname) == 'G') + SetForegroundWindow(srv); +# endif + + /* + * For --remote-wait: Wait until the server did edit each + * file. Also detect that the server no longer runs. + */ + if (ret >= 0 && argtype == ARGTYPE_EDIT_WAIT) + { + int numFiles = *argc - i - 1; + int j; + char_u *done = alloc(numFiles); + char_u *p; +# ifdef FEAT_GUI_MSWIN + NOTIFYICONDATA ni; + int count = 0; + extern HWND message_window; +# endif + + if (numFiles > 0 && argv[i + 1][0] == '+') + // Skip "+cmd" argument, don't wait for it to be edited. + --numFiles; + +# ifdef FEAT_GUI_MSWIN + ni.cbSize = sizeof(ni); + ni.hWnd = message_window; + ni.uID = 0; + ni.uFlags = NIF_ICON|NIF_TIP; + ni.hIcon = LoadIcon((HINSTANCE)GetModuleHandle(0), "IDR_VIM"); + sprintf(ni.szTip, _("%d of %d edited"), count, numFiles); + Shell_NotifyIcon(NIM_ADD, &ni); +# endif + + // Wait for all files to unload in remote + vim_memset(done, 0, numFiles); + while (memchr(done, 0, numFiles) != NULL) + { +# ifdef MSWIN + p = serverGetReply(srv, NULL, TRUE, TRUE, 0); + if (p == NULL) + break; +# else + if (serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0) + break; +# endif + j = atoi((char *)p); + if (j >= 0 && j < numFiles) + { +# ifdef FEAT_GUI_MSWIN + ++count; + sprintf(ni.szTip, _("%d of %d edited"), + count, numFiles); + Shell_NotifyIcon(NIM_MODIFY, &ni); +# endif + done[j] = 1; + } + } +# ifdef FEAT_GUI_MSWIN + Shell_NotifyIcon(NIM_DELETE, &ni); +# endif + } + } + else if (STRICMP(argv[i], "--remote-expr") == 0) + { + if (i == *argc - 1) + mainerr_arg_missing((char_u *)argv[i]); +# ifdef MSWIN + // Win32 always works? + if (serverSendToVim(sname, (char_u *)argv[i + 1], + &res, NULL, 1, 0, FALSE) < 0) +# else + if (xterm_dpy == NULL) + mch_errmsg(_("No display: Send expression failed.\n")); + else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1], + &res, NULL, 1, 0, 1, FALSE) < 0) +# endif + { + if (res != NULL && *res != NUL) + { + // Output error from remote + mch_errmsg((char *)res); + VIM_CLEAR(res); + } + mch_errmsg(_(": Send expression failed.\n")); + } + } + else if (STRICMP(argv[i], "--serverlist") == 0) + { +# ifdef MSWIN + // Win32 always works? + res = serverGetVimNames(); +# else + if (xterm_dpy != NULL) + res = serverGetVimNames(xterm_dpy); +# endif + if (did_emsg) + mch_errmsg("\n"); + } + else if (STRICMP(argv[i], "--servername") == 0) + { + // Already processed. Take it out of the command line + i++; + continue; + } + else + { + *newArgV++ = argv[i]; + newArgC++; + continue; + } + didone = TRUE; + if (res != NULL && *res != NUL) + { + mch_msg((char *)res); + if (res[STRLEN(res) - 1] != '\n') + mch_msg("\n"); + } + vim_free(res); + } + + if (didone) + { + display_errors(); // display any collected messages + exit(exiterr); // Mission accomplished - get out + } + + // Return back into main() + *argc = newArgC; + vim_free(sname); +} + +/* + * Build a ":drop" command to send to a Vim server. + */ + static char_u * +build_drop_cmd( + int filec, + char **filev, + int tabs, // Use ":tab drop" instead of ":drop". + int sendReply) +{ + garray_T ga; + int i; + char_u *inicmd = NULL; + char_u *p; + char_u *cdp; + char_u *cwd; + + if (filec > 0 && filev[0][0] == '+') + { + inicmd = (char_u *)filev[0] + 1; + filev++; + filec--; + } + // Check if we have at least one argument. + if (filec <= 0) + mainerr_arg_missing((char_u *)filev[-1]); + + // Temporarily cd to the current directory to handle relative file names. + cwd = alloc(MAXPATHL); + if (cwd == NULL) + return NULL; + if (mch_dirname(cwd, MAXPATHL) != OK) + { + vim_free(cwd); + return NULL; + } + cdp = vim_strsave_escaped_ext(cwd, +#ifdef BACKSLASH_IN_FILENAME + (char_u *)"", // rem_backslash() will tell what chars to escape +#else + PATH_ESC_CHARS, +#endif + '\\', TRUE); + vim_free(cwd); + if (cdp == NULL) + return NULL; + ga_init2(&ga, 1, 100); + ga_concat(&ga, (char_u *)"<C-\\><C-N>:cd "); + ga_concat(&ga, cdp); + + // Call inputsave() so that a prompt for an encryption key works. + ga_concat(&ga, (char_u *)"<CR>:if exists('*inputsave')|call inputsave()|endif|"); + if (tabs) + ga_concat(&ga, (char_u *)"tab "); + ga_concat(&ga, (char_u *)"drop"); + for (i = 0; i < filec; i++) + { + // On Unix the shell has already expanded the wildcards, don't want to + // do it again in the Vim server. On MS-Windows only escape + // non-wildcard characters. + p = vim_strsave_escaped((char_u *)filev[i], +#ifdef UNIX + PATH_ESC_CHARS +#else + (char_u *)" \t%#" +#endif + ); + if (p == NULL) + { + vim_free(ga.ga_data); + return NULL; + } + ga_concat(&ga, (char_u *)" "); + ga_concat(&ga, p); + vim_free(p); + } + ga_concat(&ga, (char_u *)"|if exists('*inputrestore')|call inputrestore()|endif<CR>"); + + // The :drop commands goes to Insert mode when 'insertmode' is set, use + // CTRL-\ CTRL-N again. + ga_concat(&ga, (char_u *)"<C-\\><C-N>"); + + // Switch back to the correct current directory (prior to temporary path + // switch) unless 'autochdir' is set, in which case it will already be + // correct after the :drop command. With line breaks and spaces: + // if !exists('+acd') || !&acd + // if haslocaldir() + // cd - + // lcd - + // elseif getcwd() ==# 'current path' + // cd - + // endif + // endif + ga_concat(&ga, (char_u *)":if !exists('+acd')||!&acd|if haslocaldir()|"); + ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd() ==# '"); + ga_concat(&ga, cdp); + ga_concat(&ga, (char_u *)"'|cd -|endif|endif<CR>"); + vim_free(cdp); + + if (sendReply) + ga_concat(&ga, (char_u *)":call SetupRemoteReplies()<CR>"); + ga_concat(&ga, (char_u *)":"); + if (inicmd != NULL) + { + // Can't use <CR> after "inicmd", because an "startinsert" would cause + // the following commands to be inserted as text. Use a "|", + // hopefully "inicmd" does allow this... + ga_concat(&ga, inicmd); + ga_concat(&ga, (char_u *)"|"); + } + // Bring the window to the foreground, goto Insert mode when 'im' set and + // clear command line. + ga_concat(&ga, (char_u *)"cal foreground()|if &im|star|en|redr|f<CR>"); + ga_append(&ga, NUL); + return ga.ga_data; +} + +/* + * Make our basic server name: use the specified "arg" if given, otherwise use + * the tail of the command "cmd" we were started with. + * Return the name in allocated memory. This doesn't include a serial number. + */ + static char_u * +serverMakeName(char_u *arg, char *cmd) +{ + char_u *p; + + if (arg != NULL && *arg != NUL) + p = vim_strsave_up(arg); + else + { + p = vim_strsave_up(gettail((char_u *)cmd)); + // Remove .exe or .bat from the name. + if (p != NULL && vim_strchr(p, '.') != NULL) + *vim_strchr(p, '.') = NUL; + } + return p; +} +#endif // FEAT_CLIENTSERVER + +#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) + static void +make_connection(void) +{ + if (X_DISPLAY == NULL +# ifdef FEAT_GUI + && !gui.in_use +# endif + ) + { + x_force_connect = TRUE; + setup_term_clip(); + x_force_connect = FALSE; + } +} + + static int +check_connection(void) +{ + make_connection(); + if (X_DISPLAY == NULL) + { + emsg(_("E240: No connection to the X server")); + return FAIL; + } + return OK; +} +#endif + +#ifdef FEAT_CLIENTSERVER + static void +remote_common(typval_T *argvars, typval_T *rettv, int expr) +{ + char_u *server_name; + char_u *keys; + char_u *r = NULL; + char_u buf[NUMBUFLEN]; + int timeout = 0; +# ifdef MSWIN + HWND w; +# else + Window w; +# endif + + if (check_restricted() || check_secure()) + return; + +# ifdef FEAT_X11 + if (check_connection() == FAIL) + return; +# endif + if (argvars[2].v_type != VAR_UNKNOWN + && argvars[3].v_type != VAR_UNKNOWN) + timeout = tv_get_number(&argvars[3]); + + server_name = tv_get_string_chk(&argvars[0]); + if (server_name == NULL) + 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 + if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout, + 0, TRUE) < 0) +# endif + { + if (r != NULL) + { + emsg((char *)r); // sending worked but evaluation failed + vim_free(r); + } + else + semsg(_("E241: Unable to send to %s"), server_name); + return; + } + + rettv->vval.v_string = r; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + dictitem_T v; + char_u str[30]; + char_u *idvar; + + idvar = tv_get_string_chk(&argvars[2]); + if (idvar != NULL && *idvar != NUL) + { + sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); + 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); + } + } +} +#endif + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * "remote_expr()" function + */ + void +f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, rettv, TRUE); +#endif +} + +/* + * "remote_foreground()" function + */ + void +f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CLIENTSERVER +# ifdef MSWIN + // On Win32 it's done in this application. + { + char_u *server_name = tv_get_string_chk(&argvars[0]); + + if (server_name != NULL) + serverForeground(server_name); + } +# else + // Send a foreground() expression to the server. + argvars[1].v_type = VAR_STRING; + argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); + argvars[2].v_type = VAR_UNKNOWN; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + remote_common(argvars, rettv, TRUE); + vim_free(argvars[1].vval.v_string); +# endif +#endif +} + + void +f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CLIENTSERVER + dictitem_T v; + char_u *s = NULL; +# ifdef MSWIN + long_u n = 0; +# endif + char_u *serverid; + + if (check_restricted() || check_secure()) + { + rettv->vval.v_number = -1; + return; + } + serverid = tv_get_string_chk(&argvars[0]); + if (serverid == NULL) + { + rettv->vval.v_number = -1; + 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 + { + s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0); + rettv->vval.v_number = (s != NULL); + } +# else + if (check_connection() == FAIL) + return; + + rettv->vval.v_number = serverPeekReply(X_DISPLAY, + serverStrToWin(serverid), &s); +# endif + + if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) + { + char_u *retvar; + + v.di_tv.v_type = VAR_STRING; + v.di_tv.vval.v_string = vim_strsave(s); + retvar = tv_get_string_chk(&argvars[1]); + if (retvar != NULL) + set_var(retvar, &v.di_tv, FALSE); + vim_free(v.di_tv.vval.v_string); + } +#else + rettv->vval.v_number = -1; +#endif +} + + void +f_remote_read(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER + char_u *serverid = tv_get_string_chk(&argvars[0]); + + if (serverid != NULL && !check_restricted() && !check_secure()) + { + int timeout = 0; +# ifdef MSWIN + // The server's HWND is encoded in the 'id' parameter + long_u n = 0; +# endif + + if (argvars[1].v_type != VAR_UNKNOWN) + 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) +# else + if (check_connection() == FAIL + || serverReadReply(X_DISPLAY, serverStrToWin(serverid), + &r, FALSE, timeout) < 0) +# endif + emsg(_("E277: Unable to read a server reply")); + } +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = r; +} + +/* + * "remote_send()" function + */ + void +f_remote_send(typval_T *argvars UNUSED, typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; +#ifdef FEAT_CLIENTSERVER + remote_common(argvars, rettv, FALSE); +#endif +} + +/* + * "remote_startserver()" function + */ + void +f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_CLIENTSERVER + char_u *server = tv_get_string_chk(&argvars[0]); + + if (server == NULL) + return; // type error; errmsg already given + if (serverName != NULL) + emsg(_("E941: already started a server")); + else + { +# ifdef FEAT_X11 + if (check_connection() == OK) + serverRegisterName(X_DISPLAY, server); +# else + serverSetName(server); +# endif + } +#else + emsg(_("E942: +clientserver feature not available")); +#endif +} + + void +f_server2client(typval_T *argvars UNUSED, typval_T *rettv) +{ +#ifdef FEAT_CLIENTSERVER + char_u buf[NUMBUFLEN]; + char_u *server = tv_get_string_chk(&argvars[0]); + char_u *reply = tv_get_string_buf_chk(&argvars[1], buf); + + rettv->vval.v_number = -1; + if (server == NULL || reply == NULL) + return; + if (check_restricted() || check_secure()) + return; +# ifdef FEAT_X11 + if (check_connection() == FAIL) + return; +# endif + + if (serverSendReply(server, reply) < 0) + { + emsg(_("E258: Unable to send to client")); + return; + } + rettv->vval.v_number = 0; +#else + rettv->vval.v_number = -1; +#endif +} + + void +f_serverlist(typval_T *argvars UNUSED, typval_T *rettv) +{ + char_u *r = NULL; + +#ifdef FEAT_CLIENTSERVER +# ifdef MSWIN + r = serverGetVimNames(); +# else + make_connection(); + if (X_DISPLAY != NULL) + r = serverGetVimNames(X_DISPLAY); +# endif +#endif + rettv->v_type = VAR_STRING; + rettv->vval.v_string = r; +} +#endif diff --git a/src/evalfunc.c b/src/evalfunc.c index f43ac082c..5a5daaa5a 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -168,12 +168,6 @@ static void f_rand(typval_T *argvars, typval_T *rettv); static void f_range(typval_T *argvars, typval_T *rettv); static void f_reg_executing(typval_T *argvars, typval_T *rettv); static void f_reg_recording(typval_T *argvars, typval_T *rettv); -static void f_remote_expr(typval_T *argvars, typval_T *rettv); -static void f_remote_foreground(typval_T *argvars, typval_T *rettv); -static void f_remote_peek(typval_T *argvars, typval_T *rettv); -static void f_remote_read(typval_T *argvars, typval_T *rettv); -static void f_remote_send(typval_T *argvars, typval_T *rettv); -static void f_remote_startserver(typval_T *argvars, typval_T *rettv); static void f_rename(typval_T *argvars, typval_T *rettv); static void f_repeat(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT @@ -193,8 +187,6 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv); static void f_searchpair(typval_T *argvars, typval_T *rettv); static void f_searchpairpos(typval_T *argvars, typval_T *rettv); static void f_searchpos(typval_T *argvars, typval_T *rettv); -static void f_server2client(typval_T *argvars, typval_T *rettv); -static void f_serverlist(typval_T *argvars, typval_T *rettv); static void f_setcharsearch(typval_T *argvars, typval_T *rettv); static void f_setenv(typval_T *argvars, typval_T *rettv); static void f_setfperm(typval_T *argvars, typval_T *rettv); @@ -6335,275 +6327,6 @@ f_reg_recording(typval_T *argvars UNUSED, typval_T *rettv) return_register(reg_recording, rettv); } -#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) - static void -make_connection(void) -{ - if (X_DISPLAY == NULL -# ifdef FEAT_GUI - && !gui.in_use -# endif - ) - { - x_force_connect = TRUE; - setup_term_clip(); - x_force_connect = FALSE; - } -} - - static int -check_connection(void) -{ - make_connection(); - if (X_DISPLAY == NULL) - { - emsg(_("E240: No connection to the X server")); - return FAIL; - } - return OK; -} -#endif - -#ifdef FEAT_CLIENTSERVER - static void -remote_common(typval_T *argvars, typval_T *rettv, int expr) -{ - char_u *server_name; - char_u *keys; - char_u *r = NULL; - char_u buf[NUMBUFLEN]; - int timeout = 0; -# ifdef MSWIN - HWND w; -# else - Window w; -# endif - - if (check_restricted() || check_secure()) - return; - -# ifdef FEAT_X11 - if (check_connection() == FAIL) - return; -# endif - if (argvars[2].v_type != VAR_UNKNOWN - && argvars[3].v_type != VAR_UNKNOWN) - timeout = tv_get_number(&argvars[3]); - - server_name = tv_get_string_chk(&argvars[0]); - if (server_name == NULL) - 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 - if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout, - 0, TRUE) < 0) -# endif - { - if (r != NULL) - { - emsg((char *)r); // sending worked but evaluation failed - vim_free(r); - } - else - semsg(_("E241: Unable to send to %s"), server_name); - return; - } - - rettv->vval.v_string = r; - - if (argvars[2].v_type != VAR_UNKNOWN) - { - dictitem_T v; - char_u str[30]; - char_u *idvar; - - idvar = tv_get_string_chk(&argvars[2]); - if (idvar != NULL && *idvar != NUL) - { - sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); - 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); - } - } -} -#endif - -/* - * "remote_expr()" function - */ - static void -f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_CLIENTSERVER - remote_common(argvars, rettv, TRUE); -#endif -} - -/* - * "remote_foreground()" function - */ - static void -f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_CLIENTSERVER -# ifdef MSWIN - // On Win32 it's done in this application. - { - char_u *server_name = tv_get_string_chk(&argvars[0]); - - if (server_name != NULL) - serverForeground(server_name); - } -# else - // Send a foreground() expression to the server. - argvars[1].v_type = VAR_STRING; - argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); - argvars[2].v_type = VAR_UNKNOWN; - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - remote_common(argvars, rettv, TRUE); - vim_free(argvars[1].vval.v_string); -# endif -#endif -} - - static void -f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CLIENTSERVER - dictitem_T v; - char_u *s = NULL; -# ifdef MSWIN - long_u n = 0; -# endif - char_u *serverid; - - if (check_restricted() || check_secure()) - { - rettv->vval.v_number = -1; - return; - } - serverid = tv_get_string_chk(&argvars[0]); - if (serverid == NULL) - { - rettv->vval.v_number = -1; - 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 - { - s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0); - rettv->vval.v_number = (s != NULL); - } -# else - if (check_connection() == FAIL) - return; - - rettv->vval.v_number = serverPeekReply(X_DISPLAY, - serverStrToWin(serverid), &s); -# endif - - if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) - { - char_u *retvar; - - v.di_tv.v_type = VAR_STRING; - v.di_tv.vval.v_string = vim_strsave(s); - retvar = tv_get_string_chk(&argvars[1]); - if (retvar != NULL) - set_var(retvar, &v.di_tv, FALSE); - vim_free(v.di_tv.vval.v_string); - } -#else - rettv->vval.v_number = -1; -#endif -} - - static void -f_remote_read(typval_T *argvars UNUSED, typval_T *rettv) -{ - char_u *r = NULL; - -#ifdef FEAT_CLIENTSERVER - char_u *serverid = tv_get_string_chk(&argvars[0]); - - if (serverid != NULL && !check_restricted() && !check_secure()) - { - int timeout = 0; -# ifdef MSWIN - // The server's HWND is encoded in the 'id' parameter - long_u n = 0; -# endif - - if (argvars[1].v_type != VAR_UNKNOWN) - 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) -# else - if (check_connection() == FAIL - || serverReadReply(X_DISPLAY, serverStrToWin(serverid), - &r, FALSE, timeout) < 0) -# endif - emsg(_("E277: Unable to read a server reply")); - } -#endif - rettv->v_type = VAR_STRING; - rettv->vval.v_string = r; -} - -/* - * "remote_send()" function - */ - static void -f_remote_send(typval_T *argvars UNUSED, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -#ifdef FEAT_CLIENTSERVER - remote_common(argvars, rettv, FALSE); -#endif -} - -/* - * "remote_startserver()" function - */ - static void -f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED) -{ -#ifdef FEAT_CLIENTSERVER - char_u *server = tv_get_string_chk(&argvars[0]); - - if (server == NULL) - return; // type error; errmsg already given - if (serverName != NULL) - emsg(_("E941: already started a server")); - else - { -# ifdef FEAT_X11 - if (check_connection() == OK) - serverRegisterName(X_DISPLAY, server); -# else - serverSetName(server); -# endif - } -#else - emsg(_("E942: +clientserver feature not available")); -#endif -} - /* * "rename({from}, {to})" function */ @@ -7374,53 +7097,6 @@ f_searchpos(typval_T *argvars, typval_T *rettv) } static void -f_server2client(typval_T *argvars UNUSED, typval_T *rettv) -{ -#ifdef FEAT_CLIENTSERVER - char_u buf[NUMBUFLEN]; - char_u *server = tv_get_string_chk(&argvars[0]); - char_u *reply = tv_get_string_buf_chk(&argvars[1], buf); - - rettv->vval.v_number = -1; - if (server == NULL || reply == NULL) - return; - if (check_restricted() || check_secure()) - return; -# ifdef FEAT_X11 - if (check_connection() == FAIL) - return; -# endif - - if (serverSendReply(server, reply) < 0) - { - emsg(_("E258: Unable to send to client")); - return; - } - rettv->vval.v_number = 0; -#else - rettv->vval.v_number = -1; -#endif -} - - static void -f_serverlist(typval_T *argvars UNUSED, typval_T *rettv) -{ - char_u *r = NULL; - -#ifdef FEAT_CLIENTSERVER -# ifdef MSWIN - r = serverGetVimNames(); -# else - make_connection(); - if (X_DISPLAY != NULL) - r = serverGetVimNames(X_DISPLAY); -# endif -#endif - rettv->v_type = VAR_STRING; - rettv->vval.v_string = r; -} - - static void f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED) { dict_T *d; diff --git a/src/main.c b/src/main.c index 79973cbaa..e8984ee59 100644 --- a/src/main.c +++ b/src/main.c @@ -54,12 +54,6 @@ static void check_swap_exists_action(void); # ifdef FEAT_EVAL static void set_progpath(char_u *argv0); # endif -# if defined(FEAT_CLIENTSERVER) || defined(PROTO) -static void exec_on_server(mparm_T *parmp); -static void prepare_server(mparm_T *parmp); -static void cmdsrv_main(int *argc, char **argv, char_u *serverName_arg, char_u **serverStr); -static char_u *serverMakeName(char_u *arg, char *cmd); -# endif #endif @@ -3714,670 +3708,3 @@ set_progpath(char_u *argv0) } #endif // NO_VIM_MAIN - -#if (defined(FEAT_CLIENTSERVER) && !defined(NO_VIM_MAIN)) || defined(PROTO) - -/* - * Common code for the X command server and the Win32 command server. - */ - -static char_u *build_drop_cmd(int filec, char **filev, int tabs, int sendReply); - -/* - * Do the client-server stuff, unless "--servername ''" was used. - */ - static void -exec_on_server(mparm_T *parmp) -{ - if (parmp->serverName_arg == NULL || *parmp->serverName_arg != NUL) - { -# ifdef MSWIN - // Initialise the client/server messaging infrastructure. - serverInitMessaging(); -# endif - - /* - * When a command server argument was found, execute it. This may - * exit Vim when it was successful. Otherwise it's executed further - * on. Remember the encoding used here in "serverStrEnc". - */ - if (parmp->serverArg) - { - cmdsrv_main(&parmp->argc, parmp->argv, - parmp->serverName_arg, &parmp->serverStr); - parmp->serverStrEnc = vim_strsave(p_enc); - } - - // If we're still running, get the name to register ourselves. - // On Win32 can register right now, for X11 need to setup the - // clipboard first, it's further down. - parmp->servername = serverMakeName(parmp->serverName_arg, - parmp->argv[0]); -# ifdef MSWIN - if (parmp->servername != NULL) - { - serverSetName(parmp->servername); - vim_free(parmp->servername); - } -# endif - } -} - -/* - * Prepare for running as a Vim server. - */ - static void -prepare_server(mparm_T *parmp) -{ -# if defined(FEAT_X11) - /* - * Register for remote command execution with :serversend and --remote - * unless there was a -X or a --servername '' on the command line. - * Only register nongui-vim's with an explicit --servername argument, - * or when compiling with autoservername. - * When running as root --servername is also required. - */ - if (X_DISPLAY != NULL && parmp->servername != NULL && ( -# if defined(FEAT_AUTOSERVERNAME) || defined(FEAT_GUI) - ( -# if defined(FEAT_AUTOSERVERNAME) - 1 -# else - gui.in_use -# endif -# ifdef UNIX - && getuid() != ROOT_UID -# endif - ) || -# endif - parmp->serverName_arg != NULL)) - { - (void)serverRegisterName(X_DISPLAY, parmp->servername); - vim_free(parmp->servername); - TIME_MSG("register server name"); - } - else - serverDelayedStartName = parmp->servername; -# endif - - /* - * Execute command ourselves if we're here because the send failed (or - * else we would have exited above). - */ - if (parmp->serverStr != NULL) - { - char_u *p; - - server_to_input_buf(serverConvert(parmp->serverStrEnc, - parmp->serverStr, &p)); - vim_free(p); - } -} - - static void -cmdsrv_main( - int *argc, - char **argv, - char_u *serverName_arg, - char_u **serverStr) -{ - char_u *res; - int i; - char_u *sname; - int ret; - int didone = FALSE; - int exiterr = 0; - char **newArgV = argv + 1; - int newArgC = 1, - Argc = *argc; - int argtype; -#define ARGTYPE_OTHER 0 -#define ARGTYPE_EDIT 1 -#define ARGTYPE_EDIT_WAIT 2 -#define ARGTYPE_SEND 3 - int silent = FALSE; - int tabs = FALSE; -# ifndef FEAT_X11 - HWND srv; -# else - Window srv; - - setup_term_clip(); -# endif - - sname = serverMakeName(serverName_arg, argv[0]); - if (sname == NULL) - return; - - /* - * Execute the command server related arguments and remove them - * from the argc/argv array; We may have to return into main() - */ - for (i = 1; i < Argc; i++) - { - res = NULL; - if (STRCMP(argv[i], "--") == 0) // end of option arguments - { - for (; i < *argc; i++) - { - *newArgV++ = argv[i]; - newArgC++; - } - break; - } - - if (STRICMP(argv[i], "--remote-send") == 0) - argtype = ARGTYPE_SEND; - else if (STRNICMP(argv[i], "--remote", 8) == 0) - { - char *p = argv[i] + 8; - - argtype = ARGTYPE_EDIT; - while (*p != NUL) - { - if (STRNICMP(p, "-wait", 5) == 0) - { - argtype = ARGTYPE_EDIT_WAIT; - p += 5; - } - else if (STRNICMP(p, "-silent", 7) == 0) - { - silent = TRUE; - p += 7; - } - else if (STRNICMP(p, "-tab", 4) == 0) - { - tabs = TRUE; - p += 4; - } - else - { - argtype = ARGTYPE_OTHER; - break; - } - } - } - else - argtype = ARGTYPE_OTHER; - - if (argtype != ARGTYPE_OTHER) - { - if (i == *argc - 1) - mainerr_arg_missing((char_u *)argv[i]); - if (argtype == ARGTYPE_SEND) - { - *serverStr = (char_u *)argv[i + 1]; - i++; - } - else - { - *serverStr = build_drop_cmd(*argc - i - 1, argv + i + 1, - tabs, argtype == ARGTYPE_EDIT_WAIT); - if (*serverStr == NULL) - { - // Probably out of memory, exit. - didone = TRUE; - exiterr = 1; - break; - } - Argc = i; - } -# ifdef FEAT_X11 - if (xterm_dpy == NULL) - { - mch_errmsg(_("No display")); - ret = -1; - } - else - ret = serverSendToVim(xterm_dpy, sname, *serverStr, - NULL, &srv, 0, 0, 0, silent); -# else - // Win32 always works? - ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent); -# endif - if (ret < 0) - { - if (argtype == ARGTYPE_SEND) - { - // Failed to send, abort. - mch_errmsg(_(": Send failed.\n")); - didone = TRUE; - exiterr = 1; - } - else if (!silent) - // Let vim start normally. - mch_errmsg(_(": Send failed. Trying to execute locally\n")); - break; - } - -# ifdef FEAT_GUI_MSWIN - // Guess that when the server name starts with "g" it's a GUI - // server, which we can bring to the foreground here. - // Foreground() in the server doesn't work very well. - if (argtype != ARGTYPE_SEND && TOUPPER_ASC(*sname) == 'G') - SetForegroundWindow(srv); -# endif - - /* - * For --remote-wait: Wait until the server did edit each - * file. Also detect that the server no longer runs. - */ - if (ret >= 0 && argtype == ARGTYPE_EDIT_WAIT) - { - int numFiles = *argc - i - 1; - int j; - char_u *done = alloc(numFiles); - char_u *p; -# ifdef FEAT_GUI_MSWIN - NOTIFYICONDATA ni; - int count = 0; - extern HWND message_window; -# endif - - if (numFiles > 0 && argv[i + 1][0] == '+') - // Skip "+cmd" argument, don't wait for it to be edited. - --numFiles; - -# ifdef FEAT_GUI_MSWIN - ni.cbSize = sizeof(ni); - ni.hWnd = message_window; - ni.uID = 0; - ni.uFlags = NIF_ICON|NIF_TIP; - ni.hIcon = LoadIcon((HINSTANCE)GetModuleHandle(0), "IDR_VIM"); - sprintf(ni.szTip, _("%d of %d edited"), count, numFiles); - Shell_NotifyIcon(NIM_ADD, &ni); -# endif - - // Wait for all files to unload in remote - vim_memset(done, 0, numFiles); - while (memchr(done, 0, numFiles) != NULL) - { -# ifdef MSWIN - p = serverGetReply(srv, NULL, TRUE, TRUE, 0); - if (p == NULL) - break; -# else - if (serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0) - break; -# endif - j = atoi((char *)p); - if (j >= 0 && j < numFiles) - { -# ifdef FEAT_GUI_MSWIN - ++count; - sprintf(ni.szTip, _("%d of %d edited"), - count, numFiles); - Shell_NotifyIcon(NIM_MODIFY, &ni); -# endif - done[j] = 1; - } - } -# ifdef FEAT_GUI_MSWIN - Shell_NotifyIcon(NIM_DELETE, &ni); -# endif - } - } - else if (STRICMP(argv[i], "--remote-expr") == 0) - { - if (i == *argc - 1) - mainerr_arg_missing((char_u *)argv[i]); -# ifdef MSWIN - // Win32 always works? - if (serverSendToVim(sname, (char_u *)argv[i + 1], - &res, NULL, 1, 0, FALSE) < 0) -# else - if (xterm_dpy == NULL) - mch_errmsg(_("No display: Send expression failed.\n")); - else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1], - &res, NULL, 1, 0, 1, FALSE) < 0) -# endif - { - if (res != NULL && *res != NUL) - { - // Output error from remote - mch_errmsg((char *)res); - VIM_CLEAR(res); - } - mch_errmsg(_(": Send expression failed.\n")); - } - } - else if (STRICMP(argv[i], "--serverlist") == 0) - { -# ifdef MSWIN - // Win32 always works? - res = serverGetVimNames(); -# else - if (xterm_dpy != NULL) - res = serverGetVimNames(xterm_dpy); -# endif - if (did_emsg) - mch_errmsg("\n"); - } - else if (STRICMP(argv[i], "--servername") == 0) - { - // Already processed. Take it out of the command line - i++; - continue; - } - else - { - *newArgV++ = argv[i]; - newArgC++; - continue; - } - didone = TRUE; - if (res != NULL && *res != NUL) - { - mch_msg((char *)res); - if (res[STRLEN(res) - 1] != '\n') - mch_msg("\n"); - } - vim_free(res); - } - - if (didone) - { - display_errors(); // display any collected messages - exit(exiterr); // Mission accomplished - get out - } - - // Return back into main() - *argc = newArgC; - vim_free(sname); -} - -/* - * Build a ":drop" command to send to a Vim server. - */ - static char_u * -build_drop_cmd( - int filec, - char **filev, - int tabs, // Use ":tab drop" instead of ":drop". - int sendReply) -{ - garray_T ga; - int i; - char_u *inicmd = NULL; - char_u *p; - char_u *cdp; - char_u *cwd; - - if (filec > 0 && filev[0][0] == '+') - { - inicmd = (char_u *)filev[0] + 1; - filev++; - filec--; - } - // Check if we have at least one argument. - if (filec <= 0) - mainerr_arg_missing((char_u *)filev[-1]); - - // Temporarily cd to the current directory to handle relative file names. - cwd = alloc(MAXPATHL); - if (cwd == NULL) - return NULL; - if (mch_dirname(cwd, MAXPATHL) != OK) - { - vim_free(cwd); - return NULL; - } - cdp = vim_strsave_escaped_ext(cwd, -#ifdef BACKSLASH_IN_FILENAME - (char_u *)"", // rem_backslash() will tell what chars to escape -#else - PATH_ESC_CHARS, -#endif - '\\', TRUE); - vim_free(cwd); - if (cdp == NULL) - return NULL; - ga_init2(&ga, 1, 100); - ga_concat(&ga, (char_u *)"<C-\\><C-N>:cd "); - ga_concat(&ga, cdp); - - // Call inputsave() so that a prompt for an encryption key works. - ga_concat(&ga, (char_u *)"<CR>:if exists('*inputsave')|call inputsave()|endif|"); - if (tabs) - ga_concat(&ga, (char_u *)"tab "); - ga_concat(&ga, (char_u *)"drop"); - for (i = 0; i < filec; i++) - { - // On Unix the shell has already expanded the wildcards, don't want to - // do it again in the Vim server. On MS-Windows only escape - // non-wildcard characters. - p = vim_strsave_escaped((char_u *)filev[i], -#ifdef UNIX - PATH_ESC_CHARS -#else - (char_u *)" \t%#" -#endif - ); - if (p == NULL) - { - vim_free(ga.ga_data); - return NULL; - } - ga_concat(&ga, (char_u *)" "); - ga_concat(&ga, p); - vim_free(p); - } - ga_concat(&ga, (char_u *)"|if exists('*inputrestore')|call inputrestore()|endif<CR>"); - - // The :drop commands goes to Insert mode when 'insertmode' is set, use - // CTRL-\ CTRL-N again. - ga_concat(&ga, (char_u *)"<C-\\><C-N>"); - - // Switch back to the correct current directory (prior to temporary path - // switch) unless 'autochdir' is set, in which case it will already be - // correct after the :drop command. With line breaks and spaces: - // if !exists('+acd') || !&acd - // if haslocaldir() - // cd - - // lcd - - // elseif getcwd() ==# 'current path' - // cd - - // endif - // endif - ga_concat(&ga, (char_u *)":if !exists('+acd')||!&acd|if haslocaldir()|"); - ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd() ==# '"); - ga_concat(&ga, cdp); - ga_concat(&ga, (char_u *)"'|cd -|endif|endif<CR>"); - vim_free(cdp); - - if (sendReply) - ga_concat(&ga, (char_u *)":call SetupRemoteReplies()<CR>"); - ga_concat(&ga, (char_u *)":"); - if (inicmd != NULL) - { - // Can't use <CR> after "inicmd", because an "startinsert" would cause - // the following commands to be inserted as text. Use a "|", - // hopefully "inicmd" does allow this... - ga_concat(&ga, inicmd); - ga_concat(&ga, (char_u *)"|"); - } - // Bring the window to the foreground, goto Insert mode when 'im' set and - // clear command line. - ga_concat(&ga, (char_u *)"cal foreground()|if &im|star|en|redr|f<CR>"); - ga_append(&ga, NUL); - return ga.ga_data; -} - -/* - * Make our basic server name: use the specified "arg" if given, otherwise use - * the tail of the command "cmd" we were started with. - * Return the name in allocated memory. This doesn't include a serial number. - */ - static char_u * -serverMakeName(char_u *arg, char *cmd) -{ - char_u *p; - - if (arg != NULL && *arg != NUL) - p = vim_strsave_up(arg); - else - { - p = vim_strsave_up(gettail((char_u *)cmd)); - // Remove .exe or .bat from the name. - if (p != NULL && vim_strchr(p, '.') != NULL) - *vim_strchr(p, '.') = NUL; - } - return p; -} -#endif // FEAT_CLIENTSERVER - -#if defined(FEAT_CLIENTSERVER) || defined(PROTO) -/* - * Replace termcodes such as <CR> and insert as key presses if there is room. - */ - void -server_to_input_buf(char_u *str) -{ - char_u *ptr = NULL; - char_u *cpo_save = p_cpo; - - // Set 'cpoptions' the way we want it. - // B set - backslashes are *not* treated specially - // k set - keycodes are *not* reverse-engineered - // < unset - <Key> sequences *are* interpreted - // The last but one parameter of replace_termcodes() is TRUE so that the - // <lt> sequence is recognised - needed for a real backslash. - p_cpo = (char_u *)"Bk"; - str = replace_termcodes((char_u *)str, &ptr, REPTERM_DO_LT, NULL); - p_cpo = cpo_save; - - if (*ptr != NUL) // trailing CTRL-V results in nothing - { - /* - * Add the string to the input stream. - * Can't use add_to_input_buf() here, we now have K_SPECIAL bytes. - * - * First clear typed characters from the typeahead buffer, there could - * be half a mapping there. Then append to the existing string, so - * that multiple commands from a client are concatenated. - */ - if (typebuf.tb_maplen < typebuf.tb_len) - del_typebuf(typebuf.tb_len - typebuf.tb_maplen, typebuf.tb_maplen); - (void)ins_typebuf(str, REMAP_NONE, typebuf.tb_len, TRUE, FALSE); - - // Let input_available() know we inserted text in the typeahead - // buffer. - typebuf_was_filled = TRUE; - } - vim_free((char_u *)ptr); -} - -/* - * Evaluate an expression that the client sent to a string. - */ - char_u * -eval_client_expr_to_string(char_u *expr) -{ - char_u *res; - int save_dbl = debug_break_level; - int save_ro = redir_off; - funccal_entry_T funccal_entry; - int did_save_funccal = FALSE; - - // Evaluate the expression at the toplevel, don't use variables local to - // the calling function. Except when in debug mode. - if (!debug_mode) - { - save_funccal(&funccal_entry); - did_save_funccal = TRUE; - } - - // Disable debugging, otherwise Vim hangs, waiting for "cont" to be - // typed. - debug_break_level = -1; - redir_off = 0; - // Do not display error message, otherwise Vim hangs, waiting for "cont" - // to be typed. Do generate errors so that try/catch works. - ++emsg_silent; - - res = eval_to_string(expr, NULL, TRUE); - - debug_break_level = save_dbl; - redir_off = save_ro; - --emsg_silent; - if (emsg_silent < 0) - emsg_silent = 0; - if (did_save_funccal) - restore_funccal(); - - // A client can tell us to redraw, but not to display the cursor, so do - // that here. - setcursor(); - out_flush_cursor(FALSE, FALSE); - - return res; -} - -/* - * Evaluate a command or expression sent to ourselves. - */ - int -sendToLocalVim(char_u *cmd, int asExpr, char_u **result) -{ - if (asExpr) - { - char_u *ret; - - ret = eval_client_expr_to_string(cmd); - if (result != NULL) - { - if (ret == NULL) - { - char *err = _(e_invexprmsg); - size_t len = STRLEN(cmd) + STRLEN(err) + 5; - char_u *msg; - - msg = alloc(len); - if (msg != NULL) - vim_snprintf((char *)msg, len, "%s: \"%s\"", err, cmd); - *result = msg; - } - else - *result = ret; - } - else - vim_free(ret); - return ret == NULL ? -1 : 0; - } - server_to_input_buf(cmd); - return 0; -} - -/* - * If conversion is needed, convert "data" from "client_enc" to 'encoding' and - * return an allocated string. Otherwise return "data". - * "*tofree" is set to the result when it needs to be freed later. - */ - char_u * -serverConvert( - char_u *client_enc UNUSED, - char_u *data, - char_u **tofree) -{ - char_u *res = data; - - *tofree = NULL; - if (client_enc != NULL && p_enc != NULL) - { - vimconv_T vimconv; - - vimconv.vc_type = CONV_NONE; - if (convert_setup(&vimconv, client_enc, p_enc) != FAIL - && vimconv.vc_type != CONV_NONE) - { - res = string_convert(&vimconv, data, NULL); - if (res == NULL) - res = data; - else - *tofree = res; - } - convert_setup(&vimconv, NULL, NULL); - } - return res; -} -#endif diff --git a/src/proto.h b/src/proto.h index 5d8155769..0115c68d1 100644 --- a/src/proto.h +++ b/src/proto.h @@ -68,6 +68,7 @@ extern int _stricoll(char *a, char *b); # include "change.pro" # include "charset.pro" # include "cindent.pro" +# include "clientserver.pro" # include "clipboard.pro" # include "cmdexpand.pro" # include "cmdhist.pro" diff --git a/src/proto/clientserver.pro b/src/proto/clientserver.pro new file mode 100644 index 000000000..3acd5e01e --- /dev/null +++ b/src/proto/clientserver.pro @@ -0,0 +1,16 @@ +/* clientserver.c */ +void server_to_input_buf(char_u *str); +char_u *eval_client_expr_to_string(char_u *expr); +int sendToLocalVim(char_u *cmd, int asExpr, char_u **result); +char_u *serverConvert(char_u *client_enc, char_u *data, char_u **tofree); +void exec_on_server(mparm_T *parmp); +void prepare_server(mparm_T *parmp); +void f_remote_expr(typval_T *argvars, typval_T *rettv); +void f_remote_foreground(typval_T *argvars, typval_T *rettv); +void f_remote_peek(typval_T *argvars, typval_T *rettv); +void f_remote_read(typval_T *argvars, typval_T *rettv); +void f_remote_send(typval_T *argvars, typval_T *rettv); +void f_remote_startserver(typval_T *argvars, typval_T *rettv); +void f_server2client(typval_T *argvars, typval_T *rettv); +void f_serverlist(typval_T *argvars, typval_T *rettv); +/* vim: set ft=c : */ diff --git a/src/proto/main.pro b/src/proto/main.pro index 5fa96c390..ff3e1a503 100644 --- a/src/proto/main.pro +++ b/src/proto/main.pro @@ -12,8 +12,4 @@ void getout_preserve_modified(int exitval); void getout(int exitval); int process_env(char_u *env, int is_viminit); void mainerr_arg_missing(char_u *str); -void server_to_input_buf(char_u *str); -char_u *eval_client_expr_to_string(char_u *expr); -int sendToLocalVim(char_u *cmd, int asExpr, char_u **result); -char_u *serverConvert(char_u *client_enc, char_u *data, char_u **tofree); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c index 078775ca6..fb736142d 100644 --- a/src/version.c +++ b/src/version.c @@ -739,6 +739,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 516, +/**/ 515, /**/ 514, |