PLplot 5.15.0
Loading...
Searching...
No Matches
tclMain.c
Go to the documentation of this file.
1// Modified version of tclMain.c, from Tcl 8.3.2.
2// Maurice LeBrun
3// Jan 2 2001
4//
5// Copyright (C) 2004 Joao Cardoso
6//
7// This file is part of PLplot.
8//
9// PLplot is free software; you can redistribute it and/or modify
10// it under the terms of the GNU Library General Public License as published
11// by the Free Software Foundation; either version 2 of the License, or
12// (at your option) any later version.
13//
14// PLplot is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17// GNU Library General Public License for more details.
18//
19// You should have received a copy of the GNU Library General Public License
20// along with PLplot; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23//
24// Based on previous version of tclMain.c, from Tcl 7.3.
25// Modifications include:
26// 1. Tcl_Main() changed to pltclMain().
27// 2. Changes to work with ANSI C
28// 3. Changes to support user-installable error or output handlers.
29// 4. PLplot argument parsing routine called to handle arguments.
30// 5. Added define of _POSIX_SOURCE and eliminated include of tclInt.h.
31//
32// Original comments follow.
33//
34
35//
36// tclMain.c --
37//
38// Main program for Tcl shells and other Tcl-based applications.
39//
40// Copyright (c) 1988-1994 The Regents of the University of California.
41// Copyright (c) 1994-1997 Sun Microsystems, Inc.
42//
43// See the file "license.terms" for information on usage and redistribution
44// of this file, and for a DISCLAIMER OF ALL WARRANTIES.
45//
46
47#include "pltcl.h"
48// Required for definition of PL_UNUSED macro
49#include "plplotP.h"
50
51#if defined( TCL_MAJOR_VERSION ) && TCL_MAJOR_VERSION >= 9
52/* Tcl 9 provides Tcl_Size directly. */
53#else
54typedef int Tcl_Size;
55#endif
56
57#define TclFormatInt( buf, n ) sprintf( ( buf ), "%ld", (long) ( n ) )
58
59# undef TCL_STORAGE_CLASS
60# define TCL_STORAGE_CLASS DLLEXPORT
61
62//
63// The following code ensures that tclLink.c is linked whenever
64// Tcl is linked. Without this code there's no reference to the
65// code in that file from anywhere in Tcl, so it may not be
66// linked into the application.
67//
68
69// Experiments show this is no longer required, and in any case
70// it screws up using the Tcl stub library. So comment out (AWI).
71//EXTERN int Tcl_LinkVar( );
72//int ( *tclDummyLinkVarPtr )() = Tcl_LinkVar;
73
74//
75// Declarations for various library procedures and variables (don't want
76// to include tclPort.h here, because people might copy this file out of
77// the Tcl source directory to make their own modified versions).
78// Note: "exit" should really be declared here, but there's no way to
79// declare it without causing conflicts with other definitions elsewher
80// on some systems, so it's better just to leave it out.
81//
82
83extern int isatty ( int fd );
84extern char * strcpy ( char *dst, const char *src );
85
86static const char *tclStartupScriptFileName = NULL;
87
88// pltcl enhancements
89
90static void
91plPrepOutputHandler( Tcl_Interp *interp, int code, int tty );
92
93// Other function prototypes
95const char *TclGetStartupScriptFileName( void );
96
97// These are globally visible and can be replaced
98
99void ( *tclErrorHandler )( Tcl_Interp *interp, int code, int tty ) = NULL;
100
101void ( *tclPrepOutputHandler )( Tcl_Interp *interp, int code, int tty )
103
104// Options data structure definition.
105
106static char *tclStartupScript = NULL;
107static const char *pltcl_notes[] = {
108 "Specifying the filename on the command line is compatible with modern",
109 "tclsh syntax. Old tclsh's used the -f syntax, which is still supported.",
110 "You may use either syntax but not both.",
111 NULL
112};
113
115 {
116 "f", // File to read & process
117 NULL,
118 NULL,
121 "-f",
122 "File from which to read commands"
123 },
124 {
125 "file", // File to read & process (alias)
126 NULL,
127 NULL,
130 "-file",
131 "File from which to read commands"
132 },
133 {
134 "e", // Script to run on startup
135 NULL,
136 NULL,
139 "-e",
140 "Script to execute on startup"
141 },
142 {
143 NULL, // option
144 NULL, // handler
145 NULL, // client data
146 NULL, // address of variable to set
147 0, // mode flag
148 NULL, // short syntax
149 NULL
150 } // long syntax
151};
152
153
154//
155//--------------------------------------------------------------------------
156//
157// TclSetStartupScriptFileName --
158//
159// Primes the startup script file name, used to override the
160// command line processing.
161//
162// Results:
163// None.
164//
165// Side effects:
166// This procedure initializes the file name of the Tcl script to
167// run at startup.
168//
169//--------------------------------------------------------------------------
170//
175
176
177//
178//--------------------------------------------------------------------------
179//
180// TclGetStartupScriptFileName --
181//
182// Gets the startup script file name, used to override the
183// command line processing.
184//
185// Results:
186// The startup script file name, NULL if none has been set.
187//
188// Side effects:
189// None.
190//
191//--------------------------------------------------------------------------
192//
194{
196}
197
198
199
200//
201//--------------------------------------------------------------------------
202//
203// Tcl_Main --
204//
205// Main program for tclsh and most other Tcl-based applications.
206//
207// Results:
208// None. This procedure never returns (it exits the process when
209// it's done.
210//
211// Side effects:
212// This procedure initializes the Tcl world and then starts
213// interpreting commands; almost anything could happen, depending
214// on the script being interpreted.
215//
216//--------------------------------------------------------------------------
217//
218
219int PLDLLEXPORT
220pltclMain( int argc, char **argv, char * PL_UNUSED( RcFileName ) /* OBSOLETE */,
221 int ( *appInitProc )( Tcl_Interp *interp ) )
222{
223 Tcl_Obj *resultPtr;
224 Tcl_Obj *commandPtr = NULL;
225 char buffer[1000], *args;
226 int code, gotPartial, tty;
227 Tcl_Size length;
228 int exitCode = 0;
229 Tcl_Channel inChannel, outChannel, errChannel;
230 Tcl_Interp *interp;
231 Tcl_DString argString;
232
233 char usage[500];
234
235 Tcl_FindExecutable( argv[0] );
236 interp = Tcl_CreateInterp();
237 Tcl_InitMemory( interp ); //no-op if TCL_MEM_DEBUG undefined
238
239 // First process plplot-specific args using the PLplot parser.
240
241 sprintf( usage, "\nUsage:\n %s [filename] [options]\n", argv[0] );
242 plSetUsage( NULL, usage );
243 plMergeOpts( options, "pltcl options", pltcl_notes );
245
246 //
247 // Make (remaining) command-line arguments available in the Tcl variables
248 // "argc" and "argv". If the first argument doesn't start with a "-" then
249 // strip it off and use it as the name of a script file to process.
250 //
251
252 if ( tclStartupScriptFileName == NULL )
253 {
254 if ( ( argc > 1 ) && ( argv[1][0] != '-' ) )
255 {
257 argc--;
258 argv++;
259 }
260 }
261 args = Tcl_Merge( argc - 1, ( const char * const * )argv + 1 );
262 Tcl_ExternalToUtfDString( NULL, args, -1, &argString );
263 Tcl_SetVar( interp, "argv", Tcl_DStringValue( &argString ), TCL_GLOBAL_ONLY );
264 Tcl_DStringFree( &argString );
265 ckfree( args );
266
267 if ( tclStartupScriptFileName == NULL )
268 {
269 Tcl_ExternalToUtfDString( NULL, argv[0], -1, &argString );
270 }
271 else
272 {
273 tclStartupScriptFileName = Tcl_ExternalToUtfDString( NULL,
274 tclStartupScriptFileName, -1, &argString );
275 }
276
277 TclFormatInt( buffer, argc - 1 );
278 Tcl_SetVar( interp, "argc", buffer, TCL_GLOBAL_ONLY );
279 Tcl_SetVar( interp, "argv0", Tcl_DStringValue( &argString ), TCL_GLOBAL_ONLY );
280
281 //
282 // Set the "tcl_interactive" variable.
283 //
284
285 tty = isatty( 0 );
286 Tcl_SetVar( interp, "tcl_interactive",
287 ( ( tclStartupScriptFileName == NULL ) && tty ) ? "1" : "0",
288 TCL_GLOBAL_ONLY );
289
290 //
291 // Invoke application-specific initialization.
292 //
293
294 if ( ( *appInitProc )( interp ) != TCL_OK )
295 {
296 errChannel = Tcl_GetStdChannel( TCL_STDERR );
297 if ( errChannel )
298 {
299 Tcl_WriteChars( errChannel,
300 "application-specific initialization failed: ", -1 );
301 Tcl_WriteObj( errChannel, Tcl_GetObjResult( interp ) );
302 Tcl_WriteChars( errChannel, "\n", 1 );
303 }
304 }
305
306 //
307 // Process the startup script, if any.
308 //
309
310 if ( tclStartupScript != NULL )
311 {
312 code = Tcl_VarEval( interp, tclStartupScript, (char *) NULL );
313 if ( code != TCL_OK )
314 {
315 fprintf( stderr, "%s\n", Tcl_GetStringResult( interp ) );
316 exitCode = 1;
317 }
318 }
319
320 //
321 // If a script file was specified then just source that file
322 // and quit.
323 //
324
325 if ( tclStartupScriptFileName != NULL )
326 {
327 code = Tcl_EvalFile( interp, tclStartupScriptFileName );
328 if ( code != TCL_OK )
329 {
330 errChannel = Tcl_GetStdChannel( TCL_STDERR );
331 if ( errChannel )
332 {
333 //
334 // The following statement guarantees that the errorInfo
335 // variable is set properly.
336 //
337
338 Tcl_AddErrorInfo( interp, "" );
339 Tcl_WriteObj( errChannel, Tcl_GetVar2Ex( interp, "errorInfo",
340 NULL, TCL_GLOBAL_ONLY ) );
341 Tcl_WriteChars( errChannel, "\n", 1 );
342 }
343 exitCode = 1;
344 }
345 goto done;
346 }
347 Tcl_DStringFree( &argString );
348
349 //
350 // We're running interactively. Source a user-specific startup
351 // file if the application specified one and if the file exists.
352 //
353
354 Tcl_SourceRCFile( interp );
355
356 //
357 // Process commands from stdin until there's an end-of-file. Note
358 // that we need to fetch the standard channels again after every
359 // eval, since they may have been changed.
360 //
361
362 commandPtr = Tcl_NewObj();
363 Tcl_IncrRefCount( commandPtr );
364
365 inChannel = Tcl_GetStdChannel( TCL_STDIN );
366 outChannel = Tcl_GetStdChannel( TCL_STDOUT );
367 gotPartial = 0;
368 while ( 1 )
369 {
370 if ( tty )
371 {
372 Tcl_Obj *promptCmdPtr;
373
374 promptCmdPtr = Tcl_GetVar2Ex( interp,
375 ( gotPartial ? "tcl_prompt2" : "tcl_prompt1" ),
376 NULL, TCL_GLOBAL_ONLY );
377 if ( promptCmdPtr == NULL )
378 {
379defaultPrompt:
380 if ( !gotPartial && outChannel )
381 {
382 Tcl_WriteChars( outChannel, "% ", 2 );
383 }
384 }
385 else
386 {
387 code = Tcl_EvalObjEx( interp, promptCmdPtr, 0 );
388 inChannel = Tcl_GetStdChannel( TCL_STDIN );
389 outChannel = Tcl_GetStdChannel( TCL_STDOUT );
390 errChannel = Tcl_GetStdChannel( TCL_STDERR );
391 if ( code != TCL_OK )
392 {
393 if ( errChannel )
394 {
395 Tcl_WriteObj( errChannel, Tcl_GetObjResult( interp ) );
396 Tcl_WriteChars( errChannel, "\n", 1 );
397 }
398 Tcl_AddErrorInfo( interp,
399 "\n (script that generates prompt)" );
400 goto defaultPrompt;
401 }
402 }
403 if ( outChannel )
404 {
405 Tcl_Flush( outChannel );
406 }
407 }
408 if ( !inChannel )
409 {
410 goto done;
411 }
412 length = Tcl_GetsObj( inChannel, commandPtr );
413 if ( length < 0 )
414 {
415 goto done;
416 }
417 if ( ( length == 0 ) && Tcl_Eof( inChannel ) && ( !gotPartial ) )
418 {
419 goto done;
420 }
421
422 //
423 // Add the newline removed by Tcl_GetsObj back to the string.
424 //
425
426 Tcl_AppendToObj( commandPtr, "\n", 1 );
427 if ( !Tcl_CommandComplete( Tcl_GetString( commandPtr ) ) )
428 {
429 gotPartial = 1;
430 continue;
431 }
432
433 gotPartial = 0;
434 code = Tcl_RecordAndEvalObj( interp, commandPtr, 0 );
435 inChannel = Tcl_GetStdChannel( TCL_STDIN );
436 outChannel = Tcl_GetStdChannel( TCL_STDOUT );
437 errChannel = Tcl_GetStdChannel( TCL_STDERR );
438 Tcl_DecrRefCount( commandPtr );
439 commandPtr = Tcl_NewObj();
440 Tcl_IncrRefCount( commandPtr );
441
442 // User defined function to deal with tcl command output
443 // Deprecated; for backward compatibility only
444 if ( ( ( code != TCL_OK ) || tty ) && tclErrorHandler )
445 ( *tclErrorHandler )( interp, code, tty );
446 else
447 {
448 // User defined function to prepare for tcl output
449 // This is the new way
450 if ( ( ( code != TCL_OK ) || tty ) && tclPrepOutputHandler )
451 ( *tclPrepOutputHandler )( interp, code, tty );
452 // Back to the stock tcl code
453 if ( code != TCL_OK )
454 {
455 if ( errChannel )
456 {
457 Tcl_WriteObj( errChannel, Tcl_GetObjResult( interp ) );
458 Tcl_WriteChars( errChannel, "\n", 1 );
459 }
460 }
461 else if ( tty )
462 {
463 resultPtr = Tcl_GetObjResult( interp );
464 Tcl_GetStringFromObj( resultPtr, &length );
465 if ( ( length > 0 ) && outChannel )
466 {
467 Tcl_WriteObj( outChannel, resultPtr );
468 Tcl_WriteChars( outChannel, "\n", 1 );
469 }
470 }
471 }
472 }
473
474 //
475 // Rather than calling exit, invoke the "exit" command so that
476 // users can replace "exit" with some other command to do additional
477 // cleanup on exit. The Tcl_Eval call should never return.
478 //
479
480done:
481 if ( commandPtr != NULL )
482 {
483 Tcl_DecrRefCount( commandPtr );
484 }
485 sprintf( buffer, "exit %d", exitCode );
486 Tcl_Eval( interp, buffer );
487 return 0; // to silence warnings
488}
489
490//
491//--------------------------------------------------------------------------
492//
493// plPrepOutputHandler --
494//
495// Prepares for output during command parsing. We use it here to
496// ensure we are on the text screen before issuing the error message,
497// otherwise it may disappear.
498//
499// Results:
500// None.
501//
502// Side effects:
503// For some graphics devices, a switch between graphics and text modes
504// is done.
505//
506//--------------------------------------------------------------------------
507//
508
509static void
510plPrepOutputHandler( Tcl_Interp *PL_UNUSED( interp ), int PL_UNUSED( code ), int PL_UNUSED( tty ) )
511{
512 pltext();
513}
static PLCHAR_VECTOR usage
Definition plargs.c:179
PLINT plMergeOpts(PLOptionTable *options, PLCHAR_VECTOR name, PLCHAR_VECTOR *notes)
Definition plargs.c:783
void plSetUsage(PLCHAR_VECTOR program_string, PLCHAR_VECTOR usage_string)
Definition plargs.c:1287
#define PLDLLEXPORT
Definition pldll.h:36
static PLINT * buffer
Definition plfill.c:74
#define PL_PARSE_FULL
Definition plplot.h:359
#define PL_PARSE_SKIP
Definition plplot.h:367
#define PL_UNUSED(x)
Definition plplot.h:138
#define plparseopts
Definition plplot.h:778
#define pltext
Definition plplot.h:855
#define PL_OPT_INVISIBLE
Definition plplot.h:344
#define PL_OPT_STRING
Definition plplot.h:353
static int argc
Definition qt.cpp:48
static char ** argv
Definition qt.cpp:49
int Tcl_Size
Definition tclAPI.c:53
static const char * pltcl_notes[]
Definition tclMain.c:107
const char * TclGetStartupScriptFileName(void)
Definition tclMain.c:193
void(* tclPrepOutputHandler)(Tcl_Interp *interp, int code, int tty)
Definition tclMain.c:101
int isatty(int fd)
char * strcpy(char *dst, const char *src)
void(* tclErrorHandler)(Tcl_Interp *interp, int code, int tty)
Definition tclMain.c:99
static char * tclStartupScript
Definition tclMain.c:106
static void plPrepOutputHandler(Tcl_Interp *interp, int code, int tty)
int PLDLLEXPORT pltclMain(int argc, char **argv, char *PL_UNUSED(RcFileName), int(*appInitProc)(Tcl_Interp *interp))
Definition tclMain.c:220
static PLOptionTable options[]
Definition tclMain.c:114
static const char * tclStartupScriptFileName
Definition tclMain.c:86
#define TclFormatInt(buf, n)
Definition tclMain.c:57
void TclSetStartupScriptFileName(char *fileName)
Definition tclMain.c:171
static Tcl_Interp * interp
Definition tkMain.c:117
static const char * fileName
Definition tkMain.c:131
static int tty
Definition tkMain.c:120