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