home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / generic / tclEvent.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  19.5 KB  |  698 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclEvent.c --
  3.  *
  4.  *    This file implements some general event related interfaces including
  5.  *    background errors, exit handlers, and the "vwait" and "update"
  6.  *    command procedures. 
  7.  *
  8.  * Copyright (c) 1990-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tclEvent.c 1.153 97/08/11 20:22:31
  15.  */
  16.  
  17. #include "tclInt.h"
  18. #include "tclPort.h"
  19.  
  20. /*
  21.  * The data structure below is used to report background errors.  One
  22.  * such structure is allocated for each error;  it holds information
  23.  * about the interpreter and the error until bgerror can be invoked
  24.  * later as an idle handler.
  25.  */
  26.  
  27. typedef struct BgError {
  28.     Tcl_Interp *interp;        /* Interpreter in which error occurred.  NULL
  29.                  * means this error report has been cancelled
  30.                  * (a previous report generated a break). */
  31.     char *errorMsg;        /* The error message (interp->result when
  32.                  * the error occurred).  Malloc-ed. */
  33.     char *errorInfo;        /* Value of the errorInfo variable
  34.                  * (malloc-ed). */
  35.     char *errorCode;        /* Value of the errorCode variable
  36.                  * (malloc-ed). */
  37.     struct BgError *nextPtr;    /* Next in list of all pending error
  38.                  * reports for this interpreter, or NULL
  39.                  * for end of list. */
  40. } BgError;
  41.  
  42. /*
  43.  * One of the structures below is associated with the "tclBgError"
  44.  * assoc data for each interpreter.  It keeps track of the head and
  45.  * tail of the list of pending background errors for the interpreter.
  46.  */
  47.  
  48. typedef struct ErrAssocData {
  49.     BgError *firstBgPtr;    /* First in list of all background errors
  50.                  * waiting to be processed for this
  51.                  * interpreter (NULL if none). */
  52.     BgError *lastBgPtr;        /* Last in list of all background errors
  53.                  * waiting to be processed for this
  54.                  * interpreter (NULL if none). */
  55. } ErrAssocData;
  56.  
  57. /*
  58.  * For each exit handler created with a call to Tcl_CreateExitHandler
  59.  * there is a structure of the following type:
  60.  */
  61.  
  62. typedef struct ExitHandler {
  63.     Tcl_ExitProc *proc;        /* Procedure to call when process exits. */
  64.     ClientData clientData;    /* One word of information to pass to proc. */
  65.     struct ExitHandler *nextPtr;/* Next in list of all exit handlers for
  66.                  * this application, or NULL for end of list. */
  67. } ExitHandler;
  68.  
  69. static ExitHandler *firstExitPtr = NULL;
  70.                 /* First in list of all exit handlers for
  71.                  * application. */
  72.  
  73. /*
  74.  * The following variable is a "secret" indication to Tcl_Exit that
  75.  * it should dump out the state of memory before exiting.  If the
  76.  * value is non-NULL, it gives the name of the file in which to
  77.  * dump memory usage information.
  78.  */
  79.  
  80. char *tclMemDumpFileName = NULL;
  81.  
  82. /*
  83.  * This variable is set to 1 when Tcl_Exit is called, and at the end of
  84.  * its work, it is reset to 0. The variable is checked by TclInExit() to
  85.  * allow different behavior for exit-time processing, e.g. in closing of
  86.  * files and pipes.
  87.  */
  88.  
  89. static int tclInExit = 0;
  90.  
  91. /*
  92.  * Prototypes for procedures referenced only in this file:
  93.  */
  94.  
  95. static void        BgErrorDeleteProc _ANSI_ARGS_((ClientData clientData,
  96.                 Tcl_Interp *interp));
  97. static void        HandleBgErrors _ANSI_ARGS_((ClientData clientData));
  98. static char *        VwaitVarProc _ANSI_ARGS_((ClientData clientData,
  99.                 Tcl_Interp *interp, char *name1, char *name2,
  100.                 int flags));
  101.  
  102. /*
  103.  *----------------------------------------------------------------------
  104.  *
  105.  * Tcl_BackgroundError --
  106.  *
  107.  *    This procedure is invoked to handle errors that occur in Tcl
  108.  *    commands that are invoked in "background" (e.g. from event or
  109.  *    timer bindings).
  110.  *
  111.  * Results:
  112.  *    None.
  113.  *
  114.  * Side effects:
  115.  *    The command "bgerror" is invoked later as an idle handler to
  116.  *    process the error, passing it the error message.  If that fails,
  117.  *    then an error message is output on stderr.
  118.  *
  119.  *----------------------------------------------------------------------
  120.  */
  121.  
  122. void
  123. Tcl_BackgroundError(interp)
  124.     Tcl_Interp *interp;        /* Interpreter in which an error has
  125.                  * occurred. */
  126. {
  127.     BgError *errPtr;
  128.     char *errResult, *varValue;
  129.     ErrAssocData *assocPtr;
  130.  
  131.     /*
  132.      * The Tcl_AddErrorInfo call below (with an empty string) ensures that
  133.      * errorInfo gets properly set.  It's needed in cases where the error
  134.      * came from a utility procedure like Tcl_GetVar instead of Tcl_Eval;
  135.      * in these cases errorInfo still won't have been set when this
  136.      * procedure is called.
  137.      */
  138.  
  139.     Tcl_AddErrorInfo(interp, "");
  140.  
  141.     errResult = TclGetStringFromObj(Tcl_GetObjResult(interp), (int *) NULL);
  142.     
  143.     errPtr = (BgError *) ckalloc(sizeof(BgError));
  144.     errPtr->interp = interp;
  145.     errPtr->errorMsg = (char *) ckalloc((unsigned) (strlen(errResult) + 1));
  146.     strcpy(errPtr->errorMsg, errResult);
  147.     varValue = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
  148.     if (varValue == NULL) {
  149.     varValue = errPtr->errorMsg;
  150.     }
  151.     errPtr->errorInfo = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
  152.     strcpy(errPtr->errorInfo, varValue);
  153.     varValue = Tcl_GetVar(interp, "errorCode", TCL_GLOBAL_ONLY);
  154.     if (varValue == NULL) {
  155.     varValue = "";
  156.     }
  157.     errPtr->errorCode = (char *) ckalloc((unsigned) (strlen(varValue) + 1));
  158.     strcpy(errPtr->errorCode, varValue);
  159.     errPtr->nextPtr = NULL;
  160.  
  161.     assocPtr = (ErrAssocData *) Tcl_GetAssocData(interp, "tclBgError",
  162.         (Tcl_InterpDeleteProc **) NULL);
  163.     if (assocPtr == NULL) {
  164.  
  165.     /*
  166.      * This is the first time a background error has occurred in
  167.      * this interpreter.  Create associated data to keep track of
  168.      * pending error reports.
  169.      */
  170.  
  171.     assocPtr = (ErrAssocData *) ckalloc(sizeof(ErrAssocData));
  172.     assocPtr->firstBgPtr = NULL;
  173.     assocPtr->lastBgPtr = NULL;
  174.     Tcl_SetAssocData(interp, "tclBgError", BgErrorDeleteProc,
  175.         (ClientData) assocPtr);
  176.     }
  177.     if (assocPtr->firstBgPtr == NULL) {
  178.     assocPtr->firstBgPtr = errPtr;
  179.     Tcl_DoWhenIdle(HandleBgErrors, (ClientData) assocPtr);
  180.     } else {
  181.     assocPtr->lastBgPtr->nextPtr = errPtr;
  182.     }
  183.     assocPtr->lastBgPtr = errPtr;
  184.     Tcl_ResetResult(interp);
  185. }
  186.  
  187. /*
  188.  *----------------------------------------------------------------------
  189.  *
  190.  * HandleBgErrors --
  191.  *
  192.  *    This procedure is invoked as an idle handler to process all of
  193.  *    the accumulated background errors.
  194.  *
  195.  * Results:
  196.  *    None.
  197.  *
  198.  * Side effects:
  199.  *    Depends on what actions "bgerror" takes for the errors.
  200.  *
  201.  *----------------------------------------------------------------------
  202.  */
  203.  
  204. static void
  205. HandleBgErrors(clientData)
  206.     ClientData clientData;    /* Pointer to ErrAssocData structure. */
  207. {
  208.     Tcl_Interp *interp;
  209.     char *command;
  210.     char *argv[2];
  211.     int code;
  212.     BgError *errPtr;
  213.     ErrAssocData *assocPtr = (ErrAssocData *) clientData;
  214.     Tcl_Channel errChannel;
  215.  
  216.     Tcl_Preserve((ClientData) assocPtr);
  217.     
  218.     while (assocPtr->firstBgPtr != NULL) {
  219.     interp = assocPtr->firstBgPtr->interp;
  220.     if (interp == NULL) {
  221.         goto doneWithInterp;
  222.     }
  223.  
  224.     /*
  225.      * Restore important state variables to what they were at
  226.      * the time the error occurred.
  227.      */
  228.  
  229.     Tcl_SetVar(interp, "errorInfo", assocPtr->firstBgPtr->errorInfo,
  230.         TCL_GLOBAL_ONLY);
  231.     Tcl_SetVar(interp, "errorCode", assocPtr->firstBgPtr->errorCode,
  232.         TCL_GLOBAL_ONLY);
  233.  
  234.     /*
  235.      * Create and invoke the bgerror command.
  236.      */
  237.  
  238.     argv[0] = "bgerror";
  239.     argv[1] = assocPtr->firstBgPtr->errorMsg;
  240.     command = Tcl_Merge(2, argv);
  241.     Tcl_AllowExceptions(interp);
  242.         Tcl_Preserve((ClientData) interp);
  243.     code = Tcl_GlobalEval(interp, command);
  244.     ckfree(command);
  245.     if (code == TCL_ERROR) {
  246.  
  247.             /*
  248.              * If the interpreter is safe, we look for a hidden command
  249.              * named "bgerror" and call that with the error information.
  250.              * Otherwise, simply ignore the error. The rationale is that
  251.              * this could be an error caused by a malicious applet trying
  252.              * to cause an infinite barrage of error messages. The hidden
  253.              * "bgerror" command can be used by a security policy to
  254.              * interpose on such attacks and e.g. kill the applet after a
  255.              * few attempts.
  256.              */
  257.  
  258.             if (Tcl_IsSafe(interp)) {
  259.                 Tcl_HashTable *hTblPtr;
  260.                 Tcl_HashEntry *hPtr;
  261.  
  262.                 hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
  263.                         "tclHiddenCmds", NULL);
  264.                 if (hTblPtr == (Tcl_HashTable *) NULL) {
  265.                     goto doneWithInterp;
  266.                 }
  267.                 hPtr = Tcl_FindHashEntry(hTblPtr, "bgerror");
  268.                 if (hPtr == (Tcl_HashEntry *) NULL) {
  269.                     goto doneWithInterp;
  270.                 }
  271.  
  272.                 /*
  273.                  * OK, the hidden command "bgerror" exists, invoke it.
  274.                  */
  275.  
  276.                 argv[0] = "bgerror";
  277.                 argv[1] = ckalloc((unsigned)
  278.                         strlen(assocPtr->firstBgPtr->errorMsg));
  279.                 strcpy(argv[1], assocPtr->firstBgPtr->errorMsg);
  280.                 (void) TclInvoke(interp, 2, argv, TCL_INVOKE_HIDDEN);
  281.                 ckfree(argv[1]);
  282.  
  283.                 goto doneWithInterp;
  284.             } 
  285.  
  286.             /*
  287.              * We have to get the error output channel at the latest possible
  288.              * time, because the eval (above) might have changed the channel.
  289.              */
  290.             
  291.             errChannel = Tcl_GetStdChannel(TCL_STDERR);
  292.             if (errChannel != (Tcl_Channel) NULL) {
  293.                 if (strcmp(interp->result,
  294.            "\"bgerror\" is an invalid command name or ambiguous abbreviation")
  295.                         == 0) {
  296.                     Tcl_Write(errChannel, assocPtr->firstBgPtr->errorInfo, -1);
  297.                     Tcl_Write(errChannel, "\n", -1);
  298.                 } else {
  299.                     Tcl_Write(errChannel,
  300.                             "bgerror failed to handle background error.\n",
  301.                             -1);
  302.                     Tcl_Write(errChannel, "    Original error: ", -1);
  303.                     Tcl_Write(errChannel, assocPtr->firstBgPtr->errorMsg,
  304.                             -1);
  305.                     Tcl_Write(errChannel, "\n", -1);
  306.                     Tcl_Write(errChannel, "    Error in bgerror: ", -1);
  307.                     Tcl_Write(errChannel, interp->result, -1);
  308.                     Tcl_Write(errChannel, "\n", -1);
  309.                 }
  310.                 Tcl_Flush(errChannel);
  311.             }
  312.     } else if (code == TCL_BREAK) {
  313.  
  314.         /*
  315.          * Break means cancel any remaining error reports for this
  316.          * interpreter.
  317.          */
  318.  
  319.         for (errPtr = assocPtr->firstBgPtr; errPtr != NULL;
  320.             errPtr = errPtr->nextPtr) {
  321.         if (errPtr->interp == interp) {
  322.             errPtr->interp = NULL;
  323.         }
  324.         }
  325.     }
  326.  
  327.     /*
  328.      * Discard the command and the information about the error report.
  329.      */
  330.  
  331. doneWithInterp:
  332.  
  333.     if (assocPtr->firstBgPtr) {
  334.         ckfree(assocPtr->firstBgPtr->errorMsg);
  335.         ckfree(assocPtr->firstBgPtr->errorInfo);
  336.         ckfree(assocPtr->firstBgPtr->errorCode);
  337.         errPtr = assocPtr->firstBgPtr->nextPtr;
  338.         ckfree((char *) assocPtr->firstBgPtr);
  339.         assocPtr->firstBgPtr = errPtr;
  340.     }
  341.         
  342.         if (interp != NULL) {
  343.             Tcl_Release((ClientData) interp);
  344.         }
  345.     }
  346.     assocPtr->lastBgPtr = NULL;
  347.  
  348.     Tcl_Release((ClientData) assocPtr);
  349. }
  350.  
  351. /*
  352.  *----------------------------------------------------------------------
  353.  *
  354.  * BgErrorDeleteProc --
  355.  *
  356.  *    This procedure is associated with the "tclBgError" assoc data
  357.  *    for an interpreter;  it is invoked when the interpreter is
  358.  *    deleted in order to free the information assoicated with any
  359.  *    pending error reports.
  360.  *
  361.  * Results:
  362.  *    None.
  363.  *
  364.  * Side effects:
  365.  *    Background error information is freed: if there were any
  366.  *    pending error reports, they are cancelled.
  367.  *
  368.  *----------------------------------------------------------------------
  369.  */
  370.  
  371. static void
  372. BgErrorDeleteProc(clientData, interp)
  373.     ClientData clientData;    /* Pointer to ErrAssocData structure. */
  374.     Tcl_Interp *interp;        /* Interpreter being deleted. */
  375. {
  376.     ErrAssocData *assocPtr = (ErrAssocData *) clientData;
  377.     BgError *errPtr;
  378.  
  379.     while (assocPtr->firstBgPtr != NULL) {
  380.     errPtr = assocPtr->firstBgPtr;
  381.     assocPtr->firstBgPtr = errPtr->nextPtr;
  382.     ckfree(errPtr->errorMsg);
  383.     ckfree(errPtr->errorInfo);
  384.     ckfree(errPtr->errorCode);
  385.     ckfree((char *) errPtr);
  386.     }
  387.     Tcl_CancelIdleCall(HandleBgErrors, (ClientData) assocPtr);
  388.     Tcl_EventuallyFree((ClientData) assocPtr, TCL_DYNAMIC);
  389. }
  390.  
  391. /*
  392.  *----------------------------------------------------------------------
  393.  *
  394.  * Tcl_CreateExitHandler --
  395.  *
  396.  *    Arrange for a given procedure to be invoked just before the
  397.  *    application exits.
  398.  *
  399.  * Results:
  400.  *    None.
  401.  *
  402.  * Side effects:
  403.  *    Proc will be invoked with clientData as argument when the
  404.  *    application exits.
  405.  *
  406.  *----------------------------------------------------------------------
  407.  */
  408.  
  409. void
  410. Tcl_CreateExitHandler(proc, clientData)
  411.     Tcl_ExitProc *proc;        /* Procedure to invoke. */
  412.     ClientData clientData;    /* Arbitrary value to pass to proc. */
  413. {
  414.     ExitHandler *exitPtr;
  415.  
  416.     exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler));
  417.     exitPtr->proc = proc;
  418.     exitPtr->clientData = clientData;
  419.     exitPtr->nextPtr = firstExitPtr;
  420.     firstExitPtr = exitPtr;
  421. }
  422.  
  423. /*
  424.  *----------------------------------------------------------------------
  425.  *
  426.  * Tcl_DeleteExitHandler --
  427.  *
  428.  *    This procedure cancels an existing exit handler matching proc
  429.  *    and clientData, if such a handler exits.
  430.  *
  431.  * Results:
  432.  *    None.
  433.  *
  434.  * Side effects:
  435.  *    If there is an exit handler corresponding to proc and clientData
  436.  *    then it is cancelled;  if no such handler exists then nothing
  437.  *    happens.
  438.  *
  439.  *----------------------------------------------------------------------
  440.  */
  441.  
  442. void
  443. Tcl_DeleteExitHandler(proc, clientData)
  444.     Tcl_ExitProc *proc;        /* Procedure that was previously registered. */
  445.     ClientData clientData;    /* Arbitrary value to pass to proc. */
  446. {
  447.     ExitHandler *exitPtr, *prevPtr;
  448.  
  449.     for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL;
  450.         prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) {
  451.     if ((exitPtr->proc == proc)
  452.         && (exitPtr->clientData == clientData)) {
  453.         if (prevPtr == NULL) {
  454.         firstExitPtr = exitPtr->nextPtr;
  455.         } else {
  456.         prevPtr->nextPtr = exitPtr->nextPtr;
  457.         }
  458.         ckfree((char *) exitPtr);
  459.         return;
  460.     }
  461.     }
  462. }
  463.  
  464. /*
  465.  *----------------------------------------------------------------------
  466.  *
  467.  * Tcl_Exit --
  468.  *
  469.  *    This procedure is called to terminate the application.
  470.  *
  471.  * Results:
  472.  *    None.
  473.  *
  474.  * Side effects:
  475.  *    All existing exit handlers are invoked, then the application
  476.  *    ends.
  477.  *
  478.  *----------------------------------------------------------------------
  479.  */
  480.  
  481. void
  482. Tcl_Exit(status)
  483.     int status;            /* Exit status for application;  typically
  484.                  * 0 for normal return, 1 for error return. */
  485. {
  486.     Tcl_Finalize();
  487. #ifdef TCL_MEM_DEBUG
  488.     if (tclMemDumpFileName != NULL) {
  489.     Tcl_DumpActiveMemory(tclMemDumpFileName);
  490.     }
  491. #endif
  492.     TclPlatformExit(status);
  493. }
  494.  
  495. /*
  496.  *----------------------------------------------------------------------
  497.  *
  498.  * Tcl_Finalize --
  499.  *
  500.  *    Runs the exit handlers to allow Tcl to clean up its state prior
  501.  *    to being unloaded. Called by Tcl_Exit and when Tcl was dynamically
  502.  *    loaded and is now being unloaded.
  503.  *
  504.  * Results:
  505.  *    None.
  506.  *
  507.  * Side effects:
  508.  *    Whatever the exit handlers do. Also frees up storage associated
  509.  *    with the Tcl object type table.
  510.  *
  511.  *----------------------------------------------------------------------
  512.  */
  513.  
  514. void
  515. Tcl_Finalize()
  516. {
  517.     ExitHandler *exitPtr;
  518.     
  519.     /*
  520.      * Invoke exit handler first.
  521.      */
  522.  
  523.     tclInExit = 1;
  524.     for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) {
  525.     /*
  526.      * Be careful to remove the handler from the list before invoking
  527.      * its callback.  This protects us against double-freeing if the
  528.      * callback should call Tcl_DeleteExitHandler on itself.
  529.      */
  530.  
  531.     firstExitPtr = exitPtr->nextPtr;
  532.     (*exitPtr->proc)(exitPtr->clientData);
  533.     ckfree((char *) exitPtr);
  534.     }
  535.  
  536.     /*
  537.      * Now finalize the Tcl execution environment.  Note that this must be done
  538.      * after the exit handlers, because there are order dependencies.
  539.      */
  540.     
  541.     TclFinalizeCompExecEnv();
  542.     TclFinalizeEnvironment();
  543.     firstExitPtr = NULL;
  544.     tclInExit = 0;
  545. }
  546.  
  547. /*
  548.  *----------------------------------------------------------------------
  549.  *
  550.  * TclInExit --
  551.  *
  552.  *    Determines if we are in the middle of exit-time cleanup.
  553.  *
  554.  * Results:
  555.  *    If we are in the middle of exiting, 1, otherwise 0.
  556.  *
  557.  * Side effects:
  558.  *    None.
  559.  *
  560.  *----------------------------------------------------------------------
  561.  */
  562.  
  563. int
  564. TclInExit()
  565. {
  566.     return tclInExit;
  567. }
  568.  
  569. /*
  570.  *----------------------------------------------------------------------
  571.  *
  572.  * Tcl_VwaitCmd --
  573.  *
  574.  *    This procedure is invoked to process the "vwait" Tcl command.
  575.  *    See the user documentation for details on what it does.
  576.  *
  577.  * Results:
  578.  *    A standard Tcl result.
  579.  *
  580.  * Side effects:
  581.  *    See the user documentation.
  582.  *
  583.  *----------------------------------------------------------------------
  584.  */
  585.  
  586.     /* ARGSUSED */
  587. int
  588. Tcl_VwaitCmd(clientData, interp, argc, argv)
  589.     ClientData clientData;    /* Not used. */
  590.     Tcl_Interp *interp;        /* Current interpreter. */
  591.     int argc;            /* Number of arguments. */
  592.     char **argv;        /* Argument strings. */
  593. {
  594.     int done, foundEvent;
  595.  
  596.     if (argc != 2) {
  597.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  598.         argv[0], " name\"", (char *) NULL);
  599.     return TCL_ERROR;
  600.     }
  601.     if (Tcl_TraceVar(interp, argv[1],
  602.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  603.         VwaitVarProc, (ClientData) &done) != TCL_OK) {
  604.     return TCL_ERROR;
  605.     };
  606.     done = 0;
  607.     foundEvent = 1;
  608.     while (!done && foundEvent) {
  609.     foundEvent = Tcl_DoOneEvent(TCL_ALL_EVENTS);
  610.     }
  611.     Tcl_UntraceVar(interp, argv[1],
  612.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  613.         VwaitVarProc, (ClientData) &done);
  614.  
  615.     /*
  616.      * Clear out the interpreter's result, since it may have been set
  617.      * by event handlers.
  618.      */
  619.  
  620.     Tcl_ResetResult(interp);
  621.     if (!foundEvent) {
  622.     Tcl_AppendResult(interp, "can't wait for variable \"", argv[1],
  623.         "\":  would wait forever", (char *) NULL);
  624.     return TCL_ERROR;
  625.     }
  626.     return TCL_OK;
  627. }
  628.  
  629.     /* ARGSUSED */
  630. static char *
  631. VwaitVarProc(clientData, interp, name1, name2, flags)
  632.     ClientData clientData;    /* Pointer to integer to set to 1. */
  633.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  634.     char *name1;        /* Name of variable. */
  635.     char *name2;        /* Second part of variable name. */
  636.     int flags;            /* Information about what happened. */
  637. {
  638.     int *donePtr = (int *) clientData;
  639.  
  640.     *donePtr = 1;
  641.     return (char *) NULL;
  642. }
  643.  
  644. /*
  645.  *----------------------------------------------------------------------
  646.  *
  647.  * Tcl_UpdateCmd --
  648.  *
  649.  *    This procedure is invoked to process the "update" Tcl command.
  650.  *    See the user documentation for details on what it does.
  651.  *
  652.  * Results:
  653.  *    A standard Tcl result.
  654.  *
  655.  * Side effects:
  656.  *    See the user documentation.
  657.  *
  658.  *----------------------------------------------------------------------
  659.  */
  660.  
  661.     /* ARGSUSED */
  662. int
  663. Tcl_UpdateCmd(clientData, interp, argc, argv)
  664.     ClientData clientData;    /* Not used. */
  665.     Tcl_Interp *interp;        /* Current interpreter. */
  666.     int argc;            /* Number of arguments. */
  667.     char **argv;        /* Argument strings. */
  668. {
  669.     int flags;
  670.  
  671.     if (argc == 1) {
  672.     flags = TCL_ALL_EVENTS|TCL_DONT_WAIT;
  673.     } else if (argc == 2) {
  674.     if (strncmp(argv[1], "idletasks", strlen(argv[1])) != 0) {
  675.         Tcl_AppendResult(interp, "bad option \"", argv[1],
  676.             "\": must be idletasks", (char *) NULL);
  677.         return TCL_ERROR;
  678.     }
  679.     flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT;
  680.     } else {
  681.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  682.         argv[0], " ?idletasks?\"", (char *) NULL);
  683.     return TCL_ERROR;
  684.     }
  685.  
  686.     while (Tcl_DoOneEvent(flags) != 0) {
  687.     /* Empty loop body */
  688.     }
  689.  
  690.     /*
  691.      * Must clear the interpreter's result because event handlers could
  692.      * have executed commands.
  693.      */
  694.  
  695.     Tcl_ResetResult(interp);
  696.     return TCL_OK;
  697. }
  698.