summaryrefslogtreecommitdiffstats
path: root/tk8.6/unix/tkUnixSend.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2017-09-22 18:51:12 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2017-09-22 18:51:12 (GMT)
commit3fa8e6dc88e8041b6cb88d1b1e9c05676d3346b7 (patch)
tree69afbb41089c8358615879f7cd3c4cf7997f4c7e /tk8.6/unix/tkUnixSend.c
parenta0e17db23c0fd7c771c0afce8cce350c98f90b02 (diff)
downloadblt-3fa8e6dc88e8041b6cb88d1b1e9c05676d3346b7.zip
blt-3fa8e6dc88e8041b6cb88d1b1e9c05676d3346b7.tar.gz
blt-3fa8e6dc88e8041b6cb88d1b1e9c05676d3346b7.tar.bz2
update to tcl/tk 8.6.7
Diffstat (limited to 'tk8.6/unix/tkUnixSend.c')
-rw-r--r--tk8.6/unix/tkUnixSend.c2039
1 files changed, 0 insertions, 2039 deletions
diff --git a/tk8.6/unix/tkUnixSend.c b/tk8.6/unix/tkUnixSend.c
deleted file mode 100644
index bbbdd77..0000000
--- a/tk8.6/unix/tkUnixSend.c
+++ /dev/null
@@ -1,2039 +0,0 @@
-/*
- * tkUnixSend.c --
- *
- * This file provides functions that implement the "send" command,
- * allowing commands to be passed from interpreter to interpreter.
- *
- * Copyright (c) 1989-1994 The Regents of the University of California.
- * Copyright (c) 1994-1996 Sun Microsystems, Inc.
- * Copyright (c) 1998-1999 by Scriptics Corporation.
- *
- * See the file "license.terms" for information on usage and redistribution of
- * this file, and for a DISCLAIMER OF ALL WARRANTIES.
- */
-
-#include "tkUnixInt.h"
-
-/*
- * The following structure is used to keep track of the interpreters
- * registered by this process.
- */
-
-typedef struct RegisteredInterp {
- char *name; /* Interpreter's name (malloc-ed). */
- Tcl_Interp *interp; /* Interpreter associated with name. NULL
- * means that the application was unregistered
- * or deleted while a send was in progress to
- * it. */
- TkDisplay *dispPtr; /* Display for the application. Needed because
- * we may need to unregister the interpreter
- * after its main window has been deleted. */
- struct RegisteredInterp *nextPtr;
- /* Next in list of names associated with
- * interps in this process. NULL means end of
- * list. */
-} RegisteredInterp;
-
-/*
- * A registry of all interpreters for a display is kept in a property
- * "InterpRegistry" on the root window of the display. It is organized as a
- * series of zero or more concatenated strings (in no particular order), each
- * of the form
- * window space name '\0'
- * where "window" is the hex id of the comm. window to use to talk to an
- * interpreter named "name".
- *
- * When the registry is being manipulated by an application (e.g. to add or
- * remove an entry), it is loaded into memory using a structure of the
- * following type:
- */
-
-typedef struct NameRegistry {
- TkDisplay *dispPtr; /* Display from which the registry was
- * read. */
- int locked; /* Non-zero means that the display was locked
- * when the property was read in. */
- int modified; /* Non-zero means that the property has been
- * modified, so it needs to be written out
- * when the NameRegistry is closed. */
- unsigned long propLength; /* Length of the property, in bytes. */
- char *property; /* The contents of the property, or NULL if
- * none. See format description above; this is
- * *not* terminated by the first null
- * character. Dynamically allocated. */
- int allocedByX; /* Non-zero means must free property with
- * XFree; zero means use ckfree. */
-} NameRegistry;
-
-/*
- * When a result is being awaited from a sent command, one of the following
- * structures is present on a list of all outstanding sent commands. The
- * information in the structure is used to process the result when it arrives.
- * You're probably wondering how there could ever be multiple outstanding sent
- * commands. This could happen if interpreters invoke each other recursively.
- * It's unlikely, but possible.
- */
-
-typedef struct PendingCommand {
- int serial; /* Serial number expected in result. */
- TkDisplay *dispPtr; /* Display being used for communication. */
- const char *target; /* Name of interpreter command is being sent
- * to. */
- Window commWindow; /* Target's communication window. */
- Tcl_Interp *interp; /* Interpreter from which the send was
- * invoked. */
- int code; /* Tcl return code for command will be stored
- * here. */
- char *result; /* String result for command (malloc'ed), or
- * NULL. */
- char *errorInfo; /* Information for "errorInfo" variable, or
- * NULL (malloc'ed). */
- char *errorCode; /* Information for "errorCode" variable, or
- * NULL (malloc'ed). */
- int gotResponse; /* 1 means a response has been received, 0
- * means the command is still outstanding. */
- struct PendingCommand *nextPtr;
- /* Next in list of all outstanding commands.
- * NULL means end of list. */
-} PendingCommand;
-
-typedef struct ThreadSpecificData {
- PendingCommand *pendingCommands;
- /* List of all commands currently being waited
- * for. */
- RegisteredInterp *interpListPtr;
- /* List of all interpreters registered in the
- * current process. */
-} ThreadSpecificData;
-static Tcl_ThreadDataKey dataKey;
-
-/*
- * The information below is used for communication between processes during
- * "send" commands. Each process keeps a private window, never even mapped,
- * with one property, "Comm". When a command is sent to an interpreter, the
- * command is appended to the comm property of the communication window
- * associated with the interp's process. Similarly, when a result is returned
- * from a sent command, it is also appended to the comm property.
- *
- * Each command and each result takes the form of ASCII text. For a command,
- * the text consists of a zero character followed by several null-terminated
- * ASCII strings. The first string consists of the single letter "c".
- * Subsequent strings have the form "option value" where the following options
- * are supported:
- *
- * -r commWindow serial
- *
- * This option means that a response should be sent to the window whose X
- * identifier is "commWindow" (in hex), and the response should be
- * identified with the serial number given by "serial" (in decimal). If
- * this option isn't specified then the send is asynchronous and no
- * response is sent.
- *
- * -n name
- *
- * "Name" gives the name of the application for which the command is
- * intended. This option must be present.
- *
- * -s script
- *
- * "Script" is the script to be executed. This option must be present.
- *
- * The options may appear in any order. The -n and -s options must be present,
- * but -r may be omitted for asynchronous RPCs. For compatibility with future
- * releases that may add new features, there may be additional options
- * present; as long as they start with a "-" character, they will be ignored.
- *
- * A result also consists of a zero character followed by several null-
- * terminated ASCII strings. The first string consists of the single letter
- * "r". Subsequent strings have the form "option value" where the following
- * options are supported:
- *
- * -s serial
- *
- * Identifies the command for which this is the result. It is the same as
- * the "serial" field from the -s option in the command. This option must
- * be present.
- *
- * -c code
- *
- * "Code" is the completion code for the script, in decimal. If the code
- * is omitted it defaults to TCL_OK.
- *
- * -r result
- *
- * "Result" is the result string for the script, which may be either a
- * result or an error message. If this field is omitted then it defaults
- * to an empty string.
- *
- * -i errorInfo
- *
- * "ErrorInfo" gives a string with which to initialize the errorInfo
- * variable. This option may be omitted; it is ignored unless the
- * completion code is TCL_ERROR.
- *
- * -e errorCode
- *
- * "ErrorCode" gives a string with with to initialize the errorCode
- * variable. This option may be omitted; it is ignored unless the
- * completion code is TCL_ERROR.
- *
- * Options may appear in any order, and only the -s option must be present. As
- * with commands, there may be additional options besides these; unknown
- * options are ignored.
- */
-
-/*
- * Other miscellaneous per-process data:
- */
-
-static struct {
- int sendSerial; /* The serial number that was used in the last
- * "send" command. */
- int sendDebug; /* This can be set while debugging to do
- * things like skip locking the server. */
-} localData = {0, 0};
-
-/*
- * Maximum size property that can be read at one time by this module:
- */
-
-#define MAX_PROP_WORDS 100000
-
-/*
- * Forward declarations for functions defined later in this file:
- */
-
-static int AppendErrorProc(ClientData clientData,
- XErrorEvent *errorPtr);
-static void AppendPropCarefully(Display *display,
- Window window, Atom property, char *value,
- int length, PendingCommand *pendingPtr);
-static void DeleteProc(ClientData clientData);
-static void RegAddName(NameRegistry *regPtr,
- const char *name, Window commWindow);
-static void RegClose(NameRegistry *regPtr);
-static void RegDeleteName(NameRegistry *regPtr, const char *name);
-static Window RegFindName(NameRegistry *regPtr, const char *name);
-static NameRegistry * RegOpen(Tcl_Interp *interp,
- TkDisplay *dispPtr, int lock);
-static void SendEventProc(ClientData clientData, XEvent *eventPtr);
-static int SendInit(Tcl_Interp *interp, TkDisplay *dispPtr);
-static Tk_RestrictProc SendRestrictProc;
-static int ServerSecure(TkDisplay *dispPtr);
-static void UpdateCommWindow(TkDisplay *dispPtr);
-static int ValidateName(TkDisplay *dispPtr, const char *name,
- Window commWindow, int oldOK);
-
-/*
- *----------------------------------------------------------------------
- *
- * RegOpen --
- *
- * This function loads the name registry for a display into memory so
- * that it can be manipulated.
- *
- * Results:
- * The return value is a pointer to the loaded registry.
- *
- * Side effects:
- * If "lock" is set then the server will be locked. It is the caller's
- * responsibility to call RegClose when finished with the registry, so
- * that we can write back the registry if needed, unlock the server if
- * needed, and free memory.
- *
- *----------------------------------------------------------------------
- */
-
-static NameRegistry *
-RegOpen(
- Tcl_Interp *interp, /* Interpreter to use for error reporting
- * (errors cause a panic so in fact no error
- * is ever returned, but the interpreter is
- * needed anyway). */
- TkDisplay *dispPtr, /* Display whose name registry is to be
- * opened. */
- int lock) /* Non-zero means lock the window server when
- * opening the registry, so no-one else can
- * use the registry until we close it. */
-{
- NameRegistry *regPtr;
- int result, actualFormat;
- unsigned long bytesAfter;
- Atom actualType;
- char **propertyPtr;
-
- if (dispPtr->commTkwin == NULL) {
- SendInit(interp, dispPtr);
- }
-
- regPtr = ckalloc(sizeof(NameRegistry));
- regPtr->dispPtr = dispPtr;
- regPtr->locked = 0;
- regPtr->modified = 0;
- regPtr->allocedByX = 1;
- propertyPtr = &regPtr->property;
-
- if (lock && !localData.sendDebug) {
- XGrabServer(dispPtr->display);
- regPtr->locked = 1;
- }
-
- /*
- * Read the registry property.
- */
-
- result = XGetWindowProperty(dispPtr->display,
- RootWindow(dispPtr->display, 0),
- dispPtr->registryProperty, 0, MAX_PROP_WORDS,
- False, XA_STRING, &actualType, &actualFormat,
- &regPtr->propLength, &bytesAfter,
- (unsigned char **) propertyPtr);
-
- if (actualType == None) {
- regPtr->propLength = 0;
- regPtr->property = NULL;
- } else if ((result != Success) || (actualFormat != 8)
- || (actualType != XA_STRING)) {
- /*
- * The property is improperly formed; delete it.
- */
-
- if (regPtr->property != NULL) {
- XFree(regPtr->property);
- regPtr->propLength = 0;
- regPtr->property = NULL;
- }
- XDeleteProperty(dispPtr->display,
- RootWindow(dispPtr->display, 0),
- dispPtr->registryProperty);
- }
-
- /*
- * Xlib placed an extra null byte after the end of the property, just to
- * make sure that it is always NULL-terminated. Be sure to include this
- * byte in our count if it's needed to ensure null termination (note: as
- * of 8/95 I'm no longer sure why this code is needed; seems like it
- * shouldn't be).
- */
-
- if ((regPtr->propLength > 0)
- && (regPtr->property[regPtr->propLength-1] != 0)) {
- regPtr->propLength++;
- }
- return regPtr;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * RegFindName --
- *
- * Given an open name registry, this function finds an entry with a given
- * name, if there is one, and returns information about that entry.
- *
- * Results:
- * The return value is the X identifier for the comm window for the
- * application named "name", or None if there is no such entry in the
- * registry.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-static Window
-RegFindName(
- NameRegistry *regPtr, /* Pointer to a registry opened with a
- * previous call to RegOpen. */
- const char *name) /* Name of an application. */
-{
- char *p;
-
- for (p=regPtr->property ; p-regPtr->property<(int)regPtr->propLength ;) {
- char *entry = p;
-
- while ((*p != 0) && (!isspace(UCHAR(*p)))) {
- p++;
- }
- if ((*p != 0) && (strcmp(name, p+1) == 0)) {
- unsigned id;
-
- if (sscanf(entry, "%x", &id) == 1) {
- /*
- * Must cast from an unsigned int to a Window in case we are
- * on a 64-bit architecture.
- */
-
- return (Window) id;
- }
- }
- while (*p != 0) {
- p++;
- }
- p++;
- }
- return None;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * RegDeleteName --
- *
- * This function deletes the entry for a given name from an open
- * registry.
- *
- * Results:
- * None.
- *
- * Side effects:
- * If there used to be an entry named "name" in the registry, then it is
- * deleted and the registry is marked as modified so it will be written
- * back when closed.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-RegDeleteName(
- NameRegistry *regPtr, /* Pointer to a registry opened with a
- * previous call to RegOpen. */
- const char *name) /* Name of an application. */
-{
- char *p;
-
- for (p=regPtr->property ; p-regPtr->property<(int)regPtr->propLength ;) {
- char *entry = p, *entryName;
-
- while ((*p != 0) && (!isspace(UCHAR(*p)))) {
- p++;
- }
- if (*p != 0) {
- p++;
- }
- entryName = p;
- while (*p != 0) {
- p++;
- }
- p++;
- if (strcmp(name, entryName) == 0) {
- int count;
-
- /*
- * Found the matching entry. Copy everything after it down on top
- * of it.
- */
-
- count = regPtr->propLength - (p - regPtr->property);
- if (count > 0) {
- char *src, *dst;
-
- for (src=p , dst=entry ; count>0 ; src++, dst++, count--) {
- *dst = *src;
- }
- }
- regPtr->propLength -= p - entry;
- regPtr->modified = 1;
- return;
- }
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * RegAddName --
- *
- * Add a new entry to an open registry.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The open registry is expanded; it is marked as modified so that it
- * will be written back when closed.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-RegAddName(
- NameRegistry *regPtr, /* Pointer to a registry opened with a
- * previous call to RegOpen. */
- const char *name, /* Name of an application. The caller must
- * ensure that this name isn't already
- * registered. */
- Window commWindow) /* X identifier for comm. window of
- * application. */
-{
- char id[30], *newProp;
- int idLength, newBytes;
-
- sprintf(id, "%x ", (unsigned) commWindow);
- idLength = strlen(id);
- newBytes = idLength + strlen(name) + 1;
- newProp = ckalloc(regPtr->propLength + newBytes);
- strcpy(newProp, id);
- strcpy(newProp+idLength, name);
- if (regPtr->property != NULL) {
- memcpy(newProp + newBytes, regPtr->property, regPtr->propLength);
- if (regPtr->allocedByX) {
- XFree(regPtr->property);
- } else {
- ckfree(regPtr->property);
- }
- }
- regPtr->modified = 1;
- regPtr->propLength += newBytes;
- regPtr->property = newProp;
- regPtr->allocedByX = 0;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * RegClose --
- *
- * This function is called to end a series of operations on a name
- * registry.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The registry is written back if it has been modified, and the X server
- * is unlocked if it was locked. Memory for the registry is freed, so the
- * caller should never use regPtr again.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-RegClose(
- NameRegistry *regPtr) /* Pointer to a registry opened with a
- * previous call to RegOpen. */
-{
- if (regPtr->modified) {
- if (!regPtr->locked && !localData.sendDebug) {
- Tcl_Panic("The name registry was modified without being locked!");
- }
- XChangeProperty(regPtr->dispPtr->display,
- RootWindow(regPtr->dispPtr->display, 0),
- regPtr->dispPtr->registryProperty, XA_STRING, 8,
- PropModeReplace, (unsigned char *) regPtr->property,
- (int) regPtr->propLength);
- }
-
- if (regPtr->locked) {
- XUngrabServer(regPtr->dispPtr->display);
- }
-
- /*
- * After ungrabbing the server, it's important to flush the output
- * immediately so that the server sees the ungrab command. Otherwise we
- * might do something else that needs to communicate with the server (such
- * as invoking a subprocess that needs to do I/O to the screen); if the
- * ungrab command is still sitting in our output buffer, we could
- * deadlock.
- */
-
- XFlush(regPtr->dispPtr->display);
-
- if (regPtr->property != NULL) {
- if (regPtr->allocedByX) {
- XFree(regPtr->property);
- } else {
- ckfree(regPtr->property);
- }
- }
- ckfree(regPtr);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * ValidateName --
- *
- * This function checks to see if an entry in the registry is still
- * valid.
- *
- * Results:
- * The return value is 1 if the given commWindow exists and its name is
- * "name". Otherwise 0 is returned.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-ValidateName(
- TkDisplay *dispPtr, /* Display for which to perform the
- * validation. */
- const char *name, /* The name of an application. */
- Window commWindow, /* X identifier for the application's comm.
- * window. */
- int oldOK) /* Non-zero means that we should consider an
- * application to be valid even if it looks
- * like an old-style (pre-4.0) one; 0 means
- * consider these invalid. */
-{
- int result, actualFormat, argc, i;
- unsigned long length, bytesAfter;
- Atom actualType;
- char *property, **propertyPtr = &property;
- Tk_ErrorHandler handler;
- const char **argv;
-
- property = NULL;
-
- /*
- * Ignore X errors when reading the property (e.g., the window might not
- * exist). If an error occurs, result will be some value other than
- * Success.
- */
-
- handler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1, NULL, NULL);
- result = XGetWindowProperty(dispPtr->display, commWindow,
- dispPtr->appNameProperty, 0, MAX_PROP_WORDS,
- False, XA_STRING, &actualType, &actualFormat,
- &length, &bytesAfter, (unsigned char **) propertyPtr);
-
- if ((result == Success) && (actualType == None)) {
- XWindowAttributes atts;
-
- /*
- * The comm. window exists but the property we're looking for doesn't
- * exist. This probably means that the application comes from an older
- * version of Tk (< 4.0) that didn't set the property; if this is the
- * case, then assume for compatibility's sake that everything's OK.
- * However, it's also possible that some random application has
- * re-used the window id for something totally unrelated. Check a few
- * characteristics of the window, such as its dimensions and mapped
- * state, to be sure that it still "smells" like a commWindow.
- */
-
- if (!oldOK
- || !XGetWindowAttributes(dispPtr->display, commWindow, &atts)
- || (atts.width != 1) || (atts.height != 1)
- || (atts.map_state != IsUnmapped)) {
- result = 0;
- } else {
- result = 1;
- }
- } else if ((result == Success) && (actualFormat == 8)
- && (actualType == XA_STRING)) {
- result = 0;
- if (Tcl_SplitList(NULL, property, &argc, &argv) == TCL_OK) {
- for (i = 0; i < argc; i++) {
- if (strcmp(argv[i], name) == 0) {
- result = 1;
- break;
- }
- }
- ckfree(argv);
- }
- } else {
- result = 0;
- }
- Tk_DeleteErrorHandler(handler);
- if (property != NULL) {
- XFree(property);
- }
- return result;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * ServerSecure --
- *
- * Check whether a server is secure enough for us to trust Tcl scripts
- * arriving via that server.
- *
- * Results:
- * The return value is 1 if the server is secure, which means that
- * host-style authentication is turned on but there are no hosts in the
- * enabled list. This means that some other form of authorization
- * (presumably more secure, such as xauth) is in use.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-ServerSecure(
- TkDisplay *dispPtr) /* Display to check. */
-{
-#ifdef TK_NO_SECURITY
- return 1;
-#else
- XHostAddress *addrPtr;
- int numHosts, secure;
- Bool enabled;
-
- addrPtr = XListHosts(dispPtr->display, &numHosts, &enabled);
- if (!enabled) {
- insecure:
- secure = 0;
- } else if (numHosts == 0) {
- secure = 1;
- } else {
- /*
- * Recent versions of X11 have the extra feature of allowing more
- * sophisticated authorization checks to be performed than the dozy
- * old ones that used to plague xhost usage. However, not all deployed
- * versions of Xlib know how to deal with this feature, so this code
- * is conditional on having the right #def in place. [Bug 1909931]
- *
- * Note that at this point we know that there's at least one entry in
- * the list returned by XListHosts. However there may be multiple
- * entries; as long as each is one of either 'SI:localhost:*' or
- * 'SI:localgroup:*' then we will claim to be secure enough.
- */
-
-#ifdef FamilyServerInterpreted
- XServerInterpretedAddress *siPtr;
- int i;
-
- for (i=0 ; i<numHosts ; i++) {
- if (addrPtr[i].family != FamilyServerInterpreted) {
- /*
- * We don't understand what the X server is letting in, so we
- * err on the side of safety.
- */
-
- goto insecure;
- }
- siPtr = (XServerInterpretedAddress *) addrPtr[0].address;
-
- /*
- * We don't check the username or group here. This is because it's
- * officially non-portable and we are just making sure there
- * aren't silly misconfigurations. (Apparently 'root' is not a
- * very good choice, but we still don't put any effort in to spot
- * that.) However we do check to see that the constraints are
- * imposed against the connecting user and/or group.
- */
-
- if ( !(siPtr->typelength == 9 /* ==strlen("localuser") */
- && !memcmp(siPtr->type, "localuser", 9))
- && !(siPtr->typelength == 10 /* ==strlen("localgroup") */
- && !memcmp(siPtr->type, "localgroup", 10))) {
- /*
- * The other defined types of server-interpreted controls
- * involve particular hosts. These are still insecure for the
- * same reasons that classic xhost access is insecure; there's
- * just no way to be sure that the users on those systems are
- * the ones who should be allowed to connect to this display.
- */
-
- goto insecure;
- }
- }
- secure = 1;
-#else
- /*
- * We don't understand what the X server is letting in, so we err on
- * the side of safety.
- */
-
- secure = 0;
-#endif /* FamilyServerInterpreted */
- }
- if (addrPtr != NULL) {
- XFree((char *) addrPtr);
- }
- return secure;
-#endif /* TK_NO_SECURITY */
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tk_SetAppName --
- *
- * This function is called to associate an ASCII name with a Tk
- * application. If the application has already been named, the name
- * replaces the old one.
- *
- * Results:
- * The return value is the name actually given to the application. This
- * will normally be the same as name, but if name was already in use for
- * an application then a name of the form "name #2" will be chosen, with
- * a high enough number to make the name unique.
- *
- * Side effects:
- * Registration info is saved, thereby allowing the "send" command to be
- * used later to invoke commands in the application. In addition, the
- * "send" command is created in the application's interpreter. The
- * registration will be removed automatically if the interpreter is
- * deleted or the "send" command is removed.
- *
- *----------------------------------------------------------------------
- */
-
-const char *
-Tk_SetAppName(
- Tk_Window tkwin, /* Token for any window in the application to
- * be named: it is just used to identify the
- * application and the display. */
- const char *name) /* The name that will be used to refer to the
- * interpreter in later "send" commands. Must
- * be globally unique. */
-{
- RegisteredInterp *riPtr, *riPtr2;
- Window w;
- TkWindow *winPtr = (TkWindow *) tkwin;
- TkDisplay *dispPtr = winPtr->dispPtr;
- NameRegistry *regPtr;
- Tcl_Interp *interp;
- const char *actualName;
- Tcl_DString dString;
- int offset, i;
- ThreadSpecificData *tsdPtr =
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
-
- interp = winPtr->mainPtr->interp;
- if (dispPtr->commTkwin == NULL) {
- SendInit(interp, winPtr->dispPtr);
- }
-
- /*
- * See if the application is already registered; if so, remove its current
- * name from the registry.
- */
-
- regPtr = RegOpen(interp, winPtr->dispPtr, 1);
- for (riPtr = tsdPtr->interpListPtr; ; riPtr = riPtr->nextPtr) {
- if (riPtr == NULL) {
- /*
- * This interpreter isn't currently registered; create the data
- * structure that will be used to register it locally, plus add
- * the "send" command to the interpreter.
- */
-
- riPtr = ckalloc(sizeof(RegisteredInterp));
- riPtr->interp = interp;
- riPtr->dispPtr = winPtr->dispPtr;
- riPtr->nextPtr = tsdPtr->interpListPtr;
- tsdPtr->interpListPtr = riPtr;
- riPtr->name = NULL;
- Tcl_CreateObjCommand(interp, "send", Tk_SendObjCmd, riPtr, DeleteProc);
- if (Tcl_IsSafe(interp)) {
- Tcl_HideCommand(interp, "send", "send");
- }
- break;
- }
- if (riPtr->interp == interp) {
- /*
- * The interpreter is currently registered; remove it from the
- * name registry.
- */
-
- if (riPtr->name) {
- RegDeleteName(regPtr, riPtr->name);
- ckfree(riPtr->name);
- }
- break;
- }
- }
-
- /*
- * Pick a name to use for the application. Use "name" if it's not already
- * in use. Otherwise add a suffix such as " #2", trying larger and larger
- * numbers until we eventually find one that is unique.
- */
-
- actualName = name;
- offset = 0; /* Needed only to avoid "used before
- * set" compiler warnings. */
- for (i = 1; ; i++) {
- if (i > 1) {
- if (i == 2) {
- Tcl_DStringInit(&dString);
- Tcl_DStringAppend(&dString, name, -1);
- Tcl_DStringAppend(&dString, " #", 2);
- offset = Tcl_DStringLength(&dString);
- Tcl_DStringSetLength(&dString, offset+TCL_INTEGER_SPACE);
- actualName = Tcl_DStringValue(&dString);
- }
- sprintf(Tcl_DStringValue(&dString) + offset, "%d", i);
- }
- w = RegFindName(regPtr, actualName);
- if (w == None) {
- break;
- }
-
- /*
- * The name appears to be in use already, but double-check to be sure
- * (perhaps the application died without removing its name from the
- * registry?).
- */
-
- if (w == Tk_WindowId(dispPtr->commTkwin)) {
- for (riPtr2 = tsdPtr->interpListPtr; riPtr2 != NULL;
- riPtr2 = riPtr2->nextPtr) {
- if ((riPtr2->interp != interp) &&
- (strcmp(riPtr2->name, actualName) == 0)) {
- goto nextSuffix;
- }
- }
- RegDeleteName(regPtr, actualName);
- break;
- } else if (!ValidateName(winPtr->dispPtr, actualName, w, 1)) {
- RegDeleteName(regPtr, actualName);
- break;
- }
- nextSuffix:
- continue;
- }
-
- /*
- * We've now got a name to use. Store it in the name registry and in the
- * local entry for this application, plus put it in a property on the
- * commWindow.
- */
-
- RegAddName(regPtr, actualName, Tk_WindowId(dispPtr->commTkwin));
- RegClose(regPtr);
- riPtr->name = ckalloc(strlen(actualName) + 1);
- strcpy(riPtr->name, actualName);
- if (actualName != name) {
- Tcl_DStringFree(&dString);
- }
- UpdateCommWindow(dispPtr);
-
- return riPtr->name;
-}
-
-/*
- *--------------------------------------------------------------
- *
- * Tk_SendObjCmd --
- *
- * This function is invoked to process the "send" Tcl command. See the
- * user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
-
-int
-Tk_SendObjCmd(
- ClientData clientData, /* Information about sender (only dispPtr
- * field is used). */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument strings. */
-{
- enum {
- SEND_ASYNC, SEND_DISPLAYOF, SEND_LAST
- };
- static const char *const sendOptions[] = {
- "-async", "-displayof", "--", NULL
- };
- TkWindow *winPtr;
- Window commWindow;
- PendingCommand pending;
- register RegisteredInterp *riPtr;
- const char *destName;
- int result, index, async, i, firstArg;
- Tk_RestrictProc *prevProc;
- ClientData prevArg;
- TkDisplay *dispPtr;
- Tcl_Time timeout;
- NameRegistry *regPtr;
- Tcl_DString request;
- ThreadSpecificData *tsdPtr =
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
- Tcl_Interp *localInterp; /* Used when the interpreter to send the
- * command to is within the same process. */
-
- /*
- * Process options, if any.
- */
-
- async = 0;
- winPtr = (TkWindow *) Tk_MainWindow(interp);
- if (winPtr == NULL) {
- return TCL_ERROR;
- }
- for (i = 1; i < objc; i++) {
- if (Tcl_GetIndexFromObjStruct(interp, objv[i], sendOptions,
- sizeof(char *), "option", 0, &index) != TCL_OK) {
- break;
- }
- if (index == SEND_ASYNC) {
- ++async;
- } else if (index == SEND_DISPLAYOF) {
- winPtr = (TkWindow *) Tk_NameToWindow(interp, Tcl_GetString(objv[++i]),
- (Tk_Window) winPtr);
- if (winPtr == NULL) {
- return TCL_ERROR;
- }
- } else if (index == SEND_LAST) {
- i++;
- break;
- }
- }
-
- if (objc < (i+2)) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "?-option value ...? interpName arg ?arg ...?");
- return TCL_ERROR;
- }
- destName = Tcl_GetString(objv[i]);
- firstArg = i+1;
-
- dispPtr = winPtr->dispPtr;
- if (dispPtr->commTkwin == NULL) {
- SendInit(interp, winPtr->dispPtr);
- }
-
- /*
- * See if the target interpreter is local. If so, execute the command
- * directly without going through the X server. The only tricky thing is
- * passing the result from the target interpreter to the invoking
- * interpreter. Watch out: they could be the same!
- */
-
- for (riPtr = tsdPtr->interpListPtr; riPtr != NULL;
- riPtr = riPtr->nextPtr) {
- if ((riPtr->dispPtr != dispPtr)
- || (strcmp(riPtr->name, destName) != 0)) {
- continue;
- }
- Tcl_Preserve(riPtr);
- localInterp = riPtr->interp;
- Tcl_Preserve(localInterp);
- if (firstArg == (objc-1)) {
- result = Tcl_EvalEx(localInterp, Tcl_GetString(objv[firstArg]), -1, TCL_EVAL_GLOBAL);
- } else {
- Tcl_DStringInit(&request);
- Tcl_DStringAppend(&request, Tcl_GetString(objv[firstArg]), -1);
- for (i = firstArg+1; i < objc; i++) {
- Tcl_DStringAppend(&request, " ", 1);
- Tcl_DStringAppend(&request, Tcl_GetString(objv[i]), -1);
- }
- result = Tcl_EvalEx(localInterp, Tcl_DStringValue(&request), -1, TCL_EVAL_GLOBAL);
- Tcl_DStringFree(&request);
- }
- if (interp != localInterp) {
- if (result == TCL_ERROR) {
- Tcl_Obj *errorObjPtr;
-
- /*
- * An error occurred, so transfer error information from the
- * destination interpreter back to our interpreter. Must clear
- * interp's result before calling Tcl_AddErrorInfo, since
- * Tcl_AddErrorInfo will store the interp's result in
- * errorInfo before appending riPtr's $errorInfo; we've
- * already got everything we need in riPtr's $errorInfo.
- */
-
- Tcl_ResetResult(interp);
- Tcl_AddErrorInfo(interp, Tcl_GetVar2(localInterp,
- "errorInfo", NULL, TCL_GLOBAL_ONLY));
- errorObjPtr = Tcl_GetVar2Ex(localInterp, "errorCode", NULL,
- TCL_GLOBAL_ONLY);
- Tcl_SetObjErrorCode(interp, errorObjPtr);
- }
- Tcl_SetObjResult(interp, Tcl_GetObjResult(localInterp));
- Tcl_ResetResult(localInterp);
- }
- Tcl_Release(riPtr);
- Tcl_Release(localInterp);
- return result;
- }
-
- /*
- * Bind the interpreter name to a communication window.
- */
-
- regPtr = RegOpen(interp, winPtr->dispPtr, 0);
- commWindow = RegFindName(regPtr, destName);
- RegClose(regPtr);
- if (commWindow == None) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "no application named \"%s\"", destName));
- Tcl_SetErrorCode(interp, "TK", "LOOKUP", "APPLICATION", destName,
- NULL);
- return TCL_ERROR;
- }
-
- /*
- * Send the command to the target interpreter by appending it to the comm
- * window in the communication window.
- */
-
- localData.sendSerial++;
- Tcl_DStringInit(&request);
- Tcl_DStringAppend(&request, "\0c\0-n ", 6);
- Tcl_DStringAppend(&request, destName, -1);
- if (!async) {
- char buffer[TCL_INTEGER_SPACE * 2];
-
- sprintf(buffer, "%x %d",
- (unsigned) Tk_WindowId(dispPtr->commTkwin),
- localData.sendSerial);
- Tcl_DStringAppend(&request, "\0-r ", 4);
- Tcl_DStringAppend(&request, buffer, -1);
- }
- Tcl_DStringAppend(&request, "\0-s ", 4);
- Tcl_DStringAppend(&request, Tcl_GetString(objv[firstArg]), -1);
- for (i = firstArg+1; i < objc; i++) {
- Tcl_DStringAppend(&request, " ", 1);
- Tcl_DStringAppend(&request, Tcl_GetString(objv[i]), -1);
- }
- (void) AppendPropCarefully(dispPtr->display, commWindow,
- dispPtr->commProperty, Tcl_DStringValue(&request),
- Tcl_DStringLength(&request) + 1, (async ? NULL : &pending));
- Tcl_DStringFree(&request);
- if (async) {
- /*
- * This is an asynchronous send: return immediately without waiting
- * for a response.
- */
-
- return TCL_OK;
- }
-
- /*
- * Register the fact that we're waiting for a command to complete (this is
- * needed by SendEventProc and by AppendErrorProc to pass back the
- * command's results). Set up a timeout handler so that we can check
- * during long sends to make sure that the destination application is
- * still alive.
- */
-
- pending.serial = localData.sendSerial;
- pending.dispPtr = dispPtr;
- pending.target = destName;
- pending.commWindow = commWindow;
- pending.interp = interp;
- pending.result = NULL;
- pending.errorInfo = NULL;
- pending.errorCode = NULL;
- pending.gotResponse = 0;
- pending.nextPtr = tsdPtr->pendingCommands;
- tsdPtr->pendingCommands = &pending;
-
- /*
- * Enter a loop processing X events until the result comes in or the
- * target is declared to be dead. While waiting for a result, look only at
- * send-related events so that the send is synchronous with respect to
- * other events in the application.
- */
-
- prevProc = Tk_RestrictEvents(SendRestrictProc, NULL, &prevArg);
- Tcl_GetTime(&timeout);
- timeout.sec += 2;
- while (!pending.gotResponse) {
- if (!TkUnixDoOneXEvent(&timeout)) {
- /*
- * An unusually long amount of time has elapsed during the
- * processing of a sent command. Check to make sure that the
- * target application still exists. If it does, reset the timeout.
- */
-
- if (!ValidateName(pending.dispPtr, pending.target,
- pending.commWindow, 0)) {
- const char *msg;
-
- if (ValidateName(pending.dispPtr, pending.target,
- pending.commWindow, 1)) {
- msg = "target application died or uses a Tk version before 4.0";
- } else {
- msg = "target application died";
- }
- pending.code = TCL_ERROR;
- pending.result = ckalloc(strlen(msg) + 1);
- strcpy(pending.result, msg);
- pending.gotResponse = 1;
- } else {
- Tcl_GetTime(&timeout);
- timeout.sec += 2;
- }
- }
- }
- Tk_RestrictEvents(prevProc, prevArg, &prevArg);
-
- /*
- * Unregister the information about the pending command and return the
- * result.
- */
-
- if (tsdPtr->pendingCommands != &pending) {
- Tcl_Panic("Tk_SendCmd: corrupted send stack");
- }
- tsdPtr->pendingCommands = pending.nextPtr;
- if (pending.errorInfo != NULL) {
- /*
- * Special trick: must clear the interp's result before calling
- * Tcl_AddErrorInfo, since Tcl_AddErrorInfo will store the interp's
- * result in errorInfo before appending pending.errorInfo; we've
- * already got everything we need in pending.errorInfo.
- */
-
- Tcl_ResetResult(interp);
- Tcl_AddErrorInfo(interp, pending.errorInfo);
- ckfree(pending.errorInfo);
- }
- if (pending.errorCode != NULL) {
- Tcl_SetObjErrorCode(interp, Tcl_NewStringObj(pending.errorCode, -1));
- ckfree(pending.errorCode);
- }
- Tcl_SetObjResult(interp, Tcl_NewStringObj(pending.result, -1));
- ckfree(pending.result);
- return pending.code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TkGetInterpNames --
- *
- * This function is invoked to fetch a list of all the interpreter names
- * currently registered for the display of a particular window.
- *
- * Results:
- * A standard Tcl return value. The interp's result will be set to hold a
- * list of all the interpreter names defined for tkwin's display. If an
- * error occurs, then TCL_ERROR is returned and the interp's result will
- * hold an error message.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TkGetInterpNames(
- Tcl_Interp *interp, /* Interpreter for returning a result. */
- Tk_Window tkwin) /* Window whose display is to be used for the
- * lookup. */
-{
- TkWindow *winPtr = (TkWindow *) tkwin;
- NameRegistry *regPtr;
- Tcl_Obj *resultObj = Tcl_NewObj();
- char *p;
-
- /*
- * Read the registry property, then scan through all of its entries.
- * Validate each entry to be sure that its application still exists.
- */
-
- regPtr = RegOpen(interp, winPtr->dispPtr, 1);
- for (p=regPtr->property ; p-regPtr->property<(int)regPtr->propLength ;) {
- char *entry = p, *entryName;
- Window commWindow;
- unsigned id;
-
- if (sscanf(p, "%x", (unsigned *) &id) != 1) {
- commWindow = None;
- } else {
- commWindow = id;
- }
- while ((*p != 0) && (!isspace(UCHAR(*p)))) {
- p++;
- }
- if (*p != 0) {
- p++;
- }
- entryName = p;
- while (*p != 0) {
- p++;
- }
- p++;
- if (ValidateName(winPtr->dispPtr, entryName, commWindow, 1)) {
- /*
- * The application still exists; add its name to the result.
- */
-
- Tcl_ListObjAppendElement(NULL, resultObj,
- Tcl_NewStringObj(entryName, -1));
- } else {
- int count;
-
- /*
- * This name is bogus (perhaps the application died without
- * cleaning up its entry in the registry?). Delete the name.
- */
-
- count = regPtr->propLength - (p - regPtr->property);
- if (count > 0) {
- char *src, *dst;
-
- for (src = p, dst = entry; count > 0; src++, dst++, count--) {
- *dst = *src;
- }
- }
- regPtr->propLength -= p - entry;
- regPtr->modified = 1;
- p = entry;
- }
- }
- RegClose(regPtr);
- Tcl_SetObjResult(interp, resultObj);
- return TCL_OK;
-}
-
-/*
- *--------------------------------------------------------------
- *
- * TkSendCleanup --
- *
- * This function is called to free resources used by the communication
- * channels for sending commands and receiving results.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Frees various data structures and windows.
- *
- *--------------------------------------------------------------
- */
-
-void
-TkSendCleanup(
- TkDisplay *dispPtr)
-{
- if (dispPtr->commTkwin != NULL) {
- Tk_DeleteEventHandler(dispPtr->commTkwin, PropertyChangeMask,
- SendEventProc, dispPtr);
- Tk_DestroyWindow(dispPtr->commTkwin);
- Tcl_Release(dispPtr->commTkwin);
- dispPtr->commTkwin = NULL;
- }
-}
-
-/*
- *--------------------------------------------------------------
- *
- * SendInit --
- *
- * This function is called to initialize the communication channels for
- * sending commands and receiving results.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Sets up various data structures and windows.
- *
- *--------------------------------------------------------------
- */
-
-static int
-SendInit(
- Tcl_Interp *interp, /* Interpreter to use for error reporting (no
- * errors are ever returned, but the
- * interpreter is needed anyway). */
- TkDisplay *dispPtr) /* Display to initialize. */
-{
- XSetWindowAttributes atts;
-
- /*
- * Create the window used for communication, and set up an event handler
- * for it.
- */
-
- dispPtr->commTkwin = (Tk_Window) TkAllocWindow(dispPtr,
- DefaultScreen(dispPtr->display), NULL);
- Tcl_Preserve(dispPtr->commTkwin);
- ((TkWindow *) dispPtr->commTkwin)->flags |=
- TK_TOP_HIERARCHY|TK_TOP_LEVEL|TK_HAS_WRAPPER|TK_WIN_MANAGED;
- TkWmNewWindow((TkWindow *) dispPtr->commTkwin);
- atts.override_redirect = True;
- Tk_ChangeWindowAttributes(dispPtr->commTkwin,
- CWOverrideRedirect, &atts);
- Tk_CreateEventHandler(dispPtr->commTkwin, PropertyChangeMask,
- SendEventProc, dispPtr);
- Tk_MakeWindowExist(dispPtr->commTkwin);
-
- /*
- * Get atoms used as property names.
- */
-
- dispPtr->commProperty = Tk_InternAtom(dispPtr->commTkwin, "Comm");
- dispPtr->registryProperty = Tk_InternAtom(dispPtr->commTkwin,
- "InterpRegistry");
- dispPtr->appNameProperty = Tk_InternAtom(dispPtr->commTkwin,
- "TK_APPLICATION");
-
- return TCL_OK;
-}
-
-/*
- *--------------------------------------------------------------
- *
- * SendEventProc --
- *
- * This function is invoked automatically by the toolkit event manager
- * when a property changes on the communication window. This function
- * reads the property and handles command requests and responses.
- *
- * Results:
- * None.
- *
- * Side effects:
- * If there are command requests in the property, they are executed. If
- * there are responses in the property, their information is saved for
- * the (ostensibly waiting) "send" commands. The property is deleted.
- *
- *--------------------------------------------------------------
- */
-
-static void
-SendEventProc(
- ClientData clientData, /* Display information. */
- XEvent *eventPtr) /* Information about event. */
-{
- TkDisplay *dispPtr = clientData;
- char *propInfo, **propInfoPtr = &propInfo;
- const char *p;
- int result, actualFormat;
- unsigned long numItems, bytesAfter;
- Atom actualType;
- Tcl_Interp *remoteInterp; /* Interp in which to execute the command. */
- ThreadSpecificData *tsdPtr =
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
-
- if ((eventPtr->xproperty.atom != dispPtr->commProperty)
- || (eventPtr->xproperty.state != PropertyNewValue)) {
- return;
- }
-
- /*
- * Read the comm property and delete it.
- */
-
- propInfo = NULL;
- result = XGetWindowProperty(dispPtr->display,
- Tk_WindowId(dispPtr->commTkwin), dispPtr->commProperty, 0,
- MAX_PROP_WORDS, True, XA_STRING, &actualType, &actualFormat,
- &numItems, &bytesAfter, (unsigned char **) propInfoPtr);
-
- /*
- * If the property doesn't exist or is improperly formed then ignore it.
- */
-
- if ((result != Success) || (actualType != XA_STRING)
- || (actualFormat != 8)) {
- if (propInfo != NULL) {
- XFree(propInfo);
- }
- return;
- }
-
- /*
- * Several commands and results could arrive in the property at one time;
- * each iteration through the outer loop handles a single command or
- * result.
- */
-
- for (p = propInfo; (p-propInfo) < (int) numItems; ) {
- /*
- * Ignore leading NULLs; each command or result starts with a NULL so
- * that no matter how badly formed a preceding command is, we'll be
- * able to tell that a new command/result is starting.
- */
-
- if (*p == 0) {
- p++;
- continue;
- }
-
- if ((*p == 'c') && (p[1] == 0)) {
- Window commWindow;
- const char *interpName, *script, *serial;
- char *end;
- Tcl_DString reply;
- RegisteredInterp *riPtr;
-
- /*
- *----------------------------------------------------------
- * This is an incoming command from some other application.
- * Iterate over all of its options. Stop when we reach the end of
- * the property or something that doesn't look like an option.
- *----------------------------------------------------------
- */
-
- p += 2;
- interpName = NULL;
- commWindow = None;
- serial = "";
- script = NULL;
- while (((p-propInfo) < (int) numItems) && (*p == '-')) {
- switch (p[1]) {
- case 'r':
- commWindow = (Window) strtoul(p+2, &end, 16);
- if ((end == p+2) || (*end != ' ')) {
- commWindow = None;
- } else {
- p = serial = end+1;
- }
- break;
- case 'n':
- if (p[2] == ' ') {
- interpName = p+3;
- }
- break;
- case 's':
- if (p[2] == ' ') {
- script = p+3;
- }
- break;
- }
- while (*p != 0) {
- p++;
- }
- p++;
- }
-
- if ((script == NULL) || (interpName == NULL)) {
- continue;
- }
-
- /*
- * Initialize the result property, so that we're ready at any time
- * if we need to return an error.
- */
-
- if (commWindow != None) {
- Tcl_DStringInit(&reply);
- Tcl_DStringAppend(&reply, "\0r\0-s ", 6);
- Tcl_DStringAppend(&reply, serial, -1);
- Tcl_DStringAppend(&reply, "\0-r ", 4);
- }
-
- if (!ServerSecure(dispPtr)) {
- if (commWindow != None) {
- Tcl_DStringAppend(&reply,
- "X server insecure (must use xauth-style "
- "authorization); command ignored", -1);
- }
- result = TCL_ERROR;
- goto returnResult;
- }
-
- /*
- * Locate the application, then execute the script.
- */
-
- for (riPtr = tsdPtr->interpListPtr; ; riPtr = riPtr->nextPtr) {
- if (riPtr == NULL) {
- if (commWindow != None) {
- Tcl_DStringAppend(&reply,
- "receiver never heard of interpreter \"", -1);
- Tcl_DStringAppend(&reply, interpName, -1);
- Tcl_DStringAppend(&reply, "\"", 1);
- }
- result = TCL_ERROR;
- goto returnResult;
- }
- if (strcmp(riPtr->name, interpName) == 0) {
- break;
- }
- }
- Tcl_Preserve(riPtr);
-
- /*
- * We must protect the interpreter because the script may enter
- * another event loop, which might call Tcl_DeleteInterp.
- */
-
- remoteInterp = riPtr->interp;
- Tcl_Preserve(remoteInterp);
-
- result = Tcl_EvalEx(remoteInterp, script, -1, TCL_EVAL_GLOBAL);
-
- /*
- * The call to Tcl_Release may have released the interpreter which
- * will cause the "send" command for that interpreter to be
- * deleted. The command deletion callback will set the
- * riPtr->interp field to NULL, hence the check below for NULL.
- */
-
- if (commWindow != None) {
- Tcl_DStringAppend(&reply, Tcl_GetString(Tcl_GetObjResult(remoteInterp)),
- -1);
- if (result == TCL_ERROR) {
- const char *varValue;
-
- varValue = Tcl_GetVar2(remoteInterp, "errorInfo",
- NULL, TCL_GLOBAL_ONLY);
- if (varValue != NULL) {
- Tcl_DStringAppend(&reply, "\0-i ", 4);
- Tcl_DStringAppend(&reply, varValue, -1);
- }
- varValue = Tcl_GetVar2(remoteInterp, "errorCode",
- NULL, TCL_GLOBAL_ONLY);
- if (varValue != NULL) {
- Tcl_DStringAppend(&reply, "\0-e ", 4);
- Tcl_DStringAppend(&reply, varValue, -1);
- }
- }
- }
- Tcl_Release(remoteInterp);
- Tcl_Release(riPtr);
-
- /*
- * Return the result to the sender if a commWindow was specified
- * (if none was specified then this is an asynchronous call).
- * Right now reply has everything but the completion code, but it
- * needs the NULL to terminate the current option.
- */
-
- returnResult:
- if (commWindow != None) {
- if (result != TCL_OK) {
- char buffer[TCL_INTEGER_SPACE];
-
- sprintf(buffer, "%d", result);
- Tcl_DStringAppend(&reply, "\0-c ", 4);
- Tcl_DStringAppend(&reply, buffer, -1);
- }
- (void) AppendPropCarefully(dispPtr->display, commWindow,
- dispPtr->commProperty, Tcl_DStringValue(&reply),
- Tcl_DStringLength(&reply) + 1, NULL);
- XFlush(dispPtr->display);
- Tcl_DStringFree(&reply);
- }
- } else if ((*p == 'r') && (p[1] == 0)) {
- int serial, code, gotSerial;
- const char *errorInfo, *errorCode, *resultString;
- PendingCommand *pcPtr;
-
- /*
- *----------------------------------------------------------
- * This is a reply to some command that we sent out. Iterate over
- * all of its options. Stop when we reach the end of the property
- * or something that doesn't look like an option.
- *----------------------------------------------------------
- */
-
- p += 2;
- code = TCL_OK;
- gotSerial = 0;
- errorInfo = NULL;
- errorCode = NULL;
- resultString = "";
- while (((p-propInfo) < (int) numItems) && (*p == '-')) {
- switch (p[1]) {
- case 'c':
- if (sscanf(p+2, " %d", &code) != 1) {
- code = TCL_OK;
- }
- break;
- case 'e':
- if (p[2] == ' ') {
- errorCode = p+3;
- }
- break;
- case 'i':
- if (p[2] == ' ') {
- errorInfo = p+3;
- }
- break;
- case 'r':
- if (p[2] == ' ') {
- resultString = p+3;
- }
- break;
- case 's':
- if (sscanf(p+2, " %d", &serial) == 1) {
- gotSerial = 1;
- }
- break;
- }
- while (*p != 0) {
- p++;
- }
- p++;
- }
-
- if (!gotSerial) {
- continue;
- }
-
- /*
- * Give the result information to anyone who's waiting for it.
- */
-
- for (pcPtr = tsdPtr->pendingCommands; pcPtr != NULL;
- pcPtr = pcPtr->nextPtr) {
- if ((serial != pcPtr->serial) || (pcPtr->result != NULL)) {
- continue;
- }
- pcPtr->code = code;
- if (resultString != NULL) {
- pcPtr->result = ckalloc(strlen(resultString) + 1);
- strcpy(pcPtr->result, resultString);
- }
- if (code == TCL_ERROR) {
- if (errorInfo != NULL) {
- pcPtr->errorInfo = ckalloc(strlen(errorInfo) + 1);
- strcpy(pcPtr->errorInfo, errorInfo);
- }
- if (errorCode != NULL) {
- pcPtr->errorCode = ckalloc(strlen(errorCode) + 1);
- strcpy(pcPtr->errorCode, errorCode);
- }
- }
- pcPtr->gotResponse = 1;
- break;
- }
- } else {
- /*
- * Didn't recognize this thing. Just skip through the next null
- * character and try again.
- */
-
- while (*p != 0) {
- p++;
- }
- p++;
- }
- }
- XFree(propInfo);
-}
-
-/*
- *--------------------------------------------------------------
- *
- * AppendPropCarefully --
- *
- * Append a given property to a given window, but set up an X error
- * handler so that if the append fails this function can return an error
- * code rather than having Xlib panic.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The given property on the given window is appended to. If this
- * operation fails and if pendingPtr is non-NULL, then the pending
- * operation is marked as complete with an error.
- *
- *--------------------------------------------------------------
- */
-
-static void
-AppendPropCarefully(
- Display *display, /* Display on which to operate. */
- Window window, /* Window whose property is to be modified. */
- Atom property, /* Name of property. */
- char *value, /* Characters to append to property. */
- int length, /* Number of bytes to append. */
- PendingCommand *pendingPtr) /* Pending command to mark complete if an
- * error occurs during the property op. NULL
- * means just ignore the error. */
-{
- Tk_ErrorHandler handler;
-
- handler = Tk_CreateErrorHandler(display, -1, -1, -1, AppendErrorProc,
- pendingPtr);
- XChangeProperty(display, window, property, XA_STRING, 8,
- PropModeAppend, (unsigned char *) value, length);
- Tk_DeleteErrorHandler(handler);
-}
-
-/*
- * The function below is invoked if an error occurs during the XChangeProperty
- * operation above.
- */
-
- /* ARGSUSED */
-static int
-AppendErrorProc(
- ClientData clientData, /* Command to mark complete, or NULL. */
- XErrorEvent *errorPtr) /* Information about error. */
-{
- PendingCommand *pendingPtr = clientData;
- register PendingCommand *pcPtr;
- ThreadSpecificData *tsdPtr =
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
-
- if (pendingPtr == NULL) {
- return 0;
- }
-
- /*
- * Make sure this command is still pending.
- */
-
- for (pcPtr = tsdPtr->pendingCommands; pcPtr != NULL;
- pcPtr = pcPtr->nextPtr) {
- if ((pcPtr == pendingPtr) && (pcPtr->result == NULL)) {
- pcPtr->result = ckalloc(strlen(pcPtr->target) + 50);
- sprintf(pcPtr->result, "no application named \"%s\"",
- pcPtr->target);
- pcPtr->code = TCL_ERROR;
- pcPtr->gotResponse = 1;
- break;
- }
- }
- return 0;
-}
-
-/*
- *--------------------------------------------------------------
- *
- * DeleteProc --
- *
- * This function is invoked by Tcl when the "send" command is deleted in
- * an interpreter. It unregisters the interpreter.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The interpreter given by riPtr is unregistered.
- *
- *--------------------------------------------------------------
- */
-
-static void
-DeleteProc(
- ClientData clientData) /* Info about registration, passed as
- * ClientData. */
-{
- RegisteredInterp *riPtr = clientData;
- register RegisteredInterp *riPtr2;
- NameRegistry *regPtr;
- ThreadSpecificData *tsdPtr =
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
-
- regPtr = RegOpen(riPtr->interp, riPtr->dispPtr, 1);
- RegDeleteName(regPtr, riPtr->name);
- RegClose(regPtr);
-
- if (tsdPtr->interpListPtr == riPtr) {
- tsdPtr->interpListPtr = riPtr->nextPtr;
- } else {
- for (riPtr2 = tsdPtr->interpListPtr; riPtr2 != NULL;
- riPtr2 = riPtr2->nextPtr) {
- if (riPtr2->nextPtr == riPtr) {
- riPtr2->nextPtr = riPtr->nextPtr;
- break;
- }
- }
- }
- ckfree(riPtr->name);
- riPtr->interp = NULL;
- UpdateCommWindow(riPtr->dispPtr);
- Tcl_EventuallyFree(riPtr, TCL_DYNAMIC);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * SendRestrictProc --
- *
- * This function filters incoming events when a "send" command is
- * outstanding. It defers all events except those containing send
- * commands and results.
- *
- * Results:
- * False is returned except for property-change events on a commWindow.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-static Tk_RestrictAction
-SendRestrictProc(
- ClientData clientData, /* Not used. */
- register XEvent *eventPtr) /* Event that just arrived. */
-{
- TkDisplay *dispPtr;
-
- if (eventPtr->type != PropertyNotify) {
- return TK_DEFER_EVENT;
- }
- for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
- dispPtr = dispPtr->nextPtr) {
- if ((eventPtr->xany.display == dispPtr->display)
- && (eventPtr->xproperty.window
- == Tk_WindowId(dispPtr->commTkwin))) {
- return TK_PROCESS_EVENT;
- }
- }
- return TK_DEFER_EVENT;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * UpdateCommWindow --
- *
- * This function updates the list of application names stored on our
- * commWindow. It is typically called when interpreters are registered
- * and unregistered.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The TK_APPLICATION property on the comm window is updated.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-UpdateCommWindow(
- TkDisplay *dispPtr) /* Display whose commWindow is to be
- * updated. */
-{
- Tcl_DString names;
- RegisteredInterp *riPtr;
- ThreadSpecificData *tsdPtr =
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
-
- Tcl_DStringInit(&names);
- for (riPtr = tsdPtr->interpListPtr; riPtr != NULL;
- riPtr = riPtr->nextPtr) {
- Tcl_DStringAppendElement(&names, riPtr->name);
- }
- XChangeProperty(dispPtr->display, Tk_WindowId(dispPtr->commTkwin),
- dispPtr->appNameProperty, XA_STRING, 8, PropModeReplace,
- (unsigned char *) Tcl_DStringValue(&names),
- Tcl_DStringLength(&names));
- Tcl_DStringFree(&names);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TkpTestsendCmd --
- *
- * This function implements the "testsend" command. It provides a set of
- * functions for testing the "send" command and support function in
- * tkSend.c.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Depends on option; see below.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-TkpTestsendCmd(
- ClientData clientData, /* Main window for application. */
- Tcl_Interp *interp, /* Current interpreter. */
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument strings. */
-{
- enum {
- TESTSEND_BOGUS, TESTSEND_PROP, TESTSEND_SERIAL
- };
- static const char *const testsendOptions[] = {
- "bogus", "prop", "serial", NULL
- };
- TkWindow *winPtr = clientData;
- int index;
-
- if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "option ?arg ...?");
- return TCL_ERROR;
- }
-
- if (Tcl_GetIndexFromObjStruct(interp, objv[1], testsendOptions,
- sizeof(char *), "option", 0, &index) != TCL_OK) {
- return TCL_ERROR;
- }
- if (index == TESTSEND_BOGUS) {
- XChangeProperty(winPtr->dispPtr->display,
- RootWindow(winPtr->dispPtr->display, 0),
- winPtr->dispPtr->registryProperty, XA_INTEGER, 32,
- PropModeReplace,
- (unsigned char *) "This is bogus information", 6);
- } else if (index == TESTSEND_PROP) {
- int result, actualFormat;
- unsigned long length, bytesAfter;
- Atom actualType, propName;
- char *property, **propertyPtr = &property, *p, *end;
- Window w;
-
- if ((objc != 4) && (objc != 5)) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "prop window name ?value ?");
- return TCL_ERROR;
- }
- if (strcmp(Tcl_GetString(objv[2]), "root") == 0) {
- w = RootWindow(winPtr->dispPtr->display, 0);
- } else if (strcmp(Tcl_GetString(objv[2]), "comm") == 0) {
- w = Tk_WindowId(winPtr->dispPtr->commTkwin);
- } else {
- w = strtoul(Tcl_GetString(objv[2]), &end, 0);
- }
- propName = Tk_InternAtom((Tk_Window) winPtr, Tcl_GetString(objv[3]));
- if (objc == 4) {
- property = NULL;
- result = XGetWindowProperty(winPtr->dispPtr->display, w, propName,
- 0, 100000, False, XA_STRING, &actualType, &actualFormat,
- &length, &bytesAfter, (unsigned char **) propertyPtr);
- if ((result == Success) && (actualType != None)
- && (actualFormat == 8) && (actualType == XA_STRING)) {
- for (p = property; (unsigned long)(p-property) < length; p++) {
- if (*p == 0) {
- *p = '\n';
- }
- }
- Tcl_SetObjResult(interp, Tcl_NewStringObj(property, -1));
- }
- if (property != NULL) {
- XFree(property);
- }
- } else if (Tcl_GetString(objv[4])[0] == 0) {
- XDeleteProperty(winPtr->dispPtr->display, w, propName);
- } else {
- Tcl_DString tmp;
-
- Tcl_DStringInit(&tmp);
- for (p = Tcl_DStringAppend(&tmp, Tcl_GetString(objv[4]),
- (int) strlen(Tcl_GetString(objv[4]))); *p != 0; p++) {
- if (*p == '\n') {
- *p = 0;
- }
- }
-
- XChangeProperty(winPtr->dispPtr->display, w, propName, XA_STRING,
- 8, PropModeReplace, (unsigned char*)Tcl_DStringValue(&tmp),
- p-Tcl_DStringValue(&tmp));
- Tcl_DStringFree(&tmp);
- }
- } else if (index == TESTSEND_SERIAL) {
- Tcl_SetObjResult(interp, Tcl_NewIntObj(localData.sendSerial+1));
- }
- return TCL_OK;
-}
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 4
- * fill-column: 78
- * End:
- */