2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
13 static const char sccsid
[] = "@(#)tk_main.c 8.18 (Berkeley) 9/24/96";
16 #include <sys/types.h>
17 #include <sys/queue.h>
19 #include <bitstring.h>
29 #include "../common/common.h"
31 #include "pathnames.h"
33 GS
*__global_list
; /* GLOBAL: List of screens. */
34 sigset_t __sigblockset
; /* GLOBAL: Blocked signals. */
36 static GS
*gs_init
__P((char *));
37 static void killsig
__P((SCR
*));
38 static void perr
__P((char *, char *));
39 static void sig_end
__P((GS
*));
40 static int sig_init
__P((GS
*));
41 static int tcl_init
__P((GS
*));
42 static void tcl_err
__P((TK_PRIVATE
*));
46 * This is the main loop for the standalone Tcl/Tk editor.
58 char **p_av
, **t_av
, *script
;
60 /* If loaded at 0 and jumping through a NULL pointer, stop. */
64 /* Create and initialize the global structure. */
65 __global_list
= gp
= gs_init(argv
[0]);
67 /* Initialize Tk/Tcl. */
72 * Strip out any arguments that the common editor doesn't understand
73 * (i.e. the Tk/Tcl arguments). Search for -i first, it's the Tk/Tcl
74 * startup script and needs to be run first.
77 * There's no way to portably call getopt twice.
80 for (p_av
= t_av
= argv
;;) {
85 if (!strcmp(*t_av
, "--")) {
86 while ((*p_av
++ = *t_av
++) != NULL
);
89 if (!memcmp(*t_av
, "-i", sizeof("-i") - 1)) {
90 if (t_av
[0][2] != '\0') {
96 if (t_av
[1] != NULL
) {
105 for (p_av
= t_av
= argv
;;) {
110 if (!strcmp(*t_av
, "--")) {
111 while ((*p_av
++ = *t_av
++) != NULL
);
114 if (t_av
[1] != NULL
&&
115 (!memcmp(*t_av
, "-background", sizeof("-background") - 1) ||
116 !memcmp(*t_av
, "-bg", sizeof("-bg") - 1) ||
117 !memcmp(*t_av
, "-borderwidth", sizeof("-borderwidth") - 1)||
118 !memcmp(*t_av
, "-bd", sizeof("-bd") - 1) ||
119 !memcmp(*t_av
, "-foreground", sizeof("-foreground") - 1) ||
120 !memcmp(*t_av
, "-fg", sizeof("-fg") - 1) ||
121 !memcmp(*t_av
, "-font", sizeof("-font") - 1))) {
122 if (Tcl_VarEval(tkp
->interp
, ".t configure ",
123 t_av
[0], " ", t_av
[1], NULL
) == TCL_ERROR
)
129 if (!memcmp(*t_av
, "-geometry", sizeof("-geometry") - 1)) {
130 if (Tcl_VarEval(tkp
->interp
, "wm geometry . ",
131 *t_av
+ sizeof("-geometry") - 1, NULL
) == TCL_ERROR
)
140 /* Load the initial Tcl/Tk script. */
142 if (Tcl_EvalFile(tkp
->interp
, script
) == TCL_ERROR
)
145 /* Add the terminal type to the global structure. */
146 if ((OG_D_STR(gp
, GO_TERM
) =
147 OG_STR(gp
, GO_TERM
) = strdup("tkterm")) == NULL
)
148 perr(gp
->progname
, NULL
);
150 /* Figure out how big the screen is. */
151 if (tk_ssize(NULL
, 0, &rows
, &cols
, NULL
))
154 /* Add the rows and columns to the global structure. */
155 OG_VAL(gp
, GO_LINES
) = OG_D_VAL(gp
, GO_LINES
) = rows
;
156 OG_VAL(gp
, GO_COLUMNS
) = OG_D_VAL(gp
, GO_COLUMNS
) = cols
;
158 /* Start catching signals. */
163 rval
= editor(gp
, argc
, argv
);
165 /* Clean up signals. */
168 /* Clean up the terminal. */
171 /* If a killer signal arrived, pretend we just got it. */
172 if (tkp
->killersig
) {
173 (void)signal(tkp
->killersig
, SIG_DFL
);
174 (void)kill(getpid(), tkp
->killersig
);
178 /* Free the global and TK private areas. */
179 #if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
189 * Create and partially initialize the GS structure.
200 /* Figure out what our name is. */
201 if ((p
= strrchr(name
, '/')) != NULL
)
204 /* Allocate the global structure. */
205 CALLOC_NOMSG(NULL
, gp
, GS
*, 1, sizeof(GS
));
207 /* Allocate the CL private structure. */
209 CALLOC_NOMSG(NULL
, tkp
, TK_PRIVATE
*, 1, sizeof(TK_PRIVATE
));
210 if (gp
== NULL
|| tkp
== NULL
)
212 gp
->tk_private
= tkp
;
213 TAILQ_INIT(&tkp
->evq
);
215 /* Initialize the list of curses functions. */
216 gp
->scr_addstr
= tk_addstr
;
217 gp
->scr_attr
= tk_attr
;
218 gp
->scr_baud
= tk_baud
;
219 gp
->scr_bell
= tk_bell
;
221 gp
->scr_clrtoeol
= tk_clrtoeol
;
222 gp
->scr_cursor
= tk_cursor
;
223 gp
->scr_deleteln
= tk_deleteln
;
224 gp
->scr_event
= tk_event
;
225 gp
->scr_ex_adjust
= tk_ex_adjust
;
226 gp
->scr_fmap
= tk_fmap
;
227 gp
->scr_insertln
= tk_insertln
;
228 gp
->scr_keyval
= tk_keyval
;
229 gp
->scr_move
= tk_move
;
231 gp
->scr_optchange
= tk_optchange
;
232 gp
->scr_refresh
= tk_refresh
;
233 gp
->scr_rename
= tk_rename
;
234 gp
->scr_screen
= tk_screen
;
235 gp
->scr_suspend
= tk_suspend
;
236 gp
->scr_usage
= tk_usage
;
239 * We expect that if we've lost our controlling terminal that the
240 * open() (but not the tcgetattr()) will fail.
242 if (isatty(STDIN_FILENO
)) {
243 if (tcgetattr(STDIN_FILENO
, &tkp
->orig
) == -1)
245 } else if ((fd
= open(_PATH_TTY
, O_RDONLY
, 0)) != -1) {
246 if (tcgetattr(fd
, &tkp
->orig
) == -1)
247 tcfail
: perr(name
, "tcgetattr");
257 * Get Tcl/Tk up and running.
266 if ((tkp
->interp
= Tcl_CreateInterp()) == NULL
)
268 /* XXX: Tk 4.1 has an incompatible change. */
269 #if (TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION == 0)
270 if (Tk_CreateMainWindow(tkp
->interp
, NULL
, "vi", "Vi") == NULL
)
273 if (Tcl_Init(tkp
->interp
) == TCL_ERROR
)
275 if (Tk_Init(tkp
->interp
) == TCL_ERROR
)
278 /* Shared variables. */
279 (void)Tcl_LinkVar(tkp
->interp
,
280 "tk_cursor_row", (char *)&tkp
->tk_cursor_row
, TCL_LINK_INT
);
281 (void)Tcl_LinkVar(tkp
->interp
,
282 "tk_cursor_col", (char *)&tkp
->tk_cursor_col
, TCL_LINK_INT
);
283 (void)Tcl_LinkVar(tkp
->interp
,
284 "tk_ssize_row", (char *)&tkp
->tk_ssize_row
, TCL_LINK_INT
);
285 (void)Tcl_LinkVar(tkp
->interp
,
286 "tk_ssize_col", (char *)&tkp
->tk_ssize_col
, TCL_LINK_INT
);
288 /* Functions called by Tcl script. */
289 Tcl_CreateCommand(tkp
->interp
, "tk_key", tk_key
, tkp
, NULL
);
290 Tcl_CreateCommand(tkp
->interp
, "tk_op", tk_op
, tkp
, NULL
);
291 Tcl_CreateCommand(tkp
->interp
, "tk_opt_init", tk_opt_init
, tkp
, NULL
);
292 Tcl_CreateCommand(tkp
->interp
, "tk_opt_set", tk_opt_set
, tkp
, NULL
);
293 Tcl_CreateCommand(tkp
->interp
, "tk_version", tk_version
, tkp
, NULL
);
295 /* Other initialization. */
296 if (Tcl_Eval(tkp
->interp
, "wm geometry . =80x28+0+0") == TCL_ERROR
)
303 * Tcl/Tk error message during initialization.
309 (void)fprintf(stderr
, "%s\n", tkp
->interp
->result
!= NULL
?
310 tkp
->interp
->result
: "Tcl/Tk: initialization error");
316 TK_PRIVATE *tkp = GTKP(__global_list);
323 F_SET(tkp
, TK_SIGHUP
);
324 tkp
->killersig
= SIGHUP
;
333 F_SET(tkp
, TK_SIGINT
);
342 F_SET(tkp
, TK_SIGTERM
);
343 tkp
->killersig
= SIGTERM
;
352 F_SET(tkp
, TK_SIGWINCH
);
358 * Initialize signals.
365 struct sigaction act
;
369 (void)sigemptyset(&__sigblockset
);
372 * Use sigaction(2), not signal(3), since we don't always want to
373 * restart system calls. The example is when waiting for a command
374 * mode keystroke and SIGWINCH arrives. Besides, you can't portably
375 * restart system calls (thanks, POSIX!).
377 #define SETSIG(signal, handler, off) { \
378 if (sigaddset(&__sigblockset, signal)) \
380 act.sa_handler = handler; \
381 sigemptyset(&act.sa_mask); \
383 if (sigaction(signal, &act, &tkp->oact[off])) \
389 err
: perr(gp
->progname
, NULL
);
404 (void)sigaction(SIGHUP
, NULL
, &tkp
->oact
[INDX_HUP
]);
405 (void)sigaction(SIGINT
, NULL
, &tkp
->oact
[INDX_INT
]);
406 (void)sigaction(SIGTERM
, NULL
, &tkp
->oact
[INDX_TERM
]);
407 (void)sigaction(SIGWINCH
, NULL
, &tkp
->oact
[INDX_WINCH
]);
412 * Print system error.
418 (void)fprintf(stderr
, "%s:", name
);
420 (void)fprintf(stderr
, "%s:", msg
);
421 (void)fprintf(stderr
, "%s\n", strerror(errno
));