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