tkUnixSend.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:54k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkUnixSend.c --
  3.  *
  4.  * This file provides procedures that implement the "send"
  5.  * command, allowing commands to be passed from interpreter
  6.  * to interpreter.
  7.  *
  8.  * Copyright (c) 1989-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1996 Sun Microsystems, Inc.
  10.  * Copyright (c) 1998-1999 by Scriptics Corporation.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * RCS: @(#) $Id: tkUnixSend.c,v 1.11 2002/08/13 16:20:49 rmax Exp $
  16.  */
  17. #include "tkPort.h"
  18. #include "tkInt.h"
  19. #include "tkUnixInt.h"
  20. /* 
  21.  * The following structure is used to keep track of the interpreters
  22.  * registered by this process.
  23.  */
  24. typedef struct RegisteredInterp {
  25.     char *name; /* Interpreter's name (malloc-ed). */
  26.     Tcl_Interp *interp; /* Interpreter associated with name.  NULL
  27.  * means that the application was unregistered
  28.  * or deleted while a send was in progress
  29.  * to it. */
  30.     TkDisplay *dispPtr; /* Display for the application.  Needed
  31.  * because we may need to unregister the
  32.  * interpreter after its main window has
  33.  * been deleted. */
  34.     struct RegisteredInterp *nextPtr;
  35. /* Next in list of names associated
  36.  * with interps in this process.
  37.  * NULL means end of list. */
  38. } RegisteredInterp;
  39. /*
  40.  * A registry of all interpreters for a display is kept in a
  41.  * property "InterpRegistry" on the root window of the display.
  42.  * It is organized as a series of zero or more concatenated strings
  43.  * (in no particular order), each of the form
  44.  *  window space name ''
  45.  * where "window" is the hex id of the comm. window to use to talk
  46.  * to an interpreter named "name".
  47.  *
  48.  * When the registry is being manipulated by an application (e.g. to
  49.  * add or remove an entry), it is loaded into memory using a structure
  50.  * of the following type:
  51.  */
  52. typedef struct NameRegistry {
  53.     TkDisplay *dispPtr; /* Display from which the registry was
  54.  * read. */
  55.     int locked; /* Non-zero means that the display was
  56.  * locked when the property was read in. */
  57.     int modified; /* Non-zero means that the property has
  58.  * been modified, so it needs to be written
  59.  * out when the NameRegistry is closed. */
  60.     unsigned long propLength; /* Length of the property, in bytes. */
  61.     char *property; /* The contents of the property, or NULL
  62.  * if none.  See format description above;
  63.  * this is *not* terminated by the first
  64.  * null character.  Dynamically allocated. */
  65.     int allocedByX; /* Non-zero means must free property with
  66.  * XFree;  zero means use ckfree. */
  67. } NameRegistry;
  68. /*
  69.  * When a result is being awaited from a sent command, one of
  70.  * the following structures is present on a list of all outstanding
  71.  * sent commands.  The information in the structure is used to
  72.  * process the result when it arrives.  You're probably wondering
  73.  * how there could ever be multiple outstanding sent commands.
  74.  * This could happen if interpreters invoke each other recursively.
  75.  * It's unlikely, but possible.
  76.  */
  77. typedef struct PendingCommand {
  78.     int serial; /* Serial number expected in
  79.  * result. */
  80.     TkDisplay *dispPtr; /* Display being used for communication. */
  81.     CONST char *target; /* Name of interpreter command is
  82.  * being sent to. */
  83.     Window commWindow; /* Target's communication window. */
  84.     Tcl_Interp *interp; /* Interpreter from which the send
  85.  * was invoked. */
  86.     int code; /* Tcl return code for command
  87.  * will be stored here. */
  88.     char *result; /* String result for command (malloc'ed),
  89.  * or NULL. */
  90.     char *errorInfo; /* Information for "errorInfo" variable,
  91.  * or NULL (malloc'ed). */
  92.     char *errorCode; /* Information for "errorCode" variable,
  93.  * or NULL (malloc'ed). */
  94.     int gotResponse; /* 1 means a response has been received,
  95.  * 0 means the command is still outstanding. */
  96.     struct PendingCommand *nextPtr;
  97. /* Next in list of all outstanding
  98.  * commands.  NULL means end of
  99.  * list. */
  100. } PendingCommand;
  101. typedef struct ThreadSpecificData {
  102.     PendingCommand *pendingCommands;
  103.                                 /* List of all commands currently
  104.  * being waited for. */
  105.     RegisteredInterp *interpListPtr;
  106.                                 /* List of all interpreters registered
  107.  * in the current process. */
  108. } ThreadSpecificData;
  109. static Tcl_ThreadDataKey dataKey;
  110. /*
  111.  * The information below is used for communication between processes
  112.  * during "send" commands.  Each process keeps a private window, never
  113.  * even mapped, with one property, "Comm".  When a command is sent to
  114.  * an interpreter, the command is appended to the comm property of the
  115.  * communication window associated with the interp's process.  Similarly,
  116.  * when a result is returned from a sent command, it is also appended
  117.  * to the comm property.
  118.  *
  119.  * Each command and each result takes the form of ASCII text.  For a
  120.  * command, the text consists of a zero character followed by several
  121.  * null-terminated ASCII strings.  The first string consists of the
  122.  * single letter "c".  Subsequent strings have the form "option value"
  123.  * where the following options are supported:
  124.  *
  125.  * -r commWindow serial
  126.  *
  127.  * This option means that a response should be sent to the window
  128.  * whose X identifier is "commWindow" (in hex), and the response should
  129.  * be identified with the serial number given by "serial" (in decimal).
  130.  * If this option isn't specified then the send is asynchronous and
  131.  * no response is sent.
  132.  *
  133.  * -n name
  134.  * "Name" gives the name of the application for which the command is
  135.  * intended.  This option must be present.
  136.  *
  137.  * -s script
  138.  *
  139.  * "Script" is the script to be executed.  This option must be present.
  140.  *
  141.  * The options may appear in any order.  The -n and -s options must be
  142.  * present, but -r may be omitted for asynchronous RPCs.  For compatibility
  143.  * with future releases that may add new features, there may be additional
  144.  * options present;  as long as they start with a "-" character, they will
  145.  * be ignored.
  146.  *
  147.  * A result also consists of a zero character followed by several null-
  148.  * terminated ASCII strings.  The first string consists of the single
  149.  * letter "r".  Subsequent strings have the form "option value" where
  150.  * the following options are supported:
  151.  *
  152.  * -s serial
  153.  *
  154.  * Identifies the command for which this is the result.  It is the
  155.  * same as the "serial" field from the -s option in the command.  This
  156.  * option must be present.
  157.  *
  158.  * -c code
  159.  *
  160.  * "Code" is the completion code for the script, in decimal.  If the
  161.  * code is omitted it defaults to TCL_OK.
  162.  *
  163.  * -r result
  164.  *
  165.  * "Result" is the result string for the script, which may be either
  166.  * a result or an error message.  If this field is omitted then it
  167.  * defaults to an empty string.
  168.  *
  169.  * -i errorInfo
  170.  *
  171.  * "ErrorInfo" gives a string with which to initialize the errorInfo
  172.  * variable.  This option may be omitted;  it is ignored unless the
  173.  * completion code is TCL_ERROR.
  174.  *
  175.  * -e errorCode
  176.  *
  177.  * "ErrorCode" gives a string with with to initialize the errorCode
  178.  * variable.  This option may be omitted;  it is ignored  unless the
  179.  * completion code is TCL_ERROR.
  180.  *
  181.  * Options may appear in any order, and only the -s option must be
  182.  * present.  As with commands, there may be additional options besides
  183.  * these;  unknown options are ignored.
  184.  */
  185. /*
  186.  * The following variable is the serial number that was used in the
  187.  * last "send" command.  It is exported only for testing purposes.
  188.  */
  189. int tkSendSerial = 0;
  190. /*
  191.  * Maximum size property that can be read at one time by
  192.  * this module:
  193.  */
  194. #define MAX_PROP_WORDS 100000
  195. /*
  196.  * The following variable can be set while debugging to do things like
  197.  * skip locking the server.
  198.  */
  199. static int sendDebug = 0;
  200. /*
  201.  * Forward declarations for procedures defined later in this file:
  202.  */
  203. static int AppendErrorProc _ANSI_ARGS_((ClientData clientData,
  204. XErrorEvent *errorPtr));
  205. static void AppendPropCarefully _ANSI_ARGS_((Display *display,
  206.     Window window, Atom property, char *value,
  207.     int length, PendingCommand *pendingPtr));
  208. static void DeleteProc _ANSI_ARGS_((ClientData clientData));
  209. static void RegAddName _ANSI_ARGS_((NameRegistry *regPtr,
  210.     CONST char *name, Window commWindow));
  211. static void RegClose _ANSI_ARGS_((NameRegistry *regPtr));
  212. static void RegDeleteName _ANSI_ARGS_((NameRegistry *regPtr,
  213.     CONST char *name));
  214. static Window RegFindName _ANSI_ARGS_((NameRegistry *regPtr,
  215.     CONST char *name));
  216. static NameRegistry * RegOpen _ANSI_ARGS_((Tcl_Interp *interp,
  217.     TkDisplay *dispPtr, int lock));
  218. static void SendEventProc _ANSI_ARGS_((ClientData clientData,
  219.     XEvent *eventPtr));
  220. static int SendInit _ANSI_ARGS_((Tcl_Interp *interp,
  221.     TkDisplay *dispPtr));
  222. static Tk_RestrictAction SendRestrictProc _ANSI_ARGS_((ClientData clientData,
  223.     XEvent *eventPtr));
  224. static int ServerSecure _ANSI_ARGS_((TkDisplay *dispPtr));
  225. static void UpdateCommWindow _ANSI_ARGS_((TkDisplay *dispPtr));
  226. static int ValidateName _ANSI_ARGS_((TkDisplay *dispPtr,
  227.     CONST char *name, Window commWindow, int oldOK));
  228. /*
  229.  *----------------------------------------------------------------------
  230.  *
  231.  * RegOpen --
  232.  *
  233.  * This procedure loads the name registry for a display into
  234.  * memory so that it can be manipulated.
  235.  *
  236.  * Results:
  237.  * The return value is a pointer to the loaded registry.
  238.  *
  239.  * Side effects:
  240.  * If "lock" is set then the server will be locked.  It is the
  241.  * caller's responsibility to call RegClose when finished with
  242.  * the registry, so that we can write back the registry if
  243.  * needed, unlock the server if needed, and free memory.
  244.  *
  245.  *----------------------------------------------------------------------
  246.  */
  247. static NameRegistry *
  248. RegOpen(interp, dispPtr, lock)
  249.     Tcl_Interp *interp; /* Interpreter to use for error reporting
  250.  * (errors cause a panic so in fact no
  251.  * error is ever returned, but the interpreter
  252.  * is needed anyway). */
  253.     TkDisplay *dispPtr; /* Display whose name registry is to be
  254.  * opened. */
  255.     int lock; /* Non-zero means lock the window server
  256.  * when opening the registry, so no-one
  257.  * else can use the registry until we
  258.  * close it. */
  259. {
  260.     NameRegistry *regPtr;
  261.     int result, actualFormat;
  262.     unsigned long bytesAfter;
  263.     Atom actualType;
  264.     if (dispPtr->commTkwin == NULL) {
  265. SendInit(interp, dispPtr);
  266.     }
  267.     regPtr = (NameRegistry *) ckalloc(sizeof(NameRegistry));
  268.     regPtr->dispPtr = dispPtr;
  269.     regPtr->locked = 0;
  270.     regPtr->modified = 0;
  271.     regPtr->allocedByX = 1;
  272.     if (lock && !sendDebug) {
  273. XGrabServer(dispPtr->display);
  274. regPtr->locked = 1;
  275.     }
  276.     /*
  277.      * Read the registry property.
  278.      */
  279.     result = XGetWindowProperty(dispPtr->display,
  280.     RootWindow(dispPtr->display, 0),
  281.     dispPtr->registryProperty, 0, MAX_PROP_WORDS,
  282.     False, XA_STRING, &actualType, &actualFormat,
  283.     &regPtr->propLength, &bytesAfter,
  284.     (unsigned char **) &regPtr->property);
  285.     if (actualType == None) {
  286. regPtr->propLength = 0;
  287. regPtr->property = NULL;
  288.     } else if ((result != Success) || (actualFormat != 8)
  289.     || (actualType != XA_STRING)) {
  290. /*
  291.  * The property is improperly formed;  delete it.
  292.  */
  293. if (regPtr->property != NULL) {
  294.     XFree(regPtr->property);
  295.     regPtr->propLength = 0;
  296.     regPtr->property = NULL;
  297. }
  298. XDeleteProperty(dispPtr->display,
  299. RootWindow(dispPtr->display, 0),
  300. dispPtr->registryProperty);
  301.     }
  302.     /*
  303.      * Xlib placed an extra null byte after the end of the property, just
  304.      * to make sure that it is always NULL-terminated.  Be sure to include
  305.      * this byte in our count if it's needed to ensure null termination
  306.      * (note: as of 8/95 I'm no longer sure why this code is needed;  seems
  307.      * like it shouldn't be).
  308.      */
  309.     if ((regPtr->propLength > 0)
  310.     && (regPtr->property[regPtr->propLength-1] != 0)) {
  311. regPtr->propLength++;
  312.     }
  313.     return regPtr;
  314. }
  315. /*
  316.  *----------------------------------------------------------------------
  317.  *
  318.  * RegFindName --
  319.  *
  320.  * Given an open name registry, this procedure finds an entry
  321.  * with a given name, if there is one, and returns information
  322.  * about that entry.
  323.  *
  324.  * Results:
  325.  * The return value is the X identifier for the comm window for
  326.  * the application named "name", or None if there is no such
  327.  * entry in the registry.
  328.  *
  329.  * Side effects:
  330.  * None.
  331.  *
  332.  *----------------------------------------------------------------------
  333.  */
  334. static Window
  335. RegFindName(regPtr, name)
  336.     NameRegistry *regPtr; /* Pointer to a registry opened with a
  337.  * previous call to RegOpen. */
  338.     CONST char *name; /* Name of an application. */
  339. {
  340.     char *p, *entry;
  341.     unsigned int id;
  342.     for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) {
  343. entry = p;
  344. while ((*p != 0) && (!isspace(UCHAR(*p)))) {
  345.     p++;
  346. }
  347. if ((*p != 0) && (strcmp(name, p+1) == 0)) {
  348.     if (sscanf(entry, "%x", &id) == 1) {
  349. /*
  350.  * Must cast from an unsigned int to a Window in case we
  351.  * are on a 64-bit architecture.
  352.  */
  353. return (Window) id;
  354.     }
  355. }
  356. while (*p != 0) {
  357.     p++;
  358. }
  359. p++;
  360.     }
  361.     return None;
  362. }
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  * RegDeleteName --
  367.  *
  368.  * This procedure deletes the entry for a given name from
  369.  * an open registry.
  370.  *
  371.  * Results:
  372.  * None.
  373.  *
  374.  * Side effects:
  375.  * If there used to be an entry named "name" in the registry,
  376.  * then it is deleted and the registry is marked as modified
  377.  * so it will be written back when closed.
  378.  *
  379.  *----------------------------------------------------------------------
  380.  */
  381. static void
  382. RegDeleteName(regPtr, name)
  383.     NameRegistry *regPtr; /* Pointer to a registry opened with a
  384.  * previous call to RegOpen. */
  385.     CONST char *name; /* Name of an application. */
  386. {
  387.     char *p, *entry, *entryName;
  388.     int count;
  389.     for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) {
  390. entry = p;
  391. while ((*p != 0) && (!isspace(UCHAR(*p)))) {
  392.     p++;
  393. }
  394. if (*p != 0) {
  395.     p++;
  396. }
  397. entryName = p;
  398. while (*p != 0) {
  399.     p++;
  400. }
  401. p++;
  402. if ((strcmp(name, entryName) == 0)) {
  403.     /*
  404.      * Found the matching entry.  Copy everything after it
  405.      * down on top of it.
  406.      */
  407.     count = regPtr->propLength - (p - regPtr->property);
  408.     if (count > 0)  {
  409. char *src, *dst;
  410. for (src = p, dst = entry; count > 0; src++, dst++, count--) {
  411.     *dst = *src;
  412. }
  413.     }
  414.     regPtr->propLength -=  p - entry;
  415.     regPtr->modified = 1;
  416.     return;
  417. }
  418.     }
  419. }
  420. /*
  421.  *----------------------------------------------------------------------
  422.  *
  423.  * RegAddName --
  424.  *
  425.  * Add a new entry to an open registry.
  426.  *
  427.  * Results:
  428.  * None.
  429.  *
  430.  * Side effects:
  431.  * The open registry is expanded;  it is marked as modified so that
  432.  * it will be written back when closed.
  433.  *
  434.  *----------------------------------------------------------------------
  435.  */
  436. static void
  437. RegAddName(regPtr, name, commWindow)
  438.     NameRegistry *regPtr; /* Pointer to a registry opened with a
  439.  * previous call to RegOpen. */
  440.     CONST char *name; /* Name of an application.  The caller
  441.  * must ensure that this name isn't
  442.  * already registered. */
  443.     Window commWindow; /* X identifier for comm. window of
  444.  * application.  */
  445. {
  446.     char id[30];
  447.     char *newProp;
  448.     int idLength, newBytes;
  449.     sprintf(id, "%x ", (unsigned int) commWindow);
  450.     idLength = strlen(id);
  451.     newBytes = idLength + strlen(name) + 1;
  452.     newProp = (char *) ckalloc((unsigned) (regPtr->propLength + newBytes));
  453.     strcpy(newProp, id);
  454.     strcpy(newProp+idLength, name);
  455.     if (regPtr->property != NULL) {
  456. memcpy((VOID *) (newProp + newBytes), (VOID *) regPtr->property,
  457. regPtr->propLength);
  458. if (regPtr->allocedByX) {
  459.     XFree(regPtr->property);
  460. } else {
  461.     ckfree(regPtr->property);
  462. }
  463.     }
  464.     regPtr->modified = 1;
  465.     regPtr->propLength += newBytes;
  466.     regPtr->property = newProp;
  467.     regPtr->allocedByX = 0;
  468. }
  469. /*
  470.  *----------------------------------------------------------------------
  471.  *
  472.  * RegClose --
  473.  *
  474.  * This procedure is called to end a series of operations on
  475.  * a name registry.
  476.  *
  477.  * Results:
  478.  * None.
  479.  *
  480.  * Side effects:
  481.  * The registry is written back if it has been modified, and the
  482.  * X server is unlocked if it was locked.  Memory for the
  483.  * registry is freed, so the caller should never use regPtr
  484.  * again.
  485.  *
  486.  *----------------------------------------------------------------------
  487.  */
  488. static void
  489. RegClose(regPtr)
  490.     NameRegistry *regPtr; /* Pointer to a registry opened with a
  491.  * previous call to RegOpen. */
  492. {
  493.     if (regPtr->modified) {
  494. if (!regPtr->locked && !sendDebug) {
  495.     panic("The name registry was modified without being locked!");
  496. }
  497. XChangeProperty(regPtr->dispPtr->display,
  498. RootWindow(regPtr->dispPtr->display, 0),
  499. regPtr->dispPtr->registryProperty, XA_STRING, 8,
  500. PropModeReplace, (unsigned char *) regPtr->property,
  501. (int) regPtr->propLength);
  502.     }
  503.     if (regPtr->locked) {
  504. XUngrabServer(regPtr->dispPtr->display);
  505.     }
  506.     /*
  507.      * After ungrabbing the server, it's important to flush the output
  508.      * immediately so that the server sees the ungrab command.  Otherwise
  509.      * we might do something else that needs to communicate with the
  510.      * server (such as invoking a subprocess that needs to do I/O to
  511.      * the screen); if the ungrab command is still sitting in our
  512.      * output buffer, we could deadlock.
  513.      */
  514.     XFlush(regPtr->dispPtr->display);
  515.     if (regPtr->property != NULL) {
  516. if (regPtr->allocedByX) {
  517.     XFree(regPtr->property);
  518. } else {
  519.     ckfree(regPtr->property);
  520. }
  521.     }
  522.     ckfree((char *) regPtr);
  523. }
  524. /*
  525.  *----------------------------------------------------------------------
  526.  *
  527.  * ValidateName --
  528.  *
  529.  * This procedure checks to see if an entry in the registry
  530.  * is still valid.
  531.  *
  532.  * Results:
  533.  * The return value is 1 if the given commWindow exists and its
  534.  * name is "name".  Otherwise 0 is returned.
  535.  *
  536.  * Side effects:
  537.  * None.
  538.  *
  539.  *----------------------------------------------------------------------
  540.  */
  541. static int
  542. ValidateName(dispPtr, name, commWindow, oldOK)
  543.     TkDisplay *dispPtr; /* Display for which to perform the
  544.  * validation. */
  545.     CONST char *name; /* The name of an application. */
  546.     Window commWindow; /* X identifier for the application's
  547.  * comm. window. */
  548.     int oldOK; /* Non-zero means that we should consider
  549.  * an application to be valid even if it
  550.  * looks like an old-style (pre-4.0) one;
  551.  * 0 means consider these invalid. */
  552. {
  553.     int result, actualFormat, argc, i;
  554.     unsigned long length, bytesAfter;
  555.     Atom actualType;
  556.     char *property;
  557.     Tk_ErrorHandler handler;
  558.     CONST char **argv;
  559.     property = NULL;
  560.     /*
  561.      * Ignore X errors when reading the property (e.g., the window
  562.      * might not exist).  If an error occurs, result will be some
  563.      * value other than Success.
  564.      */
  565.     handler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
  566.     (Tk_ErrorProc *) NULL, (ClientData) NULL);
  567.     result = XGetWindowProperty(dispPtr->display, commWindow,
  568.     dispPtr->appNameProperty, 0, MAX_PROP_WORDS,
  569.     False, XA_STRING, &actualType, &actualFormat,
  570.     &length, &bytesAfter, (unsigned char **) &property);
  571.     if ((result == Success) && (actualType == None)) {
  572. XWindowAttributes atts;
  573. /*
  574.  * The comm. window exists but the property we're looking for
  575.  * doesn't exist.  This probably means that the application
  576.  * comes from an older version of Tk (< 4.0) that didn't set the
  577.  * property;  if this is the case, then assume for compatibility's
  578.  * sake that everything's OK.  However, it's also possible that
  579.  * some random application has re-used the window id for something
  580.  * totally unrelated.  Check a few characteristics of the window,
  581.  * such as its dimensions and mapped state, to be sure that it
  582.  * still "smells" like a commWindow.
  583.  */
  584. if (!oldOK
  585. || !XGetWindowAttributes(dispPtr->display, commWindow, &atts)
  586. || (atts.width != 1) || (atts.height != 1)
  587. || (atts.map_state != IsUnmapped)) {
  588.     result = 0;
  589. } else {
  590.     result = 1;
  591. }
  592.     } else if ((result == Success) && (actualFormat == 8)
  593.    && (actualType == XA_STRING)) {
  594. result = 0;
  595. if (Tcl_SplitList((Tcl_Interp *) NULL, property, &argc, &argv)
  596. == TCL_OK) {
  597.     for (i = 0; i < argc; i++) {
  598. if (strcmp(argv[i], name) == 0) {
  599.     result = 1;
  600.     break;
  601. }
  602.     }
  603.     ckfree((char *) argv);
  604. }
  605.     } else {
  606.        result = 0;
  607.     }
  608.     Tk_DeleteErrorHandler(handler);
  609.     if (property != NULL) {
  610. XFree(property);
  611.     }
  612.     return result;
  613. }
  614. /*
  615.  *----------------------------------------------------------------------
  616.  *
  617.  * ServerSecure --
  618.  *
  619.  * Check whether a server is secure enough for us to trust
  620.  * Tcl scripts arriving via that server.
  621.  *
  622.  * Results:
  623.  * The return value is 1 if the server is secure, which means
  624.  * that host-style authentication is turned on but there are
  625.  * no hosts in the enabled list.  This means that some other
  626.  * form of authorization (presumably more secure, such as xauth)
  627.  * is in use.
  628.  *
  629.  * Side effects:
  630.  * None.
  631.  *
  632.  *----------------------------------------------------------------------
  633.  */
  634. static int
  635. ServerSecure(dispPtr)
  636.     TkDisplay *dispPtr; /* Display to check. */
  637. {
  638. #ifdef TK_NO_SECURITY
  639.     return 1;
  640. #else
  641.     XHostAddress *addrPtr;
  642.     int numHosts, secure;
  643.     Bool enabled;
  644.     secure = 0;
  645.     addrPtr = XListHosts(dispPtr->display, &numHosts, &enabled);
  646.     if (enabled && (numHosts == 0)) {
  647. secure = 1;
  648.     }
  649.     if (addrPtr != NULL) {
  650. XFree((char *) addrPtr);
  651.     }
  652.     return secure;
  653. #endif /* TK_NO_SECURITY */
  654. }
  655. /*
  656.  *--------------------------------------------------------------
  657.  *
  658.  * Tk_SetAppName --
  659.  *
  660.  * This procedure is called to associate an ASCII name with a Tk
  661.  * application.  If the application has already been named, the
  662.  * name replaces the old one.
  663.  *
  664.  * Results:
  665.  * The return value is the name actually given to the application.
  666.  * This will normally be the same as name, but if name was already
  667.  * in use for an application then a name of the form "name #2" will
  668.  * be chosen,  with a high enough number to make the name unique.
  669.  *
  670.  * Side effects:
  671.  * Registration info is saved, thereby allowing the "send" command
  672.  * to be used later to invoke commands in the application.  In
  673.  * addition, the "send" command is created in the application's
  674.  * interpreter.  The registration will be removed automatically
  675.  * if the interpreter is deleted or the "send" command is removed.
  676.  *
  677.  *--------------------------------------------------------------
  678.  */
  679. CONST char *
  680. Tk_SetAppName(tkwin, name)
  681.     Tk_Window tkwin; /* Token for any window in the application
  682.  * to be named:  it is just used to identify
  683.  * the application and the display.  */
  684.     CONST char *name; /* The name that will be used to
  685.  * refer to the interpreter in later
  686.  * "send" commands.  Must be globally
  687.  * unique. */
  688. {
  689.     RegisteredInterp *riPtr, *riPtr2;
  690.     Window w;
  691.     TkWindow *winPtr = (TkWindow *) tkwin;
  692.     TkDisplay *dispPtr = winPtr->dispPtr;
  693.     NameRegistry *regPtr;
  694.     Tcl_Interp *interp;
  695.     CONST char *actualName;
  696.     Tcl_DString dString;
  697.     int offset, i;
  698.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  699.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  700.     interp = winPtr->mainPtr->interp;
  701.     if (dispPtr->commTkwin == NULL) {
  702. SendInit(interp, winPtr->dispPtr);
  703.     }
  704.     /*
  705.      * See if the application is already registered;  if so, remove its
  706.      * current name from the registry.
  707.      */
  708.     regPtr = RegOpen(interp, winPtr->dispPtr, 1);
  709.     for (riPtr = tsdPtr->interpListPtr; ; riPtr = riPtr->nextPtr) {
  710. if (riPtr == NULL) {
  711.     /*
  712.      * This interpreter isn't currently registered;  create
  713.      * the data structure that will be used to register it locally,
  714.      * plus add the "send" command to the interpreter.
  715.      */
  716.     riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
  717.     riPtr->interp = interp;
  718.     riPtr->dispPtr = winPtr->dispPtr;
  719.     riPtr->nextPtr = tsdPtr->interpListPtr;
  720.     tsdPtr->interpListPtr = riPtr;
  721.     riPtr->name = NULL;
  722.     Tcl_CreateCommand(interp, "send", Tk_SendCmd, (ClientData) riPtr,
  723.     DeleteProc);
  724.             if (Tcl_IsSafe(interp)) {
  725.                 Tcl_HideCommand(interp, "send", "send");
  726.             }
  727.     break;
  728. }
  729. if (riPtr->interp == interp) {
  730.     /*
  731.      * The interpreter is currently registered;  remove it from
  732.      * the name registry.
  733.      */
  734.     if (riPtr->name) {
  735. RegDeleteName(regPtr, riPtr->name);
  736. ckfree(riPtr->name);
  737.     }
  738.     break;
  739. }
  740.     }
  741.     /*
  742.      * Pick a name to use for the application.  Use "name" if it's not
  743.      * already in use.  Otherwise add a suffix such as " #2", trying
  744.      * larger and larger numbers until we eventually find one that is
  745.      * unique.
  746.      */
  747.     actualName = name;
  748.     offset = 0; /* Needed only to avoid "used before
  749.  * set" compiler warnings. */
  750.     for (i = 1; ; i++) {
  751. if (i > 1) {
  752.     if (i == 2) {
  753. Tcl_DStringInit(&dString);
  754. Tcl_DStringAppend(&dString, name, -1);
  755. Tcl_DStringAppend(&dString, " #", 2);
  756. offset = Tcl_DStringLength(&dString);
  757. Tcl_DStringSetLength(&dString, offset+TCL_INTEGER_SPACE);
  758. actualName = Tcl_DStringValue(&dString);
  759.     }
  760.     sprintf(Tcl_DStringValue(&dString) + offset, "%d", i);
  761. }
  762. w = RegFindName(regPtr, actualName);
  763. if (w == None) {
  764.     break;
  765. }
  766.     
  767. /*
  768.  * The name appears to be in use already, but double-check to
  769.  * be sure (perhaps the application died without removing its
  770.  * name from the registry?).
  771.  */
  772. if (w == Tk_WindowId(dispPtr->commTkwin)) {
  773.     for (riPtr2 = tsdPtr->interpListPtr; riPtr2 != NULL; 
  774.                     riPtr2 = riPtr2->nextPtr) {
  775. if ((riPtr2->interp != interp) &&
  776. (strcmp(riPtr2->name, actualName) == 0)) {
  777.     goto nextSuffix;
  778. }
  779.     }
  780.     RegDeleteName(regPtr, actualName);
  781.     break;
  782. } else if (!ValidateName(winPtr->dispPtr, actualName, w, 1)) {
  783.     RegDeleteName(regPtr, actualName);
  784.     break;
  785. }
  786. nextSuffix:
  787. continue;
  788.     }
  789.     /*
  790.      * We've now got a name to use.  Store it in the name registry and
  791.      * in the local entry for this application, plus put it in a property
  792.      * on the commWindow.
  793.      */
  794.     RegAddName(regPtr, actualName, Tk_WindowId(dispPtr->commTkwin));
  795.     RegClose(regPtr);
  796.     riPtr->name = (char *) ckalloc((unsigned) (strlen(actualName) + 1));
  797.     strcpy(riPtr->name, actualName);
  798.     if (actualName != name) {
  799. Tcl_DStringFree(&dString);
  800.     }
  801.     UpdateCommWindow(dispPtr);
  802.     return riPtr->name;
  803. }
  804. /*
  805.  *--------------------------------------------------------------
  806.  *
  807.  * Tk_SendCmd --
  808.  *
  809.  * This procedure is invoked to process the "send" Tcl command.
  810.  * See the user documentation for details on what it does.
  811.  *
  812.  * Results:
  813.  * A standard Tcl result.
  814.  *
  815.  * Side effects:
  816.  * See the user documentation.
  817.  *
  818.  *--------------------------------------------------------------
  819.  */
  820. int
  821. Tk_SendCmd(clientData, interp, argc, argv)
  822.     ClientData clientData; /* Information about sender (only
  823.  * dispPtr field is used). */
  824.     Tcl_Interp *interp; /* Current interpreter. */
  825.     int argc; /* Number of arguments. */
  826.     CONST char **argv; /* Argument strings. */
  827. {
  828.     TkWindow *winPtr;
  829.     Window commWindow;
  830.     PendingCommand pending;
  831.     register RegisteredInterp *riPtr;
  832.     CONST char *destName;
  833.     int result, c, async, i, firstArg;
  834.     size_t length;
  835.     Tk_RestrictProc *prevRestrictProc;
  836.     ClientData prevArg;
  837.     TkDisplay *dispPtr;
  838.     Tcl_Time timeout;
  839.     NameRegistry *regPtr;
  840.     Tcl_DString request;
  841.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  842.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  843.     Tcl_Interp *localInterp; /* Used when the interpreter to
  844.                                          * send the command to is within
  845.                                          * the same process. */
  846.     /*
  847.      * Process options, if any.
  848.      */
  849.     async = 0;
  850.     winPtr = (TkWindow *) Tk_MainWindow(interp);
  851.     if (winPtr == NULL) {
  852. return TCL_ERROR;
  853.     }
  854.     for (i = 1; i < (argc-1); ) {
  855. if (argv[i][0] != '-') {
  856.     break;
  857. }
  858. c = argv[i][1];
  859. length = strlen(argv[i]);
  860. if ((c == 'a') && (strncmp(argv[i], "-async", length) == 0)) {
  861.     async = 1;
  862.     i++;
  863. } else if ((c == 'd') && (strncmp(argv[i], "-displayof",
  864. length) == 0)) {
  865.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[i+1],
  866.     (Tk_Window) winPtr);
  867.     if (winPtr == NULL) {
  868. return TCL_ERROR;
  869.     }
  870.     i += 2;
  871. } else if (strcmp(argv[i], "--") == 0) {
  872.     i++;
  873.     break;
  874. } else {
  875.     Tcl_AppendResult(interp, "bad option "", argv[i],
  876.     "": must be -async, -displayof, or --", (char *) NULL);
  877.     return TCL_ERROR;
  878. }
  879.     }
  880.     if (argc < (i+2)) {
  881. Tcl_AppendResult(interp, "wrong # args: should be "", argv[0],
  882. " ?options? interpName arg ?arg ...?"", (char *) NULL);
  883. return TCL_ERROR;
  884.     }
  885.     destName = argv[i];
  886.     firstArg = i+1;
  887.     dispPtr = winPtr->dispPtr;
  888.     if (dispPtr->commTkwin == NULL) {
  889. SendInit(interp, winPtr->dispPtr);
  890.     }
  891.     /*
  892.      * See if the target interpreter is local.  If so, execute
  893.      * the command directly without going through the X server.
  894.      * The only tricky thing is passing the result from the target
  895.      * interpreter to the invoking interpreter.  Watch out:  they
  896.      * could be the same!
  897.      */
  898.     for (riPtr = tsdPtr->interpListPtr; riPtr != NULL; 
  899.             riPtr = riPtr->nextPtr) {
  900. if ((riPtr->dispPtr != dispPtr)
  901. || (strcmp(riPtr->name, destName) != 0)) {
  902.     continue;
  903. }
  904. Tcl_Preserve((ClientData) riPtr);
  905.         localInterp = riPtr->interp;
  906.         Tcl_Preserve((ClientData) localInterp);
  907. if (firstArg == (argc-1)) {
  908.     result = Tcl_GlobalEval(localInterp, argv[firstArg]);
  909. } else {
  910.     Tcl_DStringInit(&request);
  911.     Tcl_DStringAppend(&request, argv[firstArg], -1);
  912.     for (i = firstArg+1; i < argc; i++) {
  913. Tcl_DStringAppend(&request, " ", 1);
  914. Tcl_DStringAppend(&request, argv[i], -1);
  915.     }
  916.     result = Tcl_GlobalEval(localInterp, Tcl_DStringValue(&request));
  917.     Tcl_DStringFree(&request);
  918. }
  919. if (interp != localInterp) {
  920.     if (result == TCL_ERROR) {
  921. Tcl_Obj *errorObjPtr;
  922. /*
  923.  * An error occurred, so transfer error information from the
  924.  * destination interpreter back to our interpreter.  Must clear
  925.  * interp's result before calling Tcl_AddErrorInfo, since
  926.  * Tcl_AddErrorInfo will store the interp's result in errorInfo
  927.  * before appending riPtr's $errorInfo;  we've already got
  928.  * everything we need in riPtr's $errorInfo.
  929.  */
  930. Tcl_ResetResult(interp);
  931. Tcl_AddErrorInfo(interp, Tcl_GetVar2(localInterp,
  932. "errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
  933. errorObjPtr = Tcl_GetVar2Ex(localInterp, "errorCode", NULL,
  934. TCL_GLOBAL_ONLY);
  935. Tcl_SetObjErrorCode(interp, errorObjPtr);
  936.     }
  937.     Tcl_SetObjResult(interp, Tcl_GetObjResult(localInterp));
  938.             Tcl_ResetResult(localInterp);
  939. }
  940. Tcl_Release((ClientData) riPtr);
  941.         Tcl_Release((ClientData) localInterp);
  942. return result;
  943.     }
  944.     /*
  945.      * Bind the interpreter name to a communication window.
  946.      */
  947.     regPtr = RegOpen(interp, winPtr->dispPtr, 0);
  948.     commWindow = RegFindName(regPtr, destName);
  949.     RegClose(regPtr);
  950.     if (commWindow == None) {
  951. Tcl_AppendResult(interp, "no application named "",
  952. destName, """, (char *) NULL);
  953. return TCL_ERROR;
  954.     }
  955.     /*
  956.      * Send the command to the target interpreter by appending it to the
  957.      * comm window in the communication window.
  958.      */
  959.     tkSendSerial++;
  960.     Tcl_DStringInit(&request);
  961.     Tcl_DStringAppend(&request, "c-n ", 6);
  962.     Tcl_DStringAppend(&request, destName, -1);
  963.     if (!async) {
  964. char buffer[TCL_INTEGER_SPACE * 2];
  965. sprintf(buffer, "%x %d",
  966. (unsigned int) Tk_WindowId(dispPtr->commTkwin),
  967. tkSendSerial);
  968. Tcl_DStringAppend(&request, "-r ", 4);
  969. Tcl_DStringAppend(&request, buffer, -1);
  970.     }
  971.     Tcl_DStringAppend(&request, "-s ", 4);
  972.     Tcl_DStringAppend(&request, argv[firstArg], -1);
  973.     for (i = firstArg+1; i < argc; i++) {
  974. Tcl_DStringAppend(&request, " ", 1);
  975. Tcl_DStringAppend(&request, argv[i], -1);
  976.     }
  977.     (void) AppendPropCarefully(dispPtr->display, commWindow,
  978.     dispPtr->commProperty, Tcl_DStringValue(&request),
  979.     Tcl_DStringLength(&request) + 1,
  980.     (async) ? (PendingCommand *) NULL : &pending);
  981.     Tcl_DStringFree(&request);
  982.     if (async) {
  983. /*
  984.  * This is an asynchronous send:  return immediately without
  985.  * waiting for a response.
  986.  */
  987. return TCL_OK;
  988.     }
  989.     /*
  990.      * Register the fact that we're waiting for a command to complete
  991.      * (this is needed by SendEventProc and by AppendErrorProc to pass
  992.      * back the command's results).  Set up a timeout handler so that
  993.      * we can check during long sends to make sure that the destination
  994.      * application is still alive.
  995.      */
  996.     pending.serial = tkSendSerial;
  997.     pending.dispPtr = dispPtr;
  998.     pending.target = destName;
  999.     pending.commWindow = commWindow;
  1000.     pending.interp = interp;
  1001.     pending.result = NULL;
  1002.     pending.errorInfo = NULL;
  1003.     pending.errorCode = NULL;
  1004.     pending.gotResponse = 0;
  1005.     pending.nextPtr = tsdPtr->pendingCommands;
  1006.     tsdPtr->pendingCommands = &pending;
  1007.     /*
  1008.      * Enter a loop processing X events until the result comes
  1009.      * in or the target is declared to be dead.  While waiting
  1010.      * for a result, look only at send-related events so that
  1011.      * the send is synchronous with respect to other events in
  1012.      * the application.
  1013.      */
  1014.     prevRestrictProc = Tk_RestrictEvents(SendRestrictProc,
  1015.     (ClientData) NULL, &prevArg);
  1016.     Tcl_GetTime(&timeout);
  1017.     timeout.sec += 2;
  1018.     while (!pending.gotResponse) {
  1019. if (!TkUnixDoOneXEvent(&timeout)) {
  1020.     /*
  1021.      * An unusually long amount of time has elapsed during the
  1022.      * processing of a sent command.  Check to make sure that the
  1023.      * target application still exists.  If it does, reset the timeout.
  1024.      */
  1025.     if (!ValidateName(pending.dispPtr, pending.target,
  1026.     pending.commWindow, 0)) {
  1027. char *msg;
  1028. if (ValidateName(pending.dispPtr, pending.target,
  1029. pending.commWindow, 1)) {
  1030.     msg = "target application died or uses a Tk version before 4.0";
  1031. } else {
  1032.     msg = "target application died";
  1033. }
  1034. pending.code = TCL_ERROR;
  1035. pending.result = (char *) ckalloc((unsigned) (strlen(msg) + 1));
  1036. strcpy(pending.result, msg);
  1037. pending.gotResponse = 1;
  1038.     } else {
  1039. Tcl_GetTime(&timeout);
  1040. timeout.sec += 2;
  1041.     }
  1042. }
  1043.     }
  1044.     (void) Tk_RestrictEvents(prevRestrictProc, prevArg, &prevArg);
  1045.     /*
  1046.      * Unregister the information about the pending command
  1047.      * and return the result.
  1048.      */
  1049.     if (tsdPtr->pendingCommands != &pending) {
  1050. panic("Tk_SendCmd: corrupted send stack");
  1051.     }
  1052.     tsdPtr->pendingCommands = pending.nextPtr;
  1053.     if (pending.errorInfo != NULL) {
  1054. /*
  1055.  * Special trick: must clear the interp's result before calling
  1056.  * Tcl_AddErrorInfo, since Tcl_AddErrorInfo will store the interp's
  1057.  * result in errorInfo before appending pending.errorInfo;  we've
  1058.  * already got everything we need in pending.errorInfo.
  1059.  */
  1060. Tcl_ResetResult(interp);
  1061. Tcl_AddErrorInfo(interp, pending.errorInfo);
  1062. ckfree(pending.errorInfo);
  1063.     }
  1064.     if (pending.errorCode != NULL) {
  1065. Tcl_Obj *errorObjPtr;
  1066. errorObjPtr = Tcl_NewStringObj(pending.errorCode, -1);
  1067. Tcl_SetObjErrorCode(interp, errorObjPtr);
  1068. ckfree(pending.errorCode);
  1069.     }
  1070.     Tcl_SetResult(interp, pending.result, TCL_DYNAMIC);
  1071.     return pending.code;
  1072. }
  1073. /*
  1074.  *----------------------------------------------------------------------
  1075.  *
  1076.  * TkGetInterpNames --
  1077.  *
  1078.  * This procedure is invoked to fetch a list of all the
  1079.  * interpreter names currently registered for the display
  1080.  * of a particular window.
  1081.  *
  1082.  * Results:
  1083.  * A standard Tcl return value.  The interp's result will be set
  1084.  * to hold a list of all the interpreter names defined for
  1085.  * tkwin's display.  If an error occurs, then TCL_ERROR
  1086.  * is returned and the interp's result will hold an error message.
  1087.  *
  1088.  * Side effects:
  1089.  * None.
  1090.  *
  1091.  *----------------------------------------------------------------------
  1092.  */
  1093. int
  1094. TkGetInterpNames(interp, tkwin)
  1095.     Tcl_Interp *interp; /* Interpreter for returning a result. */
  1096.     Tk_Window tkwin; /* Window whose display is to be used
  1097.  * for the lookup. */
  1098. {
  1099.     TkWindow *winPtr = (TkWindow *) tkwin;
  1100.     char *p, *entry, *entryName;
  1101.     NameRegistry *regPtr;
  1102.     Window commWindow;
  1103.     int count;
  1104.     unsigned int id;
  1105.     /*
  1106.      * Read the registry property, then scan through all of its entries.
  1107.      * Validate each entry to be sure that its application still exists.
  1108.      */
  1109.     regPtr = RegOpen(interp, winPtr->dispPtr, 1);
  1110.     for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) {
  1111. entry = p;
  1112. if (sscanf(p,  "%x",(unsigned int *) &id) != 1) {
  1113.     commWindow =  None;
  1114. } else {
  1115.     commWindow = id;
  1116. }
  1117. while ((*p != 0) && (!isspace(UCHAR(*p)))) {
  1118.     p++;
  1119. }
  1120. if (*p != 0) {
  1121.     p++;
  1122. }
  1123. entryName = p;
  1124. while (*p != 0) {
  1125.     p++;
  1126. }
  1127. p++;
  1128. if (ValidateName(winPtr->dispPtr, entryName, commWindow, 1)) {
  1129.     /*
  1130.      * The application still exists; add its name to the result.
  1131.      */
  1132.     Tcl_AppendElement(interp, entryName);
  1133. } else {
  1134.     /*
  1135.      * This name is bogus (perhaps the application died without
  1136.      * cleaning up its entry in the registry?).  Delete the name.
  1137.      */
  1138.     count = regPtr->propLength - (p - regPtr->property);
  1139.     if (count > 0)  {
  1140. char *src, *dst;
  1141. for (src = p, dst = entry; count > 0; src++, dst++, count--) {
  1142.     *dst = *src;
  1143. }
  1144.     }
  1145.     regPtr->propLength -= p - entry;
  1146.     regPtr->modified = 1;
  1147.     p = entry;
  1148. }
  1149.     }
  1150.     RegClose(regPtr);
  1151.     return TCL_OK;
  1152. }
  1153. /*
  1154.  *--------------------------------------------------------------
  1155.  *
  1156.  * TkSendCleanup --
  1157.  *
  1158.  * This procedure is called to free resources used by the
  1159.  * communication channels for sending commands and
  1160.  * receiving results.
  1161.  *
  1162.  * Results:
  1163.  * None.
  1164.  *
  1165.  * Side effects:
  1166.  * Frees various data structures and windows.
  1167.  *
  1168.  *--------------------------------------------------------------
  1169.  */
  1170. void
  1171. TkSendCleanup(dispPtr)
  1172.     TkDisplay *dispPtr;
  1173. {
  1174.     if (dispPtr->commTkwin != NULL) {
  1175. Tk_DeleteEventHandler(dispPtr->commTkwin, PropertyChangeMask,
  1176. SendEventProc, (ClientData) dispPtr);
  1177. Tk_DestroyWindow(dispPtr->commTkwin);
  1178. Tcl_Release((ClientData) dispPtr->commTkwin);
  1179. dispPtr->commTkwin = NULL;
  1180.     }
  1181. }
  1182. /*
  1183.  *--------------------------------------------------------------
  1184.  *
  1185.  * SendInit --
  1186.  *
  1187.  * This procedure is called to initialize the
  1188.  * communication channels for sending commands and
  1189.  * receiving results.
  1190.  *
  1191.  * Results:
  1192.  * None.
  1193.  *
  1194.  * Side effects:
  1195.  * Sets up various data structures and windows.
  1196.  *
  1197.  *--------------------------------------------------------------
  1198.  */
  1199. static int
  1200. SendInit(interp, dispPtr)
  1201.     Tcl_Interp *interp; /* Interpreter to use for error reporting
  1202.  * (no errors are ever returned, but the
  1203.  * interpreter is needed anyway). */
  1204.     TkDisplay *dispPtr; /* Display to initialize. */
  1205. {
  1206.     XSetWindowAttributes atts;
  1207.     /*
  1208.      * Create the window used for communication, and set up an
  1209.      * event handler for it.
  1210.      */
  1211.     dispPtr->commTkwin = Tk_CreateWindow(interp, (Tk_Window) NULL,
  1212.     "_comm", DisplayString(dispPtr->display));
  1213.     if (dispPtr->commTkwin == NULL) {
  1214. panic("Tk_CreateWindow failed in SendInit!");
  1215.     }
  1216.     Tcl_Preserve((ClientData) dispPtr->commTkwin);
  1217.     atts.override_redirect = True;
  1218.     Tk_ChangeWindowAttributes(dispPtr->commTkwin,
  1219.     CWOverrideRedirect, &atts);
  1220.     Tk_CreateEventHandler(dispPtr->commTkwin, PropertyChangeMask,
  1221.     SendEventProc, (ClientData) dispPtr);
  1222.     Tk_MakeWindowExist(dispPtr->commTkwin);
  1223.     /*
  1224.      * Get atoms used as property names.
  1225.      */
  1226.     dispPtr->commProperty = Tk_InternAtom(dispPtr->commTkwin, "Comm");
  1227.     dispPtr->registryProperty = Tk_InternAtom(dispPtr->commTkwin,
  1228.     "InterpRegistry");
  1229.     dispPtr->appNameProperty = Tk_InternAtom(dispPtr->commTkwin,
  1230.     "TK_APPLICATION");
  1231.     return TCL_OK;
  1232. }
  1233. /*
  1234.  *--------------------------------------------------------------
  1235.  *
  1236.  * SendEventProc --
  1237.  *
  1238.  * This procedure is invoked automatically by the toolkit
  1239.  * event manager when a property changes on the communication
  1240.  * window.  This procedure reads the property and handles
  1241.  * command requests and responses.
  1242.  *
  1243.  * Results:
  1244.  * None.
  1245.  *
  1246.  * Side effects:
  1247.  * If there are command requests in the property, they
  1248.  * are executed.  If there are responses in the property,
  1249.  * their information is saved for the (ostensibly waiting)
  1250.  * "send" commands. The property is deleted.
  1251.  *
  1252.  *--------------------------------------------------------------
  1253.  */
  1254. static void
  1255. SendEventProc(clientData, eventPtr)
  1256.     ClientData clientData; /* Display information. */
  1257.     XEvent *eventPtr; /* Information about event. */
  1258. {
  1259.     TkDisplay *dispPtr = (TkDisplay *) clientData;
  1260.     char *propInfo;
  1261.     register char *p;
  1262.     int result, actualFormat;
  1263.     unsigned long numItems, bytesAfter;
  1264.     Atom actualType;
  1265.     Tcl_Interp *remoteInterp; /* Interp in which to execute the command. */
  1266.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  1267.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  1268.     if ((eventPtr->xproperty.atom != dispPtr->commProperty)
  1269.     || (eventPtr->xproperty.state != PropertyNewValue)) {
  1270. return;
  1271.     }
  1272.     /*
  1273.      * Read the comm property and delete it.
  1274.      */
  1275.     propInfo = NULL;
  1276.     result = XGetWindowProperty(dispPtr->display,
  1277.     Tk_WindowId(dispPtr->commTkwin),
  1278.     dispPtr->commProperty, 0, MAX_PROP_WORDS, True,
  1279.     XA_STRING, &actualType, &actualFormat,
  1280.     &numItems, &bytesAfter, (unsigned char **) &propInfo);
  1281.     /*
  1282.      * If the property doesn't exist or is improperly formed
  1283.      * then ignore it.
  1284.      */
  1285.     if ((result != Success) || (actualType != XA_STRING)
  1286.     || (actualFormat != 8)) {
  1287. if (propInfo != NULL) {
  1288.     XFree(propInfo);
  1289. }
  1290. return;
  1291.     }
  1292.     /*
  1293.      * Several commands and results could arrive in the property at
  1294.      * one time;  each iteration through the outer loop handles a
  1295.      * single command or result.
  1296.      */
  1297.     for (p = propInfo; (p-propInfo) < (int) numItems; ) {
  1298. /*
  1299.  * Ignore leading NULLs; each command or result starts with a
  1300.  * NULL so that no matter how badly formed a preceding command
  1301.  * is, we'll be able to tell that a new command/result is
  1302.  * starting.
  1303.  */
  1304. if (*p == 0) {
  1305.     p++;
  1306.     continue;
  1307. }
  1308. if ((*p == 'c') && (p[1] == 0)) {
  1309.     Window commWindow;
  1310.     char *interpName, *script, *serial, *end;
  1311.     Tcl_DString reply;
  1312.     RegisteredInterp *riPtr;
  1313.     /*
  1314.      *----------------------------------------------------------
  1315.      * This is an incoming command from some other application.
  1316.      * Iterate over all of its options.  Stop when we reach
  1317.      * the end of the property or something that doesn't look
  1318.      * like an option.
  1319.      *----------------------------------------------------------
  1320.      */
  1321.     p += 2;
  1322.     interpName = NULL;
  1323.     commWindow = None;
  1324.     serial = "";
  1325.     script = NULL;
  1326.     while (((p-propInfo) < (int) numItems) && (*p == '-')) {
  1327. switch (p[1]) {
  1328.     case 'r':
  1329. commWindow = (Window) strtoul(p+2, &end, 16);
  1330. if ((end == p+2) || (*end != ' ')) {
  1331.     commWindow = None;
  1332. } else {
  1333.     p = serial = end+1;
  1334. }
  1335. break;
  1336.     case 'n':
  1337. if (p[2] == ' ') {
  1338.     interpName = p+3;
  1339. }
  1340. break;
  1341.     case 's':
  1342. if (p[2] == ' ') {
  1343.     script = p+3;
  1344. }
  1345. break;
  1346. }
  1347. while (*p != 0) {
  1348.     p++;
  1349. }
  1350. p++;
  1351.     }
  1352.     if ((script == NULL) || (interpName == NULL)) {
  1353. continue;
  1354.     }
  1355.     /*
  1356.      * Initialize the result property, so that we're ready at any
  1357.      * time if we need to return an error.
  1358.      */
  1359.     if (commWindow != None) {
  1360. Tcl_DStringInit(&reply);
  1361. Tcl_DStringAppend(&reply, "r-s ", 6);
  1362. Tcl_DStringAppend(&reply, serial, -1);
  1363. Tcl_DStringAppend(&reply, "-r ", 4);
  1364.     }
  1365.     if (!ServerSecure(dispPtr)) {
  1366. if (commWindow != None) {
  1367.     Tcl_DStringAppend(&reply, "X server insecure (must use xauth-style authorization); command ignored", -1);
  1368. }
  1369. result = TCL_ERROR;
  1370. goto returnResult;
  1371.     }
  1372.     /*
  1373.      * Locate the application, then execute the script.
  1374.      */
  1375.     for (riPtr = tsdPtr->interpListPtr; ; riPtr = riPtr->nextPtr) {
  1376. if (riPtr == NULL) {
  1377.     if (commWindow != None) {
  1378. Tcl_DStringAppend(&reply,
  1379. "receiver never heard of interpreter "", -1);
  1380. Tcl_DStringAppend(&reply, interpName, -1);
  1381. Tcl_DStringAppend(&reply, """, 1);
  1382.     }
  1383.     result = TCL_ERROR;
  1384.     goto returnResult;
  1385. }
  1386. if (strcmp(riPtr->name, interpName) == 0) {
  1387.     break;
  1388. }
  1389.     }
  1390.     Tcl_Preserve((ClientData) riPtr);
  1391.             /*
  1392.              * We must protect the interpreter because the script may
  1393.              * enter another event loop, which might call Tcl_DeleteInterp.
  1394.              */
  1395.             remoteInterp = riPtr->interp;
  1396.             Tcl_Preserve((ClientData) remoteInterp);
  1397.             result = Tcl_GlobalEval(remoteInterp, script);
  1398.             /*
  1399.              * The call to Tcl_Release may have released the interpreter
  1400.              * which will cause the "send" command for that interpreter
  1401.              * to be deleted. The command deletion callback will set the
  1402.              * riPtr->interp field to NULL, hence the check below for NULL.
  1403.              */
  1404.     if (commWindow != None) {
  1405. Tcl_DStringAppend(&reply, Tcl_GetStringResult(remoteInterp),
  1406. -1);
  1407. if (result == TCL_ERROR) {
  1408.     CONST char *varValue;
  1409.     
  1410.     varValue = Tcl_GetVar2(remoteInterp, "errorInfo",
  1411.     (char *) NULL, TCL_GLOBAL_ONLY);
  1412.     if (varValue != NULL) {
  1413. Tcl_DStringAppend(&reply, "-i ", 4);
  1414. Tcl_DStringAppend(&reply, varValue, -1);
  1415.     }
  1416.     varValue = Tcl_GetVar2(remoteInterp, "errorCode",
  1417.     (char *) NULL, TCL_GLOBAL_ONLY);
  1418.     if (varValue != NULL) {
  1419. Tcl_DStringAppend(&reply, "-e ", 4);
  1420. Tcl_DStringAppend(&reply, varValue, -1);
  1421.     }
  1422. }
  1423.     }
  1424.             Tcl_Release((ClientData) remoteInterp);
  1425.     Tcl_Release((ClientData) riPtr);
  1426.     /*
  1427.      * Return the result to the sender if a commWindow was
  1428.      * specified (if none was specified then this is an asynchronous
  1429.      * call).  Right now reply has everything but the completion
  1430.      * code, but it needs the NULL to terminate the current option.
  1431.      */
  1432.     returnResult:
  1433.     if (commWindow != None) {
  1434. if (result != TCL_OK) {
  1435.     char buffer[TCL_INTEGER_SPACE];
  1436.     
  1437.     sprintf(buffer, "%d", result);
  1438.     Tcl_DStringAppend(&reply, "-c ", 4);
  1439.     Tcl_DStringAppend(&reply, buffer, -1);
  1440. }
  1441. (void) AppendPropCarefully(dispPtr->display, commWindow,
  1442. dispPtr->commProperty, Tcl_DStringValue(&reply),
  1443. Tcl_DStringLength(&reply) + 1,
  1444. (PendingCommand *) NULL);
  1445. XFlush(dispPtr->display);
  1446. Tcl_DStringFree(&reply);
  1447.     }
  1448. } else if ((*p == 'r') && (p[1] == 0)) {
  1449.     int serial, code, gotSerial;
  1450.     char *errorInfo, *errorCode, *resultString;
  1451.     PendingCommand *pcPtr;
  1452.     /*
  1453.      *----------------------------------------------------------
  1454.      * This is a reply to some command that we sent out.  Iterate
  1455.      * over all of its options.  Stop when we reach the end of the
  1456.      * property or something that doesn't look like an option.
  1457.      *----------------------------------------------------------
  1458.      */
  1459.     p += 2;
  1460.     code = TCL_OK;
  1461.     gotSerial = 0;
  1462.     errorInfo = NULL;
  1463.     errorCode = NULL;
  1464.     resultString = "";
  1465.     while (((p-propInfo) < (int) numItems) && (*p == '-')) {
  1466. switch (p[1]) {
  1467.     case 'c':
  1468. if (sscanf(p+2, " %d", &code) != 1) {
  1469.     code = TCL_OK;
  1470. }
  1471. break;
  1472.     case 'e':
  1473. if (p[2] == ' ') {
  1474.     errorCode = p+3;
  1475. }
  1476. break;
  1477.     case 'i':
  1478. if (p[2] == ' ') {
  1479.     errorInfo = p+3;
  1480. }
  1481. break;
  1482.     case 'r':
  1483. if (p[2] == ' ') {
  1484.     resultString = p+3;
  1485. }
  1486. break;
  1487.     case 's':
  1488. if (sscanf(p+2, " %d", &serial) == 1) {
  1489.     gotSerial = 1;
  1490. }
  1491. break;
  1492. }
  1493. while (*p != 0) {
  1494.     p++;
  1495. }
  1496. p++;
  1497.     }
  1498.     if (!gotSerial) {
  1499. continue;
  1500.     }
  1501.     /*
  1502.      * Give the result information to anyone who's
  1503.      * waiting for it.
  1504.      */
  1505.     for (pcPtr = tsdPtr->pendingCommands; pcPtr != NULL;
  1506.     pcPtr = pcPtr->nextPtr) {
  1507. if ((serial != pcPtr->serial) || (pcPtr->result != NULL)) {
  1508.     continue;
  1509. }
  1510. pcPtr->code = code;
  1511. if (resultString != NULL) {
  1512.     pcPtr->result = (char *) ckalloc((unsigned)
  1513.     (strlen(resultString) + 1));
  1514.     strcpy(pcPtr->result, resultString);
  1515. }
  1516. if (code == TCL_ERROR) {
  1517.     if (errorInfo != NULL) {
  1518. pcPtr->errorInfo = (char *) ckalloc((unsigned)
  1519. (strlen(errorInfo) + 1));
  1520. strcpy(pcPtr->errorInfo, errorInfo);
  1521.     }
  1522.     if (errorCode != NULL) {
  1523. pcPtr->errorCode = (char *) ckalloc((unsigned)
  1524. (strlen(errorCode) + 1));
  1525. strcpy(pcPtr->errorCode, errorCode);
  1526.     }
  1527. }
  1528. pcPtr->gotResponse = 1;
  1529. break;
  1530.     }
  1531. } else {
  1532.     /*
  1533.      * Didn't recognize this thing.  Just skip through the next
  1534.      * null character and try again.
  1535.      */
  1536.     while (*p != 0) {
  1537. p++;
  1538.     }
  1539.     p++;
  1540. }
  1541.     }
  1542.     XFree(propInfo);
  1543. }
  1544. /*
  1545.  *--------------------------------------------------------------
  1546.  *
  1547.  * AppendPropCarefully --
  1548.  *
  1549.  * Append a given property to a given window, but set up
  1550.  * an X error handler so that if the append fails this
  1551.  * procedure can return an error code rather than having
  1552.  * Xlib panic.
  1553.  *
  1554.  * Results:
  1555.  * None.
  1556.  *
  1557.  * Side effects:
  1558.  * The given property on the given window is appended to.
  1559.  * If this operation fails and if pendingPtr is non-NULL,
  1560.  * then the pending operation is marked as complete with
  1561.  * an error.
  1562.  *
  1563.  *--------------------------------------------------------------
  1564.  */
  1565. static void
  1566. AppendPropCarefully(display, window, property, value, length, pendingPtr)
  1567.     Display *display; /* Display on which to operate. */
  1568.     Window window; /* Window whose property is to
  1569.  * be modified. */
  1570.     Atom property; /* Name of property. */
  1571.     char *value; /* Characters to append to property. */
  1572.     int length; /* Number of bytes to append. */
  1573.     PendingCommand *pendingPtr; /* Pending command to mark complete
  1574.  * if an error occurs during the
  1575.  * property op.  NULL means just
  1576.  * ignore the error. */
  1577. {
  1578.     Tk_ErrorHandler handler;
  1579.     handler = Tk_CreateErrorHandler(display, -1, -1, -1, AppendErrorProc,
  1580. (ClientData) pendingPtr);
  1581.     XChangeProperty(display, window, property, XA_STRING, 8,
  1582.     PropModeAppend, (unsigned char *) value, length);
  1583.     Tk_DeleteErrorHandler(handler);
  1584. }
  1585. /*
  1586.  * The procedure below is invoked if an error occurs during
  1587.  * the XChangeProperty operation above.
  1588.  */
  1589. /* ARGSUSED */
  1590. static int
  1591. AppendErrorProc(clientData, errorPtr)
  1592.     ClientData clientData; /* Command to mark complete, or NULL. */
  1593.     XErrorEvent *errorPtr; /* Information about error. */
  1594. {
  1595.     PendingCommand *pendingPtr = (PendingCommand *) clientData;
  1596.     register PendingCommand *pcPtr;
  1597.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  1598.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  1599.     if (pendingPtr == NULL) {
  1600. return 0;
  1601.     }
  1602.     /*
  1603.      * Make sure this command is still pending.
  1604.      */
  1605.     for (pcPtr = tsdPtr->pendingCommands; pcPtr != NULL;
  1606.     pcPtr = pcPtr->nextPtr) {
  1607. if ((pcPtr == pendingPtr) && (pcPtr->result == NULL)) {
  1608.     pcPtr->result = (char *) ckalloc((unsigned)
  1609.     (strlen(pcPtr->target) + 50));
  1610.     sprintf(pcPtr->result, "no application named "%s"",
  1611.     pcPtr->target);
  1612.     pcPtr->code = TCL_ERROR;
  1613.     pcPtr->gotResponse = 1;
  1614.     break;
  1615. }
  1616.     }
  1617.     return 0;
  1618. }
  1619. /*
  1620.  *--------------------------------------------------------------
  1621.  *
  1622.  * DeleteProc --
  1623.  *
  1624.  * This procedure is invoked by Tcl when the "send" command
  1625.  * is deleted in an interpreter.  It unregisters the interpreter.
  1626.  *
  1627.  * Results:
  1628.  * None.
  1629.  *
  1630.  * Side effects:
  1631.  * The interpreter given by riPtr is unregistered.
  1632.  *
  1633.  *--------------------------------------------------------------
  1634.  */
  1635. static void
  1636. DeleteProc(clientData)
  1637.     ClientData clientData; /* Info about registration, passed
  1638.  * as ClientData. */
  1639. {
  1640.     RegisteredInterp *riPtr = (RegisteredInterp *) clientData;
  1641.     register RegisteredInterp *riPtr2;
  1642.     NameRegistry *regPtr;
  1643.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  1644.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  1645.     regPtr = RegOpen(riPtr->interp, riPtr->dispPtr, 1);
  1646.     RegDeleteName(regPtr, riPtr->name);
  1647.     RegClose(regPtr);
  1648.     if (tsdPtr->interpListPtr == riPtr) {
  1649. tsdPtr->interpListPtr = riPtr->nextPtr;
  1650.     } else {
  1651. for (riPtr2 = tsdPtr->interpListPtr; riPtr2 != NULL;
  1652. riPtr2 = riPtr2->nextPtr) {
  1653.     if (riPtr2->nextPtr == riPtr) {
  1654. riPtr2->nextPtr = riPtr->nextPtr;
  1655. break;
  1656.     }
  1657. }
  1658.     }
  1659.     ckfree((char *) riPtr->name);
  1660.     riPtr->interp = NULL;
  1661.     UpdateCommWindow(riPtr->dispPtr);
  1662.     Tcl_EventuallyFree((ClientData) riPtr, TCL_DYNAMIC);
  1663. }
  1664. /*
  1665.  *----------------------------------------------------------------------
  1666.  *
  1667.  * SendRestrictProc --
  1668.  *
  1669.  * This procedure filters incoming events when a "send" command
  1670.  * is outstanding.  It defers all events except those containing
  1671.  * send commands and results.
  1672.  *
  1673.  * Results:
  1674.  * False is returned except for property-change events on a
  1675.  * commWindow.
  1676.  *
  1677.  * Side effects:
  1678.  * None.
  1679.  *
  1680.  *----------------------------------------------------------------------
  1681.  */
  1682.     /* ARGSUSED */
  1683. static Tk_RestrictAction
  1684. SendRestrictProc(clientData, eventPtr)
  1685.     ClientData clientData; /* Not used. */
  1686.     register XEvent *eventPtr; /* Event that just arrived. */
  1687. {
  1688.     TkDisplay *dispPtr;
  1689.     if (eventPtr->type != PropertyNotify) {
  1690. return TK_DEFER_EVENT;
  1691.     }
  1692.     for (dispPtr = TkGetDisplayList(); dispPtr != NULL; 
  1693.             dispPtr = dispPtr->nextPtr) {
  1694. if ((eventPtr->xany.display == dispPtr->display)
  1695. && (eventPtr->xproperty.window
  1696. == Tk_WindowId(dispPtr->commTkwin))) {
  1697.     return TK_PROCESS_EVENT;
  1698. }
  1699.     }
  1700.     return TK_DEFER_EVENT;
  1701. }
  1702. /*
  1703.  *----------------------------------------------------------------------
  1704.  *
  1705.  * UpdateCommWindow --
  1706.  *
  1707.  * This procedure updates the list of application names stored
  1708.  * on our commWindow.  It is typically called when interpreters
  1709.  * are registered and unregistered.
  1710.  *
  1711.  * Results:
  1712.  * None.
  1713.  *
  1714.  * Side effects:
  1715.  * The TK_APPLICATION property on the comm window is updated.
  1716.  *
  1717.  *----------------------------------------------------------------------
  1718.  */
  1719. static void
  1720. UpdateCommWindow(dispPtr)
  1721.     TkDisplay *dispPtr; /* Display whose commWindow is to be
  1722.  * updated. */
  1723. {
  1724.     Tcl_DString names;
  1725.     RegisteredInterp *riPtr;
  1726.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  1727.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  1728.     Tcl_DStringInit(&names);
  1729.     for (riPtr = tsdPtr->interpListPtr; riPtr != NULL; 
  1730.             riPtr = riPtr->nextPtr) {
  1731. Tcl_DStringAppendElement(&names, riPtr->name);
  1732.     }
  1733.     XChangeProperty(dispPtr->display, Tk_WindowId(dispPtr->commTkwin),
  1734.     dispPtr->appNameProperty, XA_STRING, 8, PropModeReplace,
  1735.     (unsigned char *) Tcl_DStringValue(&names),
  1736.     Tcl_DStringLength(&names));
  1737.     Tcl_DStringFree(&names);
  1738. }