xref: /vim-8.2.3635/src/clientserver.c (revision 4490ec4e)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * clientserver.c: functions for Client Server functionality
12  */
13 
14 #include "vim.h"
15 
16 #if defined(FEAT_CLIENTSERVER) || defined(PROTO)
17 
18 static void cmdsrv_main(int *argc, char **argv, char_u *serverName_arg, char_u **serverStr);
19 static char_u *serverMakeName(char_u *arg, char *cmd);
20 
21 /*
22  * Replace termcodes such as <CR> and insert as key presses if there is room.
23  */
24     void
server_to_input_buf(char_u * str)25 server_to_input_buf(char_u *str)
26 {
27     char_u      *ptr = NULL;
28     char_u      *cpo_save = p_cpo;
29 
30     // Set 'cpoptions' the way we want it.
31     //    B set - backslashes are *not* treated specially
32     //    k set - keycodes are *not* reverse-engineered
33     //    < unset - <Key> sequences *are* interpreted
34     //  The last but one parameter of replace_termcodes() is TRUE so that the
35     //  <lt> sequence is recognised - needed for a real backslash.
36     p_cpo = (char_u *)"Bk";
37     str = replace_termcodes((char_u *)str, &ptr, REPTERM_DO_LT, NULL);
38     p_cpo = cpo_save;
39 
40     if (*ptr != NUL)	// trailing CTRL-V results in nothing
41     {
42 	/*
43 	 * Add the string to the input stream.
44 	 * Can't use add_to_input_buf() here, we now have K_SPECIAL bytes.
45 	 *
46 	 * First clear typed characters from the typeahead buffer, there could
47 	 * be half a mapping there.  Then append to the existing string, so
48 	 * that multiple commands from a client are concatenated.
49 	 */
50 	if (typebuf.tb_maplen < typebuf.tb_len)
51 	    del_typebuf(typebuf.tb_len - typebuf.tb_maplen, typebuf.tb_maplen);
52 	(void)ins_typebuf(str, REMAP_NONE, typebuf.tb_len, TRUE, FALSE);
53 
54 	// Let input_available() know we inserted text in the typeahead
55 	// buffer.
56 	typebuf_was_filled = TRUE;
57     }
58     vim_free((char_u *)ptr);
59 }
60 
61 /*
62  * Evaluate an expression that the client sent to a string.
63  */
64     char_u *
eval_client_expr_to_string(char_u * expr)65 eval_client_expr_to_string(char_u *expr)
66 {
67     char_u	*res;
68     int		save_dbl = debug_break_level;
69     int		save_ro = redir_off;
70     funccal_entry_T funccal_entry;
71     int		did_save_funccal = FALSE;
72 
73     // Evaluate the expression at the toplevel, don't use variables local to
74     // the calling function. Except when in debug mode.
75     if (!debug_mode)
76     {
77 	save_funccal(&funccal_entry);
78 	did_save_funccal = TRUE;
79     }
80 
81      // Disable debugging, otherwise Vim hangs, waiting for "cont" to be
82      // typed.
83     debug_break_level = -1;
84     redir_off = 0;
85     // Do not display error message, otherwise Vim hangs, waiting for "cont"
86     // to be typed.  Do generate errors so that try/catch works.
87     ++emsg_silent;
88 
89     res = eval_to_string(expr, TRUE);
90 
91     debug_break_level = save_dbl;
92     redir_off = save_ro;
93     --emsg_silent;
94     if (emsg_silent < 0)
95 	emsg_silent = 0;
96     if (did_save_funccal)
97 	restore_funccal();
98 
99     // A client can tell us to redraw, but not to display the cursor, so do
100     // that here.
101     setcursor();
102     out_flush_cursor(FALSE, FALSE);
103 
104     return res;
105 }
106 
107 /*
108  * Evaluate a command or expression sent to ourselves.
109  */
110     int
sendToLocalVim(char_u * cmd,int asExpr,char_u ** result)111 sendToLocalVim(char_u *cmd, int asExpr, char_u **result)
112 {
113     if (asExpr)
114     {
115 	char_u *ret;
116 
117 	ret = eval_client_expr_to_string(cmd);
118 	if (result != NULL)
119 	{
120 	    if (ret == NULL)
121 	    {
122 		char	*err = _(e_invexprmsg);
123 		size_t	len = STRLEN(cmd) + STRLEN(err) + 5;
124 		char_u	*msg;
125 
126 		msg = alloc(len);
127 		if (msg != NULL)
128 		    vim_snprintf((char *)msg, len, "%s: \"%s\"", err, cmd);
129 		*result = msg;
130 	    }
131 	    else
132 		*result = ret;
133 	}
134 	else
135 	    vim_free(ret);
136 	return ret == NULL ? -1 : 0;
137     }
138     server_to_input_buf(cmd);
139     return 0;
140 }
141 
142 /*
143  * If conversion is needed, convert "data" from "client_enc" to 'encoding' and
144  * return an allocated string.  Otherwise return "data".
145  * "*tofree" is set to the result when it needs to be freed later.
146  */
147     char_u *
serverConvert(char_u * client_enc UNUSED,char_u * data,char_u ** tofree)148 serverConvert(
149     char_u *client_enc UNUSED,
150     char_u *data,
151     char_u **tofree)
152 {
153     char_u	*res = data;
154 
155     *tofree = NULL;
156     if (client_enc != NULL && p_enc != NULL)
157     {
158 	vimconv_T	vimconv;
159 
160 	vimconv.vc_type = CONV_NONE;
161 	if (convert_setup(&vimconv, client_enc, p_enc) != FAIL
162 					      && vimconv.vc_type != CONV_NONE)
163 	{
164 	    res = string_convert(&vimconv, data, NULL);
165 	    if (res == NULL)
166 		res = data;
167 	    else
168 		*tofree = res;
169 	}
170 	convert_setup(&vimconv, NULL, NULL);
171     }
172     return res;
173 }
174 #endif
175 
176 #if (defined(FEAT_CLIENTSERVER) && !defined(NO_VIM_MAIN)) || defined(PROTO)
177 
178 /*
179  * Common code for the X command server and the Win32 command server.
180  */
181 
182 static char_u *build_drop_cmd(int filec, char **filev, int tabs, int sendReply);
183 
184 /*
185  * Do the client-server stuff, unless "--servername ''" was used.
186  */
187     void
exec_on_server(mparm_T * parmp)188 exec_on_server(mparm_T *parmp)
189 {
190     if (parmp->serverName_arg == NULL || *parmp->serverName_arg != NUL)
191     {
192 # ifdef MSWIN
193 	// Initialise the client/server messaging infrastructure.
194 	serverInitMessaging();
195 # endif
196 
197 	/*
198 	 * When a command server argument was found, execute it.  This may
199 	 * exit Vim when it was successful.  Otherwise it's executed further
200 	 * on.  Remember the encoding used here in "serverStrEnc".
201 	 */
202 	if (parmp->serverArg)
203 	{
204 	    cmdsrv_main(&parmp->argc, parmp->argv,
205 				    parmp->serverName_arg, &parmp->serverStr);
206 	    parmp->serverStrEnc = vim_strsave(p_enc);
207 	}
208 
209 	// If we're still running, get the name to register ourselves.
210 	// On Win32 can register right now, for X11 need to setup the
211 	// clipboard first, it's further down.
212 	parmp->servername = serverMakeName(parmp->serverName_arg,
213 							      parmp->argv[0]);
214 # ifdef MSWIN
215 	if (parmp->servername != NULL)
216 	{
217 	    serverSetName(parmp->servername);
218 	    vim_free(parmp->servername);
219 	}
220 # endif
221     }
222 }
223 
224 /*
225  * Prepare for running as a Vim server.
226  */
227     void
prepare_server(mparm_T * parmp)228 prepare_server(mparm_T *parmp)
229 {
230 # if defined(FEAT_X11)
231     /*
232      * Register for remote command execution with :serversend and --remote
233      * unless there was a -X or a --servername '' on the command line.
234      * Only register nongui-vim's with an explicit --servername argument,
235      * or when compiling with autoservername.
236      * When running as root --servername is also required.
237      */
238     if (X_DISPLAY != NULL && parmp->servername != NULL && (
239 #  if defined(FEAT_AUTOSERVERNAME) || defined(FEAT_GUI)
240 		(
241 #   if defined(FEAT_AUTOSERVERNAME)
242 		    1
243 #   else
244 		    gui.in_use
245 #   endif
246 #   ifdef UNIX
247 		 && getuid() != ROOT_UID
248 #   endif
249 		) ||
250 #  endif
251 		parmp->serverName_arg != NULL))
252     {
253 	(void)serverRegisterName(X_DISPLAY, parmp->servername);
254 	vim_free(parmp->servername);
255 	TIME_MSG("register server name");
256     }
257     else
258 	serverDelayedStartName = parmp->servername;
259 # endif
260 
261     /*
262      * Execute command ourselves if we're here because the send failed (or
263      * else we would have exited above).
264      */
265     if (parmp->serverStr != NULL)
266     {
267 	char_u *p;
268 
269 	server_to_input_buf(serverConvert(parmp->serverStrEnc,
270 						       parmp->serverStr, &p));
271 	vim_free(p);
272     }
273 }
274 
275     static void
cmdsrv_main(int * argc,char ** argv,char_u * serverName_arg,char_u ** serverStr)276 cmdsrv_main(
277     int		*argc,
278     char	**argv,
279     char_u	*serverName_arg,
280     char_u	**serverStr)
281 {
282     char_u	*res;
283     int		i;
284     char_u	*sname;
285     int		ret;
286     int		didone = FALSE;
287     int		exiterr = 0;
288     char	**newArgV = argv + 1;
289     int		newArgC = 1,
290 		Argc = *argc;
291     int		argtype;
292 #define ARGTYPE_OTHER		0
293 #define ARGTYPE_EDIT		1
294 #define ARGTYPE_EDIT_WAIT	2
295 #define ARGTYPE_SEND		3
296     int		silent = FALSE;
297     int		tabs = FALSE;
298 # ifndef FEAT_X11
299     HWND	srv;
300 # else
301     Window	srv;
302 
303     setup_term_clip();
304 # endif
305 
306     sname = serverMakeName(serverName_arg, argv[0]);
307     if (sname == NULL)
308 	return;
309 
310     /*
311      * Execute the command server related arguments and remove them
312      * from the argc/argv array; We may have to return into main()
313      */
314     for (i = 1; i < Argc; i++)
315     {
316 	res = NULL;
317 	if (STRCMP(argv[i], "--") == 0)	// end of option arguments
318 	{
319 	    for (; i < *argc; i++)
320 	    {
321 		*newArgV++ = argv[i];
322 		newArgC++;
323 	    }
324 	    break;
325 	}
326 
327 	if (STRICMP(argv[i], "--remote-send") == 0)
328 	    argtype = ARGTYPE_SEND;
329 	else if (STRNICMP(argv[i], "--remote", 8) == 0)
330 	{
331 	    char	*p = argv[i] + 8;
332 
333 	    argtype = ARGTYPE_EDIT;
334 	    while (*p != NUL)
335 	    {
336 		if (STRNICMP(p, "-wait", 5) == 0)
337 		{
338 		    argtype = ARGTYPE_EDIT_WAIT;
339 		    p += 5;
340 		}
341 		else if (STRNICMP(p, "-silent", 7) == 0)
342 		{
343 		    silent = TRUE;
344 		    p += 7;
345 		}
346 		else if (STRNICMP(p, "-tab", 4) == 0)
347 		{
348 		    tabs = TRUE;
349 		    p += 4;
350 		}
351 		else
352 		{
353 		    argtype = ARGTYPE_OTHER;
354 		    break;
355 		}
356 	    }
357 	}
358 	else
359 	    argtype = ARGTYPE_OTHER;
360 
361 	if (argtype != ARGTYPE_OTHER)
362 	{
363 	    if (i == *argc - 1)
364 		mainerr_arg_missing((char_u *)argv[i]);
365 	    if (argtype == ARGTYPE_SEND)
366 	    {
367 		*serverStr = (char_u *)argv[i + 1];
368 		i++;
369 	    }
370 	    else
371 	    {
372 		*serverStr = build_drop_cmd(*argc - i - 1, argv + i + 1,
373 					  tabs, argtype == ARGTYPE_EDIT_WAIT);
374 		if (*serverStr == NULL)
375 		{
376 		    // Probably out of memory, exit.
377 		    didone = TRUE;
378 		    exiterr = 1;
379 		    break;
380 		}
381 		Argc = i;
382 	    }
383 # ifdef FEAT_X11
384 	    if (xterm_dpy == NULL)
385 	    {
386 		mch_errmsg(_("No display"));
387 		ret = -1;
388 	    }
389 	    else
390 		ret = serverSendToVim(xterm_dpy, sname, *serverStr,
391 						  NULL, &srv, 0, 0, 0, silent);
392 # else
393 	    // Win32 always works?
394 	    ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent);
395 # endif
396 	    if (ret < 0)
397 	    {
398 		if (argtype == ARGTYPE_SEND)
399 		{
400 		    // Failed to send, abort.
401 		    mch_errmsg(_(": Send failed.\n"));
402 		    didone = TRUE;
403 		    exiterr = 1;
404 		}
405 		else if (!silent)
406 		    // Let vim start normally.
407 		    mch_errmsg(_(": Send failed. Trying to execute locally\n"));
408 		break;
409 	    }
410 
411 # ifdef FEAT_GUI_MSWIN
412 	    // Guess that when the server name starts with "g" it's a GUI
413 	    // server, which we can bring to the foreground here.
414 	    // Foreground() in the server doesn't work very well.
415 	    if (argtype != ARGTYPE_SEND && TOUPPER_ASC(*sname) == 'G')
416 		SetForegroundWindow(srv);
417 # endif
418 
419 	    /*
420 	     * For --remote-wait: Wait until the server did edit each
421 	     * file.  Also detect that the server no longer runs.
422 	     */
423 	    if (ret >= 0 && argtype == ARGTYPE_EDIT_WAIT)
424 	    {
425 		int	numFiles = *argc - i - 1;
426 		int	j;
427 		char_u  *done = alloc(numFiles);
428 		char_u  *p;
429 # ifdef FEAT_GUI_MSWIN
430 		NOTIFYICONDATA ni;
431 		int	count = 0;
432 		extern HWND message_window;
433 # endif
434 
435 		if (numFiles > 0 && argv[i + 1][0] == '+')
436 		    // Skip "+cmd" argument, don't wait for it to be edited.
437 		    --numFiles;
438 
439 # ifdef FEAT_GUI_MSWIN
440 		ni.cbSize = sizeof(ni);
441 		ni.hWnd = message_window;
442 		ni.uID = 0;
443 		ni.uFlags = NIF_ICON|NIF_TIP;
444 		ni.hIcon = LoadIcon((HINSTANCE)GetModuleHandle(0), "IDR_VIM");
445 		sprintf(ni.szTip, _("%d of %d edited"), count, numFiles);
446 		Shell_NotifyIcon(NIM_ADD, &ni);
447 # endif
448 
449 		// Wait for all files to unload in remote
450 		vim_memset(done, 0, numFiles);
451 		while (memchr(done, 0, numFiles) != NULL)
452 		{
453 # ifdef MSWIN
454 		    p = serverGetReply(srv, NULL, TRUE, TRUE, 0);
455 		    if (p == NULL)
456 			break;
457 # else
458 		    if (serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0)
459 			break;
460 # endif
461 		    j = atoi((char *)p);
462 		    if (j >= 0 && j < numFiles)
463 		    {
464 # ifdef FEAT_GUI_MSWIN
465 			++count;
466 			sprintf(ni.szTip, _("%d of %d edited"),
467 							     count, numFiles);
468 			Shell_NotifyIcon(NIM_MODIFY, &ni);
469 # endif
470 			done[j] = 1;
471 		    }
472 		}
473 # ifdef FEAT_GUI_MSWIN
474 		Shell_NotifyIcon(NIM_DELETE, &ni);
475 # endif
476 		vim_free(done);
477 	    }
478 	}
479 	else if (STRICMP(argv[i], "--remote-expr") == 0)
480 	{
481 	    if (i == *argc - 1)
482 		mainerr_arg_missing((char_u *)argv[i]);
483 # ifdef MSWIN
484 	    // Win32 always works?
485 	    if (serverSendToVim(sname, (char_u *)argv[i + 1],
486 						  &res, NULL, 1, 0, FALSE) < 0)
487 # else
488 	    if (xterm_dpy == NULL)
489 		mch_errmsg(_("No display: Send expression failed.\n"));
490 	    else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1],
491 					       &res, NULL, 1, 0, 1, FALSE) < 0)
492 # endif
493 	    {
494 		if (res != NULL && *res != NUL)
495 		{
496 		    // Output error from remote
497 		    mch_errmsg((char *)res);
498 		    VIM_CLEAR(res);
499 		}
500 		mch_errmsg(_(": Send expression failed.\n"));
501 	    }
502 	}
503 	else if (STRICMP(argv[i], "--serverlist") == 0)
504 	{
505 # ifdef MSWIN
506 	    // Win32 always works?
507 	    res = serverGetVimNames();
508 # else
509 	    if (xterm_dpy != NULL)
510 		res = serverGetVimNames(xterm_dpy);
511 # endif
512 	    if (did_emsg)
513 		mch_errmsg("\n");
514 	}
515 	else if (STRICMP(argv[i], "--servername") == 0)
516 	{
517 	    // Already processed. Take it out of the command line
518 	    i++;
519 	    continue;
520 	}
521 	else
522 	{
523 	    *newArgV++ = argv[i];
524 	    newArgC++;
525 	    continue;
526 	}
527 	didone = TRUE;
528 	if (res != NULL && *res != NUL)
529 	{
530 	    mch_msg((char *)res);
531 	    if (res[STRLEN(res) - 1] != '\n')
532 		mch_msg("\n");
533 	}
534 	vim_free(res);
535     }
536 
537     if (didone)
538     {
539 	display_errors();	// display any collected messages
540 	exit(exiterr);	// Mission accomplished - get out
541     }
542 
543     // Return back into main()
544     *argc = newArgC;
545     vim_free(sname);
546 }
547 
548 /*
549  * Build a ":drop" command to send to a Vim server.
550  */
551     static char_u *
build_drop_cmd(int filec,char ** filev,int tabs,int sendReply)552 build_drop_cmd(
553     int		filec,
554     char	**filev,
555     int		tabs,		// Use ":tab drop" instead of ":drop".
556     int		sendReply)
557 {
558     garray_T	ga;
559     int		i;
560     char_u	*inicmd = NULL;
561     char_u	*p;
562     char_u	*cdp;
563     char_u	*cwd;
564 
565     if (filec > 0 && filev[0][0] == '+')
566     {
567 	inicmd = (char_u *)filev[0] + 1;
568 	filev++;
569 	filec--;
570     }
571     // Check if we have at least one argument.
572     if (filec <= 0)
573 	mainerr_arg_missing((char_u *)filev[-1]);
574 
575     // Temporarily cd to the current directory to handle relative file names.
576     cwd = alloc(MAXPATHL);
577     if (cwd == NULL)
578 	return NULL;
579     if (mch_dirname(cwd, MAXPATHL) != OK)
580     {
581 	vim_free(cwd);
582 	return NULL;
583     }
584     cdp = vim_strsave_escaped_ext(cwd,
585 #ifdef BACKSLASH_IN_FILENAME
586 	    (char_u *)"",  // rem_backslash() will tell what chars to escape
587 #else
588 	    PATH_ESC_CHARS,
589 #endif
590 	    '\\', TRUE);
591     vim_free(cwd);
592     if (cdp == NULL)
593 	return NULL;
594     ga_init2(&ga, 1, 100);
595     ga_concat(&ga, (char_u *)"<C-\\><C-N>:cd ");
596     ga_concat(&ga, cdp);
597 
598     // Call inputsave() so that a prompt for an encryption key works.
599     ga_concat(&ga, (char_u *)
600 		       "<CR>:if exists('*inputsave')|call inputsave()|endif|");
601     if (tabs)
602 	ga_concat(&ga, (char_u *)"tab ");
603     ga_concat(&ga, (char_u *)"drop");
604     for (i = 0; i < filec; i++)
605     {
606 	// On Unix the shell has already expanded the wildcards, don't want to
607 	// do it again in the Vim server.  On MS-Windows only escape
608 	// non-wildcard characters.
609 	p = vim_strsave_escaped((char_u *)filev[i],
610 #ifdef UNIX
611 		PATH_ESC_CHARS
612 #else
613 		(char_u *)" \t%#"
614 #endif
615 		);
616 	if (p == NULL)
617 	{
618 	    vim_free(ga.ga_data);
619 	    return NULL;
620 	}
621 	ga_concat(&ga, (char_u *)" ");
622 	ga_concat(&ga, p);
623 	vim_free(p);
624     }
625     ga_concat(&ga, (char_u *)
626 		  "|if exists('*inputrestore')|call inputrestore()|endif<CR>");
627 
628     // The :drop commands goes to Insert mode when 'insertmode' is set, use
629     // CTRL-\ CTRL-N again.
630     ga_concat(&ga, (char_u *)"<C-\\><C-N>");
631 
632     // Switch back to the correct current directory (prior to temporary path
633     // switch) unless 'autochdir' is set, in which case it will already be
634     // correct after the :drop command. With line breaks and spaces:
635     //  if !exists('+acd') || !&acd
636     //    if haslocaldir()
637     //	    cd -
638     //      lcd -
639     //    elseif getcwd() ==# 'current path'
640     //      cd -
641     //    endif
642     //  endif
643     ga_concat(&ga, (char_u *)":if !exists('+acd')||!&acd|if haslocaldir()|");
644     ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd() ==# '");
645     ga_concat(&ga, cdp);
646     ga_concat(&ga, (char_u *)"'|cd -|endif|endif<CR>");
647     vim_free(cdp);
648 
649     if (sendReply)
650 	ga_concat(&ga, (char_u *)":call SetupRemoteReplies()<CR>");
651     ga_concat(&ga, (char_u *)":");
652     if (inicmd != NULL)
653     {
654 	// Can't use <CR> after "inicmd", because an "startinsert" would cause
655 	// the following commands to be inserted as text.  Use a "|",
656 	// hopefully "inicmd" does allow this...
657 	ga_concat(&ga, inicmd);
658 	ga_concat(&ga, (char_u *)"|");
659     }
660     // Bring the window to the foreground, goto Insert mode when 'im' set and
661     // clear command line.
662     ga_concat(&ga, (char_u *)"cal foreground()|if &im|star|en|redr|f<CR>");
663     ga_append(&ga, NUL);
664     return ga.ga_data;
665 }
666 
667 /*
668  * Make our basic server name: use the specified "arg" if given, otherwise use
669  * the tail of the command "cmd" we were started with.
670  * Return the name in allocated memory.  This doesn't include a serial number.
671  */
672     static char_u *
serverMakeName(char_u * arg,char * cmd)673 serverMakeName(char_u *arg, char *cmd)
674 {
675     char_u *p;
676 
677     if (arg != NULL && *arg != NUL)
678 	p = vim_strsave_up(arg);
679     else
680     {
681 	p = vim_strsave_up(gettail((char_u *)cmd));
682 	// Remove .exe or .bat from the name.
683 	if (p != NULL && vim_strchr(p, '.') != NULL)
684 	    *vim_strchr(p, '.') = NUL;
685     }
686     return p;
687 }
688 #endif // FEAT_CLIENTSERVER
689 
690 #if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
691     static void
make_connection(void)692 make_connection(void)
693 {
694     if (X_DISPLAY == NULL
695 # ifdef FEAT_GUI
696 	    && !gui.in_use
697 # endif
698 	    )
699     {
700 	x_force_connect = TRUE;
701 	setup_term_clip();
702 	x_force_connect = FALSE;
703     }
704 }
705 
706     static int
check_connection(void)707 check_connection(void)
708 {
709     make_connection();
710     if (X_DISPLAY == NULL)
711     {
712 	emsg(_("E240: No connection to the X server"));
713 	return FAIL;
714     }
715     return OK;
716 }
717 #endif
718 
719 #ifdef FEAT_CLIENTSERVER
720     static void
remote_common(typval_T * argvars,typval_T * rettv,int expr)721 remote_common(typval_T *argvars, typval_T *rettv, int expr)
722 {
723     char_u	*server_name;
724     char_u	*keys;
725     char_u	*r = NULL;
726     char_u	buf[NUMBUFLEN];
727     int		timeout = 0;
728 # ifdef MSWIN
729     HWND	w;
730 # else
731     Window	w;
732 # endif
733 
734     if (check_restricted() || check_secure())
735 	return;
736 
737 # ifdef FEAT_X11
738     if (check_connection() == FAIL)
739 	return;
740 # endif
741     if (argvars[2].v_type != VAR_UNKNOWN
742 	    && argvars[3].v_type != VAR_UNKNOWN)
743 	timeout = tv_get_number(&argvars[3]);
744 
745     server_name = tv_get_string_chk(&argvars[0]);
746     if (server_name == NULL)
747 	return;		// type error; errmsg already given
748     keys = tv_get_string_buf(&argvars[1], buf);
749 # ifdef MSWIN
750     if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0)
751 # else
752     if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
753 								  0, TRUE) < 0)
754 # endif
755     {
756 	if (r != NULL)
757 	{
758 	    emsg((char *)r);	// sending worked but evaluation failed
759 	    vim_free(r);
760 	}
761 	else
762 	    semsg(_("E241: Unable to send to %s"), server_name);
763 	return;
764     }
765 
766     rettv->vval.v_string = r;
767 
768     if (argvars[2].v_type != VAR_UNKNOWN)
769     {
770 	dictitem_T	v;
771 	char_u		str[30];
772 	char_u		*idvar;
773 
774 	idvar = tv_get_string_chk(&argvars[2]);
775 	if (idvar != NULL && *idvar != NUL)
776 	{
777 	    sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
778 	    v.di_tv.v_type = VAR_STRING;
779 	    v.di_tv.vval.v_string = vim_strsave(str);
780 	    set_var(idvar, &v.di_tv, FALSE);
781 	    vim_free(v.di_tv.vval.v_string);
782 	}
783     }
784 }
785 #endif
786 
787 #if defined(FEAT_EVAL) || defined(PROTO)
788 /*
789  * "remote_expr()" function
790  */
791     void
f_remote_expr(typval_T * argvars UNUSED,typval_T * rettv)792 f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv)
793 {
794     rettv->v_type = VAR_STRING;
795     rettv->vval.v_string = NULL;
796 
797     if (in_vim9script()
798 	    && (check_for_string_arg(argvars, 0) == FAIL
799 		|| check_for_string_arg(argvars, 1) == FAIL
800 		|| check_for_opt_string_arg(argvars, 2) == FAIL
801 		|| (argvars[2].v_type != VAR_UNKNOWN
802 		    && check_for_opt_number_arg(argvars, 3) == FAIL)))
803 	return;
804 
805 #ifdef FEAT_CLIENTSERVER
806     remote_common(argvars, rettv, TRUE);
807 #endif
808 }
809 
810 /*
811  * "remote_foreground()" function
812  */
813     void
f_remote_foreground(typval_T * argvars UNUSED,typval_T * rettv UNUSED)814 f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
815 {
816 #ifdef FEAT_CLIENTSERVER
817     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
818 	return;
819 
820 # ifdef MSWIN
821     // On Win32 it's done in this application.
822     {
823 	char_u	*server_name = tv_get_string_chk(&argvars[0]);
824 
825 	if (server_name != NULL)
826 	    serverForeground(server_name);
827     }
828 # else
829     // Send a foreground() expression to the server.
830     argvars[1].v_type = VAR_STRING;
831     argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()");
832     argvars[2].v_type = VAR_UNKNOWN;
833     rettv->v_type = VAR_STRING;
834     rettv->vval.v_string = NULL;
835     remote_common(argvars, rettv, TRUE);
836     vim_free(argvars[1].vval.v_string);
837 # endif
838 #endif
839 }
840 
841     void
f_remote_peek(typval_T * argvars UNUSED,typval_T * rettv)842 f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv)
843 {
844 #ifdef FEAT_CLIENTSERVER
845     dictitem_T	v;
846     char_u	*s = NULL;
847 # ifdef MSWIN
848     long_u	n = 0;
849 # endif
850     char_u	*serverid;
851 
852     rettv->vval.v_number = -1;
853     if (check_restricted() || check_secure())
854 	return;
855 
856     if (in_vim9script()
857 	    && (check_for_string_arg(argvars, 0) == FAIL
858 		|| check_for_opt_string_arg(argvars, 1) == FAIL))
859 	return;
860 
861     serverid = tv_get_string_chk(&argvars[0]);
862     if (serverid == NULL)
863 	return;		// type error; errmsg already given
864 # ifdef MSWIN
865     sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n);
866     if (n == 0)
867 	rettv->vval.v_number = -1;
868     else
869     {
870 	s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0);
871 	rettv->vval.v_number = (s != NULL);
872     }
873 # else
874     if (check_connection() == FAIL)
875 	return;
876 
877     rettv->vval.v_number = serverPeekReply(X_DISPLAY,
878 						serverStrToWin(serverid), &s);
879 # endif
880 
881     if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0)
882     {
883 	char_u		*retvar;
884 
885 	v.di_tv.v_type = VAR_STRING;
886 	v.di_tv.vval.v_string = vim_strsave(s);
887 	retvar = tv_get_string_chk(&argvars[1]);
888 	if (retvar != NULL)
889 	    set_var(retvar, &v.di_tv, FALSE);
890 	vim_free(v.di_tv.vval.v_string);
891     }
892 #else
893     rettv->vval.v_number = -1;
894 #endif
895 }
896 
897     void
f_remote_read(typval_T * argvars UNUSED,typval_T * rettv)898 f_remote_read(typval_T *argvars UNUSED, typval_T *rettv)
899 {
900     char_u	*r = NULL;
901 
902 #ifdef FEAT_CLIENTSERVER
903     char_u	*serverid;
904 
905     if (in_vim9script()
906 	    && (check_for_string_arg(argvars, 0) == FAIL
907 		|| check_for_opt_number_arg(argvars, 1) == FAIL))
908 	return;
909 
910     serverid = tv_get_string_chk(&argvars[0]);
911     if (serverid != NULL && !check_restricted() && !check_secure())
912     {
913 	int timeout = 0;
914 # ifdef MSWIN
915 	// The server's HWND is encoded in the 'id' parameter
916 	long_u		n = 0;
917 # endif
918 
919 	if (argvars[1].v_type != VAR_UNKNOWN)
920 	    timeout = tv_get_number(&argvars[1]);
921 
922 # ifdef MSWIN
923 	sscanf((char *)serverid, SCANF_HEX_LONG_U, &n);
924 	if (n != 0)
925 	    r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout);
926 	if (r == NULL)
927 # else
928 	if (check_connection() == FAIL
929 		|| serverReadReply(X_DISPLAY, serverStrToWin(serverid),
930 						       &r, FALSE, timeout) < 0)
931 # endif
932 	    emsg(_("E277: Unable to read a server reply"));
933     }
934 #endif
935     rettv->v_type = VAR_STRING;
936     rettv->vval.v_string = r;
937 }
938 
939 /*
940  * "remote_send()" function
941  */
942     void
f_remote_send(typval_T * argvars UNUSED,typval_T * rettv)943 f_remote_send(typval_T *argvars UNUSED, typval_T *rettv)
944 {
945     rettv->v_type = VAR_STRING;
946     rettv->vval.v_string = NULL;
947 
948     if (in_vim9script()
949 	    && (check_for_string_arg(argvars, 0) == FAIL
950 		|| check_for_string_arg(argvars, 1) == FAIL
951 		|| check_for_opt_string_arg(argvars, 2) == FAIL))
952 	return;
953 
954 #ifdef FEAT_CLIENTSERVER
955     remote_common(argvars, rettv, FALSE);
956 #endif
957 }
958 
959 /*
960  * "remote_startserver()" function
961  */
962     void
f_remote_startserver(typval_T * argvars UNUSED,typval_T * rettv UNUSED)963 f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
964 {
965 #ifdef FEAT_CLIENTSERVER
966     char_u	*server;
967 
968     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
969 	return;
970 
971     server = tv_get_string_chk(&argvars[0]);
972     if (server == NULL)
973 	return;		// type error; errmsg already given
974     if (serverName != NULL)
975 	emsg(_("E941: already started a server"));
976     else
977     {
978 # ifdef FEAT_X11
979 	if (check_connection() == OK)
980 	    serverRegisterName(X_DISPLAY, server);
981 # else
982 	serverSetName(server);
983 # endif
984     }
985 #else
986     emsg(_("E942: +clientserver feature not available"));
987 #endif
988 }
989 
990     void
f_server2client(typval_T * argvars UNUSED,typval_T * rettv)991 f_server2client(typval_T *argvars UNUSED, typval_T *rettv)
992 {
993 #ifdef FEAT_CLIENTSERVER
994     char_u	buf[NUMBUFLEN];
995     char_u	*server;
996     char_u	*reply;
997 
998     rettv->vval.v_number = -1;
999     if (check_restricted() || check_secure())
1000 	return;
1001 
1002     if (in_vim9script()
1003 	    && (check_for_string_arg(argvars, 0) == FAIL
1004 		|| check_for_string_arg(argvars, 1) == FAIL))
1005 	return;
1006 
1007     server = tv_get_string_chk(&argvars[0]);
1008     reply = tv_get_string_buf_chk(&argvars[1], buf);
1009     if (server == NULL || reply == NULL)
1010 	return;
1011 
1012 # ifdef FEAT_X11
1013     if (check_connection() == FAIL)
1014 	return;
1015 # endif
1016 
1017     if (serverSendReply(server, reply) < 0)
1018     {
1019 	emsg(_("E258: Unable to send to client"));
1020 	return;
1021     }
1022     rettv->vval.v_number = 0;
1023 #else
1024     rettv->vval.v_number = -1;
1025 #endif
1026 }
1027 
1028     void
f_serverlist(typval_T * argvars UNUSED,typval_T * rettv)1029 f_serverlist(typval_T *argvars UNUSED, typval_T *rettv)
1030 {
1031     char_u	*r = NULL;
1032 
1033 #ifdef FEAT_CLIENTSERVER
1034 # ifdef MSWIN
1035     r = serverGetVimNames();
1036 # else
1037     make_connection();
1038     if (X_DISPLAY != NULL)
1039 	r = serverGetVimNames(X_DISPLAY);
1040 # endif
1041 #endif
1042     rettv->v_type = VAR_STRING;
1043     rettv->vval.v_string = r;
1044 }
1045 #endif
1046