To: vim_dev@googlegroups.com Subject: Patch 8.2.0516 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0516 Problem: Client-server code is spread out. Solution: Move client-server code to a new file. (Yegappan Lakshmanan, closes #5885) Files: Filelist, src/Make_cyg_ming.mak, src/Make_morph.mak, src/Make_mvc.mak, src/Make_vms.mms, src/Makefile, src/README.md, src/clientserver.c, src/evalfunc.c, src/main.c, src/proto.h, src/proto/clientserver.pro, src/proto/main.pro *** ../vim-8.2.0515/Filelist 2020-03-27 20:58:33.341005930 +0100 --- Filelist 2020-04-05 20:05:58.028769866 +0200 *************** *** 30,35 **** --- 30,36 ---- src/channel.c \ src/charset.c \ src/cindent.c \ + src/clientserver.c \ src/clipboard.c \ src/cmdexpand.c \ src/cmdhist.c \ *************** *** 203,208 **** --- 204,210 ---- 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 \ *** ../vim-8.2.0515/src/Make_cyg_ming.mak 2020-03-24 21:41:38.023535429 +0100 --- src/Make_cyg_ming.mak 2020-04-05 20:05:58.028769866 +0200 *************** *** 710,715 **** --- 710,716 ---- $(OUTDIR)/change.o \ $(OUTDIR)/charset.o \ $(OUTDIR)/cindent.o \ + $(OUTDIR)/clientserver.o \ $(OUTDIR)/clipboard.o \ $(OUTDIR)/cmdexpand.o \ $(OUTDIR)/cmdhist.o \ *** ../vim-8.2.0515/src/Make_morph.mak 2020-03-24 21:41:38.023535429 +0100 --- src/Make_morph.mak 2020-04-05 20:05:58.028769866 +0200 *************** *** 33,38 **** --- 33,39 ---- change.c \ charset.c \ cindent.c \ + clientserver.c \ clipboard.c \ cmdexpand.c \ cmdhist.c \ *** ../vim-8.2.0515/src/Make_mvc.mak 2020-03-24 21:41:38.023535429 +0100 --- src/Make_mvc.mak 2020-04-05 20:05:58.028769866 +0200 *************** *** 730,735 **** --- 730,736 ---- $(OUTDIR)\change.obj \ $(OUTDIR)\charset.obj \ $(OUTDIR)\cindent.obj \ + $(OUTDIR)\clientserver.obj \ $(OUTDIR)\clipboard.obj \ $(OUTDIR)\cmdexpand.obj \ $(OUTDIR)\cmdhist.obj \ *************** *** 1516,1521 **** --- 1517,1524 ---- $(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,1870 **** --- 1868,1874 ---- proto/change.pro \ proto/charset.pro \ proto/cindent.pro \ + proto/clientserver.pro \ proto/clipboard.pro \ proto/cmdexpand.pro \ proto/cmdhist.pro \ *** ../vim-8.2.0515/src/Make_vms.mms 2020-03-24 21:41:38.023535429 +0100 --- src/Make_vms.mms 2020-04-05 20:05:58.028769866 +0200 *************** *** 306,311 **** --- 306,312 ---- change.c \ charset.c \ cindent.c \ + clientserver.c \ clipboard.c \ cmdexpand.c \ cmdhist.c \ *************** *** 413,418 **** --- 414,420 ---- change.obj \ charset.obj \ cindent.obj \ + clientserver.obj \ clipboard.obj \ cmdexpand.obj \ cmdhist.obj \ *************** *** 702,707 **** --- 704,713 ---- 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 \ *** ../vim-8.2.0515/src/Makefile 2020-04-01 16:34:12.555263953 +0200 --- src/Makefile 2020-04-05 20:05:58.028769866 +0200 *************** *** 1602,1607 **** --- 1602,1608 ---- change.c \ charset.c \ cindent.c \ + clientserver.c \ clipboard.c \ cmdexpand.c \ cmdhist.c \ *************** *** 1747,1752 **** --- 1748,1754 ---- objects/blob.o \ objects/blowfish.o \ objects/cindent.o \ + objects/clientserver.o \ objects/clipboard.o \ objects/cmdexpand.o \ objects/cmdhist.o \ *************** *** 1908,1913 **** --- 1910,1916 ---- channel.pro \ charset.pro \ cindent.pro \ + clientserver.pro \ clipboard.pro \ cmdexpand.pro \ cmdhist.pro \ *************** *** 3123,3128 **** --- 3126,3134 ---- 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,3748 **** --- 3749,3758 ---- 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 \ *** ../vim-8.2.0515/src/README.md 2020-03-24 21:41:38.023535429 +0100 --- src/README.md 2020-04-05 20:05:58.028769866 +0200 *************** *** 30,35 **** --- 30,36 ---- 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 *** ../vim-8.2.0515/src/clientserver.c 2020-04-05 20:20:01.972765384 +0200 --- src/clientserver.c 2020-04-05 20:09:13.427773245 +0200 *************** *** 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 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 - sequences *are* interpreted + // The last but one parameter of replace_termcodes() is TRUE so that the + // 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 *)":cd "); + ga_concat(&ga, cdp); + + // Call inputsave() so that a prompt for an encryption key works. + ga_concat(&ga, (char_u *)":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"); + + // The :drop commands goes to Insert mode when 'insertmode' is set, use + // CTRL-\ CTRL-N again. + ga_concat(&ga, (char_u *)""); + + // 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"); + vim_free(cdp); + + if (sendReply) + ga_concat(&ga, (char_u *)":call SetupRemoteReplies()"); + ga_concat(&ga, (char_u *)":"); + if (inicmd != NULL) + { + // Can't use 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"); + 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 *** ../vim-8.2.0515/src/evalfunc.c 2020-04-03 21:59:29.329665634 +0200 --- src/evalfunc.c 2020-04-05 20:05:58.032769845 +0200 *************** *** 168,179 **** 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 --- 168,173 ---- *************** *** 193,200 **** 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); --- 187,192 ---- *************** *** 6335,6609 **** 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 */ --- 6327,6332 ---- *************** *** 7374,7426 **** } 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; --- 7097,7102 ---- *** ../vim-8.2.0515/src/main.c 2020-04-04 14:00:34.193098268 +0200 --- src/main.c 2020-04-05 20:05:58.032769845 +0200 *************** *** 54,65 **** # 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 --- 54,59 ---- *************** *** 3714,4383 **** } #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 *)":cd "); - ga_concat(&ga, cdp); - - // Call inputsave() so that a prompt for an encryption key works. - ga_concat(&ga, (char_u *)":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"); - - // The :drop commands goes to Insert mode when 'insertmode' is set, use - // CTRL-\ CTRL-N again. - ga_concat(&ga, (char_u *)""); - - // 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"); - vim_free(cdp); - - if (sendReply) - ga_concat(&ga, (char_u *)":call SetupRemoteReplies()"); - ga_concat(&ga, (char_u *)":"); - if (inicmd != NULL) - { - // Can't use 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"); - 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 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 - sequences *are* interpreted - // The last but one parameter of replace_termcodes() is TRUE so that the - // 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 --- 3708,3710 ---- *** ../vim-8.2.0515/src/proto.h 2020-03-24 21:41:38.027535418 +0100 --- src/proto.h 2020-04-05 20:05:58.032769845 +0200 *************** *** 68,73 **** --- 68,74 ---- # include "change.pro" # include "charset.pro" # include "cindent.pro" + # include "clientserver.pro" # include "clipboard.pro" # include "cmdexpand.pro" # include "cmdhist.pro" *** ../vim-8.2.0515/src/proto/clientserver.pro 2020-04-05 20:20:01.996765279 +0200 --- src/proto/clientserver.pro 2020-04-05 20:05:58.032769845 +0200 *************** *** 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 : */ *** ../vim-8.2.0515/src/proto/main.pro 2020-02-14 13:21:55.646197062 +0100 --- src/proto/main.pro 2020-04-05 20:05:58.032769845 +0200 *************** *** 12,19 **** 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 : */ --- 12,15 ---- *** ../vim-8.2.0515/src/version.c 2020-04-05 19:09:02.134503484 +0200 --- src/version.c 2020-04-05 20:07:16.576361428 +0200 *************** *** 740,741 **** --- 740,743 ---- { /* Add new patch number below this line */ + /**/ + 516, /**/ -- If an elephant is left tied to a parking meter, the parking fee has to be paid just as it would for a vehicle. [real standing law in Florida, United States of America] /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///