summaryrefslogtreecommitdiffstats
path: root/generic/tclMain.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclMain.c')
-rw-r--r--generic/tclMain.c974
1 files changed, 375 insertions, 599 deletions
diff --git a/generic/tclMain.c b/generic/tclMain.c
index 88b4e51..8252170 100644
--- a/generic/tclMain.c
+++ b/generic/tclMain.c
@@ -1,429 +1,309 @@
-/*
+/*
* tclMain.c --
*
* Main program for Tcl shells and other Tcl-based applications.
- * This file contains a generic main program for Tcl shells and other
- * Tcl-based applications. It can be used as-is for many applications,
- * just by supplying a different appInitProc function for each specific
- * application. Or, it can be used as a template for creating new main
- * programs for Tcl applications.
*
* Copyright (c) 1988-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
* Copyright (c) 2000 Ajuba Solutions.
*
- * See the file "license.terms" for information on usage and redistribution of
- * this file, and for a DISCLAIMER OF ALL WARRANTIES.
- */
-
-/**
- * On Windows, this file needs to be compiled twice, once with
- * TCL_ASCII_MAIN defined. This way both Tcl_Main and Tcl_MainExW
- * can be implemented, sharing the same source code.
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
-#if defined(TCL_ASCII_MAIN)
-# ifdef UNICODE
-# undef UNICODE
-# undef _UNICODE
-# else
-# define UNICODE
-# define _UNICODE
-# endif
-#endif
+#include "tcl.h"
#include "tclInt.h"
-/*
- * The default prompt used when the user has not overridden it.
- */
-
-#define DEFAULT_PRIMARY_PROMPT "% "
+# undef TCL_STORAGE_CLASS
+# define TCL_STORAGE_CLASS DLLEXPORT
/*
- * This file can be compiled on Windows in UNICODE mode, as well as
- * on all other platforms using the native encoding. This is done
- * by using the normal Windows functions like _tcscmp, but on
- * platforms which don't have <tchar.h> we have to translate that
- * to strcmp here.
+ * Declarations for various library procedures and variables (don't want
+ * to include tclPort.h here, because people might copy this file out of
+ * the Tcl source directory to make their own modified versions).
*/
-#ifndef __WIN32__
-# define TCHAR char
-# define TEXT(arg) arg
-# define _tcscmp strcmp
-#endif
-
-/*
- * Further on, in UNICODE mode we just use Tcl_NewUnicodeObj, otherwise
- * NewNativeObj is needed (which provides proper conversion from native
- * encoding to UTF-8).
- */
-
-#ifdef UNICODE
-# define NewNativeObj Tcl_NewUnicodeObj
-#else /* !UNICODE */
-static inline Tcl_Obj *
-NewNativeObj(
- char *string,
- int length)
-{
- Tcl_DString ds;
- Tcl_ExternalToUtfDString(NULL, string, length, &ds);
- return TclDStringToObj(&ds);
-}
-#endif /* !UNICODE */
-
-/*
- * Declarations for various library functions and variables (don't want to
- * include tclPort.h here, because people might copy this file out of the Tcl
- * source directory to make their own modified versions).
- */
-
-extern CRTIMPORT int isatty(int fd);
+#if !defined(MAC_TCL)
+extern int isatty _ANSI_ARGS_((int fd));
+#else
+#include <unistd.h>
+#endif
-/*
- * The thread-local variables for this file's functions.
- */
+static Tcl_Obj *tclStartupScriptPath = NULL;
-typedef struct {
- Tcl_Obj *path; /* The filename of the script for *_Main()
- * routines to [source] as a startup script,
- * or NULL for none set, meaning enter
- * interactive mode. */
- Tcl_Obj *encoding; /* The encoding of the startup script file. */
- Tcl_MainLoopProc *mainLoopProc;
- /* Any installed main loop handler. The main
- * extension that installs these is Tk. */
-} ThreadSpecificData;
+static Tcl_MainLoopProc *mainLoopProc = NULL;
-/*
- * Structure definition for information used to keep the state of an
- * interactive command processor that reads lines from standard input and
- * writes prompts and results to standard output.
+/*
+ * Structure definition for information used to keep the state of
+ * an interactive command processor that reads lines from standard
+ * input and writes prompts and results to standard output.
*/
typedef enum {
- PROMPT_NONE, /* Print no prompt */
- PROMPT_START, /* Print prompt for command start */
- PROMPT_CONTINUE /* Print prompt for command continuation */
+ PROMPT_NONE, /* Print no prompt */
+ PROMPT_START, /* Print prompt for command start */
+ PROMPT_CONTINUE /* Print prompt for command continuation */
} PromptType;
typedef struct InteractiveState {
- Tcl_Channel input; /* The standard input channel from which lines
- * are read. */
- int tty; /* Non-zero means standard input is a
- * terminal-like device. Zero means it's a
- * file. */
- Tcl_Obj *commandPtr; /* Used to assemble lines of input into Tcl
- * commands. */
+ Tcl_Channel input; /* The standard input channel from which
+ * lines are read. */
+ int tty; /* Non-zero means standard input is a
+ * terminal-like device. Zero means it's
+ * a file. */
+ Tcl_Obj *commandPtr; /* Used to assemble lines of input into
+ * Tcl commands. */
PromptType prompt; /* Next prompt to print */
Tcl_Interp *interp; /* Interpreter that evaluates interactive
* commands. */
} InteractiveState;
/*
- * Forward declarations for functions defined later in this file.
+ * Forward declarations for procedures defined later in this file.
*/
-MODULE_SCOPE Tcl_MainLoopProc *TclGetMainLoop(void);
-static void Prompt(Tcl_Interp *interp, InteractiveState *isPtr);
-static void StdinProc(ClientData clientData, int mask);
-static void FreeMainInterp(ClientData clientData);
+static void Prompt _ANSI_ARGS_((Tcl_Interp *interp,
+ PromptType *promptPtr));
+static void StdinProc _ANSI_ARGS_((ClientData clientData,
+ int mask));
+
-#ifndef TCL_ASCII_MAIN
-static Tcl_ThreadDataKey dataKey;
/*
*----------------------------------------------------------------------
*
- * Tcl_SetStartupScript --
+ * TclSetStartupScriptPath --
*
- * Sets the path and encoding of the startup script to be evaluated by
- * Tcl_Main, used to override the command line processing.
+ * Primes the startup script VFS path, used to override the
+ * command line processing.
*
* Results:
- * None.
+ * None.
*
* Side effects:
+ * This procedure initializes the VFS path of the Tcl script to
+ * run at startup.
*
*----------------------------------------------------------------------
*/
-
-void
-Tcl_SetStartupScript(
- Tcl_Obj *path, /* Filesystem path of startup script file */
- const char *encoding) /* Encoding of the data in that file */
+void TclSetStartupScriptPath(pathPtr)
+ Tcl_Obj *pathPtr;
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- Tcl_Obj *newEncoding = NULL;
-
- if (encoding != NULL) {
- newEncoding = Tcl_NewStringObj(encoding, -1);
- }
-
- if (tsdPtr->path != NULL) {
- Tcl_DecrRefCount(tsdPtr->path);
+ if (tclStartupScriptPath != NULL) {
+ Tcl_DecrRefCount(tclStartupScriptPath);
}
- tsdPtr->path = path;
- if (tsdPtr->path != NULL) {
- Tcl_IncrRefCount(tsdPtr->path);
- }
-
- if (tsdPtr->encoding != NULL) {
- Tcl_DecrRefCount(tsdPtr->encoding);
- }
- tsdPtr->encoding = newEncoding;
- if (tsdPtr->encoding != NULL) {
- Tcl_IncrRefCount(tsdPtr->encoding);
+ tclStartupScriptPath = pathPtr;
+ if (tclStartupScriptPath != NULL) {
+ Tcl_IncrRefCount(tclStartupScriptPath);
}
}
-
+
+
/*
*----------------------------------------------------------------------
*
- * Tcl_GetStartupScript --
+ * TclGetStartupScriptPath --
*
- * Gets the path and encoding of the startup script to be evaluated by
- * Tcl_Main.
+ * Gets the startup script VFS path, used to override the
+ * command line processing.
*
* Results:
- * The path of the startup script; NULL if none has been set.
+ * The startup script VFS path, NULL if none has been set.
*
* Side effects:
- * If encodingPtr is not NULL, stores a (const char *) in it pointing to
- * the encoding name registered for the startup script. Tcl retains
- * ownership of the string, and may free it. Caller should make a copy
- * for long-term use.
+ * None.
*
*----------------------------------------------------------------------
*/
-
-Tcl_Obj *
-Tcl_GetStartupScript(
- const char **encodingPtr) /* When not NULL, points to storage for the
- * (const char *) that points to the
- * registered encoding name for the startup
- * script. */
+Tcl_Obj *TclGetStartupScriptPath()
{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ return tclStartupScriptPath;
+}
- if (encodingPtr != NULL) {
- if (tsdPtr->encoding == NULL) {
- *encodingPtr = NULL;
- } else {
- *encodingPtr = Tcl_GetString(tsdPtr->encoding);
- }
- }
- return tsdPtr->path;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSetStartupScriptFileName --
+ *
+ * Primes the startup script file name, used to override the
+ * command line processing.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * This procedure initializes the file name of the Tcl script to
+ * run at startup.
+ *
+ *----------------------------------------------------------------------
+ */
+void TclSetStartupScriptFileName(fileName)
+ CONST char *fileName;
+{
+ Tcl_Obj *pathPtr = Tcl_NewStringObj(fileName,-1);
+ TclSetStartupScriptPath(pathPtr);
}
+
-/*----------------------------------------------------------------------
+/*
+ *----------------------------------------------------------------------
*
- * Tcl_SourceRCFile --
+ * TclGetStartupScriptFileName --
*
- * This function is typically invoked by Tcl_Main of Tk_Main function to
- * source an application specific rc file into the interpreter at startup
- * time.
+ * Gets the startup script file name, used to override the
+ * command line processing.
*
* Results:
- * None.
+ * The startup script file name, NULL if none has been set.
*
* Side effects:
- * Depends on what's in the rc script.
+ * None.
*
*----------------------------------------------------------------------
*/
-
-void
-Tcl_SourceRCFile(
- Tcl_Interp *interp) /* Interpreter to source rc file into. */
+CONST char *TclGetStartupScriptFileName()
{
- Tcl_DString temp;
- const char *fileName;
- Tcl_Channel chan;
-
- fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY);
- if (fileName != NULL) {
- Tcl_Channel c;
- const char *fullName;
-
- Tcl_DStringInit(&temp);
- fullName = Tcl_TranslateFileName(interp, fileName, &temp);
- if (fullName == NULL) {
- /*
- * Couldn't translate the file name (e.g. it referred to a bogus
- * user or there was no HOME environment variable). Just do
- * nothing.
- */
- } else {
- /*
- * Test for the existence of the rc file before trying to read it.
- */
+ Tcl_Obj *pathPtr = TclGetStartupScriptPath();
- c = Tcl_OpenFileChannel(NULL, fullName, "r", 0);
- if (c != NULL) {
- Tcl_Close(NULL, c);
- if (Tcl_EvalFile(interp, fullName) != TCL_OK) {
- chan = Tcl_GetStdChannel(TCL_STDERR);
- if (chan) {
- Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
- Tcl_WriteChars(chan, "\n", 1);
- }
- }
- }
- }
- Tcl_DStringFree(&temp);
+ if (pathPtr == NULL) {
+ return NULL;
}
+ return Tcl_GetString(pathPtr);
}
-#endif /* !TCL_ASCII_MAIN */
+
+
-/*----------------------------------------------------------------------
+/*
+ *----------------------------------------------------------------------
*
- * Tcl_Main, Tcl_MainEx --
+ * Tcl_Main --
*
* Main program for tclsh and most other Tcl-based applications.
*
* Results:
- * None. This function never returns (it exits the process when it's
- * done).
+ * None. This procedure never returns (it exits the process when
+ * it's done).
*
* Side effects:
- * This function initializes the Tcl world and then starts interpreting
- * commands; almost anything could happen, depending on the script being
- * interpreted.
+ * This procedure initializes the Tcl world and then starts
+ * interpreting commands; almost anything could happen, depending
+ * on the script being interpreted.
*
*----------------------------------------------------------------------
*/
void
-Tcl_MainEx(
- int argc, /* Number of arguments. */
- TCHAR **argv, /* Array of argument strings. */
- Tcl_AppInitProc *appInitProc,
+Tcl_Main(argc, argv, appInitProc)
+ int argc; /* Number of arguments. */
+ char **argv; /* Array of argument strings. */
+ Tcl_AppInitProc *appInitProc;
/* Application-specific initialization
- * function to call after most initialization
- * but before starting to execute commands. */
- Tcl_Interp *interp)
+ * procedure to call after most
+ * initialization but before starting to
+ * execute commands. */
{
- Tcl_Obj *path, *resultPtr, *argvPtr, *appName;
- const char *encodingName = NULL;
- int code, exitCode = 0;
- Tcl_MainLoopProc *mainLoopProc;
- Tcl_Channel chan;
- InteractiveState is;
+ Tcl_Obj *resultPtr, *argvPtr, *commandPtr = NULL;
+ PromptType prompt = PROMPT_START;
+ int code, length, tty, exitCode = 0;
+ Tcl_Channel inChannel, outChannel, errChannel;
+ Tcl_Interp *interp;
+ Tcl_DString appName;
+ Tcl_Obj *objPtr;
- Tcl_InitMemory(interp);
+ Tcl_FindExecutable(argv[0]);
- is.interp = interp;
- is.prompt = PROMPT_START;
- is.commandPtr = Tcl_NewObj();
+ interp = Tcl_CreateInterp();
+ Tcl_InitMemory(interp);
/*
- * If the application has not already set a startup script, parse the
- * first few command line arguments to determine the script path and
- * encoding.
+ * Make command-line arguments available in the Tcl variables "argc"
+ * and "argv". If the first argument doesn't start with a "-" then
+ * strip it off and use it as the name of a script file to process.
*/
- if (NULL == Tcl_GetStartupScript(NULL)) {
- /*
- * Check whether first 3 args (argv[1] - argv[3]) look like
- * -encoding ENCODING FILENAME
- * or like
- * FILENAME
- */
-
- if ((argc > 3) && (0 == _tcscmp(TEXT("-encoding"), argv[1]))
- && (TEXT('-') != argv[3][0])) {
- Tcl_Obj *value = NewNativeObj(argv[2], -1);
- Tcl_SetStartupScript(NewNativeObj(argv[3], -1), Tcl_GetString(value));
- Tcl_DecrRefCount(value);
- argc -= 3;
- argv += 3;
- } else if ((argc > 1) && (TEXT('-') != argv[1][0])) {
- Tcl_SetStartupScript(NewNativeObj(argv[1], -1), NULL);
+ if (TclGetStartupScriptPath() == NULL) {
+ if ((argc > 1) && (argv[1][0] != '-')) {
+ TclSetStartupScriptFileName(argv[1]);
argc--;
argv++;
}
}
- path = Tcl_GetStartupScript(&encodingName);
- if (path == NULL) {
- appName = NewNativeObj(argv[0], -1);
+ if (TclGetStartupScriptPath() == NULL) {
+ Tcl_ExternalToUtfDString(NULL, argv[0], -1, &appName);
} else {
- appName = path;
+ TclSetStartupScriptFileName(Tcl_ExternalToUtfDString(NULL,
+ TclGetStartupScriptFileName(), -1, &appName));
}
- Tcl_SetVar2Ex(interp, "argv0", NULL, appName, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&appName), TCL_GLOBAL_ONLY);
+ Tcl_DStringFree(&appName);
argc--;
argv++;
- Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(argc), TCL_GLOBAL_ONLY);
-
+ objPtr = Tcl_NewIntObj(argc);
+ Tcl_IncrRefCount(objPtr);
+ Tcl_SetVar2Ex(interp, "argc", NULL, objPtr, TCL_GLOBAL_ONLY);
+ Tcl_DecrRefCount(objPtr);
+
argvPtr = Tcl_NewListObj(0, NULL);
while (argc--) {
- Tcl_ListObjAppendElement(NULL, argvPtr, NewNativeObj(*argv++, -1));
+ Tcl_DString ds;
+ Tcl_ExternalToUtfDString(NULL, *argv++, -1, &ds);
+ Tcl_ListObjAppendElement(NULL, argvPtr, Tcl_NewStringObj(
+ Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)));
+ Tcl_DStringFree(&ds);
}
+ Tcl_IncrRefCount(argvPtr);
Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY);
+ Tcl_DecrRefCount(argvPtr);
/*
* Set the "tcl_interactive" variable.
*/
- is.tty = isatty(0);
- Tcl_SetVar2Ex(interp, "tcl_interactive", NULL,
- Tcl_NewIntObj(!path && is.tty), TCL_GLOBAL_ONLY);
-
+ tty = isatty(0);
+ Tcl_SetVar(interp, "tcl_interactive",
+ ((TclGetStartupScriptPath() == NULL) && tty) ? "1" : "0",
+ TCL_GLOBAL_ONLY);
+
/*
* Invoke application-specific initialization.
*/
- Tcl_Preserve(interp);
- if (appInitProc(interp) != TCL_OK) {
- chan = Tcl_GetStdChannel(TCL_STDERR);
- if (chan) {
- Tcl_WriteChars(chan,
+ Tcl_Preserve((ClientData) interp);
+ if ((*appInitProc)(interp) != TCL_OK) {
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel) {
+ Tcl_WriteChars(errChannel,
"application-specific initialization failed: ", -1);
- Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
- Tcl_WriteChars(chan, "\n", 1);
+ Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
+ Tcl_WriteChars(errChannel, "\n", 1);
}
}
if (Tcl_InterpDeleted(interp)) {
goto done;
}
- if (Tcl_LimitExceeded(interp)) {
- goto done;
- }
- if (TclFullFinalizationRequested()) {
- /*
- * Arrange for final deletion of the main interp
- */
- /* ARGH Munchhausen effect */
- Tcl_CreateExitHandler(FreeMainInterp, (ClientData)interp);
- }
/*
- * Invoke the script specified on the command line, if any. Must fetch it
- * again, as the appInitProc might have reset it.
+ * If a script file was specified then just source that file
+ * and quit.
*/
- path = Tcl_GetStartupScript(&encodingName);
- if (path != NULL) {
- Tcl_ResetResult(interp);
- code = Tcl_FSEvalFileEx(interp, path, encodingName);
+ if (TclGetStartupScriptPath() != NULL) {
+ code = Tcl_FSEvalFile(interp, TclGetStartupScriptPath());
if (code != TCL_OK) {
- chan = Tcl_GetStdChannel(TCL_STDERR);
- if (chan) {
- Tcl_Obj *options = Tcl_GetReturnOptions(interp, code);
- Tcl_Obj *keyPtr, *valuePtr;
-
- TclNewLiteralStringObj(keyPtr, "-errorinfo");
- Tcl_IncrRefCount(keyPtr);
- Tcl_DictObjGet(NULL, options, keyPtr, &valuePtr);
- Tcl_DecrRefCount(keyPtr);
-
- if (valuePtr) {
- Tcl_WriteObj(chan, valuePtr);
- }
- Tcl_WriteChars(chan, "\n", 1);
- Tcl_DecrRefCount(options);
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel) {
+
+ /*
+ * The following statement guarantees that the errorInfo
+ * variable is set properly.
+ */
+
+ Tcl_AddErrorInfo(interp, "");
+ Tcl_WriteObj(errChannel, Tcl_GetVar2Ex(interp, "errorInfo",
+ NULL, TCL_GLOBAL_ONLY));
+ Tcl_WriteChars(errChannel, "\n", 1);
}
exitCode = 1;
}
@@ -431,336 +311,272 @@ Tcl_MainEx(
}
/*
- * We're running interactively. Source a user-specific startup file if the
- * application specified one and if the file exists.
+ * We're running interactively. Source a user-specific startup
+ * file if the application specified one and if the file exists.
*/
Tcl_SourceRCFile(interp);
- if (Tcl_LimitExceeded(interp)) {
- goto done;
- }
/*
- * Process commands from stdin until there's an end-of-file. Note that we
- * need to fetch the standard channels again after every eval, since they
- * may have been changed.
+ * Process commands from stdin until there's an end-of-file. Note
+ * that we need to fetch the standard channels again after every
+ * eval, since they may have been changed.
*/
- Tcl_IncrRefCount(is.commandPtr);
+ commandPtr = Tcl_NewObj();
+ Tcl_IncrRefCount(commandPtr);
/*
* Get a new value for tty if anyone writes to ::tcl_interactive
*/
-
- Tcl_LinkVar(interp, "tcl_interactive", (char *) &is.tty, TCL_LINK_BOOLEAN);
- is.input = Tcl_GetStdChannel(TCL_STDIN);
- while ((is.input != NULL) && !Tcl_InterpDeleted(interp)) {
- mainLoopProc = TclGetMainLoop();
+ Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, TCL_LINK_BOOLEAN);
+ inChannel = Tcl_GetStdChannel(TCL_STDIN);
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ while ((inChannel != (Tcl_Channel) NULL) && !Tcl_InterpDeleted(interp)) {
if (mainLoopProc == NULL) {
- int length;
- if (is.tty) {
- Prompt(interp, &is);
+ if (tty) {
+ Prompt(interp, &prompt);
if (Tcl_InterpDeleted(interp)) {
break;
}
- if (Tcl_LimitExceeded(interp)) {
- break;
- }
- is.input = Tcl_GetStdChannel(TCL_STDIN);
- if (is.input == NULL) {
- break;
+ inChannel = Tcl_GetStdChannel(TCL_STDIN);
+ if (inChannel == (Tcl_Channel) NULL) {
+ break;
}
}
- if (Tcl_IsShared(is.commandPtr)) {
- Tcl_DecrRefCount(is.commandPtr);
- is.commandPtr = Tcl_DuplicateObj(is.commandPtr);
- Tcl_IncrRefCount(is.commandPtr);
+ if (Tcl_IsShared(commandPtr)) {
+ Tcl_DecrRefCount(commandPtr);
+ commandPtr = Tcl_DuplicateObj(commandPtr);
+ Tcl_IncrRefCount(commandPtr);
}
- length = Tcl_GetsObj(is.input, is.commandPtr);
+ length = Tcl_GetsObj(inChannel, commandPtr);
if (length < 0) {
- if (Tcl_InputBlocked(is.input)) {
+ if (Tcl_InputBlocked(inChannel)) {
+
/*
* This can only happen if stdin has been set to
- * non-blocking. In that case cycle back and try again.
- * This sets up a tight polling loop (since we have no
- * event loop running). If this causes bad CPU hogging, we
- * might try toggling the blocking on stdin instead.
+ * non-blocking. In that case cycle back and try
+ * again. This sets up a tight polling loop (since
+ * we have no event loop running). If this causes
+ * bad CPU hogging, we might try toggling the blocking
+ * on stdin instead.
*/
continue;
}
- /*
+ /*
* Either EOF, or an error on stdin; we're done
*/
break;
}
- /*
- * Add the newline removed by Tcl_GetsObj back to the string. Have
- * to add it back before testing completeness, because it can make
- * a difference. [Bug 1775878]
- */
+ /*
+ * Add the newline removed by Tcl_GetsObj back to the string.
+ */
- if (Tcl_IsShared(is.commandPtr)) {
- Tcl_DecrRefCount(is.commandPtr);
- is.commandPtr = Tcl_DuplicateObj(is.commandPtr);
- Tcl_IncrRefCount(is.commandPtr);
+ if (Tcl_IsShared(commandPtr)) {
+ Tcl_DecrRefCount(commandPtr);
+ commandPtr = Tcl_DuplicateObj(commandPtr);
+ Tcl_IncrRefCount(commandPtr);
}
- Tcl_AppendToObj(is.commandPtr, "\n", 1);
- if (!TclObjCommandComplete(is.commandPtr)) {
- is.prompt = PROMPT_CONTINUE;
+ Tcl_AppendToObj(commandPtr, "\n", 1);
+ if (!TclObjCommandComplete(commandPtr)) {
+ prompt = PROMPT_CONTINUE;
continue;
}
- is.prompt = PROMPT_START;
-
- /*
- * The final newline is syntactically redundant, and causes some
- * error messages troubles deeper in, so lop it back off.
- */
-
- Tcl_GetStringFromObj(is.commandPtr, &length);
- Tcl_SetObjLength(is.commandPtr, --length);
- code = Tcl_RecordAndEvalObj(interp, is.commandPtr, TCL_EVAL_GLOBAL);
- is.input = Tcl_GetStdChannel(TCL_STDIN);
- Tcl_DecrRefCount(is.commandPtr);
- is.commandPtr = Tcl_NewObj();
- Tcl_IncrRefCount(is.commandPtr);
+ prompt = PROMPT_START;
+ code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
+ inChannel = Tcl_GetStdChannel(TCL_STDIN);
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ Tcl_DecrRefCount(commandPtr);
+ commandPtr = Tcl_NewObj();
+ Tcl_IncrRefCount(commandPtr);
if (code != TCL_OK) {
- chan = Tcl_GetStdChannel(TCL_STDERR);
- if (chan) {
- Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
- Tcl_WriteChars(chan, "\n", 1);
+ if (errChannel) {
+ Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
+ Tcl_WriteChars(errChannel, "\n", 1);
}
- } else if (is.tty) {
+ } else if (tty) {
resultPtr = Tcl_GetObjResult(interp);
Tcl_IncrRefCount(resultPtr);
Tcl_GetStringFromObj(resultPtr, &length);
- chan = Tcl_GetStdChannel(TCL_STDOUT);
- if ((length > 0) && chan) {
- Tcl_WriteObj(chan, resultPtr);
- Tcl_WriteChars(chan, "\n", 1);
+ if ((length > 0) && outChannel) {
+ Tcl_WriteObj(outChannel, resultPtr);
+ Tcl_WriteChars(outChannel, "\n", 1);
}
Tcl_DecrRefCount(resultPtr);
}
} else { /* (mainLoopProc != NULL) */
/*
- * If a main loop has been defined while running interactively, we
- * want to start a fileevent based prompt by establishing a
+ * If a main loop has been defined while running interactively,
+ * we want to start a fileevent based prompt by establishing a
* channel handler for stdin.
*/
- if (is.input) {
- if (is.tty) {
- Prompt(interp, &is);
- }
-
- Tcl_CreateChannelHandler(is.input, TCL_READABLE, StdinProc, &is);
+ InteractiveState *isPtr = NULL;
+
+ if (inChannel) {
+ if (tty) {
+ Prompt(interp, &prompt);
+ }
+ isPtr = (InteractiveState *)
+ ckalloc((int) sizeof(InteractiveState));
+ isPtr->input = inChannel;
+ isPtr->tty = tty;
+ isPtr->commandPtr = commandPtr;
+ isPtr->prompt = prompt;
+ isPtr->interp = interp;
+
+ Tcl_UnlinkVar(interp, "tcl_interactive");
+ Tcl_LinkVar(interp, "tcl_interactive", (char *) &(isPtr->tty),
+ TCL_LINK_BOOLEAN);
+
+ Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
+ (ClientData) isPtr);
}
- mainLoopProc();
- Tcl_SetMainLoop(NULL);
-
- if (is.input) {
- Tcl_DeleteChannelHandler(is.input, StdinProc, &is);
+ (*mainLoopProc)();
+ mainLoopProc = NULL;
+
+ if (inChannel) {
+ tty = isPtr->tty;
+ Tcl_UnlinkVar(interp, "tcl_interactive");
+ Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty,
+ TCL_LINK_BOOLEAN);
+ prompt = isPtr->prompt;
+ commandPtr = isPtr->commandPtr;
+ if (isPtr->input != (Tcl_Channel) NULL) {
+ Tcl_DeleteChannelHandler(isPtr->input, StdinProc,
+ (ClientData) isPtr);
+ }
+ ckfree((char *)isPtr);
}
- is.input = Tcl_GetStdChannel(TCL_STDIN);
+ inChannel = Tcl_GetStdChannel(TCL_STDIN);
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
}
#ifdef TCL_MEM_DEBUG
/*
- * This code here only for the (unsupported and deprecated) [checkmem]
- * command.
+ * This code here only for the (unsupported and deprecated)
+ * [checkmem] command.
*/
if (tclMemDumpFileName != NULL) {
- Tcl_SetMainLoop(NULL);
+ mainLoopProc = NULL;
Tcl_DeleteInterp(interp);
}
#endif
}
- done:
- mainLoopProc = TclGetMainLoop();
- if ((exitCode == 0) && (mainLoopProc != NULL)
- && !Tcl_LimitExceeded(interp)) {
+ done:
+ if ((exitCode == 0) && (mainLoopProc != NULL)) {
+
/*
- * If everything has gone OK so far, call the main loop proc, if it
- * exists. Packages (like Tk) can set it to start processing events at
- * this point.
+ * If everything has gone OK so far, call the main loop proc,
+ * if it exists. Packages (like Tk) can set it to start processing
+ * events at this point.
*/
- mainLoopProc();
- Tcl_SetMainLoop(NULL);
+ (*mainLoopProc)();
+ mainLoopProc = NULL;
}
- if (is.commandPtr != NULL) {
- Tcl_DecrRefCount(is.commandPtr);
+ if (commandPtr != NULL) {
+ Tcl_DecrRefCount(commandPtr);
}
/*
- * Rather than calling exit, invoke the "exit" command so that users can
- * replace "exit" with some other command to do additional cleanup on
- * exit. The Tcl_EvalObjEx call should never return.
+ * Rather than calling exit, invoke the "exit" command so that
+ * users can replace "exit" with some other command to do additional
+ * cleanup on exit. The Tcl_Eval call should never return.
*/
if (!Tcl_InterpDeleted(interp)) {
- if (!Tcl_LimitExceeded(interp)) {
- Tcl_Obj *cmd = Tcl_ObjPrintf("exit %d", exitCode);
-
- Tcl_IncrRefCount(cmd);
- Tcl_EvalObjEx(interp, cmd, TCL_EVAL_GLOBAL);
- Tcl_DecrRefCount(cmd);
- }
- }
- /*
- * If Tcl_EvalObjEx returns, trying to eval [exit], something unusual
- * is happening. Maybe interp has been deleted; maybe [exit] was
- * redefined, maybe we've blown up because of an exceeded limit. We
- * still want to cleanup and exit.
- */
- Tcl_Exit(exitCode);
-}
+ char buffer[TCL_INTEGER_SPACE + 5];
+ sprintf(buffer, "exit %d", exitCode);
+ Tcl_Eval(interp, buffer);
-#if (TCL_MAJOR_VERSION == 8) && !defined(UNICODE)
-#undef Tcl_Main
-extern DLLEXPORT void
-Tcl_Main(
- int argc, /* Number of arguments. */
- char **argv, /* Array of argument strings. */
- Tcl_AppInitProc *appInitProc)
- /* Application-specific initialization
- * function to call after most initialization
- * but before starting to execute commands. */
-{
- Tcl_FindExecutable(argv[0]);
- Tcl_MainEx(argc, argv, appInitProc, Tcl_CreateInterp());
-}
-#endif
-
-#ifndef TCL_ASCII_MAIN
+ /*
+ * If Tcl_Eval returns, trying to eval [exit], something
+ * unusual is happening. Maybe interp has been deleted;
+ * maybe [exit] was redefined. We still want to cleanup
+ * and exit.
+ */
-/*
- *---------------------------------------------------------------
- *
- * Tcl_SetMainLoop --
- *
- * Sets an alternative main loop function.
- *
- * Results:
- * None.
- *
- * Side effects:
- * This function will be called before Tcl exits, allowing for the
- * creation of an event loop.
- *
- *---------------------------------------------------------------
- */
+ if (!Tcl_InterpDeleted(interp)) {
+ Tcl_DeleteInterp(interp);
+ }
+ }
+ TclSetStartupScriptPath(NULL);
-void
-Tcl_SetMainLoop(
- Tcl_MainLoopProc *proc)
-{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+ /*
+ * If we get here, the master interp has been deleted. Allow
+ * its destruction with the last matching Tcl_Release.
+ */
- tsdPtr->mainLoopProc = proc;
+ Tcl_Release((ClientData) interp);
+ Tcl_Exit(exitCode);
}
/*
*---------------------------------------------------------------
*
- * TclGetMainLoop --
+ * Tcl_SetMainLoop --
*
- * Returns the current alternative main loop function.
+ * Sets an alternative main loop procedure.
*
* Results:
- * Returns the previously defined main loop function, or NULL to indicate
- * that no such function has been installed and standard tclsh behaviour
- * (i.e., exit once the script is evaluated if not interactive) is
- * requested..
+ * Returns the previously defined main loop procedure.
*
* Side effects:
- * None (other than possible creation of this file's TSD block).
+ * This procedure will be called before Tcl exits, allowing for
+ * the creation of an event loop.
*
*---------------------------------------------------------------
*/
-Tcl_MainLoopProc *
-TclGetMainLoop(void)
-{
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- return tsdPtr->mainLoopProc;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclFullFinalizationRequested --
- *
- * This function returns true when either -DPURIFY is specified, or the
- * environment variable TCL_FINALIZE_ON_EXIT is set and not "0". This
- * predicate is called at places affecting the exit sequence, so that the
- * default behavior is a fast and deadlock-free exit, and the modified
- * behavior is a more thorough finalization for debugging purposes (leak
- * hunting etc).
- *
- * Results:
- * A boolean.
- *
- *----------------------------------------------------------------------
- */
-MODULE_SCOPE int
-TclFullFinalizationRequested(void)
+void
+Tcl_SetMainLoop(proc)
+ Tcl_MainLoopProc *proc;
{
-#ifdef PURIFY
- return 1;
-#else
- const char *fin;
- Tcl_DString ds;
- int finalize = 0;
-
- fin = TclGetEnv("TCL_FINALIZE_ON_EXIT", &ds);
- finalize = ((fin != NULL) && strcmp(fin, "0"));
- if (fin != NULL) {
- Tcl_DStringFree(&ds);
- }
- return finalize;
-#endif
+ mainLoopProc = proc;
}
-#endif /* !TCL_ASCII_MAIN */
/*
*----------------------------------------------------------------------
*
* StdinProc --
*
- * This function is invoked by the event dispatcher whenever standard
- * input becomes readable. It grabs the next line of input characters,
- * adds them to a command being assembled, and executes the command if
- * it's complete.
+ * This procedure is invoked by the event dispatcher whenever
+ * standard input becomes readable. It grabs the next line of
+ * input characters, adds them to a command being assembled, and
+ * executes the command if it's complete.
*
* Results:
* None.
*
* Side effects:
- * Could be almost arbitrary, depending on the command that's typed.
+ * Could be almost arbitrary, depending on the command that's
+ * typed.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static void
-StdinProc(
- ClientData clientData, /* The state of interactive cmd line */
- int mask) /* Not used. */
+StdinProc(clientData, mask)
+ ClientData clientData; /* The state of interactive cmd line */
+ int mask; /* Not used. */
{
- int code, length;
- InteractiveState *isPtr = clientData;
+ InteractiveState *isPtr = (InteractiveState *) clientData;
Tcl_Channel chan = isPtr->input;
Tcl_Obj *commandPtr = isPtr->commandPtr;
Tcl_Interp *interp = isPtr->interp;
+ int code, length;
if (Tcl_IsShared(commandPtr)) {
Tcl_DecrRefCount(commandPtr);
@@ -774,14 +590,13 @@ StdinProc(
}
if (isPtr->tty) {
/*
- * Would be better to find a way to exit the mainLoop? Or perhaps
- * evaluate [exit]? Leaving as is for now due to compatibility
- * concerns.
+ * Would be better to find a way to exit the mainLoop?
+ * Or perhaps evaluate [exit]? Leaving as is for now due
+ * to compatibility concerns.
*/
-
Tcl_Exit(0);
}
- Tcl_DeleteChannelHandler(chan, StdinProc, isPtr);
+ Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) isPtr);
return;
}
@@ -792,45 +607,43 @@ StdinProc(
}
Tcl_AppendToObj(commandPtr, "\n", 1);
if (!TclObjCommandComplete(commandPtr)) {
- isPtr->prompt = PROMPT_CONTINUE;
- goto prompt;
+ isPtr->prompt = PROMPT_CONTINUE;
+ goto prompt;
}
isPtr->prompt = PROMPT_START;
- Tcl_GetStringFromObj(commandPtr, &length);
- Tcl_SetObjLength(commandPtr, --length);
/*
* Disable the stdin channel handler while evaluating the command;
- * otherwise if the command re-enters the event loop we might process
- * commands from stdin before the current command is finished. Among other
- * things, this will trash the text of the command being evaluated.
+ * otherwise if the command re-enters the event loop we might
+ * process commands from stdin before the current command is
+ * finished. Among other things, this will trash the text of the
+ * command being evaluated.
*/
- Tcl_CreateChannelHandler(chan, 0, StdinProc, isPtr);
+ Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) isPtr);
code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL);
isPtr->input = chan = Tcl_GetStdChannel(TCL_STDIN);
Tcl_DecrRefCount(commandPtr);
isPtr->commandPtr = commandPtr = Tcl_NewObj();
Tcl_IncrRefCount(commandPtr);
- if (chan != NULL) {
- Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc, isPtr);
+ if (chan != (Tcl_Channel) NULL) {
+ Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
+ (ClientData) isPtr);
}
if (code != TCL_OK) {
- chan = Tcl_GetStdChannel(TCL_STDERR);
-
- if (chan != NULL) {
- Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
- Tcl_WriteChars(chan, "\n", 1);
+ Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel != (Tcl_Channel) NULL) {
+ Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
+ Tcl_WriteChars(errChannel, "\n", 1);
}
} else if (isPtr->tty) {
Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
- chan = Tcl_GetStdChannel(TCL_STDOUT);
-
+ Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT);
Tcl_IncrRefCount(resultPtr);
Tcl_GetStringFromObj(resultPtr, &length);
- if ((length > 0) && (chan != NULL)) {
- Tcl_WriteObj(chan, resultPtr);
- Tcl_WriteChars(chan, "\n", 1);
+ if ((length >0) && (outChannel != (Tcl_Channel) NULL)) {
+ Tcl_WriteObj(outChannel, resultPtr);
+ Tcl_WriteChars(outChannel, "\n", 1);
}
Tcl_DecrRefCount(resultPtr);
}
@@ -839,9 +652,9 @@ StdinProc(
* If a tty stdin is still around, output a prompt.
*/
- prompt:
- if (isPtr->tty && (isPtr->input != NULL)) {
- Prompt(interp, isPtr);
+ prompt:
+ if (isPtr->tty && (isPtr->input != (Tcl_Channel) NULL)) {
+ Prompt(interp, &(isPtr->prompt));
isPtr->input = Tcl_GetStdChannel(TCL_STDIN);
}
}
@@ -851,100 +664,63 @@ StdinProc(
*
* Prompt --
*
- * Issue a prompt on standard output, or invoke a script to issue the
- * prompt.
+ * Issue a prompt on standard output, or invoke a script
+ * to issue the prompt.
*
* Results:
* None.
*
* Side effects:
- * A prompt gets output, and a Tcl script may be evaluated in interp.
+ * A prompt gets output, and a Tcl script may be evaluated
+ * in interp.
*
*----------------------------------------------------------------------
*/
static void
-Prompt(
- Tcl_Interp *interp, /* Interpreter to use for prompting. */
- InteractiveState *isPtr) /* InteractiveState. Filled
- * with PROMPT_NONE after a prompt is
- * printed. */
+Prompt(interp, promptPtr)
+ Tcl_Interp *interp; /* Interpreter to use for prompting. */
+ PromptType *promptPtr; /* Points to type of prompt to print.
+ * Filled with PROMPT_NONE after a
+ * prompt is printed. */
{
Tcl_Obj *promptCmdPtr;
int code;
- Tcl_Channel chan;
+ Tcl_Channel outChannel, errChannel;
- if (isPtr->prompt == PROMPT_NONE) {
+ if (*promptPtr == PROMPT_NONE) {
return;
}
promptCmdPtr = Tcl_GetVar2Ex(interp,
- ((isPtr->prompt == PROMPT_CONTINUE) ? "tcl_prompt2" : "tcl_prompt1"),
+ ((*promptPtr == PROMPT_CONTINUE) ? "tcl_prompt2" : "tcl_prompt1"),
NULL, TCL_GLOBAL_ONLY);
-
if (Tcl_InterpDeleted(interp)) {
return;
}
if (promptCmdPtr == NULL) {
- defaultPrompt:
- if (isPtr->prompt == PROMPT_START) {
- chan = Tcl_GetStdChannel(TCL_STDOUT);
- if (chan != NULL) {
- Tcl_WriteChars(chan, DEFAULT_PRIMARY_PROMPT,
- strlen(DEFAULT_PRIMARY_PROMPT));
- }
+ defaultPrompt:
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ if ((*promptPtr == PROMPT_START)
+ && (outChannel != (Tcl_Channel) NULL)) {
+ Tcl_WriteChars(outChannel, "% ", 2);
}
} else {
code = Tcl_EvalObjEx(interp, promptCmdPtr, TCL_EVAL_GLOBAL);
if (code != TCL_OK) {
Tcl_AddErrorInfo(interp,
"\n (script that generates prompt)");
- chan = Tcl_GetStdChannel(TCL_STDERR);
- if (chan != NULL) {
- Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
- Tcl_WriteChars(chan, "\n", 1);
- }
+ errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel != (Tcl_Channel) NULL) {
+ Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
+ Tcl_WriteChars(errChannel, "\n", 1);
+ }
goto defaultPrompt;
}
}
-
- chan = Tcl_GetStdChannel(TCL_STDOUT);
- if (chan != NULL) {
- Tcl_Flush(chan);
+ outChannel = Tcl_GetStdChannel(TCL_STDOUT);
+ if (outChannel != (Tcl_Channel) NULL) {
+ Tcl_Flush(outChannel);
}
- isPtr->prompt = PROMPT_NONE;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * FreeMainInterp --
- *
- * Exit handler used to cleanup the main interpreter and ancillary startup
- * script storage at exit.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-FreeMainInterp(
- ClientData clientData)
-{
- Tcl_Interp *interp = (Tcl_Interp *) clientData;
-
- /*if (TclInExit()) return;*/
-
- if (!Tcl_InterpDeleted(interp)) {
- Tcl_DeleteInterp(interp);
- }
- Tcl_SetStartupScript(NULL, NULL);
- Tcl_Release(interp);
+ *promptPtr = PROMPT_NONE;
}
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 4
- * fill-column: 78
- * End:
- */