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 13static const char sccsid[] = "$Id: cl_main.c,v 10.56 2015/04/05 06:20:53 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 36GS *__global_list; /* GLOBAL: List of screens. */ 37sigset_t __sigblockset; /* GLOBAL: Blocked signals. */ 38 39static void cl_func_std(GS *); 40static CL_PRIVATE *cl_init(GS *); 41static GS *gs_init(char *); 42static void perr(char *, char *); 43static int setsig(int, struct sigaction *, void (*)(int)); 44static void sig_end(GS *); 45static void term_init(char *, char *); 46 47/* 48 * main -- 49 * This is the main loop for the standalone curses editor. 50 */ 51int 52main(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 = "ansi"; 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) 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 */ 164static GS * 165gs_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 */ 187static CL_PRIVATE * 188cl_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) { 219tcfail: 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 */ 235static void 236term_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); 256static void 257h_hup(int signo) 258{ 259 GLOBAL_CLP; 260 261 F_SET(clp, CL_SIGHUP); 262 clp->killersig = SIGHUP; 263} 264 265static void 266h_int(int signo) 267{ 268 GLOBAL_CLP; 269 270 F_SET(clp, CL_SIGINT); 271} 272 273static void 274h_term(int signo) 275{ 276 GLOBAL_CLP; 277 278 F_SET(clp, CL_SIGTERM); 279 clp->killersig = SIGTERM; 280} 281 282static void 283h_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(GS *, SCR *); 296 */ 297int 298sig_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 */ 339static int 340setsig(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!). 349 */ 350 act.sa_handler = handler; 351 sigemptyset(&act.sa_mask); 352 353 act.sa_flags = 0; 354 return (sigaction(signo, &act, oactp)); 355} 356 357/* 358 * sig_end -- 359 * End signal setup. 360 */ 361static void 362sig_end(GS *gp) 363{ 364 CL_PRIVATE *clp; 365 366 clp = GCLP(gp); 367 (void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]); 368 (void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]); 369 (void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]); 370#ifdef SIGWINCH 371 (void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]); 372#endif 373} 374 375/* 376 * cl_func_std -- 377 * Initialize the standard curses functions. 378 */ 379static void 380cl_func_std(GS *gp) 381{ 382 gp->scr_addstr = cl_addstr; 383 gp->scr_waddstr = cl_waddstr; 384 gp->scr_attr = cl_attr; 385 gp->scr_baud = cl_baud; 386 gp->scr_bell = cl_bell; 387 gp->scr_busy = NULL; 388 gp->scr_child = NULL; 389 gp->scr_clrtoeol = cl_clrtoeol; 390 gp->scr_cursor = cl_cursor; 391 gp->scr_deleteln = cl_deleteln; 392 gp->scr_reply = NULL; 393 gp->scr_discard = cl_discard; 394 gp->scr_event = cl_event; 395 gp->scr_ex_adjust = cl_ex_adjust; 396 gp->scr_fmap = cl_fmap; 397 gp->scr_insertln = cl_insertln; 398 gp->scr_keyval = cl_keyval; 399 gp->scr_move = cl_move; 400 gp->scr_msg = NULL; 401 gp->scr_optchange = cl_optchange; 402 gp->scr_refresh = cl_refresh; 403 gp->scr_rename = cl_rename; 404 gp->scr_screen = cl_screen; 405 gp->scr_split = cl_split; 406 gp->scr_suspend = cl_suspend; 407 gp->scr_usage = cl_usage; 408} 409 410/* 411 * perr -- 412 * Print system error. 413 */ 414static void 415perr(char *name, char *msg) 416{ 417 (void)fprintf(stderr, "%s:", name); 418 if (msg != NULL) 419 (void)fprintf(stderr, "%s:", msg); 420 (void)fprintf(stderr, "%s\n", strerror(errno)); 421 exit(1); 422} 423