Merge from vendor branch NTPD:
[dragonfly.git] / contrib / nvi / cl / cl_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[] = "@(#)cl_main.c     10.36 (Berkeley) 10/14/96";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18
19 #include <bitstring.h>
20 #include <curses.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <termios.h>
28 #include <unistd.h>
29
30 #include "../common/common.h"
31 #ifdef RUNNING_IP
32 #include "../ip/ip.h"
33 #endif
34 #include "cl.h"
35 #include "pathnames.h"
36
37 GS *__global_list;                              /* GLOBAL: List of screens. */
38 sigset_t __sigblockset;                         /* GLOBAL: Blocked signals. */
39
40 static void        cl_func_std __P((GS *));
41 static CL_PRIVATE *cl_init __P((GS *));
42 static GS         *gs_init __P((char *));
43 static void        perr __P((char *, char *));
44 static int         setsig __P((int, struct sigaction *, void (*)(int)));
45 static void        sig_end __P((GS *));
46 static void        term_init __P((char *, char *));
47
48 /*
49  * main --
50  *      This is the main loop for the standalone curses editor.
51  */
52 int
53 main(argc, argv)
54         int argc;
55         char *argv[];
56 {
57         static int reenter;
58         CL_PRIVATE *clp;
59         GS *gp;
60         size_t rows, cols;
61         int rval;
62         char *ip_arg, **p_av, **t_av, *ttype;
63
64         /* If loaded at 0 and jumping through a NULL pointer, stop. */
65         if (reenter++)
66                 abort();
67
68         /* Create and initialize the global structure. */
69         __global_list = gp = gs_init(argv[0]);
70
71         /*
72          * Strip out any arguments that vi isn't going to understand.  There's
73          * no way to portably call getopt twice, so arguments parsed here must
74          * be removed from the argument list.
75          */
76 #ifdef RUNNING_IP
77         ip_arg = NULL;
78         for (p_av = t_av = argv;;) {
79                 if (*t_av == NULL) {
80                         *p_av = NULL;
81                         break;
82                 }
83                 if (!strcmp(*t_av, "--")) {
84                         while ((*p_av++ = *t_av++) != NULL);
85                         break;
86                 }
87                 if (!memcmp(*t_av, "-I", sizeof("-I") - 1)) {
88                         if (t_av[0][2] != '\0') {
89                                 ip_arg = t_av[0] + 2;
90                                 ++t_av;
91                                 --argc;
92                                 continue;
93                         }
94                         if (t_av[1] != NULL) {
95                                 ip_arg = t_av[1];
96                                 t_av += 2;
97                                 argc -= 2;
98                                 continue;
99                         }
100                 }
101                 *p_av++ = *t_av++;
102         }
103
104         /*
105          * If we're being called as an editor library, we're done here, we
106          * get loaded with the curses screen, we don't share much code.
107          */
108         if (ip_arg != NULL)
109                 exit (ip_main(argc, argv, gp, ip_arg));
110 #else
111         ip_arg = argv[0];
112 #endif
113                 
114         /* Create and initialize the CL_PRIVATE structure. */
115         clp = cl_init(gp);
116
117         /*
118          * Initialize the terminal information.
119          *
120          * We have to know what terminal it is from the start, since we may
121          * have to use termcap/terminfo to find out how big the screen is.
122          */
123         if ((ttype = getenv("TERM")) == NULL)
124                 ttype = "unknown";
125         term_init(gp->progname, ttype);
126
127         /* Add the terminal type to the global structure. */
128         if ((OG_D_STR(gp, GO_TERM) =
129             OG_STR(gp, GO_TERM) = strdup(ttype)) == NULL)
130                 perr(gp->progname, NULL);
131
132         /* Figure out how big the screen is. */
133         if (cl_ssize(NULL, 0, &rows, &cols, NULL))
134                 exit (1);
135
136         /* Add the rows and columns to the global structure. */
137         OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
138         OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
139
140         /* Ex wants stdout to be buffered. */
141         (void)setvbuf(stdout, NULL, _IOFBF, 0);
142
143         /* Start catching signals. */
144         if (sig_init(gp, NULL))
145                 exit (1);
146
147         /* Run ex/vi. */
148         rval = editor(gp, argc, argv);
149
150         /* Clean up signals. */
151         sig_end(gp);
152
153         /* Clean up the terminal. */
154         (void)cl_quit(gp);
155
156         /*
157          * XXX
158          * Reset the O_MESG option.
159          */
160         if (clp->tgw != TGW_UNKNOWN)
161                 (void)cl_omesg(NULL, clp, clp->tgw == TGW_SET);
162
163         /*
164          * XXX
165          * Reset the X11 xterm icon/window name.
166          */
167         if (F_ISSET(clp, CL_RENAME)) {
168                 (void)printf(XTERM_RENAME, ttype);
169                 (void)fflush(stdout);
170         }
171
172         /* If a killer signal arrived, pretend we just got it. */
173         if (clp->killersig) {
174                 (void)signal(clp->killersig, SIG_DFL);
175                 (void)kill(getpid(), clp->killersig);
176                 /* NOTREACHED */
177         }
178
179         /* Free the global and CL private areas. */
180 #if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
181         free(clp);
182         free(gp);
183 #endif
184
185         exit (rval);
186 }
187
188 /*
189  * gs_init --
190  *      Create and partially initialize the GS structure.
191  */
192 static GS *
193 gs_init(name)
194         char *name;
195 {
196         CL_PRIVATE *clp;
197         GS *gp;
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         if (gp == NULL)
207                 perr(name, NULL);
208
209
210         gp->progname = name;
211         return (gp);
212 }
213
214 /*
215  * cl_init --
216  *      Create and partially initialize the CL structure.
217  */
218 static CL_PRIVATE *
219 cl_init(gp)
220         GS *gp;
221 {
222         CL_PRIVATE *clp;
223         int fd;
224
225         /* Allocate the CL private structure. */
226         CALLOC_NOMSG(NULL, clp, CL_PRIVATE *, 1, sizeof(CL_PRIVATE));
227         if (clp == NULL)
228                 perr(gp->progname, NULL);
229         gp->cl_private = clp;
230
231         /*
232          * Set the CL_STDIN_TTY flag.  It's purpose is to avoid setting
233          * and resetting the tty if the input isn't from there.  We also
234          * use the same test to determine if we're running a script or
235          * not.
236          */
237         if (isatty(STDIN_FILENO))
238                 F_SET(clp, CL_STDIN_TTY);
239         else
240                 F_SET(gp, G_SCRIPTED);
241
242         /*
243          * We expect that if we've lost our controlling terminal that the
244          * open() (but not the tcgetattr()) will fail.
245          */
246         if (F_ISSET(clp, CL_STDIN_TTY)) {
247                 if (tcgetattr(STDIN_FILENO, &clp->orig) == -1)
248                         goto tcfail;
249         } else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
250                 if (tcgetattr(fd, &clp->orig) == -1) {
251 tcfail:                 perr(gp->progname, "tcgetattr");
252                         exit (1);
253                 }
254                 (void)close(fd);
255         }
256
257         /* Initialize the list of curses functions. */
258         cl_func_std(gp);
259
260         return (clp);
261 }
262
263 /*
264  * term_init --
265  *      Initialize terminal information.
266  */
267 static void
268 term_init(name, ttype)
269         char *name, *ttype;
270 {
271         int err;
272
273         /* Set up the terminal database information. */
274         setupterm(ttype, STDOUT_FILENO, &err);
275         switch (err) {
276         case -1:
277                 (void)fprintf(stderr,
278                     "%s: No terminal database found\n", name);
279                 exit (1);
280         case 0:
281                 (void)fprintf(stderr,
282                     "%s: %s: unknown terminal type\n", name, ttype);
283                 exit (1);
284         }
285 }
286
287 #define GLOBAL_CLP \
288         CL_PRIVATE *clp = GCLP(__global_list);
289 static void
290 h_hup(signo)
291         int signo;
292 {
293         GLOBAL_CLP;
294
295         F_SET(clp, CL_SIGHUP);
296         clp->killersig = SIGHUP;
297 }
298
299 static void
300 h_int(signo)
301         int signo;
302 {
303         GLOBAL_CLP;
304
305         F_SET(clp, CL_SIGINT);
306 }
307
308 static void
309 h_term(signo)
310         int signo;
311 {
312         GLOBAL_CLP;
313
314         F_SET(clp, CL_SIGTERM);
315         clp->killersig = SIGTERM;
316 }
317
318 static void
319 h_winch(signo)
320         int signo;
321 {
322         GLOBAL_CLP;
323
324         F_SET(clp, CL_SIGWINCH);
325 }
326 #undef  GLOBAL_CLP
327
328 /*
329  * sig_init --
330  *      Initialize signals.
331  *
332  * PUBLIC: int sig_init __P((GS *, SCR *));
333  */
334 int
335 sig_init(gp, sp)
336         GS *gp;
337         SCR *sp;
338 {
339         CL_PRIVATE *clp;
340
341         clp = GCLP(gp);
342
343         if (sp == NULL) {
344                 (void)sigemptyset(&__sigblockset);
345                 if (sigaddset(&__sigblockset, SIGHUP) ||
346                     setsig(SIGHUP, &clp->oact[INDX_HUP], h_hup) ||
347                     sigaddset(&__sigblockset, SIGINT) ||
348                     setsig(SIGINT, &clp->oact[INDX_INT], h_int) ||
349                     sigaddset(&__sigblockset, SIGTERM) ||
350                     setsig(SIGTERM, &clp->oact[INDX_TERM], h_term)
351 #ifdef SIGWINCH
352                     ||
353                     sigaddset(&__sigblockset, SIGWINCH) ||
354                     setsig(SIGWINCH, &clp->oact[INDX_WINCH], h_winch)
355 #endif
356                     ) {
357                         perr(gp->progname, NULL);
358                         return (1);
359                 }
360         } else
361                 if (setsig(SIGHUP, NULL, h_hup) ||
362                     setsig(SIGINT, NULL, h_int) ||
363                     setsig(SIGTERM, NULL, h_term)
364 #ifdef SIGWINCH
365                     ||
366                     setsig(SIGWINCH, NULL, h_winch)
367 #endif
368                     ) {
369                         msgq(sp, M_SYSERR, "signal-reset");
370                 }
371         return (0);
372 }
373
374 /*
375  * setsig --
376  *      Set a signal handler.
377  */
378 static int
379 setsig(signo, oactp, handler)
380         int signo;
381         struct sigaction *oactp;
382         void (*handler) __P((int));
383 {
384         struct sigaction act;
385
386         /*
387          * Use sigaction(2), not signal(3), since we don't always want to
388          * restart system calls.  The example is when waiting for a command
389          * mode keystroke and SIGWINCH arrives.  Besides, you can't portably
390          * restart system calls (thanks, POSIX!).  On the other hand, you
391          * can't portably NOT restart system calls (thanks, Sun!).  SunOS
392          * used SA_INTERRUPT as their extension to NOT restart read calls.
393          * We sure hope nobody else used it for anything else.  Mom told me
394          * there'd be days like this.  She just never told me that there'd
395          * be so many.
396          */
397         act.sa_handler = handler;
398         sigemptyset(&act.sa_mask);
399
400 #ifdef SA_INTERRUPT
401         act.sa_flags = SA_INTERRUPT;
402 #else
403         act.sa_flags = 0;
404 #endif
405         return (sigaction(signo, &act, oactp));
406 }
407
408 /*
409  * sig_end --
410  *      End signal setup.
411  */
412 static void
413 sig_end(gp)
414         GS *gp;
415 {
416         CL_PRIVATE *clp;
417
418         clp = GCLP(gp);
419         (void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
420         (void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
421         (void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
422 #ifdef SIGWINCH
423         (void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
424 #endif
425 }
426
427 /*
428  * cl_func_std --
429  *      Initialize the standard curses functions.
430  */
431 static void
432 cl_func_std(gp)
433         GS *gp;
434 {
435         gp->scr_addstr = cl_addstr;
436         gp->scr_attr = cl_attr;
437         gp->scr_baud = cl_baud;
438         gp->scr_bell = cl_bell;
439         gp->scr_busy = NULL;
440         gp->scr_clrtoeol = cl_clrtoeol;
441         gp->scr_cursor = cl_cursor;
442         gp->scr_deleteln = cl_deleteln;
443         gp->scr_event = cl_event;
444         gp->scr_ex_adjust = cl_ex_adjust;
445         gp->scr_fmap = cl_fmap;
446         gp->scr_insertln = cl_insertln;
447         gp->scr_keyval = cl_keyval;
448         gp->scr_move = cl_move;
449         gp->scr_msg = NULL;
450         gp->scr_optchange = cl_optchange;
451         gp->scr_refresh = cl_refresh;
452         gp->scr_rename = cl_rename;
453         gp->scr_screen = cl_screen;
454         gp->scr_suspend = cl_suspend;
455         gp->scr_usage = cl_usage;
456 }
457
458 /*
459  * perr --
460  *      Print system error.
461  */
462 static void
463 perr(name, msg)
464         char *name, *msg;
465 {
466         (void)fprintf(stderr, "%s:", name);
467         if (msg != NULL)
468                 (void)fprintf(stderr, "%s:", msg);
469         (void)fprintf(stderr, "%s\n", strerror(errno));
470         exit(1);
471 }