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 */ 33 char * sendToVim(Display *dpy, char *name, char *cmd, int asKeys, int *code); 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 71 static int x_error_check(Display *dpy, XErrorEvent *error_event); 72 static int AppendPropCarefully(Display *display, 73 Window window, Atom property, char *value, int length); 74 static Window LookupName(Display *dpy, char *name, 75 int delete, char **loose); 76 static int SendInit(Display *dpy); 77 static char *SendEventProc(Display *dpy, XEvent *eventPtr, 78 int expect, int *code); 79 static int IsSerialName(char *name); 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( 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(Display *dpy) 249 { 250 XErrorHandler old_handler; 251 252 /* 253 * Create the window used for communication, and set up an 254 * event handler for it. 255 */ 256 old_handler = XSetErrorHandler(x_error_check); 257 got_x_error = FALSE; 258 259 commProperty = XInternAtom(dpy, "Comm", False); 260 /* Change this back to "InterpRegistry" to talk to tk processes */ 261 registryProperty = XInternAtom(dpy, "VimRegistry", False); 262 263 if (commWindow == None) 264 { 265 commWindow = 266 XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), 267 getpid(), 0, 10, 10, 0, 268 WhitePixel(dpy, DefaultScreen(dpy)), 269 WhitePixel(dpy, DefaultScreen(dpy))); 270 XSelectInput(dpy, commWindow, PropertyChangeMask); 271 } 272 273 XSync(dpy, False); 274 (void) XSetErrorHandler(old_handler); 275 276 return got_x_error ? -1 : 0; 277 } 278 279 /* 280 * LookupName -- 281 * Given an interpreter name, see if the name exists in 282 * the interpreter registry for a particular display. 283 * 284 * Results: 285 * If the given name is registered, return the ID of 286 * the window associated with the name. If the name 287 * isn't registered, then return 0. 288 */ 289 290 static Window 291 LookupName( 292 Display *dpy, /* Display whose registry to check. */ 293 char *name, /* Name of an interpreter. */ 294 int delete, /* If non-zero, delete info about name. */ 295 char **loose) /* Do another search matching -999 if not found 296 Return result here if a match is found */ 297 { 298 unsigned char *regProp, *entry; 299 unsigned char *p; 300 int result, actualFormat; 301 unsigned long numItems, bytesAfter; 302 Atom actualType; 303 Window returnValue; 304 305 /* 306 * Read the registry property. 307 */ 308 309 regProp = NULL; 310 result = XGetWindowProperty(dpy, RootWindow(dpy, 0), registryProperty, 0, 311 MAX_PROP_WORDS, False, XA_STRING, &actualType, 312 &actualFormat, &numItems, &bytesAfter, 313 ®Prop); 314 315 if (actualType == None) 316 return 0; 317 318 /* 319 * If the property is improperly formed, then delete it. 320 */ 321 322 if ((result != Success) || (actualFormat != 8) || (actualType != XA_STRING)) 323 { 324 if (regProp != NULL) 325 XFree(regProp); 326 XDeleteProperty(dpy, RootWindow(dpy, 0), registryProperty); 327 return 0; 328 } 329 330 /* 331 * Scan the property for the desired name. 332 */ 333 334 returnValue = None; 335 entry = NULL; /* Not needed, but eliminates compiler warning. */ 336 for (p = regProp; (p - regProp) < numItems; ) 337 { 338 entry = p; 339 while ((*p != 0) && (!isspace(*p))) 340 p++; 341 if ((*p != 0) && (strcasecmp(name, p + 1) == 0)) 342 { 343 sscanf(entry, "%x", (uint*) &returnValue); 344 break; 345 } 346 while (*p != 0) 347 p++; 348 p++; 349 } 350 351 if (loose != NULL && returnValue == None && !IsSerialName(name)) 352 { 353 for (p = regProp; (p - regProp) < numItems; ) 354 { 355 entry = p; 356 while ((*p != 0) && (!isspace(*p))) 357 p++; 358 if ((*p != 0) && IsSerialName(p + 1) 359 && (strncmp(name, p + 1, strlen(name)) == 0)) 360 { 361 sscanf(entry, "%x", (uint*) &returnValue); 362 *loose = strdup(p + 1); 363 break; 364 } 365 while (*p != 0) 366 p++; 367 p++; 368 } 369 } 370 371 /* 372 * Delete the property, if that is desired (copy down the 373 * remainder of the registry property to overlay the deleted 374 * info, then rewrite the property). 375 */ 376 377 if ((delete) && (returnValue != None)) 378 { 379 int count; 380 381 while (*p != 0) 382 p++; 383 p++; 384 count = numItems - (p-regProp); 385 if (count > 0) 386 memcpy(entry, p, count); 387 XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING, 388 8, PropModeReplace, regProp, 389 (int) (numItems - (p-entry))); 390 XSync(dpy, False); 391 } 392 393 XFree(regProp); 394 return returnValue; 395 } 396 397 static char * 398 SendEventProc( 399 Display *dpy, 400 XEvent *eventPtr, /* Information about event. */ 401 int expected, /* The one were waiting for */ 402 int *code) /* Return code. 0 => OK */ 403 { 404 unsigned char *propInfo; 405 unsigned char *p; 406 int result, actualFormat; 407 int retCode; 408 unsigned long numItems, bytesAfter; 409 Atom actualType; 410 411 if ((eventPtr->xproperty.atom != commProperty) 412 || (eventPtr->xproperty.state != PropertyNewValue)) 413 { 414 return; 415 } 416 417 /* 418 * Read the comm property and delete it. 419 */ 420 421 propInfo = NULL; 422 result = XGetWindowProperty(dpy, commWindow, commProperty, 0, 423 MAX_PROP_WORDS, True, XA_STRING, &actualType, 424 &actualFormat, &numItems, &bytesAfter, 425 &propInfo); 426 427 /* 428 * If the property doesn't exist or is improperly formed 429 * then ignore it. 430 */ 431 432 if ((result != Success) || (actualType != XA_STRING) 433 || (actualFormat != 8)) 434 { 435 if (propInfo != NULL) 436 { 437 XFree(propInfo); 438 } 439 return; 440 } 441 442 /* 443 * Several commands and results could arrive in the property at 444 * one time; each iteration through the outer loop handles a 445 * single command or result. 446 */ 447 448 for (p = propInfo; (p - propInfo) < numItems; ) 449 { 450 /* 451 * Ignore leading NULs; each command or result starts with a 452 * NUL so that no matter how badly formed a preceding command 453 * is, we'll be able to tell that a new command/result is 454 * starting. 455 */ 456 457 if (*p == 0) 458 { 459 p++; 460 continue; 461 } 462 463 if ((*p == 'r') && (p[1] == 0)) 464 { 465 int serial, gotSerial; 466 char *res; 467 468 /* 469 * This is a reply to some command that we sent out. Iterate 470 * over all of its options. Stop when we reach the end of the 471 * property or something that doesn't look like an option. 472 */ 473 474 p += 2; 475 gotSerial = 0; 476 res = ""; 477 retCode = 0; 478 while (((p-propInfo) < numItems) && (*p == '-')) 479 { 480 switch (p[1]) 481 { 482 case 'r': 483 if (p[2] == ' ') 484 res = p + 3; 485 break; 486 case 's': 487 if (sscanf(p + 2, " %d", &serial) == 1) 488 gotSerial = 1; 489 break; 490 case 'c': 491 if (sscanf(p + 2, " %d", &retCode) != 1) 492 retCode = 0; 493 break; 494 } 495 while (*p != 0) 496 p++; 497 p++; 498 } 499 500 if (!gotSerial) 501 continue; 502 503 if (code != NULL) 504 *code = retCode; 505 return serial == expected ? strdup(res) : NULL; 506 } 507 else 508 { 509 /* 510 * Didn't recognize this thing. Just skip through the next 511 * null character and try again. 512 * Also, throw away commands that we cant process anyway. 513 */ 514 515 while (*p != 0) 516 p++; 517 p++; 518 } 519 } 520 XFree(propInfo); 521 } 522 523 /* 524 * AppendPropCarefully -- 525 * 526 * Append a given property to a given window, but set up 527 * an X error handler so that if the append fails this 528 * procedure can return an error code rather than having 529 * Xlib panic. 530 * 531 * Return: 532 * 0 on OK - -1 on error 533 *-------------------------------------------------------------- 534 */ 535 536 static int 537 AppendPropCarefully( 538 Display *dpy, /* Display on which to operate. */ 539 Window window, /* Window whose property is to 540 * be modified. */ 541 Atom property, /* Name of property. */ 542 char *value, /* Characters to append to property. */ 543 int length) /* How much to append */ 544 { 545 XErrorHandler old_handler; 546 547 old_handler = XSetErrorHandler(x_error_check); 548 got_x_error = FALSE; 549 XChangeProperty(dpy, window, property, XA_STRING, 8, 550 PropModeAppend, value, length); 551 XSync(dpy, False); 552 (void) XSetErrorHandler(old_handler); 553 return got_x_error ? -1 : 0; 554 } 555 556 557 /* 558 * Another X Error handler, just used to check for errors. 559 */ 560 /* ARGSUSED */ 561 static int 562 x_error_check(Display *dpy, XErrorEvent *error_event) 563 { 564 got_x_error = TRUE; 565 return 0; 566 } 567 568 /* 569 * Check if "str" looks like it had a serial number appended. 570 * Actually just checks if the name ends in a digit. 571 */ 572 static int 573 IsSerialName(char *str) 574 { 575 int len = strlen(str); 576 577 return (len > 1 && isdigit(str[len - 1])); 578 } 579