tclMain.cGo to the documentation of this file.00001 /* 00002 * tclMain.c -- 00003 * 00004 * Main program for Tcl shells and other Tcl-based applications. 00005 * 00006 * Copyright (c) 1988-1994 The Regents of the University of California. 00007 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 00008 * Copyright (c) 2000 Ajuba Solutions. 00009 * 00010 * See the file "license.terms" for information on usage and redistribution of 00011 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00012 * 00013 * RCS: @(#) $Id: tclMain.c,v 1.44 2007/12/13 15:23:19 dgp Exp $ 00014 */ 00015 00016 #include "tclInt.h" 00017 00018 #undef TCL_STORAGE_CLASS 00019 #define TCL_STORAGE_CLASS DLLEXPORT 00020 00021 /* 00022 * The default prompt used when the user has not overridden it. 00023 */ 00024 00025 #define DEFAULT_PRIMARY_PROMPT "% " 00026 00027 /* 00028 * Declarations for various library functions and variables (don't want to 00029 * include tclPort.h here, because people might copy this file out of the Tcl 00030 * source directory to make their own modified versions). 00031 */ 00032 00033 extern CRTIMPORT int isatty(int fd); 00034 00035 static Tcl_Obj *tclStartupScriptPath = NULL; 00036 static Tcl_Obj *tclStartupScriptEncoding = NULL; 00037 static Tcl_MainLoopProc *mainLoopProc = NULL; 00038 00039 /* 00040 * Structure definition for information used to keep the state of an 00041 * interactive command processor that reads lines from standard input and 00042 * writes prompts and results to standard output. 00043 */ 00044 00045 typedef enum { 00046 PROMPT_NONE, /* Print no prompt */ 00047 PROMPT_START, /* Print prompt for command start */ 00048 PROMPT_CONTINUE /* Print prompt for command continuation */ 00049 } PromptType; 00050 00051 typedef struct InteractiveState { 00052 Tcl_Channel input; /* The standard input channel from which lines 00053 * are read. */ 00054 int tty; /* Non-zero means standard input is a 00055 * terminal-like device. Zero means it's a 00056 * file. */ 00057 Tcl_Obj *commandPtr; /* Used to assemble lines of input into Tcl 00058 * commands. */ 00059 PromptType prompt; /* Next prompt to print */ 00060 Tcl_Interp *interp; /* Interpreter that evaluates interactive 00061 * commands. */ 00062 } InteractiveState; 00063 00064 /* 00065 * Forward declarations for functions defined later in this file. 00066 */ 00067 00068 static void Prompt(Tcl_Interp *interp, PromptType *promptPtr); 00069 static void StdinProc(ClientData clientData, int mask); 00070 00071 /* 00072 *---------------------------------------------------------------------- 00073 * 00074 * Tcl_SetStartupScript -- 00075 * 00076 * Sets the path and encoding of the startup script to be evaluated by 00077 * Tcl_Main, used to override the command line processing. 00078 * 00079 * Results: 00080 * None. 00081 * 00082 * Side effects: 00083 * 00084 *---------------------------------------------------------------------- 00085 */ 00086 00087 void 00088 Tcl_SetStartupScript( 00089 Tcl_Obj *path, /* Filesystem path of startup script file */ 00090 CONST char *encoding) /* Encoding of the data in that file */ 00091 { 00092 Tcl_Obj *newEncoding = NULL; 00093 if (encoding != NULL) { 00094 newEncoding = Tcl_NewStringObj(encoding, -1); 00095 } 00096 00097 if (tclStartupScriptPath != NULL) { 00098 Tcl_DecrRefCount(tclStartupScriptPath); 00099 } 00100 tclStartupScriptPath = path; 00101 if (tclStartupScriptPath != NULL) { 00102 Tcl_IncrRefCount(tclStartupScriptPath); 00103 } 00104 00105 if (tclStartupScriptEncoding != NULL) { 00106 Tcl_DecrRefCount(tclStartupScriptEncoding); 00107 } 00108 tclStartupScriptEncoding = newEncoding; 00109 if (tclStartupScriptEncoding != NULL) { 00110 Tcl_IncrRefCount(tclStartupScriptEncoding); 00111 } 00112 } 00113 00114 /* 00115 *---------------------------------------------------------------------- 00116 * 00117 * Tcl_GetStartupScript -- 00118 * 00119 * Gets the path and encoding of the startup script to be evaluated by 00120 * Tcl_Main. 00121 * 00122 * Results: 00123 * The path of the startup script; NULL if none has been set. 00124 * 00125 * Side effects: 00126 * If encodingPtr is not NULL, stores a (CONST char *) in it pointing to 00127 * the encoding name registered for the startup script. Tcl retains 00128 * ownership of the string, and may free it. Caller should make a copy 00129 * for long-term use. 00130 * 00131 *---------------------------------------------------------------------- 00132 */ 00133 00134 Tcl_Obj * 00135 Tcl_GetStartupScript( 00136 CONST char **encodingPtr) /* When not NULL, points to storage for the 00137 * (CONST char *) that points to the 00138 * registered encoding name for the startup 00139 * script */ 00140 { 00141 if (encodingPtr != NULL) { 00142 if (tclStartupScriptEncoding == NULL) { 00143 *encodingPtr = NULL; 00144 } else { 00145 *encodingPtr = Tcl_GetString(tclStartupScriptEncoding); 00146 } 00147 } 00148 return tclStartupScriptPath; 00149 } 00150 00151 /* 00152 *---------------------------------------------------------------------- 00153 * 00154 * TclSetStartupScriptPath -- 00155 * 00156 * Primes the startup script VFS path, used to override the command line 00157 * processing. 00158 * 00159 * Results: 00160 * None. 00161 * 00162 * Side effects: 00163 * This function initializes the VFS path of the Tcl script to run at 00164 * startup. 00165 * 00166 *---------------------------------------------------------------------- 00167 */ 00168 00169 void 00170 TclSetStartupScriptPath( 00171 Tcl_Obj *path) 00172 { 00173 Tcl_SetStartupScript(path, NULL); 00174 } 00175 00176 /* 00177 *---------------------------------------------------------------------- 00178 * 00179 * TclGetStartupScriptPath -- 00180 * 00181 * Gets the startup script VFS path, used to override the command line 00182 * processing. 00183 * 00184 * Results: 00185 * The startup script VFS path, NULL if none has been set. 00186 * 00187 * Side effects: 00188 * None. 00189 * 00190 *---------------------------------------------------------------------- 00191 */ 00192 00193 Tcl_Obj * 00194 TclGetStartupScriptPath(void) 00195 { 00196 return Tcl_GetStartupScript(NULL); 00197 } 00198 00199 /* 00200 *---------------------------------------------------------------------- 00201 * 00202 * TclSetStartupScriptFileName -- 00203 * 00204 * Primes the startup script file name, used to override the command line 00205 * processing. 00206 * 00207 * Results: 00208 * None. 00209 * 00210 * Side effects: 00211 * This function initializes the file name of the Tcl script to run at 00212 * startup. 00213 * 00214 *---------------------------------------------------------------------- 00215 */ 00216 00217 void 00218 TclSetStartupScriptFileName( 00219 CONST char *fileName) 00220 { 00221 Tcl_Obj *path = Tcl_NewStringObj(fileName,-1); 00222 Tcl_SetStartupScript(path, NULL); 00223 } 00224 00225 /* 00226 *---------------------------------------------------------------------- 00227 * 00228 * TclGetStartupScriptFileName -- 00229 * 00230 * Gets the startup script file name, used to override the command line 00231 * processing. 00232 * 00233 * Results: 00234 * The startup script file name, NULL if none has been set. 00235 * 00236 * Side effects: 00237 * None. 00238 * 00239 *---------------------------------------------------------------------- 00240 */ 00241 00242 CONST char * 00243 TclGetStartupScriptFileName(void) 00244 { 00245 Tcl_Obj *path = Tcl_GetStartupScript(NULL); 00246 00247 if (path == NULL) { 00248 return NULL; 00249 } 00250 return Tcl_GetString(path); 00251 } 00252 00253 /*---------------------------------------------------------------------- 00254 * 00255 * Tcl_SourceRCFile -- 00256 * 00257 * This function is typically invoked by Tcl_Main of Tk_Main function to 00258 * source an application specific rc file into the interpreter at startup 00259 * time. 00260 * 00261 * Results: 00262 * None. 00263 * 00264 * Side effects: 00265 * Depends on what's in the rc script. 00266 * 00267 *---------------------------------------------------------------------- 00268 */ 00269 00270 void 00271 Tcl_SourceRCFile( 00272 Tcl_Interp *interp) /* Interpreter to source rc file into. */ 00273 { 00274 Tcl_DString temp; 00275 CONST char *fileName; 00276 Tcl_Channel errChannel; 00277 00278 fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY); 00279 if (fileName != NULL) { 00280 Tcl_Channel c; 00281 CONST char *fullName; 00282 00283 Tcl_DStringInit(&temp); 00284 fullName = Tcl_TranslateFileName(interp, fileName, &temp); 00285 if (fullName == NULL) { 00286 /* 00287 * Couldn't translate the file name (e.g. it referred to a bogus 00288 * user or there was no HOME environment variable). Just do 00289 * nothing. 00290 */ 00291 } else { 00292 /* 00293 * Test for the existence of the rc file before trying to read it. 00294 */ 00295 00296 c = Tcl_OpenFileChannel(NULL, fullName, "r", 0); 00297 if (c != (Tcl_Channel) NULL) { 00298 Tcl_Close(NULL, c); 00299 if (Tcl_EvalFile(interp, fullName) != TCL_OK) { 00300 errChannel = Tcl_GetStdChannel(TCL_STDERR); 00301 if (errChannel) { 00302 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); 00303 Tcl_WriteChars(errChannel, "\n", 1); 00304 } 00305 } 00306 } 00307 } 00308 Tcl_DStringFree(&temp); 00309 } 00310 } 00311 00312 /*---------------------------------------------------------------------- 00313 * 00314 * Tcl_Main -- 00315 * 00316 * Main program for tclsh and most other Tcl-based applications. 00317 * 00318 * Results: 00319 * None. This function never returns (it exits the process when it's 00320 * done). 00321 * 00322 * Side effects: 00323 * This function initializes the Tcl world and then starts interpreting 00324 * commands; almost anything could happen, depending on the script being 00325 * interpreted. 00326 * 00327 *---------------------------------------------------------------------- 00328 */ 00329 00330 void 00331 Tcl_Main( 00332 int argc, /* Number of arguments. */ 00333 char **argv, /* Array of argument strings. */ 00334 Tcl_AppInitProc *appInitProc) 00335 /* Application-specific initialization 00336 * function to call after most initialization 00337 * but before starting to execute commands. */ 00338 { 00339 Tcl_Obj *path, *resultPtr, *argvPtr, *commandPtr = NULL; 00340 CONST char *encodingName = NULL; 00341 PromptType prompt = PROMPT_START; 00342 int code, length, tty, exitCode = 0; 00343 Tcl_Channel inChannel, outChannel, errChannel; 00344 Tcl_Interp *interp; 00345 Tcl_DString appName; 00346 00347 Tcl_FindExecutable(argv[0]); 00348 00349 interp = Tcl_CreateInterp(); 00350 Tcl_InitMemory(interp); 00351 00352 /* 00353 * If the application has not already set a startup script, parse the 00354 * first few command line arguments to determine the script path and 00355 * encoding. 00356 */ 00357 00358 if (NULL == Tcl_GetStartupScript(NULL)) { 00359 00360 /* 00361 * Check whether first 3 args (argv[1] - argv[3]) look like 00362 * -encoding ENCODING FILENAME 00363 * or like 00364 * FILENAME 00365 */ 00366 00367 if ((argc > 3) && (0 == strcmp("-encoding", argv[1])) 00368 && ('-' != argv[3][0])) { 00369 Tcl_SetStartupScript(Tcl_NewStringObj(argv[3], -1), argv[2]); 00370 argc -= 3; 00371 argv += 3; 00372 } else if ((argc > 1) && ('-' != argv[1][0])) { 00373 Tcl_SetStartupScript(Tcl_NewStringObj(argv[1], -1), NULL); 00374 argc--; 00375 argv++; 00376 } 00377 } 00378 00379 path = Tcl_GetStartupScript(&encodingName); 00380 if (path == NULL) { 00381 Tcl_ExternalToUtfDString(NULL, argv[0], -1, &appName); 00382 } else { 00383 CONST char *pathName = Tcl_GetStringFromObj(path, &length); 00384 Tcl_ExternalToUtfDString(NULL, pathName, length, &appName); 00385 path = Tcl_NewStringObj(Tcl_DStringValue(&appName), -1); 00386 Tcl_SetStartupScript(path, encodingName); 00387 } 00388 Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&appName), TCL_GLOBAL_ONLY); 00389 Tcl_DStringFree(&appName); 00390 argc--; 00391 argv++; 00392 00393 Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(argc), TCL_GLOBAL_ONLY); 00394 00395 argvPtr = Tcl_NewListObj(0, NULL); 00396 while (argc--) { 00397 Tcl_DString ds; 00398 Tcl_ExternalToUtfDString(NULL, *argv++, -1, &ds); 00399 Tcl_ListObjAppendElement(NULL, argvPtr, Tcl_NewStringObj( 00400 Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); 00401 Tcl_DStringFree(&ds); 00402 } 00403 Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY); 00404 00405 /* 00406 * Set the "tcl_interactive" variable. 00407 */ 00408 00409 tty = isatty(0); 00410 Tcl_SetVar(interp, "tcl_interactive", ((path == NULL) && tty) ? "1" : "0", 00411 TCL_GLOBAL_ONLY); 00412 00413 /* 00414 * Invoke application-specific initialization. 00415 */ 00416 00417 Tcl_Preserve((ClientData) interp); 00418 if ((*appInitProc)(interp) != TCL_OK) { 00419 errChannel = Tcl_GetStdChannel(TCL_STDERR); 00420 if (errChannel) { 00421 Tcl_WriteChars(errChannel, 00422 "application-specific initialization failed: ", -1); 00423 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); 00424 Tcl_WriteChars(errChannel, "\n", 1); 00425 } 00426 } 00427 if (Tcl_InterpDeleted(interp)) { 00428 goto done; 00429 } 00430 if (Tcl_LimitExceeded(interp)) { 00431 goto done; 00432 } 00433 00434 /* 00435 * If a script file was specified then just source that file and quit. 00436 * Must fetch it again, as the appInitProc might have reset it. 00437 */ 00438 00439 path = Tcl_GetStartupScript(&encodingName); 00440 if (path != NULL) { 00441 code = Tcl_FSEvalFileEx(interp, path, encodingName); 00442 if (code != TCL_OK) { 00443 errChannel = Tcl_GetStdChannel(TCL_STDERR); 00444 if (errChannel) { 00445 Tcl_Obj *options = Tcl_GetReturnOptions(interp, code); 00446 Tcl_Obj *keyPtr, *valuePtr; 00447 00448 TclNewLiteralStringObj(keyPtr, "-errorinfo"); 00449 Tcl_IncrRefCount(keyPtr); 00450 Tcl_DictObjGet(NULL, options, keyPtr, &valuePtr); 00451 Tcl_DecrRefCount(keyPtr); 00452 00453 if (valuePtr) { 00454 Tcl_WriteObj(errChannel, valuePtr); 00455 } 00456 Tcl_WriteChars(errChannel, "\n", 1); 00457 } 00458 exitCode = 1; 00459 } 00460 goto done; 00461 } 00462 00463 /* 00464 * We're running interactively. Source a user-specific startup file if the 00465 * application specified one and if the file exists. 00466 */ 00467 00468 Tcl_SourceRCFile(interp); 00469 if (Tcl_LimitExceeded(interp)) { 00470 goto done; 00471 } 00472 00473 /* 00474 * Process commands from stdin until there's an end-of-file. Note that we 00475 * need to fetch the standard channels again after every eval, since they 00476 * may have been changed. 00477 */ 00478 00479 commandPtr = Tcl_NewObj(); 00480 Tcl_IncrRefCount(commandPtr); 00481 00482 /* 00483 * Get a new value for tty if anyone writes to ::tcl_interactive 00484 */ 00485 00486 Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, TCL_LINK_BOOLEAN); 00487 inChannel = Tcl_GetStdChannel(TCL_STDIN); 00488 outChannel = Tcl_GetStdChannel(TCL_STDOUT); 00489 while ((inChannel != (Tcl_Channel) NULL) && !Tcl_InterpDeleted(interp)) { 00490 if (mainLoopProc == NULL) { 00491 if (tty) { 00492 Prompt(interp, &prompt); 00493 if (Tcl_InterpDeleted(interp)) { 00494 break; 00495 } 00496 if (Tcl_LimitExceeded(interp)) { 00497 break; 00498 } 00499 inChannel = Tcl_GetStdChannel(TCL_STDIN); 00500 if (inChannel == (Tcl_Channel) NULL) { 00501 break; 00502 } 00503 } 00504 if (Tcl_IsShared(commandPtr)) { 00505 Tcl_DecrRefCount(commandPtr); 00506 commandPtr = Tcl_DuplicateObj(commandPtr); 00507 Tcl_IncrRefCount(commandPtr); 00508 } 00509 length = Tcl_GetsObj(inChannel, commandPtr); 00510 if (length < 0) { 00511 if (Tcl_InputBlocked(inChannel)) { 00512 /* 00513 * This can only happen if stdin has been set to 00514 * non-blocking. In that case cycle back and try again. 00515 * This sets up a tight polling loop (since we have no 00516 * event loop running). If this causes bad CPU hogging, 00517 * we might try toggling the blocking on stdin instead. 00518 */ 00519 00520 continue; 00521 } 00522 00523 /* 00524 * Either EOF, or an error on stdin; we're done 00525 */ 00526 00527 break; 00528 } 00529 00530 /* 00531 * Add the newline removed by Tcl_GetsObj back to the string. 00532 * Have to add it back before testing completeness, because 00533 * it can make a difference. [Bug 1775878]. 00534 */ 00535 00536 if (Tcl_IsShared(commandPtr)) { 00537 Tcl_DecrRefCount(commandPtr); 00538 commandPtr = Tcl_DuplicateObj(commandPtr); 00539 Tcl_IncrRefCount(commandPtr); 00540 } 00541 Tcl_AppendToObj(commandPtr, "\n", 1); 00542 if (!TclObjCommandComplete(commandPtr)) { 00543 prompt = PROMPT_CONTINUE; 00544 continue; 00545 } 00546 00547 prompt = PROMPT_START; 00548 /* 00549 * The final newline is syntactically redundant, and causes 00550 * some error messages troubles deeper in, so lop it back off. 00551 */ 00552 Tcl_GetStringFromObj(commandPtr, &length); 00553 Tcl_SetObjLength(commandPtr, --length); 00554 code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL); 00555 inChannel = Tcl_GetStdChannel(TCL_STDIN); 00556 outChannel = Tcl_GetStdChannel(TCL_STDOUT); 00557 errChannel = Tcl_GetStdChannel(TCL_STDERR); 00558 Tcl_DecrRefCount(commandPtr); 00559 commandPtr = Tcl_NewObj(); 00560 Tcl_IncrRefCount(commandPtr); 00561 if (code != TCL_OK) { 00562 if (errChannel) { 00563 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); 00564 Tcl_WriteChars(errChannel, "\n", 1); 00565 } 00566 } else if (tty) { 00567 resultPtr = Tcl_GetObjResult(interp); 00568 Tcl_IncrRefCount(resultPtr); 00569 Tcl_GetStringFromObj(resultPtr, &length); 00570 if ((length > 0) && outChannel) { 00571 Tcl_WriteObj(outChannel, resultPtr); 00572 Tcl_WriteChars(outChannel, "\n", 1); 00573 } 00574 Tcl_DecrRefCount(resultPtr); 00575 } 00576 } else { /* (mainLoopProc != NULL) */ 00577 /* 00578 * If a main loop has been defined while running interactively, we 00579 * want to start a fileevent based prompt by establishing a 00580 * channel handler for stdin. 00581 */ 00582 00583 InteractiveState *isPtr = NULL; 00584 00585 if (inChannel) { 00586 if (tty) { 00587 Prompt(interp, &prompt); 00588 } 00589 isPtr = (InteractiveState *) 00590 ckalloc((int) sizeof(InteractiveState)); 00591 isPtr->input = inChannel; 00592 isPtr->tty = tty; 00593 isPtr->commandPtr = commandPtr; 00594 isPtr->prompt = prompt; 00595 isPtr->interp = interp; 00596 00597 Tcl_UnlinkVar(interp, "tcl_interactive"); 00598 Tcl_LinkVar(interp, "tcl_interactive", (char *) &(isPtr->tty), 00599 TCL_LINK_BOOLEAN); 00600 00601 Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc, 00602 (ClientData) isPtr); 00603 } 00604 00605 (*mainLoopProc)(); 00606 mainLoopProc = NULL; 00607 00608 if (inChannel) { 00609 tty = isPtr->tty; 00610 Tcl_UnlinkVar(interp, "tcl_interactive"); 00611 Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, 00612 TCL_LINK_BOOLEAN); 00613 prompt = isPtr->prompt; 00614 commandPtr = isPtr->commandPtr; 00615 if (isPtr->input != (Tcl_Channel) NULL) { 00616 Tcl_DeleteChannelHandler(isPtr->input, StdinProc, 00617 (ClientData) isPtr); 00618 } 00619 ckfree((char *)isPtr); 00620 } 00621 inChannel = Tcl_GetStdChannel(TCL_STDIN); 00622 outChannel = Tcl_GetStdChannel(TCL_STDOUT); 00623 errChannel = Tcl_GetStdChannel(TCL_STDERR); 00624 } 00625 #ifdef TCL_MEM_DEBUG 00626 00627 /* 00628 * This code here only for the (unsupported and deprecated) [checkmem] 00629 * command. 00630 */ 00631 00632 if (tclMemDumpFileName != NULL) { 00633 mainLoopProc = NULL; 00634 Tcl_DeleteInterp(interp); 00635 } 00636 #endif 00637 } 00638 00639 done: 00640 if ((exitCode == 0) && (mainLoopProc != NULL) 00641 && !Tcl_LimitExceeded(interp)) { 00642 /* 00643 * If everything has gone OK so far, call the main loop proc, if it 00644 * exists. Packages (like Tk) can set it to start processing events at 00645 * this point. 00646 */ 00647 00648 (*mainLoopProc)(); 00649 mainLoopProc = NULL; 00650 } 00651 if (commandPtr != NULL) { 00652 Tcl_DecrRefCount(commandPtr); 00653 } 00654 00655 /* 00656 * Rather than calling exit, invoke the "exit" command so that users can 00657 * replace "exit" with some other command to do additional cleanup on 00658 * exit. The Tcl_EvalObjEx call should never return. 00659 */ 00660 00661 if (!Tcl_InterpDeleted(interp)) { 00662 if (!Tcl_LimitExceeded(interp)) { 00663 Tcl_Obj *cmd = Tcl_ObjPrintf("exit %d", exitCode); 00664 Tcl_IncrRefCount(cmd); 00665 Tcl_EvalObjEx(interp, cmd, TCL_EVAL_GLOBAL); 00666 Tcl_DecrRefCount(cmd); 00667 } 00668 00669 /* 00670 * If Tcl_EvalObjEx returns, trying to eval [exit], something unusual 00671 * is happening. Maybe interp has been deleted; maybe [exit] was 00672 * redefined, maybe we've blown up because of an exceeded limit. We 00673 * still want to cleanup and exit. 00674 */ 00675 00676 if (!Tcl_InterpDeleted(interp)) { 00677 Tcl_DeleteInterp(interp); 00678 } 00679 } 00680 Tcl_SetStartupScript(NULL, NULL); 00681 00682 /* 00683 * If we get here, the master interp has been deleted. Allow its 00684 * destruction with the last matching Tcl_Release. 00685 */ 00686 00687 Tcl_Release((ClientData) interp); 00688 Tcl_Exit(exitCode); 00689 } 00690 00691 /* 00692 *--------------------------------------------------------------- 00693 * 00694 * Tcl_SetMainLoop -- 00695 * 00696 * Sets an alternative main loop function. 00697 * 00698 * Results: 00699 * Returns the previously defined main loop function. 00700 * 00701 * Side effects: 00702 * This function will be called before Tcl exits, allowing for the 00703 * creation of an event loop. 00704 * 00705 *--------------------------------------------------------------- 00706 */ 00707 00708 void 00709 Tcl_SetMainLoop( 00710 Tcl_MainLoopProc *proc) 00711 { 00712 mainLoopProc = proc; 00713 } 00714 00715 /* 00716 *---------------------------------------------------------------------- 00717 * 00718 * StdinProc -- 00719 * 00720 * This function is invoked by the event dispatcher whenever standard 00721 * input becomes readable. It grabs the next line of input characters, 00722 * adds them to a command being assembled, and executes the command if 00723 * it's complete. 00724 * 00725 * Results: 00726 * None. 00727 * 00728 * Side effects: 00729 * Could be almost arbitrary, depending on the command that's typed. 00730 * 00731 *---------------------------------------------------------------------- 00732 */ 00733 00734 /* ARGSUSED */ 00735 static void 00736 StdinProc( 00737 ClientData clientData, /* The state of interactive cmd line */ 00738 int mask) /* Not used. */ 00739 { 00740 InteractiveState *isPtr = (InteractiveState *) clientData; 00741 Tcl_Channel chan = isPtr->input; 00742 Tcl_Obj *commandPtr = isPtr->commandPtr; 00743 Tcl_Interp *interp = isPtr->interp; 00744 int code, length; 00745 00746 if (Tcl_IsShared(commandPtr)) { 00747 Tcl_DecrRefCount(commandPtr); 00748 commandPtr = Tcl_DuplicateObj(commandPtr); 00749 Tcl_IncrRefCount(commandPtr); 00750 } 00751 length = Tcl_GetsObj(chan, commandPtr); 00752 if (length < 0) { 00753 if (Tcl_InputBlocked(chan)) { 00754 return; 00755 } 00756 if (isPtr->tty) { 00757 /* 00758 * Would be better to find a way to exit the mainLoop? Or perhaps 00759 * evaluate [exit]? Leaving as is for now due to compatibility 00760 * concerns. 00761 */ 00762 00763 Tcl_Exit(0); 00764 } 00765 Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) isPtr); 00766 return; 00767 } 00768 00769 if (Tcl_IsShared(commandPtr)) { 00770 Tcl_DecrRefCount(commandPtr); 00771 commandPtr = Tcl_DuplicateObj(commandPtr); 00772 Tcl_IncrRefCount(commandPtr); 00773 } 00774 Tcl_AppendToObj(commandPtr, "\n", 1); 00775 if (!TclObjCommandComplete(commandPtr)) { 00776 isPtr->prompt = PROMPT_CONTINUE; 00777 goto prompt; 00778 } 00779 isPtr->prompt = PROMPT_START; 00780 Tcl_GetStringFromObj(commandPtr, &length); 00781 Tcl_SetObjLength(commandPtr, --length); 00782 00783 /* 00784 * Disable the stdin channel handler while evaluating the command; 00785 * otherwise if the command re-enters the event loop we might process 00786 * commands from stdin before the current command is finished. Among other 00787 * things, this will trash the text of the command being evaluated. 00788 */ 00789 00790 Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) isPtr); 00791 code = Tcl_RecordAndEvalObj(interp, commandPtr, TCL_EVAL_GLOBAL); 00792 isPtr->input = chan = Tcl_GetStdChannel(TCL_STDIN); 00793 Tcl_DecrRefCount(commandPtr); 00794 isPtr->commandPtr = commandPtr = Tcl_NewObj(); 00795 Tcl_IncrRefCount(commandPtr); 00796 if (chan != (Tcl_Channel) NULL) { 00797 Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc, 00798 (ClientData) isPtr); 00799 } 00800 if (code != TCL_OK) { 00801 Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR); 00802 if (errChannel != (Tcl_Channel) NULL) { 00803 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); 00804 Tcl_WriteChars(errChannel, "\n", 1); 00805 } 00806 } else if (isPtr->tty) { 00807 Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); 00808 Tcl_Channel outChannel = Tcl_GetStdChannel(TCL_STDOUT); 00809 Tcl_IncrRefCount(resultPtr); 00810 Tcl_GetStringFromObj(resultPtr, &length); 00811 if ((length >0) && (outChannel != (Tcl_Channel) NULL)) { 00812 Tcl_WriteObj(outChannel, resultPtr); 00813 Tcl_WriteChars(outChannel, "\n", 1); 00814 } 00815 Tcl_DecrRefCount(resultPtr); 00816 } 00817 00818 /* 00819 * If a tty stdin is still around, output a prompt. 00820 */ 00821 00822 prompt: 00823 if (isPtr->tty && (isPtr->input != (Tcl_Channel) NULL)) { 00824 Prompt(interp, &(isPtr->prompt)); 00825 isPtr->input = Tcl_GetStdChannel(TCL_STDIN); 00826 } 00827 } 00828 00829 /* 00830 *---------------------------------------------------------------------- 00831 * 00832 * Prompt -- 00833 * 00834 * Issue a prompt on standard output, or invoke a script to issue the 00835 * prompt. 00836 * 00837 * Results: 00838 * None. 00839 * 00840 * Side effects: 00841 * A prompt gets output, and a Tcl script may be evaluated in interp. 00842 * 00843 *---------------------------------------------------------------------- 00844 */ 00845 00846 static void 00847 Prompt( 00848 Tcl_Interp *interp, /* Interpreter to use for prompting. */ 00849 PromptType *promptPtr) /* Points to type of prompt to print. Filled 00850 * with PROMPT_NONE after a prompt is 00851 * printed. */ 00852 { 00853 Tcl_Obj *promptCmdPtr; 00854 int code; 00855 Tcl_Channel outChannel, errChannel; 00856 00857 if (*promptPtr == PROMPT_NONE) { 00858 return; 00859 } 00860 00861 promptCmdPtr = Tcl_GetVar2Ex(interp, 00862 ((*promptPtr == PROMPT_CONTINUE) ? "tcl_prompt2" : "tcl_prompt1"), 00863 NULL, TCL_GLOBAL_ONLY); 00864 00865 if (Tcl_InterpDeleted(interp)) { 00866 return; 00867 } 00868 if (promptCmdPtr == NULL) { 00869 defaultPrompt: 00870 outChannel = Tcl_GetStdChannel(TCL_STDOUT); 00871 if ((*promptPtr == PROMPT_START) 00872 && (outChannel != (Tcl_Channel) NULL)) { 00873 Tcl_WriteChars(outChannel, DEFAULT_PRIMARY_PROMPT, 00874 strlen(DEFAULT_PRIMARY_PROMPT)); 00875 } 00876 } else { 00877 code = Tcl_EvalObjEx(interp, promptCmdPtr, TCL_EVAL_GLOBAL); 00878 if (code != TCL_OK) { 00879 Tcl_AddErrorInfo(interp, 00880 "\n (script that generates prompt)"); 00881 errChannel = Tcl_GetStdChannel(TCL_STDERR); 00882 if (errChannel != (Tcl_Channel) NULL) { 00883 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp)); 00884 Tcl_WriteChars(errChannel, "\n", 1); 00885 } 00886 goto defaultPrompt; 00887 } 00888 } 00889 00890 outChannel = Tcl_GetStdChannel(TCL_STDOUT); 00891 if (outChannel != (Tcl_Channel) NULL) { 00892 Tcl_Flush(outChannel); 00893 } 00894 *promptPtr = PROMPT_NONE; 00895 } 00896 00897 /* 00898 * Local Variables: 00899 * mode: c 00900 * c-basic-offset: 4 00901 * fill-column: 78 00902 * End: 00903 */
Generated on Wed Mar 12 12:18:19 2008 by 1.5.1 |