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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclMain.c --
  3.  *
  4.  * Main program for Tcl shells and other Tcl-based applications.
  5.  *
  6.  * Copyright (c) 1988-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  8.  * Copyright (c) 2000 Ajuba Solutions.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * RCS: @(#) $Id: tclMain.c,v 1.20.2.3 2006/05/05 18:08:58 dgp Exp $
  14.  */
  15. #include "tcl.h"
  16. #include "tclInt.h"
  17. # undef TCL_STORAGE_CLASS
  18. # define TCL_STORAGE_CLASS DLLEXPORT
  19. /*
  20.  * Declarations for various library procedures and variables (don't want
  21.  * to include tclPort.h here, because people might copy this file out of
  22.  * the Tcl source directory to make their own modified versions).
  23.  */
  24. #if !defined(MAC_TCL)
  25. extern int isatty _ANSI_ARGS_((int fd));
  26. #else
  27. #include <unistd.h>
  28. #endif
  29. static Tcl_Obj *tclStartupScriptPath = NULL;
  30. static Tcl_MainLoopProc *mainLoopProc = NULL;
  31. /* 
  32.  * Structure definition for information used to keep the state of
  33.  * an interactive command processor that reads lines from standard
  34.  * input and writes prompts and results to standard output.
  35.  */
  36. typedef enum {
  37.     PROMPT_NONE, /* Print no prompt */
  38.     PROMPT_START, /* Print prompt for command start */
  39.     PROMPT_CONTINUE /* Print prompt for command continuation */
  40. } PromptType;
  41. typedef struct InteractiveState {
  42.     Tcl_Channel input; /* The standard input channel from which
  43.  * lines are read. */
  44.     int tty;                    /* Non-zero means standard input is a 
  45.  * terminal-like device.  Zero means it's
  46.  * a file. */
  47.     Tcl_Obj *commandPtr; /* Used to assemble lines of input into
  48.  * Tcl commands. */
  49.     PromptType prompt; /* Next prompt to print */
  50.     Tcl_Interp *interp; /* Interpreter that evaluates interactive
  51.  * commands. */
  52. } InteractiveState;
  53. /*
  54.  * Forward declarations for procedures defined later in this file.
  55.  */
  56. static void Prompt _ANSI_ARGS_((Tcl_Interp *interp,
  57.     PromptType *promptPtr));
  58. static void StdinProc _ANSI_ARGS_((ClientData clientData,
  59.     int mask));
  60. /*
  61.  *----------------------------------------------------------------------
  62.  *
  63.  * TclSetStartupScriptPath --
  64.  *
  65.  * Primes the startup script VFS path, used to override the
  66.  *      command line processing.
  67.  *
  68.  * Results:
  69.  * None. 
  70.  *
  71.  * Side effects:
  72.  * This procedure initializes the VFS path of the Tcl script to
  73.  *      run at startup.
  74.  *
  75.  *----------------------------------------------------------------------
  76.  */
  77. void TclSetStartupScriptPath(pathPtr)
  78.     Tcl_Obj *pathPtr;
  79. {
  80.     if (tclStartupScriptPath != NULL) {
  81. Tcl_DecrRefCount(tclStartupScriptPath);
  82.     }
  83.     tclStartupScriptPath = pathPtr;
  84.     if (tclStartupScriptPath != NULL) {
  85. Tcl_IncrRefCount(tclStartupScriptPath);
  86.     }
  87. }
  88. /*
  89.  *----------------------------------------------------------------------
  90.  *
  91.  * TclGetStartupScriptPath --
  92.  *
  93.  * Gets the startup script VFS path, used to override the
  94.  *      command line processing.
  95.  *
  96.  * Results:
  97.  * The startup script VFS path, NULL if none has been set.
  98.  *
  99.  * Side effects:
  100.  * None.
  101.  *
  102.  *----------------------------------------------------------------------
  103.  */
  104. Tcl_Obj *TclGetStartupScriptPath()
  105. {
  106.     return tclStartupScriptPath;
  107. }
  108. /*
  109.  *----------------------------------------------------------------------
  110.  *
  111.  * TclSetStartupScriptFileName --
  112.  *
  113.  * Primes the startup script file name, used to override the
  114.  *      command line processing.
  115.  *
  116.  * Results:
  117.  * None. 
  118.  *
  119.  * Side effects:
  120.  * This procedure initializes the file name of the Tcl script to
  121.  *      run at startup.
  122.  *
  123.  *----------------------------------------------------------------------
  124.  */
  125. void TclSetStartupScriptFileName(fileName)
  126.     CONST char *fileName;
  127. {
  128.     Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1);
  129.     TclSetStartupScriptPath(pathPtr);
  130. }
  131. /*
  132.  *----------------------------------------------------------------------
  133.  *
  134.  * TclGetStartupScriptFileName --
  135.  *
  136.  * Gets the startup script file name, used to override the
  137.  *      command line processing.
  138.  *
  139.  * Results:
  140.  * The startup script file name, NULL if none has been set.
  141.  *
  142.  * Side effects:
  143.  * None.
  144.  *
  145.  *----------------------------------------------------------------------
  146.  */
  147. CONST char *TclGetStartupScriptFileName()
  148. {
  149.     Tcl_Obj *pathPtr = TclGetStartupScriptPath();
  150.     if (pathPtr == NULL) {
  151. return NULL;
  152.     }
  153.     return Tcl_GetString(pathPtr);
  154. }
  155. /*
  156.  *----------------------------------------------------------------------
  157.  *
  158.  * Tcl_Main --
  159.  *
  160.  * Main program for tclsh and most other Tcl-based applications.
  161.  *
  162.  * Results:
  163.  * None. This procedure never returns (it exits the process when
  164.  * it's done).
  165.  *
  166.  * Side effects:
  167.  * This procedure initializes the Tcl world and then starts
  168.  * interpreting commands;  almost anything could happen, depending
  169.  * on the script being interpreted.
  170.  *
  171.  *----------------------------------------------------------------------
  172.  */
  173. void
  174. Tcl_Main(argc, argv, appInitProc)
  175.     int argc; /* Number of arguments. */
  176.     char **argv; /* Array of argument strings. */
  177.     Tcl_AppInitProc *appInitProc;
  178. /* Application-specific initialization
  179.  * procedure to call after most
  180.  * initialization but before starting to
  181.  * execute commands. */
  182. {
  183.     Tcl_Obj *resultPtr, *argvPtr, *commandPtr = NULL;
  184.     PromptType prompt = PROMPT_START;
  185.     int code, length, tty, exitCode = 0;
  186.     Tcl_Channel inChannel, outChannel, errChannel;
  187.     Tcl_Interp *interp;
  188.     Tcl_DString appName;
  189.     Tcl_Obj *objPtr;
  190.     Tcl_FindExecutable(argv[0]);
  191.     interp = Tcl_CreateInterp();
  192.     Tcl_InitMemory(interp);
  193.     /*
  194.      * Make command-line arguments available in the Tcl variables "argc"
  195.      * and "argv".  If the first argument doesn't start with a "-" then
  196.      * strip it off and use it as the name of a script file to process.
  197.      */
  198.     if (TclGetStartupScriptPath() == NULL) {
  199. if ((argc > 1) && (argv[1][0] != '-')) {
  200.     TclSetStartupScriptFileName(argv[1]);
  201.     argc--;
  202.     argv++;
  203. }
  204.     }
  205.     if (TclGetStartupScriptPath() == NULL) {
  206. Tcl_ExternalToUtfDString(NULL, argv[0], -1, &appName);
  207.     } else {
  208. TclSetStartupScriptFileName(Tcl_ExternalToUtfDString(NULL,
  209. TclGetStartupScriptFileName(), -1, &appName));
  210.     }
  211.     Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&appName), TCL_GLOBAL_ONLY);
  212.     Tcl_DStringFree(&appName);
  213.     argc--;
  214.     argv++;
  215.     objPtr = Tcl_NewIntObj(argc);
  216.     Tcl_IncrRefCount(objPtr);
  217.     Tcl_SetVar2Ex(interp, "argc", NULL, objPtr, TCL_GLOBAL_ONLY);
  218.     Tcl_DecrRefCount(objPtr);
  219.     
  220.     argvPtr = Tcl_NewListObj(0, NULL);
  221.     while (argc--) {
  222. Tcl_DString ds;
  223. Tcl_ExternalToUtfDString(NULL, *argv++, -1, &ds);
  224. Tcl_ListObjAppendElement(NULL, argvPtr, Tcl_NewStringObj(
  225. Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)));
  226. Tcl_DStringFree(&ds);
  227.     }
  228.     Tcl_IncrRefCount(argvPtr);
  229.     Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY);
  230.     Tcl_DecrRefCount(argvPtr);
  231.     /*
  232.      * Set the "tcl_interactive" variable.
  233.      */
  234.     tty = isatty(0);
  235.     Tcl_SetVar(interp, "tcl_interactive",
  236.     ((TclGetStartupScriptPath() == NULL) && tty) ? "1" : "0",
  237.     TCL_GLOBAL_ONLY);
  238.     
  239.     /*
  240.      * Invoke application-specific initialization.
  241.      */
  242.     Tcl_Preserve((ClientData) interp);
  243.     if ((*appInitProc)(interp) != TCL_OK) {
  244. errChannel = Tcl_GetStdChannel(TCL_STDERR);
  245. if (errChannel) {
  246.     Tcl_WriteChars(errChannel,
  247.     "application-specific initialization failed: ", -1);
  248.     Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
  249.     Tcl_WriteChars(errChannel, "n", 1);
  250. }
  251.     }
  252.     if (Tcl_InterpDeleted(interp)) {
  253. goto done;
  254.     }
  255.     /*
  256.      * If a script file was specified then just source that file
  257.      * and quit.
  258.      */
  259.     if (TclGetStartupScriptPath() != NULL) {
  260. code = Tcl_FSEvalFile(interp, TclGetStartupScriptPath());
  261. if (code != TCL_OK) {
  262.     errChannel = Tcl_GetStdChannel(TCL_STDERR);
  263.     if (errChannel) {
  264. /*
  265.  * The following statement guarantees that the errorInfo
  266.  * variable is set properly.
  267.  */
  268. Tcl_AddErrorInfo(interp, "");
  269. Tcl_WriteObj(errChannel, Tcl_GetVar2Ex(interp, "errorInfo",
  270. NULL, TCL_GLOBAL_ONLY));
  271. Tcl_WriteChars(errChannel, "n", 1);
  272.     }
  273.     exitCode = 1;
  274. }
  275. goto done;
  276.     }
  277.     /*
  278.      * We're running interactively.  Source a user-specific startup
  279.      * file if the application specified one and if the file exists.
  280.      */
  281.     Tcl_SourceRCFile(interp);
  282.     /*
  283.      * Process commands from stdin until there's an end-of-file.  Note
  284.      * that we need to fetch the standard channels again after every
  285.      * eval, since they may have been changed.
  286.      */
  287.     commandPtr = Tcl_NewObj();
  288.     Tcl_IncrRefCount(commandPtr);
  289.     /*
  290.      * Get a new value for tty if anyone writes to ::tcl_interactive
  291.      */
  292.     Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, TCL_LINK_BOOLEAN);
  293.     inChannel = Tcl_GetStdChannel(TCL_STDIN);
  294.     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
  295.     while ((inChannel != (Tcl_Channel) NULL) && !Tcl_InterpDeleted(interp)) {
  296. if (mainLoopProc == NULL) {
  297.     if (tty) {
  298. Prompt(interp, &prompt);
  299. if (Tcl_InterpDeleted(interp)) {
  300.     break;
  301. }
  302. inChannel = Tcl_GetStdChannel(TCL_STDIN);
  303. if (inChannel == (Tcl_Channel) NULL) {
  304.             break;
  305. }
  306.     }
  307.     if (Tcl_IsShared(commandPtr)) {
  308. Tcl_DecrRefCount(commandPtr);
  309. commandPtr = Tcl_DuplicateObj(commandPtr);
  310. Tcl_IncrRefCount(commandPtr);
  311.     }
  312.             length = Tcl_GetsObj(inChannel, commandPtr);
  313.     if (length < 0) {
  314. if (Tcl_InputBlocked(inChannel)) {
  315.     /*
  316.      * This can only happen if stdin has been set to
  317.      * non-blocking.  In that case cycle back and try
  318.      * again.  This sets up a tight polling loop (since
  319.      * we have no event loop running).  If this causes
  320.      * bad CPU hogging, we might try toggling the blocking
  321.      * on stdin instead.
  322.      */
  323.     continue;
  324. }
  325. /* 
  326.  * Either EOF, or an error on stdin; we're done
  327.  */
  328. break;
  329.     }
  330.             /*
  331.              * Add the newline removed by Tcl_GetsObj back to the string.
  332.              */
  333.     if (Tcl_IsShared(commandPtr)) {
  334. Tcl_DecrRefCount(commandPtr);
  335. commandPtr = Tcl_DuplicateObj(commandPtr);
  336. Tcl_IncrRefCount(commandPtr);
  337.     }
  338.     Tcl_AppendToObj(commandPtr, "n", 1);
  339.     if (!TclObjCommandComplete(commandPtr)) {
  340. prompt = PROMPT_CONTINUE;
  341. continue;
  342.     }
  343.     prompt = PROMPT_START;
  344.     code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
  345.     inChannel = Tcl_GetStdChannel(TCL_STDIN);
  346.     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
  347.     errChannel = Tcl_GetStdChannel(TCL_STDERR);
  348.     Tcl_DecrRefCount(commandPtr);
  349.     commandPtr = Tcl_NewObj();
  350.     Tcl_IncrRefCount(commandPtr);
  351.     if (code != TCL_OK) {
  352. if (errChannel) {
  353.     Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
  354.     Tcl_WriteChars(errChannel, "n", 1);
  355. }
  356.     } else if (tty) {
  357. resultPtr = Tcl_GetObjResult(interp);
  358. Tcl_IncrRefCount(resultPtr);
  359. Tcl_GetStringFromObj(resultPtr, &length);
  360. if ((length > 0) && outChannel) {
  361.     Tcl_WriteObj(outChannel, resultPtr);
  362.     Tcl_WriteChars(outChannel, "n", 1);
  363. }
  364. Tcl_DecrRefCount(resultPtr);
  365.     }
  366. } else { /* (mainLoopProc != NULL) */
  367.     /*
  368.      * If a main loop has been defined while running interactively,
  369.      * we want to start a fileevent based prompt by establishing a
  370.      * channel handler for stdin.
  371.      */
  372.     InteractiveState *isPtr = NULL;
  373.     if (inChannel) {
  374.         if (tty) {
  375.     Prompt(interp, &prompt);
  376.         }
  377. isPtr = (InteractiveState *) 
  378. ckalloc((int) sizeof(InteractiveState));
  379. isPtr->input = inChannel;
  380. isPtr->tty = tty;
  381. isPtr->commandPtr = commandPtr;
  382. isPtr->prompt = prompt;
  383. isPtr->interp = interp;
  384. Tcl_UnlinkVar(interp, "tcl_interactive");
  385. Tcl_LinkVar(interp, "tcl_interactive", (char *) &(isPtr->tty),
  386. TCL_LINK_BOOLEAN);
  387. Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
  388. (ClientData) isPtr);
  389.     }
  390.     (*mainLoopProc)();
  391.     mainLoopProc = NULL;
  392.     if (inChannel) {
  393. tty = isPtr->tty;
  394. Tcl_UnlinkVar(interp, "tcl_interactive");
  395. Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty,
  396. TCL_LINK_BOOLEAN);
  397. prompt = isPtr->prompt;
  398. commandPtr = isPtr->commandPtr;
  399. if (isPtr->input != (Tcl_Channel) NULL) {
  400.     Tcl_DeleteChannelHandler(isPtr->input, StdinProc,
  401.     (ClientData) isPtr);
  402. }
  403. ckfree((char *)isPtr);
  404.     }
  405.     inChannel = Tcl_GetStdChannel(TCL_STDIN);
  406.     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
  407.     errChannel = Tcl_GetStdChannel(TCL_STDERR);
  408. }
  409. #ifdef TCL_MEM_DEBUG
  410. /*
  411.  * This code here only for the (unsupported and deprecated)
  412.  * [checkmem] command.
  413.  */
  414. if (tclMemDumpFileName != NULL) {
  415.     mainLoopProc = NULL;
  416.     Tcl_DeleteInterp(interp);
  417. }
  418. #endif
  419.     }
  420.     done:
  421.     if ((exitCode == 0) && (mainLoopProc != NULL)) {
  422. /*
  423.  * If everything has gone OK so far, call the main loop proc,
  424.  * if it exists.  Packages (like Tk) can set it to start processing
  425.  * events at this point.
  426.  */
  427. (*mainLoopProc)();
  428. mainLoopProc = NULL;
  429.     }
  430.     if (commandPtr != NULL) {
  431. Tcl_DecrRefCount(commandPtr);
  432.     }
  433.     /*
  434.      * Rather than calling exit, invoke the "exit" command so that
  435.      * users can replace "exit" with some other command to do additional
  436.      * cleanup on exit.  The Tcl_Eval call should never return.
  437.      */
  438.     if (!Tcl_InterpDeleted(interp)) {
  439. char buffer[TCL_INTEGER_SPACE + 5];
  440.         sprintf(buffer, "exit %d", exitCode);
  441.         Tcl_Eval(interp, buffer);
  442.         /*
  443.          * If Tcl_Eval returns, trying to eval [exit], something
  444.          * unusual is happening.  Maybe interp has been deleted;
  445.          * maybe [exit] was redefined.  We still want to cleanup
  446.          * and exit.
  447.          */
  448.         if (!Tcl_InterpDeleted(interp)) {
  449.             Tcl_DeleteInterp(interp);
  450.         }
  451.     }
  452.     TclSetStartupScriptPath(NULL);
  453.     /*
  454.      * If we get here, the master interp has been deleted.  Allow
  455.      * its destruction with the last matching Tcl_Release.
  456.      */
  457.     Tcl_Release((ClientData) interp);
  458.     Tcl_Exit(exitCode);
  459. }
  460. /*
  461.  *---------------------------------------------------------------
  462.  *
  463.  * Tcl_SetMainLoop --
  464.  *
  465.  * Sets an alternative main loop procedure.
  466.  *
  467.  * Results:
  468.  * Returns the previously defined main loop procedure.
  469.  *
  470.  * Side effects:
  471.  * This procedure will be called before Tcl exits, allowing for
  472.  * the creation of an event loop.
  473.  *
  474.  *---------------------------------------------------------------
  475.  */
  476. void
  477. Tcl_SetMainLoop(proc)
  478.     Tcl_MainLoopProc *proc;
  479. {
  480.     mainLoopProc = proc;
  481. }
  482. /*
  483.  *----------------------------------------------------------------------
  484.  *
  485.  * StdinProc --
  486.  *
  487.  * This procedure is invoked by the event dispatcher whenever
  488.  * standard input becomes readable.  It grabs the next line of
  489.  * input characters, adds them to a command being assembled, and
  490.  * executes the command if it's complete.
  491.  *
  492.  * Results:
  493.  * None.
  494.  *
  495.  * Side effects:
  496.  * Could be almost arbitrary, depending on the command that's
  497.  * typed.
  498.  *
  499.  *----------------------------------------------------------------------
  500.  */
  501.     /* ARGSUSED */
  502. static void
  503. StdinProc(clientData, mask)
  504.     ClientData clientData; /* The state of interactive cmd line */
  505.     int mask; /* Not used. */
  506. {
  507.     InteractiveState *isPtr = (InteractiveState *) clientData;
  508.     Tcl_Channel chan = isPtr->input;
  509.     Tcl_Obj *commandPtr = isPtr->commandPtr;
  510.     Tcl_Interp *interp = isPtr->interp;
  511.     int code, length;
  512.     if (Tcl_IsShared(commandPtr)) {
  513. Tcl_DecrRefCount(commandPtr);
  514. commandPtr = Tcl_DuplicateObj(commandPtr);
  515. Tcl_IncrRefCount(commandPtr);
  516.     }
  517.     length = Tcl_GetsObj(chan, commandPtr);
  518.     if (length < 0) {
  519. if (Tcl_InputBlocked(chan)) {
  520.     return;
  521. }
  522. if (isPtr->tty) {
  523.     /*
  524.      * Would be better to find a way to exit the mainLoop?
  525.      * Or perhaps evaluate [exit]?  Leaving as is for now due
  526.      * to compatibility concerns.
  527.      */
  528.     Tcl_Exit(0);
  529. }
  530. Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) isPtr);
  531. return;
  532.     }
  533.     if (Tcl_IsShared(commandPtr)) {
  534. Tcl_DecrRefCount(commandPtr);
  535. commandPtr = Tcl_DuplicateObj(commandPtr);
  536. Tcl_IncrRefCount(commandPtr);
  537.     }
  538.     Tcl_AppendToObj(commandPtr, "n", 1);
  539.     if (!TclObjCommandComplete(commandPtr)) {
  540.         isPtr->prompt = PROMPT_CONTINUE;
  541.         goto prompt;
  542.     }
  543.     isPtr->prompt = PROMPT_START;
  544.     /*
  545.      * Disable the stdin channel handler while evaluating the command;
  546.      * otherwise if the command re-enters the event loop we might
  547.      * process commands from stdin before the current command is
  548.      * finished.  Among other things, this will trash the text of the
  549.      * command being evaluated.
  550.      */
  551.     Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) isPtr);
  552.     code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
  553.     isPtr->input = chan = Tcl_GetStdChannel(TCL_STDIN);
  554.     Tcl_DecrRefCount(commandPtr);
  555.     isPtr->commandPtr = commandPtr = Tcl_NewObj();
  556.     Tcl_IncrRefCount(commandPtr);
  557.     if (chan != (Tcl_Channel) NULL) {
  558. Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
  559. (ClientData) isPtr);
  560.     }
  561.     if (code != TCL_OK) {
  562. Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR);
  563. if (errChannel != (Tcl_Channel) NULL) {
  564.     Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
  565.     Tcl_WriteChars(errChannel, "n", 1);
  566. }
  567.     } else if (isPtr->tty) {
  568. Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
  569. Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT);
  570. Tcl_IncrRefCount(resultPtr);
  571. Tcl_GetStringFromObj(resultPtr, &length);
  572. if ((length >0) && (outChannel != (Tcl_Channel) NULL)) {
  573.     Tcl_WriteObj(outChannel, resultPtr);
  574.     Tcl_WriteChars(outChannel, "n", 1);
  575. }
  576. Tcl_DecrRefCount(resultPtr);
  577.     }
  578.     /*
  579.      * If a tty stdin is still around, output a prompt.
  580.      */
  581.     prompt:
  582.     if (isPtr->tty && (isPtr->input != (Tcl_Channel) NULL)) {
  583. Prompt(interp, &(isPtr->prompt));
  584. isPtr->input = Tcl_GetStdChannel(TCL_STDIN);
  585.     }
  586. }
  587. /*
  588.  *----------------------------------------------------------------------
  589.  *
  590.  * Prompt --
  591.  *
  592.  * Issue a prompt on standard output, or invoke a script
  593.  * to issue the prompt.
  594.  *
  595.  * Results:
  596.  * None.
  597.  *
  598.  * Side effects:
  599.  * A prompt gets output, and a Tcl script may be evaluated
  600.  * in interp.
  601.  *
  602.  *----------------------------------------------------------------------
  603.  */
  604. static void
  605. Prompt(interp, promptPtr)
  606.     Tcl_Interp *interp; /* Interpreter to use for prompting. */
  607.     PromptType *promptPtr; /* Points to type of prompt to print.
  608.  * Filled with PROMPT_NONE after a
  609.  * prompt is printed. */
  610. {
  611.     Tcl_Obj *promptCmdPtr;
  612.     int code;
  613.     Tcl_Channel outChannel, errChannel;
  614.     if (*promptPtr == PROMPT_NONE) {
  615. return;
  616.     }
  617.     promptCmdPtr = Tcl_GetVar2Ex(interp,
  618.     ((*promptPtr == PROMPT_CONTINUE) ? "tcl_prompt2" : "tcl_prompt1"),
  619.     NULL, TCL_GLOBAL_ONLY);
  620.     if (Tcl_InterpDeleted(interp)) {
  621. return;
  622.     }
  623.     if (promptCmdPtr == NULL) {
  624. defaultPrompt:
  625. outChannel = Tcl_GetStdChannel(TCL_STDOUT);
  626. if ((*promptPtr == PROMPT_START)
  627. && (outChannel != (Tcl_Channel) NULL)) {
  628.     Tcl_WriteChars(outChannel, "% ", 2);
  629. }
  630.     } else {
  631. code = Tcl_EvalObjEx(interp, promptCmdPtr, TCL_EVAL_GLOBAL);
  632. if (code != TCL_OK) {
  633.     Tcl_AddErrorInfo(interp,
  634.     "n    (script that generates prompt)");
  635.     errChannel = Tcl_GetStdChannel(TCL_STDERR);
  636.             if (errChannel != (Tcl_Channel) NULL) {
  637.                 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
  638.                 Tcl_WriteChars(errChannel, "n", 1);
  639.             }
  640.     goto defaultPrompt;
  641. }
  642.     }
  643.     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
  644.     if (outChannel != (Tcl_Channel) NULL) {
  645. Tcl_Flush(outChannel);
  646.     }
  647.     *promptPtr = PROMPT_NONE;
  648. }