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