Minsky
tclmain.cc
Go to the documentation of this file.
1 /*
2  @copyright Russell Standish 2000
3  @author Russell Standish
4  This file is a modified version of a similarly named file in EcoLab.
5 
6  It is released as public domain.
7 */
8 #include <exception> // for exception
9 #include <fstream> // for std
10 #include <map> // for map
11 #include <string> // for string, operator<
12 #include <boost/filesystem.hpp>
13 
14 #include <cairoSurfaceImage.h> // for CairoSurface
15 #include <ctype.h> // for isdigit
16 #include <pango.h> // for Pango, Pango::defaultFamily
17 #include <signal.h> // for SIGABRT, SIGBUS, SIGSEGV
18 #include <stdio.h> // for fprintf, stderr, fputs, puts
19 #include <stdlib.h> // for atoi, free, srand
20 #include <string.h> // for strcmp, strlen
21 #include <time.h> // for time
22 #include <tk.h> // for Tcl_GetStringResult, Tcl_...
23 #include <tkDecls.h> // for Tk_Init, Tk_MainWindow
24 #include "eco_hashmap.h" // for hash_map
25 #include "eco_strstream.h" // for eco_strstream
26 #include "ecolab.h" // for eco_string, classdesc
27 #include "error.h" // for error, ecolab
28 #include "tcl++.h" // for interp, tclvar, NEWCMD
29 #include "timer.h" // for print_timers, stop_timer
30 #include "version.h" // for VERSION
31 #include <ecolab_epilogue.h>
32 
33 using boost::filesystem::path;
34 
35 #ifndef SIG_DFL
36 #define SIG_DFL ((__sighandler_t) 0) /* Default action. */
37 #endif
38 
39 #define SIGILL 4 /* Illegal instruction (ANSI). */
40 
41 #ifndef SIGABRT
42 #define SIGABRT 6 /* Abort (ANSI). */
43 #endif
44 
45 #ifndef SIGBUS
46 #define SIGBUS 7 /* BUS error (4.2 BSD). */
47 #endif
48 
49 #ifndef SIGSEGV
50 #define SIGSEGV 11 /* Segmentation violation (ANSI). */
51 #endif
52 
53 using namespace std;
54 using namespace ecolab;
55 using namespace classdesc;
56 
57 #include "version.h"
58 NEWCMD(minsky_version,0)
59 {
60  tclreturn r;
61  r<<VERSION;
62 }
63 
64 namespace ecolab
65 {
66  Tk_Window mainWin=0;
67 }
68 
70 {
71  stop_timer("main");
72  print_timers();
73 }
74 
75 #if defined(_WIN32)
76 // for GetConsoleWindow()
77 #define _WIN32_WINNT 0x0501
78 #include <windows.h>
79 #endif
80 
81 int main(int argc, char* argv[])
82 {
83  const Timer timer("main");
84  // atexit(printTimersAtEnd);
85 
86 #ifdef _WIN32
87  ecolab::Pango::defaultFamily="Arial Unicode MS";
88 #else
89  ecolab::Pango::defaultFamily="Adobe Courier Regular";
90 #endif
91 
92 #if defined(NDEBUG) && defined(_WIN32)
93  ShowWindow(GetConsoleWindow(),SW_HIDE);
94 #endif
95 
96  Tcl_FindExecutable(argv[0]);
97  int r;
98 #ifndef MXE
99  // For MXE builds, override tcl_library and tk_library to the self-contained versions
100  r=Tcl_Init(interp());
101  if (r!=TCL_OK)
102 #endif
103  // one possible reason is that it failed to locate the TCL library, we we set tcl_library and try again
104  {
105  const path exeDir=path(Tcl_GetNameOfExecutable()).parent_path();
106  tclvar tcl_library("tcl_library", (exeDir/"library"/"tcl").string().c_str());
107  tclvar tk_library("tk_library", (exeDir/"library"/"tk").string().c_str());
108  if ((r=Tcl_Init(interp()))!=TCL_OK)
109  {
110  // on Macs, the TCL library may be located in
111  // ../lib/minsky/library to get around codesign's
112  // pernicketyness
113  tcl_library=(exeDir/".."/"lib"/"minsky"/"library"/"tcl").string();
114  tk_library=(exeDir/".."/"lib"/"minsky"/"library"/"tcl").string();
115  r=Tcl_Init(interp());
116  }
117  }
118  if (r!=TCL_OK)
119  {
120  fprintf(stderr,"%s\n",Tcl_GetStringResult(interp()));
121  fprintf(stderr,"%s\n",Tcl_GetVar(interp(),"errorInfo",0)); /* print out trace */
122  return 1; /* not a clean execution */
123  }
124 
125  /* set the TCL variables argc and argv to contain the
126  arguments. */
127  tclvar tcl_argc("argc"), tcl_argv("argv");
128  tcl_argc=argc;
129  for (int i=0; i<argc; i++) tcl_argv[i]=argv[i];
130 
131  path minskydir=path(Tcl_GetNameOfExecutable()).parent_path();
132  // if Minsky's libraries are not located with the executable, look in the lib
133  // directory (unix style installation)
134  if (!exists(minskydir/"minsky.tcl"))
135  minskydir/="../lib/minsky";
136  if (!exists(minskydir/"minsky.tcl"))
137  {
138  fprintf(stderr,"Minsky library not found!\n");
139  return 1;
140  }
141  const tclvar minskyHome("minskyHome",minskydir.string().c_str());
142 
143  srand(time(nullptr));
144 
145  if (Tcl_EvalFile(interp(), (minskydir/"minsky.tcl").string().c_str())!=TCL_OK)
146  {
147  fprintf(stderr,"%s\n",Tcl_GetStringResult(interp()));
148  fprintf(stderr,"%s\n",Tcl_GetVar(interp(),"errorInfo",0)); /* print out trace */
149  return 1; /* not a clean execution */
150  }
151 
152  while (mainWin) /* we are running GUI mode */
153  Tcl_DoOneEvent(0);
154  Tcl_DeleteInterp(interp());
155  Tcl_Finalize();
156 }
157 
158 namespace minsky
159 {
160  // check and perform a GUI event
161  void doOneEvent(bool idletasksOnly)
162  {
163  Tcl_DoOneEvent(TCL_DONT_WAIT | (idletasksOnly? TCL_IDLE_EVENTS: 0));
164  }
165 }
166 
167 // useful for debugging X11 errors
168 //int minskyXErrorHandler(Display *d, XErrorEvent* ev)
169 //{
170 // if (ev->error_code==BadWindow) return 0;
171 // char msg[1024];
172 // XGetErrorText(d,ev->error_code,msg,1024);
173 // puts(msg);
174 // // throw error("Xlib error %s",msg);
175 //}
176 
177 
178 NEWCMD(GUI,0)
179 {
180  if (Tk_Init(interp())==TCL_ERROR)
181  {
182  fprintf(stderr,"Error initialising Tk: %s",Tcl_GetStringResult(interp()));
183  fprintf(stderr,"%s\n",Tcl_GetVar(interp(),"errorInfo",0));
184  /* If Tk_Init fails, it is not necessarily a fatal error. For
185  example, on unpatched macs, we get an error from the attempt
186  to create a console, yet Minsky works just fine without
187  one. */
188  }
189  CairoSurface::registerImage();
190 
191  mainWin = Tk_MainWindow(interp());
192  //XSetErrorHandler(minskyXErrorHandler);
193 }
194 
195 //this seems to be needed to get Tkinit to function correctly!!
196 NEWCMD(tcl_findLibrary,-1) {}
197 
198 NEWCMD(exit_ecolab,0)
199 {
200  mainWin=0;
201 }
202 
203 
204 namespace TCLcmd
205 {
206 
207  namespace trap
208  {
209  eco_string sigcmd[32];
210  void sighand(int s) {Tcl_Eval(interp(),sigcmd[s].c_str());}
211  hash_map<eco_string,int> signum; /* signal name to number table */
212  struct init_t
213  {
215  {
216  signum["HUP"]= 1; /* Hangup (POSIX). */
217  signum["INT"]= 2; /* Interrupt (ANSI). */
218  signum["QUIT"]= 3; /* Quit (POSIX). */
219  signum["ILL"]= 4; /* Illegal instruction (ANSI). */
220  signum["TRAP"]= 5; /* Trace trap (POSIX). */
221  signum["ABRT"]= 6; /* Abort (ANSI). */
222  signum["IOT"]= 6; /* IOT trap (4.2 BSD). */
223  signum["BUS"]= 7; /* BUS error (4.2 BSD). */
224  signum["FPE"]= 8; /* Floating-point exception (ANSI). */
225  signum["KILL"]= 9; /* Kill, unblockable (POSIX). */
226  signum["USR1"]= 10; /* User-defined signal 1 (POSIX). */
227  signum["SEGV"]= 11; /* Segmentation violation (ANSI). */
228  signum["USR2"]= 12; /* User-defined signal 2 (POSIX). */
229  signum["PIPE"]= 13; /* Broken pipe (POSIX). */
230  signum["ALRM"]= 14; /* Alarm clock (POSIX). */
231  signum["TERM"]= 15; /* Termination (ANSI). */
232  signum["STKFLT"]= 16; /* Stack fault. */
233  signum["CLD"]= 17; /* Same as SIGCHLD (System V). */
234  signum["CHLD"]= 17; /* Child status has changed (POSIX). */
235  signum["CONT"]= 18; /* Continue (POSIX). */
236  signum["STOP"]= 19; /* Stop, unblockable (POSIX). */
237  signum["TSTP"]= 20; /* Keyboard stop (POSIX). */
238  signum["TTIN"]= 21; /* Background read from tty (POSIX). */
239  signum["TTOU"]= 22; /* Background write to tty (POSIX). */
240  signum["URG"]= 23; /* Urgent condition on socket (4.2 BSD). */
241  signum["XCPU"]= 24; /* CPU limit exceeded (4.2 BSD). */
242  signum["XFSZ"]= 25; /* File size limit exceeded (4.2 BSD). */
243  signum["VTALRM"]= 26; /* Virtual alarm clock (4.2 BSD). */
244  signum["PROF"]= 27; /* Profiling alarm clock (4.2 BSD). */
245  signum["WINCH"]= 28; /* Window size change (4.3 BSD, Sun). */
246  signum["POLL"]= 29;/* Pollable event occurred (System V). */
247  signum["IO"]= 29; /* I/O now possible (4.2 BSD). */
248  signum["PWR"]= 30; /* Power failure restart (System V). */
249  signum["SYS"]= 31; /* Bad system call. */
250  signum["UNUSED"]= 31;
251  }
252  } init;
253 
254  NEWCMD(trap,2) /* trap argv[2] to excute argv[1] */
255  {
256  const int signo = (isdigit(argv[1][0]))? atoi(argv[1]):
257  signum[const_cast<char*>(argv[1])];
258  sigcmd[signo]=argv[2];
259  signal(signo,sighand);
260  }
261 
262  void aborthand(int) {throw error("Fatal Error: Execution recovered");}
263 
264  NEWCMD(trapabort,-1)
265  {
266  void (*hand)(int);
267  if (argc>1 && strcmp(argv[1],"off")==0) hand=SIG_DFL;
268  else hand=aborthand;
269  signal(SIGABRT,hand);
270  signal(SIGSEGV,hand);
271  signal(SIGBUS,hand);
272  signal(SIGILL,hand);
273  }
274  }
275 
276 
277 
278 #ifdef READLINE
279  extern "C" char *readline(char *);
280  extern "C" void add_history(char *);
281 #endif
282 
283  NEWCMD(cli,0)
284  {
285  int braces=0, brackets=0;
286  bool inString=false;
287  tclcmd cmd;
288  cmd << "savebgerror\n";
289 #ifdef READLINE
290  char *c;
291  eco_string prompt((const char*)tclvar("argv(0)")); prompt+='>';
292  while ( (c=readline(const_cast<char*>(prompt.c_str())))!=NULL && strcmp(c,"exit")!=0)
293 #else
294  char c[512];
295  while (fgets(c,512,stdin)!=NULL && strcmp(c,"exit\n")!=0)
296 #endif
297  {
298  // count up number of braces, brackets etc
299  for (char* cc=c; *cc!='\0'; ++cc)
300  switch(*cc)
301  {
302  case '{':
303  if (!inString) braces++;
304  break;
305  case '[':
306  if (!inString) brackets++;
307  break;
308  case '}':
309  if (!inString) braces--;
310  break;
311  case ']':
312  if (!inString) brackets--;
313  break;
314  case '\\':
315  if (inString) cc++; //skip next character (is literal)
316  break;
317  case '"':
318  inString = !inString;
319  break;
320  }
321  cmd << chomp(c);
322  if (!inString && braces<=0 && brackets <=0)
323  { // we have a complete command, so attempt to execute it
324  try
325  {
326  cmd.exec();
327  }
328  catch (std::exception& e) {fputs(e.what(),stderr);}
329  catch (...) {fputs("caught unknown exception",stderr);}
330  puts(cmd.result.c_str());
331  }
332 #ifdef READLINE
333  if (strlen(c)) add_history(c);
334  free(c);
335 #endif
336  }
337  cmd << "restorebgerror\n";
338  }
339 
340 #ifdef _WIN32
341 #include <windef.h>
342 #include <winbase.h>
343 #include <shellapi.h>
344  NEWCMD(shellOpen,1)
345  {
346  ShellExecute(NULL,"open",argv[1],NULL,NULL,1);
347  }
348 #endif
349 
350  // support double-click opening of files on Macintosh, by adding a
351  // handler for the open event
352  int tk_mac_OpenDocument(ClientData,Tcl_Interp*,int argc,const char** argv)
353  {
354  if (argc>1)
355  {
356  tclvar("argv")[1]=argv[1];
357  tclvar("argc")=2;
358  }
359  return TCL_OK;
360  }
361 
363  (Tcl_CreateCommand
364  (interp(),"::tk::mac::OpenDocument",(Tcl_CmdProc*)tk_mac_OpenDocument,0,NULL),
365  0);
366 
367 
368 }
369 
370 // In order to get around a missing mkostemp function on MacOSX 10.11 and earlier systems
371 #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
372 extern "C" int mkostemp(char* templ, int flags)
373 {
374  mktemp(templ);
375  return open(templ,flags);
376 }
377 #endif
int tk_mac_OpenDocument(ClientData, Tcl_Interp *, int argc, const char **argv)
Definition: tclmain.cc:352
void doOneEvent(bool idletasksOnly)
checks if any GUI events are waiting, and proces an event if so
Definition: tclmain.cc:161
void printTimersAtEnd()
Definition: tclmain.cc:69
Tk_Window mainWin
Definition: tclmain.cc:66
static int tk_mac_OpenDocument_dummy
Definition: tclmain.cc:362
#define SIG_DFL
Definition: tclmain.cc:36
STL namespace.
hash_map< eco_string, int > signum
Definition: tclmain.cc:211
#define SIGSEGV
Definition: tclmain.cc:50
#define SIGABRT
Definition: tclmain.cc:42
void chomp(string &buf)
Definition: CSVParser.cc:577
struct TCLcmd::trap::init_t init
#define SIGBUS
Definition: tclmain.cc:46
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky&#39;s state cha...
Definition: constMap.h:22
void sighand(int s)
Definition: tclmain.cc:210
NEWCMD(minsky_version, 0)
Definition: tclmain.cc:58
int main(int argc, char *argv[])
Definition: tclmain.cc:81
#define SIGILL
Definition: tclmain.cc:39
void aborthand(int)
Definition: tclmain.cc:262
eco_string sigcmd[32]
Definition: tclmain.cc:209