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