Annotation of micropolis/src/tk/tksend.c, revision 1.1

1.1     ! root        1: /* 
        !             2:  * tkSend.c --
        !             3:  *
        !             4:  *     This file provides procedures that implement the "send"
        !             5:  *     command, allowing commands to be passed from interpreter
        !             6:  *     to interpreter.
        !             7:  *
        !             8:  * Copyright 1989-1992 Regents of the University of California
        !             9:  * Permission to use, copy, modify, and distribute this
        !            10:  * software and its documentation for any purpose and without
        !            11:  * fee is hereby granted, provided that the above copyright
        !            12:  * notice appear in all copies.  The University of California
        !            13:  * makes no representations about the suitability of this
        !            14:  * software for any purpose.  It is provided "as is" without
        !            15:  * express or implied warranty.
        !            16:  */
        !            17: 
        !            18: #ifndef lint
        !            19: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkSend.c,v 1.26 92/08/13 10:29:26 ouster Exp $ SPRITE (Berkeley)";
        !            20: #endif
        !            21: 
        !            22: #include "tkconfig.h"
        !            23: #include "tkint.h"
        !            24: 
        !            25: /* 
        !            26:  * The following structure is used to keep track of the
        !            27:  * interpreters registered by this process.
        !            28:  */
        !            29: 
        !            30: typedef struct RegisteredInterp {
        !            31:     char *name;                        /* Interpreter's name (malloc-ed). */
        !            32:     Tcl_Interp *interp;                /* Interpreter associated with
        !            33:                                 * name. */
        !            34:     TkDisplay *dispPtr;                /* Display associated with name. */
        !            35:     TkWindow *winPtr;          /* Window associated with name. */
        !            36:     struct RegisteredInterp *nextPtr;
        !            37:                                /* Next in list of names associated
        !            38:                                 * with interps in this process.
        !            39:                                 * NULL means end of list. */
        !            40: } RegisteredInterp;
        !            41: 
        !            42: static RegisteredInterp *registry = NULL;
        !            43:                                /* List of all interpreters
        !            44:                                 * registered by this process. */
        !            45: 
        !            46: /*
        !            47:  * When a result is being awaited from a sent command, one of
        !            48:  * the following structures is present on a list of all outstanding
        !            49:  * sent commands.  The information in the structure is used to
        !            50:  * process the result when it arrives.  You're probably wondering
        !            51:  * how there could ever be multiple outstanding sent commands.
        !            52:  * This could happen if interpreters invoke each other recursively.
        !            53:  * It's unlikely, but possible.
        !            54:  */
        !            55: 
        !            56: typedef struct PendingCommand {
        !            57:     int serial;                        /* Serial number expected in
        !            58:                                 * result. */
        !            59:     char *target;              /* Name of interpreter command is
        !            60:                                 * being sent to. */
        !            61:     Tcl_Interp *interp;                /* Interpreter from which the send
        !            62:                                 * was invoked. */
        !            63:     int code;                  /* Tcl return code for command
        !            64:                                 * will be stored here. */
        !            65:     char *result;              /* String result for command (malloc'ed).
        !            66:                                 * NULL means command still pending. */
        !            67:     struct PendingCommand *nextPtr;
        !            68:                                /* Next in list of all outstanding
        !            69:                                 * commands.  NULL means end of
        !            70:                                 * list. */
        !            71: } PendingCommand;
        !            72: 
        !            73: static PendingCommand *pendingCommands = NULL;
        !            74:                                /* List of all commands currently
        !            75:                                 * being waited for. */
        !            76: 
        !            77: /*
        !            78:  * The information below is used for communication between
        !            79:  * processes during "send" commands.  Each process keeps a
        !            80:  * private window, never even mapped, with one property,
        !            81:  * "Comm".  When a command is sent to an interpreter, the
        !            82:  * command is appended to the comm property of the communication
        !            83:  * window associated with the interp's process.  Similarly, when a
        !            84:  * result is returned from a sent command, it is also appended
        !            85:  * to the comm property.  In each case, the property information
        !            86:  * is in the form of an ASCII string.  The exact syntaxes are:
        !            87:  *
        !            88:  * Command:
        !            89:  *     'C' space window space serial space interpName '|' command '\0'
        !            90:  * The 'C' character indicates that this is a command and not
        !            91:  * a response.  Window is the hex identifier for the comm
        !            92:  * window on which to append the response.  Serial is a hex
        !            93:  * integer containing an identifying number assigned by the
        !            94:  * sender;  it may be used by the sender to sort out concurrent
        !            95:  * responses.  InterpName is the ASCII name of the desired
        !            96:  * interpreter, which must not contain any vertical bar characters
        !            97:  * The interpreter name is delimited by a vertical bar (this
        !            98:  * allows the name to include blanks), and is followed by
        !            99:  * the command to execute.  The command is terminated by a
        !           100:  * NULL character.
        !           101:  *
        !           102:  * Response:
        !           103:  *     'R' space serial space code space result '\0'
        !           104:  * The 'R' character indicates that this is a response.  Serial
        !           105:  * gives the identifier for the command (same value as in the
        !           106:  * command message).  The code field is a decimal integer giving
        !           107:  * the Tcl return code from the command, and result is the string
        !           108:  * result.  The result is terminated by a NULL character.
        !           109:  *
        !           110:  * The register of interpreters is kept in a property
        !           111:  * "InterpRegistry" on the root window of the display.  It is
        !           112:  * organized as a series of zero or more concatenated strings
        !           113:  * (in no particular order), each of the form
        !           114:  *     window space name '\0'
        !           115:  * where "window" is the hex id of the comm. window to use to talk
        !           116:  * to an interpreter named "name".
        !           117:  */
        !           118: 
        !           119: /*
        !           120:  * Maximum size property that can be read at one time by
        !           121:  * this module:
        !           122:  */
        !           123: 
        !           124: #define MAX_PROP_WORDS 100000
        !           125: 
        !           126: /*
        !           127:  * Forward declarations for procedures defined later in this file:
        !           128:  */
        !           129: 
        !           130: static int     AppendErrorProc _ANSI_ARGS_((ClientData clientData,
        !           131:                        XErrorEvent *errorPtr));
        !           132: static void    AppendPropCarefully _ANSI_ARGS_((Display *display,
        !           133:                    Window window, Atom property, char *value,
        !           134:                    PendingCommand *pendingPtr));
        !           135: static void    DeleteProc _ANSI_ARGS_((ClientData clientData));
        !           136: static Window  LookupName _ANSI_ARGS_((TkDisplay *dispPtr, char *name,
        !           137:                    int delete));
        !           138: static void    SendEventProc _ANSI_ARGS_((ClientData clientData,
        !           139:                    XEvent *eventPtr));
        !           140: static int     SendInit _ANSI_ARGS_((Tcl_Interp *interp, TkDisplay *dispPtr));
        !           141: static Bool    SendRestrictProc _ANSI_ARGS_((Display *display,
        !           142:                    XEvent *eventPtr, char *arg));
        !           143: static void    TimeoutProc _ANSI_ARGS_((ClientData clientData));
        !           144: 
        !           145: /*
        !           146:  *--------------------------------------------------------------
        !           147:  *
        !           148:  * Tk_RegisterInterp --
        !           149:  *
        !           150:  *     This procedure is called to associate an ASCII name
        !           151:  *     with an interpreter.  Tk_InitSend must previously
        !           152:  *     have been called to set up communication channels
        !           153:  *     and specify a display.
        !           154:  *
        !           155:  * Results:
        !           156:  *     Zero is returned if the name was registered successfully.
        !           157:  *     Non-zero means the name was already in use.
        !           158:  *
        !           159:  * Side effects:
        !           160:  *     Registration info is saved, thereby allowing the
        !           161:  *     "send" command to be used later to invoke commands
        !           162:  *     in the interpreter.  The registration will be removed
        !           163:  *     automatically when the interpreter is deleted.
        !           164:  *
        !           165:  *--------------------------------------------------------------
        !           166:  */
        !           167: 
        !           168: int
        !           169: Tk_RegisterInterp(interp, name, tkwin)
        !           170:     Tcl_Interp *interp;                /* Interpreter associated with name. */
        !           171:     char *name;                        /* The name that will be used to
        !           172:                                 * refer to the interpreter in later
        !           173:                                 * "send" commands.  Must be globally
        !           174:                                 * unique. */
        !           175:     Tk_Window tkwin;           /* Token for window associated with
        !           176:                                 * interp;  used to identify display
        !           177:                                 * for communication.  */
        !           178: {
        !           179: #define TCL_MAX_NAME_LENGTH 1000
        !           180:     char propInfo[TCL_MAX_NAME_LENGTH + 20];
        !           181:     register RegisteredInterp *riPtr;
        !           182:     Window w;
        !           183:     TkWindow *winPtr = (TkWindow *) tkwin;
        !           184:     TkDisplay *dispPtr;
        !           185: 
        !           186:     if (strchr(name, '|') != NULL) {
        !           187:        interp->result =
        !           188:                "interpreter name cannot contain '|' character";
        !           189:        return TCL_ERROR;
        !           190:     }
        !           191: 
        !           192:     dispPtr = winPtr->dispPtr;
        !           193:     if (dispPtr->commWindow == NULL) {
        !           194:        int result;
        !           195: 
        !           196:        result = SendInit(interp, dispPtr);
        !           197:        if (result != TCL_OK) {
        !           198:            return result;
        !           199:        }
        !           200:     }
        !           201: 
        !           202:     /*
        !           203:      * Make sure the name is unique, and append info about it to
        !           204:      * the registry property.  Eventually, it would probably be
        !           205:      * a good idea to lock the server here to prevent conflicting
        !           206:      * changes to the registry property.  But that would make
        !           207:      * testing more difficult, and probably isn't necessary
        !           208:      * anyway because new windows don't get created all that often.
        !           209:      */
        !           210: 
        !           211:     w = LookupName(dispPtr, name, 0);
        !           212:     if (w != (Window) 0) {
        !           213:        Tcl_Interp *tmpInterp;
        !           214:        RegisteredInterp tmpRi;
        !           215:        int result;
        !           216:        char *argv[3];
        !           217: 
        !           218:        /*
        !           219:         * Name already exists.  Ping the interpreter with a
        !           220:         * NULL command to see if it already exists.  If not,
        !           221:         * unregister the old name (this could happen if an
        !           222:         * application dies without cleaning up the registry).
        !           223:         */
        !           224: 
        !           225:        tmpInterp = Tcl_CreateInterp();
        !           226:        argv[0] = "send";
        !           227:        argv[1] = name;
        !           228:        argv[2] = "";
        !           229:        tmpRi.dispPtr = dispPtr;
        !           230:        tmpRi.winPtr = winPtr;
        !           231:        result = Tk_SendCmd((ClientData) &tmpRi, tmpInterp, 3, argv);
        !           232:        Tcl_DeleteInterp(tmpInterp);
        !           233:        if (result == TCL_OK) {
        !           234:            Tcl_AppendResult(interp, "interpreter name \"", name,
        !           235:                    "\" is already in use", (char *) NULL);
        !           236:            return TCL_ERROR;
        !           237:        }
        !           238:        (void) LookupName(winPtr->dispPtr, name, 1);
        !           239:     }
        !           240:     sprintf(propInfo, "%x %.*s", Tk_WindowId(dispPtr->commWindow),
        !           241:            TCL_MAX_NAME_LENGTH, name);
        !           242:     XChangeProperty(dispPtr->display,
        !           243:            Tk_DefaultRootWindow(dispPtr->display),
        !           244:            dispPtr->registryProperty, XA_STRING, 8, PropModeAppend,
        !           245:            (unsigned char *) propInfo, strlen(propInfo)+1);
        !           246: 
        !           247:     /*
        !           248:      * Add an entry in the local registry of names owned by this
        !           249:      * process.
        !           250:      */
        !           251: 
        !           252:     riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
        !           253:     riPtr->name = (char *) ckalloc((unsigned) (strlen(name) + 1));
        !           254:     strcpy(riPtr->name, name);
        !           255:     riPtr->interp = interp;
        !           256:     riPtr->dispPtr = dispPtr;
        !           257:     riPtr->winPtr = winPtr;
        !           258:     riPtr->nextPtr = registry;
        !           259:     registry = riPtr;
        !           260: 
        !           261:     /*
        !           262:      * Add the "send" command to this interpreter, and arrange for
        !           263:      * us to be notified when the interpreter is deleted (actually,
        !           264:      * when the "send" command is deleted).
        !           265:      */
        !           266: 
        !           267:     Tcl_CreateCommand(interp, "send", Tk_SendCmd, (ClientData) riPtr,
        !           268:            DeleteProc);
        !           269: 
        !           270:     return TCL_OK;
        !           271: }
        !           272: 
        !           273: /*
        !           274:  *--------------------------------------------------------------
        !           275:  *
        !           276:  * Tk_SendCmd --
        !           277:  *
        !           278:  *     This procedure is invoked to process the "send" Tcl command.
        !           279:  *     See the user documentation for details on what it does.
        !           280:  *
        !           281:  * Results:
        !           282:  *     A standard Tcl result.
        !           283:  *
        !           284:  * Side effects:
        !           285:  *     See the user documentation.
        !           286:  *
        !           287:  *--------------------------------------------------------------
        !           288:  */
        !           289: 
        !           290: int
        !           291: Tk_SendCmd(clientData, interp, argc, argv)
        !           292:     ClientData clientData;             /* Information about sender (only
        !           293:                                         * dispPtr field is used). */
        !           294:     Tcl_Interp *interp;                        /* Current interpreter. */
        !           295:     int argc;                          /* Number of arguments. */
        !           296:     char **argv;                       /* Argument strings. */
        !           297: {
        !           298:     RegisteredInterp *senderRiPtr = (RegisteredInterp *) clientData;
        !           299:     Window w;
        !           300: #define STATIC_PROP_SPACE 100
        !           301:     char *property, staticSpace[STATIC_PROP_SPACE];
        !           302:     int length;
        !           303:     int quick = 0;
        !           304:     static int serial = 0;     /* Running count of sent commands.
        !           305:                                 * Used to give each command a
        !           306:                                 * different serial number. */
        !           307:     PendingCommand pending;
        !           308:     Tk_TimerToken timeout;
        !           309:     register RegisteredInterp *riPtr;
        !           310:     char *cmd;
        !           311:     int result;
        !           312:     Bool (*prevRestrictProc)();
        !           313:     char *prevArg;
        !           314:     TkWindow *winPtr = senderRiPtr->winPtr;
        !           315:     TkDisplay *dispPtr = senderRiPtr->dispPtr;
        !           316:     int to_server = 0;
        !           317: 
        !           318:     if (argc >= 2) {
        !           319:       Tk_Window tkwin = NULL;
        !           320: 
        !           321:       if ((argv[1][0] == '-') &&
        !           322:          (strncmp(argv[1], "-quick", strlen(argv[1])) == 0)) {
        !           323:        quick = 1;
        !           324:        argv += 1; argc -= 1;
        !           325:       }
        !           326:     }
        !           327: 
        !           328:     if (argc >= 3) {
        !           329:       Tk_Window tkwin = NULL;
        !           330: 
        !           331:       if ((argv[1][0] == '-') &&
        !           332:          (strncmp(argv[1], "-server", strlen(argv[1])) == 0)) {
        !           333:        to_server = 1;
        !           334:        tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) winPtr);
        !           335:        if (tkwin == NULL) {
        !           336:          Tcl_AppendResult(interp, "bad server arg, should be window name: ",
        !           337:                           argv[2], (char *) NULL);
        !           338:          return TCL_ERROR;
        !           339:        }
        !           340:        winPtr = (TkWindow *) tkwin;
        !           341:        dispPtr = winPtr->dispPtr;
        !           342:        argv += 2; argc -= 2;
        !           343:       }
        !           344:     }
        !           345: 
        !           346:     if (dispPtr->commWindow == NULL) {
        !           347:        result = SendInit(interp, dispPtr);
        !           348:        if (result != TCL_OK) {
        !           349:            return result;
        !           350:        }
        !           351:     }
        !           352: 
        !           353:     if (argc < 3) {
        !           354:     badargs:
        !           355:        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
        !           356:                " interpName arg ?arg ...?\"", (char *) NULL);
        !           357:        return TCL_ERROR;
        !           358:     }
        !           359: 
        !           360:     if (argc == 3) {
        !           361:        cmd = argv[2];
        !           362:     } else {
        !           363:        cmd = Tcl_Concat(argc-2, argv+2);
        !           364:     }
        !           365: 
        !           366:     /*
        !           367:      * See if the target interpreter is local.  If so, execute
        !           368:      * the command directly without going through the X server.
        !           369:      * The only tricky thing is passing the result from the target
        !           370:      * interpreter to the invoking interpreter.  Watch out:  they
        !           371:      * could be the same!
        !           372:      */
        !           373: 
        !           374:     for (riPtr = registry; riPtr != NULL; riPtr = riPtr->nextPtr) {
        !           375:        if (strcmp(riPtr->name, argv[1]) != 0) {
        !           376:            continue;
        !           377:        }
        !           378:        if (interp == riPtr->interp) {
        !           379:            result = Tcl_GlobalEval(interp, cmd);
        !           380:        } else {
        !           381:            result = Tcl_GlobalEval(riPtr->interp, cmd);
        !           382:            interp->result = riPtr->interp->result;
        !           383:            interp->freeProc = riPtr->interp->freeProc;
        !           384:            riPtr->interp->freeProc = 0;
        !           385:            Tcl_ResetResult(riPtr->interp);
        !           386:        }
        !           387:        if (cmd != argv[2]) {
        !           388:            ckfree(cmd);
        !           389:        }
        !           390:        return result;
        !           391:     }
        !           392: 
        !           393:     /*
        !           394:      * Bind the interpreter name to a communication window.
        !           395:      */
        !           396: 
        !           397:     w = LookupName(dispPtr, argv[1], 0);
        !           398:     if (w == 0) {
        !           399:        Tcl_AppendResult(interp, "no registered interpeter named \"",
        !           400:                argv[1], "\"", (char *) NULL);
        !           401:        if (cmd != argv[2]) {
        !           402:            ckfree(cmd);
        !           403:        }
        !           404:        return TCL_ERROR;
        !           405:     }
        !           406: 
        !           407:     if (!quick) {
        !           408:        /*
        !           409:         * Register the fact that we're waiting for a command to
        !           410:         * complete (this is needed by SendEventProc and by
        !           411:         * AppendErrorProc to pass back the command's results).
        !           412:         */
        !           413: 
        !           414:        serial++;
        !           415:        pending.serial = serial;
        !           416:        pending.target = argv[1];
        !           417:        pending.interp = interp;
        !           418:        pending.result = NULL;
        !           419:        pending.nextPtr = pendingCommands;
        !           420:        pendingCommands = &pending;
        !           421:     }
        !           422: 
        !           423:     /*
        !           424:      * Send the command to target interpreter by appending it to the
        !           425:      * comm window in the communication window.
        !           426:      */
        !           427: 
        !           428:     length = strlen(argv[1]) + strlen(cmd) + 30;
        !           429:     if (length <= STATIC_PROP_SPACE) {
        !           430:        property = staticSpace;
        !           431:     } else {
        !           432:        property = (char *) ckalloc((unsigned) length);
        !           433:     }
        !           434:     sprintf(property, "C %x %x %s|%s",
        !           435:            Tk_WindowId(dispPtr->commWindow), serial, argv[1], cmd);
        !           436:     (void) AppendPropCarefully(dispPtr->display, w, dispPtr->commProperty,
        !           437:            property, &pending);
        !           438:     if (length > STATIC_PROP_SPACE) {
        !           439:        ckfree(property);
        !           440:     }
        !           441:     if (cmd != argv[2]) {
        !           442:        ckfree(cmd);
        !           443:     }
        !           444: 
        !           445:     if (quick) {
        !           446:       sprintf(interp->result, "NoReturnValue");
        !           447:       return TCL_OK;
        !           448:     }
        !           449: 
        !           450:     /*
        !           451:      * Enter a loop processing X events until the result comes
        !           452:      * in.  If no response is received within a few seconds,
        !           453:      * then timeout.  While waiting for a result, look only at
        !           454:      * send-related events (otherwise it would be possible for
        !           455:      * additional input events, such as mouse motion, to cause
        !           456:      * other sends, leading eventually to such a large number
        !           457:      * of nested Tcl_Eval calls that the Tcl interpreter panics).
        !           458:      */
        !           459: 
        !           460:     prevRestrictProc = Tk_RestrictEvents(SendRestrictProc,
        !           461:            (char *) dispPtr->commWindow, &prevArg);
        !           462:     timeout = Tk_CreateTimerHandler(5000, TimeoutProc,
        !           463:            (ClientData) &pending);
        !           464:     while (pending.result == NULL) {
        !           465:        Tk_DoOneEvent(0);
        !           466:     }
        !           467:     Tk_DeleteTimerHandler(timeout);
        !           468:     (void) Tk_RestrictEvents(prevRestrictProc, prevArg, &prevArg);
        !           469: 
        !           470:     /*
        !           471:      * Unregister the information about the pending command
        !           472:      * and return the result.
        !           473:      */
        !           474: 
        !           475:     if (pendingCommands == &pending) {
        !           476:        pendingCommands = pending.nextPtr;
        !           477:     } else {
        !           478:        PendingCommand *pcPtr;
        !           479: 
        !           480:        for (pcPtr = pendingCommands; pcPtr != NULL;
        !           481:                pcPtr = pcPtr->nextPtr) {
        !           482:            if (pcPtr->nextPtr == &pending) {
        !           483:                pcPtr->nextPtr = pending.nextPtr;
        !           484:                break;
        !           485:            }
        !           486:        }
        !           487:     }
        !           488:     Tcl_SetResult(interp, pending.result, TCL_DYNAMIC);
        !           489:     return pending.code;
        !           490:     
        !           491: }
        !           492: 
        !           493: /*
        !           494:  *----------------------------------------------------------------------
        !           495:  *
        !           496:  * TkGetInterpNames --
        !           497:  *
        !           498:  *     This procedure is invoked to fetch a list of all the
        !           499:  *     interpreter names currently registered for the display
        !           500:  *     of a particular window.
        !           501:  *
        !           502:  * Results:
        !           503:  *     A standard Tcl return value.  Interp->result will be set
        !           504:  *     to hold a list of all the interpreter names defined for
        !           505:  *     tkwin's display.  If an error occurs, then TCL_ERROR
        !           506:  *     is returned and interp->result will hold an error message.
        !           507:  *
        !           508:  * Side effects:
        !           509:  *     None.
        !           510:  *
        !           511:  *----------------------------------------------------------------------
        !           512:  */
        !           513: 
        !           514: int
        !           515: TkGetInterpNames(interp, tkwin)
        !           516:     Tcl_Interp *interp;                /* Interpreter for returning a result. */
        !           517:     Tk_Window tkwin;           /* Window whose display is to be used
        !           518:                                 * for the lookup. */
        !           519: {
        !           520:     TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
        !           521:     char *regProp, *separator, *name;
        !           522:     register char *p;
        !           523:     int result, actualFormat;
        !           524:     unsigned long numItems, bytesAfter;
        !           525:     Atom actualType;
        !           526: 
        !           527:     /*
        !           528:      * Read the registry property.
        !           529:      */
        !           530: 
        !           531:     regProp = NULL;
        !           532:     result = XGetWindowProperty(dispPtr->display,
        !           533:            Tk_DefaultRootWindow(dispPtr->display),
        !           534:            dispPtr->registryProperty, 0, MAX_PROP_WORDS,
        !           535:            False, XA_STRING, &actualType, &actualFormat,
        !           536:            &numItems, &bytesAfter, (unsigned char **) &regProp);
        !           537: 
        !           538:     if (actualType == None) {
        !           539:        sprintf(interp->result, "couldn't read intepreter registry property");
        !           540:        return TCL_ERROR;
        !           541:     }
        !           542: 
        !           543:     /*
        !           544:      * If the property is improperly formed, then delete it.
        !           545:      */
        !           546: 
        !           547:     if ((result != Success) || (actualFormat != 8)
        !           548:            || (actualType != XA_STRING)) {
        !           549:        if (regProp != NULL) {
        !           550:            XFree(regProp);
        !           551:        }
        !           552:        sprintf(interp->result, "intepreter registry property is badly formed");
        !           553:        return TCL_ERROR;
        !           554:     }
        !           555: 
        !           556:     /*
        !           557:      * Scan all of the names out of the property.
        !           558:      */
        !           559: 
        !           560:     separator = "";
        !           561:     for (p = regProp; (p-regProp) < numItems; p++) {
        !           562:        name = p;
        !           563:        while ((*p != 0) && (!isspace(*p))) {
        !           564:            p++;
        !           565:        }
        !           566:        if (*p != 0) {
        !           567:            name = p+1;
        !           568:            name = Tcl_Merge(1, &name);
        !           569:            Tcl_AppendResult(interp, separator, name, (char *) NULL);
        !           570:            while (*p != 0) {
        !           571:                p++;
        !           572:            }
        !           573:            separator = " ";
        !           574:        }
        !           575:     }
        !           576:     XFree(regProp);
        !           577:     return TCL_OK;
        !           578: }
        !           579: 
        !           580: /*
        !           581:  *--------------------------------------------------------------
        !           582:  *
        !           583:  * SendInit --
        !           584:  *
        !           585:  *     This procedure is called to initialize the
        !           586:  *     communication channels for sending commands and
        !           587:  *     receiving results.
        !           588:  *
        !           589:  * Results:
        !           590:  *     The result is a standard Tcl return value, which is
        !           591:  *     normally TCL_OK.  If an error occurs then an error
        !           592:  *     message is left in interp->result and TCL_ERROR is
        !           593:  *     returned.
        !           594:  *
        !           595:  * Side effects:
        !           596:  *     Sets up various data structures and windows.
        !           597:  *
        !           598:  *--------------------------------------------------------------
        !           599:  */
        !           600: 
        !           601: static int
        !           602: SendInit(interp, dispPtr)
        !           603:     Tcl_Interp *interp;                /* Interpreter to use for error
        !           604:                                 * reporting. */
        !           605:     register TkDisplay *dispPtr;/* Display to initialize. */
        !           606: 
        !           607: {
        !           608:     XSetWindowAttributes atts;
        !           609: 
        !           610:     /*
        !           611:      * Create the window used for communication, and set up an
        !           612:      * event handler for it.
        !           613:      */
        !           614: 
        !           615:     dispPtr->commWindow = Tk_CreateWindow(interp, (Tk_Window) NULL,
        !           616:            "_comm", DisplayString(dispPtr->display));
        !           617:     if (dispPtr->commWindow == NULL) {
        !           618:        return TCL_ERROR;
        !           619:     }
        !           620:     atts.override_redirect = True;
        !           621:     Tk_ChangeWindowAttributes(dispPtr->commWindow,
        !           622:            CWOverrideRedirect, &atts);
        !           623:     Tk_CreateEventHandler(dispPtr->commWindow, PropertyChangeMask,
        !           624:            SendEventProc, (ClientData) dispPtr);
        !           625:     Tk_MakeWindowExist(dispPtr->commWindow);
        !           626: 
        !           627:     /*
        !           628:      * Get atoms used as property names.
        !           629:      */
        !           630: 
        !           631:     dispPtr->commProperty = XInternAtom(dispPtr->display,
        !           632:            "Comm", False);
        !           633:     dispPtr->registryProperty = XInternAtom(dispPtr->display,
        !           634:            "InterpRegistry", False);
        !           635:     return TCL_OK;
        !           636: }
        !           637: 
        !           638: /*
        !           639:  *--------------------------------------------------------------
        !           640:  *
        !           641:  * LookupName --
        !           642:  *
        !           643:  *     Given an interpreter name, see if the name exists in
        !           644:  *     the interpreter registry for a particular display.
        !           645:  *
        !           646:  * Results:
        !           647:  *     If the given name is registered, return the ID of
        !           648:  *     the window associated with the name.  If the name
        !           649:  *     isn't registered, then return 0.
        !           650:  *
        !           651:  * Side effects:
        !           652:  *     If the registry property is improperly formed, then
        !           653:  *     it is deleted.  If "delete" is non-zero, then if the
        !           654:  *     named interpreter is found it is removed from the
        !           655:  *     registry property.
        !           656:  *
        !           657:  *--------------------------------------------------------------
        !           658:  */
        !           659: 
        !           660: static Window
        !           661: LookupName(dispPtr, name, delete)
        !           662:     register TkDisplay *dispPtr;
        !           663:                        /* Display whose registry to check. */
        !           664:     char *name;                /* Name of an interpreter. */
        !           665:     int delete;                /* If non-zero, delete info about name. */
        !           666: {
        !           667:     char *regProp, *entry;
        !           668:     register char *p;
        !           669:     int result, actualFormat;
        !           670:     unsigned long numItems, bytesAfter;
        !           671:     Atom actualType;
        !           672:     Window returnValue;
        !           673: 
        !           674:     /*
        !           675:      * Read the registry property.
        !           676:      */
        !           677: 
        !           678:     regProp = NULL;
        !           679:     result = XGetWindowProperty(dispPtr->display,
        !           680:            Tk_DefaultRootWindow(dispPtr->display),
        !           681:            dispPtr->registryProperty, 0, MAX_PROP_WORDS,
        !           682:            False, XA_STRING, &actualType, &actualFormat,
        !           683:            &numItems, &bytesAfter, (unsigned char **) &regProp);
        !           684: 
        !           685:     if (actualType == None) {
        !           686:        return 0;
        !           687:     }
        !           688: 
        !           689:     /*
        !           690:      * If the property is improperly formed, then delete it.
        !           691:      */
        !           692: 
        !           693:     if ((result != Success) || (actualFormat != 8)
        !           694:            || (actualType != XA_STRING)) {
        !           695:        if (regProp != NULL) {
        !           696:            XFree(regProp);
        !           697:        }
        !           698:        XDeleteProperty(dispPtr->display,
        !           699:                Tk_DefaultRootWindow(dispPtr->display),
        !           700:                dispPtr->registryProperty);
        !           701:        return 0;
        !           702:     }
        !           703: 
        !           704:     /*
        !           705:      * Scan the property for the desired name.
        !           706:      */
        !           707: 
        !           708:     returnValue = (Window) 0;
        !           709:     entry = NULL;      /* Not needed, but eliminates compiler warning. */
        !           710:     for (p = regProp; (p-regProp) < numItems; ) {
        !           711:        entry = p;
        !           712:        while ((*p != 0) && (!isspace(*p))) {
        !           713:            p++;
        !           714:        }
        !           715:        if ((*p != 0) && (strcmp(name, p+1) == 0)) {
        !           716:            sscanf(entry, "%x", &returnValue);
        !           717:            break;
        !           718:        }
        !           719:        while (*p != 0) {
        !           720:            p++;
        !           721:        }
        !           722:        p++;
        !           723:     }
        !           724: 
        !           725:     /*
        !           726:      * Delete the property, if that is desired (copy down the
        !           727:      * remainder of the registry property to overlay the deleted
        !           728:      * info, then rewrite the property).
        !           729:      */
        !           730: 
        !           731:     if ((delete) && (returnValue != 0)) {
        !           732:        int count;
        !           733: 
        !           734:        while (*p != 0) {
        !           735:            p++;
        !           736:        }
        !           737:        p++;
        !           738:        count = numItems - (p-regProp);
        !           739:        if (count > 0) {
        !           740:            memcpy((VOID *) entry, (VOID *) p, count);
        !           741:        }
        !           742:        XChangeProperty(dispPtr->display,
        !           743:                Tk_DefaultRootWindow(dispPtr->display),
        !           744:                dispPtr->registryProperty, XA_STRING, 8,
        !           745:                PropModeReplace, (unsigned char *) regProp,
        !           746:                (int) (numItems - (p-entry)));
        !           747:        XSync(dispPtr->display, False);
        !           748:     }
        !           749: 
        !           750:     XFree(regProp);
        !           751:     return returnValue;
        !           752: }
        !           753: 
        !           754: /*
        !           755:  *--------------------------------------------------------------
        !           756:  *
        !           757:  * SendEventProc --
        !           758:  *
        !           759:  *     This procedure is invoked automatically by the toolkit
        !           760:  *     event manager when a property changes on the communication
        !           761:  *     window.  This procedure reads the property and handles
        !           762:  *     command requests and responses.
        !           763:  *
        !           764:  * Results:
        !           765:  *     None.
        !           766:  *
        !           767:  * Side effects:
        !           768:  *     If there are command requests in the property, they
        !           769:  *     are executed.  If there are responses in the property,
        !           770:  *     their information is saved for the (ostensibly waiting)
        !           771:  *     "send" commands. The property is deleted.
        !           772:  *
        !           773:  *--------------------------------------------------------------
        !           774:  */
        !           775: 
        !           776: static void
        !           777: SendEventProc(clientData, eventPtr)
        !           778:     ClientData clientData;     /* Display information. */      
        !           779:     XEvent *eventPtr;          /* Information about event. */
        !           780: {
        !           781:     TkDisplay *dispPtr = (TkDisplay *) clientData;
        !           782:     char *propInfo;
        !           783:     register char *p;
        !           784:     int result, actualFormat;
        !           785:     unsigned long numItems, bytesAfter;
        !           786:     Atom actualType;
        !           787: 
        !           788:     if ((eventPtr->xproperty.atom != dispPtr->commProperty)
        !           789:            || (eventPtr->xproperty.state != PropertyNewValue)) {
        !           790:        return;
        !           791:     }
        !           792: 
        !           793:     /*
        !           794:      * Read the comm property and delete it.
        !           795:      */
        !           796: 
        !           797:     propInfo = NULL;
        !           798:     result = XGetWindowProperty(dispPtr->display,
        !           799:            Tk_WindowId(dispPtr->commWindow),
        !           800:            dispPtr->commProperty, 0, MAX_PROP_WORDS, True,
        !           801:            XA_STRING, &actualType, &actualFormat,
        !           802:            &numItems, &bytesAfter, (unsigned char **) &propInfo);
        !           803: 
        !           804:     /*
        !           805:      * If the property doesn't exist or is improperly formed
        !           806:      * then ignore it.
        !           807:      */
        !           808: 
        !           809:     if ((result != Success) || (actualType != XA_STRING)
        !           810:            || (actualFormat != 8)) {
        !           811:        if (propInfo != NULL) {
        !           812:            XFree(propInfo);
        !           813:        }
        !           814:        return;
        !           815:     }
        !           816: 
        !           817:     /*
        !           818:      * The property is divided into records separated by null
        !           819:      * characters.  Each record represents one command request
        !           820:      * or response.  Scan through the property one record at a
        !           821:      * time.
        !           822:      */
        !           823: 
        !           824:     for (p = propInfo; (p-propInfo) < numItems; ) {
        !           825:        if (*p == 'C') {
        !           826:            Window window;
        !           827:            int serial, resultSize;
        !           828:            char *resultString, *interpName, *returnProp, *end;
        !           829:            register RegisteredInterp *riPtr;
        !           830:            char errorMsg[100];
        !           831: #define STATIC_RESULT_SPACE 100
        !           832:            char staticSpace[STATIC_RESULT_SPACE];
        !           833: 
        !           834:            /*
        !           835:             *-----------------------------------------------------
        !           836:             * This is an incoming command sent by another window.
        !           837:             * Parse the fields of the command string.  If the command
        !           838:             * string isn't properly formed, send back an error message
        !           839:             * if there's enough well-formed information to generate
        !           840:             * a proper reply;  otherwise just ignore the message.
        !           841:             *-----------------------------------------------------
        !           842:             */
        !           843: 
        !           844:            p++;
        !           845:            window = (Window) strtol(p, &end, 16);
        !           846:            if (end == p) {
        !           847:                goto nextRecord;
        !           848:            }
        !           849:            p = end;
        !           850:            if (*p != ' ') {
        !           851:                goto nextRecord;
        !           852:            }
        !           853:            p++;
        !           854:            serial = strtol(p, &end, 16);
        !           855:            if (end == p) {
        !           856:                goto nextRecord;
        !           857:            }
        !           858:            p = end;
        !           859:            if (*p != ' ') {
        !           860:                goto nextRecord;
        !           861:            }
        !           862:            p++;
        !           863:            interpName = p;
        !           864:            while ((*p != 0) && (*p != '|')) {
        !           865:                p++;
        !           866:            }
        !           867:            if (*p != '|') {
        !           868:                result = TCL_ERROR;
        !           869:                resultString = "bad property format for sent command";
        !           870:                goto returnResult;
        !           871:            }
        !           872:            *p = 0;
        !           873:            p++;
        !           874: 
        !           875:            /*
        !           876:             * Locate the interpreter for the command, then
        !           877:             * execute the command.
        !           878:             */
        !           879: 
        !           880:            for (riPtr = registry; ; riPtr = riPtr->nextPtr) {
        !           881:                if (riPtr == NULL) {
        !           882:                    result = TCL_ERROR;
        !           883:                    sprintf(errorMsg,
        !           884:                            "receiver never heard of interpreter \"%.40s\"",
        !           885:                            interpName);
        !           886:                    resultString = errorMsg;
        !           887:                    goto returnResult;
        !           888:                }
        !           889:                if (strcmp(riPtr->name, interpName) == 0) {
        !           890:                    break;
        !           891:                }
        !           892:            }
        !           893:            result = Tcl_GlobalEval(riPtr->interp, p);
        !           894:            resultString = riPtr->interp->result;
        !           895: 
        !           896:            /*
        !           897:             * Return the result to the sender.
        !           898:             */
        !           899: 
        !           900:            returnResult:
        !           901:            resultSize = strlen(resultString) + 30;
        !           902:            if (resultSize <= STATIC_RESULT_SPACE) {
        !           903:                returnProp = staticSpace;
        !           904:            } else {
        !           905:                returnProp = (char *) ckalloc((unsigned) resultSize);
        !           906:            }
        !           907:            sprintf(returnProp, "R %x %d %s", serial, result,
        !           908:                    resultString);
        !           909:            (void) AppendPropCarefully(dispPtr->display, window,
        !           910:                    dispPtr->commProperty, returnProp,
        !           911:                    (PendingCommand *) NULL);
        !           912:            if (returnProp != staticSpace) {
        !           913:                ckfree(returnProp);
        !           914:            }
        !           915:        } else if (*p == 'R') {
        !           916:            int serial, code;
        !           917:            char *end;
        !           918:            register PendingCommand *pcPtr;
        !           919: 
        !           920:            /*
        !           921:             *-----------------------------------------------------
        !           922:             * This record in the property is a result being
        !           923:             * returned for a command sent from here.  First
        !           924:             * parse the fields.
        !           925:             *-----------------------------------------------------
        !           926:             */
        !           927: 
        !           928:            p++;
        !           929:            serial = strtol(p, &end, 16);
        !           930:            if (end == p) {
        !           931:                goto nextRecord;
        !           932:            }
        !           933:            p = end;
        !           934:            if (*p != ' ') {
        !           935:                goto nextRecord;
        !           936:            }
        !           937:            p++;
        !           938:            code = strtol(p, &end, 10);
        !           939:            if (end == p) {
        !           940:                goto nextRecord;
        !           941:            }
        !           942:            p = end;
        !           943:            if (*p != ' ') {
        !           944:                goto nextRecord;
        !           945:            }
        !           946:            p++;
        !           947: 
        !           948:            /*
        !           949:             * Give the result information to anyone who's
        !           950:             * waiting for it.
        !           951:             */
        !           952: 
        !           953:            for (pcPtr = pendingCommands; pcPtr != NULL;
        !           954:                    pcPtr = pcPtr->nextPtr) {
        !           955:                if ((serial != pcPtr->serial) || (pcPtr->result != NULL)) {
        !           956:                    continue;
        !           957:                }
        !           958:                pcPtr->code = code;
        !           959:                pcPtr->result = ckalloc((unsigned) (strlen(p) + 1));
        !           960:                strcpy(pcPtr->result, p);
        !           961:                break;
        !           962:            }
        !           963:        }
        !           964: 
        !           965:        nextRecord:
        !           966:        while (*p != 0) {
        !           967:            p++;
        !           968:        }
        !           969:        p++;
        !           970:     }
        !           971:     XFree(propInfo);
        !           972: }
        !           973: 
        !           974: /*
        !           975:  *--------------------------------------------------------------
        !           976:  *
        !           977:  * AppendPropCarefully --
        !           978:  *
        !           979:  *     Append a given property to a given window, but set up
        !           980:  *     an X error handler so that if the append fails this
        !           981:  *     procedure can return an error code rather than having
        !           982:  *     Xlib panic.
        !           983:  *
        !           984:  * Results:
        !           985:  *     None.
        !           986:  *
        !           987:  * Side effects:
        !           988:  *     The given property on the given window is appended to.
        !           989:  *     If this operation fails and if pendingPtr is non-NULL,
        !           990:  *     then the pending operation is marked as complete with
        !           991:  *     an error.
        !           992:  *
        !           993:  *--------------------------------------------------------------
        !           994:  */
        !           995: 
        !           996: static void
        !           997: AppendPropCarefully(display, window, property, value, pendingPtr)
        !           998:     Display *display;          /* Display on which to operate. */
        !           999:     Window window;             /* Window whose property is to
        !          1000:                                 * be modified. */
        !          1001:     Atom property;             /* Name of property. */
        !          1002:     char *value;               /* Characters (null-terminated) to
        !          1003:                                 * append to property. */
        !          1004:     PendingCommand *pendingPtr;        /* Pending command to mark complete
        !          1005:                                 * if an error occurs during the
        !          1006:                                 * property op.  NULL means just
        !          1007:                                 * ignore the error. */
        !          1008: {
        !          1009:     Tk_ErrorHandler handler;
        !          1010: 
        !          1011:     handler = Tk_CreateErrorHandler(display, -1, -1, -1, AppendErrorProc,
        !          1012:        (ClientData) pendingPtr);
        !          1013:     XChangeProperty(display, window, property, XA_STRING, 8,
        !          1014:            PropModeAppend, (unsigned char *) value, strlen(value)+1);
        !          1015:     Tk_DeleteErrorHandler(handler);
        !          1016: }
        !          1017: 
        !          1018: /*
        !          1019:  * The procedure below is invoked if an error occurs during
        !          1020:  * the XChangeProperty operation above.
        !          1021:  */
        !          1022: 
        !          1023:        /* ARGSUSED */
        !          1024: static int
        !          1025: AppendErrorProc(clientData, errorPtr)
        !          1026:     ClientData clientData;     /* Command to mark complete, or NULL. */
        !          1027:     XErrorEvent *errorPtr;     /* Information about error. */
        !          1028: {
        !          1029:     PendingCommand *pendingPtr = (PendingCommand *) clientData;
        !          1030:     register PendingCommand *pcPtr;
        !          1031: 
        !          1032:     if (pendingPtr == NULL) {
        !          1033:        return 0;
        !          1034:     }
        !          1035: 
        !          1036:     /*
        !          1037:      * Make sure this command is still pending.
        !          1038:      */
        !          1039: 
        !          1040:     for (pcPtr = pendingCommands; pcPtr != NULL;
        !          1041:            pcPtr = pcPtr->nextPtr) {
        !          1042:        if ((pcPtr == pendingPtr) && (pcPtr->result == NULL)) {
        !          1043:            pcPtr->result = ckalloc((unsigned) (strlen(pcPtr->target) + 50));
        !          1044:            sprintf(pcPtr->result,
        !          1045:                    "send to \"%s\" failed (no communication window)",
        !          1046:                    pcPtr->target);
        !          1047:            pcPtr->code = TCL_ERROR;
        !          1048:            break;
        !          1049:        }
        !          1050:     }
        !          1051:     return 0;
        !          1052: }
        !          1053: 
        !          1054: /*
        !          1055:  *--------------------------------------------------------------
        !          1056:  *
        !          1057:  * TimeoutProc --
        !          1058:  *
        !          1059:  *     This procedure is invoked when too much time has elapsed
        !          1060:  *     during the processing of a sent command.
        !          1061:  *
        !          1062:  * Results:
        !          1063:  *     None.
        !          1064:  *
        !          1065:  * Side effects:
        !          1066:  *     Mark the pending command as complete, with an error
        !          1067:  *     message signalling the timeout.
        !          1068:  *
        !          1069:  *--------------------------------------------------------------
        !          1070:  */
        !          1071: 
        !          1072: static void
        !          1073: TimeoutProc(clientData)
        !          1074:     ClientData clientData;     /* Information about command that
        !          1075:                                 * has been sent but not yet
        !          1076:                                 * responded to. */
        !          1077: {
        !          1078:     PendingCommand *pcPtr = (PendingCommand *) clientData;
        !          1079:     register PendingCommand *pcPtr2;
        !          1080: 
        !          1081:     /*
        !          1082:      * Make sure that the command is still in the pending list
        !          1083:      * and that it hasn't already completed.  Then register the
        !          1084:      * error.
        !          1085:      */
        !          1086: 
        !          1087:     for (pcPtr2 = pendingCommands; pcPtr2 != NULL;
        !          1088:            pcPtr2 = pcPtr2->nextPtr) {
        !          1089:        static char msg[] = "remote interpreter did not respond";
        !          1090:        if ((pcPtr2 != pcPtr) || (pcPtr2->result != NULL)) {
        !          1091:            continue;
        !          1092:        }
        !          1093:        pcPtr2->code = TCL_ERROR;
        !          1094:        pcPtr2->result = ckalloc((unsigned) (sizeof(msg) + 1));
        !          1095:        strcpy(pcPtr2->result, msg);
        !          1096:        return;
        !          1097:     }
        !          1098: }
        !          1099: 
        !          1100: /*
        !          1101:  *--------------------------------------------------------------
        !          1102:  *
        !          1103:  * DeleteProc --
        !          1104:  *
        !          1105:  *     This procedure is invoked by Tcl when a registered
        !          1106:  *     interpreter is about to be deleted.  It unregisters
        !          1107:  *     the interpreter.
        !          1108:  *
        !          1109:  * Results:
        !          1110:  *     None.
        !          1111:  *
        !          1112:  * Side effects:
        !          1113:  *     The interpreter given by riPtr is unregistered.
        !          1114:  *
        !          1115:  *--------------------------------------------------------------
        !          1116:  */
        !          1117: 
        !          1118: static void
        !          1119: DeleteProc(clientData)
        !          1120:     ClientData clientData;     /* Info about registration, passed
        !          1121:                                 * as ClientData. */
        !          1122: {
        !          1123:     RegisteredInterp *riPtr = (RegisteredInterp *) clientData;
        !          1124:     register RegisteredInterp *riPtr2;
        !          1125: 
        !          1126:     (void) LookupName(riPtr->dispPtr, riPtr->name, 1);
        !          1127:     if (registry == riPtr) {
        !          1128:        registry = riPtr->nextPtr;
        !          1129:     } else {
        !          1130:        for (riPtr2 = registry; riPtr2 != NULL;
        !          1131:                riPtr2 = riPtr2->nextPtr) {
        !          1132:            if (riPtr2->nextPtr == riPtr) {
        !          1133:                riPtr2->nextPtr = riPtr->nextPtr;
        !          1134:                break;
        !          1135:            }
        !          1136:        }
        !          1137:     }
        !          1138:     ckfree((char *) riPtr->name);
        !          1139:     ckfree((char *) riPtr);
        !          1140: }
        !          1141: 
        !          1142: /*
        !          1143:  *----------------------------------------------------------------------
        !          1144:  *
        !          1145:  * SendRestrictProc --
        !          1146:  *
        !          1147:  *     This procedure filters incoming events when a "send" command
        !          1148:  *     is outstanding.  It defers all events except those containing
        !          1149:  *     send commands and results.
        !          1150:  *
        !          1151:  * Results:
        !          1152:  *     False is returned except for property-change events on the
        !          1153:  *     given commWindow.
        !          1154:  *
        !          1155:  * Side effects:
        !          1156:  *     None.
        !          1157:  *
        !          1158:  *----------------------------------------------------------------------
        !          1159:  */
        !          1160: 
        !          1161:     /* ARGSUSED */
        !          1162: static Bool
        !          1163: SendRestrictProc(display, eventPtr, arg)
        !          1164:     Display *display;          /* Display from which event arrived. */
        !          1165:     register XEvent *eventPtr; /* Event that just arrived. */
        !          1166:     char *arg;                 /* Comunication window in which
        !          1167:                                 * we're interested. */
        !          1168: {
        !          1169:     register Tk_Window comm = (Tk_Window) arg;
        !          1170: 
        !          1171:     if ((display != Tk_Display(comm))
        !          1172:            || (eventPtr->type != PropertyNotify)
        !          1173:            || (eventPtr->xproperty.window != Tk_WindowId(comm))) {
        !          1174:        return False;
        !          1175:     }
        !          1176:     return True;
        !          1177: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.