diff options
Diffstat (limited to 'generic/tclMain.c')
| -rw-r--r-- | generic/tclMain.c | 794 | 
1 files changed, 429 insertions, 365 deletions
| diff --git a/generic/tclMain.c b/generic/tclMain.c index c3a75ef..360f5e9 100644 --- a/generic/tclMain.c +++ b/generic/tclMain.c @@ -2,6 +2,11 @@   * 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. @@ -9,14 +14,25 @@   *   * See the file "license.terms" for information on usage and redistribution of   * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclMain.c,v 1.36 2006/05/05 18:09:47 dgp Exp $   */ -#include "tclInt.h" +/* + * 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. + */ -#undef TCL_STORAGE_CLASS -#define TCL_STORAGE_CLASS DLLEXPORT +#if defined(TCL_ASCII_MAIN) +#   ifdef UNICODE +#	undef UNICODE +#	undef _UNICODE +#   else +#	define UNICODE +#	define _UNICODE +#   endif +#endif + +#include "tclInt.h"  /*   * The default prompt used when the user has not overridden it. @@ -25,6 +41,40 @@  #define DEFAULT_PRIMARY_PROMPT	"% "  /* + * 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. + */ + +#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). @@ -32,9 +82,20 @@  extern CRTIMPORT int	isatty(int fd); -static Tcl_Obj *tclStartupScriptPath = NULL; -static Tcl_Obj *tclStartupScriptEncoding = NULL; -static Tcl_MainLoopProc *mainLoopProc = NULL; +/* + * The thread-local variables for this file's functions. + */ + +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;  /*   * Structure definition for information used to keep the state of an @@ -65,9 +126,14 @@ typedef struct InteractiveState {   * Forward declarations for functions defined later in this file.   */ -static void		Prompt(Tcl_Interp *interp, PromptType *promptPtr); +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); +#ifndef TCL_ASCII_MAIN +static Tcl_ThreadDataKey dataKey; +  /*   *----------------------------------------------------------------------   * @@ -87,27 +153,29 @@ static void		StdinProc(ClientData clientData, int mask);  void  Tcl_SetStartupScript(      Tcl_Obj *path,		/* Filesystem path of startup script file */ -    CONST char *encoding)	/* Encoding of the data in that file */ +    const char *encoding)	/* Encoding of the data in that file */  { +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);      Tcl_Obj *newEncoding = NULL; +      if (encoding != NULL) {  	newEncoding = Tcl_NewStringObj(encoding, -1);      } -    if (tclStartupScriptPath != NULL) { -	Tcl_DecrRefCount(tclStartupScriptPath); +    if (tsdPtr->path != NULL) { +	Tcl_DecrRefCount(tsdPtr->path);      } -    tclStartupScriptPath = path; -    if (tclStartupScriptPath != NULL) { -	Tcl_IncrRefCount(tclStartupScriptPath); +    tsdPtr->path = path; +    if (tsdPtr->path != NULL) { +	Tcl_IncrRefCount(tsdPtr->path);      } -    if (tclStartupScriptEncoding != NULL) { -	Tcl_DecrRefCount(tclStartupScriptEncoding); +    if (tsdPtr->encoding != NULL) { +	Tcl_DecrRefCount(tsdPtr->encoding);      } -    tclStartupScriptEncoding = newEncoding; -    if (tclStartupScriptEncoding != NULL) { -	Tcl_IncrRefCount(tclStartupScriptEncoding); +    tsdPtr->encoding = newEncoding; +    if (tsdPtr->encoding != NULL) { +	Tcl_IncrRefCount(tsdPtr->encoding);      }  } @@ -123,131 +191,31 @@ Tcl_SetStartupScript(   *	The path of the startup script; 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. + *	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.   *   *----------------------------------------------------------------------   */  Tcl_Obj *  Tcl_GetStartupScript( -    CONST char **encodingPtr)	/* When not NULL, points to storage for the -				 * (CONST char *) that points to the +    const char **encodingPtr)	/* When not NULL, points to storage for the +				 * (const char *) that points to the  				 * registered encoding name for the startup -				 * script */ +				 * script. */  { +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); +      if (encodingPtr != NULL) { -	if (tclStartupScriptEncoding == NULL) { +	if (tsdPtr->encoding == NULL) {  	    *encodingPtr = NULL;  	} else { -	    *encodingPtr = Tcl_GetString(tclStartupScriptEncoding); +	    *encodingPtr = Tcl_GetString(tsdPtr->encoding);  	}      } -    return tclStartupScriptPath; -} - -/* - *---------------------------------------------------------------------- - * - * TclSetStartupScriptPath -- - * - *	Primes the startup script VFS path, used to override the command line - *	processing. - * - * Results: - *	None. - * - * Side effects: - *	This function initializes the VFS path of the Tcl script to run at - *	startup. - * - *---------------------------------------------------------------------- - */ - -void -TclSetStartupScriptPath( -    Tcl_Obj *path) -{ -    Tcl_SetStartupScript(path, NULL); -} - -/* - *---------------------------------------------------------------------- - * - * TclGetStartupScriptPath -- - * - *	Gets the startup script VFS path, used to override the command line - *	processing. - * - * Results: - *	The startup script VFS path, NULL if none has been set. - * - * Side effects: - *	None. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclGetStartupScriptPath(void) -{ -    return Tcl_GetStartupScript(NULL); -} - -/* - *---------------------------------------------------------------------- - * - * TclSetStartupScriptFileName -- - * - *	Primes the startup script file name, used to override the command line - *	processing. - * - * Results: - *	None. - * - * Side effects: - *	This function initializes the file name of the Tcl script to run at - *	startup. - * - *---------------------------------------------------------------------- - */ - -void -TclSetStartupScriptFileName( -    CONST char *fileName) -{ -    Tcl_Obj *path = Tcl_NewStringObj(fileName,-1); -    Tcl_SetStartupScript(path, NULL); -} - -/* - *---------------------------------------------------------------------- - * - * TclGetStartupScriptFileName -- - * - *	Gets the startup script file name, used to override the command line - *	processing. - * - * Results: - *	The startup script file name, NULL if none has been set. - * - * Side effects: - *	None. - * - *---------------------------------------------------------------------- - */ - -CONST char * -TclGetStartupScriptFileName(void) -{ -    Tcl_Obj *path = Tcl_GetStartupScript(NULL); - -    if (path == NULL) { -	return NULL; -    } -    return Tcl_GetString(path); +    return tsdPtr->path;  }  /*---------------------------------------------------------------------- @@ -272,13 +240,13 @@ Tcl_SourceRCFile(      Tcl_Interp *interp)		/* Interpreter to source rc file into. */  {      Tcl_DString temp; -    CONST char *fileName; -    Tcl_Channel errChannel; +    const char *fileName; +    Tcl_Channel chan;      fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY);      if (fileName != NULL) {  	Tcl_Channel c; -	CONST char *fullName; +	const char *fullName;  	Tcl_DStringInit(&temp);  	fullName = Tcl_TranslateFileName(interp, fileName, &temp); @@ -294,24 +262,25 @@ Tcl_SourceRCFile(  	     */  	    c = Tcl_OpenFileChannel(NULL, fullName, "r", 0); -	    if (c != (Tcl_Channel) NULL) { +	    if (c != NULL) {  		Tcl_Close(NULL, c);  		if (Tcl_EvalFile(interp, fullName) != TCL_OK) { -		    errChannel = Tcl_GetStdChannel(TCL_STDERR); -		    if (errChannel) { -			Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); -			Tcl_WriteChars(errChannel, "\n", 1); - 		    } - 		} - 	    } +		    chan = Tcl_GetStdChannel(TCL_STDERR); +		    if (chan) { +			Tcl_WriteObj(chan, Tcl_GetObjResult(interp)); +			Tcl_WriteChars(chan, "\n", 1); +		    } +		} +	    }  	}  	Tcl_DStringFree(&temp);      }  } +#endif /* !TCL_ASCII_MAIN */  /*----------------------------------------------------------------------   * - * Tcl_Main -- + * Tcl_Main, Tcl_MainEx --   *   *	Main program for tclsh and most other Tcl-based applications.   * @@ -328,27 +297,31 @@ Tcl_SourceRCFile(   */  void -Tcl_Main( +Tcl_MainEx(      int argc,			/* Number of arguments. */ -    char **argv,		/* Array of argument strings. */ -    Tcl_AppInitProc *appInitProc) +    TCHAR **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)  { -    Tcl_Obj *path, *resultPtr, *argvPtr, *commandPtr = NULL; -    CONST char *encodingName = NULL; -    PromptType prompt = PROMPT_START; -    int code, length, tty, exitCode = 0; -    Tcl_Channel inChannel, outChannel, errChannel; -    Tcl_Interp *interp; -    Tcl_DString appName; +    Tcl_Obj *path, *resultPtr, *argvPtr, *appName; +    const char *encodingName = NULL; +    int code, exitCode = 0; +    Tcl_MainLoopProc *mainLoopProc; +    Tcl_Channel chan; +    InteractiveState is; -    Tcl_FindExecutable(argv[0]); +    TclpSetInitialEncodings(); +    TclpFindExecutable((const char *)argv[0]); -    interp = Tcl_CreateInterp();      Tcl_InitMemory(interp); +    is.interp = interp; +    is.prompt = PROMPT_START; +    is.commandPtr = Tcl_NewObj(); +      /*       * If the application has not already set a startup script, parse the       * first few command line arguments to determine the script path and @@ -356,21 +329,23 @@ Tcl_Main(       */      if (NULL == Tcl_GetStartupScript(NULL)) { -  	/*  	 * Check whether first 3 args (argv[1] - argv[3]) look like -	 * 	-encoding ENCODING FILENAME +	 *  -encoding ENCODING FILENAME  	 * or like -	 * 	FILENAME +	 *  FILENAME  	 */ -	if ((argc > 3) && (0 == strcmp("-encoding", argv[1])) +	if ((argc > 3) && (0 == _tcscmp(TEXT("-encoding"), argv[1]))  		&& ('-' != argv[3][0])) { -	    Tcl_SetStartupScript(Tcl_NewStringObj(argv[3], -1), argv[2]); +	    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) && ('-' != argv[1][0])) { -	    Tcl_SetStartupScript(Tcl_NewStringObj(argv[1], -1), NULL); +	    Tcl_SetStartupScript(NewNativeObj(argv[1], -1), NULL);  	    argc--;  	    argv++;  	} @@ -378,15 +353,11 @@ Tcl_Main(      path = Tcl_GetStartupScript(&encodingName);      if (path == NULL) { -	Tcl_ExternalToUtfDString(NULL, argv[0], -1, &appName); +	appName = NewNativeObj(argv[0], -1);      } else { -	CONST char *pathName = Tcl_GetStringFromObj(path, &length); -	Tcl_ExternalToUtfDString(NULL, pathName, length, &appName); -	path = Tcl_NewStringObj(Tcl_DStringValue(&appName), -1); -	Tcl_SetStartupScript(path, encodingName); +	appName = path;      } -    Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&appName), TCL_GLOBAL_ONLY); -    Tcl_DStringFree(&appName); +    Tcl_SetVar2Ex(interp, "argv0", NULL, appName, TCL_GLOBAL_ONLY);      argc--;      argv++; @@ -394,11 +365,7 @@ Tcl_Main(      argvPtr = Tcl_NewListObj(0, NULL);      while (argc--) { -	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_ListObjAppendElement(NULL, argvPtr, NewNativeObj(*argv++, -1));      }      Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY); @@ -406,22 +373,22 @@ Tcl_Main(       * Set the "tcl_interactive" variable.       */ -    tty = isatty(0); -    Tcl_SetVar(interp, "tcl_interactive", ((path == NULL) && tty) ? "1" : "0", -	    TCL_GLOBAL_ONLY); +    is.tty = isatty(0); +    Tcl_SetVar2Ex(interp, "tcl_interactive", NULL, +	    Tcl_NewIntObj(!path && is.tty), TCL_GLOBAL_ONLY);      /*       * Invoke application-specific initialization.       */ -    Tcl_Preserve((ClientData) interp); -    if ((*appInitProc)(interp) != TCL_OK) { -	errChannel = Tcl_GetStdChannel(TCL_STDERR); -	if (errChannel) { -	    Tcl_WriteChars(errChannel, +    Tcl_Preserve(interp); +    if (appInitProc(interp) != TCL_OK) { +	chan = Tcl_GetStdChannel(TCL_STDERR); +	if (chan) { +	    Tcl_WriteChars(chan,  		    "application-specific initialization failed: ", -1); -	    Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); -	    Tcl_WriteChars(errChannel, "\n", 1); +	    Tcl_WriteObj(chan, Tcl_GetObjResult(interp)); +	    Tcl_WriteChars(chan, "\n", 1);  	}      }      if (Tcl_InterpDeleted(interp)) { @@ -430,30 +397,40 @@ Tcl_Main(      if (Tcl_LimitExceeded(interp)) {  	goto done;      } +    if (TclFullFinalizationRequested()) { +	/* +	 * Arrange for final deletion of the main interp +	 */ + +	/* ARGH Munchhausen effect */ +	Tcl_CreateExitHandler(FreeMainInterp, interp); +    }      /* -     * If a script file was specified then just source that file and quit. -     * Must fetch it again, as the appInitProc might have reset it. +     * Invoke the script specified on the command line, if any. Must fetch it +     * again, as the appInitProc might have reset it.       */      path = Tcl_GetStartupScript(&encodingName);      if (path != NULL) { +	Tcl_ResetResult(interp);  	code = Tcl_FSEvalFileEx(interp, path, encodingName);  	if (code != TCL_OK) { -	    errChannel = Tcl_GetStdChannel(TCL_STDERR); -	    if (errChannel) { +	    chan = Tcl_GetStdChannel(TCL_STDERR); +	    if (chan) {  		Tcl_Obj *options = Tcl_GetReturnOptions(interp, code); -		Tcl_Obj *keyPtr = Tcl_NewStringObj("-errorinfo", -1); -		Tcl_Obj *valuePtr; +		Tcl_Obj *keyPtr, *valuePtr; +		TclNewLiteralStringObj(keyPtr, "-errorinfo");  		Tcl_IncrRefCount(keyPtr);  		Tcl_DictObjGet(NULL, options, keyPtr, &valuePtr);  		Tcl_DecrRefCount(keyPtr);  		if (valuePtr) { -		    Tcl_WriteObj(errChannel, valuePtr); +		    Tcl_WriteObj(chan, valuePtr);  		} -		Tcl_WriteChars(errChannel, "\n", 1); +		Tcl_WriteChars(chan, "\n", 1); +		Tcl_DecrRefCount(options);  	    }  	    exitCode = 1;  	} @@ -476,45 +453,46 @@ Tcl_Main(       * may have been changed.       */ -    commandPtr = Tcl_NewObj(); -    Tcl_IncrRefCount(commandPtr); +    Tcl_IncrRefCount(is.commandPtr);      /*       * Get a new value for tty if anyone writes to ::tcl_interactive       */ -    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)) { +    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();  	if (mainLoopProc == NULL) { -	    if (tty) { -		Prompt(interp, &prompt); +	    int length; + +	    if (is.tty) { +		Prompt(interp, &is);  		if (Tcl_InterpDeleted(interp)) {  		    break;  		}  		if (Tcl_LimitExceeded(interp)) {  		    break;  		} -		inChannel = Tcl_GetStdChannel(TCL_STDIN); -		if (inChannel == (Tcl_Channel) NULL) { +		is.input = Tcl_GetStdChannel(TCL_STDIN); +		if (is.input == NULL) {  		    break;  		}  	    } -	    if (Tcl_IsShared(commandPtr)) { -		Tcl_DecrRefCount(commandPtr); -		commandPtr = Tcl_DuplicateObj(commandPtr); -		Tcl_IncrRefCount(commandPtr); +	    if (Tcl_IsShared(is.commandPtr)) { +		Tcl_DecrRefCount(is.commandPtr); +		is.commandPtr = Tcl_DuplicateObj(is.commandPtr); +		Tcl_IncrRefCount(is.commandPtr);  	    } -	    length = Tcl_GetsObj(inChannel, commandPtr); +	    length = Tcl_GetsObj(is.input, is.commandPtr);  	    if (length < 0) { -		if (Tcl_InputBlocked(inChannel)) { +		if (Tcl_InputBlocked(is.input)) {  		    /*  		     * This can only happen if stdin has been set to -		     * non-blocking.  In that case cycle back and try again. +		     * 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. +		     * event loop running). If this causes bad CPU hogging, we +		     * might try toggling the blocking on stdin instead.  		     */  		    continue; @@ -527,41 +505,52 @@ Tcl_Main(  		break;  	    } -	    if (!TclObjCommandComplete(commandPtr)) { -		/* -		 * Add the newline removed by Tcl_GetsObj back to the string. -		 */ +	    /* +	     * 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] +	     */ -		if (Tcl_IsShared(commandPtr)) { -		    Tcl_DecrRefCount(commandPtr); -		    commandPtr = Tcl_DuplicateObj(commandPtr); -		    Tcl_IncrRefCount(commandPtr); -		} -		Tcl_AppendToObj(commandPtr, "\n", 1); -		prompt = PROMPT_CONTINUE; +	    if (Tcl_IsShared(is.commandPtr)) { +		Tcl_DecrRefCount(is.commandPtr); +		is.commandPtr = Tcl_DuplicateObj(is.commandPtr); +		Tcl_IncrRefCount(is.commandPtr); +	    } +	    Tcl_AppendToObj(is.commandPtr, "\n", 1); +	    if (!TclObjCommandComplete(is.commandPtr)) { +		is.prompt = PROMPT_CONTINUE;  		continue;  	    } -	    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); +	    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);  	    if (code != TCL_OK) { -		if (errChannel) { -		    Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); -		    Tcl_WriteChars(errChannel, "\n", 1); +		chan = Tcl_GetStdChannel(TCL_STDERR); +		if (chan) { +		    Tcl_WriteObj(chan, Tcl_GetObjResult(interp)); +		    Tcl_WriteChars(chan, "\n", 1);  		} - 	    } else if (tty) { +	    } else if (is.tty) {  		resultPtr = Tcl_GetObjResult(interp);  		Tcl_IncrRefCount(resultPtr);  		Tcl_GetStringFromObj(resultPtr, &length); -		if ((length > 0) && outChannel) { -		    Tcl_WriteObj(outChannel, resultPtr); -		    Tcl_WriteChars(outChannel, "\n", 1); +		chan = Tcl_GetStdChannel(TCL_STDOUT); +		if ((length > 0) && chan) { +		    Tcl_WriteObj(chan, resultPtr); +		    Tcl_WriteChars(chan, "\n", 1);  		}  		Tcl_DecrRefCount(resultPtr);  	    } @@ -572,124 +561,103 @@ Tcl_Main(  	     * channel handler for stdin.  	     */ -	    InteractiveState *isPtr = NULL; - -	    if (inChannel) { -		if (tty) { -		    Prompt(interp, &prompt); +	    if (is.input) { +		if (is.tty) { +		    Prompt(interp, &is);  		} -		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); + +		Tcl_CreateChannelHandler(is.input, TCL_READABLE, +			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); +	    mainLoopProc(); +	    Tcl_SetMainLoop(NULL); + +	    if (is.input) { +		Tcl_DeleteChannelHandler(is.input, StdinProc, &is);  	    } -	    inChannel = Tcl_GetStdChannel(TCL_STDIN); -	    outChannel = Tcl_GetStdChannel(TCL_STDOUT); -	    errChannel = Tcl_GetStdChannel(TCL_STDERR); +	    is.input = Tcl_GetStdChannel(TCL_STDIN);  	} -#ifdef TCL_MEM_DEBUG  	/*  	 * This code here only for the (unsupported and deprecated) [checkmem]  	 * command.  	 */ +#ifdef TCL_MEM_DEBUG  	if (tclMemDumpFileName != NULL) { -	    mainLoopProc = NULL; +	    Tcl_SetMainLoop(NULL);  	    Tcl_DeleteInterp(interp);  	} -#endif +#endif /* TCL_MEM_DEBUG */      }    done: -    if ((exitCode == 0) && (mainLoopProc != NULL) -	    && !Tcl_LimitExceeded(interp)) { +    mainLoopProc = TclGetMainLoop(); +    if ((exitCode == 0) && mainLoopProc && !Tcl_LimitExceeded(interp)) {  	/*  	 * 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)(); -		mainLoopProc = NULL; -	    } -	    if (commandPtr != NULL) { -		Tcl_DecrRefCount(commandPtr); -	    } +	mainLoopProc(); +	Tcl_SetMainLoop(NULL); +    } +    if (is.commandPtr != NULL) { +	Tcl_DecrRefCount(is.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_EvalObjEx call should never return. +     */ -	    if (!Tcl_InterpDeleted(interp)) { -		if (!Tcl_LimitExceeded(interp)) { -		    Tcl_Obj *cmd = Tcl_NewObj(); -		    TclObjPrintf(NULL, cmd, "exit %d", exitCode); -		    Tcl_IncrRefCount(cmd); -		    Tcl_EvalObjEx(interp, cmd, TCL_EVAL_GLOBAL); -		    Tcl_DecrRefCount(cmd); -		} +    if (!Tcl_InterpDeleted(interp) && !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. -		 */ +    /* +     * 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. +     */ -		if (!Tcl_InterpDeleted(interp)) { -		    Tcl_DeleteInterp(interp); -		} -	    } -	    Tcl_SetStartupScript(NULL, NULL); +    Tcl_Exit(exitCode); +} -	    /* -	     * If we get here, the master interp has been deleted. Allow its -	     * destruction with the last matching Tcl_Release. -	     */ +#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_MainEx(argc, argv, appInitProc, Tcl_CreateInterp()); +} +#endif /* TCL_MAJOR_VERSION == 8 && !UNICODE */ + +#ifndef TCL_ASCII_MAIN -	    Tcl_Release((ClientData) interp); -	    Tcl_Exit(exitCode); -	} -	 -	/* -	 *--------------------------------------------------------------- -	 * +/* + *--------------------------------------------------------------- + *   * Tcl_SetMainLoop --   *   *	Sets an alternative main loop function.   *   * Results: - *	Returns the previously defined main loop function. + *	None.   *   * Side effects:   *	This function will be called before Tcl exits, allowing for the @@ -702,12 +670,79 @@ void  Tcl_SetMainLoop(      Tcl_MainLoopProc *proc)  { -    mainLoopProc = proc; +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + +    tsdPtr->mainLoopProc = proc; +} + +/* + *--------------------------------------------------------------- + * + * TclGetMainLoop -- + * + *	Returns the current alternative main loop function. + * + * 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.. + * + * Side effects: + *	None (other than possible creation of this file's TSD block). + * + *--------------------------------------------------------------- + */ + +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) +{ +#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 /* PURIFY */ +} +#endif /* !TCL_ASCII_MAIN */ + +/* + *---------------------------------------------------------------------- + *   * StdinProc --   *   *	This function is invoked by the event dispatcher whenever standard @@ -730,11 +765,11 @@ StdinProc(      ClientData clientData,	/* The state of interactive cmd line */      int mask)			/* Not used. */  { -    InteractiveState *isPtr = (InteractiveState *) clientData; +    int code, length; +    InteractiveState *isPtr = 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); @@ -755,21 +790,23 @@ StdinProc(  	    Tcl_Exit(0);  	} -	Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) isPtr); +	Tcl_DeleteChannelHandler(chan, StdinProc, isPtr);  	return;      } +    if (Tcl_IsShared(commandPtr)) { +	Tcl_DecrRefCount(commandPtr); +	commandPtr = Tcl_DuplicateObj(commandPtr); +	Tcl_IncrRefCount(commandPtr); +    } +    Tcl_AppendToObj(commandPtr, "\n", 1);      if (!TclObjCommandComplete(commandPtr)) { -	if (Tcl_IsShared(commandPtr)) { -	    Tcl_DecrRefCount(commandPtr); -	    commandPtr = Tcl_DuplicateObj(commandPtr); -	    Tcl_IncrRefCount(commandPtr); -	} -	Tcl_AppendToObj(commandPtr, "\n", 1);  	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; @@ -778,30 +815,31 @@ StdinProc(       * things, this will trash the text of the command being evaluated.       */ -    Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) isPtr); +    Tcl_CreateChannelHandler(chan, 0, StdinProc, 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 != (Tcl_Channel) NULL) { -	Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc, -		(ClientData) isPtr); +    if (chan != NULL) { +	Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc, isPtr);      }      if (code != TCL_OK) { -	Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR); -	if (errChannel != (Tcl_Channel) NULL) { -	    Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); -	    Tcl_WriteChars(errChannel, "\n", 1); +	chan = Tcl_GetStdChannel(TCL_STDERR); + +	if (chan != NULL) { +	    Tcl_WriteObj(chan, Tcl_GetObjResult(interp)); +	    Tcl_WriteChars(chan, "\n", 1);  	}      } else if (isPtr->tty) {  	Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); -	Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT); +	chan = Tcl_GetStdChannel(TCL_STDOUT); +  	Tcl_IncrRefCount(resultPtr);  	Tcl_GetStringFromObj(resultPtr, &length); -	if ((length >0) && (outChannel != (Tcl_Channel) NULL)) { -	    Tcl_WriteObj(outChannel, resultPtr); -	    Tcl_WriteChars(outChannel, "\n", 1); +	if ((length > 0) && (chan != NULL)) { +	    Tcl_WriteObj(chan, resultPtr); +	    Tcl_WriteChars(chan, "\n", 1);  	}  	Tcl_DecrRefCount(resultPtr);      } @@ -811,8 +849,8 @@ StdinProc(       */    prompt: -    if (isPtr->tty && (isPtr->input != (Tcl_Channel) NULL)) { -	Prompt(interp, &(isPtr->prompt)); +    if (isPtr->tty && (isPtr->input != NULL)) { +	Prompt(interp, isPtr);  	isPtr->input = Tcl_GetStdChannel(TCL_STDIN);      }  } @@ -837,20 +875,19 @@ StdinProc(  static void  Prompt(      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. */ +    InteractiveState *isPtr)	/* InteractiveState. Filled with PROMPT_NONE +				 * after a prompt is printed. */  {      Tcl_Obj *promptCmdPtr;      int code; -    Tcl_Channel outChannel, errChannel; +    Tcl_Channel chan; -    if (*promptPtr == PROMPT_NONE) { +    if (isPtr->prompt == PROMPT_NONE) {  	return;      }      promptCmdPtr = Tcl_GetVar2Ex(interp, -	    ((*promptPtr == PROMPT_CONTINUE) ? "tcl_prompt2" : "tcl_prompt1"), +	    (isPtr->prompt==PROMPT_CONTINUE ? "tcl_prompt2" : "tcl_prompt1"),  	    NULL, TCL_GLOBAL_ONLY);      if (Tcl_InterpDeleted(interp)) { @@ -858,31 +895,58 @@ Prompt(      }      if (promptCmdPtr == NULL) {      defaultPrompt: -	outChannel = Tcl_GetStdChannel(TCL_STDOUT); -	if ((*promptPtr == PROMPT_START) -		&& (outChannel != (Tcl_Channel) NULL)) { -	    Tcl_WriteChars(outChannel, DEFAULT_PRIMARY_PROMPT, -		    strlen(DEFAULT_PRIMARY_PROMPT)); +	if (isPtr->prompt == PROMPT_START) { +	    chan = Tcl_GetStdChannel(TCL_STDOUT); +	    if (chan != NULL) { +		Tcl_WriteChars(chan, DEFAULT_PRIMARY_PROMPT, +			strlen(DEFAULT_PRIMARY_PROMPT)); +	    }  	}      } else {  	code = Tcl_EvalObjEx(interp, promptCmdPtr, TCL_EVAL_GLOBAL);  	if (code != TCL_OK) {  	    Tcl_AddErrorInfo(interp,  		    "\n    (script that generates prompt)"); -	    errChannel = Tcl_GetStdChannel(TCL_STDERR); -	    if (errChannel != (Tcl_Channel) NULL) { -		Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); -		Tcl_WriteChars(errChannel, "\n", 1); +	    chan = Tcl_GetStdChannel(TCL_STDERR); +	    if (chan != NULL) { +		Tcl_WriteObj(chan, Tcl_GetObjResult(interp)); +		Tcl_WriteChars(chan, "\n", 1);  	    }  	    goto defaultPrompt;  	}      } -    outChannel = Tcl_GetStdChannel(TCL_STDOUT); -    if (outChannel != (Tcl_Channel) NULL) { -	Tcl_Flush(outChannel); +    chan = Tcl_GetStdChannel(TCL_STDOUT); +    if (chan != NULL) { +	Tcl_Flush(chan); +    } +    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 = clientData; + +    /*if (TclInExit()) return;*/ + +    if (!Tcl_InterpDeleted(interp)) { +	Tcl_DeleteInterp(interp);      } -    *promptPtr = PROMPT_NONE; +    Tcl_SetStartupScript(NULL, NULL); +    Tcl_Release(interp);  }  /* | 
