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