tclmain.c

来自「tcl是工具命令语言」· C语言 代码 · 共 726 行 · 第 1/2 页

C
726
字号
/*  * tclMain.c -- * *	Main program for Tcl shells and other Tcl-based 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. * * RCS: @(#) $Id: tclMain.c,v 1.20 2002/05/29 22:59:33 dgp Exp $ */#include "tcl.h"#include "tclInt.h"# undef TCL_STORAGE_CLASS# define TCL_STORAGE_CLASS DLLEXPORT/* * 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). */#if !defined(MAC_TCL)extern int		isatty _ANSI_ARGS_((int fd));#else#include <unistd.h>#endifstatic Tcl_Obj *tclStartupScriptPath = NULL;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. */typedef enum {    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. */    PromptType prompt;		/* Next prompt to print */    Tcl_Interp *interp;		/* Interpreter that evaluates interactive				 * commands. */} InteractiveState;/* * Forward declarations for procedures defined later in this file. */static void		Prompt _ANSI_ARGS_((Tcl_Interp *interp,			    PromptType *promptPtr));static void		StdinProc _ANSI_ARGS_((ClientData clientData,			    int mask));/* *---------------------------------------------------------------------- * * TclSetStartupScriptPath -- * *	Primes the startup script VFS path, used to override the *      command line processing. * * Results: *	None.  * * Side effects: *	This procedure initializes the VFS path of the Tcl script to *      run at startup. * *---------------------------------------------------------------------- */void TclSetStartupScriptPath(pathPtr)    Tcl_Obj *pathPtr;{    if (tclStartupScriptPath != NULL) {	Tcl_DecrRefCount(tclStartupScriptPath);    }    tclStartupScriptPath = pathPtr;    if (tclStartupScriptPath != NULL) {	Tcl_IncrRefCount(tclStartupScriptPath);    }}/* *---------------------------------------------------------------------- * * 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(){    return tclStartupScriptPath;}/* *---------------------------------------------------------------------- * * 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);}/* *---------------------------------------------------------------------- * * 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(){    Tcl_Obj *pathPtr = TclGetStartupScriptPath();    if (pathPtr == NULL) {	return NULL;    }    return Tcl_GetString(pathPtr);}/* *---------------------------------------------------------------------- * * Tcl_Main -- * *	Main program for tclsh and most other Tcl-based applications. * * Results: *	None. This procedure never returns (it exits the process when *	it's done). * * Side effects: *	This procedure initializes the Tcl world and then starts *	interpreting commands;  almost anything could happen, depending *	on the script being interpreted. * *---------------------------------------------------------------------- */voidTcl_Main(argc, argv, appInitProc)    int argc;			/* Number of arguments. */    char **argv;		/* Array of argument strings. */    Tcl_AppInitProc *appInitProc;				/* Application-specific initialization				 * procedure to call after most				 * initialization but before starting to				 * execute commands. */{    Tcl_Obj *resultPtr;    Tcl_Obj *commandPtr = NULL;    char buffer[TCL_INTEGER_SPACE + 5], *args;    PromptType prompt = PROMPT_START;    int code, length, tty;    int exitCode = 0;    Tcl_Channel inChannel, outChannel, errChannel;    Tcl_Interp *interp;    Tcl_DString argString;    Tcl_FindExecutable(argv[0]);    interp = Tcl_CreateInterp();    Tcl_InitMemory(interp);    /*     * 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 (TclGetStartupScriptPath() == NULL) {	if ((argc > 1) && (argv[1][0] != '-')) {	    TclSetStartupScriptFileName(argv[1]);	    argc--;	    argv++;	}    }    /*     * The CONST casting is safe, and better we do it here than force     * all callers of Tcl_Main to do it.  (Those callers are likely     * in a main() that can't easily change its signature.)     */        args = Tcl_Merge(argc-1, (CONST char **)argv+1);    Tcl_ExternalToUtfDString(NULL, args, -1, &argString);    Tcl_SetVar(interp, "argv", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);    Tcl_DStringFree(&argString);    ckfree(args);    if (TclGetStartupScriptPath() == NULL) {	Tcl_ExternalToUtfDString(NULL, argv[0], -1, &argString);    } else {	TclSetStartupScriptFileName(Tcl_ExternalToUtfDString(NULL,		TclGetStartupScriptFileName(), -1, &argString));    }    TclFormatInt(buffer, (long) argc-1);    Tcl_SetVar(interp, "argc", buffer, TCL_GLOBAL_ONLY);    Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);    /*     * Set the "tcl_interactive" variable.     */    tty = isatty(0);    Tcl_SetVar(interp, "tcl_interactive",	    ((TclGetStartupScriptPath() == NULL) && tty) ? "1" : "0",	    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,		    "application-specific initialization failed: ", -1);	    Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));	    Tcl_WriteChars(errChannel, "\n", 1);	}    }    if (Tcl_InterpDeleted(interp)) {	goto done;    }    /*     * If a script file was specified then just source that file     * and quit.     */    if (TclGetStartupScriptPath() != NULL) {	code = Tcl_FSEvalFile(interp, TclGetStartupScriptPath());	if (code != TCL_OK) {	    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;	}	goto done;    }    Tcl_DStringFree(&argString);    /*     * We're running interactively.  Source a user-specific startup     * file if the application specified one and if the file exists.     */    Tcl_SourceRCFile(interp);    /*     * 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.     */    commandPtr = Tcl_NewObj();    Tcl_IncrRefCount(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)) {	if (tty) {	    Prompt(interp, &prompt);	    if (Tcl_InterpDeleted(interp)) {		break;	    }	    inChannel = Tcl_GetStdChannel(TCL_STDIN);	    if (inChannel == (Tcl_Channel) NULL) {	        break;	    }	}	if (Tcl_IsShared(commandPtr)) {	    Tcl_DecrRefCount(commandPtr);	    commandPtr = Tcl_DuplicateObj(commandPtr);	    Tcl_IncrRefCount(commandPtr);	}        length = Tcl_GetsObj(inChannel, commandPtr);	if (length < 0) {	    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.		 */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?