Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/generic/tkMain.c
1810 views
1
/*
2
* tkMain.c --
3
*
4
* This file contains a generic main program for Tk-based applications.
5
* It can be used as-is for many applications, just by supplying a
6
* different appInitProc procedure for each specific application.
7
* Or, it can be used as a template for creating new main programs
8
* for Tk applications.
9
*
10
* Copyright (c) 1990-1994 The Regents of the University of California.
11
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
12
*
13
* See the file "license.terms" for information on usage and redistribution
14
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15
*
16
* SCCS: @(#) tkMain.c 1.150 96/09/05 18:42:25
17
*/
18
19
#include <tcl.h>
20
#include <tk.h>
21
#include <ctype.h>
22
23
#if !_PACKAGE_ast
24
# include <stdio.h>
25
# include <string.h>
26
# ifdef NO_STDLIB_H
27
# include "../compat/stdlib.h"
28
# else
29
# include <stdlib.h>
30
# endif
31
32
/*
33
* Declarations for various library procedures and variables (don't want
34
* to include tkInt.h or tkPort.h here, because people might copy this
35
* file out of the Tk source directory to make their own modified versions).
36
* Note: don't declare "exit" here even though a declaration is really
37
* needed, because it will conflict with a declaration elsewhere on
38
* some systems.
39
*/
40
41
extern int isatty _ANSI_ARGS_((int fd));
42
extern int read _ANSI_ARGS_((int fd, char *buf, size_t size));
43
extern char * strrchr _ANSI_ARGS_((CONST char *string, int c));
44
45
#endif
46
47
/*
48
* Global variables used by the main program:
49
*/
50
51
static Tcl_Interp *interp; /* Interpreter for this application. */
52
static Tcl_DString command; /* Used to assemble lines of terminal input
53
* into Tcl commands. */
54
static Tcl_DString line; /* Used to read the next line from the
55
* terminal input. */
56
static int tty; /* Non-zero means standard input is a
57
* terminal-like device. Zero means it's
58
* a file. */
59
60
/*
61
* Forward declarations for procedures defined later in this file.
62
*/
63
64
static void Prompt _ANSI_ARGS_((Tcl_Interp *interp, int partial));
65
static void StdinProc _ANSI_ARGS_((ClientData clientData,
66
int mask));
67
68
/*
69
*----------------------------------------------------------------------
70
*
71
* Tk_Main --
72
*
73
* Main program for Wish and most other Tk-based applications.
74
*
75
* Results:
76
* None. This procedure never returns (it exits the process when
77
* it's done.
78
*
79
* Side effects:
80
* This procedure initializes the Tk world and then starts
81
* interpreting commands; almost anything could happen, depending
82
* on the script being interpreted.
83
*
84
*----------------------------------------------------------------------
85
*/
86
87
void
88
Tk_Main(argc, argv, appInitProc)
89
int argc; /* Number of arguments. */
90
char **argv; /* Array of argument strings. */
91
Tcl_AppInitProc *appInitProc; /* Application-specific initialization
92
* procedure to call after most
93
* initialization but before starting
94
* to execute commands. */
95
{
96
char *args, *fileName;
97
char buf[20];
98
int code;
99
size_t length;
100
Tcl_Channel inChannel, outChannel, errChannel;
101
102
Tcl_FindExecutable(argv[0]);
103
interp = Tcl_CreateInterp();
104
#ifdef TCL_MEM_DEBUG
105
Tcl_InitMemory(interp);
106
#endif
107
108
/*
109
* Parse command-line arguments. A leading "-file" argument is
110
* ignored (a historical relic from the distant past). If the
111
* next argument doesn't start with a "-" then strip it off and
112
* use it as the name of a script file to process.
113
*/
114
115
fileName = NULL;
116
if (argc > 1) {
117
length = strlen(argv[1]);
118
if ((length >= 2) && (strncmp(argv[1], "-file", length) == 0)) {
119
argc--;
120
argv++;
121
}
122
}
123
if ((argc > 1) && (argv[1][0] != '-')) {
124
fileName = argv[1];
125
argc--;
126
argv++;
127
}
128
129
/*
130
* Make command-line arguments available in the Tcl variables "argc"
131
* and "argv".
132
*/
133
134
args = Tcl_Merge(argc-1, argv+1);
135
Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY);
136
ckfree(args);
137
sprintf(buf, "%d", argc-1);
138
Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);
139
Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv[0],
140
TCL_GLOBAL_ONLY);
141
142
/*
143
* Set the "tcl_interactive" variable.
144
*/
145
146
/*
147
* For now, under Windows, we assume we are not running as a console mode
148
* app, so we need to use the GUI console. In order to enable this, we
149
* always claim to be running on a tty. This probably isn't the right
150
* way to do it.
151
*/
152
153
#ifdef WIN_TCL
154
tty = 1;
155
#else
156
tty = isatty(0);
157
#endif
158
Tcl_SetVar(interp, "tcl_interactive",
159
((fileName == NULL) && tty) ? "1" : "0", TCL_GLOBAL_ONLY);
160
161
/*
162
* Invoke application-specific initialization.
163
*/
164
165
if ((*appInitProc)(interp) != TCL_OK) {
166
errChannel = Tcl_GetStdChannel(TCL_STDERR);
167
if (errChannel) {
168
Tcl_Write(errChannel,
169
"application-specific initialization failed: ", -1);
170
Tcl_Write(errChannel, interp->result, -1);
171
Tcl_Write(errChannel, "\n", 1);
172
}
173
}
174
175
/*
176
* Invoke the script specified on the command line, if any.
177
*/
178
179
if (fileName != NULL) {
180
code = Tcl_EvalFile(interp, fileName);
181
if (code != TCL_OK) {
182
goto error;
183
}
184
tty = 0;
185
} else {
186
187
/*
188
* Evaluate the .rc file, if one has been specified.
189
*/
190
191
Tcl_SourceRCFile(interp);
192
193
/*
194
* Establish a channel handler for stdin.
195
*/
196
197
inChannel = Tcl_GetStdChannel(TCL_STDIN);
198
if (inChannel) {
199
Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
200
(ClientData) inChannel);
201
}
202
if (tty) {
203
Prompt(interp, 0);
204
}
205
}
206
207
outChannel = Tcl_GetStdChannel(TCL_STDOUT);
208
if (outChannel) {
209
Tcl_Flush(outChannel);
210
}
211
Tcl_DStringInit(&command);
212
Tcl_DStringInit(&line);
213
Tcl_ResetResult(interp);
214
215
/*
216
* Loop infinitely, waiting for commands to execute. When there
217
* are no windows left, Tk_MainLoop returns and we exit.
218
*/
219
220
Tk_MainLoop();
221
Tcl_DeleteInterp(interp);
222
Tcl_Exit(0);
223
224
error:
225
/*
226
* The following statement guarantees that the errorInfo
227
* variable is set properly.
228
*/
229
230
Tcl_AddErrorInfo(interp, "");
231
errChannel = Tcl_GetStdChannel(TCL_STDERR);
232
if (errChannel) {
233
Tcl_Write(errChannel, Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY),
234
-1);
235
Tcl_Write(errChannel, "\n", 1);
236
}
237
Tcl_DeleteInterp(interp);
238
Tcl_Exit(1);
239
}
240
241
/*
242
*----------------------------------------------------------------------
243
*
244
* StdinProc --
245
*
246
* This procedure is invoked by the event dispatcher whenever
247
* standard input becomes readable. It grabs the next line of
248
* input characters, adds them to a command being assembled, and
249
* executes the command if it's complete.
250
*
251
* Results:
252
* None.
253
*
254
* Side effects:
255
* Could be almost arbitrary, depending on the command that's
256
* typed.
257
*
258
*----------------------------------------------------------------------
259
*/
260
261
/* ARGSUSED */
262
static void
263
StdinProc(clientData, mask)
264
ClientData clientData; /* Not used. */
265
int mask; /* Not used. */
266
{
267
static int gotPartial = 0;
268
char *cmd;
269
int code, count;
270
Tcl_Channel chan = (Tcl_Channel) clientData;
271
272
count = Tcl_Gets(chan, &line);
273
274
if (count < 0) {
275
if (!gotPartial) {
276
if (tty) {
277
Tcl_Exit(0);
278
} else {
279
Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan);
280
}
281
return;
282
} else {
283
count = 0;
284
}
285
}
286
287
(void) Tcl_DStringAppend(&command, Tcl_DStringValue(&line), -1);
288
cmd = Tcl_DStringAppend(&command, "\n", -1);
289
Tcl_DStringFree(&line);
290
291
if (!Tcl_CommandComplete(cmd)) {
292
gotPartial = 1;
293
goto prompt;
294
}
295
gotPartial = 0;
296
297
/*
298
* Disable the stdin channel handler while evaluating the command;
299
* otherwise if the command re-enters the event loop we might
300
* process commands from stdin before the current command is
301
* finished. Among other things, this will trash the text of the
302
* command being evaluated.
303
*/
304
305
Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) chan);
306
code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL);
307
Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
308
(ClientData) chan);
309
Tcl_DStringFree(&command);
310
if (*interp->result != 0) {
311
if ((code != TCL_OK) || (tty)) {
312
/*
313
* The statement below used to call "printf", but that resulted
314
* in core dumps under Solaris 2.3 if the result was very long.
315
*
316
* NOTE: This probably will not work under Windows either.
317
*/
318
319
puts(interp->result);
320
}
321
}
322
323
/*
324
* Output a prompt.
325
*/
326
327
prompt:
328
if (tty) {
329
Prompt(interp, gotPartial);
330
}
331
Tcl_ResetResult(interp);
332
}
333
334
/*
335
*----------------------------------------------------------------------
336
*
337
* Prompt --
338
*
339
* Issue a prompt on standard output, or invoke a script
340
* to issue the prompt.
341
*
342
* Results:
343
* None.
344
*
345
* Side effects:
346
* A prompt gets output, and a Tcl script may be evaluated
347
* in interp.
348
*
349
*----------------------------------------------------------------------
350
*/
351
352
static void
353
Prompt(interp, partial)
354
Tcl_Interp *interp; /* Interpreter to use for prompting. */
355
int partial; /* Non-zero means there already
356
* exists a partial command, so use
357
* the secondary prompt. */
358
{
359
char *promptCmd;
360
int code;
361
Tcl_Channel outChannel, errChannel;
362
363
errChannel = Tcl_GetChannel(interp, "stderr", NULL);
364
365
promptCmd = Tcl_GetVar(interp,
366
partial ? "tcl_prompt2" : "tcl_prompt1", TCL_GLOBAL_ONLY);
367
if (promptCmd == NULL) {
368
defaultPrompt:
369
if (!partial) {
370
371
/*
372
* We must check that outChannel is a real channel - it
373
* is possible that someone has transferred stdout out of
374
* this interpreter with "interp transfer".
375
*/
376
377
outChannel = Tcl_GetChannel(interp, "stdout", NULL);
378
if (outChannel != (Tcl_Channel) NULL) {
379
Tcl_Write(outChannel, "% ", 2);
380
}
381
}
382
} else {
383
code = Tcl_Eval(interp, promptCmd);
384
if (code != TCL_OK) {
385
Tcl_AddErrorInfo(interp,
386
"\n (script that generates prompt)");
387
/*
388
* We must check that errChannel is a real channel - it
389
* is possible that someone has transferred stderr out of
390
* this interpreter with "interp transfer".
391
*/
392
393
errChannel = Tcl_GetChannel(interp, "stderr", NULL);
394
if (errChannel != (Tcl_Channel) NULL) {
395
Tcl_Write(errChannel, interp->result, -1);
396
Tcl_Write(errChannel, "\n", 1);
397
}
398
goto defaultPrompt;
399
}
400
}
401
outChannel = Tcl_GetChannel(interp, "stdout", NULL);
402
if (outChannel != (Tcl_Channel) NULL) {
403
Tcl_Flush(outChannel);
404
}
405
}
406
407