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