| 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 |  |  | } |