home *** CD-ROM | disk | FTP | other *** search
/ vim.ftp.fu-berlin.de / 2015-02-03.vim.ftp.fu-berlin.de.tar / vim.ftp.fu-berlin.de / unix / vim-6.2.tar.bz2 / vim-6.2.tar / vim62 / src / if_xcmdsrv.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-04-27  |  35.8 KB  |  1,434 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved    by Bram Moolenaar
  4.  * X command server 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.  * if_xcmdsrv.c: Functions for passing commands through an X11 display.
  11.  *
  12.  */
  13.  
  14. #include "vim.h"
  15. #include "version.h"
  16.  
  17. #if defined(FEAT_CLIENTSERVER) || defined(PROTO)
  18.  
  19. # ifdef FEAT_X11
  20. #  include <X11/Intrinsic.h>
  21. #  include <X11/Xatom.h>
  22. # endif
  23.  
  24. # if defined(HAVE_SYS_SELECT_H) && \
  25.     (!defined(HAVE_SYS_TIME_H) || defined(SYS_SELECT_WITH_SYS_TIME))
  26. #  include <sys/select.h>
  27. # endif
  28.  
  29. /*
  30.  * This file provides procedures that implement the command server functionality
  31.  * of Vim when in contact with an X11 server.
  32.  *
  33.  * Adapted from TCL/TK's send command  in tkSend.c of the tk 3.6 distribution.
  34.  * Adapted for use in Vim by Flemming Madsen. Protocol changed to that of tk 4
  35.  */
  36.  
  37. /*
  38.  * Copyright (c) 1989-1993 The Regents of the University of California.
  39.  * All rights reserved.
  40.  *
  41.  * Permission is hereby granted, without written agreement and without
  42.  * license or royalty fees, to use, copy, modify, and distribute this
  43.  * software and its documentation for any purpose, provided that the
  44.  * above copyright notice and the following two paragraphs appear in
  45.  * all copies of this software.
  46.  *
  47.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  48.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  49.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  50.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  51.  *
  52.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  53.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  54.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  55.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  56.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  57.  */
  58.  
  59.  
  60. /*
  61.  * When a result is being awaited from a sent command, one of
  62.  * the following structures is present on a list of all outstanding
  63.  * sent commands.  The information in the structure is used to
  64.  * process the result when it arrives.  You're probably wondering
  65.  * how there could ever be multiple outstanding sent commands.
  66.  * This could happen if Vim instances invoke each other recursively.
  67.  * It's unlikely, but possible.
  68.  */
  69.  
  70. typedef struct PendingCommand
  71. {
  72.     int        serial;    /* Serial number expected in result. */
  73.     int        code;    /* Result Code. 0 is OK */
  74.     char_u  *result;    /* String result for command (malloc'ed).
  75.              * NULL means command still pending. */
  76.     struct PendingCommand *nextPtr;
  77.             /* Next in list of all outstanding commands.
  78.              * NULL means end of list. */
  79. } PendingCommand;
  80.  
  81. static PendingCommand *pendingCommands = NULL;
  82.                 /* List of all commands currently
  83.                  * being waited for. */
  84.  
  85. /*
  86.  * The information below is used for communication between processes
  87.  * during "send" commands.  Each process keeps a private window, never
  88.  * even mapped, with one property, "Comm".  When a command is sent to
  89.  * an interpreter, the command is appended to the comm property of the
  90.  * communication window associated with the interp's process.  Similarly,
  91.  * when a result is returned from a sent command, it is also appended
  92.  * to the comm property.
  93.  *
  94.  * Each command and each result takes the form of ASCII text.  For a
  95.  * command, the text consists of a nul character followed by several
  96.  * nul-terminated ASCII strings.  The first string consists of the
  97.  * single letter "c" for an expression, or "k" for keystrokes.  Subsequent
  98.  * strings have the form "option value" where the following options are
  99.  * supported:
  100.  *
  101.  * -r commWindow serial
  102.  *
  103.  *    This option means that a response should be sent to the window
  104.  *    whose X identifier is "commWindow" (in hex), and the response should
  105.  *    be identified with the serial number given by "serial" (in decimal).
  106.  *    If this option isn't specified then the send is asynchronous and
  107.  *    no response is sent.
  108.  *
  109.  * -n name
  110.  *    "Name" gives the name of the application for which the command is
  111.  *    intended.  This option must be present.
  112.  *
  113.  * -s script
  114.  *    "Script" is the script to be executed.  This option must be
  115.  *    present.  Taken as a series of keystrokes in a "k" command where
  116.  *    <Key>'s are expanded
  117.  *
  118.  * The options may appear in any order.  The -n and -s options must be
  119.  * present, but -r may be omitted for asynchronous RPCs.  For compatibility
  120.  * with future releases that may add new features, there may be additional
  121.  * options present;  as long as they start with a "-" character, they will
  122.  * be ignored.
  123.  *
  124.  * A result also consists of a zero character followed by several null-
  125.  * terminated ASCII strings.  The first string consists of the single
  126.  * letter "r".  Subsequent strings have the form "option value" where
  127.  * the following options are supported:
  128.  *
  129.  * -s serial
  130.  *    Identifies the command for which this is the result.  It is the
  131.  *    same as the "serial" field from the -s option in the command.  This
  132.  *    option must be present.
  133.  *
  134.  * -r result
  135.  *    "Result" is the result string for the script, which may be either
  136.  *    a result or an error message.  If this field is omitted then it
  137.  *    defaults to an empty string.
  138.  *
  139.  * -c code
  140.  *    0: for OK. This is the default.
  141.  *    1: for error: Result is the last error
  142.  *
  143.  * -i errorInfo
  144.  * -e errorCode
  145.  *    Not applicable for Vim
  146.  *
  147.  * Options may appear in any order, and only the -s option must be
  148.  * present.  As with commands, there may be additional options besides
  149.  * these;  unknown options are ignored.
  150.  */
  151.  
  152. /*
  153.  * Maximum size property that can be read at one time by
  154.  * this module:
  155.  */
  156.  
  157. #define MAX_PROP_WORDS 100000
  158.  
  159. struct ServerReply
  160. {
  161.     Window  id;
  162.     garray_T strings;
  163. };
  164. static garray_T serverReply = { 0, 0, 0, 0, 0 };
  165. enum ServerReplyOp { SROP_Find, SROP_Add, SROP_Delete };
  166.  
  167. typedef int (*EndCond) __ARGS((void *));
  168.  
  169. /*
  170.  * Forward declarations for procedures defined later in this file:
  171.  */
  172.  
  173. static Window    LookupName __ARGS((Display *dpy, char_u *name, int delete, char_u **loose));
  174. static int    SendInit __ARGS((Display *dpy));
  175. static int    DoRegisterName __ARGS((Display *dpy, char_u *name));
  176. static void    DeleteAnyLingerer __ARGS((Display *dpy, Window w));
  177. static int    GetRegProp __ARGS((Display *dpy, char_u **regPropp, long_u *numItemsp, int domsg));
  178. static int    WaitForPend __ARGS((void *p));
  179. static int    WaitForReply __ARGS((void *p));
  180. static int    WindowValid __ARGS((Display *dpy, Window w));
  181. static void    ServerWait __ARGS((Display *dpy, Window w, EndCond endCond, void *endData, int localLoop, int seconds));
  182. static struct ServerReply *ServerReplyFind __ARGS((Window w, enum ServerReplyOp op));
  183. static int    AppendPropCarefully __ARGS((Display *display, Window window, Atom property, char_u *value, int length));
  184. static int    x_error_check __ARGS((Display *dpy, XErrorEvent *error_event));
  185. static int    IsSerialName __ARGS((char_u *name));
  186.  
  187. /* Private variables for the "server" functionality */
  188. static Atom    registryProperty = None;
  189. static Atom    vimProperty = None;
  190. static int    got_x_error = FALSE;
  191.  
  192. static char_u    *empty_prop = (char_u *)"";    /* empty GetRegProp() result */
  193.  
  194. /*
  195.  * Associate an ASCII name with Vim.  Try real hard to get a unique one.
  196.  * Returns FAIL or OK.
  197.  */
  198.     int
  199. serverRegisterName(dpy, name)
  200.     Display    *dpy;        /* display to register with */
  201.     char_u    *name;        /* the name that will be used as a base */
  202. {
  203.     int        i;
  204.     int        res;
  205.     char_u    *p = NULL;
  206.  
  207.     res = DoRegisterName(dpy, name);
  208.     if (res < 0)
  209.     {
  210.     i = 1;
  211.     do
  212.     {
  213.         if (res < -1 || i >= 1000)
  214.         {
  215.         MSG_ATTR(_("Unable to register a command server name"),
  216.                                   hl_attr(HLF_W));
  217.         return FAIL;
  218.         }
  219.         if (p == NULL)
  220.         p = alloc(STRLEN(name) + 10);
  221.         if (p == NULL)
  222.         {
  223.         res = -10;
  224.         continue;
  225.         }
  226.         sprintf((char *)p, "%s%d", name, i++);
  227.         res = DoRegisterName(dpy, p);
  228.     }
  229.     while (res < 0)
  230.         ;
  231.     vim_free(p);
  232.     }
  233.     return OK;
  234. }
  235.  
  236.     static int
  237. DoRegisterName(dpy, name)
  238.     Display    *dpy;
  239.     char_u    *name;
  240. {
  241.     Window    w;
  242.     XErrorHandler old_handler;
  243. #define MAX_NAME_LENGTH 100
  244.     char_u    propInfo[MAX_NAME_LENGTH + 20];
  245.  
  246.     if (commProperty == None)
  247.     {
  248.     if (SendInit(dpy) < 0)
  249.         return -2;
  250.     }
  251.  
  252.     /*
  253.      * Make sure the name is unique, and append info about it to
  254.      * the registry property.  It's important to lock the server
  255.      * here to prevent conflicting changes to the registry property.
  256.      * WARNING: Do not step through this while debugging, it will hangup the X
  257.      * server!
  258.      */
  259.     XGrabServer(dpy);
  260.     w = LookupName(dpy, name, FALSE, NULL);
  261.     if (w != (Window)0)
  262.     {
  263.     Status        status;
  264.     int        dummyInt;
  265.     unsigned int    dummyUns;
  266.     Window        dummyWin;
  267.  
  268.     /*
  269.      * The name is currently registered.  See if the commWindow
  270.      * associated with the name exists.  If not, or if the commWindow
  271.      * is *our* commWindow, then just unregister the old name (this
  272.      * could happen if an application dies without cleaning up the
  273.      * registry).
  274.      */
  275.     old_handler = XSetErrorHandler(x_error_check);
  276.     status = XGetGeometry(dpy, w, &dummyWin, &dummyInt, &dummyInt,
  277.                   &dummyUns, &dummyUns, &dummyUns, &dummyUns);
  278.     (void)XSetErrorHandler(old_handler);
  279.     if (status != Success && w != commWindow)
  280.     {
  281.         XUngrabServer(dpy);
  282.         XFlush(dpy);
  283.         return -1;
  284.     }
  285.     (void)LookupName(dpy, name, /*delete=*/TRUE, NULL);
  286.     }
  287.     sprintf((char *)propInfo, "%x %.*s", (int_u)commWindow,
  288.                                MAX_NAME_LENGTH, name);
  289.     old_handler = XSetErrorHandler(x_error_check);
  290.     got_x_error = FALSE;
  291.     XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING, 8,
  292.             PropModeAppend, propInfo, STRLEN(propInfo) + 1);
  293.     XUngrabServer(dpy);
  294.     XSync(dpy, False);
  295.     (void)XSetErrorHandler(old_handler);
  296.  
  297.     if (!got_x_error)
  298.     {
  299. #ifdef FEAT_EVAL
  300.     set_vim_var_string(VV_SEND_SERVER, name, -1);
  301. #endif
  302.     serverName = vim_strsave(name);
  303. #ifdef FEAT_TITLE
  304.     need_maketitle = TRUE;
  305. #endif
  306.     return 0;
  307.     }
  308.     return -2;
  309. }
  310.  
  311. #if defined(FEAT_GUI) || defined(PROTO)
  312. /*
  313.  * Clean out new ID from registry and set it as comm win.
  314.  * Change any registered window ID.
  315.  */
  316.     void
  317. serverChangeRegisteredWindow(dpy, newwin)
  318.     Display    *dpy;        /* Display to register with */
  319.     Window    newwin;        /* Re-register to this ID */
  320. {
  321.     char_u    propInfo[MAX_NAME_LENGTH + 20];
  322.  
  323.     commWindow = newwin;
  324.  
  325.     /* Always call SendInit() here, to make sure commWindow is marked as a Vim
  326.      * window. */
  327.     if (SendInit(dpy) < 0)
  328.     return;
  329.  
  330.     /* WARNING: Do not step through this while debugging, it will hangup the X
  331.      * server! */
  332.     XGrabServer(dpy);
  333.     DeleteAnyLingerer(dpy, newwin);
  334.     if (serverName != NULL)
  335.     {
  336.     /* Reinsert name if we was already registered */
  337.     (void)LookupName(dpy, serverName, /*delete=*/TRUE, NULL);
  338.     sprintf((char *)propInfo, "%x %.*s",
  339.         (int_u)newwin, MAX_NAME_LENGTH, serverName);
  340.     XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING, 8,
  341.             PropModeAppend, (char_u *)propInfo,
  342.             STRLEN(propInfo) + 1);
  343.     }
  344.     XUngrabServer(dpy);
  345. }
  346. #endif
  347.  
  348. /*
  349.  * Send to an instance of Vim via the X display.
  350.  * Returns 0 for OK, negative for an error.
  351.  */
  352.     int
  353. serverSendToVim(dpy, name, cmd,  result, server, asExpr, localLoop, silent)
  354.     Display    *dpy;            /* Where to send. */
  355.     char_u    *name;            /* Where to send. */
  356.     char_u    *cmd;            /* What to send. */
  357.     char_u    **result;        /* Result of eval'ed expression */
  358.     Window    *server;        /* Actual ID of receiving app */
  359.     Bool    asExpr;            /* Interpret as keystrokes or expr ? */
  360.     Bool    localLoop;        /* Throw away everything but result */
  361.     int        silent;            /* don't complain about no server */
  362. {
  363.     Window        w;
  364.     char_u        *property;
  365.     int            length;
  366.     int            res;
  367.     static int        serial = 0;    /* Running count of sent commands.
  368.                  * Used to give each command a
  369.                  * different serial number. */
  370.     PendingCommand  pending;
  371.     char_u        *loosename = NULL;
  372.  
  373.     if (result != NULL)
  374.     *result = NULL;
  375.     if (name == NULL || *name == NUL)
  376.     name = (char_u *)"GVIM";    /* use a default name */
  377.  
  378.     if (commProperty == None && dpy != NULL)
  379.     {
  380.     if (SendInit(dpy) < 0)
  381.         return -1;
  382.     }
  383.  
  384.     /* Execute locally if no display or target is ourselves */
  385.     if (dpy == NULL || (serverName != NULL && STRICMP(name, serverName) == 0))
  386.     {
  387.     if (asExpr)
  388.     {
  389.         char_u *ret;
  390.  
  391.         ++emsg_skip;
  392.         ret = eval_to_string(cmd, NULL);
  393.         --emsg_skip;
  394.         if (result != NULL)
  395.         {
  396.         if (ret == NULL)
  397.             *result = vim_strsave((char_u *)_(e_invexprmsg));
  398.         else
  399.             *result = ret;
  400.         }
  401.         else
  402.         vim_free(ret);
  403.         return ret == NULL ? -1 : 0;
  404.     }
  405.     else
  406.         server_to_input_buf(cmd);
  407.     return 0;
  408.     }
  409.  
  410.     /*
  411.      * Bind the server name to a communication window.
  412.      *
  413.      * Find any survivor with a serialno attached to the name if the
  414.      * original registrant of the wanted name is no longer present.
  415.      *
  416.      * Delete any lingering names from dead editors.
  417.      */
  418.     while (TRUE)
  419.     {
  420.     w = LookupName(dpy, name, FALSE, &loosename);
  421.     /* Check that the window is hot */
  422.     if (w != None)
  423.     {
  424.         if (!WindowValid(dpy, w))
  425.         {
  426.         LookupName(dpy, loosename ? loosename : name,
  427.                /*DELETE=*/TRUE, NULL);
  428.         continue;
  429.         }
  430.     }
  431.     break;
  432.     }
  433.     if (w == None)
  434.     {
  435.     if (!silent)
  436.         EMSG2(_(e_noserver), name);
  437.     return -1;
  438.     }
  439.     else if (loosename != NULL)
  440.     name = loosename;
  441.     if (server != NULL)
  442.     *server = w;
  443.  
  444.     /*
  445.      * Send the command to target interpreter by appending it to the
  446.      * comm window in the communication window.
  447.      */
  448.     length = STRLEN(name) + STRLEN(cmd) + 10;
  449.     property = (char_u *)alloc((unsigned) length + 30);
  450.  
  451.     sprintf((char *)property, "%c%c%c-n %s%c-s %s",
  452.               0, asExpr ? 'c' : 'k', 0, name, 0, cmd);
  453.     if (name == loosename)
  454.     vim_free(loosename);
  455.     /* Add a back reference to our comm window */
  456.     serial++;
  457.     sprintf((char *)property + length, "%c-r %x %d",
  458.                         0, (int_u)commWindow, serial);
  459.     length += STRLEN(property + length + 1) + 1;
  460.  
  461.     res = AppendPropCarefully(dpy, w, commProperty, property, length + 1);
  462.     vim_free(property);
  463.     if (res < 0)
  464.     {
  465.     EMSG(_("E248: Failed to send command to the destination program"));
  466.     return -1;
  467.     }
  468.  
  469.     if (!asExpr) /* There is no answer for this - Keys are sent async */
  470.     return 0;
  471.  
  472.     /*
  473.      * Register the fact that we're waiting for a command to
  474.      * complete (this is needed by SendEventProc and by
  475.      * AppendErrorProc to pass back the command's results).
  476.      */
  477.     pending.serial = serial;
  478.     pending.code = 0;
  479.     pending.result = NULL;
  480.     pending.nextPtr = pendingCommands;
  481.     pendingCommands = &pending;
  482.  
  483.     ServerWait(dpy, w, WaitForPend, &pending, localLoop, 600);
  484.  
  485.     /*
  486.      * Unregister the information about the pending command
  487.      * and return the result.
  488.      */
  489.     if (pendingCommands == &pending)
  490.     pendingCommands = pending.nextPtr;
  491.     else
  492.     {
  493.     PendingCommand *pcPtr;
  494.  
  495.     for (pcPtr = pendingCommands; pcPtr != NULL; pcPtr = pcPtr->nextPtr)
  496.         if (pcPtr->nextPtr == &pending)
  497.         {
  498.         pcPtr->nextPtr = pending.nextPtr;
  499.         break;
  500.         }
  501.     }
  502.     if (result != NULL)
  503.     *result = pending.result;
  504.     else
  505.     vim_free(pending.result);
  506.  
  507.     return pending.code == 0 ? 0 : -1;
  508. }
  509.  
  510.     static int
  511. WaitForPend(p)
  512.     void    *p;
  513. {
  514.     PendingCommand *pending = (PendingCommand *) p;
  515.     return pending->result != NULL;
  516. }
  517.  
  518. /*
  519.  * Return TRUE if window "w" exists and has a "Vim" property on it.
  520.  */
  521.     static int
  522. WindowValid(dpy, w)
  523.     Display     *dpy;
  524.     Window    w;
  525. {
  526.     XErrorHandler   old_handler;
  527.     Atom        *plist;
  528.     int            numProp;
  529.     int            i;
  530.  
  531.     old_handler = XSetErrorHandler(x_error_check);
  532.     got_x_error = 0;
  533.     plist = XListProperties(dpy, w, &numProp);
  534.     XSync(dpy, False);
  535.     XSetErrorHandler(old_handler);
  536.     if (plist == NULL || got_x_error)
  537.     return FALSE;
  538.  
  539.     for (i = 0; i < numProp; i++)
  540.     if (plist[i] == vimProperty)
  541.         return TRUE;
  542.     return FALSE;
  543. }
  544.  
  545. /*
  546.  * Enter a loop processing X events & polling chars until we see a result
  547.  */
  548.     static void
  549. ServerWait(dpy, w, endCond, endData, localLoop, seconds)
  550.     Display    *dpy;
  551.     Window    w;
  552.     EndCond    endCond;
  553.     void    *endData;
  554.     int        localLoop;
  555.     int        seconds;
  556. {
  557.     time_t        start;
  558.     time_t        now;
  559.     time_t        lastChk = 0;
  560.     XEvent        event;
  561.     XPropertyEvent *e = (XPropertyEvent *)&event;
  562. #   define SEND_MSEC_POLL 50
  563.  
  564.     time(&start);
  565.     while (endCond(endData) == 0)
  566.     {
  567.     time(&now);
  568.     if (seconds >= 0 && (now - start) >= seconds)
  569.         break;
  570.     if (now != lastChk)
  571.     {
  572.         lastChk = now;
  573.         if (!WindowValid(dpy, w))
  574.         break;
  575.         /*
  576.          * Sometimes the PropertyChange event doesn't come.
  577.          * This can be seen in eg: vim -c 'echo remote_expr("gvim", "3+2")'
  578.          */
  579.         serverEventProc(dpy, NULL);
  580.     }
  581.     if (localLoop)
  582.     {
  583.         /* Just look out for the answer without calling back into Vim */
  584. #ifndef HAVE_SELECT
  585.         struct pollfd   fds;
  586.  
  587.         fds.fd = ConnectionNumber(dpy);
  588.         fds.events = POLLIN;
  589.         if (poll(&fds, 1, SEND_MSEC_POLL) < 0)
  590.         break;
  591. #else
  592.         fd_set        fds;
  593.         struct timeval  tv;
  594.  
  595.         tv.tv_sec = 0;
  596.         tv.tv_usec =  SEND_MSEC_POLL * 1000;
  597.         FD_ZERO(&fds);
  598.         FD_SET(ConnectionNumber(dpy), &fds);
  599.         if (select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &tv) < 0)
  600.         break;
  601. #endif
  602.         while (XEventsQueued(dpy, QueuedAfterReading) > 0)
  603.         {
  604.         XNextEvent(dpy, &event);
  605.         if (event.type == PropertyNotify && e->window == commWindow)
  606.             serverEventProc(dpy, &event);
  607.         }
  608.     }
  609.     else
  610.     {
  611.         if (got_int)
  612.         break;
  613.         ui_delay((long)SEND_MSEC_POLL, TRUE);
  614.         ui_breakcheck();
  615.     }
  616.     }
  617. }
  618.  
  619.  
  620. /*
  621.  * Fetch a list of all the Vim instance names currently registered for the
  622.  * display.
  623.  *
  624.  * Returns a newline separated list in allocated memory or NULL.
  625.  */
  626.     char_u *
  627. serverGetVimNames(dpy)
  628.     Display    *dpy;
  629. {
  630.     char_u    *regProp;
  631.     char_u    *entry;
  632.     char_u    *p;
  633.     long_u    numItems;
  634.     int_u    w;
  635.     garray_T    ga;
  636.  
  637.     if (registryProperty == None)
  638.     {
  639.     if (SendInit(dpy) < 0)
  640.         return NULL;
  641.     }
  642.     ga_init2(&ga, 1, 100);
  643.  
  644.     /*
  645.      * Read the registry property.
  646.      */
  647.     if (GetRegProp(dpy, ®Prop, &numItems, TRUE) == FAIL)
  648.     return NULL;
  649.  
  650.     /*
  651.      * Scan all of the names out of the property.
  652.      */
  653.     ga_init2(&ga, 1, 100);
  654.     for (p = regProp; (p - regProp) < numItems; p++)
  655.     {
  656.     entry = p;
  657.     while (*p != 0 && !isspace(*p))
  658.         p++;
  659.     if (*p != 0)
  660.     {
  661.         w = None;
  662.         sscanf((char *)entry, "%x", &w);
  663.         if (WindowValid(dpy, (Window)w))
  664.         {
  665.         ga_concat(&ga, p + 1);
  666.         ga_concat(&ga, (char_u *)"\n");
  667.         }
  668.         while (*p != 0)
  669.         p++;
  670.     }
  671.     }
  672.     if (regProp != empty_prop)
  673.     XFree(regProp);
  674.     return ga.ga_data;
  675. }
  676.  
  677. /* ----------------------------------------------------------
  678.  * Reply stuff
  679.  */
  680.  
  681.     static struct ServerReply *
  682. ServerReplyFind(w, op)
  683.     Window  w;
  684.     enum ServerReplyOp op;
  685. {
  686.     struct ServerReply *p;
  687.     struct ServerReply e;
  688.     int        i;
  689.  
  690.     p = (struct ServerReply *) serverReply.ga_data;
  691.     for (i = 0; i < serverReply.ga_len; i++, p++)
  692.     if (p->id == w)
  693.         break;
  694.     if (i >= serverReply.ga_len)
  695.     p = NULL;
  696.  
  697.     if (p == NULL && op == SROP_Add)
  698.     {
  699.     if (serverReply.ga_growsize == 0)
  700.         ga_init2(&serverReply, sizeof(struct ServerReply), 1);
  701.     if (ga_grow(&serverReply, 1) == OK)
  702.     {
  703.         p = ((struct ServerReply *) serverReply.ga_data)
  704.         + serverReply.ga_len;
  705.         e.id = w;
  706.         ga_init2(&e.strings, 1, 100);
  707.         memcpy(p, &e, sizeof(e));
  708.         serverReply.ga_len++;
  709.         serverReply.ga_room--;
  710.     }
  711.     }
  712.     else if (p != NULL && op == SROP_Delete)
  713.     {
  714.     ga_clear(&p->strings);
  715.     mch_memmove(p, p + 1, (serverReply.ga_len - i - 1) * sizeof(*p));
  716.     serverReply.ga_len--;
  717.     serverReply.ga_room++;
  718.     }
  719.  
  720.     return p;
  721. }
  722.  
  723. /*
  724.  * Convert string to windowid.
  725.  * Issue an error if the id is invalid.
  726.  */
  727.     Window
  728. serverStrToWin(str)
  729.     char_u  *str;
  730. {
  731.     unsigned  id = None;
  732.  
  733.     sscanf((char *)str, "0x%x", &id);
  734.     if (id == None)
  735.     EMSG2(_("E573: Invalid server id used: %s"), str);
  736.  
  737.     return (Window)id;
  738. }
  739.  
  740. /*
  741.  * Send a reply string to client with id "name".
  742.  * Return -1 if the window is invalid.
  743.  */
  744.     int
  745. serverSendReply(name, str)
  746.     char_u    *name;
  747.     char_u    *str;
  748. {
  749.     char_u    *property;
  750.     int        length;
  751.     int        res;
  752.     Display    *dpy = X_DISPLAY;
  753.     Window    win = serverStrToWin(name);
  754.  
  755.     if (commProperty == None)
  756.     {
  757.     if (SendInit(dpy) < 0)
  758.         return -2;
  759.     }
  760.     if (!WindowValid(dpy, win))
  761.     return -1;
  762.  
  763.     length = STRLEN(str) + 7;
  764.     if ((property = (char_u *)alloc((unsigned) length + 30)) != NULL)
  765.     {
  766.     sprintf((char *)property, "%c%c%c-n %s%c-w %x",
  767.               0, 'n', 0, str, 0, (unsigned int)commWindow);
  768.     length += STRLEN(property + length);
  769.     res = AppendPropCarefully(dpy, win, commProperty, property, length + 1);
  770.     vim_free(property);
  771.     return res;
  772.     }
  773.     return -1;
  774. }
  775.  
  776.     static int
  777. WaitForReply(p)
  778.     void    *p;
  779. {
  780.     Window  *w = (Window *) p;
  781.     return ServerReplyFind(*w, SROP_Find) != NULL;
  782. }
  783.  
  784. /*
  785.  * Wait for replies from id (win)
  786.  * Return 0 and the malloc'ed string when a reply is available.
  787.  * Return -1 if the window becomes invalid while waiting.
  788.  */
  789.     int
  790. serverReadReply(dpy, win, str, localLoop)
  791.     Display    *dpy;
  792.     Window    win;
  793.     char_u    **str;
  794.     int        localLoop;
  795. {
  796.     int        len;
  797.     char_u    *s;
  798.     struct    ServerReply *p;
  799.  
  800.     ServerWait(dpy, win, WaitForReply, &win, localLoop, -1);
  801.  
  802.     if ((p = ServerReplyFind(win, SROP_Find)) != NULL && p->strings.ga_len > 0)
  803.     {
  804.     *str = vim_strsave(p->strings.ga_data);
  805.     len = STRLEN(*str) + 1;
  806.     if (len < p->strings.ga_len)
  807.     {
  808.         s = (char_u *) p->strings.ga_data;
  809.         mch_memmove(s, s + len, p->strings.ga_len - len);
  810.         p->strings.ga_room += len;
  811.         p->strings.ga_len -= len;
  812.     }
  813.     else
  814.     {
  815.         /* Last string read.  Remove from list */
  816.         ga_clear(&p->strings);
  817.         ServerReplyFind(win, SROP_Delete);
  818.     }
  819.     return 0;
  820.     }
  821.     return -1;
  822. }
  823.  
  824. /*
  825.  * Check for replies from id (win).
  826.  * Return TRUE and a non-malloc'ed string if there is.  Else return FALSE.
  827.  */
  828.     int
  829. serverPeekReply(dpy, win, str)
  830.     Display *dpy;
  831.     Window win;
  832.     char_u **str;
  833. {
  834.     struct ServerReply *p;
  835.  
  836.     if ((p = ServerReplyFind(win, SROP_Find)) != NULL && p->strings.ga_len > 0)
  837.     {
  838.     if (str != NULL)
  839.         *str = p->strings.ga_data;
  840.     return 1;
  841.     }
  842.     if (!WindowValid(dpy, win))
  843.     return -1;
  844.     return 0;
  845. }
  846.  
  847.  
  848. /*
  849.  * Initialize the communication channels for sending commands and receiving
  850.  * results.
  851.  */
  852.     static int
  853. SendInit(dpy)
  854.     Display *dpy;
  855. {
  856.     XErrorHandler old_handler;
  857.  
  858.     /*
  859.      * Create the window used for communication, and set up an
  860.      * event handler for it.
  861.      */
  862.     old_handler = XSetErrorHandler(x_error_check);
  863.     got_x_error = FALSE;
  864.  
  865.     if (commProperty == None)
  866.     commProperty = XInternAtom(dpy, "Comm", False);
  867.     if (vimProperty == None)
  868.     vimProperty = XInternAtom(dpy, "Vim", False);
  869.     if (registryProperty == None)
  870.     registryProperty = XInternAtom(dpy, "VimRegistry", False);
  871.  
  872.     if (commWindow == None)
  873.     {
  874.     commWindow = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy),
  875.                 getpid(), 0, 10, 10, 0,
  876.                 WhitePixel(dpy, DefaultScreen(dpy)),
  877.                 WhitePixel(dpy, DefaultScreen(dpy)));
  878.     XSelectInput(dpy, commWindow, PropertyChangeMask);
  879.     /* WARNING: Do not step through this while debugging, it will hangup
  880.      * the X server! */
  881.     XGrabServer(dpy);
  882.     DeleteAnyLingerer(dpy, commWindow);
  883.     XUngrabServer(dpy);
  884.     }
  885.  
  886.     /* Make window recognizable as a vim window */
  887.     XChangeProperty(dpy, commWindow, vimProperty, XA_STRING,
  888.             8, PropModeReplace, (char_u *)VIM_VERSION_SHORT,
  889.             (int)STRLEN(VIM_VERSION_SHORT) + 1);
  890.  
  891.     XSync(dpy, False);
  892.     (void)XSetErrorHandler(old_handler);
  893.  
  894.     return got_x_error ? -1 : 0;
  895. }
  896.  
  897. /*
  898.  * Given a server name, see if the name exists in the registry for a
  899.  * particular display.
  900.  *
  901.  * If the given name is registered, return the ID of the window associated
  902.  * with the name.  If the name isn't registered, then return 0.
  903.  *
  904.  * Side effects:
  905.  *    If the registry property is improperly formed, then it is deleted.
  906.  *    If "delete" is non-zero, then if the named server is found it is
  907.  *    removed from the registry property.
  908.  */
  909.     static Window
  910. LookupName(dpy, name, delete, loose)
  911.     Display    *dpy;        /* Display whose registry to check. */
  912.     char_u    *name;        /* Name of a server. */
  913.     int        delete;        /* If non-zero, delete info about name. */
  914.     char_u    **loose;    /* Do another search matching -999 if not found
  915.                    Return result here if a match is found */
  916. {
  917.     char_u    *regProp, *entry;
  918.     char_u    *p;
  919.     long_u    numItems;
  920.     int_u    returnValue;
  921.  
  922.     /*
  923.      * Read the registry property.
  924.      */
  925.     if (GetRegProp(dpy, ®Prop, &numItems, FALSE) == FAIL)
  926.     return 0;
  927.  
  928.     /*
  929.      * Scan the property for the desired name.
  930.      */
  931.     returnValue = (int_u)None;
  932.     entry = NULL;    /* Not needed, but eliminates compiler warning. */
  933.     for (p = regProp; (p - regProp) < numItems; )
  934.     {
  935.     entry = p;
  936.     while (*p != 0 && !isspace(*p))
  937.         p++;
  938.     if (*p != 0 && STRICMP(name, p + 1) == 0)
  939.     {
  940.         sscanf((char *)entry, "%x", &returnValue);
  941.         break;
  942.     }
  943.     while (*p != 0)
  944.         p++;
  945.     p++;
  946.     }
  947.  
  948.     if (loose != NULL && returnValue == (int_u)None && !IsSerialName(name))
  949.     {
  950.     for (p = regProp; (p - regProp) < numItems; )
  951.     {
  952.         entry = p;
  953.         while (*p != 0 && !isspace(*p))
  954.         p++;
  955.         if (*p != 0 && IsSerialName(p + 1)
  956.             && STRNICMP(name, p + 1, STRLEN(name)) == 0)
  957.         {
  958.         sscanf((char *)entry, "%x", &returnValue);
  959.         *loose = vim_strsave(p + 1);
  960.         break;
  961.         }
  962.         while (*p != 0)
  963.         p++;
  964.         p++;
  965.     }
  966.     }
  967.  
  968.     /*
  969.      * Delete the property, if that is desired (copy down the
  970.      * remainder of the registry property to overlay the deleted
  971.      * info, then rewrite the property).
  972.      */
  973.     if (delete && returnValue != (int_u)None)
  974.     {
  975.     int count;
  976.  
  977.     while (*p != 0)
  978.         p++;
  979.     p++;
  980.     count = numItems - (p - regProp);
  981.     if (count > 0)
  982.         memcpy(entry, p, count);
  983.     XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING,
  984.             8, PropModeReplace, regProp,
  985.             (int)(numItems - (p - entry)));
  986.     XSync(dpy, False);
  987.     }
  988.  
  989.     if (regProp != empty_prop)
  990.     XFree(regProp);
  991.     return (Window)returnValue;
  992. }
  993.  
  994. /*
  995.  * Delete any lingering occurences of window id.  We promise that any
  996.  * occurences is not ours since it is not yet put into the registry (by us)
  997.  *
  998.  * This is necessary in the following scenario:
  999.  * 1. There is an old windowid for an exit'ed vim in the registry
  1000.  * 2. We get that id for our commWindow but only want to send, not register.
  1001.  * 3. The window will mistakenly be regarded valid because of own commWindow
  1002.  */
  1003.     static void
  1004. DeleteAnyLingerer(dpy, win)
  1005.     Display    *dpy;    /* Display whose registry to check. */
  1006.     Window    win;    /* Window to remove */
  1007. {
  1008.     char_u    *regProp, *entry = NULL;
  1009.     char_u    *p;
  1010.     long_u    numItems;
  1011.     Window    wwin;
  1012.  
  1013.     /*
  1014.      * Read the registry property.
  1015.      */
  1016.     if (GetRegProp(dpy, ®Prop, &numItems, FALSE) == FAIL)
  1017.     return;
  1018.  
  1019.     /* Scan the property for the window id.  */
  1020.     for (p = regProp; (p - regProp) < numItems; )
  1021.     {
  1022.     if (*p != 0)
  1023.     {
  1024.         sscanf((char *)p, "%x", (int_u *)&wwin);
  1025.         if (wwin == win)
  1026.         {
  1027.         int lastHalf;
  1028.  
  1029.         /* Copy down the remainder to delete entry */
  1030.         entry = p;
  1031.         while (*p != 0)
  1032.             p++;
  1033.         p++;
  1034.         lastHalf = numItems - (p - regProp);
  1035.         if (lastHalf > 0)
  1036.             memcpy(entry, p, lastHalf);
  1037.         numItems = (entry - regProp) + lastHalf;
  1038.         p = entry;
  1039.         continue;
  1040.         }
  1041.     }
  1042.     while (*p != 0)
  1043.         p++;
  1044.     p++;
  1045.     }
  1046.  
  1047.     if (entry != NULL)
  1048.     {
  1049.     XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty,
  1050.             XA_STRING, 8, PropModeReplace, regProp,
  1051.             (int)(p - regProp));
  1052.     XSync(dpy, False);
  1053.     }
  1054.  
  1055.     if (regProp != empty_prop)
  1056.     XFree(regProp);
  1057. }
  1058.  
  1059. /*
  1060.  * Read the registry property.  Delete it when it's formatted wrong.
  1061.  * Return the property in "regPropp".  "empty_prop" is used when it doesn't
  1062.  * exist yet.
  1063.  * Return OK when successful.
  1064.  */
  1065.     static int
  1066. GetRegProp(dpy, regPropp, numItemsp, domsg)
  1067.     Display    *dpy;
  1068.     char_u    **regPropp;
  1069.     long_u    *numItemsp;
  1070.     int        domsg;        /* When TRUE give error message. */
  1071. {
  1072.     int        result, actualFormat;
  1073.     long_u    bytesAfter;
  1074.     Atom    actualType;
  1075.  
  1076.     *regPropp = NULL;
  1077.     result = XGetWindowProperty(dpy, RootWindow(dpy, 0), registryProperty, 0L,
  1078.                 (long)MAX_PROP_WORDS, False,
  1079.                 XA_STRING, &actualType,
  1080.                 &actualFormat, numItemsp, &bytesAfter,
  1081.                 regPropp);
  1082.  
  1083.     if (actualType == None)
  1084.     {
  1085.     /* No prop yet. Logically equal to the empty list */
  1086.     *numItemsp = 0;
  1087.     *regPropp = empty_prop;
  1088.     return OK;
  1089.     }
  1090.  
  1091.     /* If the property is improperly formed, then delete it. */
  1092.     if (result != Success || actualFormat != 8 || actualType != XA_STRING)
  1093.     {
  1094.     if (*regPropp != NULL)
  1095.         XFree(*regPropp);
  1096.     XDeleteProperty(dpy, RootWindow(dpy, 0), registryProperty);
  1097.     if (domsg)
  1098.         EMSG(_("E251: VIM instance registry property is badly formed.  Deleted!"));
  1099.     return FAIL;
  1100.     }
  1101.     return OK;
  1102. }
  1103.  
  1104. /*
  1105.  * This procedure is invoked by the varous X event loops throughout Vims when
  1106.  * a property changes on the communication window.  This procedure reads the
  1107.  * property and handles command requests and responses.
  1108.  */
  1109.     void
  1110. serverEventProc(dpy, eventPtr)
  1111.     Display    *dpy;
  1112.     XEvent    *eventPtr;        /* Information about event. */
  1113. {
  1114.     char_u    *propInfo;
  1115.     char_u    *p;
  1116.     int        result, actualFormat, code;
  1117.     long_u    numItems, bytesAfter;
  1118.     Atom    actualType;
  1119.  
  1120.     if (eventPtr != NULL)
  1121.     {
  1122.     if (eventPtr->xproperty.atom != commProperty
  1123.         || eventPtr->xproperty.state != PropertyNewValue)
  1124.         return;
  1125.     }
  1126.  
  1127.     /*
  1128.      * Read the comm property and delete it.
  1129.      */
  1130.     propInfo = NULL;
  1131.     result = XGetWindowProperty(dpy, commWindow, commProperty, 0L,
  1132.                 (long)MAX_PROP_WORDS, True,
  1133.                 XA_STRING, &actualType,
  1134.                 &actualFormat, &numItems, &bytesAfter,
  1135.                 &propInfo);
  1136.  
  1137.     /* If the property doesn't exist or is improperly formed then ignore it. */
  1138.     if (result != Success || actualType != XA_STRING || actualFormat != 8)
  1139.     {
  1140.     if (propInfo != NULL)
  1141.         XFree(propInfo);
  1142.     return;
  1143.     }
  1144.  
  1145.     /*
  1146.      * Several commands and results could arrive in the property at
  1147.      * one time;  each iteration through the outer loop handles a
  1148.      * single command or result.
  1149.      */
  1150.     for (p = propInfo; (p - propInfo) < numItems; )
  1151.     {
  1152.     /*
  1153.      * Ignore leading NULs; each command or result starts with a
  1154.      * NUL so that no matter how badly formed a preceding command
  1155.      * is, we'll be able to tell that a new command/result is
  1156.      * starting.
  1157.      */
  1158.     if (*p == 0)
  1159.     {
  1160.         p++;
  1161.         continue;
  1162.     }
  1163.  
  1164.     if ((*p == 'c' || *p == 'k') && (p[1] == 0))
  1165.     {
  1166.         Window    resWindow;
  1167.         char_u    *name, *script, *serial, *end, *res;
  1168.         Bool    asKeys = *p == 'k';
  1169.         garray_T    reply;
  1170.  
  1171.         /*
  1172.          * This is an incoming command from some other application.
  1173.          * Iterate over all of its options.  Stop when we reach
  1174.          * the end of the property or something that doesn't look
  1175.          * like an option.
  1176.          */
  1177.         p += 2;
  1178.         name = NULL;
  1179.         resWindow = None;
  1180.         serial = (char_u *)"";
  1181.         script = NULL;
  1182.         while (p - propInfo < numItems && *p == '-')
  1183.         {
  1184.         switch (p[1])
  1185.         {
  1186.             case 'r':
  1187.             end = skipwhite(p + 2);
  1188.             resWindow = 0;
  1189.             while (isxdigit(*end))
  1190.             {
  1191.                 resWindow = 16 * resWindow + (long_u)hex2nr(*end);
  1192.                 ++end;
  1193.             }
  1194.             if (end == p + 2 || *end != ' ')
  1195.                 resWindow = None;
  1196.             else
  1197.             {
  1198.                 p = serial = end + 1;
  1199.                 clientWindow = resWindow; /* Remember in global */
  1200.             }
  1201.             break;
  1202.             case 'n':
  1203.             if (p[2] == ' ')
  1204.                 name = p + 3;
  1205.             break;
  1206.             case 's':
  1207.             if (p[2] == ' ')
  1208.                 script = p + 3;
  1209.             break;
  1210.         }
  1211.         while (*p != 0)
  1212.             p++;
  1213.         p++;
  1214.         }
  1215.  
  1216.         if (script == NULL || name == NULL)
  1217.         continue;
  1218.  
  1219.         /*
  1220.          * Initialize the result property, so that we're ready at any
  1221.          * time if we need to return an error.
  1222.          */
  1223.         if (resWindow != None)
  1224.         {
  1225.         ga_init2(&reply, 1, 100);
  1226.         ga_grow(&reply, 50);
  1227.         sprintf(reply.ga_data, "%cr%c-s %s%c-r ", 0, 0, serial, 0);
  1228.         reply.ga_len = 10 + STRLEN(serial);
  1229.         reply.ga_room -= reply.ga_len;
  1230.         }
  1231.         res = NULL;
  1232.         if (serverName != NULL && STRICMP(name, serverName) == 0)
  1233.         {
  1234.         if (asKeys)
  1235.             server_to_input_buf(script);
  1236.         else
  1237.         {
  1238.             ++emsg_skip;
  1239.             res = eval_to_string(script, NULL);
  1240.             --emsg_skip;
  1241.         }
  1242.         }
  1243.         if (resWindow != None)
  1244.         {
  1245.         if (res != NULL)
  1246.             ga_concat(&reply, res);
  1247.         else if (asKeys == 0)
  1248.         {
  1249.             ga_concat(&reply, (char_u *)_(e_invexprmsg));
  1250.             ga_append(&reply, 0);
  1251.             ga_concat(&reply, (char_u *)"-c 1");
  1252.         }
  1253.         ga_append(&reply, 0);
  1254.         (void)AppendPropCarefully(dpy, resWindow, commProperty,
  1255.                        reply.ga_data, reply.ga_len);
  1256.         }
  1257.         vim_free(res);
  1258.     }
  1259.     else if (*p == 'r' && p[1] == 0)
  1260.     {
  1261.         int        serial, gotSerial;
  1262.         char_u  *res;
  1263.         PendingCommand *pcPtr;
  1264.  
  1265.         /*
  1266.          * This is a reply to some command that we sent out.  Iterate
  1267.          * over all of its options.  Stop when we reach the end of the
  1268.          * property or something that doesn't look like an option.
  1269.          */
  1270.         p += 2;
  1271.         gotSerial = 0;
  1272.         res = (char_u *)"";
  1273.         code = 0;
  1274.         while ((p-propInfo) < numItems && *p == '-')
  1275.         {
  1276.         switch (p[1])
  1277.         {
  1278.             case 'r':
  1279.             if (p[2] == ' ')
  1280.                 res = p + 3;
  1281.             break;
  1282.             case 's':
  1283.             if (sscanf((char *)p + 2, " %d", &serial) == 1)
  1284.                 gotSerial = 1;
  1285.             break;
  1286.             case 'c':
  1287.             if (sscanf((char *)p + 2, " %d", &code) != 1)
  1288.                 code = 0;
  1289.             break;
  1290.         }
  1291.         while (*p != 0)
  1292.             p++;
  1293.         p++;
  1294.         }
  1295.  
  1296.         if (!gotSerial)
  1297.         continue;
  1298.  
  1299.         /*
  1300.          * Give the result information to anyone who's
  1301.          * waiting for it.
  1302.          */
  1303.         for (pcPtr = pendingCommands; pcPtr != NULL; pcPtr = pcPtr->nextPtr)
  1304.         {
  1305.         if (serial != pcPtr->serial || pcPtr->result != NULL)
  1306.             continue;
  1307.  
  1308.         pcPtr->code = code;
  1309.         if (res != NULL)
  1310.             pcPtr->result = vim_strsave(res);
  1311.         else
  1312.             pcPtr->result = vim_strsave((char_u *)"");
  1313.         break;
  1314.         }
  1315.     }
  1316.     else if (*p == 'n' && p[1] == 0)
  1317.     {
  1318.         Window    win = 0;
  1319.         unsigned int u;
  1320.         int        gotWindow;
  1321.         char_u    *str;
  1322.         char_u    winstr[30];
  1323.         struct    ServerReply *r;
  1324.  
  1325.         /*
  1326.          * This is a (n)otification.  Sent with serverreply_send in VimL.
  1327.          * Execute any autocommand and save it for later retrieval
  1328.          */
  1329.         p += 2;
  1330.         gotWindow = 0;
  1331.         str = (char_u *)"";
  1332.         while ((p-propInfo) < numItems && *p == '-')
  1333.         {
  1334.         switch (p[1])
  1335.         {
  1336.             case 'n':
  1337.             if (p[2] == ' ')
  1338.                 str = p + 3;
  1339.             break;
  1340.             case 'w':
  1341.             if (sscanf((char *)p + 2, " %x", &u) == 1)
  1342.             {
  1343.                 win = u;
  1344.                 gotWindow = 1;
  1345.             }
  1346.             break;
  1347.         }
  1348.         while (*p != 0)
  1349.             p++;
  1350.         p++;
  1351.         }
  1352.  
  1353.         if (!gotWindow)
  1354.         continue;
  1355.         if ((r = ServerReplyFind(win, SROP_Add)) != NULL)
  1356.         {
  1357.         ga_concat(&(r->strings), str);
  1358.         ga_append(&(r->strings), 0);
  1359.         }
  1360. #ifdef FEAT_AUTOCMD
  1361.         sprintf((char *)winstr, "0x%x", (unsigned int)win);
  1362.         apply_autocmds(EVENT_REMOTEREPLY, winstr, str, TRUE, curbuf);
  1363. #endif
  1364.  
  1365.     }
  1366.     else
  1367.     {
  1368.         /*
  1369.          * Didn't recognize this thing.  Just skip through the next
  1370.          * null character and try again.
  1371.          * Even if we get an 'r'(eply) we will throw it away as we
  1372.          * never specify (and thus expect) one
  1373.          */
  1374.         while (*p != 0)
  1375.         p++;
  1376.         p++;
  1377.     }
  1378.     }
  1379.     XFree(propInfo);
  1380. }
  1381.  
  1382. /*
  1383.  * Append a given property to a given window, but set up an X error handler so
  1384.  * that if the append fails this procedure can return an error code rather
  1385.  * than having Xlib panic.
  1386.  * Return: 0 for OK, -1 for error
  1387.  */
  1388.     static int
  1389. AppendPropCarefully(dpy, window, property, value, length)
  1390.     Display    *dpy;        /* Display on which to operate. */
  1391.     Window    window;        /* Window whose property is to be modified. */
  1392.     Atom    property;    /* Name of property. */
  1393.     char_u    *value;        /* Characters  to append to property. */
  1394.     int        length;        /* How much to append */
  1395. {
  1396.     XErrorHandler old_handler;
  1397.  
  1398.     old_handler = XSetErrorHandler(x_error_check);
  1399.     got_x_error = FALSE;
  1400.     XChangeProperty(dpy, window, property, XA_STRING, 8,
  1401.                            PropModeAppend, value, length);
  1402.     XSync(dpy, False);
  1403.     (void) XSetErrorHandler(old_handler);
  1404.     return got_x_error ? -1 : 0;
  1405. }
  1406.  
  1407.  
  1408. /*
  1409.  * Another X Error handler, just used to check for errors.
  1410.  */
  1411. /* ARGSUSED */
  1412.     static int
  1413. x_error_check(dpy, error_event)
  1414.     Display    *dpy;
  1415.     XErrorEvent    *error_event;
  1416. {
  1417.     got_x_error = TRUE;
  1418.     return 0;
  1419. }
  1420.  
  1421. /*
  1422.  * Check if "str" looks like it had a serial number appended.
  1423.  * Actually just checks if the name ends in a digit.
  1424.  */
  1425.     static int
  1426. IsSerialName(str)
  1427.     char_u    *str;
  1428. {
  1429.     int len = STRLEN(str);
  1430.  
  1431.     return (len > 1 && isdigit(str[len - 1]));
  1432. }
  1433. #endif    /* FEAT_CLIENTSERVER */
  1434.