1 |
/* |
2 |
* Driver.c |
3 |
* by Kirk Abbott and Ben Allan |
4 |
* Created: 1/94 |
5 |
* Version: $Revision: 1.48 $ |
6 |
* Version control file: $RCSfile: Driver.c,v $ |
7 |
* Date last modified: $Date: 2003/08/23 18:43:06 $ |
8 |
* Last modified by: $Author: ballan $ |
9 |
* |
10 |
* This file is part of the ASCEND Tcl/Tk interface |
11 |
* |
12 |
* Copyright 1997, Carnegie Mellon University |
13 |
* |
14 |
* The ASCEND Tcl/Tk interface is free software; you can redistribute |
15 |
* it and/or modify it under the terms of the GNU General Public License as |
16 |
* published by the Free Software Foundation; either version 2 of the |
17 |
* License, or (at your option) any later version. |
18 |
* |
19 |
* The ASCEND Tcl/Tk interface is distributed in hope that it will be |
20 |
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
22 |
* General Public License for more details. |
23 |
* |
24 |
* You should have received a copy of the GNU General Public License |
25 |
* along with the program; if not, write to the Free Software Foundation, |
26 |
* Inc., 675 Mass Ave, Cambridge, MA 02139 USA. Check the file named |
27 |
* COPYING. COPYING is found in ../compiler. |
28 |
*/ |
29 |
|
30 |
#include <stdarg.h> |
31 |
#include <ctype.h> |
32 |
#include <signal.h> |
33 |
#include <time.h> |
34 |
#include "tcl.h" |
35 |
#include "tk.h" |
36 |
#include "utilities/ascConfig.h" |
37 |
#ifndef __WIN32__ |
38 |
#include <unistd.h> |
39 |
#else |
40 |
#define WIN32_LEAN_AND_MEAN |
41 |
#include <windows.h> |
42 |
#include <locale.h> |
43 |
#undef WIN32_LEAN_AND_MEAN |
44 |
#endif /* __WIN32__ */ |
45 |
#include "utilities/ascMalloc.h" /* for ascshutdown */ |
46 |
#include "utilities/ascPanic.h" /* for Asc_Panic */ |
47 |
#include "utilities/ascEnvVar.h" |
48 |
#include "compiler/compiler.h" |
49 |
#include "compiler/ascCompiler.h" |
50 |
#include "compiler/instance_enum.h" |
51 |
#include "compiler/fractions.h" |
52 |
#include "compiler/dimen.h" |
53 |
#include "compiler/compiler.h" /* for symchar for units.h */ |
54 |
#include "compiler/units.h" |
55 |
#include "solver/slv_types.h" |
56 |
#include "solver/var.h" |
57 |
#include "solver/rel.h" |
58 |
#include "solver/logrel.h" |
59 |
#include "solver/discrete.h" |
60 |
#include "solver/mtx.h" |
61 |
#include "solver/slv_stdcalls.h" |
62 |
#include "interface/AscBitmaps.h" |
63 |
#include "interface/HelpProc.h" |
64 |
#include "interface/Commands.h" |
65 |
#include "interface/Driver.h" |
66 |
#include "interface/ScriptProc.h" |
67 |
#include "interface/SolverProc.h" |
68 |
#include "interface/UnitsProc.h" |
69 |
|
70 |
#ifndef lint |
71 |
static CONST char DriverID[] = "$Id: Driver.c,v 1.48 2003/08/23 18:43:06 ballan Exp $"; |
72 |
#endif |
73 |
|
74 |
|
75 |
/* |
76 |
* The following are the environment variables ASCEND requires. |
77 |
* If the user does not have the DIST_ENVIRONVAR set in his or her |
78 |
* environment, a default value is set based on the directory where the |
79 |
* ascend binary lives. The other enviornment variables will be set |
80 |
* to default values keyed off of DIST_ENVIRONVAR. See the function |
81 |
* CheckEnvironmentVars later in this file for the details. |
82 |
*/ |
83 |
#define DIST_ENVIRONVAR "ASCENDDIST" |
84 |
#define ASCTK_ENVIRONVAR "ASCENDTK" |
85 |
#define BITMAP_ENVIRONVAR "ASCENDBITMAPS" |
86 |
#define LIBR_ENVIRONVAR "ASCENDLIBRARY" |
87 |
|
88 |
/* |
89 |
* EXPORTED VARIABLES |
90 |
*/ |
91 |
|
92 |
/** |
93 |
* g_compiler_timing |
94 |
* |
95 |
* TRUE if compiler timing is to be printed. |
96 |
* default is false, set to TRUE by passing -t on the command line |
97 |
*/ |
98 |
int g_compiler_timing = 0; |
99 |
|
100 |
/** |
101 |
* g_interp |
102 |
* |
103 |
* Interpreter for this application. We need to make it global |
104 |
* so that our signal/floating-porint traps can access it. |
105 |
*/ |
106 |
Tcl_Interp *g_interp; |
107 |
|
108 |
/* |
109 |
* zz_debug |
110 |
* |
111 |
* Comes from the yacc file if yacc was built with debugging information |
112 |
*/ |
113 |
#ifdef ZZ_DEBUG |
114 |
extern int zz_debug; |
115 |
#endif |
116 |
|
117 |
|
118 |
/* |
119 |
* Declarations for procedures defined outside of this file. |
120 |
*/ |
121 |
extern int Tktable_Init(Tcl_Interp*); |
122 |
|
123 |
/* |
124 |
* Forward declarations for procedures defined later in this file. |
125 |
*/ |
126 |
static int AscDriver(int, CONST84 char * argv[]); |
127 |
static void AscTrap(int); |
128 |
static int AscCheckEnvironVars(Tcl_Interp*); |
129 |
static void AscPrintHelpExit(CONST char *); |
130 |
static int AscProcessCommandLine(Tcl_Interp*, int, CONST84 char **); |
131 |
static void Prompt(Tcl_Interp*, int); |
132 |
static int AscSetStartupFile(Tcl_Interp*); |
133 |
static void StdinProc(ClientData, int); |
134 |
#ifdef DEBUG_MALLOC |
135 |
static void InitDebugMalloc(void); |
136 |
#endif /* DEBUG_MALLOC */ |
137 |
#ifdef __WIN32__ |
138 |
static void setargv(int*, char ***); |
139 |
#endif /* __WIN32__ */ |
140 |
|
141 |
|
142 |
/* |
143 |
* LOCALLY GLOBAL VARIABLES |
144 |
*/ |
145 |
|
146 |
/* |
147 |
* g_interface_simplify_relations |
148 |
* |
149 |
* TRUE for compiler optimizations |
150 |
* default is TRUE, set to FALSE by passing +s on the command line |
151 |
*/ |
152 |
static int g_interface_simplify_relations = TRUE; |
153 |
|
154 |
/* |
155 |
* g_interfacever |
156 |
* |
157 |
* TRUE if windows to be built; default is TRUE, false is not supported |
158 |
*/ |
159 |
static int g_interfacever = 1; |
160 |
|
161 |
/* |
162 |
* g_command |
163 |
* |
164 |
* Used to assemble lines of terminal input into Tcl commands. |
165 |
*/ |
166 |
static Tcl_DString g_command; |
167 |
|
168 |
/* |
169 |
* g_line |
170 |
* |
171 |
* Used to read the next line from the terminal input. |
172 |
*/ |
173 |
static Tcl_DString g_line; |
174 |
|
175 |
/* |
176 |
* tty |
177 |
* |
178 |
* Non-zero means standard input is a terminal-like device. |
179 |
* Zero means it's a file. |
180 |
*/ |
181 |
static int tty; |
182 |
|
183 |
/* |
184 |
* initScriptTclAdjust |
185 |
* initScriptTkAdjust |
186 |
* |
187 |
* These two variables hold Tcl scripts that will set the TCL_LIBRARY |
188 |
* and TK_LIBRARY environment variables if they are not already |
189 |
* set in the user's environment. |
190 |
* TCL_LIBRARY is set to: dirnameofexecutable/../../Tcl/lib/tcl8.0 |
191 |
* TK_LIBRARY is set to $tcl_library/../tk8.0 |
192 |
*/ |
193 |
static char initScriptTclAdjust[] = |
194 |
"proc asc_tclInit {} {\n\ |
195 |
global env\n\ |
196 |
rename asc_tclInit {}\n\ |
197 |
set errors {}\n\ |
198 |
set dirs {}\n\ |
199 |
if [info exists env(TCL_LIBRARY)] {\n\ |
200 |
return\n\ |
201 |
} else {\n\ |
202 |
set parentDir [file dirname [info nameofexecutable]]\n\ |
203 |
set env(TCL_LIBRARY) $parentDir/../../Tcl/lib/tcl8.0\n\ |
204 |
}\n\ |
205 |
}\n\ |
206 |
asc_tclInit"; |
207 |
/* for tcl, FOO/ascend4/bin/ascend4.exe is the executable, then |
208 |
* /-- up to FOO/ascend4/bin |
209 |
* set parentDir [file dirname [info nameofexecutable]]\n\ |
210 |
* Then if Tcl is next to ascend4, ../../Tcl/lib/tcl8.0 should work. |
211 |
*/ |
212 |
|
213 |
static char initScriptTkAdjust[] = |
214 |
"proc asc_tkInit {} {\n\ |
215 |
global env\n\ |
216 |
rename asc_tkInit {}\n\ |
217 |
set errors {}\n\ |
218 |
set dirs {}\n\ |
219 |
if [info exists env(TK_LIBRARY)] {\n\ |
220 |
return\n\ |
221 |
} else {\n\ |
222 |
set parentDir [file dirname [info library]]\n\ |
223 |
set env(TK_LIBRARY) $parentDir/tk8.0\n\ |
224 |
}\n\ |
225 |
}\n\ |
226 |
asc_tkInit"; |
227 |
/* |
228 |
* This assumes tcl_library has been found and that tcl8.0 and tk8.0 |
229 |
* are installed in the same lib directory -- the default tcl/tk install. |
230 |
*/ |
231 |
|
232 |
/* |
233 |
* build_name |
234 |
* |
235 |
* who built this binary and when |
236 |
*/ |
237 |
#ifndef TIMESTAMP |
238 |
static char build_name[]="by anonymous"; |
239 |
#else |
240 |
static char build_name[]=TIMESTAMP; |
241 |
#endif /* TIMESTAMP */ |
242 |
|
243 |
|
244 |
|
245 |
/* |
246 |
* main or WinMain |
247 |
* |
248 |
* The main entry point for a Unix or Windows application. |
249 |
* |
250 |
* Each just calls AscDriver(). |
251 |
* These are based on functions from the Tk 8.0 distribution. |
252 |
* See unix/tkAppInit.c and win/winMain.c in their sources. |
253 |
*/ |
254 |
#ifndef __WIN32__ |
255 |
|
256 |
int main(int argc, CONST84 char *argv[]) |
257 |
{ |
258 |
AscDriver(argc, argv); |
259 |
return 0; |
260 |
} |
261 |
|
262 |
#else /* __WIN32__ */ |
263 |
|
264 |
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, |
265 |
LPSTR lpszCmdLine, int nCmdShow) |
266 |
{ |
267 |
int argc; |
268 |
char **argv; |
269 |
char *p; |
270 |
char buffer[MAX_PATH]; |
271 |
|
272 |
/* |
273 |
* Set up the default locale to be standard "C" locale so parsing |
274 |
* is performed correctly. |
275 |
*/ |
276 |
setlocale(LC_ALL, "C"); |
277 |
|
278 |
/* |
279 |
* Increase the application queue size from default value of 8. |
280 |
* At the default value, cross application SendMessage of WM_KILLFOCUS |
281 |
* will fail because the handler will not be able to do a PostMessage! |
282 |
* This is only needed for Windows 3.x, since NT dynamically expands |
283 |
* the queue. |
284 |
*/ |
285 |
SetMessageQueue(64); |
286 |
|
287 |
/* |
288 |
* Create the console channels and install them as the standard |
289 |
* channels. All I/O will be discarded until TkConsoleInit is |
290 |
* called to attach the console to a text widget. |
291 |
*/ |
292 |
|
293 |
/* |
294 |
* Windows expects us to parse our arguments ourselves. |
295 |
*/ |
296 |
setargv(&argc, &argv); |
297 |
|
298 |
/* |
299 |
* Replace argv[0] with full pathname of executable, and forward |
300 |
* slashes substituted for backslashes. |
301 |
*/ |
302 |
GetModuleFileName(NULL, buffer, sizeof(buffer)); |
303 |
argv[0] = buffer; |
304 |
for (p = buffer; *p != '\0'; p++) { |
305 |
if (*p == '\\') { |
306 |
*p = '/'; |
307 |
} |
308 |
} |
309 |
|
310 |
AscDriver(argc, argv); |
311 |
return 1; |
312 |
} |
313 |
#endif /* __WIN32__ */ |
314 |
|
315 |
|
316 |
/* |
317 |
* int AscDriver( argc, argv ) |
318 |
* int argc; |
319 |
* char *argv; |
320 |
* |
321 |
* A common entry point for Windows and Unix. The corresponding |
322 |
* WinMain() and main() functions just call this function. |
323 |
* |
324 |
* This function creates a Tcl interpreter, initializes Tcl and Tk, |
325 |
* initializes the Ascend data structures, sets up the user's |
326 |
* environment, sources ASCEND's startup script, and calls Tk_MainLoop |
327 |
* so the user can interact with ASCEND. Cleans up and exits the |
328 |
* program when Tk_MainLoop returns. |
329 |
* |
330 |
* This function is based on the functions Tk_Main and Tcl_AppInit |
331 |
* from the Tk8.0 distribution. See the files tkMain.c and tkAppInit.c |
332 |
* in the Tk sources. |
333 |
* |
334 |
*/ |
335 |
static int AscDriver(int argc, CONST84 char *argv[]) |
336 |
{ |
337 |
Tcl_Interp *interp; /* local version of global g_interp */ |
338 |
Tcl_Channel inChannel; |
339 |
Tcl_Channel outChannel; |
340 |
|
341 |
/* |
342 |
* Create the Tk Console |
343 |
* |
344 |
* Create the console channels and install them as the standard |
345 |
* channels. All I/O will be discarded until TkConsoleInit() is |
346 |
* called to attach the console to a text widget. |
347 |
*/ |
348 |
#ifdef ASC_USE_TK_CONSOLE |
349 |
TkConsoleCreate(); |
350 |
#endif /* ASC_USE_TK_CONSOLE */ |
351 |
|
352 |
/* |
353 |
* Find the name of the current executable---available in the |
354 |
* Tcl command `info nameofexecutable' |
355 |
*/ |
356 |
Tcl_FindExecutable(argv[0]); |
357 |
|
358 |
/* |
359 |
* Create the interpreter |
360 |
*/ |
361 |
g_interp = Tcl_CreateInterp(); |
362 |
interp = g_interp; |
363 |
if( interp == NULL ) { |
364 |
Asc_Panic(2, "Asc_Driver", |
365 |
"Call to Tcl_CreateInterp returned NULL interpreter"); |
366 |
} |
367 |
|
368 |
/* |
369 |
* Check the arguments on the command line |
370 |
*/ |
371 |
AscProcessCommandLine(interp, argc, argv); |
372 |
|
373 |
/* |
374 |
* Set the "tcl_interactive" variable. |
375 |
* |
376 |
* ASCEND sets tty to `1', since we assume ASCEND is always interactive. |
377 |
*/ |
378 |
tty = 1; |
379 |
Tcl_SetVar(interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY); |
380 |
|
381 |
/* |
382 |
* Initialize Tcl and Tk |
383 |
*/ |
384 |
(void)Tcl_Eval(interp,initScriptTclAdjust); |
385 |
if (Tcl_Init(interp) == TCL_ERROR) { |
386 |
Asc_Panic(2, "Asc_Driver", "Tcl initialization failed:\n%s", |
387 |
Tcl_GetStringResult(interp)); |
388 |
} |
389 |
(void)Tcl_Eval(interp,initScriptTkAdjust); |
390 |
if (Tk_Init(interp) == TCL_ERROR) { |
391 |
Asc_Panic(2, "Asc_Driver", "Tk initialization failed:\n%s", |
392 |
Tcl_GetStringResult(interp)); |
393 |
} |
394 |
Tcl_StaticPackage( interp, "Tk", Tk_Init, Tk_SafeInit ); |
395 |
|
396 |
/* |
397 |
* Initialize the console and the ASCEND printf() substitutes. |
398 |
* All output before this point is lost. |
399 |
*/ |
400 |
#ifdef ASC_USE_TK_CONSOLE |
401 |
if (TkConsoleInit(interp) == TCL_ERROR) { |
402 |
Asc_Panic(2, "Asc_Driver", |
403 |
"Call to TkConsoleInit failed:\n%s", |
404 |
Tcl_GetStringResult(interp)); |
405 |
} |
406 |
#endif /* ASC_USE_TK_CONSOLE */ |
407 |
#ifdef USE_ASC_PRINTF |
408 |
Asc_PrintInit(); |
409 |
#endif /* USE_ASC_PRINTF */ |
410 |
|
411 |
/* |
412 |
* Now that our console and printing functions are properly |
413 |
* initialized, print our startup banner. |
414 |
*/ |
415 |
PRINTF("ASCEND VERSION IV\n"); |
416 |
PRINTF("Compiler Implemention Version: 2.0\n"); |
417 |
PRINTF("Written by Tom Epperly,Kirk Abbott, and Ben Allan\n"); |
418 |
PRINTF(" Built: %s %s %s\n",__DATE__,__TIME__,build_name); |
419 |
PRINTF("Copyright(C) 1990, 1993, 1994 Thomas Guthrie Epperly\n"); |
420 |
PRINTF("Copyright(C) 1993-1996 Kirk Andre Abbott, Ben Allan\n"); |
421 |
PRINTF("Copyright(C) 1997 Carnegie Mellon University\n"); |
422 |
|
423 |
/* |
424 |
* Call the init procedures for included packages. |
425 |
*/ |
426 |
#ifdef STATIC_TKTABLE |
427 |
if( Tktable_Init(interp) == TCL_ERROR ) { |
428 |
Asc_Panic(2, "Asc_Driver", |
429 |
"Call to Tktable_Init failed:\n%s", |
430 |
Tcl_GetStringResult(interp)); |
431 |
} |
432 |
#endif /* STATIC_TKTABLE */ |
433 |
|
434 |
/* |
435 |
* Initialize ASCEND C Structures |
436 |
* Create ASCEND Tcl Commands |
437 |
*/ |
438 |
clock(); |
439 |
/* the next line should NOT be Asc_SignalHandlerPush */ |
440 |
(void)signal(SIGINT, AscTrap); |
441 |
#ifdef DEBUG_MALLOC |
442 |
InitDebugMalloc(); |
443 |
ascstatus("Memory status after calling InitDebugMalloc()"); |
444 |
#endif /* DEBUG_MALLOC */ |
445 |
if ( Asc_CompilerInit(g_interface_simplify_relations) != 0 ) { |
446 |
Asc_Panic(2, "Asc_CompilerInit", |
447 |
"Insufficient memory to initialize compiler."); |
448 |
} |
449 |
SlvRegisterStandardClients(); |
450 |
if( Asc_HelpInit() == TCL_ERROR ) { |
451 |
Asc_Panic(2, "Asc_HelpInit", |
452 |
"Insufficient memory to initialize help system."); |
453 |
} |
454 |
Asc_CreateCommands(interp); |
455 |
Asc_RegisterBitmaps(interp); |
456 |
|
457 |
/* |
458 |
* Set the environment, and set find the |
459 |
* location of ASCEND's startup file. |
460 |
*/ |
461 |
AscCheckEnvironVars(interp); |
462 |
if( AscSetStartupFile(interp) != TCL_OK ) { |
463 |
Asc_Panic(2, "Asc_Driver", |
464 |
"Cannot find ~/.ascendrc nor the default AscendRC\n%s", |
465 |
Tcl_GetStringResult(interp)); |
466 |
} |
467 |
|
468 |
/* |
469 |
* Evaluate the ~/.ascendrc or $ASCENDTK/AscendRC file |
470 |
*/ |
471 |
Tcl_SourceRCFile(interp); |
472 |
|
473 |
/* |
474 |
* Establish a channel handlers for stdin and stdout |
475 |
*/ |
476 |
inChannel = Tcl_GetStdChannel(TCL_STDIN); |
477 |
if (inChannel) { |
478 |
Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc, |
479 |
(ClientData) inChannel); |
480 |
} |
481 |
if (tty) { |
482 |
Prompt(interp, 0); |
483 |
} |
484 |
outChannel = Tcl_GetStdChannel(TCL_STDOUT); |
485 |
if (outChannel) { |
486 |
Tcl_Flush(outChannel); |
487 |
} |
488 |
|
489 |
/* |
490 |
* Initialize the Dynamic Strings used in StdinProc() |
491 |
*/ |
492 |
Tcl_DStringInit(&g_command); |
493 |
Tcl_DStringInit(&g_line); |
494 |
Tcl_ResetResult(interp); |
495 |
|
496 |
/* |
497 |
* Loop infinitely, waiting for commands to execute. When there |
498 |
* are no windows left, Tk_MainLoop returns and we exit. |
499 |
*/ |
500 |
#ifdef DEBUG_MALLOC |
501 |
ascstatus("Memory status before calling Tk_MainLoop()"); |
502 |
#endif /* DEBUG_MALLOC */ |
503 |
if (Asc_ScriptConfigureInterrupt(1,interp)!=0) { |
504 |
Asc_Panic(2, "Asc_ScriptConfigureInterrupt", |
505 |
"Unable to configure script interrupt."); |
506 |
} |
507 |
Tk_MainLoop(); |
508 |
Asc_ScriptConfigureInterrupt(0,interp); |
509 |
#ifdef DEBUG_MALLOC |
510 |
ascstatus("Memory status after Tk_MainLoop() exits"); |
511 |
#endif /* DEBUG_MALLOC */ |
512 |
|
513 |
/* |
514 |
* Do ASCEND Cleanup |
515 |
*/ |
516 |
Asc_HelpDestroy(); |
517 |
Asc_UnitValue(NULL); |
518 |
Asc_SolvMemoryCleanup(); |
519 |
Asc_CompilerDestroy(); |
520 |
Asc_DestroyEnvironment(); |
521 |
#ifdef DEBUG_MALLOC |
522 |
ascshutdown("Memory status just before exiting"); |
523 |
#endif /* DEBUG_MALLOC */ |
524 |
|
525 |
/* |
526 |
* Destroy the interpreter and exit |
527 |
*/ |
528 |
Tcl_DeleteInterp(interp); |
529 |
Tcl_Exit(0); |
530 |
return 0; |
531 |
} |
532 |
|
533 |
|
534 |
/* |
535 |
*----------------------------------------------------------------- |
536 |
* int AscCheckEnvironVars(interp) |
537 |
* Tcl_Interp *interp; |
538 |
* |
539 |
* This function checks the following environment variables and sets them |
540 |
* to the default value if they are not set. This function will set |
541 |
* DIST_ENVIRONVAR to the parent of the directory where the ASCEND binary |
542 |
* lives if it is not set, e.g., if the ascend binary is |
543 |
* /foo/bar/ascend4 |
544 |
* DIST_ENVIRONVAR is set to "/foo". |
545 |
* |
546 |
* We check to see if ASCTK_ENVIRONVAR points to the proper place by |
547 |
* looking for the ASCEND startup script "AscendRC". If we find it, set |
548 |
* the Tcl variable tcl_rcFileName to its location, otherwise, call |
549 |
* Asc_Panic() to exit. |
550 |
* |
551 |
* Returns a standard Tcl return code. |
552 |
* |
553 |
* CPP_MACRO ENVIRONMENT VAR DEFAULT VALUE |
554 |
* ================= =============== ============= |
555 |
* DIST_ENVIRONVAR ASCENDDIST parent of binary's directory |
556 |
* AWW20041208: ASCTK_ENVIRONVAR ASCENDTK ASCENDDIST/TK |
557 |
* ASCTK_ENVIRONVAR ASCENDTK ASCENDDIST/../../tcltk98/TK |
558 |
* BITMAP_ENVIRONVAR ASCENDBITMAPS ASCENDTK/bitmaps |
559 |
* LIBR_ENVIRONVAR ASCENDLIBRARY |
560 |
* .:ASCENDDIST/models/libraries:ASCENDDIST/models/examples |
561 |
* |
562 |
*/ |
563 |
static int AscCheckEnvironVars(Tcl_Interp *interp) |
564 |
{ |
565 |
char *tmpenv; /* holds values returned by Asc_GetEnv() */ |
566 |
Tcl_DString ascenddist; /* holds the value of DIST_ENVIRONVAR */ |
567 |
Tcl_DString buffer2; /* used to incrementally build environment |
568 |
* variable values |
569 |
*/ |
570 |
Tcl_DString buffer1; /* holds the environment variable value in the |
571 |
* native format: result of passing buffer2 |
572 |
* into Tcl_TranslateFileName() |
573 |
*/ |
574 |
Tcl_Channel c; /* used to test if file exists */ |
575 |
|
576 |
/* initialize */ |
577 |
Tcl_DStringInit(&ascenddist); |
578 |
Tcl_DStringInit(&buffer1); |
579 |
Tcl_DStringInit(&buffer2); |
580 |
|
581 |
/* |
582 |
* Get the value of the ASCENDDIST environment variable; |
583 |
* if not set, set it to the parent of the directory containing |
584 |
* the ascend binary. For example, if the ascend binary is |
585 |
* /foo/bar/bin/ascend4, set ASCENDDIST to /foo/bar |
586 |
* If Tcl doesn't know where we are---the Tcl command |
587 |
* `info nameofexecutable' returns ""---then ASCENDDIST is set |
588 |
* to "." |
589 |
*/ |
590 |
if( Asc_ImportPathList(DIST_ENVIRONVAR) == 0 ) { |
591 |
if( (tmpenv = Asc_GetEnv(DIST_ENVIRONVAR)) == NULL ) { |
592 |
/* shouldn't be NULL since we just imported it successfully */ |
593 |
Asc_Panic(2, "CheckEnvironmentVars", |
594 |
"Asc_GetEnv(%s) returned NULL value.", DIST_ENVIRONVAR); |
595 |
} |
596 |
Tcl_DStringAppend(&ascenddist, tmpenv, -1); |
597 |
ascfree(tmpenv); |
598 |
} else { |
599 |
char cmd[] = |
600 |
"file nativename [file dirname [file dirname [info nameofexecutable]]]"; |
601 |
if( Tcl_Eval(interp, cmd) == TCL_OK ) { |
602 |
Tcl_DStringGetResult(interp, &ascenddist); |
603 |
if(Asc_SetPathList(DIST_ENVIRONVAR,Tcl_DStringValue(&ascenddist)) != 0) { |
604 |
Asc_Panic(2, "AscCheckEnvironVars", |
605 |
"Asc_SetPathList() returned Nonzero: " |
606 |
"Not enough memory to extend the environment"); |
607 |
} |
608 |
} |
609 |
} |
610 |
/* Make sure the Tcl side can also see this variable */ |
611 |
Tcl_SetVar2(interp, "env", DIST_ENVIRONVAR, |
612 |
Tcl_DStringValue(&ascenddist), TCL_GLOBAL_ONLY); |
613 |
|
614 |
/* |
615 |
* If the user's environment does not have ASCENDLIBRARY set, then set |
616 |
* it to a reasonable default. |
617 |
*/ |
618 |
if( Asc_ImportPathList(LIBR_ENVIRONVAR) == 0 ) { |
619 |
if( (tmpenv = Asc_GetEnv(LIBR_ENVIRONVAR)) == NULL ) { |
620 |
/* shouldn't be NULL since we just imported it successfully */ |
621 |
Asc_Panic(2, "CheckEnvironmentVars", |
622 |
"Asc_GetEnv(%s) returned NULL value.", LIBR_ENVIRONVAR); |
623 |
} |
624 |
/* Make sure the Tcl side can also see this variable */ |
625 |
Tcl_SetVar2(interp, "env", LIBR_ENVIRONVAR, tmpenv, TCL_GLOBAL_ONLY); |
626 |
ascfree(tmpenv); |
627 |
} else { |
628 |
/* Add ``.'' to the ASCENDLIBRARY envar */ |
629 |
if( Asc_SetPathList(LIBR_ENVIRONVAR, ".") != 0 ) { |
630 |
Asc_Panic(2, "AscCheckEnvironVars", |
631 |
"Asc_SetPathList() returned Nonzero: " |
632 |
"Not enough memory to extend the environment"); |
633 |
} |
634 |
|
635 |
/* Add ``$ASCENDDIST/models'' to the ASCENDLIBRARY envar */ |
636 |
Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&ascenddist), -1); |
637 |
Tcl_DStringAppend(&buffer2, "/models", -1); |
638 |
if( NULL != (Tcl_TranslateFileName(interp, Tcl_DStringValue(&buffer2), |
639 |
&buffer1))) { |
640 |
if(Asc_AppendPath(LIBR_ENVIRONVAR, Tcl_DStringValue(&buffer1)) != 0) { |
641 |
Asc_Panic(2, "AscCheckEnvironVars", |
642 |
"Asc_AppendPath() returned Nonzero: " |
643 |
"Not enough memory to extend the environment"); |
644 |
} |
645 |
Tcl_DStringFree(&buffer1); |
646 |
} |
647 |
Tcl_DStringFree(&buffer2); |
648 |
|
649 |
/* Add ``$ASCENDDIST/models/examples'' to the ASCENDLIBRARY envar */ |
650 |
Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&ascenddist), -1); |
651 |
Tcl_DStringAppend(&buffer2, "/models/examples", -1); |
652 |
if( NULL != (Tcl_TranslateFileName(interp, Tcl_DStringValue(&buffer2), |
653 |
&buffer1))) { |
654 |
if(Asc_AppendPath(LIBR_ENVIRONVAR, Tcl_DStringValue(&buffer1)) != 0) { |
655 |
Asc_Panic(2, "AscCheckEnvironVars", |
656 |
"Asc_AppendPath() returned Nonzero: " |
657 |
"Not enough memory to extend the environment"); |
658 |
} |
659 |
Tcl_DStringFree(&buffer1); |
660 |
} |
661 |
Tcl_DStringFree(&buffer2); |
662 |
|
663 |
/* Add ``$ASCENDDIST/models/libraries'' to the ASCENDLIBRARY envar */ |
664 |
Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&ascenddist), -1); |
665 |
Tcl_DStringAppend(&buffer2, "/models/libraries", -1); |
666 |
if( NULL != (Tcl_TranslateFileName(interp, Tcl_DStringValue(&buffer2), |
667 |
&buffer1))) { |
668 |
if(Asc_AppendPath(LIBR_ENVIRONVAR, Tcl_DStringValue(&buffer1)) != 0) { |
669 |
Asc_Panic(2, "AscCheckEnvironVars", |
670 |
"Asc_AppendPath() returned Nonzero: " |
671 |
"Not enough memory to extend the environment"); |
672 |
} |
673 |
Tcl_DStringFree(&buffer1); |
674 |
} |
675 |
Tcl_DStringFree(&buffer2); |
676 |
|
677 |
/* Get the full value of the environment variable and set |
678 |
* $env(ASCENDLIBRARY) in the Tcl code |
679 |
*/ |
680 |
if( (tmpenv = Asc_GetEnv(LIBR_ENVIRONVAR)) == NULL ) { |
681 |
/* shouldn't be NULL since we just set it. memory error! */ |
682 |
Asc_Panic(2, "CheckEnvironmentVars", |
683 |
"Asc_GetEnv(%s) returned NULL value.", LIBR_ENVIRONVAR); |
684 |
} |
685 |
/* Make sure the Tcl side can also see this variable */ |
686 |
Tcl_SetVar2(interp, "env", LIBR_ENVIRONVAR, tmpenv, TCL_GLOBAL_ONLY); |
687 |
ascfree(tmpenv); |
688 |
} |
689 |
|
690 |
/* |
691 |
* If the user's environment does not have ASCENDTK set, then set it |
692 |
* by appending `TK' to ASCENDDIST. Later in this function, we check |
693 |
* to make sure it is a valid directory by checking for the existence |
694 |
* of `AscendRC' in that directory. |
695 |
*/ |
696 |
if( Asc_ImportPathList(ASCTK_ENVIRONVAR) == 0 ) { |
697 |
if( (tmpenv = Asc_GetEnv(ASCTK_ENVIRONVAR)) == NULL ) { |
698 |
/* shouldn't be NULL since we just imported it successfully */ |
699 |
Asc_Panic(2, "CheckEnvironmentVars", |
700 |
"Asc_GetEnv(%s) returned NULL value.", ASCTK_ENVIRONVAR); |
701 |
} |
702 |
/* store ASCENDTK in ``buffer1'' so we can check for ASCENDTK/AscendRC |
703 |
* below |
704 |
*/ |
705 |
Tcl_DStringAppend(&buffer1, tmpenv, -1); |
706 |
ascfree(tmpenv); |
707 |
} else { |
708 |
Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&ascenddist), -1); |
709 |
/* AWW20041208: Tcl_DStringAppend(&buffer2, "/TK", -1); |
710 |
*/ |
711 |
Tcl_DStringAppend(&buffer2, "/../../tcltk98/TK", -1); |
712 |
if(NULL != (Tcl_TranslateFileName(interp, Tcl_DStringValue(&buffer2), |
713 |
&buffer1))) { |
714 |
if( Asc_SetPathList(ASCTK_ENVIRONVAR, Tcl_DStringValue(&buffer1)) != 0) { |
715 |
Asc_Panic(2, "Asc_EnvironmentInit", |
716 |
"Not enough memory to initialize the environment"); |
717 |
} |
718 |
} |
719 |
Tcl_DStringFree(&buffer2); |
720 |
} |
721 |
/* Make sure the Tcl side can also see this variable */ |
722 |
Tcl_SetVar2(interp, "env", ASCTK_ENVIRONVAR, |
723 |
Tcl_DStringValue(&buffer1), TCL_GLOBAL_ONLY); |
724 |
|
725 |
/* |
726 |
* Check to see if ASCENDTK looks reasonable by checking |
727 |
* for ASCENDTK/AscendRC We use the Tcl channel |
728 |
* mechanism to see if file exists. |
729 |
*/ |
730 |
Tcl_DStringAppend(&buffer1, "/AscendRC", -1 ); |
731 |
c = Tcl_OpenFileChannel( NULL, Tcl_DStringValue(&buffer1), "r", 0 ); |
732 |
if( c != (Tcl_Channel)NULL ) { |
733 |
/* |
734 |
* file exists. close the channel and set tcl_rcfilename to |
735 |
* this location |
736 |
*/ |
737 |
Tcl_Close( NULL, c ); |
738 |
Tcl_SetVar(interp, "tcl_rcFileName", Tcl_DStringValue(&buffer1), |
739 |
TCL_GLOBAL_ONLY); |
740 |
} else { |
741 |
Asc_Panic(2, "AscCheckEnvironVars", |
742 |
"ERROR: Cannot find the file \"%s\" in the subdirectory \"..\/..\/tcltk98\/TK\"\n" |
743 |
"under the directory \"%s\"\n" |
744 |
"Please check the value of the environment variables %s and\n" |
745 |
"and %s and start ASCEND again.\n", |
746 |
"AscendRC", Tcl_DStringValue(&ascenddist), DIST_ENVIRONVAR, |
747 |
ASCTK_ENVIRONVAR); |
748 |
} |
749 |
Tcl_DStringFree(&buffer1); |
750 |
|
751 |
/* |
752 |
* If the user's environment does not have ASCENDBITMAPS set, then set |
753 |
* it by appending `bitmaps' to ASCENDTK. |
754 |
*/ |
755 |
if( Asc_ImportPathList(BITMAP_ENVIRONVAR) == 0 ) { |
756 |
if( (tmpenv = Asc_GetEnv(BITMAP_ENVIRONVAR)) == NULL ) { |
757 |
/* shouldn't be NULL since we just imported it successfully */ |
758 |
Asc_Panic(2, "CheckEnvironmentVars", |
759 |
"Asc_GetEnv(%s) returned NULL value.", BITMAP_ENVIRONVAR); |
760 |
} |
761 |
/* Make sure the Tcl side can also see this variable */ |
762 |
Tcl_SetVar2(interp, "env", BITMAP_ENVIRONVAR, tmpenv, TCL_GLOBAL_ONLY); |
763 |
ascfree(tmpenv); |
764 |
} else { |
765 |
Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&ascenddist), -1); |
766 |
Tcl_DStringAppend(&buffer2, "/TK/bitmaps", -1); |
767 |
if(NULL != (Tcl_TranslateFileName(interp, Tcl_DStringValue(&buffer2), |
768 |
&buffer1))) { |
769 |
if(Asc_SetPathList(BITMAP_ENVIRONVAR, Tcl_DStringValue(&buffer1)) != 0) { |
770 |
Asc_Panic(2, "Asc_EnvironmentInit", |
771 |
"Not enough memory to initialize the environment"); |
772 |
} |
773 |
} |
774 |
Tcl_DStringFree(&buffer2); |
775 |
/* Make sure the Tcl side can also see this variable */ |
776 |
Tcl_SetVar2(interp, "env", BITMAP_ENVIRONVAR, |
777 |
Tcl_DStringValue(&buffer1), TCL_GLOBAL_ONLY); |
778 |
Tcl_DStringFree(&buffer1); |
779 |
} |
780 |
|
781 |
/* Cleanup */ |
782 |
Tcl_DStringFree(&ascenddist); |
783 |
|
784 |
return TCL_OK; |
785 |
} |
786 |
|
787 |
|
788 |
/* |
789 |
* int AscSetStartupFile(interp) |
790 |
* Tcl_Interp *interp; |
791 |
* |
792 |
* Look for ~/.ascendrc; if found, set the Tcl variable tcl_rcFileName |
793 |
* to this file's location. This overrides the value set in |
794 |
* AscCheckEnvironVars(). |
795 |
* If ~/_ascendrc is available it only gets used if ~/.ascendrc is not. |
796 |
* Returns a standard Tcl return code. |
797 |
*/ |
798 |
static int AscSetStartupFile(Tcl_Interp *interp) |
799 |
{ |
800 |
char *fullname; /* try to find this if first fails */ |
801 |
Tcl_DString buffer; |
802 |
Tcl_Channel c; /* used to check for file existance */ |
803 |
|
804 |
Tcl_ResetResult(interp); |
805 |
|
806 |
fullname = Tcl_TranslateFileName( interp, "~/.ascendrc", &buffer ); |
807 |
if( fullname != NULL ) { |
808 |
/* |
809 |
* Use the Tcl file channel routines to determine if ~/.ascendrc |
810 |
* exists. We cannot use access() since Windows doesn't use it. |
811 |
*/ |
812 |
c = Tcl_OpenFileChannel( NULL, fullname, "r", 0 ); |
813 |
if( c != (Tcl_Channel)NULL ) { |
814 |
/* file exists. close the channel and set tcl_rcFileName. */ |
815 |
Tcl_Close( NULL, c ); |
816 |
Tcl_SetVar(interp, "tcl_rcFileName", fullname, TCL_GLOBAL_ONLY); |
817 |
Tcl_DStringFree(&buffer); |
818 |
return TCL_OK; |
819 |
} |
820 |
Tcl_DStringFree(&buffer); |
821 |
} |
822 |
fullname = Tcl_TranslateFileName( interp, "~/_ascendrc", &buffer ); |
823 |
if( fullname != NULL ) { |
824 |
/* |
825 |
* Use the Tcl file channel routines to determine if ~/_ascendrc |
826 |
* exists. We cannot use access() since Windows doesn't use it. |
827 |
*/ |
828 |
c = Tcl_OpenFileChannel( NULL, fullname, "r", 0 ); |
829 |
if( c != (Tcl_Channel)NULL ) { |
830 |
/* file exists. close the channel and set tcl_rcFileName */ |
831 |
Tcl_Close( NULL, c ); |
832 |
Tcl_SetVar(interp, "tcl_rcFileName", fullname, TCL_GLOBAL_ONLY); |
833 |
Tcl_DStringFree(&buffer); |
834 |
return TCL_OK; |
835 |
} |
836 |
Tcl_DStringFree(&buffer); |
837 |
} |
838 |
return TCL_OK; /* probably should be TCL_ERROR */ |
839 |
} |
840 |
|
841 |
|
842 |
|
843 |
/* |
844 |
* file = AscProcessCommandLine(argc, argv) |
845 |
* char *file; |
846 |
* int argc; |
847 |
* char *argv[]; |
848 |
* |
849 |
* Process the options given on the command line `argv' where `argc' is |
850 |
* the length of argv. |
851 |
* |
852 |
* Strip out ASCEND specific flags and then pass the rest to Tcl so it |
853 |
* can set what it needs. |
854 |
* |
855 |
* This function may call exit() if the user requests help. |
856 |
*/ |
857 |
static int AscProcessCommandLine(Tcl_Interp *interp, int argc, CONST84 char **argv) |
858 |
{ |
859 |
int i; |
860 |
int flag; /* set to 1 for `+arg', -1 for `-arg' */ |
861 |
size_t length; /* length of an argv */ |
862 |
char *args; |
863 |
char buf[MAXIMUM_NUMERIC_LENGTH]; /* space for integer->string conversion */ |
864 |
int new_argc = 0; /* the argc we will pass to Tcl */ |
865 |
#ifdef ZZ_DEBUG |
866 |
zz_debug = 0; /* nonzero to print parser debugging info*/ |
867 |
#endif |
868 |
|
869 |
for( i = 1; i < argc; i++ ) { |
870 |
if( (length = strlen(argv[i])) == 0 ) { |
871 |
/* ignore 0-length arguments */ |
872 |
continue; |
873 |
} |
874 |
|
875 |
if(( length >= 2 ) && ( strncmp(argv[i],"-h",2) == 0 )) { |
876 |
AscPrintHelpExit(argv[0]); |
877 |
} |
878 |
if(( length >= 2 ) && ( strncmp(argv[i],"-H",2) == 0 )) { |
879 |
AscPrintHelpExit(argv[0]); |
880 |
} |
881 |
if(( length >= 4 ) && ( strncmp(argv[i],"help",4) == 0 )) { |
882 |
AscPrintHelpExit(argv[0]); |
883 |
} |
884 |
|
885 |
if( argv[i][0] == '-' ) { |
886 |
flag = -1; |
887 |
} else if( argv[i][0] == '+' ) { |
888 |
flag = 1; |
889 |
} else { |
890 |
flag = 0; |
891 |
} |
892 |
|
893 |
if(( length == 2 ) && ( flag != 0 )) { |
894 |
switch( argv[i][1] ) { |
895 |
case 'd': |
896 |
/* '-d' turns on scanner debugging (if ascend was built with it) |
897 |
* '+d' turns off scanner debugging [default] |
898 |
*/ |
899 |
if( flag == -1 ) { |
900 |
#ifdef ZZ_DEBUG |
901 |
zz_debug = 1; |
902 |
} else { |
903 |
zz_debug = 0; |
904 |
#else |
905 |
FPRINTF(ASCERR, "Sorry, %s wasn't compiled with %s defined.\n", |
906 |
argv[0], "ZZ_DEBUG"); |
907 |
#endif /* ZZ_DEBUG */ |
908 |
} |
909 |
break; |
910 |
case 's': |
911 |
/* '-s' turns on compiler optimizations [default] |
912 |
* '+s' turns off compiler optimizations |
913 |
*/ |
914 |
if( flag == -1 ) { |
915 |
g_interface_simplify_relations = 1; |
916 |
} else { |
917 |
g_interface_simplify_relations = 0; |
918 |
} |
919 |
break; |
920 |
case 't': |
921 |
/* '-t' turns on timing of compiler optimizations |
922 |
* '+t' turns off timing of compiler optimizations [default] |
923 |
*/ |
924 |
if( flag == 0 ) { |
925 |
g_compiler_timing = 1; |
926 |
} else { |
927 |
g_compiler_timing = 0; |
928 |
} |
929 |
break; |
930 |
case 'c': |
931 |
case 'g': |
932 |
fprintf(ASCERR, "WARNING! Obsolete ASCEND option \"%s\"\n", argv[i]); |
933 |
break; |
934 |
default: |
935 |
/* unknown ASCEND option, pass it on to Tcl |
936 |
*/ |
937 |
argv[++new_argc] = argv[i]; |
938 |
break; |
939 |
} |
940 |
} else { |
941 |
/* unknown ASCEND option, pass it on to Tcl |
942 |
*/ |
943 |
argv[++new_argc] = argv[i]; |
944 |
} |
945 |
} |
946 |
|
947 |
/* |
948 |
* Make command-line arguments available in the Tcl variables "argc" |
949 |
* and "argv". |
950 |
*/ |
951 |
args = Tcl_Merge(new_argc, (argv+1)); |
952 |
Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY); |
953 |
ckfree(args); |
954 |
sprintf(buf, "%d", new_argc); |
955 |
Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY); |
956 |
Tcl_SetVar(interp, "argv0", argv[0], TCL_GLOBAL_ONLY); |
957 |
|
958 |
return TCL_OK; |
959 |
} |
960 |
|
961 |
|
962 |
/* |
963 |
* AscPrintHelpExit(invoke_name) |
964 |
* CONST char *invoke_name; |
965 |
* |
966 |
* Print a help message and exit. Use invoke_name as the name of |
967 |
* the binary |
968 |
*/ |
969 |
static |
970 |
void AscPrintHelpExit(CONST char *invoke_name) |
971 |
{ |
972 |
PRINTF("usage: %s [options]\n" |
973 |
"\n" |
974 |
"where options include [default value]:\n" |
975 |
" -h print this message\n" |
976 |
" -/+d turn on/off yacc debugging [off]\n" |
977 |
" -/+s turn on/off compiler optimizations [on]\n" |
978 |
" -/+t turn on/off timing of compiler operations [off]\n", |
979 |
invoke_name); |
980 |
Tcl_Exit(0); /* Show this help message and leave */ |
981 |
} |
982 |
|
983 |
|
984 |
/* |
985 |
* AscTrap(sig) |
986 |
* int sig; |
987 |
* |
988 |
* Function to call when we receive an interrupt. |
989 |
*/ |
990 |
static |
991 |
void AscTrap(int sig) |
992 |
{ |
993 |
putchar('\n'); |
994 |
Asc_Panic(sig, "AscTrap", "Caught Signal: %d", sig); |
995 |
} |
996 |
|
997 |
|
998 |
/* |
999 |
* See this file's header for documentation. |
1000 |
*/ |
1001 |
int Asc_LoadWin(ClientData cdata, Tcl_Interp *interp, |
1002 |
int argc, CONST84 char *argv[]) |
1003 |
{ |
1004 |
(void)cdata; /* stop gcc whine about unused parameter */ |
1005 |
(void)argv; /* stop gcc whine about unused parameter */ |
1006 |
|
1007 |
if ( argc != 1 ) { |
1008 |
FPRINTF(stderr,"call is: ascloadwin <no args> \n"); |
1009 |
return TCL_ERROR; |
1010 |
} |
1011 |
if (g_interfacever) { |
1012 |
Tcl_SetResult(interp, "1", TCL_STATIC); |
1013 |
} else { |
1014 |
Tcl_SetResult(interp, "0", TCL_STATIC); |
1015 |
} |
1016 |
return TCL_OK; |
1017 |
} |
1018 |
|
1019 |
|
1020 |
/* |
1021 |
*---------------------------------------------------------------------- |
1022 |
*---------------------------------------------------------------------- |
1023 |
* The following StdinProc() and Prompt() are from tkMain.c in |
1024 |
* the Tk4.1 distribution (and did not change in Tk8.0). |
1025 |
*---------------------------------------------------------------------- |
1026 |
*---------------------------------------------------------------------- |
1027 |
*/ |
1028 |
/* |
1029 |
*---------------------------------------------------------------------- |
1030 |
* |
1031 |
* StdinProc -- |
1032 |
* |
1033 |
* This procedure is invoked by the event dispatcher whenever |
1034 |
* standard input becomes readable. It grabs the next line of |
1035 |
* input characters, adds them to a command being assembled, and |
1036 |
* executes the command if it's complete. |
1037 |
* |
1038 |
* Results: |
1039 |
* None. |
1040 |
* |
1041 |
* Side effects: |
1042 |
* Could be almost arbitrary, depending on the command that's |
1043 |
* typed. |
1044 |
* |
1045 |
*---------------------------------------------------------------------- |
1046 |
*/ |
1047 |
|
1048 |
/* ARGSUSED */ |
1049 |
static void |
1050 |
StdinProc(clientData, mask) |
1051 |
ClientData clientData; /* Not used. */ |
1052 |
int mask; /* Not used. */ |
1053 |
{ |
1054 |
static int gotPartial = 0; |
1055 |
char *cmd; |
1056 |
int code, count; |
1057 |
Tcl_Channel chan = (Tcl_Channel) clientData; |
1058 |
|
1059 |
Tcl_Interp *interp = g_interp; /* use a local copy of the |
1060 |
* global tcl interpreter |
1061 |
*/ |
1062 |
|
1063 |
(void)clientData; /* stop gcc whine about unused parameter */ |
1064 |
(void)mask; /* stop gcc whine about unused parameter */ |
1065 |
|
1066 |
count = Tcl_Gets(chan, &g_line); |
1067 |
|
1068 |
if (count < 0) { |
1069 |
if (!gotPartial) { |
1070 |
if (tty) { |
1071 |
return; |
1072 |
} else { |
1073 |
Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan); |
1074 |
} |
1075 |
return; |
1076 |
} else { |
1077 |
count = 0; |
1078 |
} |
1079 |
} |
1080 |
|
1081 |
(void) Tcl_DStringAppend(&g_command, Tcl_DStringValue(&g_line), -1); |
1082 |
cmd = Tcl_DStringAppend(&g_command, "\n", -1); |
1083 |
Tcl_DStringFree(&g_line); |
1084 |
|
1085 |
if (!Tcl_CommandComplete(cmd)) { |
1086 |
gotPartial = 1; |
1087 |
goto prompt; |
1088 |
} |
1089 |
gotPartial = 0; |
1090 |
|
1091 |
/* |
1092 |
* Disable the stdin channel handler while evaluating the command; |
1093 |
* otherwise if the command re-enters the event loop we might |
1094 |
* process commands from stdin before the current command is |
1095 |
* finished. Among other things, this will trash the text of the |
1096 |
* command being evaluated. |
1097 |
*/ |
1098 |
|
1099 |
Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) chan); |
1100 |
code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL); |
1101 |
Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc, |
1102 |
(ClientData) chan); |
1103 |
Tcl_DStringFree(&g_command); |
1104 |
if (*interp->result != 0) { |
1105 |
if ((code != TCL_OK) || (tty)) { |
1106 |
/* |
1107 |
* The statement below used to call "printf", but that resulted |
1108 |
* in core dumps under Solaris 2.3 if the result was very long. |
1109 |
* |
1110 |
* NOTE: This probably will not work under Windows either. |
1111 |
*/ |
1112 |
|
1113 |
puts(interp->result); |
1114 |
} |
1115 |
} |
1116 |
|
1117 |
/* |
1118 |
* Output a prompt. |
1119 |
*/ |
1120 |
|
1121 |
prompt: |
1122 |
if (tty) { |
1123 |
Prompt(interp, gotPartial); |
1124 |
} |
1125 |
Tcl_ResetResult(interp); |
1126 |
} |
1127 |
|
1128 |
/* |
1129 |
*---------------------------------------------------------------------- |
1130 |
* |
1131 |
* Prompt -- |
1132 |
* |
1133 |
* Issue a prompt on standard output, or invoke a script |
1134 |
* to issue the prompt. |
1135 |
* |
1136 |
* Results: |
1137 |
* None. |
1138 |
* |
1139 |
* Side effects: |
1140 |
* A prompt gets output, and a Tcl script may be evaluated |
1141 |
* in interp. |
1142 |
* |
1143 |
*---------------------------------------------------------------------- |
1144 |
*/ |
1145 |
|
1146 |
static void |
1147 |
Prompt(interp, partial) |
1148 |
Tcl_Interp *interp; /* Interpreter to use for prompting. */ |
1149 |
int partial; /* Non-zero means there already |
1150 |
* exists a partial command, so use |
1151 |
* the secondary prompt. */ |
1152 |
{ |
1153 |
CONST84 char *promptCmd; |
1154 |
int code; |
1155 |
Tcl_Channel outChannel, errChannel; |
1156 |
CONST84 char *subPrompt; |
1157 |
|
1158 |
errChannel = Tcl_GetChannel(interp, "stderr", NULL); |
1159 |
|
1160 |
subPrompt = (partial ? "tcl_prompt2" : "tcl_prompt1"); |
1161 |
promptCmd = Tcl_GetVar(interp, subPrompt, TCL_GLOBAL_ONLY); |
1162 |
if (promptCmd == NULL) { |
1163 |
defaultPrompt: |
1164 |
|
1165 |
/* |
1166 |
* We must check that outChannel is a real channel - it |
1167 |
* is possible that someone has transferred stdout out of |
1168 |
* this interpreter with "interp transfer". |
1169 |
*/ |
1170 |
|
1171 |
outChannel = Tcl_GetChannel(interp, "stdout", NULL); |
1172 |
if (outChannel != (Tcl_Channel) NULL) { |
1173 |
if (!partial) { |
1174 |
Tcl_Write(outChannel, "AscendIV% ", 10); |
1175 |
} else { |
1176 |
Tcl_Write(outChannel, "more? ", 6); |
1177 |
} |
1178 |
} |
1179 |
} else { |
1180 |
code = Tcl_Eval(interp, promptCmd); |
1181 |
if (code != TCL_OK) { |
1182 |
Tcl_AddErrorInfo(interp, |
1183 |
"\n (script that generates prompt)"); |
1184 |
/* |
1185 |
* We must check that errChannel is a real channel - it |
1186 |
* is possible that someone has transferred stderr out of |
1187 |
* this interpreter with "interp transfer". |
1188 |
*/ |
1189 |
|
1190 |
errChannel = Tcl_GetChannel(interp, "stderr", NULL); |
1191 |
if (errChannel != (Tcl_Channel) NULL) { |
1192 |
Tcl_Write(errChannel, interp->result, -1); |
1193 |
Tcl_Write(errChannel, "\n", 1); |
1194 |
} |
1195 |
goto defaultPrompt; |
1196 |
} |
1197 |
} |
1198 |
outChannel = Tcl_GetChannel(interp, "stdout", NULL); |
1199 |
if (outChannel != (Tcl_Channel) NULL) { |
1200 |
Tcl_Flush(outChannel); |
1201 |
} |
1202 |
} |
1203 |
|
1204 |
/* |
1205 |
*---------------------------------------------------------------------- |
1206 |
* Tom Epperly's Malloc Debugger |
1207 |
*---------------------------------------------------------------------- |
1208 |
*/ |
1209 |
#ifdef DEBUG_MALLOC |
1210 |
static void InitDebugMalloc(void) |
1211 |
{ |
1212 |
union dbmalloptarg m; |
1213 |
m.str = NULL; |
1214 |
m.i = 0; |
1215 |
dbmallopt(MALLOC_CKDATA,&m); |
1216 |
} |
1217 |
|
1218 |
int Asc_DebugMallocCmd(ClientData cdata, Tcl_Interp *interp, |
1219 |
int argc, CONST84 char *argv[]) |
1220 |
{ |
1221 |
union dbmalloptarg m; |
1222 |
|
1223 |
if ( argc != 2 ) { |
1224 |
Tcl_SetResult(interp, "wrong # args : Usage __dbmalloc ?on?off?", |
1225 |
TCL_STATIC); |
1226 |
return TCL_ERROR; |
1227 |
} |
1228 |
m.str = NULL; |
1229 |
if (strcmp(argv[1],"on")==0) { |
1230 |
m.i = 1; |
1231 |
} else if (strcmp(argv[1],"off")==0) { |
1232 |
m.i = 0; |
1233 |
} else { |
1234 |
Tcl_SetResult(interp, "incorrect args : should be \"on\" or \"off\"", |
1235 |
TCL_STATIC); |
1236 |
return TCL_ERROR; |
1237 |
} |
1238 |
dbmallopt(MALLOC_CKDATA,&m); /* turn on str* mem* and b* checking */ |
1239 |
return TCL_OK; |
1240 |
} |
1241 |
#endif /* DEBUG_MALLOC */ |
1242 |
|
1243 |
#ifdef __WIN32__ |
1244 |
/* |
1245 |
*------------------------------------------------------------------------- |
1246 |
* |
1247 |
* setargv -- |
1248 |
* |
1249 |
* Parse the Windows command line string into argc/argv. Done here |
1250 |
* because we don't trust the builtin argument parser in crt0. |
1251 |
* Windows applications are responsible for breaking their command |
1252 |
* line into arguments. |
1253 |
* |
1254 |
* 2N backslashes + quote -> N backslashes + begin quoted string |
1255 |
* 2N + 1 backslashes + quote -> literal |
1256 |
* N backslashes + non-quote -> literal |
1257 |
* quote + quote in a quoted string -> single quote |
1258 |
* quote + quote not in quoted string -> empty string |
1259 |
* quote -> begin quoted string |
1260 |
* |
1261 |
* Results: |
1262 |
* Fills argcPtr with the number of arguments and argvPtr with the |
1263 |
* array of arguments. |
1264 |
* |
1265 |
* Side effects: |
1266 |
* Memory allocated. |
1267 |
* |
1268 |
* This function is from the Tk 8.0 distribution. See win/winMain.c in |
1269 |
* their sources. |
1270 |
* |
1271 |
*-------------------------------------------------------------------------- |
1272 |
*/ |
1273 |
static void |
1274 |
setargv(argcPtr, argvPtr) |
1275 |
int *argcPtr; /* Filled with number of argument strings. */ |
1276 |
char ***argvPtr; /* Filled with argument strings (malloc'd). */ |
1277 |
{ |
1278 |
char *cmdLine, *p, *arg, *argSpace; |
1279 |
char **argv; |
1280 |
int argc, size, inquote, copy, slashes; |
1281 |
|
1282 |
cmdLine = GetCommandLine(); |
1283 |
|
1284 |
/* |
1285 |
* Precompute an overly pessimistic guess at the number of arguments |
1286 |
* in the command line by counting non-space spans. |
1287 |
*/ |
1288 |
|
1289 |
size = 2; |
1290 |
for (p = cmdLine; *p != '\0'; p++) { |
1291 |
if (isspace(*p)) { |
1292 |
size++; |
1293 |
while (isspace(*p)) { |
1294 |
p++; |
1295 |
} |
1296 |
if (*p == '\0') { |
1297 |
break; |
1298 |
} |
1299 |
} |
1300 |
} |
1301 |
argSpace = (char *) ckalloc((unsigned) (size * sizeof(char *) |
1302 |
+ strlen(cmdLine) + 1)); |
1303 |
argv = (char **) argSpace; |
1304 |
argSpace += size * sizeof(char *); |
1305 |
size--; |
1306 |
|
1307 |
p = cmdLine; |
1308 |
for (argc = 0; argc < size; argc++) { |
1309 |
argv[argc] = arg = argSpace; |
1310 |
while (isspace(*p)) { |
1311 |
p++; |
1312 |
} |
1313 |
if (*p == '\0') { |
1314 |
break; |
1315 |
} |
1316 |
|
1317 |
inquote = 0; |
1318 |
slashes = 0; |
1319 |
while (1) { |
1320 |
copy = 1; |
1321 |
while (*p == '\\') { |
1322 |
slashes++; |
1323 |
p++; |
1324 |
} |
1325 |
if (*p == '"') { |
1326 |
if ((slashes & 1) == 0) { |
1327 |
copy = 0; |
1328 |
if ((inquote) && (p[1] == '"')) { |
1329 |
p++; |
1330 |
copy = 1; |
1331 |
} else { |
1332 |
inquote = !inquote; |
1333 |
} |
1334 |
} |
1335 |
slashes >>= 1; |
1336 |
} |
1337 |
|
1338 |
while (slashes) { |
1339 |
*arg = '\\'; |
1340 |
arg++; |
1341 |
slashes--; |
1342 |
} |
1343 |
|
1344 |
if ((*p == '\0') || (!inquote && isspace(*p))) { |
1345 |
break; |
1346 |
} |
1347 |
if (copy != 0) { |
1348 |
*arg = *p; |
1349 |
arg++; |
1350 |
} |
1351 |
p++; |
1352 |
} |
1353 |
*arg = '\0'; |
1354 |
argSpace = arg + 1; |
1355 |
} |
1356 |
argv[argc] = NULL; |
1357 |
|
1358 |
*argcPtr = argc; |
1359 |
*argvPtr = argv; |
1360 |
} |
1361 |
|
1362 |
#endif /* __WIN32__ */ |