archie/tcl7.6/generic/tclMain.c

314 lines
8.5 KiB
C
Raw Normal View History

2024-05-27 16:13:40 +02:00
/*
2024-05-27 16:40:40 +02:00
* tclMain.c --
2024-05-27 16:13:40 +02:00
*
* Main program for Tcl shells and other Tcl-based applications.
*
2024-05-27 16:40:40 +02:00
* Copyright (c) 1988-1994 The Regents of the University of California.
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
2024-05-27 16:13:40 +02:00
*
2024-05-27 16:40:40 +02:00
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
2024-05-27 16:13:40 +02:00
*
2024-05-27 16:40:40 +02:00
* SCCS: @(#) tclMain.c 1.51 96/09/05 17:57:01
2024-05-27 16:13:40 +02:00
*/
2024-05-27 16:40:40 +02:00
#include "tcl.h"
#include "tclInt.h"
/*
* The following code ensures that tclLink.c is linked whenever
* Tcl is linked. Without this code there's no reference to the
* code in that file from anywhere in Tcl, so it may not be
* linked into the application.
*/
2024-05-27 16:13:40 +02:00
2024-05-27 16:40:40 +02:00
EXTERN int Tcl_LinkVar();
int (*tclDummyLinkVarPtr)() = Tcl_LinkVar;
2024-05-27 16:13:40 +02:00
/*
* Declarations for various library procedures and variables (don't want
2024-05-27 16:40:40 +02:00
* to include tclPort.h here, because people might copy this file out of
2024-05-27 16:13:40 +02:00
* the Tcl source directory to make their own modified versions).
2024-05-27 16:40:40 +02:00
* Note: "exit" should really be declared here, but there's no way to
* declare it without causing conflicts with other definitions elsewher
* on some systems, so it's better just to leave it out.
2024-05-27 16:13:40 +02:00
*/
extern int isatty _ANSI_ARGS_((int fd));
extern char * strcpy _ANSI_ARGS_((char *dst, CONST char *src));
static Tcl_Interp *interp; /* Interpreter for application. */
static Tcl_DString command; /* Used to buffer incomplete commands being
* read from stdin. */
#ifdef TCL_MEM_DEBUG
static char dumpFile[100]; /* Records where to dump memory allocation
* information. */
static int quitFlag = 0; /* 1 means the "checkmem" command was
* invoked, so the application should quit
* and dump memory allocation information. */
#endif
/*
* Forward references for procedures defined later in this file:
*/
2024-05-27 16:40:40 +02:00
#ifdef TCL_MEM_DEBUG
2024-05-27 16:13:40 +02:00
static int CheckmemCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char *argv[]));
2024-05-27 16:40:40 +02:00
#endif
2024-05-27 16:13:40 +02:00
/*
*----------------------------------------------------------------------
*
2024-05-27 16:40:40 +02:00
* Tcl_Main --
2024-05-27 16:13:40 +02:00
*
2024-05-27 16:40:40 +02:00
* Main program for tclsh and most other Tcl-based applications.
2024-05-27 16:13:40 +02:00
*
* Results:
2024-05-27 16:40:40 +02:00
* None. This procedure never returns (it exits the process when
* it's done.
2024-05-27 16:13:40 +02:00
*
* Side effects:
2024-05-27 16:40:40 +02:00
* This procedure initializes the Tk world and then starts
* interpreting commands; almost anything could happen, depending
* on the script being interpreted.
2024-05-27 16:13:40 +02:00
*
*----------------------------------------------------------------------
*/
2024-05-27 16:40:40 +02:00
void
Tcl_Main(argc, argv, appInitProc)
2024-05-27 16:13:40 +02:00
int argc; /* Number of arguments. */
char **argv; /* Array of argument strings. */
2024-05-27 16:40:40 +02:00
Tcl_AppInitProc *appInitProc; /* Application-specific initialization
* procedure to call after most
* initialization but before starting
* to execute commands. */
2024-05-27 16:13:40 +02:00
{
char buffer[1000], *cmd, *args, *fileName;
2024-05-27 16:40:40 +02:00
int code, gotPartial, tty, length;
2024-05-27 16:13:40 +02:00
int exitCode = 0;
2024-05-27 16:40:40 +02:00
Tcl_Channel inChannel, outChannel, errChannel;
2024-05-27 16:13:40 +02:00
2024-05-27 16:40:40 +02:00
Tcl_FindExecutable(argv[0]);
2024-05-27 16:13:40 +02:00
interp = Tcl_CreateInterp();
#ifdef TCL_MEM_DEBUG
Tcl_InitMemory(interp);
Tcl_CreateCommand(interp, "checkmem", CheckmemCmd, (ClientData) 0,
(Tcl_CmdDeleteProc *) NULL);
#endif
/*
* 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.
*/
fileName = NULL;
if ((argc > 1) && (argv[1][0] != '-')) {
fileName = argv[1];
argc--;
argv++;
}
args = Tcl_Merge(argc-1, argv+1);
Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY);
ckfree(args);
sprintf(buffer, "%d", argc-1);
Tcl_SetVar(interp, "argc", buffer, TCL_GLOBAL_ONLY);
Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv[0],
TCL_GLOBAL_ONLY);
/*
* Set the "tcl_interactive" variable.
*/
tty = isatty(0);
Tcl_SetVar(interp, "tcl_interactive",
((fileName == NULL) && tty) ? "1" : "0", TCL_GLOBAL_ONLY);
2024-05-27 16:40:40 +02:00
2024-05-27 16:13:40 +02:00
/*
* Invoke application-specific initialization.
*/
2024-05-27 16:40:40 +02:00
if ((*appInitProc)(interp) != TCL_OK) {
errChannel = Tcl_GetStdChannel(TCL_STDERR);
if (errChannel) {
Tcl_Write(errChannel,
"application-specific initialization failed: ", -1);
Tcl_Write(errChannel, interp->result, -1);
Tcl_Write(errChannel, "\n", 1);
}
2024-05-27 16:13:40 +02:00
}
/*
* If a script file was specified then just source that file
* and quit.
*/
if (fileName != NULL) {
code = Tcl_EvalFile(interp, fileName);
if (code != TCL_OK) {
2024-05-27 16:40:40 +02:00
errChannel = Tcl_GetStdChannel(TCL_STDERR);
if (errChannel) {
/*
* The following statement guarantees that the errorInfo
* variable is set properly.
*/
Tcl_AddErrorInfo(interp, "");
Tcl_Write(errChannel,
Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY), -1);
Tcl_Write(errChannel, "\n", 1);
}
2024-05-27 16:13:40 +02:00
exitCode = 1;
}
goto done;
}
/*
* We're running interactively. Source a user-specific startup
2024-05-27 16:40:40 +02:00
* file if the application specified one and if the file exists.
2024-05-27 16:13:40 +02:00
*/
2024-05-27 16:40:40 +02:00
Tcl_SourceRCFile(interp);
2024-05-27 16:13:40 +02:00
/*
2024-05-27 16:40:40 +02:00
* 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.
2024-05-27 16:13:40 +02:00
*/
gotPartial = 0;
Tcl_DStringInit(&command);
2024-05-27 16:40:40 +02:00
inChannel = Tcl_GetStdChannel(TCL_STDIN);
outChannel = Tcl_GetStdChannel(TCL_STDOUT);
2024-05-27 16:13:40 +02:00
while (1) {
if (tty) {
char *promptCmd;
promptCmd = Tcl_GetVar(interp,
gotPartial ? "tcl_prompt2" : "tcl_prompt1", TCL_GLOBAL_ONLY);
if (promptCmd == NULL) {
2024-05-27 16:40:40 +02:00
defaultPrompt:
if (!gotPartial && outChannel) {
Tcl_Write(outChannel, "% ", 2);
2024-05-27 16:13:40 +02:00
}
} else {
code = Tcl_Eval(interp, promptCmd);
2024-05-27 16:40:40 +02:00
inChannel = Tcl_GetStdChannel(TCL_STDIN);
outChannel = Tcl_GetStdChannel(TCL_STDOUT);
errChannel = Tcl_GetStdChannel(TCL_STDERR);
2024-05-27 16:13:40 +02:00
if (code != TCL_OK) {
2024-05-27 16:40:40 +02:00
if (errChannel) {
Tcl_Write(errChannel, interp->result, -1);
Tcl_Write(errChannel, "\n", 1);
}
2024-05-27 16:13:40 +02:00
Tcl_AddErrorInfo(interp,
"\n (script that generates prompt)");
goto defaultPrompt;
}
}
2024-05-27 16:40:40 +02:00
if (outChannel) {
Tcl_Flush(outChannel);
2024-05-27 16:13:40 +02:00
}
}
2024-05-27 16:40:40 +02:00
if (!inChannel) {
goto done;
}
length = Tcl_Gets(inChannel, &command);
if (length < 0) {
goto done;
}
if ((length == 0) && Tcl_Eof(inChannel) && (!gotPartial)) {
goto done;
}
/*
* Add the newline removed by Tcl_Gets back to the string.
*/
(void) Tcl_DStringAppend(&command, "\n", -1);
cmd = Tcl_DStringValue(&command);
if (!Tcl_CommandComplete(cmd)) {
2024-05-27 16:13:40 +02:00
gotPartial = 1;
continue;
}
gotPartial = 0;
code = Tcl_RecordAndEval(interp, cmd, 0);
2024-05-27 16:40:40 +02:00
inChannel = Tcl_GetStdChannel(TCL_STDIN);
outChannel = Tcl_GetStdChannel(TCL_STDOUT);
errChannel = Tcl_GetStdChannel(TCL_STDERR);
2024-05-27 16:13:40 +02:00
Tcl_DStringFree(&command);
if (code != TCL_OK) {
2024-05-27 16:40:40 +02:00
if (errChannel) {
Tcl_Write(errChannel, interp->result, -1);
Tcl_Write(errChannel, "\n", 1);
}
2024-05-27 16:13:40 +02:00
} else if (tty && (*interp->result != 0)) {
2024-05-27 16:40:40 +02:00
if (outChannel) {
Tcl_Write(outChannel, interp->result, -1);
Tcl_Write(outChannel, "\n", 1);
}
2024-05-27 16:13:40 +02:00
}
#ifdef TCL_MEM_DEBUG
if (quitFlag) {
Tcl_DeleteInterp(interp);
2024-05-27 16:40:40 +02:00
Tcl_Exit(0);
2024-05-27 16:13:40 +02:00
}
#endif
}
/*
* 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.
*/
2024-05-27 16:40:40 +02:00
done:
2024-05-27 16:13:40 +02:00
sprintf(buffer, "exit %d", exitCode);
Tcl_Eval(interp, buffer);
}
/*
*----------------------------------------------------------------------
*
* CheckmemCmd --
*
* This is the command procedure for the "checkmem" command, which
* causes the application to exit after printing information about
* memory usage to the file passed to this command as its first
* argument.
*
* Results:
* Returns a standard Tcl completion code.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
#ifdef TCL_MEM_DEBUG
/* ARGSUSED */
static int
CheckmemCmd(clientData, interp, argc, argv)
ClientData clientData; /* Not used. */
Tcl_Interp *interp; /* Interpreter for evaluation. */
int argc; /* Number of arguments. */
char *argv[]; /* String values of arguments. */
{
2024-05-27 16:40:40 +02:00
extern char *tclMemDumpFileName;
2024-05-27 16:13:40 +02:00
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" fileName\"", (char *) NULL);
return TCL_ERROR;
}
strcpy(dumpFile, argv[1]);
2024-05-27 16:40:40 +02:00
tclMemDumpFileName = dumpFile;
2024-05-27 16:13:40 +02:00
quitFlag = 1;
return TCL_OK;
}
#endif