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 33GS *__global_list; /* GLOBAL: List of screens. */ 34sigset_t __sigblockset; /* GLOBAL: Blocked signals. */ 35 36static void cl_func_std(GS *); 37static CL_PRIVATE *cl_init(GS *); 38static GS *gs_init(void); 39static int setsig(int, struct sigaction *, void (*)(int)); 40static void sig_end(GS *); 41static void term_init(char *); 42 43/* 44 * main -- 45 * This is the main loop for the standalone curses editor. 46 */ 47int 48main(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 */ 159static GS * 160gs_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 */ 175static CL_PRIVATE * 176cl_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) { 206tcfail: 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 */ 222static void 223term_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); 239static void 240h_hup(int signo) 241{ 242 GLOBAL_CLP; 243 244 F_SET(clp, CL_SIGHUP); 245 clp->killersig = SIGHUP; 246} 247 248static void 249h_int(int signo) 250{ 251 GLOBAL_CLP; 252 253 F_SET(clp, CL_SIGINT); 254} 255 256static void 257h_term(int signo) 258{ 259 GLOBAL_CLP; 260 261 F_SET(clp, CL_SIGTERM); 262 clp->killersig = SIGTERM; 263} 264 265static void 266h_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 */ 280int 281sig_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 */ 322static int 323setsig(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 */ 344static void 345sig_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 */ 362static void 363cl_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