tkMain.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:13k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkMain.c --
  3.  *
  4.  * This file contains a generic main program for Tk-based applications.
  5.  * It can be used as-is for many applications, just by supplying a
  6.  * different appInitProc procedure for each specific application.
  7.  * Or, it can be used as a template for creating new main programs
  8.  * for Tk applications.
  9.  *
  10.  * Copyright (c) 1990-1994 The Regents of the University of California.
  11.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  12.  *
  13.  * See the file "license.terms" for information on usage and redistribution
  14.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  15.  *
  16.  * RCS: @(#) $Id: tkMain.c,v 1.15.2.6 2007/03/07 23:48:13 das Exp $
  17.  */
  18. #include <ctype.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <tcl.h>
  22. #include <tclInt.h>
  23. #include <tk.h>
  24. #include "tkInt.h"
  25. #ifdef NO_STDLIB_H
  26. #   include "../compat/stdlib.h"
  27. #else
  28. #   include <stdlib.h>
  29. #endif
  30. #ifdef __WIN32__
  31. #include "tkWinInt.h"
  32. #endif
  33. #ifdef MAC_OSX_TK
  34. #include "tkMacOSXInt.h"
  35. #endif
  36. typedef struct ThreadSpecificData {
  37.     Tcl_Interp *interp;         /* Interpreter for this thread. */
  38.     Tcl_DString command;        /* Used to assemble lines of terminal input
  39.  * into Tcl commands. */
  40.     Tcl_DString line;           /* Used to read the next line from the
  41.  * terminal input. */
  42.     int tty;                    /* Non-zero means standard input is a 
  43.  * terminal-like device.  Zero means it's
  44.  * a file. */
  45. } ThreadSpecificData;
  46. static Tcl_ThreadDataKey dataKey;
  47. /*
  48.  * Declarations for various library procedures and variables (don't want
  49.  * to include tkInt.h or tkPort.h here, because people might copy this
  50.  * file out of the Tk source directory to make their own modified versions).
  51.  * Note: don't declare "exit" here even though a declaration is really
  52.  * needed, because it will conflict with a declaration elsewhere on
  53.  * some systems.
  54.  */
  55. #if !defined(__WIN32__) && !defined(_WIN32)
  56. #if !defined(MAC_TCL)
  57. extern int isatty _ANSI_ARGS_((int fd));
  58. #else
  59. #include <unistd.h>
  60. #endif
  61. extern char * strrchr _ANSI_ARGS_((CONST char *string, int c));
  62. #endif
  63. /*
  64.  * Forward declarations for procedures defined later in this file.
  65.  */
  66. static void Prompt _ANSI_ARGS_((Tcl_Interp *interp, int partial));
  67. static void StdinProc _ANSI_ARGS_((ClientData clientData,
  68.     int mask));
  69. /*
  70.  *----------------------------------------------------------------------
  71.  *
  72.  * Tk_MainEx --
  73.  *
  74.  * Main program for Wish and most other Tk-based applications.
  75.  *
  76.  * Results:
  77.  * None. This procedure never returns (it exits the process when
  78.  * it's done.
  79.  *
  80.  * Side effects:
  81.  * This procedure initializes the Tk world and then starts
  82.  * interpreting commands;  almost anything could happen, depending
  83.  * on the script being interpreted.
  84.  *
  85.  *----------------------------------------------------------------------
  86.  */
  87. void
  88. Tk_MainEx(argc, argv, appInitProc, interp)
  89.     int argc; /* Number of arguments. */
  90.     char **argv; /* Array of argument strings. */
  91.     Tcl_AppInitProc *appInitProc; /* Application-specific initialization
  92.  * procedure to call after most
  93.  * initialization but before starting
  94.  * to execute commands. */
  95.     Tcl_Interp *interp;
  96. {
  97.     Tcl_Obj *argvPtr;
  98.     int code, nullStdin = 0;
  99.     size_t length;
  100.     Tcl_Channel inChannel, outChannel;
  101.     Tcl_DString appName;
  102.     ThreadSpecificData *tsdPtr;
  103. #ifdef __WIN32__
  104.     HANDLE handle;
  105. #endif
  106.     /*
  107.      * Ensure that we are getting the matching version of Tcl.  This is
  108.      * really only an issue when Tk is loaded dynamically.
  109.      */
  110.     if (Tcl_InitStubs(interp, TCL_VERSION, 1) == NULL) {
  111. abort();
  112.     }
  113.     tsdPtr = (ThreadSpecificData *) 
  114. Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  115.     
  116.     Tcl_FindExecutable(argv[0]);
  117.     tsdPtr->interp = interp;
  118.     Tcl_Preserve((ClientData) interp);
  119. #if (defined(__WIN32__) || defined(MAC_TCL))
  120.     Tk_InitConsoleChannels(interp);
  121. #endif
  122. #ifdef MAC_OSX_TK
  123.     if (TclGetStartupScriptFileName() == NULL) {
  124.         TkMacOSXDefaultStartupScript();
  125.     }
  126. #endif
  127.     
  128. #ifdef TCL_MEM_DEBUG
  129.     Tcl_InitMemory(interp);
  130. #endif
  131.     /*
  132.      * Parse command-line arguments.  A leading "-file" argument is
  133.      * ignored (a historical relic from the distant past).  If the
  134.      * next argument doesn't start with a "-" then strip it off and
  135.      * use it as the name of a script file to process.
  136.      */
  137.     if (argc > 1) {
  138. length = strlen(argv[1]);
  139. if ((length >= 2) && (strncmp(argv[1], "-file", length) == 0)) {
  140.     argc--;
  141.     argv++;
  142. }
  143.     }
  144.     if (TclGetStartupScriptFileName() == NULL) {
  145. if ((argc > 1) && (argv[1][0] != '-')) {
  146.     TclSetStartupScriptFileName(argv[1]);
  147.     argc--;
  148.     argv++;
  149. }
  150.     }
  151.     
  152.     /*
  153.      * Make command-line arguments available in the Tcl variables "argc"
  154.      * and "argv".
  155.      */
  156.     if (TclGetStartupScriptFileName() == NULL) {
  157. Tcl_ExternalToUtfDString(NULL, argv[0], -1, &appName);
  158.     } else {
  159. TclSetStartupScriptFileName(Tcl_ExternalToUtfDString(NULL,
  160. TclGetStartupScriptFileName(), -1, &appName));
  161.     }
  162.     Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&appName), TCL_GLOBAL_ONLY);
  163.     Tcl_DStringFree(&appName);
  164.     argc--;
  165.     argv++;
  166.     Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(argc), TCL_GLOBAL_ONLY);
  167.     argvPtr = Tcl_NewListObj(0, NULL);
  168.     while (argc--) {
  169. Tcl_DString ds;
  170. Tcl_ExternalToUtfDString(NULL, *argv++, -1, &ds);
  171. Tcl_ListObjAppendElement(NULL, argvPtr, Tcl_NewStringObj(
  172. Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)));
  173. Tcl_DStringFree(&ds);
  174.     }
  175.     Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY);
  176.     /*
  177.      * Set the "tcl_interactive" variable.
  178.      */
  179. #ifdef __WIN32__
  180.     /*
  181.      * For now, under Windows, we assume we are not running as a console mode
  182.      * app, so we need to use the GUI console.  In order to enable this, we
  183.      * always claim to be running on a tty.  This probably isn't the right
  184.      * way to do it.
  185.      */
  186.     handle = GetStdHandle(STD_INPUT_HANDLE);
  187.     if ((handle == INVALID_HANDLE_VALUE) || (handle == 0) 
  188.      || (GetFileType(handle) == FILE_TYPE_UNKNOWN)) {
  189. /*
  190.  * If it's a bad or closed handle, then it's been connected
  191.  * to a wish console window.
  192.  */
  193. tsdPtr->tty = 1;
  194.     } else if (GetFileType(handle) == FILE_TYPE_CHAR) {
  195. /*
  196.  * A character file handle is a tty by definition.
  197.  */
  198. tsdPtr->tty = 1;
  199.     } else {
  200. tsdPtr->tty = 0;
  201.     }
  202. #else
  203.     tsdPtr->tty = isatty(0);
  204. #endif
  205. #if defined(MAC_OSX_TK)
  206.     /*
  207.      * On TkAqua, if we don't have a TTY and stdin is a special character file
  208.      * of length 0, (e.g. /dev/null, which is what Finder sets when double
  209.      * clicking Wish) then use the GUI console.
  210.      */
  211.     
  212.     if (!tsdPtr->tty) {
  213. struct stat st;
  214. nullStdin = fstat(0, &st) || (S_ISCHR(st.st_mode) && !st.st_blocks);
  215.     }
  216. #endif
  217.     Tcl_SetVar(interp, "tcl_interactive",
  218.     ((TclGetStartupScriptFileName() == NULL) && (tsdPtr->tty
  219.     || nullStdin)) ? "1" : "0", TCL_GLOBAL_ONLY);
  220.     /*
  221.      * Invoke application-specific initialization.
  222.      */
  223.     if ((*appInitProc)(interp) != TCL_OK) {
  224. TkpDisplayWarning(Tcl_GetStringResult(interp),
  225. "Application initialization failed");
  226.     }
  227.     /*
  228.      * Invoke the script specified on the command line, if any.
  229.      */
  230.     if (TclGetStartupScriptFileName() != NULL) {
  231. Tcl_ResetResult(interp);
  232. code = Tcl_EvalFile(interp, TclGetStartupScriptFileName());
  233. if (code != TCL_OK) {
  234.     /*
  235.      * The following statement guarantees that the errorInfo
  236.      * variable is set properly.
  237.      */
  238.     Tcl_AddErrorInfo(interp, "");
  239.     TkpDisplayWarning(Tcl_GetVar(interp, "errorInfo",
  240.     TCL_GLOBAL_ONLY), "Error in startup script");
  241.     Tcl_DeleteInterp(interp);
  242.     Tcl_Exit(1);
  243. }
  244. tsdPtr->tty = 0;
  245.     } else {
  246. /*
  247.  * Evaluate the .rc file, if one has been specified.
  248.  */
  249. Tcl_SourceRCFile(interp);
  250. /*
  251.  * Establish a channel handler for stdin.
  252.  */
  253. inChannel = Tcl_GetStdChannel(TCL_STDIN);
  254. if (inChannel) {
  255.     Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
  256.     (ClientData) inChannel);
  257. }
  258. if (tsdPtr->tty) {
  259.     Prompt(interp, 0);
  260. }
  261.     }
  262.     outChannel = Tcl_GetStdChannel(TCL_STDOUT);
  263.     if (outChannel) {
  264. Tcl_Flush(outChannel);
  265.     }
  266.     Tcl_DStringInit(&tsdPtr->command);
  267.     Tcl_DStringInit(&tsdPtr->line);
  268.     Tcl_ResetResult(interp);
  269.     /*
  270.      * Loop infinitely, waiting for commands to execute.  When there
  271.      * are no windows left, Tk_MainLoop returns and we exit.
  272.      */
  273.     Tk_MainLoop();
  274.     Tcl_DeleteInterp(interp);
  275.     Tcl_Release((ClientData) interp);
  276.     Tcl_Exit(0);
  277. }
  278. /*
  279.  *----------------------------------------------------------------------
  280.  *
  281.  * StdinProc --
  282.  *
  283.  * This procedure is invoked by the event dispatcher whenever
  284.  * standard input becomes readable.  It grabs the next line of
  285.  * input characters, adds them to a command being assembled, and
  286.  * executes the command if it's complete.
  287.  *
  288.  * Results:
  289.  * None.
  290.  *
  291.  * Side effects:
  292.  * Could be almost arbitrary, depending on the command that's
  293.  * typed.
  294.  *
  295.  *----------------------------------------------------------------------
  296.  */
  297.     /* ARGSUSED */
  298. static void
  299. StdinProc(clientData, mask)
  300.     ClientData clientData; /* Not used. */
  301.     int mask; /* Not used. */
  302. {
  303.     static int gotPartial = 0;
  304.     char *cmd;
  305.     int code, count;
  306.     Tcl_Channel chan = (Tcl_Channel) clientData;
  307.     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 
  308.             Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
  309.     Tcl_Interp *interp = tsdPtr->interp;
  310.     count = Tcl_Gets(chan, &tsdPtr->line);
  311.     if (count < 0) {
  312. if (!gotPartial) {
  313.     if (tsdPtr->tty) {
  314. Tcl_Exit(0);
  315.     } else {
  316. Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan);
  317.     }
  318.     return;
  319.     }
  320.     (void) Tcl_DStringAppend(&tsdPtr->command, Tcl_DStringValue(
  321.             &tsdPtr->line), -1);
  322.     cmd = Tcl_DStringAppend(&tsdPtr->command, "n", -1);
  323.     Tcl_DStringFree(&tsdPtr->line);
  324.     if (!Tcl_CommandComplete(cmd)) {
  325.         gotPartial = 1;
  326.         goto prompt;
  327.     }
  328.     gotPartial = 0;
  329.     /*
  330.      * Disable the stdin channel handler while evaluating the command;
  331.      * otherwise if the command re-enters the event loop we might
  332.      * process commands from stdin before the current command is
  333.      * finished.  Among other things, this will trash the text of the
  334.      * command being evaluated.
  335.      */
  336.     Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) chan);
  337.     code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL);
  338.     
  339.     chan = Tcl_GetStdChannel(TCL_STDIN);
  340.     if (chan) {
  341. Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
  342. (ClientData) chan);
  343.     }
  344.     Tcl_DStringFree(&tsdPtr->command);
  345.     if (Tcl_GetStringResult(interp)[0] != '') {
  346. if ((code != TCL_OK) || (tsdPtr->tty)) {
  347.     chan = Tcl_GetStdChannel(TCL_STDOUT);
  348.     if (chan) {
  349. Tcl_WriteObj(chan, Tcl_GetObjResult(interp));
  350. Tcl_WriteChars(chan, "n", 1);
  351.     }
  352. }
  353.     }
  354.     /*
  355.      * Output a prompt.
  356.      */
  357.     prompt:
  358.     if (tsdPtr->tty) {
  359. Prompt(interp, gotPartial);
  360.     }
  361.     Tcl_ResetResult(interp);
  362. }
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  * Prompt --
  367.  *
  368.  * Issue a prompt on standard output, or invoke a script
  369.  * to issue the prompt.
  370.  *
  371.  * Results:
  372.  * None.
  373.  *
  374.  * Side effects:
  375.  * A prompt gets output, and a Tcl script may be evaluated
  376.  * in interp.
  377.  *
  378.  *----------------------------------------------------------------------
  379.  */
  380. static void
  381. Prompt(interp, partial)
  382.     Tcl_Interp *interp; /* Interpreter to use for prompting. */
  383.     int partial; /* Non-zero means there already
  384.  * exists a partial command, so use
  385.  * the secondary prompt. */
  386. {
  387.     Tcl_Obj *promptCmd;
  388.     int code;
  389.     Tcl_Channel outChannel, errChannel;
  390.     promptCmd = Tcl_GetVar2Ex(interp,
  391. partial ? "tcl_prompt2" : "tcl_prompt1", NULL, TCL_GLOBAL_ONLY);
  392.     if (promptCmd == NULL) {
  393. defaultPrompt:
  394. if (!partial) {
  395.             /*
  396.              * We must check that outChannel is a real channel - it
  397.              * is possible that someone has transferred stdout out of
  398.              * this interpreter with "interp transfer".
  399.              */
  400.     outChannel = Tcl_GetChannel(interp, "stdout", NULL);
  401.             if (outChannel != (Tcl_Channel) NULL) {
  402.                 Tcl_WriteChars(outChannel, "% ", 2);
  403.             }
  404. }
  405.     } else {
  406. code = Tcl_EvalObjEx(interp, promptCmd, TCL_EVAL_GLOBAL);
  407. if (code != TCL_OK) {
  408.     Tcl_AddErrorInfo(interp,
  409.     "n    (script that generates prompt)");
  410.             /*
  411.              * We must check that errChannel is a real channel - it
  412.              * is possible that someone has transferred stderr out of
  413.              * this interpreter with "interp transfer".
  414.              */
  415.             
  416.     errChannel = Tcl_GetChannel(interp, "stderr", NULL);
  417.             if (errChannel != (Tcl_Channel) NULL) {
  418.                 Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
  419.                 Tcl_WriteChars(errChannel, "n", 1);
  420.             }
  421.     goto defaultPrompt;
  422. }
  423.     }
  424.     outChannel = Tcl_GetChannel(interp, "stdout", NULL);
  425.     if (outChannel != (Tcl_Channel) NULL) {
  426.         Tcl_Flush(outChannel);
  427.     }
  428. }