Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/tksh/tkMain.c
1808 views
1
#pragma prototyped
2
/*
3
* tkMain.c --
4
*
5
* This file contains a generic main program for Tk-based applications.
6
* It can be used as-is for many applications, just by supplying a
7
* different appInitProc procedure for each specific application.
8
* Or, it can be used as a template for creating new main programs
9
* for Tk applications.
10
*
11
* Copyright (c) 1990-1994 The Regents of the University of California.
12
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
13
*
14
* See the file "license.terms" for information on usage and redistribution
15
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16
*
17
* SCCS: @(#) tkMain.c 1.148 96/03/25 18:08:43
18
*/
19
20
#include "tksh.h"
21
#include <ctype.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <tcl.h>
25
#include <tk.h>
26
#if 0
27
#ifdef NO_STDLIB_H
28
# include "../compat/stdlib.h"
29
#else
30
# include <stdlib.h>
31
#endif
32
#endif
33
34
/*
35
* Declarations for various library procedures and variables (don't want
36
* to include tkInt.h or tkPort.h here, because people might copy this
37
* file out of the Tk source directory to make their own modified versions).
38
* Note: don't declare "exit" here even though a declaration is really
39
* needed, because it will conflict with a declaration elsewhere on
40
* some systems.
41
*/
42
43
extern int isatty _ANSI_ARGS_((int fd));
44
#if 0
45
extern int read _ANSI_ARGS_((int fd, char *buf, size_t size));
46
extern char * strrchr _ANSI_ARGS_((CONST char *string, int c));
47
#endif
48
49
/*
50
* Global variables used by the main program:
51
*/
52
53
static Tcl_Interp *interp; /* Interpreter for this application. */
54
static Tcl_DString command; /* Used to assemble lines of terminal input
55
* into Tcl commands. */
56
static Tcl_DString line; /* Used to read the next line from the
57
* terminal input. */
58
static int tty; /* Non-zero means standard input is a
59
* terminal-like device. Zero means it's
60
* a file. */
61
62
/*
63
* Forward declarations for procedures defined later in this file.
64
*/
65
66
static void Prompt _ANSI_ARGS_((Tcl_Interp *interp, int partial));
67
static void StdinProc _ANSI_ARGS_((ClientData clientData,
68
int mask));
69
70
/*
71
*----------------------------------------------------------------------
72
*
73
* Tk_Main --
74
*
75
* Main program for Wish and most other Tk-based applications.
76
*
77
* Results:
78
* None. This procedure never returns (it exits the process when
79
* it's done.
80
*
81
* Side effects:
82
* This procedure initializes the Tk world and then starts
83
* interpreting commands; almost anything could happen, depending
84
* on the script being interpreted.
85
*
86
*----------------------------------------------------------------------
87
*/
88
89
void
90
Tksh_TkMain(argc, argv, appInitProc)
91
int argc; /* Number of arguments. */
92
char **argv; /* Array of argument strings. */
93
Tcl_AppInitProc *appInitProc; /* Application-specific initialization
94
* procedure to call after most
95
* initialization but before starting
96
* to execute commands. */
97
{
98
char *args, *fileName;
99
char buf[20];
100
int code;
101
size_t length;
102
#if 0
103
Tcl_Channel inChannel, outChannel;
104
#endif
105
Tcl_Channel errChannel, chan;
106
107
Tcl_FindExecutable(argv[0]);
108
interp = Tcl_CreateInterp();
109
#ifdef TCL_MEM_DEBUG
110
Tcl_InitMemory(interp);
111
#endif
112
113
/*
114
* Parse command-line arguments. A leading "-file" argument is
115
* ignored (a historical relic from the distant past). If the
116
* next argument doesn't start with a "-" then strip it off and
117
* use it as the name of a script file to process.
118
*/
119
120
fileName = NULL;
121
if (argc > 1) {
122
length = strlen(argv[1]);
123
if ((length >= 2) && (strncmp(argv[1], "-file", length) == 0)) {
124
argc--;
125
argv++;
126
}
127
}
128
if ((argc > 1) && (argv[1][0] != '-')) {
129
fileName = argv[1];
130
argc--;
131
argv++;
132
}
133
134
/*
135
* Make command-line arguments available in the Tcl variables "argc"
136
* and "argv".
137
*/
138
139
args = Tcl_Merge(argc-1, argv+1);
140
Tcl_SetVar(interp, "argv", args, TCL_GLOBAL_ONLY);
141
ckfree(args);
142
sprintf(buf, "%d", argc-1);
143
Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);
144
Tcl_SetVar(interp, "argv0", (fileName != NULL) ? fileName : argv[0],
145
TCL_GLOBAL_ONLY);
146
147
/*
148
* Set the "tcl_interactive" variable.
149
*/
150
151
/*
152
* For now, under Windows, we assume we are not running as a console mode
153
* app, so we need to use the GUI console. In order to enable this, we
154
* always claim to be running on a tty. This probably isn't the right
155
* way to do it.
156
*/
157
158
#ifdef __WIN32__
159
tty = 1;
160
#else
161
tty = isatty(0);
162
#endif
163
Tcl_SetVar(interp, "tcl_interactive",
164
((fileName == NULL) && tty) ? "1" : "0", TCL_GLOBAL_ONLY);
165
166
/*
167
* Invoke application-specific initialization.
168
*/
169
170
if ((*appInitProc)(interp) != TCL_OK) {
171
errChannel = Tcl_GetStdChannel(TCL_STDERR);
172
if (errChannel) {
173
Tcl_Write(errChannel,
174
"application-specific initialization failed: ", -1);
175
Tcl_Write(errChannel, interp->result, -1);
176
Tcl_Write(errChannel, "\n", 1);
177
}
178
Tcl_DeleteInterp(interp);
179
Tcl_Exit(1); /* added so tksh will exit here */
180
}
181
182
/*
183
* Invoke the script specified on the command line, if any.
184
*/
185
186
if (fileName != NULL) {
187
code = Tcl_EvalFile(interp, fileName);
188
if (code != TCL_OK) {
189
goto error;
190
}
191
tty = 0;
192
} else {
193
194
/*
195
* Commands will come from standard input, so set up an event
196
* handler for standard input. Evaluate the .rc file, if one
197
* has been specified, set up an event handler for standard
198
* input, and print a prompt if the input device is a terminal.
199
*/
200
201
fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY);
202
203
if (fileName != NULL) {
204
Tcl_DString buffer;
205
char *fullName;
206
207
fullName = Tcl_TranslateFileName(interp, fileName, &buffer);
208
if (fullName == NULL) {
209
errChannel = Tcl_GetStdChannel(TCL_STDERR);
210
if (errChannel) {
211
Tcl_Write(errChannel, interp->result, -1);
212
Tcl_Write(errChannel, "\n", 1);
213
}
214
} else {
215
216
/*
217
* NOTE: The following relies on O_RDONLY==0.
218
*/
219
220
chan = Tcl_OpenFileChannel(interp, fullName, "r", 0);
221
if (chan != (Tcl_Channel) NULL) {
222
Tcl_Close(NULL, chan);
223
if (Tcl_EvalFile(interp, fullName) != TCL_OK) {
224
errChannel = Tcl_GetStdChannel(TCL_STDERR);
225
if (errChannel) {
226
Tcl_Write(errChannel, interp->result, -1);
227
Tcl_Write(errChannel, "\n", 1);
228
}
229
}
230
}
231
}
232
233
Tcl_DStringFree(&buffer);
234
}
235
236
#if 0
237
/*
238
* Establish a channel handler for stdin.
239
*/
240
241
inChannel = Tcl_GetStdChannel(TCL_STDIN);
242
if (inChannel) {
243
Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
244
(ClientData) inChannel);
245
}
246
if (tty) {
247
Prompt(interp, 0);
248
}
249
#endif
250
}
251
252
#if 0
253
outChannel = Tcl_GetStdChannel(TCL_STDOUT);
254
if (outChannel) {
255
Tcl_Flush(outChannel);
256
}
257
Tcl_DStringInit(&command);
258
Tcl_DStringInit(&line);
259
#endif
260
Tcl_ResetResult(interp);
261
262
/*
263
* Loop infinitely, waiting for commands to execute. When there
264
* are no windows left, Tk_MainLoop returns and we exit.
265
*/
266
267
#if 0
268
Tk_MainLoop();
269
Tcl_DeleteInterp(interp);
270
Tcl_Exit(0);
271
#else
272
return;
273
#endif
274
275
error:
276
/*
277
* The following statement guarantees that the errorInfo
278
* variable is set properly.
279
*/
280
281
Tcl_AddErrorInfo(interp, "");
282
errChannel = Tcl_GetStdChannel(TCL_STDERR);
283
if (errChannel) {
284
Tcl_Write(errChannel, Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY),
285
-1);
286
Tcl_Write(errChannel, "\n", 1);
287
}
288
Tcl_DeleteInterp(interp);
289
Tcl_Exit(1);
290
}
291
292
#if 0
293
/*
294
*----------------------------------------------------------------------
295
*
296
* StdinProc --
297
*
298
* This procedure is invoked by the event dispatcher whenever
299
* standard input becomes readable. It grabs the next line of
300
* input characters, adds them to a command being assembled, and
301
* executes the command if it's complete.
302
*
303
* Results:
304
* None.
305
*
306
* Side effects:
307
* Could be almost arbitrary, depending on the command that's
308
* typed.
309
*
310
*----------------------------------------------------------------------
311
*/
312
313
/* ARGSUSED */
314
static void
315
StdinProc(clientData, mask)
316
ClientData clientData; /* Not used. */
317
int mask; /* Not used. */
318
{
319
static int gotPartial = 0;
320
char *cmd;
321
int code, count;
322
Tcl_Channel chan = (Tcl_Channel) clientData;
323
324
count = Tcl_Gets(chan, &line);
325
326
if (count < 0) {
327
if (!gotPartial) {
328
if (tty) {
329
Tcl_Exit(0);
330
} else {
331
Tcl_DeleteChannelHandler(chan, StdinProc, (ClientData) chan);
332
}
333
return;
334
} else {
335
count = 0;
336
}
337
}
338
339
(void) Tcl_DStringAppend(&command, Tcl_DStringValue(&line), -1);
340
cmd = Tcl_DStringAppend(&command, "\n", -1);
341
Tcl_DStringFree(&line);
342
343
if (!Tcl_CommandComplete(cmd)) {
344
gotPartial = 1;
345
goto prompt;
346
}
347
gotPartial = 0;
348
349
/*
350
* Disable the stdin channel handler while evaluating the command;
351
* otherwise if the command re-enters the event loop we might
352
* process commands from stdin before the current command is
353
* finished. Among other things, this will trash the text of the
354
* command being evaluated.
355
*/
356
357
Tcl_CreateChannelHandler(chan, 0, StdinProc, (ClientData) chan);
358
code = Tcl_RecordAndEval(interp, cmd, TCL_EVAL_GLOBAL);
359
Tcl_CreateChannelHandler(chan, TCL_READABLE, StdinProc,
360
(ClientData) chan);
361
Tcl_DStringFree(&command);
362
if (*interp->result != 0) {
363
if ((code != TCL_OK) || (tty)) {
364
/*
365
* The statement below used to call "printf", but that resulted
366
* in core dumps under Solaris 2.3 if the result was very long.
367
*
368
* NOTE: This probably will not work under Windows either.
369
*/
370
371
puts(interp->result);
372
}
373
}
374
375
/*
376
* Output a prompt.
377
*/
378
379
prompt:
380
if (tty) {
381
Prompt(interp, gotPartial);
382
}
383
Tcl_ResetResult(interp);
384
}
385
386
/*
387
*----------------------------------------------------------------------
388
*
389
* Prompt --
390
*
391
* Issue a prompt on standard output, or invoke a script
392
* to issue the prompt.
393
*
394
* Results:
395
* None.
396
*
397
* Side effects:
398
* A prompt gets output, and a Tcl script may be evaluated
399
* in interp.
400
*
401
*----------------------------------------------------------------------
402
*/
403
404
static void
405
Prompt(interp, partial)
406
Tcl_Interp *interp; /* Interpreter to use for prompting. */
407
int partial; /* Non-zero means there already
408
* exists a partial command, so use
409
* the secondary prompt. */
410
{
411
char *promptCmd;
412
int code;
413
Tcl_Channel outChannel, errChannel;
414
415
errChannel = Tcl_GetChannel(interp, "stderr", NULL);
416
417
promptCmd = Tcl_GetVar(interp,
418
partial ? "tcl_prompt2" : "tcl_prompt1", TCL_GLOBAL_ONLY);
419
if (promptCmd == NULL) {
420
defaultPrompt:
421
if (!partial) {
422
423
/*
424
* We must check that outChannel is a real channel - it
425
* is possible that someone has transferred stdout out of
426
* this interpreter with "interp transfer".
427
*/
428
429
outChannel = Tcl_GetChannel(interp, "stdout", NULL);
430
if (outChannel != (Tcl_Channel) NULL) {
431
Tcl_Write(outChannel, "% ", 2);
432
}
433
}
434
} else {
435
code = Tcl_Eval(interp, promptCmd);
436
if (code != TCL_OK) {
437
Tcl_AddErrorInfo(interp,
438
"\n (script that generates prompt)");
439
/*
440
* We must check that errChannel is a real channel - it
441
* is possible that someone has transferred stderr out of
442
* this interpreter with "interp transfer".
443
*/
444
445
errChannel = Tcl_GetChannel(interp, "stderr", NULL);
446
if (errChannel != (Tcl_Channel) NULL) {
447
Tcl_Write(errChannel, interp->result, -1);
448
Tcl_Write(errChannel, "\n", 1);
449
}
450
goto defaultPrompt;
451
}
452
}
453
outChannel = Tcl_GetChannel(interp, "stdout", NULL);
454
if (outChannel != (Tcl_Channel) NULL) {
455
Tcl_Flush(outChannel);
456
}
457
}
458
#endif
459
460
461
/*********************************************************************/
462
463
static int Tksh_BindCmd(clientData, interp, argc, argv)
464
ClientData clientData; /* Main window associated with
465
* interpreter. */
466
Tcl_Interp *interp; /* Current interpreter. */
467
int argc; /* Number of arguments. */
468
char **argv; /* Argument strings. */
469
{
470
char *bindscript, *script = NULL, *oldarg;
471
int result;
472
473
if ((argc == 4) && (argv[3][0] != '+'))
474
{
475
static char *bindprefixksh = "#!ksh\n";
476
static char *bindprefixtcl = "#!tcl\n";
477
# define BINDPRELEN 6
478
479
bindscript = argv[3];
480
if ((bindscript[0] == '#') && (bindscript[1] == '!' ))
481
{
482
if ((strcmp(bindscript, bindprefixksh) == 0) ||
483
(strcmp(bindscript, bindprefixtcl) == 0))
484
return Tk_BindCmd(clientData,interp,argc,argv);
485
}
486
script = (char *) malloc(strlen(bindscript) + BINDPRELEN +1);
487
strcpy(script, (((Interp *) interp)->interpType == INTERP_TCL)?
488
bindprefixtcl : bindprefixksh);
489
strcpy(script + BINDPRELEN, bindscript);
490
oldarg = argv[3];
491
argv[3] = script;
492
result = Tk_BindCmd(clientData, interp, argc, argv);
493
argv[3] = oldarg;
494
free(script);
495
return result;
496
}
497
return Tk_BindCmd(clientData, interp, argc, argv);
498
}
499
static void bindsetup(Tcl_Interp *interp)
500
{
501
Tcl_CmdInfo bindInfo;
502
if (Tcl_GetCommandInfo(interp, "bind", & bindInfo))
503
{
504
bindInfo.proc = Tksh_BindCmd;
505
/* Tcl_SetCommandInfo(interp, "bind", &bindInfo); */
506
Tcl_CreateCommand(interp, "bind", bindInfo.proc,
507
bindInfo.clientData, bindInfo.deleteProc);
508
Tksh_SetCommandType(interp, "bind", INTERP_CURRENT);
509
}
510
}
511
static int b_tkloop(int argc, char **argv, Shbltin_t *context)
512
{
513
Tcl_Interp *interp = (Tcl_Interp *)context->ptr;
514
Tksh_BeginBlock(interp, INTERP_TCL);
515
Tk_MainLoop();
516
Tksh_EndBlock(interp);
517
return 0;
518
}
519
int Tksh_Init(interp)
520
Tcl_Interp *interp; /* Interpreter to initialize. */
521
{
522
#if 0
523
static char initCmd[] =
524
"if [[ -f $tk_library/tk.ksh ]] ; then \n\
525
. $tk_library/tk.ksh\n\
526
else \n\
527
msg=\"can't find $tk_library/tk.ksh; perhaps you \"\n\
528
msg=\"$msg need to\\ninstall Tk or set your TK_LIBRARY \"\n\
529
msg=\"$msg environment variable?\"\n\
530
print -u2 $msg\n\
531
fi\n";
532
#endif
533
bindsetup(interp);
534
sh_addbuiltin("tkloop", b_tkloop, (void *) interp);
535
return TCL_OK;
536
}
537
538
static int
539
Tksh_AppInit(interp)
540
Tcl_Interp *interp; /* Interpreter for application. */
541
{
542
if (Tcl_Init(interp) == TCL_ERROR) {
543
return TCL_ERROR;
544
}
545
Tksh_BeginBlock(interp, INTERP_TCL);
546
/* Should be current, but Tk_Init evals a script. */
547
if (Tk_Init(interp) == TCL_ERROR) {
548
return TCL_ERROR;
549
}
550
if (Tksh_Init(interp) == TCL_ERROR) {
551
return TCL_ERROR;
552
}
553
Tksh_SetCommandType(interp, "button", INTERP_CURRENT); /* Why do this? */
554
Tksh_EndBlock(interp);
555
Tcl_StaticPackage(interp, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL);
556
#ifdef TK_TEST
557
if (Tktest_Init(interp) == TCL_ERROR) {
558
return TCL_ERROR;
559
}
560
Tcl_StaticPackage(interp, "Tktest", Tktest_Init,
561
(Tcl_PackageInitProc *) NULL);
562
#endif /* TK_TEST */
563
564
565
/*
566
* Call the init procedures for included packages. Each call should
567
* look like this:
568
*
569
* if (Mod_Init(interp) == TCL_ERROR) {
570
* return TCL_ERROR;
571
* }
572
*
573
* where "Mod" is the name of the module.
574
*/
575
576
/*
577
* Call Tcl_CreateCommand for application-specific commands, if
578
* they weren't already created by the init procedures called above.
579
*/
580
581
/*
582
* Specify a user-specific startup file to invoke if the application
583
* is run interactively. Typically the startup file is "~/.apprc"
584
* where "app" is the name of the application. If this line is deleted
585
* then no user-specific startup file will be run under any conditions.
586
*/
587
588
Tcl_SetVar(interp, "tcl_rcFileName", "~/.wishrc", TCL_GLOBAL_ONLY);
589
return TCL_OK;
590
}
591
#include <signal.h>
592
static int gotIntr;
593
extern int Tcl_NumEventsFound(void);
594
static void SigEventSetup(ClientData clientData, int flags)
595
{
596
}
597
static int SigEventProcess(Tcl_Event *evPtr, int flags)
598
{
599
return 1;
600
}
601
static void SigEventCheck(ClientData clientData, int flags)
602
{
603
Tcl_Event *evPtr;
604
if (Tcl_NumEventsFound() < 0)
605
{
606
evPtr = (Tcl_Event *) malloc(sizeof(Tcl_Event));
607
evPtr->proc = SigEventProcess;
608
gotIntr = 1;;
609
Tcl_QueueEvent(evPtr, TCL_QUEUE_TAIL);
610
}
611
}
612
static void TmoutProc(ClientData clientData)
613
{
614
*((int *)clientData) = 1;
615
}
616
static void fileReady(ClientData clientData, int mask)
617
{
618
Tcl_File *filePtr = (Tcl_File *) clientData;
619
/* Tcl_DeleteFileHandler(*filePtr); */
620
Tcl_CreateFileHandler(*filePtr, 0, fileReady, (ClientData) 0);
621
*filePtr = NULL;
622
}
623
#include <wait.h>
624
int tksh_waitevent(int fd, long tmout, int rw)
625
{
626
int tFlag = 0, result = 1;
627
Tcl_TimerToken token;
628
Tcl_File file = NULL;
629
gotIntr = 0;
630
631
if (fd >= 0)
632
{
633
file = Tcl_GetFile((ClientData)fd ,TCL_UNIX_FD);
634
Tcl_CreateFileHandler(file, TCL_READABLE, fileReady, &file);
635
}
636
637
if (tmout> 0)
638
token = Tcl_CreateTimerHandler((int)tmout,TmoutProc,&(tFlag));
639
640
Tksh_BeginBlock(interp, INTERP_TCL); /* Best Guess */
641
while ((!gotIntr) && (!tFlag) && ((fd<0)||file) && result && (fd>=0 || !sh_waitsafe()))
642
result = Tcl_DoOneEvent(0);
643
Tksh_EndBlock(interp);
644
645
if (gotIntr)
646
{
647
result = -1;
648
errno = EINTR;
649
} else
650
{
651
result = 1;
652
}
653
654
if (tmout > 0)
655
Tcl_DeleteTimerHandler(token);
656
if (file)
657
Tcl_CreateFileHandler(file, 0, fileReady, (ClientData) 0);
658
659
return result;
660
}
661
#if 0
662
static void stoptk(void)
663
{
664
Tcl_Exit(0);
665
}
666
#endif
667
int b_tkinit(int argc, char *argv[], Shbltin_t *context)
668
{
669
static char *av[] = { "tkinit", 0 };
670
671
if (argc == 0)
672
{
673
argc = 1;
674
argv = av;
675
}
676
Tksh_TkMain(argc,argv,context ? (Tcl_AppInitProc*)context->ptr : Tksh_AppInit);
677
Tcl_CreateEventSource(SigEventSetup,SigEventCheck,NULL);
678
sh_waitnotify(tksh_waitevent);
679
/* atexit(stoptk); */
680
return 0;
681
}
682
683