📄 tkunixsend.c
字号:
/* * tkUnixSend.c -- * * This file provides procedures 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. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkUnixSend.c 1.74 97/11/04 17:12:18 */#include "tkPort.h"#include "tkInt.h"#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;static RegisteredInterp *registry = NULL; /* List of all interpreters * registered by this process. *//* * 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. */ 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;static PendingCommand *pendingCommands = NULL; /* List of all commands currently * being waited for. *//* * 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. *//* * The following variable is the serial number that was used in the * last "send" command. It is exported only for testing purposes. */int tkSendSerial = 0;/* * Maximum size property that can be read at one time by * this module: */#define MAX_PROP_WORDS 100000/* * The following variable can be set while debugging to do things like * skip locking the server. */static int sendDebug = 0;/* * Forward declarations for procedures defined later in this file: */static int AppendErrorProc _ANSI_ARGS_((ClientData clientData, XErrorEvent *errorPtr));static void AppendPropCarefully _ANSI_ARGS_((Display *display, Window window, Atom property, char *value, int length, PendingCommand *pendingPtr));static void DeleteProc _ANSI_ARGS_((ClientData clientData));static void RegAddName _ANSI_ARGS_((NameRegistry *regPtr, char *name, Window commWindow));static void RegClose _ANSI_ARGS_((NameRegistry *regPtr));static void RegDeleteName _ANSI_ARGS_((NameRegistry *regPtr, char *name));static Window RegFindName _ANSI_ARGS_((NameRegistry *regPtr, char *name));static NameRegistry * RegOpen _ANSI_ARGS_((Tcl_Interp *interp, TkDisplay *dispPtr, int lock));static void SendEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));static int SendInit _ANSI_ARGS_((Tcl_Interp *interp, TkDisplay *dispPtr));static Tk_RestrictAction SendRestrictProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));static int ServerSecure _ANSI_ARGS_((TkDisplay *dispPtr));static void UpdateCommWindow _ANSI_ARGS_((TkDisplay *dispPtr));static int ValidateName _ANSI_ARGS_((TkDisplay *dispPtr, char *name, Window commWindow, int oldOK));/* *---------------------------------------------------------------------- * * RegOpen -- * * This procedure 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 * neeeded, unlock the server if needed, and free memory. * *---------------------------------------------------------------------- */static NameRegistry *RegOpen(interp, dispPtr, lock) 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; if (dispPtr->commTkwin == NULL) { SendInit(interp, dispPtr); } regPtr = (NameRegistry *) ckalloc(sizeof(NameRegistry)); regPtr->dispPtr = dispPtr; regPtr->locked = 0; regPtr->modified = 0; regPtr->allocedByX = 1; if (lock && !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, ®Ptr->propLength, &bytesAfter, (unsigned char **) ®Ptr->property); 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 procedure 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 WindowRegFindName(regPtr, name) NameRegistry *regPtr; /* Pointer to a registry opened with a * previous call to RegOpen. */ char *name; /* Name of an application. */{ char *p, *entry; unsigned int id; for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) { entry = p; while ((*p != 0) && (!isspace(UCHAR(*p)))) { p++; } if ((*p != 0) && (strcmp(name, p+1) == 0)) { 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 procedure 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 voidRegDeleteName(regPtr, name) NameRegistry *regPtr; /* Pointer to a registry opened with a * previous call to RegOpen. */ char *name; /* Name of an application. */{ char *p, *entry, *entryName; int count; for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) { entry = p; while ((*p != 0) && (!isspace(UCHAR(*p)))) { p++; } if (*p != 0) { p++; } entryName = p; while (*p != 0) { p++; } p++; if ((strcmp(name, entryName) == 0)) { /* * 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -