Make setthetime() static per the prototype.
[dragonfly.git] / contrib / nvi / tk / tk_main.c
1 /*-
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.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "@(#)tk_main.c     8.18 (Berkeley) 9/24/96";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18
19 #include <bitstring.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <termios.h>
27 #include <unistd.h>
28
29 #include "../common/common.h"
30 #include "tki.h"
31 #include "pathnames.h"
32
33 GS *__global_list;                              /* GLOBAL: List of screens. */
34 sigset_t __sigblockset;                         /* GLOBAL: Blocked signals. */
35
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 *));
43
44 /*
45  * main --
46  *      This is the main loop for the standalone Tcl/Tk editor.
47  */
48 int
49 main(argc, argv)
50         int argc;
51         char *argv[];
52 {
53         static int reenter;
54         GS *gp;
55         TK_PRIVATE *tkp;
56         size_t rows, cols;
57         int rval;
58         char **p_av, **t_av, *script;
59
60         /* If loaded at 0 and jumping through a NULL pointer, stop. */
61         if (reenter++)
62                 abort();
63
64         /* Create and initialize the global structure. */
65         __global_list = gp = gs_init(argv[0]);
66
67         /* Initialize Tk/Tcl. */
68         if (tcl_init(gp))
69                 exit (1);
70
71         /*
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.
75          *
76          * XXX
77          * There's no way to portably call getopt twice.
78          */
79         script = "init.tcl";
80         for (p_av = t_av = argv;;) {
81                 if (*t_av == NULL) {
82                         *p_av = NULL;
83                         break;
84                 }
85                 if (!strcmp(*t_av, "--")) {
86                         while ((*p_av++ = *t_av++) != NULL);
87                         break;
88                 }
89                 if (!memcmp(*t_av, "-i", sizeof("-i") - 1)) {
90                         if (t_av[0][2] != '\0') {
91                                 script = t_av[0] + 2;
92                                 ++t_av;
93                                 --argc;
94                                 continue;
95                         }
96                         if (t_av[1] != NULL) {
97                                 script = t_av[1];
98                                 t_av += 2;
99                                 argc -= 2;
100                                 continue;
101                         }
102                 }
103                 *p_av++ = *t_av++;
104         }
105         for (p_av = t_av = argv;;) {
106                 if (*t_av == NULL) {
107                         *p_av = NULL;
108                         break;
109                 }
110                 if (!strcmp(*t_av, "--")) {
111                         while ((*p_av++ = *t_av++) != NULL);
112                         break;
113                 }
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)
124                                 tcl_err(tkp);
125                         t_av += 2;
126                         argc -= 2;
127                         continue;
128                 }
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)
132                                 tcl_err(tkp);
133                         ++t_av;
134                         --argc;
135                         continue;
136                 }
137                 *p_av++ = *t_av++;
138         }
139
140         /* Load the initial Tcl/Tk script. */
141         tkp = GTKP(gp);
142         if (Tcl_EvalFile(tkp->interp, script) == TCL_ERROR)
143                 tcl_err(tkp);
144
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);
149
150         /* Figure out how big the screen is. */
151         if (tk_ssize(NULL, 0, &rows, &cols, NULL))
152                 exit (1);
153
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;
157
158         /* Start catching signals. */
159         if (sig_init(gp))
160                 exit (1);
161
162         /* Run ex/vi. */
163         rval = editor(gp, argc, argv);
164
165         /* Clean up signals. */
166         sig_end(gp);
167
168         /* Clean up the terminal. */
169         (void)tk_quit(gp);
170
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);
175                 /* NOTREACHED */
176         }
177
178         /* Free the global and TK private areas. */
179 #if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
180         free(tkp);
181         free(gp);
182 #endif
183
184         exit (rval);
185 }
186
187 /*
188  * gs_init --
189  *      Create and partially initialize the GS structure.
190  */
191 static GS *
192 gs_init(name)
193         char *name;
194 {
195         TK_PRIVATE *tkp;
196         GS *gp;
197         int fd;
198         char *p;
199
200         /* Figure out what our name is. */
201         if ((p = strrchr(name, '/')) != NULL)
202                 name = p + 1;
203
204         /* Allocate the global structure. */
205         CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
206
207         /* Allocate the CL private structure. */
208         if (gp != NULL)
209                 CALLOC_NOMSG(NULL, tkp, TK_PRIVATE *, 1, sizeof(TK_PRIVATE));
210         if (gp == NULL || tkp == NULL)
211                 perr(name, NULL);
212         gp->tk_private = tkp;
213         TAILQ_INIT(&tkp->evq);
214
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;
220         gp->scr_busy = NULL;
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;
230         gp->scr_msg = NULL;
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;
237
238         /*
239          * We expect that if we've lost our controlling terminal that the
240          * open() (but not the tcgetattr()) will fail.
241          */
242         if (isatty(STDIN_FILENO)) {
243                 if (tcgetattr(STDIN_FILENO, &tkp->orig) == -1)
244                         goto tcfail;
245         } else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
246                 if (tcgetattr(fd, &tkp->orig) == -1)
247 tcfail:                 perr(name, "tcgetattr");
248                 (void)close(fd);
249         }
250
251         gp->progname = name;
252         return (gp);
253 }
254
255 /*
256  *  tcl_init --
257  *      Get Tcl/Tk up and running.
258  */
259 static int
260 tcl_init(gp)
261         GS *gp;
262 {
263         TK_PRIVATE *tkp;
264
265         tkp = GTKP(gp);
266         if ((tkp->interp = Tcl_CreateInterp()) == NULL)
267                 tcl_err(tkp);
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)
271                 tcl_err(tkp);
272 #endif
273         if (Tcl_Init(tkp->interp) == TCL_ERROR)
274                 tcl_err(tkp);
275         if (Tk_Init(tkp->interp) == TCL_ERROR)
276                 tcl_err(tkp);
277
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);
287
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);
294
295         /* Other initialization. */
296         if (Tcl_Eval(tkp->interp, "wm geometry . =80x28+0+0") == TCL_ERROR)
297                 tcl_err(tkp);
298         return (0);
299 }
300
301 /*
302  * tcl_err --
303  *      Tcl/Tk error message during initialization.
304  */
305 static void
306 tcl_err(tkp)
307         TK_PRIVATE *tkp;
308 {
309         (void)fprintf(stderr, "%s\n", tkp->interp->result != NULL ?
310             tkp->interp->result : "Tcl/Tk: initialization error");
311         (void)tk_usage();
312         exit (1);
313 }
314
315 #define GLOBAL_TKP \
316         TK_PRIVATE *tkp = GTKP(__global_list);
317 static void
318 h_hup(signo)
319         int signo;
320 {
321         GLOBAL_TKP;
322
323         F_SET(tkp, TK_SIGHUP);
324         tkp->killersig = SIGHUP;
325 }
326
327 static void
328 h_int(signo)
329         int signo;
330 {
331         GLOBAL_TKP;
332
333         F_SET(tkp, TK_SIGINT);
334 }
335
336 static void
337 h_term(signo)
338         int signo;
339 {
340         GLOBAL_TKP;
341
342         F_SET(tkp, TK_SIGTERM);
343         tkp->killersig = SIGTERM;
344 }
345
346 static void
347 h_winch(signo)
348         int signo;
349 {
350         GLOBAL_TKP;
351
352         F_SET(tkp, TK_SIGWINCH);
353 }
354 #undef  GLOBAL_TKP
355
356 /*
357  * sig_init --
358  *      Initialize signals.
359  */
360 static int
361 sig_init(gp)
362         GS *gp;
363 {
364         TK_PRIVATE *tkp;
365         struct sigaction act;
366
367         tkp = GTKP(gp);
368
369         (void)sigemptyset(&__sigblockset);
370
371         /*
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!).
376          */
377 #define SETSIG(signal, handler, off) {                                  \
378         if (sigaddset(&__sigblockset, signal))                          \
379                 goto err;                                               \
380         act.sa_handler = handler;                                       \
381         sigemptyset(&act.sa_mask);                                      \
382         act.sa_flags = 0;                                               \
383         if (sigaction(signal, &act, &tkp->oact[off]))                   \
384                 goto err;                                               \
385 }
386 #undef SETSIG
387         return (0);
388
389 err:    perr(gp->progname, NULL);
390         return (1);
391 }
392
393 /*
394  * sig_end --
395  *      End signal setup.
396  */
397 static void
398 sig_end(gp)
399         GS *gp;
400 {
401         TK_PRIVATE *tkp;
402
403         tkp = GTKP(gp);
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]);
408 }
409
410 /*
411  * perr --
412  *      Print system error.
413  */
414 static void
415 perr(name, msg)
416         char *name, *msg;
417 {
418         (void)fprintf(stderr, "%s:", name);
419         if (msg != NULL)
420                 (void)fprintf(stderr, "%s:", msg);
421         (void)fprintf(stderr, "%s\n", strerror(errno));
422         exit(1);
423 }