tkMain.cpp

Go to the documentation of this file.
00001 /* 
00002  * 
00003  * TkMain.c --
00004  *
00005  *      This file contains a generic main program for Tk-based applications.
00006  *      It can be used as-is for many applications, just by supplying a
00007  *      different appInitProc procedure for each specific application.
00008  *      Or, it can be used as a template for creating new main programs
00009  *      for Tk applications.
00010  *
00011  * Copyright (c) 1990-1994 The Regents of the University of California.
00012  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
00013  *
00014  * See the file "license.terms" for information on usage and redistribution
00015  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00016  *
00017  * RCS: @(#) $Id: tkMain.cpp,v 1.19 2006/09/26 18:58:12 fmk Exp $
00018  */
00019 
00020 /*                       MODIFIED   FOR                              */
00021 
00022 /* ****************************************************************** **
00023 **    OpenSees - Open System for Earthquake Engineering Simulation    **
00024 **          Pacific Earthquake Engineering Research Center            **
00025 ** ****************************************************************** */
00026 
00027 extern "C" {
00028 #include <ctype.h>
00029 #include <stdio.h>
00030 #include <string.h>
00031 #include <tcl.h>
00032 #include <tk.h>
00033 #ifdef NO_STDLIB_H
00034 #   include "../compat/stdlib.h"
00035 #else
00036 #   include <stdlib.h>
00037 #endif
00038 }
00039 #ifdef __WIN32__
00040 #include <tkWin.h>
00041 #endif
00042 
00043 #include <OPS_Globals.h>
00044 
00045 
00046 #include <FileStream.h>
00047 #include <SimulationInformation.h>
00048 SimulationInformation simulationInfo;
00049 char *simulationInfoOutputFilename = 0;
00050 
00051 
00052 typedef struct ThreadSpecificData {
00053     Tcl_Interp *interp;         /* Interpreter for this thread. */
00054     Tcl_DString command;        /* Used to assemble lines of terminal input
00055                                  * into Tcl commands. */
00056     Tcl_DString line;           /* Used to read the next line from the
00057                                  * terminal input. */
00058     int tty;                    /* Non-zero means standard input is a 
00059                                  * terminal-like device.  Zero means it's
00060                                  * a file. */
00061 } ThreadSpecificData;
00062 Tcl_ThreadDataKey dataKey;
00063 
00064 /*
00065  * Declarations for various library procedures and variables (don't want
00066  * to include tkInt.h or tkPort.h here, because people might copy this
00067  * file out of the Tk source directory to make their own modified versions).
00068  * Note: don't declare "exit" here even though a declaration is really
00069  * needed, because it will conflict with a declaration elsewhere on
00070  * some systems.
00071  */
00072 
00073 #if !defined(__WIN32__) && !defined(_WIN32) && !defined(_KAI)
00074 extern "C" int          isatty _ANSI_ARGS_((int fd));
00075 extern "C" char *       strrchr _ANSI_ARGS_((CONST char *string, int c)) throw();
00076 #endif
00077 
00078 #if !defined(__WIN32__) && !defined(_WIN32) && defined(_KAI)
00079 extern "C" int          isatty _ANSI_ARGS_((int fd));
00080 extern "C" char *       strrchr _ANSI_ARGS_((CONST char *string, int c));
00081 #endif
00082 
00083 #ifdef _TCL84
00084 extern "C" void  TkpDisplayWarning _ANSI_ARGS_((const char *msg, char *title));
00085 #else
00086 extern "C" void  TkpDisplayWarning _ANSI_ARGS_((char *msg, char *title));
00087 #endif
00088 
00089 /*
00090  * Forward declarations for procedures defined later in this file.
00091  */
00092 
00093 static void             Prompt _ANSI_ARGS_((Tcl_Interp *interp, int partial));
00094 static void             StdinProc _ANSI_ARGS_((ClientData clientData,
00095                             int mask));
00096 
00097 
00098 static char *tclStartupScriptFileName = NULL;
00099 
00100 void TclSetStartupScriptFileName(char *fileName)
00101 {
00102     tclStartupScriptFileName = fileName;
00103 }
00104 
00105 
00106 char *TclGetStartupScriptFileName()
00107 {
00108     return tclStartupScriptFileName;
00109 }
00110 
00111 
00112 
00113 
00114 /*
00115  *----------------------------------------------------------------------
00116  *
00117  * Tk_MainOpenSees --
00118  *
00119  *      Main program for Wish and most other Tk-based applications.
00120  *
00121  * Results:
00122  *      None. This procedure never returns (it exits the process when
00123  *      it's done.
00124  *
00125  * Side effects:
00126  *      This procedure initializes the Tk world and then starts
00127  *      interpreting commands;  almost anything could happen, depending
00128  *      on the script being interpreted.
00129  *
00130  *----------------------------------------------------------------------
00131  */
00132 void
00133 Tk_MainOpenSees(int argc, char **argv, Tcl_AppInitProc *appInitProc, Tcl_Interp *interp)
00134 {
00135     char *args, *fileName;
00136     char buf[TCL_INTEGER_SPACE];
00137     int code;
00138     size_t length;
00139     Tcl_Channel inChannel, outChannel;
00140     Tcl_DString argString;
00141     ThreadSpecificData *tsdPtr;
00142 
00143 #ifdef __WIN32__
00144     HANDLE handle;
00145 #endif
00146 
00147     /* fmk - beginning of modifications for OpenSees */
00148     fprintf(stderr,"\n\n\t OpenSees -- Open System For Earthquake Engineering Simulation");
00149     fprintf(stderr,"\n\tPacific Earthquake Engineering Research Center -- %s\n\n", OPS_VERSION);
00150     
00151     fprintf(stderr,"\t    (c) Copyright 1999 The Regents of the University of California");
00152     fprintf(stderr,"\n\t\t\t\t All Rights Reserved \n\n\n");    
00153     fprintf(stderr,"\t(Copyright statement @ http://www.berkeley.edu/OpenSees/copyright.html)\n\n\n");
00154     /* fmk - end of modifications for OpenSees */
00155 
00156     /*
00157      * Ensure that we are getting the matching version of Tcl.  This is
00158      * really only an issue when Tk is loaded dynamically.
00159      */
00160 
00161     if (Tcl_InitStubs(interp, TCL_VERSION, 1) == NULL) {
00162         abort();
00163     }
00164 
00165     tsdPtr = (ThreadSpecificData *) 
00166         Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
00167     
00168     Tcl_FindExecutable(argv[0]);
00169     tsdPtr->interp = interp;
00170 
00171 #if (defined(__WIN32__) || defined(MAC_TCL))
00172     Tk_InitConsoleChannels(interp);
00173 #endif
00174     
00175 #ifdef TCL_MEM_DEBUG
00176     Tcl_InitMemory(interp);
00177 #endif
00178 
00179     /*
00180      * Parse command-line arguments.  A leading "-file" argument is
00181      * ignored (a historical relic from the distant past).  If the
00182      * next argument doesn't start with a "-" then strip it off and
00183      * use it as the name of a script file to process.
00184      */
00185 
00186     fileName = TclGetStartupScriptFileName();
00187 
00188     if (argc > 1) {
00189         length = strlen(argv[1]);
00190         if ((length >= 2) && (strncmp(argv[1], "-file", length) == 0)) {
00191             argc--;
00192             argv++;
00193         }
00194     }
00195     if (fileName == NULL) {
00196         if ((argc > 1) && (argv[1][0] != '-')) {
00197             fileName = argv[1];
00198             argc--;
00199             argv++;
00200         }
00201     }
00202     
00203     /*
00204      * Make command-line arguments available in the Tcl variables "argc"
00205      * and "argv".
00206      */
00207 
00208     args = Tcl_Merge(argc-1, argv+1);
00209     Tcl_ExternalToUtfDString(NULL, args, -1, &argString);
00210     Tcl_SetVar(interp, "argv", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);
00211     Tcl_DStringFree(&argString);
00212     ckfree(args);
00213     sprintf(buf, "%d", argc-1);
00214 
00215     if (fileName == NULL) {
00216         Tcl_ExternalToUtfDString(NULL, argv[0], -1, &argString);
00217     } else {
00218         fileName = Tcl_ExternalToUtfDString(NULL, fileName, -1, &argString);
00219     }
00220     Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);
00221     Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);
00222 
00223     /*
00224      * Set the "tcl_interactive" variable.
00225      */
00226 
00227     /*
00228      * For now, under Windows, we assume we are not running as a console mode
00229      * app, so we need to use the GUI console.  In order to enable this, we
00230      * always claim to be running on a tty.  This probably isn't the right
00231      * way to do it.
00232      */
00233 
00234 #ifdef __WIN32__
00235     handle = GetStdHandle(STD_INPUT_HANDLE);
00236 
00237     if ((handle == INVALID_HANDLE_VALUE) || (handle == 0) 
00238              || (GetFileType(handle) == FILE_TYPE_UNKNOWN)) {
00239         /*
00240          * If it's a bad or closed handle, then it's been connected
00241          * to a wish console window.
00242          */
00243 
00244         tsdPtr->tty = 1;
00245     } else if (GetFileType(handle) == FILE_TYPE_CHAR) {
00246         /*
00247          * A character file handle is a tty by definition.
00248          */
00249 
00250         tsdPtr->tty = 1;
00251     } else {
00252         tsdPtr->tty = 0;
00253     }
00254 
00255 #else
00256     tsdPtr->tty = isatty(0);
00257 #endif
00258     char one[2] = "1";
00259     char zero[2] = "0";
00260     Tcl_SetVar(interp, "tcl_interactive",
00261             ((fileName == NULL) && tsdPtr->tty) ? one : zero, TCL_GLOBAL_ONLY);
00262 
00263     /*
00264      * Invoke application-specific initialization.
00265      */
00266         if ((*appInitProc)(interp) != TCL_OK) {
00267       TkpDisplayWarning(Tcl_GetStringResult(interp), 
00268         "Application initialization failed");
00269         }
00270     /*
00271      * Invoke the script specified on the command line, if any.
00272      */
00273 
00274     if (fileName != NULL) {
00275         Tcl_ResetResult(interp);
00276         code = Tcl_EvalFile(interp, fileName);
00277         if (code != TCL_OK) {
00278             /*
00279              * The following statement guarantees that the errorInfo
00280              * variable is set properly.
00281              */
00282 
00283             Tcl_AddErrorInfo(interp, "");
00284             TkpDisplayWarning(Tcl_GetVar(interp, "errorInfo",
00285                                          TCL_GLOBAL_ONLY), "Error in startup script");
00286             Tcl_DeleteInterp(interp);
00287             Tcl_Exit(1);
00288         }
00289         tsdPtr->tty = 0;
00290     } else {
00291 
00292         /*
00293          * Evaluate the .rc file, if one has been specified.
00294          */
00295 
00296         Tcl_SourceRCFile(interp);
00297 
00298         /*
00299          * Establish a channel handler for stdin.
00300          */
00301 
00302         inChannel = Tcl_GetStdChannel(TCL_STDIN);
00303         if (inChannel) {
00304             Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
00305                     (ClientData) inChannel);
00306         }
00307         if (tsdPtr->tty) {
00308             Prompt(interp, 0);
00309         }
00310     }
00311     Tcl_DStringFree(&argString);
00312 
00313     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
00314     if (outChannel) {
00315         Tcl_Flush(outChannel);
00316     }
00317     Tcl_DStringInit(&tsdPtr->command);
00318     Tcl_DStringInit(&tsdPtr->line);
00319     Tcl_ResetResult(interp);
00320 
00321     /*
00322      * Loop infinitely, waiting for commands to execute.  When there
00323      * are no windows left, Tk_MainLoop returns and we exit.
00324      */
00325 
00326     Tk_MainLoop();
00327     Tcl_DeleteInterp(interp);
00328     Tcl_Exit(0);
00329 }
00330 
00331 /*
00332  *----------------------------------------------------------------------
00333  *
00334  * StdinProc --
00335  *
00336  *      This procedure is invoked by the event dispatcher whenever
00337  *      standard input becomes readable.  It grabs the next line of
00338  *      input characters, adds them to a command being assembled, and
00339  *      executes the command if it's complete.
00340  *
00341  * Results:
00342  *      None.
00343  *
00344  * Side effects:
00345  *      Could be almost arbitrary, depending on the command that's
00346  *      typed.
00347  *
00348  *----------------------------------------------------------------------
00349  */
00350 
00351     /* ARGSUSED */
00352 static void
00353 StdinProc(ClientData clientData, int mask)
00354 {
00355     static int gotPartial = 0;
00356     char *cmd;
00357     int code, count;
00358     Tcl_Channel chan = (Tcl_Channel) clientData;
00359     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
00360             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
00361     Tcl_Interp *interp = tsdPtr->interp;
00362 
00363     count = Tcl_Gets(chan, &tsdPtr->line);
00364 
00365     if (count < 0) {
00366         if (!gotPartial) {
00367             if (tsdPtr->tty) {
00368                 Tcl_Exit(0);
00369             } else {
00370                 Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan);
00371             }
00372             return;
00373         } 
00374     }
00375 
00376     (void) Tcl_DStringAppend(&tsdPtr->command, Tcl_DStringValue(
00377             &tsdPtr->line), -1);
00378     cmd = Tcl_DStringAppend(&tsdPtr->command, "\n", -1);
00379     Tcl_DStringFree(&tsdPtr->line);
00380     if (!Tcl_CommandComplete(cmd)) {
00381         gotPartial = 1;
00382         goto prompt;
00383     }
00384     gotPartial = 0;
00385 
00386     /*
00387      * Disable the stdin channel handler while evaluating the command;
00388      * otherwise if the command re-enters the event loop we might
00389      * process commands from stdin before the current command is
00390      * finished.  Among other things, this will trash the text of the
00391      * command being evaluated.
00392      */
00393 
00394     Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) chan);
00395     code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL);
00396     
00397     chan = Tcl_GetStdChannel(TCL_STDIN);
00398     if (chan) {
00399         Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
00400                 (ClientData) chan);
00401     }
00402     Tcl_DStringFree(&tsdPtr->command);
00403     if (Tcl_GetStringResult(interp)[0] != '\0') {
00404         if ((code != TCL_OK) || (tsdPtr->tty)) {
00405             chan = Tcl_GetStdChannel(TCL_STDOUT);
00406             if (chan) {
00407                 Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
00408                 Tcl_WriteChars(chan, "\n", 1);
00409             }
00410         }
00411     }
00412 
00413     /*
00414      * Output a prompt.
00415      */
00416 
00417     prompt:
00418     if (tsdPtr->tty) {
00419         Prompt(interp, gotPartial);
00420     }
00421     Tcl_ResetResult(interp);
00422 }
00423 
00424 /*
00425  *----------------------------------------------------------------------
00426  *
00427  * Prompt --
00428  *
00429  *      Issue a prompt on standard output, or invoke a script
00430  *      to issue the prompt.
00431  *
00432  * Results:
00433  *      None.
00434  *
00435  * Side effects:
00436  *      A prompt gets output, and a Tcl script may be evaluated
00437  *      in interp.
00438  *
00439  *----------------------------------------------------------------------
00440  */
00441 
00442 static void
00443 Prompt(Tcl_Interp *interp, int partial)
00444 {
00445 
00446 #ifdef _TCL84
00447   const char *promptCmd;
00448   const char one[12] = "tcl_prompt1";
00449   const char two[12] = "tcl_prompt2";
00450 #else
00451   char *promptCmd;
00452   char one[12] = "tcl_prompt1";
00453   char two[12] = "tcl_prompt2";
00454 #endif
00455 
00456   int code;
00457   Tcl_Channel outChannel, errChannel;
00458 
00459   promptCmd = Tcl_GetVar(interp, partial ? two : one, TCL_GLOBAL_ONLY);
00460                            
00461   if (promptCmd == NULL) {
00462   defaultPrompt:
00463         if (!partial) {
00464           
00465           /*
00466            * We must check that outChannel is a real channel - it
00467            * is possible that someone has transferred stdout out of
00468            * this interpreter with "interp transfer".
00469            */
00470           
00471           outChannel = Tcl_GetChannel(interp, "stdout", NULL);
00472           if (outChannel != (Tcl_Channel) NULL) {
00473             Tcl_WriteChars(outChannel, "OpenSees > ", 11);
00474           }
00475         }
00476     } else {
00477       code = Tcl_Eval(interp, promptCmd);
00478       if (code != TCL_OK) {
00479         Tcl_AddErrorInfo(interp,
00480                          "\n    (script that generates prompt)");
00481         /*
00482          * We must check that errChannel is a real channel - it
00483          * is possible that someone has transferred stderr out of
00484          * this interpreter with "interp transfer".
00485          */
00486         
00487         errChannel = Tcl_GetChannel(interp, "stderr", NULL);
00488         if (errChannel != (Tcl_Channel) NULL) {
00489           Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
00490           Tcl_WriteChars(errChannel, "\n", 1);
00491         }
00492         goto defaultPrompt;
00493       }
00494     }
00495   outChannel = Tcl_GetChannel(interp, "stdout", NULL);
00496   if (outChannel != (Tcl_Channel) NULL) {
00497     Tcl_Flush(outChannel);
00498   }
00499 }
00500 
00501 
00502 int OpenSeesExit(ClientData clientData, Tcl_Interp *interp, int argc, TCL_Char **argv)
00503 {
00504   Tcl_Exit(0);
00505   return 0;
00506 }
00507 

Generated on Mon Oct 23 15:05:31 2006 for OpenSees by doxygen 1.5.0