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