blob: b19cd447fa1035b6e446a012782d3cbd22f1e9ef [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";
zeertzjq7e0bae02023-08-11 23:15:38 +020037 str = replace_termcodes(str, &ptr, 0, 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;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000160 if (client_enc == NULL || p_enc == NULL)
161 return res;
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200162
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000163 vimconv_T vimconv;
164
165 vimconv.vc_type = CONV_NONE;
166 if (convert_setup(&vimconv, client_enc, p_enc) != FAIL
167 && vimconv.vc_type != CONV_NONE)
168 {
169 res = string_convert(&vimconv, data, NULL);
170 if (res == NULL)
171 res = data;
172 else
173 *tofree = res;
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200174 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000175 convert_setup(&vimconv, NULL, NULL);
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200176 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;
Christian Brabandtcc979b42024-01-23 21:13:58 +0100569 // reset wildignore temporarily
570 const char *wig[] =
571 { "<CR><C-\\><C-N>:let g:_wig=&wig|set wig=",
572 "<C-\\><C-N>:let &wig=g:_wig|unlet g:_wig<CR>"};
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200573
574 if (filec > 0 && filev[0][0] == '+')
575 {
576 inicmd = (char_u *)filev[0] + 1;
577 filev++;
578 filec--;
579 }
580 // Check if we have at least one argument.
581 if (filec <= 0)
582 mainerr_arg_missing((char_u *)filev[-1]);
583
584 // Temporarily cd to the current directory to handle relative file names.
585 cwd = alloc(MAXPATHL);
586 if (cwd == NULL)
587 return NULL;
588 if (mch_dirname(cwd, MAXPATHL) != OK)
589 {
590 vim_free(cwd);
591 return NULL;
592 }
593 cdp = vim_strsave_escaped_ext(cwd,
594#ifdef BACKSLASH_IN_FILENAME
595 (char_u *)"", // rem_backslash() will tell what chars to escape
596#else
597 PATH_ESC_CHARS,
598#endif
599 '\\', TRUE);
600 vim_free(cwd);
601 if (cdp == NULL)
602 return NULL;
603 ga_init2(&ga, 1, 100);
604 ga_concat(&ga, (char_u *)"<C-\\><C-N>:cd ");
605 ga_concat(&ga, cdp);
Christian Brabandtcc979b42024-01-23 21:13:58 +0100606 // reset wildignorecase temporarily
607 ga_concat(&ga, (char_u *)wig[0]);
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200608
609 // Call inputsave() so that a prompt for an encryption key works.
Bram Moolenaara9a47d12020-08-09 21:45:52 +0200610 ga_concat(&ga, (char_u *)
Christian Brabandt349f5cd2024-04-19 15:22:33 +0200611 "<CR><C-\\><C-N>:if exists('*inputsave')|call inputsave()|endif|");
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200612 if (tabs)
613 ga_concat(&ga, (char_u *)"tab ");
614 ga_concat(&ga, (char_u *)"drop");
615 for (i = 0; i < filec; i++)
616 {
617 // On Unix the shell has already expanded the wildcards, don't want to
618 // do it again in the Vim server. On MS-Windows only escape
619 // non-wildcard characters.
620 p = vim_strsave_escaped((char_u *)filev[i],
621#ifdef UNIX
622 PATH_ESC_CHARS
623#else
624 (char_u *)" \t%#"
625#endif
626 );
627 if (p == NULL)
628 {
629 vim_free(ga.ga_data);
630 return NULL;
631 }
632 ga_concat(&ga, (char_u *)" ");
633 ga_concat(&ga, p);
634 vim_free(p);
635 }
Bram Moolenaara9a47d12020-08-09 21:45:52 +0200636 ga_concat(&ga, (char_u *)
637 "|if exists('*inputrestore')|call inputrestore()|endif<CR>");
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200638
639 // The :drop commands goes to Insert mode when 'insertmode' is set, use
640 // CTRL-\ CTRL-N again.
641 ga_concat(&ga, (char_u *)"<C-\\><C-N>");
642
643 // Switch back to the correct current directory (prior to temporary path
644 // switch) unless 'autochdir' is set, in which case it will already be
645 // correct after the :drop command. With line breaks and spaces:
646 // if !exists('+acd') || !&acd
647 // if haslocaldir()
648 // cd -
649 // lcd -
650 // elseif getcwd() ==# 'current path'
651 // cd -
652 // endif
653 // endif
654 ga_concat(&ga, (char_u *)":if !exists('+acd')||!&acd|if haslocaldir()|");
Christian Brabandt349f5cd2024-04-19 15:22:33 +0200655#ifdef MSWIN
656 // in case :set shellslash is set, need to normalize the directory separators
657 // '/' is not valid in a filename so replacing '/' by '\\' should be safe
658 ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd()->tr('/','\\') ==# '");
659#else
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200660 ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd() ==# '");
Christian Brabandt349f5cd2024-04-19 15:22:33 +0200661#endif
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200662 ga_concat(&ga, cdp);
663 ga_concat(&ga, (char_u *)"'|cd -|endif|endif<CR>");
664 vim_free(cdp);
Christian Brabandtcc979b42024-01-23 21:13:58 +0100665 // reset wildignorecase
666 ga_concat(&ga, (char_u *)wig[1]);
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200667
668 if (sendReply)
669 ga_concat(&ga, (char_u *)":call SetupRemoteReplies()<CR>");
670 ga_concat(&ga, (char_u *)":");
671 if (inicmd != NULL)
672 {
Bram Moolenaar54969f42022-02-07 13:56:44 +0000673 // Can't use <CR> after "inicmd", because a "startinsert" would cause
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200674 // the following commands to be inserted as text. Use a "|",
675 // hopefully "inicmd" does allow this...
676 ga_concat(&ga, inicmd);
677 ga_concat(&ga, (char_u *)"|");
678 }
679 // Bring the window to the foreground, goto Insert mode when 'im' set and
680 // clear command line.
681 ga_concat(&ga, (char_u *)"cal foreground()|if &im|star|en|redr|f<CR>");
682 ga_append(&ga, NUL);
683 return ga.ga_data;
684}
685
686/*
687 * Make our basic server name: use the specified "arg" if given, otherwise use
688 * the tail of the command "cmd" we were started with.
689 * Return the name in allocated memory. This doesn't include a serial number.
690 */
691 static char_u *
692serverMakeName(char_u *arg, char *cmd)
693{
694 char_u *p;
695
696 if (arg != NULL && *arg != NUL)
697 p = vim_strsave_up(arg);
698 else
699 {
700 p = vim_strsave_up(gettail((char_u *)cmd));
701 // Remove .exe or .bat from the name.
702 if (p != NULL && vim_strchr(p, '.') != NULL)
703 *vim_strchr(p, '.') = NUL;
704 }
705 return p;
706}
707#endif // FEAT_CLIENTSERVER
708
709#if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
710 static void
711make_connection(void)
712{
713 if (X_DISPLAY == NULL
714# ifdef FEAT_GUI
715 && !gui.in_use
716# endif
717 )
718 {
719 x_force_connect = TRUE;
720 setup_term_clip();
721 x_force_connect = FALSE;
722 }
723}
724
725 static int
726check_connection(void)
727{
728 make_connection();
729 if (X_DISPLAY == NULL)
730 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +0000731 emsg(_(e_no_connection_to_x_server));
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200732 return FAIL;
733 }
734 return OK;
735}
736#endif
737
738#ifdef FEAT_CLIENTSERVER
739 static void
740remote_common(typval_T *argvars, typval_T *rettv, int expr)
741{
742 char_u *server_name;
743 char_u *keys;
744 char_u *r = NULL;
745 char_u buf[NUMBUFLEN];
746 int timeout = 0;
747# ifdef MSWIN
748 HWND w;
749# else
750 Window w;
751# endif
752
753 if (check_restricted() || check_secure())
754 return;
755
756# ifdef FEAT_X11
757 if (check_connection() == FAIL)
758 return;
759# endif
760 if (argvars[2].v_type != VAR_UNKNOWN
761 && argvars[3].v_type != VAR_UNKNOWN)
762 timeout = tv_get_number(&argvars[3]);
763
764 server_name = tv_get_string_chk(&argvars[0]);
765 if (server_name == NULL)
766 return; // type error; errmsg already given
767 keys = tv_get_string_buf(&argvars[1], buf);
768# ifdef MSWIN
769 if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0)
770# else
771 if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
772 0, TRUE) < 0)
773# endif
774 {
775 if (r != NULL)
776 {
777 emsg((char *)r); // sending worked but evaluation failed
778 vim_free(r);
779 }
780 else
Bram Moolenaarcbadefe2022-01-01 19:33:50 +0000781 semsg(_(e_unable_to_send_to_str), server_name);
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200782 return;
783 }
784
785 rettv->vval.v_string = r;
786
787 if (argvars[2].v_type != VAR_UNKNOWN)
788 {
789 dictitem_T v;
790 char_u str[30];
791 char_u *idvar;
792
793 idvar = tv_get_string_chk(&argvars[2]);
794 if (idvar != NULL && *idvar != NUL)
795 {
796 sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
797 v.di_tv.v_type = VAR_STRING;
798 v.di_tv.vval.v_string = vim_strsave(str);
799 set_var(idvar, &v.di_tv, FALSE);
800 vim_free(v.di_tv.vval.v_string);
801 }
802 }
803}
804#endif
805
806#if defined(FEAT_EVAL) || defined(PROTO)
807/*
808 * "remote_expr()" function
809 */
810 void
811f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv)
812{
813 rettv->v_type = VAR_STRING;
814 rettv->vval.v_string = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200815
Bram Moolenaare18acb02022-03-21 20:40:35 +0000816#ifdef FEAT_CLIENTSERVER
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200817 if (in_vim9script()
818 && (check_for_string_arg(argvars, 0) == FAIL
819 || check_for_string_arg(argvars, 1) == FAIL
820 || check_for_opt_string_arg(argvars, 2) == FAIL
821 || (argvars[2].v_type != VAR_UNKNOWN
822 && check_for_opt_number_arg(argvars, 3) == FAIL)))
823 return;
824
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200825 remote_common(argvars, rettv, TRUE);
826#endif
827}
828
829/*
830 * "remote_foreground()" function
831 */
832 void
833f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
834{
835#ifdef FEAT_CLIENTSERVER
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200836 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
837 return;
838
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200839# ifdef MSWIN
840 // On Win32 it's done in this application.
841 {
842 char_u *server_name = tv_get_string_chk(&argvars[0]);
843
844 if (server_name != NULL)
845 serverForeground(server_name);
846 }
847# else
848 // Send a foreground() expression to the server.
849 argvars[1].v_type = VAR_STRING;
850 argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()");
851 argvars[2].v_type = VAR_UNKNOWN;
852 rettv->v_type = VAR_STRING;
853 rettv->vval.v_string = NULL;
854 remote_common(argvars, rettv, TRUE);
855 vim_free(argvars[1].vval.v_string);
856# endif
857#endif
858}
859
860 void
861f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv)
862{
863#ifdef FEAT_CLIENTSERVER
864 dictitem_T v;
865 char_u *s = NULL;
866# ifdef MSWIN
867 long_u n = 0;
868# endif
869 char_u *serverid;
870
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200871 rettv->vval.v_number = -1;
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200872 if (check_restricted() || check_secure())
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200873 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200874
875 if (in_vim9script()
876 && (check_for_string_arg(argvars, 0) == FAIL
877 || check_for_opt_string_arg(argvars, 1) == FAIL))
878 return;
879
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200880 serverid = tv_get_string_chk(&argvars[0]);
881 if (serverid == NULL)
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200882 return; // type error; errmsg already given
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200883# ifdef MSWIN
884 sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n);
885 if (n == 0)
886 rettv->vval.v_number = -1;
887 else
888 {
889 s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0);
890 rettv->vval.v_number = (s != NULL);
891 }
892# else
893 if (check_connection() == FAIL)
894 return;
895
896 rettv->vval.v_number = serverPeekReply(X_DISPLAY,
897 serverStrToWin(serverid), &s);
898# endif
899
900 if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0)
901 {
902 char_u *retvar;
903
904 v.di_tv.v_type = VAR_STRING;
905 v.di_tv.vval.v_string = vim_strsave(s);
906 retvar = tv_get_string_chk(&argvars[1]);
907 if (retvar != NULL)
908 set_var(retvar, &v.di_tv, FALSE);
909 vim_free(v.di_tv.vval.v_string);
910 }
911#else
912 rettv->vval.v_number = -1;
913#endif
914}
915
916 void
917f_remote_read(typval_T *argvars UNUSED, typval_T *rettv)
918{
919 char_u *r = NULL;
920
921#ifdef FEAT_CLIENTSERVER
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200922 char_u *serverid;
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200923
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200924 if (in_vim9script()
925 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200926 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200927 return;
928
929 serverid = tv_get_string_chk(&argvars[0]);
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200930 if (serverid != NULL && !check_restricted() && !check_secure())
931 {
932 int timeout = 0;
933# ifdef MSWIN
934 // The server's HWND is encoded in the 'id' parameter
935 long_u n = 0;
936# endif
937
938 if (argvars[1].v_type != VAR_UNKNOWN)
939 timeout = tv_get_number(&argvars[1]);
940
941# ifdef MSWIN
942 sscanf((char *)serverid, SCANF_HEX_LONG_U, &n);
943 if (n != 0)
944 r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout);
945 if (r == NULL)
946# else
947 if (check_connection() == FAIL
948 || serverReadReply(X_DISPLAY, serverStrToWin(serverid),
949 &r, FALSE, timeout) < 0)
950# endif
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000951 emsg(_(e_unable_to_read_server_reply));
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200952 }
953#endif
954 rettv->v_type = VAR_STRING;
955 rettv->vval.v_string = r;
956}
957
958/*
959 * "remote_send()" function
960 */
961 void
962f_remote_send(typval_T *argvars UNUSED, typval_T *rettv)
963{
964 rettv->v_type = VAR_STRING;
965 rettv->vval.v_string = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200966
Bram Moolenaare18acb02022-03-21 20:40:35 +0000967#ifdef FEAT_CLIENTSERVER
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200968 if (in_vim9script()
969 && (check_for_string_arg(argvars, 0) == FAIL
970 || check_for_string_arg(argvars, 1) == FAIL
971 || check_for_opt_string_arg(argvars, 2) == FAIL))
972 return;
973
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200974 remote_common(argvars, rettv, FALSE);
975#endif
976}
977
978/*
979 * "remote_startserver()" function
980 */
981 void
982f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
983{
984#ifdef FEAT_CLIENTSERVER
h-east17b69512023-05-01 22:36:56 +0100985 if (check_for_nonempty_string_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200986 return;
987
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200988 if (serverName != NULL)
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200989 {
h-east17b69512023-05-01 22:36:56 +0100990 emsg(_(e_already_started_server));
991 return;
Bram Moolenaarf87a0402020-04-05 20:21:03 +0200992 }
h-east17b69512023-05-01 22:36:56 +0100993
994 char_u *server = tv_get_string_chk(&argvars[0]);
995# ifdef FEAT_X11
996 if (check_connection() == OK)
997 serverRegisterName(X_DISPLAY, server);
998# else
999 serverSetName(server);
1000# endif
1001
Bram Moolenaarf87a0402020-04-05 20:21:03 +02001002#else
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001003 emsg(_(e_clientserver_feature_not_available));
Bram Moolenaarf87a0402020-04-05 20:21:03 +02001004#endif
1005}
1006
1007 void
1008f_server2client(typval_T *argvars UNUSED, typval_T *rettv)
1009{
1010#ifdef FEAT_CLIENTSERVER
1011 char_u buf[NUMBUFLEN];
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001012 char_u *server;
1013 char_u *reply;
Bram Moolenaarf87a0402020-04-05 20:21:03 +02001014
1015 rettv->vval.v_number = -1;
Bram Moolenaarf87a0402020-04-05 20:21:03 +02001016 if (check_restricted() || check_secure())
1017 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001018
1019 if (in_vim9script()
1020 && (check_for_string_arg(argvars, 0) == FAIL
1021 || check_for_string_arg(argvars, 1) == FAIL))
1022 return;
1023
1024 server = tv_get_string_chk(&argvars[0]);
1025 reply = tv_get_string_buf_chk(&argvars[1], buf);
1026 if (server == NULL || reply == NULL)
1027 return;
1028
Bram Moolenaarf87a0402020-04-05 20:21:03 +02001029# ifdef FEAT_X11
1030 if (check_connection() == FAIL)
1031 return;
1032# endif
1033
1034 if (serverSendReply(server, reply) < 0)
1035 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00001036 emsg(_(e_unable_to_send_to_client));
Bram Moolenaarf87a0402020-04-05 20:21:03 +02001037 return;
1038 }
1039 rettv->vval.v_number = 0;
1040#else
1041 rettv->vval.v_number = -1;
1042#endif
1043}
1044
1045 void
1046f_serverlist(typval_T *argvars UNUSED, typval_T *rettv)
1047{
1048 char_u *r = NULL;
1049
1050#ifdef FEAT_CLIENTSERVER
1051# ifdef MSWIN
1052 r = serverGetVimNames();
1053# else
1054 make_connection();
1055 if (X_DISPLAY != NULL)
1056 r = serverGetVimNames(X_DISPLAY);
1057# endif
1058#endif
1059 rettv->v_type = VAR_STRING;
1060 rettv->vval.v_string = r;
1061}
1062#endif