tclMain.c

Go to the documentation of this file.
00001 /*
00002  * tclMain.c --
00003  *
00004  *      Main program for Tcl shells and other Tcl-based applications.
00005  *
00006  * Copyright (c) 1988-1994 The Regents of the University of California.
00007  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
00008  * Copyright (c) 2000 Ajuba Solutions.
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * RCS: @(#) $Id: tclMain.c,v 1.44 2007/12/13 15:23:19 dgp Exp $
00014  */
00015 
00016 #include "tclInt.h"
00017 
00018 #undef TCL_STORAGE_CLASS
00019 #define TCL_STORAGE_CLASS DLLEXPORT
00020 
00021 /*
00022  * The default prompt used when the user has not overridden it.
00023  */
00024 
00025 #define DEFAULT_PRIMARY_PROMPT  "% "
00026 
00027 /*
00028  * Declarations for various library functions and variables (don't want to
00029  * include tclPort.h here, because people might copy this file out of the Tcl
00030  * source directory to make their own modified versions).
00031  */
00032 
00033 extern CRTIMPORT int    isatty(int fd);
00034 
00035 static Tcl_Obj *tclStartupScriptPath = NULL;
00036 static Tcl_Obj *tclStartupScriptEncoding = NULL;
00037 static Tcl_MainLoopProc *mainLoopProc = NULL;
00038 
00039 /*
00040  * Structure definition for information used to keep the state of an
00041  * interactive command processor that reads lines from standard input and
00042  * writes prompts and results to standard output.
00043  */
00044 
00045 typedef enum {
00046     PROMPT_NONE,                /* Print no prompt */
00047     PROMPT_START,               /* Print prompt for command start */
00048     PROMPT_CONTINUE             /* Print prompt for command continuation */
00049 } PromptType;
00050 
00051 typedef struct InteractiveState {
00052     Tcl_Channel input;          /* The standard input channel from which lines
00053                                  * are read. */
00054     int tty;                    /* Non-zero means standard input is a
00055                                  * terminal-like device. Zero means it's a
00056                                  * file. */
00057     Tcl_Obj *commandPtr;        /* Used to assemble lines of input into Tcl
00058                                  * commands. */
00059     PromptType prompt;          /* Next prompt to print */
00060     Tcl_Interp *interp;         /* Interpreter that evaluates interactive
00061                                  * commands. */
00062 } InteractiveState;
00063 
00064 /*
00065  * Forward declarations for functions defined later in this file.
00066  */
00067 
00068 static void             Prompt(Tcl_Interp *interp, PromptType *promptPtr);
00069 static void             StdinProc(ClientData clientData, int mask);
00070 
00071 /*
00072  *----------------------------------------------------------------------
00073  *
00074  * Tcl_SetStartupScript --
00075  *
00076  *      Sets the path and encoding of the startup script to be evaluated by
00077  *      Tcl_Main, used to override the command line processing.
00078  *
00079  * Results:
00080  *      None.
00081  *
00082  * Side effects:
00083  *
00084  *----------------------------------------------------------------------
00085  */
00086 
00087 void
00088 Tcl_SetStartupScript(
00089     Tcl_Obj *path,              /* Filesystem path of startup script file */
00090     CONST char *encoding)       /* Encoding of the data in that file */
00091 {
00092     Tcl_Obj *newEncoding = NULL;
00093     if (encoding != NULL) {
00094         newEncoding = Tcl_NewStringObj(encoding, -1);
00095     }
00096 
00097     if (tclStartupScriptPath != NULL) {
00098         Tcl_DecrRefCount(tclStartupScriptPath);
00099     }
00100     tclStartupScriptPath = path;
00101     if (tclStartupScriptPath != NULL) {
00102         Tcl_IncrRefCount(tclStartupScriptPath);
00103     }
00104 
00105     if (tclStartupScriptEncoding != NULL) {
00106         Tcl_DecrRefCount(tclStartupScriptEncoding);
00107     }
00108     tclStartupScriptEncoding = newEncoding;
00109     if (tclStartupScriptEncoding != NULL) {
00110         Tcl_IncrRefCount(tclStartupScriptEncoding);
00111     }
00112 }
00113 
00114 /*
00115  *----------------------------------------------------------------------
00116  *
00117  * Tcl_GetStartupScript --
00118  *
00119  *      Gets the path and encoding of the startup script to be evaluated by
00120  *      Tcl_Main.
00121  *
00122  * Results:
00123  *      The path of the startup script; NULL if none has been set.
00124  *
00125  * Side effects:
00126  *      If encodingPtr is not NULL, stores a (CONST char *) in it pointing to
00127  *      the encoding name registered for the startup script. Tcl retains
00128  *      ownership of the string, and may free it. Caller should make a copy
00129  *      for long-term use.
00130  *
00131  *----------------------------------------------------------------------
00132  */
00133 
00134 Tcl_Obj *
00135 Tcl_GetStartupScript(
00136     CONST char **encodingPtr)   /* When not NULL, points to storage for the
00137                                  * (CONST char *) that points to the
00138                                  * registered encoding name for the startup
00139                                  * script */
00140 {
00141     if (encodingPtr != NULL) {
00142         if (tclStartupScriptEncoding == NULL) {
00143             *encodingPtr = NULL;
00144         } else {
00145             *encodingPtr = Tcl_GetString(tclStartupScriptEncoding);
00146         }
00147     }
00148     return tclStartupScriptPath;
00149 }
00150 
00151 /*
00152  *----------------------------------------------------------------------
00153  *
00154  * TclSetStartupScriptPath --
00155  *
00156  *      Primes the startup script VFS path, used to override the command line
00157  *      processing.
00158  *
00159  * Results:
00160  *      None.
00161  *
00162  * Side effects:
00163  *      This function initializes the VFS path of the Tcl script to run at
00164  *      startup.
00165  *
00166  *----------------------------------------------------------------------
00167  */
00168 
00169 void
00170 TclSetStartupScriptPath(
00171     Tcl_Obj *path)
00172 {
00173     Tcl_SetStartupScript(path, NULL);
00174 }
00175 
00176 /*
00177  *----------------------------------------------------------------------
00178  *
00179  * TclGetStartupScriptPath --
00180  *
00181  *      Gets the startup script VFS path, used to override the command line
00182  *      processing.
00183  *
00184  * Results:
00185  *      The startup script VFS path, NULL if none has been set.
00186  *
00187  * Side effects:
00188  *      None.
00189  *
00190  *----------------------------------------------------------------------
00191  */
00192 
00193 Tcl_Obj *
00194 TclGetStartupScriptPath(void)
00195 {
00196     return Tcl_GetStartupScript(NULL);
00197 }
00198 
00199 /*
00200  *----------------------------------------------------------------------
00201  *
00202  * TclSetStartupScriptFileName --
00203  *
00204  *      Primes the startup script file name, used to override the command line
00205  *      processing.
00206  *
00207  * Results:
00208  *      None.
00209  *
00210  * Side effects:
00211  *      This function initializes the file name of the Tcl script to run at
00212  *      startup.
00213  *
00214  *----------------------------------------------------------------------
00215  */
00216 
00217 void
00218 TclSetStartupScriptFileName(
00219     CONST char *fileName)
00220 {
00221     Tcl_Obj *path = Tcl_NewStringObj(fileName,-1);
00222     Tcl_SetStartupScript(path, NULL);
00223 }
00224 
00225 /*
00226  *----------------------------------------------------------------------
00227  *
00228  * TclGetStartupScriptFileName --
00229  *
00230  *      Gets the startup script file name, used to override the command line
00231  *      processing.
00232  *
00233  * Results:
00234  *      The startup script file name, NULL if none has been set.
00235  *
00236  * Side effects:
00237  *      None.
00238  *
00239  *----------------------------------------------------------------------
00240  */
00241 
00242 CONST char *
00243 TclGetStartupScriptFileName(void)
00244 {
00245     Tcl_Obj *path = Tcl_GetStartupScript(NULL);
00246 
00247     if (path == NULL) {
00248         return NULL;
00249     }
00250     return Tcl_GetString(path);
00251 }
00252 
00253 /*----------------------------------------------------------------------
00254  *
00255  * Tcl_SourceRCFile --
00256  *
00257  *      This function is typically invoked by Tcl_Main of Tk_Main function to
00258  *      source an application specific rc file into the interpreter at startup
00259  *      time.
00260  *
00261  * Results:
00262  *      None.
00263  *
00264  * Side effects:
00265  *      Depends on what's in the rc script.
00266  *
00267  *----------------------------------------------------------------------
00268  */
00269 
00270 void
00271 Tcl_SourceRCFile(
00272     Tcl_Interp *interp)         /* Interpreter to source rc file into. */
00273 {
00274     Tcl_DString temp;
00275     CONST char *fileName;
00276     Tcl_Channel errChannel;
00277 
00278     fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY);
00279     if (fileName != NULL) {
00280         Tcl_Channel c;
00281         CONST char *fullName;
00282 
00283         Tcl_DStringInit(&temp);
00284         fullName = Tcl_TranslateFileName(interp, fileName, &temp);
00285         if (fullName == NULL) {
00286             /*
00287              * Couldn't translate the file name (e.g. it referred to a bogus
00288              * user or there was no HOME environment variable). Just do
00289              * nothing.
00290              */
00291         } else {
00292             /*
00293              * Test for the existence of the rc file before trying to read it.
00294              */
00295 
00296             c = Tcl_OpenFileChannel(NULL, fullName, "r", 0);
00297             if (c != (Tcl_Channel) NULL) {
00298                 Tcl_Close(NULL, c);
00299                 if (Tcl_EvalFile(interp, fullName) != TCL_OK) {
00300                     errChannel = Tcl_GetStdChannel(TCL_STDERR);
00301                     if (errChannel) {
00302                         Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
00303                         Tcl_WriteChars(errChannel, "\n", 1);
00304                     }
00305                 }
00306             }
00307         }
00308         Tcl_DStringFree(&temp);
00309     }
00310 }
00311 
00312 /*----------------------------------------------------------------------
00313  *
00314  * Tcl_Main --
00315  *
00316  *      Main program for tclsh and most other Tcl-based applications.
00317  *
00318  * Results:
00319  *      None. This function never returns (it exits the process when it's
00320  *      done).
00321  *
00322  * Side effects:
00323  *      This function initializes the Tcl world and then starts interpreting
00324  *      commands; almost anything could happen, depending on the script being
00325  *      interpreted.
00326  *
00327  *----------------------------------------------------------------------
00328  */
00329 
00330 void
00331 Tcl_Main(
00332     int argc,                   /* Number of arguments. */
00333     char **argv,                /* Array of argument strings. */
00334     Tcl_AppInitProc *appInitProc)
00335                                 /* Application-specific initialization
00336                                  * function to call after most initialization
00337                                  * but before starting to execute commands. */
00338 {
00339     Tcl_Obj *path, *resultPtr, *argvPtr, *commandPtr = NULL;
00340     CONST char *encodingName = NULL;
00341     PromptType prompt = PROMPT_START;
00342     int code, length, tty, exitCode = 0;
00343     Tcl_Channel inChannel, outChannel, errChannel;
00344     Tcl_Interp *interp;
00345     Tcl_DString appName;
00346 
00347     Tcl_FindExecutable(argv[0]);
00348 
00349     interp = Tcl_CreateInterp();
00350     Tcl_InitMemory(interp);
00351 
00352     /*
00353      * If the application has not already set a startup script, parse the
00354      * first few command line arguments to determine the script path and
00355      * encoding.
00356      */
00357 
00358     if (NULL == Tcl_GetStartupScript(NULL)) {
00359 
00360         /*
00361          * Check whether first 3 args (argv[1] - argv[3]) look like
00362          *      -encoding ENCODING FILENAME
00363          * or like
00364          *      FILENAME
00365          */
00366 
00367         if ((argc > 3) && (0 == strcmp("-encoding", argv[1]))
00368                 && ('-' != argv[3][0])) {
00369             Tcl_SetStartupScript(Tcl_NewStringObj(argv[3], -1), argv[2]);
00370             argc -= 3;
00371             argv += 3;
00372         } else if ((argc > 1) && ('-' != argv[1][0])) {
00373             Tcl_SetStartupScript(Tcl_NewStringObj(argv[1], -1), NULL);
00374             argc--;
00375             argv++;
00376         }
00377     }
00378 
00379     path = Tcl_GetStartupScript(&encodingName);
00380     if (path == NULL) {
00381         Tcl_ExternalToUtfDString(NULL, argv[0], -1, &appName);
00382     } else {
00383         CONST char *pathName = Tcl_GetStringFromObj(path, &length);
00384         Tcl_ExternalToUtfDString(NULL, pathName, length, &appName);
00385         path = Tcl_NewStringObj(Tcl_DStringValue(&appName), -1);
00386         Tcl_SetStartupScript(path, encodingName);
00387     }
00388     Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&appName), TCL_GLOBAL_ONLY);
00389     Tcl_DStringFree(&appName);
00390     argc--;
00391     argv++;
00392 
00393     Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(argc), TCL_GLOBAL_ONLY);
00394 
00395     argvPtr = Tcl_NewListObj(0, NULL);
00396     while (argc--) {
00397         Tcl_DString ds;
00398         Tcl_ExternalToUtfDString(NULL, *argv++, -1, &ds);
00399         Tcl_ListObjAppendElement(NULL, argvPtr, Tcl_NewStringObj(
00400                 Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)));
00401         Tcl_DStringFree(&ds);
00402     }
00403     Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY);
00404 
00405     /*
00406      * Set the "tcl_interactive" variable.
00407      */
00408 
00409     tty = isatty(0);
00410     Tcl_SetVar(interp, "tcl_interactive", ((path == NULL) && tty) ? "1" : "0",
00411             TCL_GLOBAL_ONLY);
00412 
00413     /*
00414      * Invoke application-specific initialization.
00415      */
00416 
00417     Tcl_Preserve((ClientData) interp);
00418     if ((*appInitProc)(interp) != TCL_OK) {
00419         errChannel = Tcl_GetStdChannel(TCL_STDERR);
00420         if (errChannel) {
00421             Tcl_WriteChars(errChannel,
00422                     "application-specific initialization failed: ", -1);
00423             Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
00424             Tcl_WriteChars(errChannel, "\n", 1);
00425         }
00426     }
00427     if (Tcl_InterpDeleted(interp)) {
00428         goto done;
00429     }
00430     if (Tcl_LimitExceeded(interp)) {
00431         goto done;
00432     }
00433 
00434     /*
00435      * If a script file was specified then just source that file and quit.
00436      * Must fetch it again, as the appInitProc might have reset it.
00437      */
00438 
00439     path = Tcl_GetStartupScript(&encodingName);
00440     if (path != NULL) {
00441         code = Tcl_FSEvalFileEx(interp, path, encodingName);
00442         if (code != TCL_OK) {
00443             errChannel = Tcl_GetStdChannel(TCL_STDERR);
00444             if (errChannel) {
00445                 Tcl_Obj *options = Tcl_GetReturnOptions(interp, code);
00446                 Tcl_Obj *keyPtr, *valuePtr;
00447 
00448                 TclNewLiteralStringObj(keyPtr, "-errorinfo");
00449                 Tcl_IncrRefCount(keyPtr);
00450                 Tcl_DictObjGet(NULL, options, keyPtr, &valuePtr);
00451                 Tcl_DecrRefCount(keyPtr);
00452 
00453                 if (valuePtr) {
00454                     Tcl_WriteObj(errChannel, valuePtr);
00455                 }
00456                 Tcl_WriteChars(errChannel, "\n", 1);
00457             }
00458             exitCode = 1;
00459         }
00460         goto done;
00461     }
00462 
00463     /*
00464      * We're running interactively. Source a user-specific startup file if the
00465      * application specified one and if the file exists.
00466      */
00467 
00468     Tcl_SourceRCFile(interp);
00469     if (Tcl_LimitExceeded(interp)) {
00470         goto done;
00471     }
00472 
00473     /*
00474      * Process commands from stdin until there's an end-of-file. Note that we
00475      * need to fetch the standard channels again after every eval, since they
00476      * may have been changed.
00477      */
00478 
00479     commandPtr = Tcl_NewObj();
00480     Tcl_IncrRefCount(commandPtr);
00481 
00482     /*
00483      * Get a new value for tty if anyone writes to ::tcl_interactive
00484      */
00485 
00486     Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, TCL_LINK_BOOLEAN);
00487     inChannel = Tcl_GetStdChannel(TCL_STDIN);
00488     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
00489     while ((inChannel != (Tcl_Channel) NULL) && !Tcl_InterpDeleted(interp)) {
00490         if (mainLoopProc == NULL) {
00491             if (tty) {
00492                 Prompt(interp, &prompt);
00493                 if (Tcl_InterpDeleted(interp)) {
00494                     break;
00495                 }
00496                 if (Tcl_LimitExceeded(interp)) {
00497                     break;
00498                 }
00499                 inChannel = Tcl_GetStdChannel(TCL_STDIN);
00500                 if (inChannel == (Tcl_Channel) NULL) {
00501                     break;
00502                 }
00503             }
00504             if (Tcl_IsShared(commandPtr)) {
00505                 Tcl_DecrRefCount(commandPtr);
00506                 commandPtr = Tcl_DuplicateObj(commandPtr);
00507                 Tcl_IncrRefCount(commandPtr);
00508             }
00509             length = Tcl_GetsObj(inChannel, commandPtr);
00510             if (length < 0) {
00511                 if (Tcl_InputBlocked(inChannel)) {
00512                     /*
00513                      * This can only happen if stdin has been set to
00514                      * non-blocking.  In that case cycle back and try again.
00515                      * This sets up a tight polling loop (since we have no
00516                      * event loop running). If this causes bad CPU hogging,
00517                      * we might try toggling the blocking on stdin instead.
00518                      */
00519 
00520                     continue;
00521                 }
00522 
00523                 /*
00524                  * Either EOF, or an error on stdin; we're done
00525                  */
00526 
00527                 break;
00528             }
00529 
00530             /*
00531              * Add the newline removed by Tcl_GetsObj back to the string.
00532              * Have to add it back before testing completeness, because
00533              * it can make a difference.  [Bug 1775878].
00534              */
00535 
00536             if (Tcl_IsShared(commandPtr)) {
00537                 Tcl_DecrRefCount(commandPtr);
00538                 commandPtr = Tcl_DuplicateObj(commandPtr);
00539                 Tcl_IncrRefCount(commandPtr);
00540             }
00541             Tcl_AppendToObj(commandPtr, "\n", 1);
00542             if (!TclObjCommandComplete(commandPtr)) {
00543                 prompt = PROMPT_CONTINUE;
00544                 continue;
00545             }
00546 
00547             prompt = PROMPT_START;
00548             /*
00549              * The final newline is syntactically redundant, and causes
00550              * some error messages troubles deeper in, so lop it back off.
00551              */
00552             Tcl_GetStringFromObj(commandPtr, &length);
00553             Tcl_SetObjLength(commandPtr, --length);
00554             code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
00555             inChannel = Tcl_GetStdChannel(TCL_STDIN);
00556             outChannel = Tcl_GetStdChannel(TCL_STDOUT);
00557             errChannel = Tcl_GetStdChannel(TCL_STDERR);
00558             Tcl_DecrRefCount(commandPtr);
00559             commandPtr = Tcl_NewObj();
00560             Tcl_IncrRefCount(commandPtr);
00561             if (code != TCL_OK) {
00562                 if (errChannel) {
00563                     Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
00564                     Tcl_WriteChars(errChannel, "\n", 1);
00565                 }
00566             } else if (tty) {
00567                 resultPtr = Tcl_GetObjResult(interp);
00568                 Tcl_IncrRefCount(resultPtr);
00569                 Tcl_GetStringFromObj(resultPtr, &length);
00570                 if ((length > 0) && outChannel) {
00571                     Tcl_WriteObj(outChannel, resultPtr);
00572                     Tcl_WriteChars(outChannel, "\n", 1);
00573                 }
00574                 Tcl_DecrRefCount(resultPtr);
00575             }
00576         } else {        /* (mainLoopProc != NULL) */
00577             /*
00578              * If a main loop has been defined while running interactively, we
00579              * want to start a fileevent based prompt by establishing a
00580              * channel handler for stdin.
00581              */
00582 
00583             InteractiveState *isPtr = NULL;
00584 
00585             if (inChannel) {
00586                 if (tty) {
00587                     Prompt(interp, &prompt);
00588                 }
00589                 isPtr = (InteractiveState *)
00590                         ckalloc((int) sizeof(InteractiveState));
00591                 isPtr->input = inChannel;
00592                 isPtr->tty = tty;
00593                 isPtr->commandPtr = commandPtr;
00594                 isPtr->prompt = prompt;
00595                 isPtr->interp = interp;
00596 
00597                 Tcl_UnlinkVar(interp, "tcl_interactive");
00598                 Tcl_LinkVar(interp, "tcl_interactive", (char *) &(isPtr->tty),
00599                         TCL_LINK_BOOLEAN);
00600 
00601                 Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
00602                         (ClientData) isPtr);
00603             }
00604 
00605             (*mainLoopProc)();
00606             mainLoopProc = NULL;
00607 
00608             if (inChannel) {
00609                 tty = isPtr->tty;
00610                 Tcl_UnlinkVar(interp, "tcl_interactive");
00611                 Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty,
00612                         TCL_LINK_BOOLEAN);
00613                 prompt = isPtr->prompt;
00614                 commandPtr = isPtr->commandPtr;
00615                 if (isPtr->input != (Tcl_Channel) NULL) {
00616                     Tcl_DeleteChannelHandler(isPtr->input, StdinProc,
00617                             (ClientData) isPtr);
00618                 }
00619                 ckfree((char *)isPtr);
00620             }
00621             inChannel = Tcl_GetStdChannel(TCL_STDIN);
00622             outChannel = Tcl_GetStdChannel(TCL_STDOUT);
00623             errChannel = Tcl_GetStdChannel(TCL_STDERR);
00624         }
00625 #ifdef TCL_MEM_DEBUG
00626 
00627         /*
00628          * This code here only for the (unsupported and deprecated) [checkmem]
00629          * command.
00630          */
00631 
00632         if (tclMemDumpFileName != NULL) {
00633             mainLoopProc = NULL;
00634             Tcl_DeleteInterp(interp);
00635         }
00636 #endif
00637     }
00638 
00639   done:
00640     if ((exitCode == 0) && (mainLoopProc != NULL)
00641             && !Tcl_LimitExceeded(interp)) {
00642         /*
00643          * If everything has gone OK so far, call the main loop proc, if it
00644          * exists. Packages (like Tk) can set it to start processing events at
00645          * this point.
00646          */
00647 
00648         (*mainLoopProc)();
00649         mainLoopProc = NULL;
00650     }
00651     if (commandPtr != NULL) {
00652         Tcl_DecrRefCount(commandPtr);
00653     }
00654 
00655     /*
00656      * Rather than calling exit, invoke the "exit" command so that users can
00657      * replace "exit" with some other command to do additional cleanup on
00658      * exit. The Tcl_EvalObjEx call should never return.
00659      */
00660 
00661     if (!Tcl_InterpDeleted(interp)) {
00662         if (!Tcl_LimitExceeded(interp)) {
00663             Tcl_Obj *cmd = Tcl_ObjPrintf("exit %d", exitCode);
00664             Tcl_IncrRefCount(cmd);
00665             Tcl_EvalObjEx(interp, cmd, TCL_EVAL_GLOBAL);
00666             Tcl_DecrRefCount(cmd);
00667         }
00668 
00669         /*
00670          * If Tcl_EvalObjEx returns, trying to eval [exit], something unusual
00671          * is happening. Maybe interp has been deleted; maybe [exit] was
00672          * redefined, maybe we've blown up because of an exceeded limit. We
00673          * still want to cleanup and exit.
00674          */
00675 
00676         if (!Tcl_InterpDeleted(interp)) {
00677             Tcl_DeleteInterp(interp);
00678         }
00679     }
00680     Tcl_SetStartupScript(NULL, NULL);
00681 
00682     /*
00683      * If we get here, the master interp has been deleted. Allow its
00684      * destruction with the last matching Tcl_Release.
00685      */
00686 
00687     Tcl_Release((ClientData) interp);
00688     Tcl_Exit(exitCode);
00689 }
00690 
00691 /*
00692  *---------------------------------------------------------------
00693  *
00694  * Tcl_SetMainLoop --
00695  *
00696  *      Sets an alternative main loop function.
00697  *
00698  * Results:
00699  *      Returns the previously defined main loop function.
00700  *
00701  * Side effects:
00702  *      This function will be called before Tcl exits, allowing for the
00703  *      creation of an event loop.
00704  *
00705  *---------------------------------------------------------------
00706  */
00707 
00708 void
00709 Tcl_SetMainLoop(
00710     Tcl_MainLoopProc *proc)
00711 {
00712     mainLoopProc = proc;
00713 }
00714 
00715 /*
00716  *----------------------------------------------------------------------
00717  *
00718  * StdinProc --
00719  *
00720  *      This function is invoked by the event dispatcher whenever standard
00721  *      input becomes readable. It grabs the next line of input characters,
00722  *      adds them to a command being assembled, and executes the command if
00723  *      it's complete.
00724  *
00725  * Results:
00726  *      None.
00727  *
00728  * Side effects:
00729  *      Could be almost arbitrary, depending on the command that's typed.
00730  *
00731  *----------------------------------------------------------------------
00732  */
00733 
00734     /* ARGSUSED */
00735 static void
00736 StdinProc(
00737     ClientData clientData,      /* The state of interactive cmd line */
00738     int mask)                   /* Not used. */
00739 {
00740     InteractiveState *isPtr = (InteractiveState *) clientData;
00741     Tcl_Channel chan = isPtr->input;
00742     Tcl_Obj *commandPtr = isPtr->commandPtr;
00743     Tcl_Interp *interp = isPtr->interp;
00744     int code, length;
00745 
00746     if (Tcl_IsShared(commandPtr)) {
00747         Tcl_DecrRefCount(commandPtr);
00748         commandPtr = Tcl_DuplicateObj(commandPtr);
00749         Tcl_IncrRefCount(commandPtr);
00750     }
00751     length = Tcl_GetsObj(chan, commandPtr);
00752     if (length < 0) {
00753         if (Tcl_InputBlocked(chan)) {
00754             return;
00755         }
00756         if (isPtr->tty) {
00757             /*
00758              * Would be better to find a way to exit the mainLoop? Or perhaps
00759              * evaluate [exit]? Leaving as is for now due to compatibility
00760              * concerns.
00761              */
00762 
00763             Tcl_Exit(0);
00764         }
00765         Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) isPtr);
00766         return;
00767     }
00768 
00769     if (Tcl_IsShared(commandPtr)) {
00770         Tcl_DecrRefCount(commandPtr);
00771         commandPtr = Tcl_DuplicateObj(commandPtr);
00772         Tcl_IncrRefCount(commandPtr);
00773     }
00774     Tcl_AppendToObj(commandPtr, "\n", 1);
00775     if (!TclObjCommandComplete(commandPtr)) {
00776         isPtr->prompt = PROMPT_CONTINUE;
00777         goto prompt;
00778     }
00779     isPtr->prompt = PROMPT_START;
00780     Tcl_GetStringFromObj(commandPtr, &length);
00781     Tcl_SetObjLength(commandPtr, --length);
00782 
00783     /*
00784      * Disable the stdin channel handler while evaluating the command;
00785      * otherwise if the command re-enters the event loop we might process
00786      * commands from stdin before the current command is finished. Among other
00787      * things, this will trash the text of the command being evaluated.
00788      */
00789 
00790     Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) isPtr);
00791     code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
00792     isPtr->input = chan = Tcl_GetStdChannel(TCL_STDIN);
00793     Tcl_DecrRefCount(commandPtr);
00794     isPtr->commandPtr = commandPtr = Tcl_NewObj();
00795     Tcl_IncrRefCount(commandPtr);
00796     if (chan != (Tcl_Channel) NULL) {
00797         Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
00798                 (ClientData) isPtr);
00799     }
00800     if (code != TCL_OK) {
00801         Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR);
00802         if (errChannel != (Tcl_Channel) NULL) {
00803             Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
00804             Tcl_WriteChars(errChannel, "\n", 1);
00805         }
00806     } else if (isPtr->tty) {
00807         Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
00808         Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT);
00809         Tcl_IncrRefCount(resultPtr);
00810         Tcl_GetStringFromObj(resultPtr, &length);
00811         if ((length >0) && (outChannel != (Tcl_Channel) NULL)) {
00812             Tcl_WriteObj(outChannel, resultPtr);
00813             Tcl_WriteChars(outChannel, "\n", 1);
00814         }
00815         Tcl_DecrRefCount(resultPtr);
00816     }
00817 
00818     /*
00819      * If a tty stdin is still around, output a prompt.
00820      */
00821 
00822   prompt:
00823     if (isPtr->tty && (isPtr->input != (Tcl_Channel) NULL)) {
00824         Prompt(interp, &(isPtr->prompt));
00825         isPtr->input = Tcl_GetStdChannel(TCL_STDIN);
00826     }
00827 }
00828 
00829 /*
00830  *----------------------------------------------------------------------
00831  *
00832  * Prompt --
00833  *
00834  *      Issue a prompt on standard output, or invoke a script to issue the
00835  *      prompt.
00836  *
00837  * Results:
00838  *      None.
00839  *
00840  * Side effects:
00841  *      A prompt gets output, and a Tcl script may be evaluated in interp.
00842  *
00843  *----------------------------------------------------------------------
00844  */
00845 
00846 static void
00847 Prompt(
00848     Tcl_Interp *interp,         /* Interpreter to use for prompting. */
00849     PromptType *promptPtr)      /* Points to type of prompt to print. Filled
00850                                  * with PROMPT_NONE after a prompt is
00851                                  * printed. */
00852 {
00853     Tcl_Obj *promptCmdPtr;
00854     int code;
00855     Tcl_Channel outChannel, errChannel;
00856 
00857     if (*promptPtr == PROMPT_NONE) {
00858         return;
00859     }
00860 
00861     promptCmdPtr = Tcl_GetVar2Ex(interp,
00862             ((*promptPtr == PROMPT_CONTINUE) ? "tcl_prompt2" : "tcl_prompt1"),
00863             NULL, TCL_GLOBAL_ONLY);
00864 
00865     if (Tcl_InterpDeleted(interp)) {
00866         return;
00867     }
00868     if (promptCmdPtr == NULL) {
00869     defaultPrompt:
00870         outChannel = Tcl_GetStdChannel(TCL_STDOUT);
00871         if ((*promptPtr == PROMPT_START)
00872                 && (outChannel != (Tcl_Channel) NULL)) {
00873             Tcl_WriteChars(outChannel, DEFAULT_PRIMARY_PROMPT,
00874                     strlen(DEFAULT_PRIMARY_PROMPT));
00875         }
00876     } else {
00877         code = Tcl_EvalObjEx(interp, promptCmdPtr, TCL_EVAL_GLOBAL);
00878         if (code != TCL_OK) {
00879             Tcl_AddErrorInfo(interp,
00880                     "\n    (script that generates prompt)");
00881             errChannel = Tcl_GetStdChannel(TCL_STDERR);
00882             if (errChannel != (Tcl_Channel) NULL) {
00883                 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
00884                 Tcl_WriteChars(errChannel, "\n", 1);
00885             }
00886             goto defaultPrompt;
00887         }
00888     }
00889 
00890     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
00891     if (outChannel != (Tcl_Channel) NULL) {
00892         Tcl_Flush(outChannel);
00893     }
00894     *promptPtr = PROMPT_NONE;
00895 }
00896 
00897 /*
00898  * Local Variables:
00899  * mode: c
00900  * c-basic-offset: 4
00901  * fill-column: 78
00902  * End:
00903  */



Generated on Wed Mar 12 12:18:19 2008 by  doxygen 1.5.1