2017-04-25 14:41:48 +02:00
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
/* 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"
|
2020-05-23 23:13:14 +02:00
|
|
|
#include "utils/main.h"
|
|
|
|
|
#include "utils/magic.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "utils/geometry.h"
|
2020-05-23 23:13:14 +02:00
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#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"
|
2020-05-23 23:13:14 +02:00
|
|
|
#include "utils/malloc.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#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. */
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
|
2023-09-12 17:12:00 +02:00
|
|
|
int
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2020-05-23 23:13:14 +02:00
|
|
|
/* sequence, so save it now and restore it before returning. */
|
2017-04-25 14:41:48 +02:00
|
|
|
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 */
|
2020-05-23 23:13:14 +02:00
|
|
|
/* a null list "{}". */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
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]);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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[])
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
int wval;
|
2017-04-25 14:41:48 +02:00
|
|
|
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 *));
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* 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 */
|
2022-11-02 22:12:46 +01:00
|
|
|
/* has an extension which is not ".mag" or ".gz", we will */
|
|
|
|
|
/* return the error. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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)
|
2022-11-02 22:12:46 +01:00
|
|
|
if (strcmp(dotptr + 1, "mag") && strcmp(dotptr + 1, "gz"))
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
2024-10-04 20:59:55 +02:00
|
|
|
arg0 = argv[0];
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
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 */
|
|
|
|
|
/*------------------------------------------------------*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
2024-10-10 21:16:36 +02:00
|
|
|
const char * const *commandTable;
|
2017-04-25 14:41:48 +02:00
|
|
|
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)) {
|
2020-05-23 23:13:14 +02:00
|
|
|
Tcl_WrongNumArgs(interp, 1, objv, "flag ?value?");
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 16:55:08 +02:00
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
/* 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;
|
2020-05-23 23:13:14 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
/* 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;
|
2022-05-03 14:32:00 +02:00
|
|
|
void *q; /* unused */
|
|
|
|
|
void *event; /* always NULL (ignored) */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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). */
|
2021-11-24 18:40:59 +01:00
|
|
|
/* */
|
|
|
|
|
/* 11/24/21---Routing extended to excape any dollar sign that */
|
|
|
|
|
/* does not represent a valid Tcl variable. */
|
2017-04-25 14:41:48 +02:00
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
int
|
2024-10-20 14:34:42 +02:00
|
|
|
Tcl_printf(FILE *f, const char *fmt, va_list args_in)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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++;
|
2021-11-24 18:40:59 +01:00
|
|
|
else if (*(outptr + i) == '$')
|
2017-04-25 14:41:48 +02:00
|
|
|
escapes += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (escapes > 0)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
/* "+ 4" required to process "$$...$$"; haven't figured out why. */
|
2017-04-25 14:41:48 +02:00
|
|
|
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++;
|
|
|
|
|
}
|
2021-11-24 18:40:59 +01:00
|
|
|
else if (*(outptr + i) == '$')
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-11-24 18:40:59 +01:00
|
|
|
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 = ' ';
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
*(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;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
/* 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. */
|
2021-11-24 18:40:59 +01:00
|
|
|
/* 11/24/21---modified like Tcl_printf to escape any dollar */
|
|
|
|
|
/* sign that does not precede a valid Tcl variable name. */
|
2017-04-25 14:41:48 +02:00
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
Tcl_escape(instring)
|
|
|
|
|
char *instring;
|
|
|
|
|
{
|
|
|
|
|
char *newstr;
|
|
|
|
|
int nchars = 0;
|
|
|
|
|
int escapes = 0;
|
|
|
|
|
int i;
|
2021-11-24 18:40:59 +01:00
|
|
|
Tcl_Interp *printinterp = (TxTkOutput) ? consoleinterp : magicinterp;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
for (i = 0; *(instring + i) != '\0'; i++)
|
|
|
|
|
{
|
|
|
|
|
nchars++;
|
|
|
|
|
if (*(instring + i) == '\"' || *(instring + i) == '[' ||
|
2021-11-24 18:40:59 +01:00
|
|
|
*(instring + i) == ']' || *(instring + i) == '$')
|
2017-04-25 14:41:48 +02:00
|
|
|
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++;
|
|
|
|
|
}
|
2021-11-24 18:40:59 +01:00
|
|
|
else if (*(instring + i) == '$')
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-11-24 18:40:59 +01:00
|
|
|
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 = ' ';
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
*(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;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Tclmagic_Init(interp)
|
|
|
|
|
Tcl_Interp *interp;
|
|
|
|
|
{
|
2021-01-04 16:02:15 +01:00
|
|
|
const char *cadroot;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Sanity check! */
|
|
|
|
|
if (interp == NULL) return TCL_ERROR;
|
|
|
|
|
|
|
|
|
|
/* Remember the interpreter */
|
|
|
|
|
magicinterp = interp;
|
|
|
|
|
|
2017-10-13 16:28:07 +02:00
|
|
|
if (Tcl_InitStubs(interp, "8.5", 0) == NULL) return TCL_ERROR;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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);
|
2022-03-30 16:55:08 +02:00
|
|
|
Tcl_CreateCommand(interp, "magic::display", (Tcl_CmdProc *)_magic_display,
|
|
|
|
|
(ClientData)NULL, (Tcl_CmdDeleteProc *) NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* 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 );
|
|
|
|
|
|
2021-01-04 15:47:03 +01:00
|
|
|
/* 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. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-01-04 15:47:03 +01:00
|
|
|
cadroot = Tcl_GetVar(interp, "CAD_ROOT", TCL_GLOBAL_ONLY);
|
|
|
|
|
if (cadroot == NULL)
|
|
|
|
|
{
|
2021-01-04 16:02:15 +01:00
|
|
|
cadroot = (const char *)getenv("CAD_ROOT");
|
2021-01-04 15:47:03 +01:00
|
|
|
if (cadroot == NULL) cadroot = CAD_DIR;
|
|
|
|
|
Tcl_SetVar(interp, "CAD_ROOT", cadroot, TCL_GLOBAL_ONLY);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
Tcl_PkgProvide(interp, "Tclmagic", MAGIC_VERSION);
|
|
|
|
|
return TCL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
/* Define a "safe init" function for those platforms that */
|
|
|
|
|
/* require it. */
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Tclmagic_SafeInit(interp)
|
2020-05-23 23:13:14 +02:00
|
|
|
Tcl_Interp *interp;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
return Tclmagic_Init(interp);
|
|
|
|
|
}
|