mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-09-05 00:50:50 +02:00
supported). Fixed the long-standing issue in which DRC does not get stopped by the "drc off" command (the behavior for interrupting the DRC was dependent on the DRC being turned on, and the "drc off" command was turning it off before breaking, causing the interrupt to be ignored).
1286 lines
36 KiB
C
1286 lines
36 KiB
C
/*----------------------------------------------------------------------*/
|
|
/* tclmagic.c --- Creates the interpreter-wrapped version of magic. */
|
|
/* */
|
|
/* Written by Tim Edwards August 2002 */
|
|
/* */
|
|
/* Note that this file is tied to Tcl. The original version (from */
|
|
/* around April 2002) relied on SWIG, the only differences being */
|
|
/* as few %{ ... %} boundaries and the replacement of the */
|
|
/* Tclmagic_Init function header with "%init %{", and call the */
|
|
/* file "tclmagic.i". However, the rest of the associated wrapper */
|
|
/* code got so dependent on Tcl commands that there is no longer any */
|
|
/* point in using SWIG. */
|
|
/* */
|
|
/* When using SWIG, the Makefile requires: */
|
|
/* */
|
|
/* tclmagic.c: tclmagic.i */
|
|
/* swig -tcl8 -o tclmagic.c tclmagic.i */
|
|
/* */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
#include "utils/main.h"
|
|
#include "utils/magic.h"
|
|
#include "utils/geometry.h"
|
|
#include "tiles/tile.h"
|
|
#include "utils/hash.h"
|
|
#include "utils/dqueue.h"
|
|
#include "database/database.h"
|
|
#include "windows/windows.h"
|
|
#include "commands/commands.h"
|
|
#include "utils/utils.h"
|
|
#include "textio/textio.h"
|
|
#include "textio/txcommands.h"
|
|
#include "utils/signals.h"
|
|
#include "graphics/graphics.h"
|
|
#include "utils/malloc.h"
|
|
#include "dbwind/dbwind.h"
|
|
|
|
/*
|
|
* String containing the version number of magic. Don't change the string
|
|
* here, nor its format. It is updated by the Makefile in this directory.
|
|
*/
|
|
|
|
char *MagicVersion = MAGIC_VERSION;
|
|
char *MagicRevision = MAGIC_REVISION;
|
|
char *MagicCompileTime = MAGIC_DATE;
|
|
|
|
Tcl_Interp *magicinterp;
|
|
Tcl_Interp *consoleinterp;
|
|
|
|
HashTable txTclTagTable;
|
|
|
|
Tcl_ChannelType inChannel;
|
|
|
|
/* Forward declarations */
|
|
|
|
int TerminalInputProc(ClientData, char *, int, int *);
|
|
void TxFlushErr();
|
|
void TxFlushOut();
|
|
void RegisterTkCommands();
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Verify if a command has a tag callback. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
int
|
|
TagVerify(keyword)
|
|
char *keyword;
|
|
{
|
|
char *croot, *postcmd;
|
|
HashEntry *entry;
|
|
|
|
/* Skip over namespace qualifier, if any */
|
|
|
|
croot = keyword;
|
|
if (!strncmp(croot, "::", 2)) croot += 2;
|
|
if (!strncmp(croot, "magic::", 7)) croot += 7;
|
|
|
|
entry = HashLookOnly(&txTclTagTable, croot);
|
|
postcmd = (entry) ? (char *)HashGetValue(entry) : NULL;
|
|
return (postcmd) ? TRUE : FALSE;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Find any tags associated with a command and execute them. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static int
|
|
TagCallback(interp, tkpath, argc, argv)
|
|
Tcl_Interp *interp;
|
|
char *tkpath;
|
|
int argc; /* original command's number of arguments */
|
|
char *argv[]; /* original command's argument list */
|
|
{
|
|
int argidx, result = TCL_OK;
|
|
char *postcmd, *substcmd, *newcmd, *sptr, *sres;
|
|
char *croot;
|
|
HashEntry *entry;
|
|
Tcl_SavedResult state;
|
|
bool reset = FALSE;
|
|
int cmdnum;
|
|
|
|
/* No command, no action */
|
|
|
|
if (argc == 0) return TCL_OK;
|
|
|
|
/* Skip over namespace qualifier, if any */
|
|
|
|
croot = argv[0];
|
|
if (!strncmp(croot, "::", 2)) croot += 2;
|
|
if (!strncmp(croot, "magic::", 7)) croot += 7;
|
|
|
|
entry = HashLookOnly(&txTclTagTable, croot);
|
|
postcmd = (entry) ? (char *)HashGetValue(entry) : NULL;
|
|
|
|
if (postcmd)
|
|
{
|
|
/* The Tag callback should not increase the command number */
|
|
/* sequence, so save it now and restore it before returning. */
|
|
cmdnum = TxCommandNumber;
|
|
|
|
substcmd = (char *)mallocMagic(strlen(postcmd) + 1);
|
|
strcpy(substcmd, postcmd);
|
|
sptr = substcmd;
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Parse "postcmd" for Tk-substitution escapes */
|
|
/* Allowed escapes are: */
|
|
/* %W substitute the tk path of the layout window */
|
|
/* %r substitute the previous Tcl result string */
|
|
/* %R substitute the previous Tcl result string and */
|
|
/* reset the Tcl result. */
|
|
/* %[0-5] substitute the argument to the original command */
|
|
/* %% substitute a single percent character */
|
|
/* %* (all others) no action: print as-is. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
while ((sptr = strchr(sptr, '%')) != NULL)
|
|
{
|
|
switch (*(sptr + 1))
|
|
{
|
|
case 'W':
|
|
|
|
/* In the case of the %W escape, first we see if a Tk */
|
|
/* path has been passed in the argument. If not, get */
|
|
/* the window path if there is only one window. */
|
|
/* Otherwise, the window is unknown so we substitute */
|
|
/* a null list "{}". */
|
|
|
|
if (tkpath == NULL)
|
|
{
|
|
MagWindow *w = NULL;
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
if (w != NULL && !(w->w_flags & WIND_OFFSCREEN))
|
|
{
|
|
Tk_Window tkwind = (Tk_Window) w->w_grdata;
|
|
if (tkwind != NULL) tkpath = Tk_PathName(tkwind);
|
|
}
|
|
}
|
|
if (tkpath == NULL)
|
|
newcmd = (char *)mallocMagic(strlen(substcmd) + 2);
|
|
else
|
|
newcmd = (char *)mallocMagic(strlen(substcmd) + strlen(tkpath));
|
|
|
|
strcpy(newcmd, substcmd);
|
|
|
|
if (tkpath == NULL)
|
|
strcpy(newcmd + (int)(sptr - substcmd), "{}");
|
|
else
|
|
strcpy(newcmd + (int)(sptr - substcmd), tkpath);
|
|
|
|
strcat(newcmd, sptr + 2);
|
|
freeMagic(substcmd);
|
|
substcmd = newcmd;
|
|
sptr = substcmd;
|
|
break;
|
|
|
|
case 'R':
|
|
reset = TRUE;
|
|
case 'r':
|
|
sres = (char *)Tcl_GetStringResult(magicinterp);
|
|
newcmd = (char *)mallocMagic(strlen(substcmd)
|
|
+ strlen(sres) + 1);
|
|
strcpy(newcmd, substcmd);
|
|
sprintf(newcmd + (int)(sptr - substcmd), "\"%s\"", sres);
|
|
strcat(newcmd, sptr + 2);
|
|
freeMagic(substcmd);
|
|
substcmd = newcmd;
|
|
sptr = substcmd;
|
|
|
|
break;
|
|
|
|
case '0': case '1': case '2': case '3': case '4': case '5':
|
|
argidx = (int)(*(sptr + 1) - '0');
|
|
if ((argidx >= 0) && (argidx < argc))
|
|
{
|
|
newcmd = (char *)mallocMagic(strlen(substcmd)
|
|
+ strlen(argv[argidx]));
|
|
strcpy(newcmd, substcmd);
|
|
strcpy(newcmd + (int)(sptr - substcmd), argv[argidx]);
|
|
strcat(newcmd, sptr + 2);
|
|
freeMagic(substcmd);
|
|
substcmd = newcmd;
|
|
sptr = substcmd;
|
|
}
|
|
else if (argidx >= argc)
|
|
{
|
|
newcmd = (char *)mallocMagic(strlen(substcmd) + 1);
|
|
strcpy(newcmd, substcmd);
|
|
strcpy(newcmd + (int)(sptr - substcmd), sptr + 2);
|
|
freeMagic(substcmd);
|
|
substcmd = newcmd;
|
|
sptr = substcmd;
|
|
}
|
|
else sptr++;
|
|
break;
|
|
|
|
case '%':
|
|
newcmd = (char *)mallocMagic(strlen(substcmd) + 1);
|
|
strcpy(newcmd, substcmd);
|
|
strcpy(newcmd + (int)(sptr - substcmd), sptr + 1);
|
|
freeMagic(substcmd);
|
|
substcmd = newcmd;
|
|
sptr = substcmd;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* fprintf(stderr, "Substituted tag callback is \"%s\"\n", substcmd); */
|
|
/* fflush(stderr); */
|
|
|
|
Tcl_SaveResult(interp, &state);
|
|
result = Tcl_EvalEx(interp, substcmd, -1, 0);
|
|
if ((result == TCL_OK) && (reset == FALSE))
|
|
Tcl_RestoreResult(interp, &state);
|
|
else
|
|
Tcl_DiscardResult(&state);
|
|
|
|
freeMagic(substcmd);
|
|
TxCommandNumber = cmdnum; /* restore original value */
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Add a command tag callback */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static int
|
|
AddCommandTag(ClientData clientData,
|
|
Tcl_Interp *interp, int argc, char *argv[])
|
|
{
|
|
HashEntry *entry;
|
|
char *hstring;
|
|
|
|
if (argc != 2 && argc != 3)
|
|
return TCL_ERROR;
|
|
|
|
entry = HashFind(&txTclTagTable, argv[1]);
|
|
|
|
if (entry == NULL) return TCL_ERROR;
|
|
|
|
hstring = (char *)HashGetValue(entry);
|
|
|
|
if (argc == 2)
|
|
{
|
|
Tcl_SetResult(magicinterp, hstring, NULL);
|
|
return TCL_OK;
|
|
}
|
|
|
|
if (hstring != NULL) freeMagic(hstring);
|
|
|
|
if (strlen(argv[2]) == 0)
|
|
{
|
|
HashSetValue(entry, NULL);
|
|
}
|
|
else
|
|
{
|
|
hstring = StrDup((char **)NULL, argv[2]);
|
|
HashSetValue(entry, hstring);
|
|
}
|
|
return TCL_OK;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Dispatch a command from Tcl */
|
|
/* See TxTclDispatch() in textio/txCommands.c */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static int
|
|
_tcl_dispatch(ClientData clientData,
|
|
Tcl_Interp *interp, int argc, char *argv[])
|
|
{
|
|
int wval;
|
|
int result, idx;
|
|
Tcl_Obj *objv0;
|
|
char *argv0, *tkwind;
|
|
|
|
/* Check command (argv[0]) against known conflicting */
|
|
/* command names. If the command is potentially a */
|
|
/* Tcl/Tk command, try it as such, first. If a Tcl */
|
|
/* error is returned, then try it as a magic */
|
|
/* command. Note that the other way (try the magic */
|
|
/* command first) would necessitate setting Tcl */
|
|
/* results for every magic command. Too much work. */
|
|
|
|
static char *conflicts[] =
|
|
{
|
|
"clockwise", "flush", "load", "label", "array", "grid", NULL
|
|
};
|
|
static char *resolutions[] =
|
|
{
|
|
"orig_clock", "tcl_flush", "tcl_load", "tcl_label", "tcl_array",
|
|
"tcl_grid", NULL
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
IDX_CLOCK, IDX_FLUSH, IDX_LOAD, IDX_LABEL, IDX_ARRAY,
|
|
IDX_GRID
|
|
} conflictCommand;
|
|
|
|
/* Skip any "::" namespace prefix before parsing */
|
|
argv0 = argv[0];
|
|
if (!strncmp(argv0, "::", 2)) argv0 += 2;
|
|
|
|
objv0 = Tcl_NewStringObj(argv0, strlen(argv0));
|
|
if (Tcl_GetIndexFromObj(interp, objv0, (CONST84 char **)conflicts,
|
|
"overloaded command", 0, &idx) == TCL_OK)
|
|
{
|
|
int i;
|
|
Tcl_Obj **objv = (Tcl_Obj **)Tcl_Alloc(argc * sizeof(Tcl_Obj *));
|
|
|
|
/* Create a Tcl_Obj array suitable for calling Tcl_EvalObjv. */
|
|
/* The first argument is changed from the magic command name to */
|
|
/* "tcl" + the command name. This assumes that all conflicting */
|
|
/* command names have been so renamed in the startup script! */
|
|
|
|
objv[0] = Tcl_NewStringObj(resolutions[idx], strlen(resolutions[idx]));
|
|
Tcl_IncrRefCount(objv[0]);
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
objv[i] = Tcl_NewStringObj(argv[i], strlen(argv[i]));
|
|
Tcl_IncrRefCount(objv[i]);
|
|
}
|
|
|
|
result = Tcl_EvalObjv(interp, argc, objv, 0);
|
|
|
|
for (i = 0; i < argc; i++)
|
|
Tcl_DecrRefCount(objv[i]);
|
|
Tcl_Free((char *)objv);
|
|
|
|
if (result == TCL_OK)
|
|
return result;
|
|
|
|
/* The rule is to execute Magic commands for any Tcl command */
|
|
/* with the same name that returns an error. However, this */
|
|
/* rule hangs magic when the "load" command is used on a shared */
|
|
/* object file that fails to load properly. So if the filename */
|
|
/* has an extension which is not ".mag" or ".gz", we will */
|
|
/* return the error. */
|
|
|
|
/* Updated 1/20/2015: Need to check for a '.' AFTER the last */
|
|
/* slash, so as to avoid problems with ./, ../, etc. */
|
|
|
|
if (idx == IDX_LOAD)
|
|
{
|
|
char *dotptr, *slashptr;
|
|
if (argc >= 2)
|
|
{
|
|
slashptr = strrchr(argv[1], '/');
|
|
if (slashptr == NULL)
|
|
slashptr = argv[1];
|
|
else
|
|
slashptr++;
|
|
|
|
if ((dotptr = strrchr(slashptr, '.')) != NULL)
|
|
if (strcmp(dotptr + 1, "mag") && strcmp(dotptr + 1, "gz"))
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
Tcl_ResetResult(interp);
|
|
|
|
if (TxInputRedirect == TX_INPUT_REDIRECTED)
|
|
TxInputRedirect = TX_INPUT_PENDING_RESET;
|
|
|
|
wval = TxTclDispatch(clientData, argc, argv, TRUE);
|
|
|
|
if (TxInputRedirect == TX_INPUT_PENDING_RESET)
|
|
TxInputRedirect = TX_INPUT_NORMAL;
|
|
|
|
/* If the command did not pass through _tk_dispatch, but the command was */
|
|
/* entered by key redirection from a window, then TxInputRedirect will be */
|
|
/* set to TX_INPUT_PROCESSING and the window ID will have been set by */
|
|
/* TxSetPoint(). Do our level best to find the Tk window name. */
|
|
|
|
if (TxInputRedirect == TX_INPUT_PROCESSING)
|
|
{
|
|
if (GrWindowNamePtr)
|
|
{
|
|
MagWindow *mw = WindSearchWid(TxGetPoint(NULL));
|
|
if (mw != NULL)
|
|
tkwind = (*GrWindowNamePtr)(mw);
|
|
else
|
|
tkwind = NULL;
|
|
}
|
|
else
|
|
tkwind = NULL;
|
|
}
|
|
else
|
|
tkwind = NULL;
|
|
|
|
// Pass back an error if TxTclDispatch failed
|
|
if (wval != 0) return TCL_ERROR;
|
|
|
|
return TagCallback(interp, tkwind, argc, argv);
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Dispatch a window-related command. The first argument is */
|
|
/* the window to which the command should be directed, so we */
|
|
/* determine which window this is, set "TxCurCommand" values */
|
|
/* to point to the window, then dispatch the command. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static int
|
|
_tk_dispatch(ClientData clientData,
|
|
Tcl_Interp *interp, int argc, char *argv[])
|
|
{
|
|
int id;
|
|
char *tkpath;
|
|
char *arg0;
|
|
Point txp;
|
|
|
|
if (GrWindowIdPtr)
|
|
{
|
|
/* Key macros set the point from the graphics module code but */
|
|
/* set up the command to be dispatched via _tk_dispatch(). */
|
|
/* Therefore it is necessary to check if a point position */
|
|
/* has already been set for this command. If not, then the */
|
|
/* command was probably called from the command entry window, */
|
|
/* so we choose an arbitrary point which is somewhere in the */
|
|
/* window, so that command functions have a point of reference. */
|
|
|
|
id = (*GrWindowIdPtr)(argv[0]);
|
|
|
|
if (TxGetPoint(&txp) != id)
|
|
{
|
|
/* This is a point in the window, inside the */
|
|
/* scrollbars if they are managed by magic. */
|
|
|
|
txp.p_x = 20;
|
|
txp.p_y = 20;
|
|
}
|
|
TxSetPoint(txp.p_x, txp.p_y, id);
|
|
arg0 = argv[0];
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
TxTclDispatch(clientData, argc, argv, FALSE);
|
|
|
|
/* Get pathname of window and pass to TagCallback */
|
|
return TagCallback(interp, arg0, argc, argv);
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Set up a window to use commands via _tk_dispatch */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
MakeWindowCommand(char *wname, MagWindow *mw)
|
|
{
|
|
char *tclcmdstr;
|
|
|
|
Tcl_CreateCommand(magicinterp, wname, (Tcl_CmdProc *)_tk_dispatch,
|
|
(ClientData)mw, (Tcl_CmdDeleteProc *) NULL);
|
|
|
|
/* Force the window manager to use magic's "close" command to close */
|
|
/* down a window. */
|
|
|
|
tclcmdstr = (char *)mallocMagic(52 + 2 * strlen(wname));
|
|
sprintf(tclcmdstr, "wm protocol %s WM_DELETE_WINDOW "
|
|
"{magic::closewindow %s}", wname, wname);
|
|
Tcl_EvalEx(magicinterp, tclcmdstr, -1, 0);
|
|
freeMagic(tclcmdstr);
|
|
}
|
|
|
|
/*------------------------------------------------------*/
|
|
/* Main startup procedure */
|
|
/*------------------------------------------------------*/
|
|
|
|
static int
|
|
_magic_initialize(ClientData clientData,
|
|
Tcl_Interp *interp, int argc, char *argv[])
|
|
{
|
|
WindClient client;
|
|
int n, i;
|
|
char keyword[100];
|
|
char *kwptr = keyword + 7;
|
|
char **commandTable;
|
|
int result;
|
|
|
|
/* Is magic being executed in a slave interpreter? */
|
|
|
|
if ((consoleinterp = Tcl_GetMaster(interp)) == NULL)
|
|
consoleinterp = interp;
|
|
|
|
// Force tkcon to send output to terminal during initialization
|
|
else
|
|
{
|
|
RuntimeFlags |= (MAIN_TK_CONSOLE | MAIN_TK_PRINTF);
|
|
Tcl_Eval(consoleinterp, "rename ::puts ::unused_puts\n");
|
|
Tcl_Eval(consoleinterp, "rename ::tkcon_tcl_puts ::puts\n");
|
|
}
|
|
|
|
/* Did we start in the same interpreter as we initialized? */
|
|
if (magicinterp != interp)
|
|
{
|
|
TxError("Warning: Switching interpreters. Tcl-magic is not set up "
|
|
"to handle this.\n");
|
|
magicinterp = interp;
|
|
}
|
|
|
|
if (mainInitBeforeArgs(argc, argv) != 0) goto magicfatal;
|
|
if (mainDoArgs(argc, argv) != 0) goto magicfatal;
|
|
|
|
// Redirect output back to the console
|
|
if (TxTkConsole)
|
|
{
|
|
RuntimeFlags &= ~MAIN_TK_PRINTF;
|
|
Tcl_Eval(consoleinterp, "rename ::puts ::tkcon_tcl_puts\n");
|
|
Tcl_Eval(consoleinterp, "rename ::unused_puts ::puts\n");
|
|
}
|
|
|
|
/* Identify version and revision */
|
|
|
|
TxPrintf("\nMagic %s revision %s - Compiled on %s.\n", MagicVersion,
|
|
MagicRevision, MagicCompileTime);
|
|
TxPrintf("Starting magic under Tcl interpreter\n");
|
|
if (TxTkConsole)
|
|
TxPrintf("Using Tk console window\n");
|
|
else
|
|
TxPrintf("Using the terminal as the console.\n");
|
|
TxFlushOut();
|
|
|
|
if (mainInitAfterArgs() != 0) goto magicfatal;
|
|
|
|
/* Registration of commands is performed after calling the */
|
|
/* start function, not after initialization, as the command */
|
|
/* modularization requires magic initialization to get a */
|
|
/* valid DBWclientID, windClientID, etc. */
|
|
|
|
sprintf(keyword, "magic::");
|
|
|
|
/* Work through all the known clients, and register the */
|
|
/* commands of all of them. */
|
|
|
|
client = (WindClient)NULL;
|
|
while ((client = WindNextClient(client)) != NULL)
|
|
{
|
|
commandTable = WindGetCommandTable(client);
|
|
for (n = 0; commandTable[n] != NULL; n++)
|
|
{
|
|
sscanf(commandTable[n], "%s ", kwptr); /* get first word */
|
|
Tcl_CreateCommand(interp, keyword, (Tcl_CmdProc *)_tcl_dispatch,
|
|
(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);
|
|
}
|
|
}
|
|
|
|
/* Extra commands provided by the Tk graphics routines */
|
|
/* (See graphics/grTkCommon.c) */
|
|
/* (Unless "-dnull" option has been given) */
|
|
|
|
if (strcmp(MainDisplayType, "NULL"))
|
|
RegisterTkCommands(interp);
|
|
|
|
/* Set up the console so that its menu option File->Exit */
|
|
/* calls magic's exit routine first. This should not be */
|
|
/* done in console.tcl, or else it puts the console in a */
|
|
/* state where it is difficult to exit, if magic doesn't */
|
|
/* start up correctly. */
|
|
|
|
if (TxTkConsole)
|
|
{
|
|
Tcl_Eval(consoleinterp, "rename ::exit ::quit\n");
|
|
Tcl_Eval(consoleinterp, "proc ::exit args {slave eval quit}\n");
|
|
}
|
|
|
|
return TCL_OK;
|
|
|
|
magicfatal:
|
|
TxResetTerminal();
|
|
Tcl_SetResult(interp, "Magic initialization encountered a fatal error.", NULL);
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
typedef struct FileState {
|
|
Tcl_Channel channel;
|
|
int fd;
|
|
int validMask;
|
|
} FileState;
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* "Wizard" command for manipulating run-time flags. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static int
|
|
_magic_flags(ClientData clientData,
|
|
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
|
|
{
|
|
int index, index2;
|
|
bool value;
|
|
static char *flagOptions[] = {"debug", "recover", "silent",
|
|
"window", "console", "printf", (char *)NULL};
|
|
static char *yesNo[] = {"off", "no", "false", "0", "on", "yes",
|
|
"true", "1", (char *)NULL};
|
|
|
|
if ((objc != 2) && (objc != 3)) {
|
|
Tcl_WrongNumArgs(interp, 1, objv, "flag ?value?");
|
|
return TCL_ERROR;
|
|
}
|
|
if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)flagOptions,
|
|
"option", 0, &index) != TCL_OK) {
|
|
return TCL_ERROR;
|
|
}
|
|
if (objc == 2) {
|
|
switch (index) {
|
|
case 0:
|
|
value = (RuntimeFlags & MAIN_DEBUG) ? TRUE : FALSE;
|
|
break;
|
|
case 1:
|
|
value = (RuntimeFlags & MAIN_RECOVER) ? TRUE : FALSE;
|
|
break;
|
|
case 2:
|
|
value = (RuntimeFlags & MAIN_SILENT) ? TRUE : FALSE;
|
|
break;
|
|
case 3:
|
|
value = (RuntimeFlags & MAIN_MAKE_WINDOW) ? TRUE : FALSE;
|
|
break;
|
|
case 4:
|
|
value = (RuntimeFlags & MAIN_TK_CONSOLE) ? TRUE : FALSE;
|
|
break;
|
|
case 5:
|
|
value = (RuntimeFlags & MAIN_TK_PRINTF) ? TRUE : FALSE;
|
|
break;
|
|
}
|
|
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(value));
|
|
}
|
|
else {
|
|
if (Tcl_GetIndexFromObj(interp, objv[2], (CONST84 char **)yesNo,
|
|
"value", 0, &index2) != TCL_OK)
|
|
return TCL_ERROR;
|
|
|
|
value = (index2 > 3) ? TRUE : FALSE;
|
|
switch (index) {
|
|
case 0:
|
|
if (value == TRUE)
|
|
RuntimeFlags |= MAIN_DEBUG;
|
|
else
|
|
RuntimeFlags &= ~MAIN_DEBUG;
|
|
break;
|
|
case 1:
|
|
if (value == TRUE)
|
|
RuntimeFlags |= MAIN_RECOVER;
|
|
else
|
|
RuntimeFlags &= ~MAIN_RECOVER;
|
|
break;
|
|
case 2:
|
|
if (value == TRUE)
|
|
RuntimeFlags |= MAIN_SILENT;
|
|
else
|
|
RuntimeFlags &= ~MAIN_SILENT;
|
|
break;
|
|
case 3:
|
|
if (value == TRUE)
|
|
RuntimeFlags |= MAIN_MAKE_WINDOW;
|
|
else
|
|
RuntimeFlags &= ~MAIN_MAKE_WINDOW;
|
|
break;
|
|
case 4:
|
|
if (value == TRUE)
|
|
RuntimeFlags |= MAIN_TK_CONSOLE;
|
|
else
|
|
RuntimeFlags &= ~MAIN_TK_CONSOLE;
|
|
break;
|
|
case 5:
|
|
if (value == TRUE)
|
|
RuntimeFlags |= MAIN_TK_PRINTF;
|
|
else
|
|
RuntimeFlags &= ~MAIN_TK_PRINTF;
|
|
break;
|
|
}
|
|
}
|
|
return TCL_OK;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Pre-initialization: Return the state of the graphics type */
|
|
/* so that certain steps can be taken when NULL graphics are */
|
|
/* specified. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static int
|
|
_magic_display(ClientData clientData,
|
|
Tcl_Interp *interp, int argc, char *argv[])
|
|
{
|
|
/* Set the result to the name of the graphics mode used. */
|
|
Tcl_SetResult(magicinterp, MainDisplayType, NULL);
|
|
return TCL_OK;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Post-initialization: read in the magic startup files and */
|
|
/* load any initial layout. Note that this is not done via */
|
|
/* script, but probably should be. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static int
|
|
_magic_startup(ClientData clientData,
|
|
Tcl_Interp *interp, int argc, char *argv[])
|
|
{
|
|
/* Execute contents of startup files and load any initial cell */
|
|
|
|
if (mainInitFinal() != 0)
|
|
{
|
|
/* We don't want mainInitFinal errors to return TCL_ERROR from */
|
|
/* magic::start; otherwise, the window won't come up. As long */
|
|
/* as we have successfully passed mainInitAfterArgs(), magic is */
|
|
/* fundamentally sound. */
|
|
|
|
Tcl_SetResult(interp,
|
|
"Magic encountered problems with the startup files.",
|
|
NULL);
|
|
}
|
|
|
|
TxResetTerminal();
|
|
|
|
if (TxTkConsole)
|
|
{
|
|
Tcl_EvalEx(consoleinterp, "tkcon set ::tkcon::OPT(showstatusbar) 1", -1, 0);
|
|
TxSetPrompt('%');
|
|
}
|
|
else
|
|
{
|
|
Tcl_Channel oldchannel;
|
|
Tcl_ChannelType *stdChannel;
|
|
FileState *fsPtr, *fsOrig;
|
|
|
|
/* Use the terminal. */
|
|
/* Replace the input proc for stdin with our own. */
|
|
|
|
oldchannel = Tcl_GetStdChannel(TCL_STDIN); // Get existing stdin
|
|
fsOrig = Tcl_GetChannelInstanceData(oldchannel);
|
|
|
|
/* Copy the structure from the old to the new channel */
|
|
stdChannel = (Tcl_ChannelType *)Tcl_GetChannelType(oldchannel);
|
|
memcpy(&inChannel, stdChannel, sizeof(Tcl_ChannelType));
|
|
inChannel.inputProc = TerminalInputProc;
|
|
|
|
fsPtr = (FileState *)Tcl_Alloc(sizeof(FileState));
|
|
fsPtr->validMask = fsOrig->validMask;
|
|
fsPtr->fd = fsOrig->fd;
|
|
fsPtr->channel = Tcl_CreateChannel(&inChannel, "stdin",
|
|
(ClientData)fsPtr, TCL_READABLE);
|
|
|
|
Tcl_SetStdChannel(fsPtr->channel, TCL_STDIN); // Apply new stdin
|
|
Tcl_RegisterChannel(NULL, fsPtr->channel);
|
|
}
|
|
|
|
return TCL_OK;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Tk version of TxDialog */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
int
|
|
TxDialog(prompt, responses, defresp)
|
|
char *prompt;
|
|
char *(responses[]);
|
|
int defresp;
|
|
{
|
|
Tcl_Obj *objPtr;
|
|
int code, result, pos;
|
|
char *evalstr, *newstr;
|
|
|
|
/* Ensure that use of TxPrintString doesn't overwrite the */
|
|
/* value of prompt my making a copy of it. */
|
|
/* 5/11/05---use Tcl_escape() to do the duplication; this */
|
|
/* ensures that cell names with special characters like '$' */
|
|
/* will be handled properly. */
|
|
|
|
newstr = Tcl_escape(prompt);
|
|
/* newstr = StrDup((char **)NULL, prompt); */
|
|
evalstr = TxPrintString("tk_dialog .dialog \"Dialog\""
|
|
" \"%s\" {} %d ", newstr, defresp);
|
|
/* freeMagic(newstr); */
|
|
Tcl_Free(newstr); /* Tcl_escape() uses Tcl_Alloc() */
|
|
|
|
for (pos = 0; responses[pos] != 0; pos++)
|
|
{
|
|
newstr = StrDup((char **)NULL, evalstr);
|
|
evalstr = TxPrintString("%s \"%s\" ", newstr,
|
|
responses[pos]);
|
|
freeMagic(newstr);
|
|
}
|
|
|
|
Tcl_EvalEx(magicinterp, evalstr, -1, 0);
|
|
objPtr = Tcl_GetObjResult(magicinterp);
|
|
result = Tcl_GetIntFromObj(magicinterp, objPtr, &code);
|
|
|
|
if (result == TCL_OK) return code;
|
|
else return -1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* TxUseMore and TxStopMore are dummy functions, although they */
|
|
/* could be used to set up a top-level window containing the */
|
|
/* result (redefine "puts" to write to the window). */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
TxUseMore()
|
|
{
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
TxStopMore()
|
|
{
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Set the prompt, if we are using the TkCon console */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
extern char txPromptChar;
|
|
|
|
void
|
|
TxSetPrompt(ch)
|
|
char ch;
|
|
{
|
|
Tcl_SavedResult state;
|
|
char promptline[16];
|
|
|
|
if (TxTkConsole)
|
|
{
|
|
sprintf(promptline, "replaceprompt %c", ch);
|
|
Tcl_SaveResult(consoleinterp, &state);
|
|
Tcl_EvalEx(consoleinterp, promptline, 15, 0);
|
|
Tcl_RestoreResult(consoleinterp, &state);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Get a line from stdin (Tcl replacement for Tx function) */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
char *
|
|
TxGetLinePfix(dest, maxChars, prefix)
|
|
char *dest;
|
|
int maxChars;
|
|
char *prefix;
|
|
{
|
|
Tcl_Obj *objPtr;
|
|
int charsStored, length;
|
|
char *string;
|
|
|
|
if (TxTkConsole)
|
|
{
|
|
/* Use dialog function (must be defined in magic.tcl!) */
|
|
if (prefix != NULL)
|
|
{
|
|
string = Tcl_Alloc(20 + strlen(prefix));
|
|
sprintf(string, "magic::dialog \"\" \"%s\"\n", prefix);
|
|
Tcl_EvalEx(magicinterp, string, -1, 0);
|
|
Tcl_Free(string);
|
|
}
|
|
else
|
|
Tcl_EvalEx(magicinterp, "magic::dialog", 13, 0);
|
|
}
|
|
else
|
|
{
|
|
if (prefix != NULL)
|
|
{
|
|
TxPrintf("%s", prefix);
|
|
TxFlushOut();
|
|
}
|
|
Tcl_EvalEx(magicinterp, "gets stdin", 10, 0);
|
|
}
|
|
|
|
objPtr = Tcl_GetObjResult(magicinterp);
|
|
string = Tcl_GetStringFromObj(objPtr, &length);
|
|
|
|
if (length > 0)
|
|
if (*(string + length - 1) == '\n')
|
|
length--;
|
|
|
|
if (length == 0)
|
|
return NULL;
|
|
else if (length >= maxChars)
|
|
length = (maxChars - 1);
|
|
|
|
strncpy(dest, string, length);
|
|
*(dest + length) = '\0';
|
|
return dest;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Parse a file. This is a skeleton version of the TxDispatch */
|
|
/* routine in textio/txCommands.c */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
TxDispatch(f)
|
|
FILE *f; /* Under Tcl, we never call this with NULL */
|
|
{
|
|
if (f == NULL)
|
|
{
|
|
TxError("Error: TxDispatch(NULL) was called\n");
|
|
}
|
|
while (!feof(f))
|
|
{
|
|
if (SigInterruptPending)
|
|
{
|
|
TxError("Read-in of file aborted.\n");
|
|
SigInterruptPending = FALSE;
|
|
return;
|
|
}
|
|
txGetFileCommand(f, NULL);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Send a command line which was collected by magic's TxEvent */
|
|
/* handler to the interpreter's event queue. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
TxParseString(str, q, event)
|
|
char *str;
|
|
void *q; /* unused */
|
|
void *event; /* always NULL (ignored) */
|
|
{
|
|
char *reply;
|
|
|
|
Tcl_EvalEx(magicinterp, str, -1, 0);
|
|
|
|
reply = (char *)Tcl_GetStringResult(magicinterp);
|
|
|
|
if (strlen(reply) > 0)
|
|
TxPrintf("%s: %s\n", str, reply);
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Replacement for TxFlush(): use Tcl interpreter */
|
|
/* If we just call "flush", _tcl_dispatch gets called, and */
|
|
/* bad things will happen. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
TxFlushErr()
|
|
{
|
|
Tcl_SavedResult state;
|
|
|
|
Tcl_SaveResult(magicinterp, &state);
|
|
Tcl_EvalEx(magicinterp, "::tcl_flush stderr", 18, 0);
|
|
Tcl_RestoreResult(magicinterp, &state);
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
TxFlushOut()
|
|
{
|
|
Tcl_SavedResult state;
|
|
|
|
Tcl_SaveResult(magicinterp, &state);
|
|
Tcl_EvalEx(magicinterp, "::tcl_flush stdout", 18, 0);
|
|
Tcl_RestoreResult(magicinterp, &state);
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void
|
|
TxFlush()
|
|
{
|
|
TxFlushOut();
|
|
TxFlushErr();
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Tcl_printf() replaces vfprintf() for use by every Tx output */
|
|
/* function (namely, TxError() for stderr and TxPrintf() for */
|
|
/* stdout). It changes the result to a Tcl "puts" call, which */
|
|
/* can be changed inside Tcl, as, for example, by TkCon. */
|
|
/* */
|
|
/* 6/17/04---Routine extended to escape double-dollar-sign '$$' */
|
|
/* which is used by some tools when generating via cells. */
|
|
/* */
|
|
/* 12/23/16---Noted that using consoleinterp simply prevents */
|
|
/* the output from being redirected to another window such as */
|
|
/* the command entry window. Split off another bit TxTkOutput */
|
|
/* from TxTkConsole and set it to zero by default. The */
|
|
/* original behavior can be restored using the *flags wizard */
|
|
/* command (*flags printf true). */
|
|
/* */
|
|
/* 11/24/21---Routing extended to excape any dollar sign that */
|
|
/* does not represent a valid Tcl variable. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
int
|
|
Tcl_printf(FILE *f, char *fmt, va_list args_in)
|
|
{
|
|
va_list args;
|
|
static char outstr[128] = "puts -nonewline std";
|
|
char *outptr, *bigstr = NULL, *finalstr = NULL;
|
|
int i, nchars, result, escapes = 0, limit;
|
|
Tcl_Interp *printinterp = (TxTkOutput) ? consoleinterp : magicinterp;
|
|
|
|
strcpy (outstr + 19, (f == stderr) ? "err \"" : "out \"");
|
|
|
|
va_copy(args, args_in);
|
|
outptr = outstr;
|
|
nchars = vsnprintf(outptr + 24, 102, fmt, args);
|
|
va_end(args);
|
|
|
|
if (nchars >= 102)
|
|
{
|
|
va_copy(args, args_in);
|
|
bigstr = Tcl_Alloc(nchars + 26);
|
|
strncpy(bigstr, outptr, 24);
|
|
outptr = bigstr;
|
|
vsnprintf(outptr + 24, nchars + 2, fmt, args);
|
|
va_end(args);
|
|
}
|
|
else if (nchars == -1) nchars = 126;
|
|
|
|
for (i = 24; *(outptr + i) != '\0'; i++)
|
|
{
|
|
if (*(outptr + i) == '\"' || *(outptr + i) == '[' ||
|
|
*(outptr + i) == ']' || *(outptr + i) == '\\')
|
|
escapes++;
|
|
else if (*(outptr + i) == '$')
|
|
escapes += 2;
|
|
}
|
|
|
|
if (escapes > 0)
|
|
{
|
|
/* "+ 4" required to process "$$...$$"; haven't figured out why. */
|
|
finalstr = Tcl_Alloc(nchars + escapes + 26 + 4);
|
|
strncpy(finalstr, outptr, 24);
|
|
escapes = 0;
|
|
for (i = 24; *(outptr + i) != '\0'; i++)
|
|
{
|
|
if (*(outptr + i) == '\"' || *(outptr + i) == '[' ||
|
|
*(outptr + i) == ']' || *(outptr + i) == '\\')
|
|
{
|
|
*(finalstr + i + escapes) = '\\';
|
|
escapes++;
|
|
}
|
|
else if (*(outptr + i) == '$')
|
|
{
|
|
char *wsp;
|
|
|
|
/* Determine if what follows the '$' is a valid Tcl */
|
|
/* variable name. If not, then escape the '$'. */
|
|
|
|
wsp = strchr(outptr + i + 1, ' ');
|
|
if (wsp != NULL) *wsp = '\0';
|
|
if (Tcl_GetVar(printinterp, outptr + i + 1, 0) == NULL)
|
|
{
|
|
*(finalstr + i + escapes) = '\\';
|
|
escapes++;
|
|
}
|
|
if (wsp != NULL) *wsp = ' ';
|
|
}
|
|
*(finalstr + i + escapes) = *(outptr + i);
|
|
}
|
|
outptr = finalstr;
|
|
}
|
|
|
|
*(outptr + 24 + nchars + escapes) = '\"';
|
|
*(outptr + 25 + nchars + escapes) = '\0';
|
|
|
|
result = Tcl_EvalEx(printinterp, outptr, -1, 0);
|
|
|
|
if (bigstr != NULL) Tcl_Free(bigstr);
|
|
if (finalstr != NULL) Tcl_Free(finalstr);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Tcl_escape() takes a string as input and produces a string */
|
|
/* in which characters are escaped as necessary to make them */
|
|
/* printable from Tcl. The new string is allocated by */
|
|
/* Tcl_Alloc() which needs to be free'd with Tcl_Free(). */
|
|
/* */
|
|
/* 6/17/04---extended like Tcl_printf to escape double-dollar- */
|
|
/* sign ('$$') in names. */
|
|
/* 11/24/21---modified like Tcl_printf to escape any dollar */
|
|
/* sign that does not precede a valid Tcl variable name. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
char *
|
|
Tcl_escape(instring)
|
|
char *instring;
|
|
{
|
|
char *newstr;
|
|
int nchars = 0;
|
|
int escapes = 0;
|
|
int i;
|
|
Tcl_Interp *printinterp = (TxTkOutput) ? consoleinterp : magicinterp;
|
|
|
|
for (i = 0; *(instring + i) != '\0'; i++)
|
|
{
|
|
nchars++;
|
|
if (*(instring + i) == '\"' || *(instring + i) == '[' ||
|
|
*(instring + i) == ']' || *(instring + i) == '$')
|
|
escapes++;
|
|
}
|
|
|
|
newstr = Tcl_Alloc(nchars + escapes + 1);
|
|
escapes = 0;
|
|
for (i = 0; *(instring + i) != '\0'; i++)
|
|
{
|
|
if (*(instring + i) == '\"' || *(instring + i) == '[' ||
|
|
*(instring + i) == ']')
|
|
{
|
|
*(newstr + i + escapes) = '\\';
|
|
escapes++;
|
|
}
|
|
else if (*(instring + i) == '$')
|
|
{
|
|
char *wsp;
|
|
|
|
/* If what follows '$' is a valid Tcl variable, don't escape it */
|
|
wsp = strchr(instring + i + 1, ' ');
|
|
if (wsp != NULL) *wsp = '\0';
|
|
if (Tcl_GetVar(printinterp, instring + i + 1, 0) == NULL)
|
|
{
|
|
*(newstr + i + escapes) = '\\';
|
|
escapes++;
|
|
}
|
|
if (wsp != NULL) *wsp = ' ';
|
|
}
|
|
*(newstr + i + escapes) = *(instring + i);
|
|
}
|
|
*(newstr + i + escapes) = '\0';
|
|
return newstr;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
int
|
|
TerminalInputProc(instanceData, buf, toRead, errorCodePtr)
|
|
ClientData instanceData;
|
|
char *buf;
|
|
int toRead;
|
|
int *errorCodePtr;
|
|
{
|
|
FileState *fsPtr = (FileState *)instanceData;
|
|
int bytesRead, i, tlen;
|
|
char *locbuf;
|
|
|
|
*errorCodePtr = 0;
|
|
|
|
TxInputRedirect = TX_INPUT_NORMAL;
|
|
if (TxBuffer != NULL) {
|
|
tlen = strlen(TxBuffer);
|
|
if (tlen < toRead) {
|
|
strcpy(buf, TxBuffer);
|
|
Tcl_Free(TxBuffer);
|
|
TxBuffer = NULL;
|
|
return tlen;
|
|
}
|
|
else {
|
|
strncpy(buf, TxBuffer, toRead);
|
|
locbuf = Tcl_Alloc(tlen - toRead + 1);
|
|
strcpy(locbuf, TxBuffer + toRead);
|
|
Tcl_Free(TxBuffer);
|
|
TxBuffer = locbuf;
|
|
return toRead;
|
|
}
|
|
}
|
|
|
|
while (1) {
|
|
bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
|
|
if (bytesRead > -1)
|
|
return bytesRead;
|
|
|
|
// Ignore interrupts, which may be generated by new
|
|
// terminal windows (added by Tim, 9/30/2014)
|
|
|
|
if (errno != EINTR) break;
|
|
}
|
|
*errorCodePtr = errno;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
int
|
|
Tclmagic_Init(interp)
|
|
Tcl_Interp *interp;
|
|
{
|
|
const char *cadroot;
|
|
|
|
/* Sanity check! */
|
|
if (interp == NULL) return TCL_ERROR;
|
|
|
|
/* Remember the interpreter */
|
|
magicinterp = interp;
|
|
|
|
if (Tcl_InitStubs(interp, "8.5", 0) == NULL) return TCL_ERROR;
|
|
|
|
/* Initialization and Startup commands */
|
|
Tcl_CreateCommand(interp, "magic::initialize", (Tcl_CmdProc *)_magic_initialize,
|
|
(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);
|
|
Tcl_CreateCommand(interp, "magic::startup", (Tcl_CmdProc *)_magic_startup,
|
|
(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);
|
|
Tcl_CreateCommand(interp, "magic::display", (Tcl_CmdProc *)_magic_display,
|
|
(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);
|
|
|
|
/* Initialize the command-tag callback feature */
|
|
|
|
HashInit(&txTclTagTable, 10, HT_STRINGKEYS);
|
|
Tcl_CreateCommand(interp, "magic::tag", (Tcl_CmdProc *)AddCommandTag,
|
|
(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);
|
|
|
|
/* Add "*flags" command for manipulating run-time flags */
|
|
Tcl_CreateObjCommand(interp, "magic::*flags", (Tcl_ObjCmdProc *)_magic_flags,
|
|
(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);
|
|
|
|
/* Add the magic TCL directory to the Tcl library search path */
|
|
|
|
Tcl_Eval(interp, "lappend auto_path " TCL_DIR );
|
|
|
|
/* Get $CAD_ROOT from a Tcl variable, if it exists, and if not, then */
|
|
/* set CAD_ROOT from the environment variable of the same name, if */
|
|
/* it exists, and finally fall back on the CAD_DIR set at compile */
|
|
/* time. */
|
|
|
|
cadroot = Tcl_GetVar(interp, "CAD_ROOT", TCL_GLOBAL_ONLY);
|
|
if (cadroot == NULL)
|
|
{
|
|
cadroot = (const char *)getenv("CAD_ROOT");
|
|
if (cadroot == NULL) cadroot = CAD_DIR;
|
|
Tcl_SetVar(interp, "CAD_ROOT", cadroot, TCL_GLOBAL_ONLY);
|
|
}
|
|
|
|
Tcl_PkgProvide(interp, "Tclmagic", MAGIC_VERSION);
|
|
return TCL_OK;
|
|
}
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Define a "safe init" function for those platforms that */
|
|
/* require it. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
int
|
|
Tclmagic_SafeInit(interp)
|
|
Tcl_Interp *interp;
|
|
{
|
|
return Tclmagic_Init(interp);
|
|
}
|