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