Mercurial > vim
annotate runtime/tools/xcmdsrv_client.c @ 7807:1a5d34492798 v7.4.1200
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri Jan 29 23:20:40 2016 +0100
patch 7.4.1200
Problem: Still using __ARGS.
Solution: Remove __ARGS in several files. (script by Hirohito Higashi)
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Fri, 29 Jan 2016 23:30:06 +0100 |
parents | 3fc0f57ecb91 |
children | 33ba2adb6065 |
rev | line source |
---|---|
7 | 1 /* vi:set ts=8 sts=4 sw=4: |
2 * | |
3 * VIM - Vi IMproved by Bram Moolenaar | |
4 * X-Windows communication by Flemming Madsen | |
5 * | |
6 * Do ":help uganda" in Vim to read copying and usage conditions. | |
7 * Do ":help credits" in Vim to see a list of people who contributed. | |
8 * See README.txt for an overview of the Vim source code. | |
9 * | |
10 * Client for sending commands to an '+xcmdsrv' enabled vim. | |
11 * This is mostly a de-Vimified version of if_xcmdsrv.c in vim. | |
12 * See that file for a protocol specification. | |
13 * | |
14 * You can make a test program with a Makefile like: | |
15 * xcmdsrv_client: xcmdsrv_client.c | |
16 * cc -o $@ -g -DMAIN -I/usr/X11R6/include -L/usr/X11R6/lib $< -lX11 | |
17 * | |
18 */ | |
19 | |
20 #include <stdio.h> | |
21 #include <string.h> | |
22 #ifdef HAVE_SELECT | |
23 #include <sys/time.h> | |
24 #include <sys/types.h> | |
25 #include <unistd.h> | |
26 #else | |
27 #include <sys/poll.h> | |
28 #endif | |
29 #include <X11/Intrinsic.h> | |
30 #include <X11/Xatom.h> | |
31 | |
32 /* Client API */ | |
7807
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
33 char * sendToVim(Display *dpy, char *name, char *cmd, int asKeys, int *code); |
7 | 34 |
35 #ifdef MAIN | |
36 /* A sample program */ | |
37 main(int argc, char **argv) | |
38 { | |
39 char *res; | |
40 int code; | |
41 | |
42 if (argc == 4) | |
43 { | |
44 if ((res = sendToVim(XOpenDisplay(NULL), argv[2], argv[3], | |
45 argv[1][0] != 'e', &code)) != NULL) | |
46 { | |
47 if (code) | |
48 printf("Error code returned: %d\n", code); | |
49 puts(res); | |
50 } | |
51 exit(0); | |
52 } | |
53 else | |
54 fprintf(stderr, "Usage: %s {k|e} <server> <command>", argv[0]); | |
55 | |
56 exit(1); | |
57 } | |
58 #endif | |
59 | |
60 /* | |
61 * Maximum size property that can be read at one time by | |
62 * this module: | |
63 */ | |
64 | |
65 #define MAX_PROP_WORDS 100000 | |
66 | |
67 /* | |
68 * Forward declarations for procedures defined later in this file: | |
69 */ | |
70 | |
7807
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
71 static int x_error_check(Display *dpy, XErrorEvent *error_event); |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
72 static int AppendPropCarefully(Display *display, |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
73 Window window, Atom property, char *value, int length); |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
74 static Window LookupName(Display *dpy, char *name, |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
75 int delete, char **loose); |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
76 static int SendInit(Display *dpy); |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
77 static char *SendEventProc(Display *dpy, XEvent *eventPtr, |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
78 int expect, int *code); |
1a5d34492798
commit https://github.com/vim/vim/commit/d99df423c559d85c17779b3685426c489554908c
Christian Brabandt <cb@256bit.org>
parents:
7
diff
changeset
|
79 static int IsSerialName(char *name); |
7 | 80 |
81 /* Private variables */ | |
82 static Atom registryProperty = None; | |
83 static Atom commProperty = None; | |
84 static Window commWindow = None; | |
85 static int got_x_error = FALSE; | |
86 | |
87 | |
88 /* | |
89 * sendToVim -- | |
90 * Send to an instance of Vim via the X display. | |
91 * | |
92 * Results: | |
93 * A string with the result or NULL. Caller must free if non-NULL | |
94 */ | |
95 | |
96 char * | |
97 sendToVim(dpy, name, cmd, asKeys, code) | |
98 Display *dpy; /* Where to send. */ | |
99 char *name; /* Where to send. */ | |
100 char *cmd; /* What to send. */ | |
101 int asKeys; /* Interpret as keystrokes or expr ? */ | |
102 int *code; /* Return code. 0 => OK */ | |
103 { | |
104 Window w; | |
105 Atom *plist; | |
106 XErrorHandler old_handler; | |
107 #define STATIC_SPACE 500 | |
108 char *property, staticSpace[STATIC_SPACE]; | |
109 int length; | |
110 int res; | |
111 static int serial = 0; /* Running count of sent commands. | |
112 * Used to give each command a | |
113 * different serial number. */ | |
114 XEvent event; | |
115 XPropertyEvent *e = (XPropertyEvent *)&event; | |
116 time_t start; | |
117 char *result; | |
118 char *loosename = NULL; | |
119 | |
120 if (commProperty == None && dpy != NULL) | |
121 { | |
122 if (SendInit(dpy) < 0) | |
123 return NULL; | |
124 } | |
125 | |
126 /* | |
127 * Bind the server name to a communication window. | |
128 * | |
129 * Find any survivor with a serialno attached to the name if the | |
130 * original registrant of the wanted name is no longer present. | |
131 * | |
132 * Delete any lingering names from dead editors. | |
133 */ | |
134 | |
135 old_handler = XSetErrorHandler(x_error_check); | |
136 while (TRUE) | |
137 { | |
138 got_x_error = FALSE; | |
139 w = LookupName(dpy, name, 0, &loosename); | |
140 /* Check that the window is hot */ | |
141 if (w != None) | |
142 { | |
143 plist = XListProperties(dpy, w, &res); | |
144 XSync(dpy, False); | |
145 if (plist != NULL) | |
146 XFree(plist); | |
147 if (got_x_error) | |
148 { | |
149 LookupName(dpy, loosename ? loosename : name, | |
150 /*DELETE=*/TRUE, NULL); | |
151 continue; | |
152 } | |
153 } | |
154 break; | |
155 } | |
156 if (w == None) | |
157 { | |
158 fprintf(stderr, "no registered server named %s\n", name); | |
159 return NULL; | |
160 } | |
161 else if (loosename != NULL) | |
162 name = loosename; | |
163 | |
164 /* | |
165 * Send the command to target interpreter by appending it to the | |
166 * comm window in the communication window. | |
167 */ | |
168 | |
169 length = strlen(name) + strlen(cmd) + 10; | |
170 if (length <= STATIC_SPACE) | |
171 property = staticSpace; | |
172 else | |
173 property = (char *) malloc((unsigned) length); | |
174 | |
175 serial++; | |
176 sprintf(property, "%c%c%c-n %s%c-s %s", | |
177 0, asKeys ? 'k' : 'c', 0, name, 0, cmd); | |
178 if (name == loosename) | |
179 free(loosename); | |
180 if (!asKeys) | |
181 { | |
182 /* Add a back reference to our comm window */ | |
183 sprintf(property + length, "%c-r %x %d", 0, (uint) commWindow, serial); | |
184 length += strlen(property + length + 1) + 1; | |
185 } | |
186 | |
187 res = AppendPropCarefully(dpy, w, commProperty, property, length + 1); | |
188 if (length > STATIC_SPACE) | |
189 free(property); | |
190 if (res < 0) | |
191 { | |
192 fprintf(stderr, "Failed to send command to the destination program\n"); | |
193 return NULL; | |
194 } | |
195 | |
196 if (asKeys) /* There is no answer for this - Keys are sent async */ | |
197 return NULL; | |
198 | |
199 | |
200 /* | |
201 * Enter a loop processing X events & pooling chars until we see the result | |
202 */ | |
203 | |
204 #define SEND_MSEC_POLL 50 | |
205 | |
206 time(&start); | |
207 while ((time((time_t *) 0) - start) < 60) | |
208 { | |
209 /* Look out for the answer */ | |
210 #ifndef HAVE_SELECT | |
211 struct pollfd fds; | |
212 | |
213 fds.fd = ConnectionNumber(dpy); | |
214 fds.events = POLLIN; | |
215 if (poll(&fds, 1, SEND_MSEC_POLL) < 0) | |
216 break; | |
217 #else | |
218 fd_set fds; | |
219 struct timeval tv; | |
220 | |
221 tv.tv_sec = 0; | |
222 tv.tv_usec = SEND_MSEC_POLL * 1000; | |
223 FD_ZERO(&fds); | |
224 FD_SET(ConnectionNumber(dpy), &fds); | |
225 if (select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &tv) < 0) | |
226 break; | |
227 #endif | |
228 while (XEventsQueued(dpy, QueuedAfterReading) > 0) | |
229 { | |
230 XNextEvent(dpy, &event); | |
231 if (event.type == PropertyNotify && e->window == commWindow) | |
232 if ((result = SendEventProc(dpy, &event, serial, code)) != NULL) | |
233 return result; | |
234 } | |
235 } | |
236 return NULL; | |
237 } | |
238 | |
239 | |
240 /* | |
241 * SendInit -- | |
242 * This procedure is called to initialize the | |
243 * communication channels for sending commands and | |
244 * receiving results. | |
245 */ | |
246 | |
247 static int | |
248 SendInit(dpy) | |
249 Display *dpy; | |
250 { | |
251 XErrorHandler old_handler; | |
252 | |
253 /* | |
254 * Create the window used for communication, and set up an | |
255 * event handler for it. | |
256 */ | |
257 old_handler = XSetErrorHandler(x_error_check); | |
258 got_x_error = FALSE; | |
259 | |
260 commProperty = XInternAtom(dpy, "Comm", False); | |
261 /* Change this back to "InterpRegistry" to talk to tk processes */ | |
262 registryProperty = XInternAtom(dpy, "VimRegistry", False); | |
263 | |
264 if (commWindow == None) | |
265 { | |
266 commWindow = | |
267 XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), | |
268 getpid(), 0, 10, 10, 0, | |
269 WhitePixel(dpy, DefaultScreen(dpy)), | |
270 WhitePixel(dpy, DefaultScreen(dpy))); | |
271 XSelectInput(dpy, commWindow, PropertyChangeMask); | |
272 } | |
273 | |
274 XSync(dpy, False); | |
275 (void) XSetErrorHandler(old_handler); | |
276 | |
277 return got_x_error ? -1 : 0; | |
278 } | |
279 | |
280 /* | |
281 * LookupName -- | |
282 * Given an interpreter name, see if the name exists in | |
283 * the interpreter registry for a particular display. | |
284 * | |
285 * Results: | |
286 * If the given name is registered, return the ID of | |
287 * the window associated with the name. If the name | |
288 * isn't registered, then return 0. | |
289 */ | |
290 | |
291 static Window | |
292 LookupName(dpy, name, delete, loose) | |
293 Display *dpy; /* Display whose registry to check. */ | |
294 char *name; /* Name of an interpreter. */ | |
295 int delete; /* If non-zero, delete info about name. */ | |
296 char **loose; /* Do another search matching -999 if not found | |
297 Return result here if a match is found */ | |
298 { | |
299 unsigned char *regProp, *entry; | |
300 unsigned char *p; | |
301 int result, actualFormat; | |
302 unsigned long numItems, bytesAfter; | |
303 Atom actualType; | |
304 Window returnValue; | |
305 | |
306 /* | |
307 * Read the registry property. | |
308 */ | |
309 | |
310 regProp = NULL; | |
311 result = XGetWindowProperty(dpy, RootWindow(dpy, 0), registryProperty, 0, | |
312 MAX_PROP_WORDS, False, XA_STRING, &actualType, | |
313 &actualFormat, &numItems, &bytesAfter, | |
314 ®Prop); | |
315 | |
316 if (actualType == None) | |
317 return 0; | |
318 | |
319 /* | |
320 * If the property is improperly formed, then delete it. | |
321 */ | |
322 | |
323 if ((result != Success) || (actualFormat != 8) || (actualType != XA_STRING)) | |
324 { | |
325 if (regProp != NULL) | |
326 XFree(regProp); | |
327 XDeleteProperty(dpy, RootWindow(dpy, 0), registryProperty); | |
328 return 0; | |
329 } | |
330 | |
331 /* | |
332 * Scan the property for the desired name. | |
333 */ | |
334 | |
335 returnValue = None; | |
336 entry = NULL; /* Not needed, but eliminates compiler warning. */ | |
337 for (p = regProp; (p - regProp) < numItems; ) | |
338 { | |
339 entry = p; | |
340 while ((*p != 0) && (!isspace(*p))) | |
341 p++; | |
342 if ((*p != 0) && (strcasecmp(name, p + 1) == 0)) | |
343 { | |
344 sscanf(entry, "%x", (uint*) &returnValue); | |
345 break; | |
346 } | |
347 while (*p != 0) | |
348 p++; | |
349 p++; | |
350 } | |
351 | |
352 if (loose != NULL && returnValue == None && !IsSerialName(name)) | |
353 { | |
354 for (p = regProp; (p - regProp) < numItems; ) | |
355 { | |
356 entry = p; | |
357 while ((*p != 0) && (!isspace(*p))) | |
358 p++; | |
359 if ((*p != 0) && IsSerialName(p + 1) | |
360 && (strncmp(name, p + 1, strlen(name)) == 0)) | |
361 { | |
362 sscanf(entry, "%x", (uint*) &returnValue); | |
363 *loose = strdup(p + 1); | |
364 break; | |
365 } | |
366 while (*p != 0) | |
367 p++; | |
368 p++; | |
369 } | |
370 } | |
371 | |
372 /* | |
373 * Delete the property, if that is desired (copy down the | |
374 * remainder of the registry property to overlay the deleted | |
375 * info, then rewrite the property). | |
376 */ | |
377 | |
378 if ((delete) && (returnValue != None)) | |
379 { | |
380 int count; | |
381 | |
382 while (*p != 0) | |
383 p++; | |
384 p++; | |
385 count = numItems - (p-regProp); | |
386 if (count > 0) | |
387 memcpy(entry, p, count); | |
388 XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING, | |
389 8, PropModeReplace, regProp, | |
390 (int) (numItems - (p-entry))); | |
391 XSync(dpy, False); | |
392 } | |
393 | |
394 XFree(regProp); | |
395 return returnValue; | |
396 } | |
397 | |
398 static char * | |
399 SendEventProc(dpy, eventPtr, expected, code) | |
400 Display *dpy; | |
401 XEvent *eventPtr; /* Information about event. */ | |
402 int expected; /* The one were waiting for */ | |
403 int *code; /* Return code. 0 => OK */ | |
404 { | |
405 unsigned char *propInfo; | |
406 unsigned char *p; | |
407 int result, actualFormat; | |
408 int retCode; | |
409 unsigned long numItems, bytesAfter; | |
410 Atom actualType; | |
411 | |
412 if ((eventPtr->xproperty.atom != commProperty) | |
413 || (eventPtr->xproperty.state != PropertyNewValue)) | |
414 { | |
415 return; | |
416 } | |
417 | |
418 /* | |
419 * Read the comm property and delete it. | |
420 */ | |
421 | |
422 propInfo = NULL; | |
423 result = XGetWindowProperty(dpy, commWindow, commProperty, 0, | |
424 MAX_PROP_WORDS, True, XA_STRING, &actualType, | |
425 &actualFormat, &numItems, &bytesAfter, | |
426 &propInfo); | |
427 | |
428 /* | |
429 * If the property doesn't exist or is improperly formed | |
430 * then ignore it. | |
431 */ | |
432 | |
433 if ((result != Success) || (actualType != XA_STRING) | |
434 || (actualFormat != 8)) | |
435 { | |
436 if (propInfo != NULL) | |
437 { | |
438 XFree(propInfo); | |
439 } | |
440 return; | |
441 } | |
442 | |
443 /* | |
444 * Several commands and results could arrive in the property at | |
445 * one time; each iteration through the outer loop handles a | |
446 * single command or result. | |
447 */ | |
448 | |
449 for (p = propInfo; (p - propInfo) < numItems; ) | |
450 { | |
451 /* | |
452 * Ignore leading NULs; each command or result starts with a | |
453 * NUL so that no matter how badly formed a preceding command | |
454 * is, we'll be able to tell that a new command/result is | |
455 * starting. | |
456 */ | |
457 | |
458 if (*p == 0) | |
459 { | |
460 p++; | |
461 continue; | |
462 } | |
463 | |
464 if ((*p == 'r') && (p[1] == 0)) | |
465 { | |
466 int serial, gotSerial; | |
467 char *res; | |
468 | |
469 /* | |
470 * This is a reply to some command that we sent out. Iterate | |
471 * over all of its options. Stop when we reach the end of the | |
472 * property or something that doesn't look like an option. | |
473 */ | |
474 | |
475 p += 2; | |
476 gotSerial = 0; | |
477 res = ""; | |
478 retCode = 0; | |
479 while (((p-propInfo) < numItems) && (*p == '-')) | |
480 { | |
481 switch (p[1]) | |
482 { | |
483 case 'r': | |
484 if (p[2] == ' ') | |
485 res = p + 3; | |
486 break; | |
487 case 's': | |
488 if (sscanf(p + 2, " %d", &serial) == 1) | |
489 gotSerial = 1; | |
490 break; | |
491 case 'c': | |
492 if (sscanf(p + 2, " %d", &retCode) != 1) | |
493 retCode = 0; | |
494 break; | |
495 } | |
496 while (*p != 0) | |
497 p++; | |
498 p++; | |
499 } | |
500 | |
501 if (!gotSerial) | |
502 continue; | |
503 | |
504 if (code != NULL) | |
505 *code = retCode; | |
506 return serial == expected ? strdup(res) : NULL; | |
507 } | |
508 else | |
509 { | |
510 /* | |
511 * Didn't recognize this thing. Just skip through the next | |
512 * null character and try again. | |
513 * Also, throw away commands that we cant process anyway. | |
514 */ | |
515 | |
516 while (*p != 0) | |
517 p++; | |
518 p++; | |
519 } | |
520 } | |
521 XFree(propInfo); | |
522 } | |
523 | |
524 /* | |
525 * AppendPropCarefully -- | |
526 * | |
527 * Append a given property to a given window, but set up | |
528 * an X error handler so that if the append fails this | |
529 * procedure can return an error code rather than having | |
530 * Xlib panic. | |
531 * | |
532 * Return: | |
533 * 0 on OK - -1 on error | |
534 *-------------------------------------------------------------- | |
535 */ | |
536 | |
537 static int | |
538 AppendPropCarefully(dpy, window, property, value, length) | |
539 Display *dpy; /* Display on which to operate. */ | |
540 Window window; /* Window whose property is to | |
541 * be modified. */ | |
542 Atom property; /* Name of property. */ | |
543 char *value; /* Characters to append to property. */ | |
544 int length; /* How much to append */ | |
545 { | |
546 XErrorHandler old_handler; | |
547 | |
548 old_handler = XSetErrorHandler(x_error_check); | |
549 got_x_error = FALSE; | |
550 XChangeProperty(dpy, window, property, XA_STRING, 8, | |
551 PropModeAppend, value, length); | |
552 XSync(dpy, False); | |
553 (void) XSetErrorHandler(old_handler); | |
554 return got_x_error ? -1 : 0; | |
555 } | |
556 | |
557 | |
558 /* | |
559 * Another X Error handler, just used to check for errors. | |
560 */ | |
561 /* ARGSUSED */ | |
562 static int | |
563 x_error_check(dpy, error_event) | |
564 Display *dpy; | |
565 XErrorEvent *error_event; | |
566 { | |
567 got_x_error = TRUE; | |
568 return 0; | |
569 } | |
570 | |
571 /* | |
572 * Check if "str" looks like it had a serial number appended. | |
573 * Actually just checks if the name ends in a digit. | |
574 */ | |
575 static int | |
576 IsSerialName(str) | |
577 char *str; | |
578 { | |
579 int len = strlen(str); | |
580 | |
581 return (len > 1 && isdigit(str[len - 1])); | |
582 } |