xref: /openbsd/usr.bin/vi/cl/cl_main.c (revision 09467b48)
1 /*	$OpenBSD: cl_main.c,v 1.33 2016/05/05 20:36:41 martijn Exp $	*/
2 
3 /*-
4  * Copyright (c) 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1993, 1994, 1995, 1996
7  *	Keith Bostic.  All rights reserved.
8  *
9  * See the LICENSE file for redistribution information.
10  */
11 
12 #include "config.h"
13 
14 #include <sys/types.h>
15 #include <sys/queue.h>
16 
17 #include <bitstring.h>
18 #include <curses.h>
19 #include <err.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <paths.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <term.h>
28 #include <termios.h>
29 #include <unistd.h>
30 
31 #include "../common/common.h"
32 #include "cl.h"
33 
34 GS *__global_list;				/* GLOBAL: List of screens. */
35 
36 static void	   cl_func_std(GS *);
37 static CL_PRIVATE *cl_init(GS *);
38 static GS	  *gs_init(void);
39 static int	   setsig(int, struct sigaction *, void (*)(int));
40 static void	   sig_end(GS *);
41 static void	   term_init(char *);
42 
43 /*
44  * main --
45  *	This is the main loop for the standalone curses editor.
46  */
47 int
48 main(int argc, char *argv[])
49 {
50 	CL_PRIVATE *clp;
51 	GS *gp;
52 	size_t rows, cols;
53 	int rval;
54 	char *ttype;
55 
56 	/* Create and initialize the global structure. */
57 	__global_list = gp = gs_init();
58 
59 	/* Create and initialize the CL_PRIVATE structure. */
60 	clp = cl_init(gp);
61 
62 	/*
63 	 * Initialize the terminal information.
64 	 *
65 	 * We have to know what terminal it is from the start, since we may
66 	 * have to use termcap/terminfo to find out how big the screen is.
67 	 */
68 	if ((ttype = getenv("TERM")) == NULL)
69 		ttype = "unknown";
70 	term_init(ttype);
71 
72 	/* Add the terminal type to the global structure. */
73 	if ((OG_D_STR(gp, GO_TERM) =
74 	    OG_STR(gp, GO_TERM) = strdup(ttype)) == NULL)
75 		err(1, NULL);
76 
77 	/* Figure out how big the screen is. */
78 	if (cl_ssize(NULL, 0, &rows, &cols, NULL))
79 		exit (1);
80 
81 	/* Add the rows and columns to the global structure. */
82 	OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
83 	OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
84 
85 	/* Ex wants stdout to be buffered. */
86 	(void)setvbuf(stdout, NULL, _IOFBF, 0);
87 
88 	/* Start catching signals. */
89 	if (sig_init(gp, NULL))
90 		exit (1);
91 
92 	/* Run ex/vi. */
93 	rval = editor(gp, argc, argv);
94 
95 	/* Clean up signals. */
96 	sig_end(gp);
97 
98 	/* Clean up the terminal. */
99 	(void)cl_quit(gp);
100 
101 	/*
102 	 * XXX
103 	 * Reset the O_MESG option.
104 	 */
105 	if (clp->tgw != TGW_UNKNOWN)
106 		(void)cl_omesg(NULL, clp, clp->tgw == TGW_SET);
107 
108 	/*
109 	 * XXX
110 	 * Reset the X11 xterm icon/window name.
111 	 */
112 	if (F_ISSET(clp, CL_RENAME)) {
113 		(void)printf(XTERM_RENAME, ttype);
114 		(void)fflush(stdout);
115 	}
116 
117 	/* If a killer signal arrived, pretend we just got it. */
118 	if (clp->killersig) {
119 		(void)signal(clp->killersig, SIG_DFL);
120 		(void)kill(getpid(), clp->killersig);
121 		/* NOTREACHED */
122 	}
123 
124 	/* Free the global and CL private areas. */
125 #if defined(DEBUG) || defined(PURIFY)
126 	free(clp);
127 	free(gp);
128 #endif
129 
130 	exit (rval);
131 }
132 
133 /*
134  * gs_init --
135  *	Create and partially initialize the GS structure.
136  */
137 static GS *
138 gs_init(void)
139 {
140 	GS *gp;
141 
142 	/* Allocate the global structure. */
143 	if ((gp = calloc(1, sizeof(GS))) == NULL)
144 		err(1, NULL);
145 
146 	return (gp);
147 }
148 
149 /*
150  * cl_init --
151  *	Create and partially initialize the CL structure.
152  */
153 static CL_PRIVATE *
154 cl_init(GS *gp)
155 {
156 	CL_PRIVATE *clp;
157 	int fd;
158 
159 	/* Allocate the CL private structure. */
160 	if ((clp = calloc(1, sizeof(CL_PRIVATE))) == NULL)
161 		err(1, NULL);
162 	gp->cl_private = clp;
163 
164 	/*
165 	 * Set the CL_STDIN_TTY flag.  It's purpose is to avoid setting
166 	 * and resetting the tty if the input isn't from there.  We also
167 	 * use the same test to determine if we're running a script or
168 	 * not.
169 	 */
170 	if (isatty(STDIN_FILENO))
171 		F_SET(clp, CL_STDIN_TTY);
172 	else
173 		F_SET(gp, G_SCRIPTED);
174 
175 	/*
176 	 * We expect that if we've lost our controlling terminal that the
177 	 * open() (but not the tcgetattr()) will fail.
178 	 */
179 	if (F_ISSET(clp, CL_STDIN_TTY)) {
180 		if (tcgetattr(STDIN_FILENO, &clp->orig) == -1)
181 			goto tcfail;
182 	} else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
183 		if (tcgetattr(fd, &clp->orig) == -1)
184 tcfail:			err(1, "tcgetattr");
185 		(void)close(fd);
186 	}
187 
188 	/* Initialize the list of curses functions. */
189 	cl_func_std(gp);
190 
191 	return (clp);
192 }
193 
194 /*
195  * term_init --
196  *	Initialize terminal information.
197  */
198 static void
199 term_init(char *ttype)
200 {
201 	int err;
202 
203 	/* Set up the terminal database information. */
204 	setupterm(ttype, STDOUT_FILENO, &err);
205 	switch (err) {
206 	case -1:
207 		errx(1, "No terminal database found");
208 	case 0:
209 		errx(1, "%s: unknown terminal type", ttype);
210 	}
211 }
212 
213 #define	GLOBAL_CLP \
214 	CL_PRIVATE *clp = GCLP(__global_list);
215 static void
216 h_hup(int signo)
217 {
218 	GLOBAL_CLP;
219 
220 	F_SET(clp, CL_SIGHUP);
221 	clp->killersig = SIGHUP;
222 }
223 
224 static void
225 h_int(int signo)
226 {
227 	GLOBAL_CLP;
228 
229 	F_SET(clp, CL_SIGINT);
230 }
231 
232 static void
233 h_term(int signo)
234 {
235 	GLOBAL_CLP;
236 
237 	F_SET(clp, CL_SIGTERM);
238 	clp->killersig = SIGTERM;
239 }
240 
241 static void
242 h_winch(int signo)
243 {
244 	GLOBAL_CLP;
245 
246 	F_SET(clp, CL_SIGWINCH);
247 }
248 #undef	GLOBAL_CLP
249 
250 /*
251  * sig_init --
252  *	Initialize signals.
253  *
254  * PUBLIC: int sig_init(GS *, SCR *);
255  */
256 int
257 sig_init(GS *gp, SCR *sp)
258 {
259 	CL_PRIVATE *clp;
260 
261 	clp = GCLP(gp);
262 
263 	if (sp == NULL) {
264 		if (setsig(SIGHUP, &clp->oact[INDX_HUP], h_hup) ||
265 		    setsig(SIGINT, &clp->oact[INDX_INT], h_int) ||
266 		    setsig(SIGTERM, &clp->oact[INDX_TERM], h_term) ||
267 		    setsig(SIGWINCH, &clp->oact[INDX_WINCH], h_winch)
268 		    )
269 			err(1, NULL);
270 	} else
271 		if (setsig(SIGHUP, NULL, h_hup) ||
272 		    setsig(SIGINT, NULL, h_int) ||
273 		    setsig(SIGTERM, NULL, h_term) ||
274 		    setsig(SIGWINCH, NULL, h_winch)
275 		    ) {
276 			msgq(sp, M_SYSERR, "signal-reset");
277 		}
278 	return (0);
279 }
280 
281 /*
282  * setsig --
283  *	Set a signal handler.
284  */
285 static int
286 setsig(int signo, struct sigaction *oactp, void (*handler)(int))
287 {
288 	struct sigaction act;
289 
290 	/*
291 	 * Use sigaction(2), not signal(3), since we don't always want to
292 	 * restart system calls.  The example is when waiting for a command
293 	 * mode keystroke and SIGWINCH arrives.  Besides, you can't portably
294 	 * restart system calls (thanks, POSIX!).
295 	 */
296 	act.sa_handler = handler;
297 	sigemptyset(&act.sa_mask);
298 
299 	act.sa_flags = 0;
300 	return (sigaction(signo, &act, oactp));
301 }
302 
303 /*
304  * sig_end --
305  *	End signal setup.
306  */
307 static void
308 sig_end(GS *gp)
309 {
310 	CL_PRIVATE *clp;
311 
312 	clp = GCLP(gp);
313 	(void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
314 	(void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
315 	(void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
316 	(void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
317 }
318 
319 /*
320  * cl_func_std --
321  *	Initialize the standard curses functions.
322  */
323 static void
324 cl_func_std(GS *gp)
325 {
326 	gp->scr_addstr = cl_addstr;
327 	gp->scr_attr = cl_attr;
328 	gp->scr_baud = cl_baud;
329 	gp->scr_bell = cl_bell;
330 	gp->scr_busy = NULL;
331 	gp->scr_clrtoeol = cl_clrtoeol;
332 	gp->scr_cursor = cl_cursor;
333 	gp->scr_deleteln = cl_deleteln;
334 	gp->scr_event = cl_event;
335 	gp->scr_ex_adjust = cl_ex_adjust;
336 	gp->scr_fmap = cl_fmap;
337 	gp->scr_insertln = cl_insertln;
338 	gp->scr_keyval = cl_keyval;
339 	gp->scr_move = cl_move;
340 	gp->scr_msg = NULL;
341 	gp->scr_optchange = cl_optchange;
342 	gp->scr_refresh = cl_refresh;
343 	gp->scr_rename = cl_rename;
344 	gp->scr_screen = cl_screen;
345 	gp->scr_suspend = cl_suspend;
346 	gp->scr_usage = cl_usage;
347 }
348