Mercurial > vim
comparison src/clientserver.c @ 19920:5e41b2e63c73 v8.2.0516
patch 8.2.0516: client-server code is spread out
Commit: https://github.com/vim/vim/commit/f87a0400fd81862c33d6ad2291a56e178db7dddd
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Apr 5 20:21:03 2020 +0200
patch 8.2.0516: client-server code is spread out
Problem: Client-server code is spread out.
Solution: Move client-server code to a new file. (Yegappan Lakshmanan,
closes #5885)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 05 Apr 2020 20:30:35 +0200 |
parents | |
children | 72b437855299 |
comparison
equal
deleted
inserted
replaced
19919:b4b102849236 | 19920:5e41b2e63c73 |
---|---|
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 | |
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 * | |
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, NULL, 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 | |
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 * | |
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 | |
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 | |
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 | |
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 } | |
477 } | |
478 else if (STRICMP(argv[i], "--remote-expr") == 0) | |
479 { | |
480 if (i == *argc - 1) | |
481 mainerr_arg_missing((char_u *)argv[i]); | |
482 # ifdef MSWIN | |
483 // Win32 always works? | |
484 if (serverSendToVim(sname, (char_u *)argv[i + 1], | |
485 &res, NULL, 1, 0, FALSE) < 0) | |
486 # else | |
487 if (xterm_dpy == NULL) | |
488 mch_errmsg(_("No display: Send expression failed.\n")); | |
489 else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1], | |
490 &res, NULL, 1, 0, 1, FALSE) < 0) | |
491 # endif | |
492 { | |
493 if (res != NULL && *res != NUL) | |
494 { | |
495 // Output error from remote | |
496 mch_errmsg((char *)res); | |
497 VIM_CLEAR(res); | |
498 } | |
499 mch_errmsg(_(": Send expression failed.\n")); | |
500 } | |
501 } | |
502 else if (STRICMP(argv[i], "--serverlist") == 0) | |
503 { | |
504 # ifdef MSWIN | |
505 // Win32 always works? | |
506 res = serverGetVimNames(); | |
507 # else | |
508 if (xterm_dpy != NULL) | |
509 res = serverGetVimNames(xterm_dpy); | |
510 # endif | |
511 if (did_emsg) | |
512 mch_errmsg("\n"); | |
513 } | |
514 else if (STRICMP(argv[i], "--servername") == 0) | |
515 { | |
516 // Already processed. Take it out of the command line | |
517 i++; | |
518 continue; | |
519 } | |
520 else | |
521 { | |
522 *newArgV++ = argv[i]; | |
523 newArgC++; | |
524 continue; | |
525 } | |
526 didone = TRUE; | |
527 if (res != NULL && *res != NUL) | |
528 { | |
529 mch_msg((char *)res); | |
530 if (res[STRLEN(res) - 1] != '\n') | |
531 mch_msg("\n"); | |
532 } | |
533 vim_free(res); | |
534 } | |
535 | |
536 if (didone) | |
537 { | |
538 display_errors(); // display any collected messages | |
539 exit(exiterr); // Mission accomplished - get out | |
540 } | |
541 | |
542 // Return back into main() | |
543 *argc = newArgC; | |
544 vim_free(sname); | |
545 } | |
546 | |
547 /* | |
548 * Build a ":drop" command to send to a Vim server. | |
549 */ | |
550 static char_u * | |
551 build_drop_cmd( | |
552 int filec, | |
553 char **filev, | |
554 int tabs, // Use ":tab drop" instead of ":drop". | |
555 int sendReply) | |
556 { | |
557 garray_T ga; | |
558 int i; | |
559 char_u *inicmd = NULL; | |
560 char_u *p; | |
561 char_u *cdp; | |
562 char_u *cwd; | |
563 | |
564 if (filec > 0 && filev[0][0] == '+') | |
565 { | |
566 inicmd = (char_u *)filev[0] + 1; | |
567 filev++; | |
568 filec--; | |
569 } | |
570 // Check if we have at least one argument. | |
571 if (filec <= 0) | |
572 mainerr_arg_missing((char_u *)filev[-1]); | |
573 | |
574 // Temporarily cd to the current directory to handle relative file names. | |
575 cwd = alloc(MAXPATHL); | |
576 if (cwd == NULL) | |
577 return NULL; | |
578 if (mch_dirname(cwd, MAXPATHL) != OK) | |
579 { | |
580 vim_free(cwd); | |
581 return NULL; | |
582 } | |
583 cdp = vim_strsave_escaped_ext(cwd, | |
584 #ifdef BACKSLASH_IN_FILENAME | |
585 (char_u *)"", // rem_backslash() will tell what chars to escape | |
586 #else | |
587 PATH_ESC_CHARS, | |
588 #endif | |
589 '\\', TRUE); | |
590 vim_free(cwd); | |
591 if (cdp == NULL) | |
592 return NULL; | |
593 ga_init2(&ga, 1, 100); | |
594 ga_concat(&ga, (char_u *)"<C-\\><C-N>:cd "); | |
595 ga_concat(&ga, cdp); | |
596 | |
597 // Call inputsave() so that a prompt for an encryption key works. | |
598 ga_concat(&ga, (char_u *)"<CR>:if exists('*inputsave')|call inputsave()|endif|"); | |
599 if (tabs) | |
600 ga_concat(&ga, (char_u *)"tab "); | |
601 ga_concat(&ga, (char_u *)"drop"); | |
602 for (i = 0; i < filec; i++) | |
603 { | |
604 // On Unix the shell has already expanded the wildcards, don't want to | |
605 // do it again in the Vim server. On MS-Windows only escape | |
606 // non-wildcard characters. | |
607 p = vim_strsave_escaped((char_u *)filev[i], | |
608 #ifdef UNIX | |
609 PATH_ESC_CHARS | |
610 #else | |
611 (char_u *)" \t%#" | |
612 #endif | |
613 ); | |
614 if (p == NULL) | |
615 { | |
616 vim_free(ga.ga_data); | |
617 return NULL; | |
618 } | |
619 ga_concat(&ga, (char_u *)" "); | |
620 ga_concat(&ga, p); | |
621 vim_free(p); | |
622 } | |
623 ga_concat(&ga, (char_u *)"|if exists('*inputrestore')|call inputrestore()|endif<CR>"); | |
624 | |
625 // The :drop commands goes to Insert mode when 'insertmode' is set, use | |
626 // CTRL-\ CTRL-N again. | |
627 ga_concat(&ga, (char_u *)"<C-\\><C-N>"); | |
628 | |
629 // Switch back to the correct current directory (prior to temporary path | |
630 // switch) unless 'autochdir' is set, in which case it will already be | |
631 // correct after the :drop command. With line breaks and spaces: | |
632 // if !exists('+acd') || !&acd | |
633 // if haslocaldir() | |
634 // cd - | |
635 // lcd - | |
636 // elseif getcwd() ==# 'current path' | |
637 // cd - | |
638 // endif | |
639 // endif | |
640 ga_concat(&ga, (char_u *)":if !exists('+acd')||!&acd|if haslocaldir()|"); | |
641 ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd() ==# '"); | |
642 ga_concat(&ga, cdp); | |
643 ga_concat(&ga, (char_u *)"'|cd -|endif|endif<CR>"); | |
644 vim_free(cdp); | |
645 | |
646 if (sendReply) | |
647 ga_concat(&ga, (char_u *)":call SetupRemoteReplies()<CR>"); | |
648 ga_concat(&ga, (char_u *)":"); | |
649 if (inicmd != NULL) | |
650 { | |
651 // Can't use <CR> after "inicmd", because an "startinsert" would cause | |
652 // the following commands to be inserted as text. Use a "|", | |
653 // hopefully "inicmd" does allow this... | |
654 ga_concat(&ga, inicmd); | |
655 ga_concat(&ga, (char_u *)"|"); | |
656 } | |
657 // Bring the window to the foreground, goto Insert mode when 'im' set and | |
658 // clear command line. | |
659 ga_concat(&ga, (char_u *)"cal foreground()|if &im|star|en|redr|f<CR>"); | |
660 ga_append(&ga, NUL); | |
661 return ga.ga_data; | |
662 } | |
663 | |
664 /* | |
665 * Make our basic server name: use the specified "arg" if given, otherwise use | |
666 * the tail of the command "cmd" we were started with. | |
667 * Return the name in allocated memory. This doesn't include a serial number. | |
668 */ | |
669 static char_u * | |
670 serverMakeName(char_u *arg, char *cmd) | |
671 { | |
672 char_u *p; | |
673 | |
674 if (arg != NULL && *arg != NUL) | |
675 p = vim_strsave_up(arg); | |
676 else | |
677 { | |
678 p = vim_strsave_up(gettail((char_u *)cmd)); | |
679 // Remove .exe or .bat from the name. | |
680 if (p != NULL && vim_strchr(p, '.') != NULL) | |
681 *vim_strchr(p, '.') = NUL; | |
682 } | |
683 return p; | |
684 } | |
685 #endif // FEAT_CLIENTSERVER | |
686 | |
687 #if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) | |
688 static void | |
689 make_connection(void) | |
690 { | |
691 if (X_DISPLAY == NULL | |
692 # ifdef FEAT_GUI | |
693 && !gui.in_use | |
694 # endif | |
695 ) | |
696 { | |
697 x_force_connect = TRUE; | |
698 setup_term_clip(); | |
699 x_force_connect = FALSE; | |
700 } | |
701 } | |
702 | |
703 static int | |
704 check_connection(void) | |
705 { | |
706 make_connection(); | |
707 if (X_DISPLAY == NULL) | |
708 { | |
709 emsg(_("E240: No connection to the X server")); | |
710 return FAIL; | |
711 } | |
712 return OK; | |
713 } | |
714 #endif | |
715 | |
716 #ifdef FEAT_CLIENTSERVER | |
717 static void | |
718 remote_common(typval_T *argvars, typval_T *rettv, int expr) | |
719 { | |
720 char_u *server_name; | |
721 char_u *keys; | |
722 char_u *r = NULL; | |
723 char_u buf[NUMBUFLEN]; | |
724 int timeout = 0; | |
725 # ifdef MSWIN | |
726 HWND w; | |
727 # else | |
728 Window w; | |
729 # endif | |
730 | |
731 if (check_restricted() || check_secure()) | |
732 return; | |
733 | |
734 # ifdef FEAT_X11 | |
735 if (check_connection() == FAIL) | |
736 return; | |
737 # endif | |
738 if (argvars[2].v_type != VAR_UNKNOWN | |
739 && argvars[3].v_type != VAR_UNKNOWN) | |
740 timeout = tv_get_number(&argvars[3]); | |
741 | |
742 server_name = tv_get_string_chk(&argvars[0]); | |
743 if (server_name == NULL) | |
744 return; // type error; errmsg already given | |
745 keys = tv_get_string_buf(&argvars[1], buf); | |
746 # ifdef MSWIN | |
747 if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0) | |
748 # else | |
749 if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout, | |
750 0, TRUE) < 0) | |
751 # endif | |
752 { | |
753 if (r != NULL) | |
754 { | |
755 emsg((char *)r); // sending worked but evaluation failed | |
756 vim_free(r); | |
757 } | |
758 else | |
759 semsg(_("E241: Unable to send to %s"), server_name); | |
760 return; | |
761 } | |
762 | |
763 rettv->vval.v_string = r; | |
764 | |
765 if (argvars[2].v_type != VAR_UNKNOWN) | |
766 { | |
767 dictitem_T v; | |
768 char_u str[30]; | |
769 char_u *idvar; | |
770 | |
771 idvar = tv_get_string_chk(&argvars[2]); | |
772 if (idvar != NULL && *idvar != NUL) | |
773 { | |
774 sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); | |
775 v.di_tv.v_type = VAR_STRING; | |
776 v.di_tv.vval.v_string = vim_strsave(str); | |
777 set_var(idvar, &v.di_tv, FALSE); | |
778 vim_free(v.di_tv.vval.v_string); | |
779 } | |
780 } | |
781 } | |
782 #endif | |
783 | |
784 #if defined(FEAT_EVAL) || defined(PROTO) | |
785 /* | |
786 * "remote_expr()" function | |
787 */ | |
788 void | |
789 f_remote_expr(typval_T *argvars UNUSED, typval_T *rettv) | |
790 { | |
791 rettv->v_type = VAR_STRING; | |
792 rettv->vval.v_string = NULL; | |
793 #ifdef FEAT_CLIENTSERVER | |
794 remote_common(argvars, rettv, TRUE); | |
795 #endif | |
796 } | |
797 | |
798 /* | |
799 * "remote_foreground()" function | |
800 */ | |
801 void | |
802 f_remote_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED) | |
803 { | |
804 #ifdef FEAT_CLIENTSERVER | |
805 # ifdef MSWIN | |
806 // On Win32 it's done in this application. | |
807 { | |
808 char_u *server_name = tv_get_string_chk(&argvars[0]); | |
809 | |
810 if (server_name != NULL) | |
811 serverForeground(server_name); | |
812 } | |
813 # else | |
814 // Send a foreground() expression to the server. | |
815 argvars[1].v_type = VAR_STRING; | |
816 argvars[1].vval.v_string = vim_strsave((char_u *)"foreground()"); | |
817 argvars[2].v_type = VAR_UNKNOWN; | |
818 rettv->v_type = VAR_STRING; | |
819 rettv->vval.v_string = NULL; | |
820 remote_common(argvars, rettv, TRUE); | |
821 vim_free(argvars[1].vval.v_string); | |
822 # endif | |
823 #endif | |
824 } | |
825 | |
826 void | |
827 f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) | |
828 { | |
829 #ifdef FEAT_CLIENTSERVER | |
830 dictitem_T v; | |
831 char_u *s = NULL; | |
832 # ifdef MSWIN | |
833 long_u n = 0; | |
834 # endif | |
835 char_u *serverid; | |
836 | |
837 if (check_restricted() || check_secure()) | |
838 { | |
839 rettv->vval.v_number = -1; | |
840 return; | |
841 } | |
842 serverid = tv_get_string_chk(&argvars[0]); | |
843 if (serverid == NULL) | |
844 { | |
845 rettv->vval.v_number = -1; | |
846 return; // type error; errmsg already given | |
847 } | |
848 # ifdef MSWIN | |
849 sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n); | |
850 if (n == 0) | |
851 rettv->vval.v_number = -1; | |
852 else | |
853 { | |
854 s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0); | |
855 rettv->vval.v_number = (s != NULL); | |
856 } | |
857 # else | |
858 if (check_connection() == FAIL) | |
859 return; | |
860 | |
861 rettv->vval.v_number = serverPeekReply(X_DISPLAY, | |
862 serverStrToWin(serverid), &s); | |
863 # endif | |
864 | |
865 if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) | |
866 { | |
867 char_u *retvar; | |
868 | |
869 v.di_tv.v_type = VAR_STRING; | |
870 v.di_tv.vval.v_string = vim_strsave(s); | |
871 retvar = tv_get_string_chk(&argvars[1]); | |
872 if (retvar != NULL) | |
873 set_var(retvar, &v.di_tv, FALSE); | |
874 vim_free(v.di_tv.vval.v_string); | |
875 } | |
876 #else | |
877 rettv->vval.v_number = -1; | |
878 #endif | |
879 } | |
880 | |
881 void | |
882 f_remote_read(typval_T *argvars UNUSED, typval_T *rettv) | |
883 { | |
884 char_u *r = NULL; | |
885 | |
886 #ifdef FEAT_CLIENTSERVER | |
887 char_u *serverid = tv_get_string_chk(&argvars[0]); | |
888 | |
889 if (serverid != NULL && !check_restricted() && !check_secure()) | |
890 { | |
891 int timeout = 0; | |
892 # ifdef MSWIN | |
893 // The server's HWND is encoded in the 'id' parameter | |
894 long_u n = 0; | |
895 # endif | |
896 | |
897 if (argvars[1].v_type != VAR_UNKNOWN) | |
898 timeout = tv_get_number(&argvars[1]); | |
899 | |
900 # ifdef MSWIN | |
901 sscanf((char *)serverid, SCANF_HEX_LONG_U, &n); | |
902 if (n != 0) | |
903 r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout); | |
904 if (r == NULL) | |
905 # else | |
906 if (check_connection() == FAIL | |
907 || serverReadReply(X_DISPLAY, serverStrToWin(serverid), | |
908 &r, FALSE, timeout) < 0) | |
909 # endif | |
910 emsg(_("E277: Unable to read a server reply")); | |
911 } | |
912 #endif | |
913 rettv->v_type = VAR_STRING; | |
914 rettv->vval.v_string = r; | |
915 } | |
916 | |
917 /* | |
918 * "remote_send()" function | |
919 */ | |
920 void | |
921 f_remote_send(typval_T *argvars UNUSED, typval_T *rettv) | |
922 { | |
923 rettv->v_type = VAR_STRING; | |
924 rettv->vval.v_string = NULL; | |
925 #ifdef FEAT_CLIENTSERVER | |
926 remote_common(argvars, rettv, FALSE); | |
927 #endif | |
928 } | |
929 | |
930 /* | |
931 * "remote_startserver()" function | |
932 */ | |
933 void | |
934 f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED) | |
935 { | |
936 #ifdef FEAT_CLIENTSERVER | |
937 char_u *server = tv_get_string_chk(&argvars[0]); | |
938 | |
939 if (server == NULL) | |
940 return; // type error; errmsg already given | |
941 if (serverName != NULL) | |
942 emsg(_("E941: already started a server")); | |
943 else | |
944 { | |
945 # ifdef FEAT_X11 | |
946 if (check_connection() == OK) | |
947 serverRegisterName(X_DISPLAY, server); | |
948 # else | |
949 serverSetName(server); | |
950 # endif | |
951 } | |
952 #else | |
953 emsg(_("E942: +clientserver feature not available")); | |
954 #endif | |
955 } | |
956 | |
957 void | |
958 f_server2client(typval_T *argvars UNUSED, typval_T *rettv) | |
959 { | |
960 #ifdef FEAT_CLIENTSERVER | |
961 char_u buf[NUMBUFLEN]; | |
962 char_u *server = tv_get_string_chk(&argvars[0]); | |
963 char_u *reply = tv_get_string_buf_chk(&argvars[1], buf); | |
964 | |
965 rettv->vval.v_number = -1; | |
966 if (server == NULL || reply == NULL) | |
967 return; | |
968 if (check_restricted() || check_secure()) | |
969 return; | |
970 # ifdef FEAT_X11 | |
971 if (check_connection() == FAIL) | |
972 return; | |
973 # endif | |
974 | |
975 if (serverSendReply(server, reply) < 0) | |
976 { | |
977 emsg(_("E258: Unable to send to client")); | |
978 return; | |
979 } | |
980 rettv->vval.v_number = 0; | |
981 #else | |
982 rettv->vval.v_number = -1; | |
983 #endif | |
984 } | |
985 | |
986 void | |
987 f_serverlist(typval_T *argvars UNUSED, typval_T *rettv) | |
988 { | |
989 char_u *r = NULL; | |
990 | |
991 #ifdef FEAT_CLIENTSERVER | |
992 # ifdef MSWIN | |
993 r = serverGetVimNames(); | |
994 # else | |
995 make_connection(); | |
996 if (X_DISPLAY != NULL) | |
997 r = serverGetVimNames(X_DISPLAY); | |
998 # endif | |
999 #endif | |
1000 rettv->v_type = VAR_STRING; | |
1001 rettv->vval.v_string = r; | |
1002 } | |
1003 #endif |