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.56 2015/04/05 06:20:53 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(GS *);
40 static CL_PRIVATE *cl_init(GS *);
41 static GS *gs_init(char *);
42 static void perr(char *, char *);
43 static int setsig(int, struct sigaction *, void (*)(int));
44 static void sig_end(GS *);
45 static void term_init(char *, char *);
46
47 /*
48 * main --
49 * This is the main loop for the standalone curses editor.
50 */
51 int
main(int argc,char * argv[])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 = "ansi";
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)
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 *
gs_init(char * name)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 *
cl_init(GS * gp)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
term_init(char * name,char * ttype)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
h_hup(int signo)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
h_int(int signo)266 h_int(int signo)
267 {
268 GLOBAL_CLP;
269
270 F_SET(clp, CL_SIGINT);
271 }
272
273 static void
h_term(int signo)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
h_winch(int signo)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(GS *, SCR *);
296 */
297 int
sig_init(GS * gp,SCR * sp)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
setsig(int signo,struct sigaction * oactp,void (* handler)(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!).
349 */
350 act.sa_handler = handler;
351 sigemptyset(&act.sa_mask);
352
353 act.sa_flags = 0;
354 return (sigaction(signo, &act, oactp));
355 }
356
357 /*
358 * sig_end --
359 * End signal setup.
360 */
361 static void
sig_end(GS * gp)362 sig_end(GS *gp)
363 {
364 CL_PRIVATE *clp;
365
366 clp = GCLP(gp);
367 (void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
368 (void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
369 (void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
370 #ifdef SIGWINCH
371 (void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
372 #endif
373 }
374
375 /*
376 * cl_func_std --
377 * Initialize the standard curses functions.
378 */
379 static void
cl_func_std(GS * gp)380 cl_func_std(GS *gp)
381 {
382 gp->scr_addstr = cl_addstr;
383 gp->scr_waddstr = cl_waddstr;
384 gp->scr_attr = cl_attr;
385 gp->scr_baud = cl_baud;
386 gp->scr_bell = cl_bell;
387 gp->scr_busy = NULL;
388 gp->scr_child = NULL;
389 gp->scr_clrtoeol = cl_clrtoeol;
390 gp->scr_cursor = cl_cursor;
391 gp->scr_deleteln = cl_deleteln;
392 gp->scr_reply = NULL;
393 gp->scr_discard = cl_discard;
394 gp->scr_event = cl_event;
395 gp->scr_ex_adjust = cl_ex_adjust;
396 gp->scr_fmap = cl_fmap;
397 gp->scr_insertln = cl_insertln;
398 gp->scr_keyval = cl_keyval;
399 gp->scr_move = cl_move;
400 gp->scr_msg = NULL;
401 gp->scr_optchange = cl_optchange;
402 gp->scr_refresh = cl_refresh;
403 gp->scr_rename = cl_rename;
404 gp->scr_screen = cl_screen;
405 gp->scr_split = cl_split;
406 gp->scr_suspend = cl_suspend;
407 gp->scr_usage = cl_usage;
408 }
409
410 /*
411 * perr --
412 * Print system error.
413 */
414 static void
perr(char * name,char * msg)415 perr(char *name, char *msg)
416 {
417 (void)fprintf(stderr, "%s:", name);
418 if (msg != NULL)
419 (void)fprintf(stderr, "%s:", msg);
420 (void)fprintf(stderr, "%s\n", strerror(errno));
421 exit(1);
422 }
423