vim/src/clientserver.c
Christian Brabandt 349f5cd818
patch 9.1.0356: MS-Windows: --remote may change working directory
Problem:  MS-Windows: --remote may change working directory when
          'shellslash' is set
Solution: normalize directory separators on MS-Windows

fixes: #14549
closes: #14587

Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-04-19 15:26:21 +02:00

1062 lines
25 KiB
C

/* 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(str, &ptr, 0, 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(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;
#if defined(FEAT_EVAL)
ch_log(NULL, "eval_client_expr_to_string(\"%s\")", expr);
#endif
// 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, TRUE, FALSE);
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_invalid_expression_received);
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)
return res;
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)
return;
# 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 (argtype == ARGTYPE_EDIT_WAIT)
{
int numFiles = *argc - i - 1;
char_u *done = alloc(numFiles);
# 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)
{
char_u *p;
int j;
# 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);
vim_free(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
vim_free(done);
}
}
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;
// reset wildignore temporarily
const char *wig[] =
{ "<CR><C-\\><C-N>:let g:_wig=&wig|set wig=",
"<C-\\><C-N>:let &wig=g:_wig|unlet g:_wig<CR>"};
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);
// reset wildignorecase temporarily
ga_concat(&ga, (char_u *)wig[0]);
// Call inputsave() so that a prompt for an encryption key works.
ga_concat(&ga, (char_u *)
"<CR><C-\\><C-N>: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()|");
#ifdef MSWIN
// in case :set shellslash is set, need to normalize the directory separators
// '/' is not valid in a filename so replacing '/' by '\\' should be safe
ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd()->tr('/','\\') ==# '");
#else
ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd() ==# '");
#endif
ga_concat(&ga, cdp);
ga_concat(&ga, (char_u *)"'|cd -|endif|endif<CR>");
vim_free(cdp);
// reset wildignorecase
ga_concat(&ga, (char_u *)wig[1]);
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 a "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(_(e_no_connection_to_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(_(e_unable_to_send_to_str), 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
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_opt_string_arg(argvars, 2) == FAIL
|| (argvars[2].v_type != VAR_UNKNOWN
&& check_for_opt_number_arg(argvars, 3) == FAIL)))
return;
remote_common(argvars, rettv, TRUE);
#endif
}
/*
* "remote_foreground()" function
*/
void
f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
#ifdef FEAT_CLIENTSERVER
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
return;
# 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;
rettv->vval.v_number = -1;
if (check_restricted() || check_secure())
return;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_string_arg(argvars, 1) == FAIL))
return;
serverid = tv_get_string_chk(&argvars[0]);
if (serverid == NULL)
return; // type error; errmsg already given
# ifdef MSWIN
sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n);
if (n == 0)
rettv->vval.v_number = -1;
else
{
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;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_number_arg(argvars, 1) == FAIL))
return;
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(_(e_unable_to_read_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
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL
|| check_for_opt_string_arg(argvars, 2) == FAIL))
return;
remote_common(argvars, rettv, FALSE);
#endif
}
/*
* "remote_startserver()" function
*/
void
f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
#ifdef FEAT_CLIENTSERVER
if (check_for_nonempty_string_arg(argvars, 0) == FAIL)
return;
if (serverName != NULL)
{
emsg(_(e_already_started_server));
return;
}
char_u *server = tv_get_string_chk(&argvars[0]);
# ifdef FEAT_X11
if (check_connection() == OK)
serverRegisterName(X_DISPLAY, server);
# else
serverSetName(server);
# endif
#else
emsg(_(e_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;
char_u *reply;
rettv->vval.v_number = -1;
if (check_restricted() || check_secure())
return;
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_string_arg(argvars, 1) == FAIL))
return;
server = tv_get_string_chk(&argvars[0]);
reply = tv_get_string_buf_chk(&argvars[1], buf);
if (server == NULL || reply == NULL)
return;
# ifdef FEAT_X11
if (check_connection() == FAIL)
return;
# endif
if (serverSendReply(server, reply) < 0)
{
emsg(_(e_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