119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: cl_main.c,v 10.55 2011/08/15 19:52:28 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter
1919304Speter#include <bitstring.h>
2019304Speter#include <errno.h>
2119304Speter#include <fcntl.h>
2219304Speter#include <signal.h>
2319304Speter#include <stdio.h>
2419304Speter#include <stdlib.h>
2519304Speter#include <string.h>
26254225Speter#ifdef HAVE_TERM_H
27254225Speter#include <term.h>
28254225Speter#endif
2919304Speter#include <termios.h>
3019304Speter#include <unistd.h>
3119304Speter
3219304Speter#include "../common/common.h"
3319304Speter#include "cl.h"
3419304Speter#include "pathnames.h"
3519304Speter
3619304SpeterGS *__global_list;				/* GLOBAL: List of screens. */
3719304Spetersigset_t __sigblockset;				/* GLOBAL: Blocked signals. */
3819304Speter
3919304Speterstatic void	   cl_func_std __P((GS *));
4019304Speterstatic CL_PRIVATE *cl_init __P((GS *));
4119304Speterstatic GS	  *gs_init __P((char *));
4219304Speterstatic void	   perr __P((char *, char *));
4319304Speterstatic int	   setsig __P((int, struct sigaction *, void (*)(int)));
4419304Speterstatic void	   sig_end __P((GS *));
4519304Speterstatic void	   term_init __P((char *, char *));
4619304Speter
4719304Speter/*
4819304Speter * main --
4919304Speter *	This is the main loop for the standalone curses editor.
5019304Speter */
5119304Speterint
52254225Spetermain(int argc, char **argv)
5319304Speter{
5419304Speter	static int reenter;
5519304Speter	CL_PRIVATE *clp;
5619304Speter	GS *gp;
5719304Speter	size_t rows, cols;
5819304Speter	int rval;
59254225Speter	char **p_av, **t_av, *ttype;
6019304Speter
6119304Speter	/* If loaded at 0 and jumping through a NULL pointer, stop. */
6219304Speter	if (reenter++)
6319304Speter		abort();
6419304Speter
6519304Speter	/* Create and initialize the global structure. */
6619304Speter	__global_list = gp = gs_init(argv[0]);
6719304Speter
6819304Speter	/*
6919304Speter	 * Strip out any arguments that vi isn't going to understand.  There's
7019304Speter	 * no way to portably call getopt twice, so arguments parsed here must
7119304Speter	 * be removed from the argument list.
7219304Speter	 */
7319304Speter	for (p_av = t_av = argv;;) {
7419304Speter		if (*t_av == NULL) {
7519304Speter			*p_av = NULL;
7619304Speter			break;
7719304Speter		}
7819304Speter		if (!strcmp(*t_av, "--")) {
7919304Speter			while ((*p_av++ = *t_av++) != NULL);
8019304Speter			break;
8119304Speter		}
8219304Speter		*p_av++ = *t_av++;
8319304Speter	}
8419304Speter
8519304Speter	/* Create and initialize the CL_PRIVATE structure. */
8619304Speter	clp = cl_init(gp);
8719304Speter
8819304Speter	/*
8919304Speter	 * Initialize the terminal information.
9019304Speter	 *
9119304Speter	 * We have to know what terminal it is from the start, since we may
9219304Speter	 * have to use termcap/terminfo to find out how big the screen is.
9319304Speter	 */
9419304Speter	if ((ttype = getenv("TERM")) == NULL)
9519304Speter		ttype = "unknown";
9619304Speter	term_init(gp->progname, ttype);
9719304Speter
9819304Speter	/* Add the terminal type to the global structure. */
9919304Speter	if ((OG_D_STR(gp, GO_TERM) =
10019304Speter	    OG_STR(gp, GO_TERM) = strdup(ttype)) == NULL)
10119304Speter		perr(gp->progname, NULL);
10219304Speter
10319304Speter	/* Figure out how big the screen is. */
10419304Speter	if (cl_ssize(NULL, 0, &rows, &cols, NULL))
10519304Speter		exit (1);
10619304Speter
10719304Speter	/* Add the rows and columns to the global structure. */
10819304Speter	OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
10919304Speter	OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
11019304Speter
11119304Speter	/* Ex wants stdout to be buffered. */
11219304Speter	(void)setvbuf(stdout, NULL, _IOFBF, 0);
11319304Speter
11419304Speter	/* Start catching signals. */
11519304Speter	if (sig_init(gp, NULL))
11619304Speter		exit (1);
11719304Speter
11819304Speter	/* Run ex/vi. */
11919304Speter	rval = editor(gp, argc, argv);
12019304Speter
12119304Speter	/* Clean up signals. */
12219304Speter	sig_end(gp);
12319304Speter
12419304Speter	/* Clean up the terminal. */
12519304Speter	(void)cl_quit(gp);
12619304Speter
12719304Speter	/*
12819304Speter	 * XXX
12919304Speter	 * Reset the O_MESG option.
13019304Speter	 */
13119304Speter	if (clp->tgw != TGW_UNKNOWN)
13219304Speter		(void)cl_omesg(NULL, clp, clp->tgw == TGW_SET);
13319304Speter
13419304Speter	/*
13519304Speter	 * XXX
13619304Speter	 * Reset the X11 xterm icon/window name.
13719304Speter	 */
138254225Speter	if (F_ISSET(clp, CL_RENAME))
139254225Speter		cl_setname(gp, clp->oname);
14019304Speter
14119304Speter	/* If a killer signal arrived, pretend we just got it. */
14219304Speter	if (clp->killersig) {
14319304Speter		(void)signal(clp->killersig, SIG_DFL);
14419304Speter		(void)kill(getpid(), clp->killersig);
14519304Speter		/* NOTREACHED */
14619304Speter	}
14719304Speter
14819304Speter	/* Free the global and CL private areas. */
14919304Speter#if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
150254225Speter	if (clp->oname != NULL)
151254225Speter		free(clp->oname);
15219304Speter	free(clp);
153254225Speter	free(OG_STR(gp, GO_TERM));
15419304Speter	free(gp);
15519304Speter#endif
15619304Speter
15719304Speter	exit (rval);
15819304Speter}
15919304Speter
16019304Speter/*
16119304Speter * gs_init --
16219304Speter *	Create and partially initialize the GS structure.
16319304Speter */
16419304Speterstatic GS *
165254225Spetergs_init(char *name)
16619304Speter{
16719304Speter	GS *gp;
16819304Speter	char *p;
16919304Speter
17019304Speter	/* Figure out what our name is. */
17119304Speter	if ((p = strrchr(name, '/')) != NULL)
17219304Speter		name = p + 1;
17319304Speter
17419304Speter	/* Allocate the global structure. */
17519304Speter	CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
17619304Speter	if (gp == NULL)
17719304Speter		perr(name, NULL);
17819304Speter
17919304Speter	gp->progname = name;
18019304Speter	return (gp);
18119304Speter}
18219304Speter
18319304Speter/*
18419304Speter * cl_init --
18519304Speter *	Create and partially initialize the CL structure.
18619304Speter */
18719304Speterstatic CL_PRIVATE *
188254225Spetercl_init(GS *gp)
18919304Speter{
19019304Speter	CL_PRIVATE *clp;
19119304Speter	int fd;
19219304Speter
19319304Speter	/* Allocate the CL private structure. */
19419304Speter	CALLOC_NOMSG(NULL, clp, CL_PRIVATE *, 1, sizeof(CL_PRIVATE));
19519304Speter	if (clp == NULL)
19619304Speter		perr(gp->progname, NULL);
19719304Speter	gp->cl_private = clp;
19819304Speter
19919304Speter	/*
20019304Speter	 * Set the CL_STDIN_TTY flag.  It's purpose is to avoid setting
20119304Speter	 * and resetting the tty if the input isn't from there.  We also
20219304Speter	 * use the same test to determine if we're running a script or
20319304Speter	 * not.
20419304Speter	 */
20519304Speter	if (isatty(STDIN_FILENO))
20619304Speter		F_SET(clp, CL_STDIN_TTY);
20719304Speter	else
20819304Speter		F_SET(gp, G_SCRIPTED);
20919304Speter
21019304Speter	/*
21119304Speter	 * We expect that if we've lost our controlling terminal that the
21219304Speter	 * open() (but not the tcgetattr()) will fail.
21319304Speter	 */
21419304Speter	if (F_ISSET(clp, CL_STDIN_TTY)) {
21519304Speter		if (tcgetattr(STDIN_FILENO, &clp->orig) == -1)
21619304Speter			goto tcfail;
21719304Speter	} else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
21819304Speter		if (tcgetattr(fd, &clp->orig) == -1) {
21919304Spetertcfail:			perr(gp->progname, "tcgetattr");
22019304Speter			exit (1);
22119304Speter		}
22219304Speter		(void)close(fd);
22319304Speter	}
22419304Speter
22519304Speter	/* Initialize the list of curses functions. */
22619304Speter	cl_func_std(gp);
22719304Speter
22819304Speter	return (clp);
22919304Speter}
23019304Speter
23119304Speter/*
23219304Speter * term_init --
23319304Speter *	Initialize terminal information.
23419304Speter */
23519304Speterstatic void
236254225Speterterm_init(char *name, char *ttype)
23719304Speter{
23819304Speter	int err;
23919304Speter
24019304Speter	/* Set up the terminal database information. */
24119304Speter	setupterm(ttype, STDOUT_FILENO, &err);
24219304Speter	switch (err) {
24319304Speter	case -1:
24419304Speter		(void)fprintf(stderr,
24519304Speter		    "%s: No terminal database found\n", name);
24619304Speter		exit (1);
24719304Speter	case 0:
24819304Speter		(void)fprintf(stderr,
24919304Speter		    "%s: %s: unknown terminal type\n", name, ttype);
25019304Speter		exit (1);
25119304Speter	}
25219304Speter}
25319304Speter
25419304Speter#define	GLOBAL_CLP \
25519304Speter	CL_PRIVATE *clp = GCLP(__global_list);
25619304Speterstatic void
257254225Speterh_hup(int signo)
25819304Speter{
25919304Speter	GLOBAL_CLP;
26019304Speter
26119304Speter	F_SET(clp, CL_SIGHUP);
26219304Speter	clp->killersig = SIGHUP;
26319304Speter}
26419304Speter
26519304Speterstatic void
266254225Speterh_int(int signo)
26719304Speter{
26819304Speter	GLOBAL_CLP;
26919304Speter
27019304Speter	F_SET(clp, CL_SIGINT);
27119304Speter}
27219304Speter
27319304Speterstatic void
274254225Speterh_term(int signo)
27519304Speter{
27619304Speter	GLOBAL_CLP;
27719304Speter
27819304Speter	F_SET(clp, CL_SIGTERM);
27919304Speter	clp->killersig = SIGTERM;
28019304Speter}
28119304Speter
28219304Speterstatic void
283254225Speterh_winch(int signo)
28419304Speter{
28519304Speter	GLOBAL_CLP;
28619304Speter
28719304Speter	F_SET(clp, CL_SIGWINCH);
28819304Speter}
28919304Speter#undef	GLOBAL_CLP
29019304Speter
29119304Speter/*
29219304Speter * sig_init --
29319304Speter *	Initialize signals.
29419304Speter *
29519304Speter * PUBLIC: int sig_init __P((GS *, SCR *));
29619304Speter */
29719304Speterint
298254225Spetersig_init(GS *gp, SCR *sp)
29919304Speter{
30019304Speter	CL_PRIVATE *clp;
30119304Speter
30219304Speter	clp = GCLP(gp);
30319304Speter
30419304Speter	if (sp == NULL) {
30519304Speter		(void)sigemptyset(&__sigblockset);
30619304Speter		if (sigaddset(&__sigblockset, SIGHUP) ||
30719304Speter		    setsig(SIGHUP, &clp->oact[INDX_HUP], h_hup) ||
30819304Speter		    sigaddset(&__sigblockset, SIGINT) ||
30919304Speter		    setsig(SIGINT, &clp->oact[INDX_INT], h_int) ||
31019304Speter		    sigaddset(&__sigblockset, SIGTERM) ||
31119304Speter		    setsig(SIGTERM, &clp->oact[INDX_TERM], h_term)
31219304Speter#ifdef SIGWINCH
31319304Speter		    ||
31419304Speter		    sigaddset(&__sigblockset, SIGWINCH) ||
31519304Speter		    setsig(SIGWINCH, &clp->oact[INDX_WINCH], h_winch)
31619304Speter#endif
31719304Speter		    ) {
31819304Speter			perr(gp->progname, NULL);
31919304Speter			return (1);
32019304Speter		}
32119304Speter	} else
32219304Speter		if (setsig(SIGHUP, NULL, h_hup) ||
32319304Speter		    setsig(SIGINT, NULL, h_int) ||
32419304Speter		    setsig(SIGTERM, NULL, h_term)
32519304Speter#ifdef SIGWINCH
32619304Speter		    ||
32719304Speter		    setsig(SIGWINCH, NULL, h_winch)
32819304Speter#endif
32919304Speter		    ) {
33019304Speter			msgq(sp, M_SYSERR, "signal-reset");
33119304Speter		}
33219304Speter	return (0);
33319304Speter}
33419304Speter
33519304Speter/*
33619304Speter * setsig --
33719304Speter *	Set a signal handler.
33819304Speter */
33919304Speterstatic int
340254225Spetersetsig(int signo, struct sigaction *oactp, void (*handler) (int))
34119304Speter{
34219304Speter	struct sigaction act;
34319304Speter
34419304Speter	/*
34519304Speter	 * Use sigaction(2), not signal(3), since we don't always want to
34619304Speter	 * restart system calls.  The example is when waiting for a command
34719304Speter	 * mode keystroke and SIGWINCH arrives.  Besides, you can't portably
34819304Speter	 * restart system calls (thanks, POSIX!).  On the other hand, you
34919304Speter	 * can't portably NOT restart system calls (thanks, Sun!).  SunOS
35019304Speter	 * used SA_INTERRUPT as their extension to NOT restart read calls.
35119304Speter	 * We sure hope nobody else used it for anything else.  Mom told me
35219304Speter	 * there'd be days like this.  She just never told me that there'd
35319304Speter	 * be so many.
35419304Speter	 */
35519304Speter	act.sa_handler = handler;
35619304Speter	sigemptyset(&act.sa_mask);
35719304Speter
35819304Speter#ifdef SA_INTERRUPT
35919304Speter	act.sa_flags = SA_INTERRUPT;
36019304Speter#else
36119304Speter	act.sa_flags = 0;
36219304Speter#endif
36319304Speter	return (sigaction(signo, &act, oactp));
36419304Speter}
36519304Speter
36619304Speter/*
36719304Speter * sig_end --
36819304Speter *	End signal setup.
36919304Speter */
37019304Speterstatic void
371254225Spetersig_end(GS *gp)
37219304Speter{
37319304Speter	CL_PRIVATE *clp;
37419304Speter
37519304Speter	clp = GCLP(gp);
37619304Speter	(void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
37719304Speter	(void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
37819304Speter	(void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
37919304Speter#ifdef SIGWINCH
38019304Speter	(void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
38119304Speter#endif
38219304Speter}
38319304Speter
38419304Speter/*
38519304Speter * cl_func_std --
38619304Speter *	Initialize the standard curses functions.
38719304Speter */
38819304Speterstatic void
389254225Spetercl_func_std(GS *gp)
39019304Speter{
39119304Speter	gp->scr_addstr = cl_addstr;
392254225Speter	gp->scr_waddstr = cl_waddstr;
39319304Speter	gp->scr_attr = cl_attr;
39419304Speter	gp->scr_baud = cl_baud;
39519304Speter	gp->scr_bell = cl_bell;
39619304Speter	gp->scr_busy = NULL;
397254225Speter	gp->scr_child = NULL;
39819304Speter	gp->scr_clrtoeol = cl_clrtoeol;
39919304Speter	gp->scr_cursor = cl_cursor;
40019304Speter	gp->scr_deleteln = cl_deleteln;
401254225Speter	gp->scr_reply = NULL;
402254225Speter	gp->scr_discard = cl_discard;
40319304Speter	gp->scr_event = cl_event;
40419304Speter	gp->scr_ex_adjust = cl_ex_adjust;
40519304Speter	gp->scr_fmap = cl_fmap;
40619304Speter	gp->scr_insertln = cl_insertln;
40719304Speter	gp->scr_keyval = cl_keyval;
40819304Speter	gp->scr_move = cl_move;
40919304Speter	gp->scr_msg = NULL;
41019304Speter	gp->scr_optchange = cl_optchange;
41119304Speter	gp->scr_refresh = cl_refresh;
41219304Speter	gp->scr_rename = cl_rename;
41319304Speter	gp->scr_screen = cl_screen;
414254225Speter	gp->scr_split = cl_split;
41519304Speter	gp->scr_suspend = cl_suspend;
41619304Speter	gp->scr_usage = cl_usage;
41719304Speter}
41819304Speter
41919304Speter/*
42019304Speter * perr --
42119304Speter *	Print system error.
42219304Speter */
42319304Speterstatic void
424254225Speterperr(char *name, char *msg)
42519304Speter{
42619304Speter	(void)fprintf(stderr, "%s:", name);
42719304Speter	if (msg != NULL)
42819304Speter		(void)fprintf(stderr, "%s:", msg);
42919304Speter	(void)fprintf(stderr, "%s\n", strerror(errno));
43019304Speter	exit(1);
43119304Speter}
432