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