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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkMacSend.c --
  3.  *
  4.  * This file provides procedures that implement the "send"
  5.  * command, allowing commands to be passed from interpreter
  6.  * to interpreter.  This current implementation for the Mac
  7.  * has most functionality stubed out.
  8.  *
  9.  * The current plan, which we have not had time to implement, is
  10.  * for the first Wish app to create a gestalt of type 'WIsH'.
  11.  * This gestalt will point to a table, in system memory, of
  12.  * Tk apps.  Each Tk app, when it starts up, will register their
  13.  * name, and process ID, in this table.  This will allow us to 
  14.  * implement "tk appname".
  15.  *
  16.  * Then the send command will look up the process id of the target
  17.  * app in this table, and send an AppleEvent to that process.  The
  18.  * AppleEvent handler is much like the do script handler, except that
  19.  *      you have to specify the name of the tk app as well, since there may
  20.  * be many interps in one wish app, and you need to send it to the
  21.  * right one.
  22.  *
  23.  * Implementing this has been on our list of things to do, but what
  24.  * with the demise of Tcl at Sun, and the lack of resources at 
  25.  * Scriptics it may not get done for awhile.  So this sketch is
  26.  * offered for the brave to attempt if they need the functionality...
  27.  *
  28.  * Copyright (c) 1989-1994 The Regents of the University of California.
  29.  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
  30.  *
  31.  * See the file "license.terms" for information on usage and redistribution
  32.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  33.  *
  34.  * RCS: @(#) $Id: tkMacSend.c,v 1.7 2002/10/09 11:56:54 das Exp $
  35.  */
  36. #include <Gestalt.h>
  37. #include "tkPort.h"
  38. #include "tkInt.h"
  39. EXTERN int Tk_SendObjCmd _ANSI_ARGS_((ClientData clientData,
  40.     Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
  41.      /* 
  42.       * The following structure is used to keep track of the
  43.       * interpreters registered by this process.
  44.       */
  45. typedef struct RegisteredInterp {
  46.     char *name; /* Interpreter's name (malloc-ed). */
  47.     Tcl_Interp *interp; /* Interpreter associated with
  48.  * name. */
  49.     struct RegisteredInterp *nextPtr;
  50.     /* Next in list of names associated
  51.      * with interps in this process.
  52.      * NULL means end of list. */
  53. } RegisteredInterp;
  54. /*
  55.  * A registry of all interpreters for a display is kept in a
  56.  * property "InterpRegistry" on the root window of the display.
  57.  * It is organized as a series of zero or more concatenated strings
  58.  * (in no particular order), each of the form
  59.  *  window space name ''
  60.  * where "window" is the hex id of the comm. window to use to talk
  61.  * to an interpreter named "name".
  62.  *
  63.  * When the registry is being manipulated by an application (e.g. to
  64.  * add or remove an entry), it is loaded into memory using a structure
  65.  * of the following type:
  66.  */
  67. typedef struct NameRegistry {
  68.     TkDisplay *dispPtr; /* Display from which the registry was
  69.  * read. */
  70.     int locked; /* Non-zero means that the display was
  71.  * locked when the property was read in. */
  72.     int modified; /* Non-zero means that the property has
  73.  * been modified, so it needs to be written
  74.  * out when the NameRegistry is closed. */
  75.     unsigned long propLength; /* Length of the property, in bytes. */
  76.     char *property; /* The contents of the property, or NULL
  77.  * if none.  See format description above;
  78.  * this is *not* terminated by the first
  79.  * null character.  Dynamically allocated. */
  80.     int allocedByX; /* Non-zero means must free property with
  81.  * XFree;  zero means use ckfree. */
  82. } NameRegistry;
  83. static initialized = false; /* A flag to denote if we have initialized yet. */
  84. static RegisteredInterp *interpListPtr = NULL;
  85. /* List of all interpreters
  86.  * registered by this process. */
  87.      /*
  88.       * The information below is used for communication between processes
  89.       * during "send" commands.  Each process keeps a private window, never
  90.       * even mapped, with one property, "Comm".  When a command is sent to
  91.       * an interpreter, the command is appended to the comm property of the
  92.       * communication window associated with the interp's process.  Similarly,
  93.       * when a result is returned from a sent command, it is also appended
  94.       * to the comm property.
  95.       *
  96.       * Each command and each result takes the form of ASCII text.  For a
  97.       * command, the text consists of a zero character followed by several
  98.       * null-terminated ASCII strings.  The first string consists of the
  99.       * single letter "c".  Subsequent strings have the form "option value"
  100.       * where the following options are supported:
  101.       *
  102.       * -r commWindow serial
  103.       *
  104.       * This option means that a response should be sent to the window
  105.       * whose X identifier is "commWindow" (in hex), and the response should
  106.       * be identified with the serial number given by "serial" (in decimal).
  107.       * If this option isn't specified then the send is asynchronous and
  108.       * no response is sent.
  109.       *
  110.       * -n name
  111.       * "Name" gives the name of the application for which the command is
  112.       * intended.  This option must be present.
  113.       *
  114.       * -s script
  115.       *
  116.       * "Script" is the script to be executed.  This option must be present.
  117.       *
  118.       * The options may appear in any order.  The -n and -s options must be
  119.       * present, but -r may be omitted for asynchronous RPCs.  For compatibility
  120.       * with future releases that may add new features, there may be additional
  121.       * options present;  as long as they start with a "-" character, they will
  122.       * be ignored.
  123.       *
  124.       * A result also consists of a zero character followed by several null-
  125.       * terminated ASCII strings.  The first string consists of the single
  126.       * letter "r".  Subsequent strings have the form "option value" where
  127.       * the following options are supported:
  128.       *
  129.       * -s serial
  130.       *
  131.       * Identifies the command for which this is the result.  It is the
  132.       * same as the "serial" field from the -s option in the command.  This
  133.       * option must be present.
  134.       *
  135.       * -c code
  136.       *
  137.       * "Code" is the completion code for the script, in decimal.  If the
  138.       * code is omitted it defaults to TCL_OK.
  139.       *
  140.       * -r result
  141.       *
  142.       * "Result" is the result string for the script, which may be either
  143.       * a result or an error message.  If this field is omitted then it
  144.       * defaults to an empty string.
  145.       *
  146.       * -i errorInfo
  147.       *
  148.       * "ErrorInfo" gives a string with which to initialize the errorInfo
  149.       * variable.  This option may be omitted;  it is ignored unless the
  150.       * completion code is TCL_ERROR.
  151.       *
  152.       * -e errorCode
  153.       *
  154.       * "ErrorCode" gives a string with with to initialize the errorCode
  155.       * variable.  This option may be omitted;  it is ignored  unless the
  156.       * completion code is TCL_ERROR.
  157.       *
  158.       * Options may appear in any order, and only the -s option must be
  159.       * present.  As with commands, there may be additional options besides
  160.       * these;  unknown options are ignored.
  161.       */
  162.      /*
  163.       * The following variable is the serial number that was used in the
  164.       * last "send" command.  It is exported only for testing purposes.
  165.       */
  166. int tkSendSerial = 0;
  167.      /*
  168.       * Maximum size property that can be read at one time by
  169.       * this module:
  170.       */
  171. #define MAX_PROP_WORDS 100000
  172. /*
  173.  * Forward declarations for procedures defined later in this file:
  174.  */
  175. static int AppendErrorProc _ANSI_ARGS_((ClientData clientData,
  176. XErrorEvent *errorPtr));
  177. static void DeleteProc _ANSI_ARGS_((ClientData clientData));
  178. static void RegAddName _ANSI_ARGS_((NameRegistry *regPtr,
  179. char *name, Window commWindow));
  180. static void RegClose _ANSI_ARGS_((NameRegistry *regPtr));
  181. static void RegDeleteName _ANSI_ARGS_((NameRegistry *regPtr,
  182.                char *name));
  183. static Window RegFindName _ANSI_ARGS_((NameRegistry *regPtr,
  184. char *name));
  185. static NameRegistry * RegOpen _ANSI_ARGS_((Tcl_Interp *interp,
  186.      TkWindow *winPtr, int lock));
  187. static void SendEventProc _ANSI_ARGS_((ClientData clientData,
  188.    XEvent *eventPtr));
  189. static int SendInit _ANSI_ARGS_((Tcl_Interp *interp));
  190. static Bool SendRestrictProc _ANSI_ARGS_((Display *display,
  191.       XEvent *eventPtr, char *arg));
  192. static int ServerSecure _ANSI_ARGS_((TkDisplay *dispPtr));
  193. static void TimeoutProc _ANSI_ARGS_((ClientData clientData));
  194. static int ValidateName _ANSI_ARGS_((TkDisplay *dispPtr,
  195.      char *name, Window commWindow, int oldOK));
  196. /*
  197.  *--------------------------------------------------------------
  198.  *
  199.  * Tk_SetAppName --
  200.  *
  201.  * This procedure is called to associate an ASCII name with a Tk
  202.  * application.  If the application has already been named, the
  203.  * name replaces the old one.
  204.  *
  205.  * Results:
  206.  * The return value is the name actually given to the application.
  207.  * This will normally be the same as name, but if name was already
  208.  * in use for an application then a name of the form "name #2" will
  209.  * be chosen,  with a high enough number to make the name unique.
  210.  *
  211.  * Side effects:
  212.  * Registration info is saved, thereby allowing the "send" command
  213.  * to be used later to invoke commands in the application.  In
  214.  * addition, the "send" command is created in the application's
  215.  * interpreter.  The registration will be removed automatically
  216.  * if the interpreter is deleted or the "send" command is removed.
  217.  *
  218.  *--------------------------------------------------------------
  219.  */
  220. CONST char *
  221. Tk_SetAppName(
  222.     Tk_Window tkwin, /* Token for any window in the application
  223.  * to be named:  it is just used to identify
  224.  * the application and the display.  */
  225.     CONST char *name) /* The name that will be used to
  226.  * refer to the interpreter in later
  227.  * "send" commands.  Must be globally
  228.  * unique. */
  229. {
  230.     TkWindow *winPtr = (TkWindow *) tkwin;
  231.     Tcl_Interp *interp = winPtr->mainPtr->interp;
  232.     int i, suffix, offset, result;
  233.     int createCommand = 0;
  234.     RegisteredInterp *riPtr, *prevPtr;
  235.     CONST char *actualName;
  236.     Tcl_DString dString;
  237.     Tcl_Obj *resultObjPtr, *interpNamePtr;
  238.     char *interpName;
  239.     if (!initialized) {
  240. SendInit(interp);
  241.     }
  242.     /*
  243.      * See if the application is already registered; if so, remove its
  244.      * current name from the registry. The deletion of the command
  245.      * will take care of disposing of this entry.
  246.      */
  247.     for (riPtr = interpListPtr, prevPtr = NULL; riPtr != NULL; 
  248.     prevPtr = riPtr, riPtr = riPtr->nextPtr) {
  249. if (riPtr->interp == interp) {
  250.     if (prevPtr == NULL) {
  251. interpListPtr = interpListPtr->nextPtr;
  252.     } else {
  253. prevPtr->nextPtr = riPtr->nextPtr;
  254.     }
  255.     break;
  256. }
  257.     }
  258.     /*
  259.      * Pick a name to use for the application.  Use "name" if it's not
  260.      * already in use.  Otherwise add a suffix such as " #2", trying
  261.      * larger and larger numbers until we eventually find one that is
  262.      * unique.
  263.      */
  264.     actualName = name;
  265.     suffix = 1;
  266.     offset = 0;
  267.     Tcl_DStringInit(&dString);
  268.     TkGetInterpNames(interp, tkwin);
  269.     resultObjPtr = Tcl_GetObjResult(interp);
  270.     Tcl_IncrRefCount(resultObjPtr);
  271.     for (i = 0; ; ) {
  272. result = Tcl_ListObjIndex(NULL, resultObjPtr, i, &interpNamePtr);
  273. if (interpNamePtr == NULL) {
  274.     break;
  275. }
  276. interpName = Tcl_GetStringFromObj(interpNamePtr, NULL);
  277. if (strcmp(actualName, interpName) == 0) {
  278.     if (suffix == 1) {
  279. Tcl_DStringAppend(&dString, name, -1);
  280. Tcl_DStringAppend(&dString, " #", 2);
  281. offset = Tcl_DStringLength(&dString);
  282. Tcl_DStringSetLength(&dString, offset + 10);
  283. actualName = Tcl_DStringValue(&dString);
  284.     }
  285.     suffix++;
  286.     sprintf(Tcl_DStringValue(&dString) + offset, "%d", suffix);
  287.     i = 0;
  288. } else {
  289.     i++;
  290. }
  291.     }
  292.     Tcl_DecrRefCount(resultObjPtr);
  293.     Tcl_ResetResult(interp);
  294.     /*
  295.      * We have found a unique name. Now add it to the registry.
  296.      */
  297.     riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
  298.     riPtr->interp = interp;
  299.     riPtr->name = ckalloc(strlen(actualName) + 1);
  300.     riPtr->nextPtr = interpListPtr;
  301.     interpListPtr = riPtr;
  302.     strcpy(riPtr->name, actualName);
  303.     Tcl_CreateObjCommand(interp, "send", Tk_SendObjCmd, 
  304.     (ClientData) riPtr, NULL /* TODO: DeleteProc */);
  305.     if (Tcl_IsSafe(interp)) {
  306. Tcl_HideCommand(interp, "send", "send");
  307.     }
  308.     Tcl_DStringFree(&dString);
  309.     return riPtr->name;
  310. }
  311. /*
  312.  *--------------------------------------------------------------
  313.  *
  314.  * Tk_SendObjCmd --
  315.  *
  316.  * This procedure is invoked to process the "send" Tcl command.
  317.  * See the user documentation for details on what it does.
  318.  *
  319.  * Results:
  320.  * A standard Tcl result.
  321.  *
  322.  * Side effects:
  323.  * See the user documentation.
  324.  *
  325.  *--------------------------------------------------------------
  326.  */
  327. int
  328. Tk_SendObjCmd(
  329.     ClientData clientData, /* Used only for deletion */
  330.     Tcl_Interp *interp, /* The interp we are sending from */
  331.     int objc, /* Number of arguments */
  332.     Tcl_Obj *CONST objv[]) /* The arguments */
  333. {
  334.     static CONST char *sendOptions[] = {"-async", "-displayof", "-", (char *) NULL};
  335.     char *stringRep, *destName;
  336.     int async = 0;
  337.     int i, index, firstArg;
  338.     RegisteredInterp *riPtr;
  339.     Tcl_Obj *resultPtr, *listObjPtr;
  340.     int result;
  341.     for (i = 1; i < (objc - 1); ) {
  342. stringRep = Tcl_GetStringFromObj(objv[i], NULL);
  343. if (stringRep[0] == '-') {
  344.     if (Tcl_GetIndexFromObj(interp, objv[i], sendOptions, "option", 0,
  345.     &index) != TCL_OK) {
  346. return TCL_ERROR;
  347.     }
  348.     if (index == 0) {
  349. async = 1;
  350. i++;
  351.     } else if (index == 1) {
  352. i += 2;
  353.     } else {
  354. i++;
  355.     }
  356. } else {
  357.     break;
  358. }
  359.     }
  360.     if (objc < (i + 2)) {
  361. Tcl_WrongNumArgs(interp, 1, objv,
  362. "?options? interpName arg ?arg ...?");
  363. return TCL_ERROR;
  364.     }
  365.     destName = Tcl_GetStringFromObj(objv[i], NULL);
  366.     firstArg = i + 1;
  367.     resultPtr = Tcl_GetObjResult(interp);
  368.     /*
  369.      * See if the target interpreter is local.  If so, execute
  370.      * the command directly without going through the DDE server.
  371.      * The only tricky thing is passing the result from the target
  372.      * interpreter to the invoking interpreter.  Watch out:  they
  373.      * could be the same!
  374.      */
  375.     for (riPtr = interpListPtr; (riPtr != NULL) 
  376.     && (strcmp(destName, riPtr->name)); riPtr = riPtr->nextPtr) {
  377. /*
  378.  * Empty loop body.
  379.  */
  380.     
  381.     }
  382.     if (riPtr != NULL) {
  383. /*
  384.  * This command is to a local interp. No need to go through
  385.  * the server.
  386.  */
  387. Tcl_Interp *localInterp;
  388. Tcl_Preserve((ClientData) riPtr);
  389. localInterp = riPtr->interp;
  390. Tcl_Preserve((ClientData) localInterp);
  391. if (firstArg == (objc - 1)) {
  392.     /*
  393.      * This might be one of those cases where the new
  394.      * parser is faster.
  395.      */
  396.     result = Tcl_EvalObjEx(localInterp, objv[firstArg], TCL_EVAL_DIRECT);
  397. } else {
  398.     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  399.     for (i = firstArg; i < objc; i++) {
  400. Tcl_ListObjAppendList(interp, listObjPtr, objv[i]);
  401.     }
  402.     Tcl_IncrRefCount(listObjPtr);
  403.     result = Tcl_EvalObjEx(localInterp, listObjPtr, TCL_EVAL_DIRECT);
  404.     Tcl_DecrRefCount(listObjPtr);
  405. }
  406. if (interp != localInterp) {
  407.     if (result == TCL_ERROR) {
  408. /* Tcl_Obj *errorObjPtr; */
  409. /*
  410.  * An error occurred, so transfer error information from the
  411.  * destination interpreter back to our interpreter.  Must clear
  412.  * interp's result before calling Tcl_AddErrorInfo, since
  413.  * Tcl_AddErrorInfo will store the interp's result in errorInfo
  414.  * before appending riPtr's $errorInfo;  we've already got
  415.  * everything we need in riPtr's $errorInfo.
  416.  */
  417. Tcl_ResetResult(interp);
  418. Tcl_AddErrorInfo(interp, Tcl_GetVar2(localInterp,
  419. "errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
  420. /* errorObjPtr = Tcl_GetObjVar2(localInterp, "errorCode", NULL,
  421. TCL_GLOBAL_ONLY);
  422. Tcl_SetObjErrorCode(interp, errorObjPtr); */
  423.     }
  424.     Tcl_SetObjResult(interp, Tcl_GetObjResult(localInterp));
  425. }
  426. Tcl_Release((ClientData) riPtr);
  427. Tcl_Release((ClientData) localInterp);
  428.     } else {
  429. /*
  430.  * This is a non-local request. Send the script to the server and poll
  431.  * it for a result. TODO!!!
  432.  */
  433.     }
  434. done:
  435.     return result;
  436. }
  437. /*
  438.  *----------------------------------------------------------------------
  439.  *
  440.  * TkGetInterpNames --
  441.  *
  442.  * This procedure is invoked to fetch a list of all the
  443.  * interpreter names currently registered for the display
  444.  * of a particular window.
  445.  *
  446.  * Results:
  447.  * A standard Tcl return value.  Interp->result will be set
  448.  * to hold a list of all the interpreter names defined for
  449.  * tkwin's display.  If an error occurs, then TCL_ERROR
  450.  * is returned and interp->result will hold an error message.
  451.  *
  452.  * Side effects:
  453.  * None.
  454.  *
  455.  *----------------------------------------------------------------------
  456.  */
  457. int
  458. TkGetInterpNames(
  459.     Tcl_Interp *interp, /* Interpreter for returning a result. */
  460.     Tk_Window tkwin) /* Window whose display is to be used
  461.  * for the lookup. */
  462. {
  463.     Tcl_Obj *listObjPtr;
  464.     RegisteredInterp *riPtr;
  465.     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  466.     riPtr = interpListPtr;
  467.     while (riPtr != NULL) {
  468. Tcl_ListObjAppendElement(interp, listObjPtr, 
  469. Tcl_NewStringObj(riPtr->name, -1));
  470. riPtr = riPtr->nextPtr;
  471.     }
  472.     
  473.     Tcl_SetObjResult(interp, listObjPtr);
  474.     return TCL_OK;
  475. }
  476. /*
  477.  *--------------------------------------------------------------
  478.  *
  479.  * SendInit --
  480.  *
  481.  * This procedure is called to initialize the
  482.  * communication channels for sending commands and
  483.  * receiving results.
  484.  *
  485.  * Results:
  486.  * None.
  487.  *
  488.  * Side effects:
  489.  * Sets up various data structures and windows.
  490.  *
  491.  *--------------------------------------------------------------
  492.  */
  493. static int
  494. SendInit(
  495.     Tcl_Interp *interp) /* Interpreter to use for error reporting
  496.  * (no errors are ever returned, but the
  497.  * interpreter is needed anyway). */
  498. {
  499.     return TCL_OK;
  500. }