/***************************************************************************
 * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
 * is provided to you without charge, and with no warranty.  You may give  *
 * away copies of JOVE, including sources, provided that this notice is    *
 * included in all the files.                                              *
 ***************************************************************************/

/* Contains the main loop initializations, and some system dependent
   type things, e.g. putting terminal in CBREAK mode, etc. */

#include "jove.h"
#include "io.h"
#include "termcap.h"

#ifdef MAC
#	include "mac.h"
#else
#	include <varargs.h>
#	include <sys/stat.h>
#endif

#include <signal.h>
#include <errno.h>

#ifdef UNIX
#	ifndef SYSV
#		include <sgtty.h>
#	else
#		include <termio.h>
#	endif /* SYSV */
#endif /* UNIX */

#ifdef MSDOS
#	include <process.h>
#endif /* MSDOS */

#ifndef MAC
#	include <fcntl.h>
#endif

#ifdef MAC
#	undef private
#	define private
#endif

#ifdef	LINT_ARGS
extern	long	time(long *);
private	void	break_off(void);
private	void	break_rst(void);
#else
extern	long	time();
private	void	break_off();
private	void	break_rst();
#endif

#ifdef MAC
#	undef private
#	define private static
#endif

#ifdef UNIX
#   ifdef TIOCSLTC
struct ltchars	ls1, ls2;
#   endif /* TIOCSLTC */

#   ifdef TIOCGETC
struct tchars	tc1, tc2;
#   endif

#   ifdef PASS8			/* use pass8 instead of raw for meta-key */
int	lmword1, lmword2;	/* local mode word */
#   endif

#   ifdef BRLUNIX
struct sg_brl	sg1, sg2;
#   else
#	ifdef SYSV
struct termio	sg1, sg2;
#	else /* SYSV */
struct sgttyb	sg1, sg2;
#	endif /* SYSV */
#   endif /* BRLUNIX */

#   ifdef BIFF
private struct stat	tt_stat;	/* for biff */
#	ifndef BSD4_2
private char	*tt_name = 0;		/* name of the control tty */
extern char	*ttyname();		/* for systems w/o fchmod ... */
#	endif
private int	dw_biff = NO;		/* whether or not to fotz at all */
#   endif /* BIFF */
#endif /* UNIX */

int	errormsg;
extern char	*tfname;
char	NullStr[] = "";

#ifdef MSDOS
#    define SIGHUP	99
#endif /* MSDOS */

void
finish(code)
{
	int	CoreDump = (code != 0 && code != SIGHUP),
		DelTmps = 1;		/* Usually we delete them. */

	if (code == SIGINT) {
		char	c;

#ifndef MENLO_JCL
		(void) signal(code, finish);
#endif
		f_mess("Abort (Type 'n' if you're not sure)? ");
#ifndef MSDOS
		(void) read(0, &c, 1);
#else /* MSDOS */
		c = getrawinchar();
#endif /* MSDOS */
		message(NullStr);
		if ((c & 0377) != 'y') {
			redisplay();
			return;
		}
	}
	ttyset(OFF);
	UnsetTerm(NullStr);
#ifndef MSDOS
	if (code != 0) {
		if (!Crashing) {
			Crashing = YES;
			lsave();
			SyncRec();
			printf("JOVE CRASH!! (code %d)\n", code);
			if (ModBufs(1)) {
				printf("Your buffers have been saved.\n");
				printf("Use \"jove -r\" to have a look at them.\n");
				DelTmps = 0;	/* Don't delete anymore. */
			} else
				printf("You didn't lose any work.\n");
		} else
			printf("\r\nYou may have lost your work!\n");
	}
#endif /* MSDOS */
	flusho();
	if (DelTmps) {
		tmpclose();
#ifndef MSDOS
		recclose();
#endif /* MSDOS */
	}
#ifdef UNIX
	if (CoreDump)
		abort();
#ifdef PROFILING
	monitor(0);
#endif
	_exit(0);
#else /* MSDOS or MAC*/
#ifdef MSDOS
	break_rst();	/* restore previous ctrl-c handling */
#endif
	exit(0);
#endif /* UNIX */
}

private char	smbuf[20],
		*bp = smbuf;
private int	nchars = 0;

private char	peekbuf[10],
		*peekp = peekbuf;

#if defined(SYSV) || defined(M_XENIX)
void
setblock(fd, on)	/* turn blocking on or off */
register int	fd, on;
{
    static int blockf, nonblockf;
    static int first = 1;
    int flags;

    if (first) {
	first = 0;
	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
	    finish(SIGHUP);
	blockf = flags & ~O_NDELAY;	/* make sure O_NDELAY is off */
	nonblockf = flags | O_NDELAY;	/* make sure O_NDELAY is on */
    }
    if (fcntl(fd, F_SETFL, on ? blockf : nonblockf) == -1)
	finish(SIGHUP);
}
#endif /* SYSV */

int
Peekc()
{
	int	c;

	if (peekp == peekbuf)
		c = -1;
	else
		c = *--peekp & 0377;
	return c;
}

void
Ungetc(c)
{
	if (peekp == &peekbuf[(sizeof peekbuf) - 1])
		return;		/* Sorry, can't oblige you ... */
	*peekp++ = c;
}

int	InputPending = 0;

char	*Inputp = 0;

#if (defined(IPROCS) && !defined(PIPEPROCS))	/* that is, if ptys */
getchar()
{
	extern long	global_fd;
	long		reads;
	extern int	NumProcs,
			errno;
	register int	tmp,
			nfds;
	int		c;

	if (nchars <= 0) {
		/* Get a character from the keyboard, first checking for
		   any input from a process.  Handle that first, and then
		   deal with the terminal input. */
		do {
			do {
				reads = global_fd;
				nfds = select(32, &reads, (long *) 0, (long *) 0, (struct timeval *) 0);
			} while (nfds < 0 && errno == EINTR);

			if (nfds == -1)
				complain("\rerror %d in select %ld", errno, global_fd);
			else {
				if (reads & 01) {
					nchars = read(0, smbuf, sizeof(smbuf));
					reads &= ~01;
					nfds -= 1;
				}
				while (nfds--) {
					tmp = ffs(reads) - 1;
					read_proc(tmp);
					reads &= ~(1L << tmp);
				}
			}
		} while (nchars <= 0);

		if (nchars <= 0)
			finish(SIGHUP);

		bp = smbuf;
		InputPending = (nchars > 1);
	}

	if (((c = *bp) & 0200) && MetaKey != 0) {
		*bp = (c & CHARMASK);
		return '\033';
	}
	nchars -= 1;
	return *bp++ & 0377;
}

#else

getchar()
{
	extern int	errno;
	register int	c;

	if (nchars <= 0) {
		bp = smbuf;
#ifdef MSDOS
		*bp = getrawinchar();
		nchars = 1;
#else
		do
			nchars = read(0, smbuf, sizeof smbuf);
#   ifdef SYSV
		while (nchars == 0 || (nchars < 0 && errno == EINTR));
		if (nchars < 0)
#   else
		while (nchars < 0 && errno == EINTR);
		if (nchars <= 0)
#   endif /* SYSV */
			finish(SIGHUP);
#endif MSDOS
		InputPending = nchars > 1;
	}
	if (((c = *bp) & 0200) && MetaKey != 0) {
		*bp = (c & CHARMASK);
		return '\033';
	}
	nchars -= 1;
	return (*bp++ & CHARMASK);
}

#endif /* IPROCS */

/* Returns non-zero if a character waiting */

int
charp()
{
#ifndef MSDOS
	int	some = 0;
#endif /* MSDOS */

	if (InJoverc != 0 || nchars > 0 || Inputp != 0)
		return 1;
#ifdef BRLUNIX
	{
		static struct sg_brl gttyBuf;

		gtty(0, (char *) &gttyBuf);
		if (gttyBuf.sg_xflags & INWAIT)
			some += 1;
	}
#endif
#ifdef FIONREAD
	{
		long c;

		if (ioctl(0, FIONREAD, (struct sgttyb *) &c) == -1)
			c = 0;
		some = (c > 0);
	}
#endif /* FIONREAD */
#if defined(SYSV) || defined(M_XENIX)
	setblock(0, 0);		/* turn blocking off */
	nchars = read(0, smbuf, sizeof smbuf);	/* Is anything there? */
	setblock(0, 1);		/* turn blocking on */
	if (nchars > 0)		/* something was there */
	    bp = smbuf;		/* make sure bp points to it */
	some = (nchars > 0);	/* just say we found something */
#endif /* SYSV */
#ifdef c70
	some = !empty(0);
#endif
#ifdef MSDOS
    return rawkey_ready();
#else
#ifdef MAC
	some = rawchkc();
#endif
	return some;
#endif /* MSDOS */
}

#ifdef TERMCAP
void
ResetTerm()
{
	putpad(TI, 1);
	putpad(VS, 1);
	putpad(KS, 1);
#ifndef MSDOS
	(void) chkmail(YES);	/* force it to check to we can be accurate */
#endif
#ifdef BIFF
	if (BiffChk != dw_biff)
		biff_init();
	/* just in case we changed our minds about whether to deal with
	   biff */
#endif
	do_sgtty();		/* this is so if you change baudrate or stuff
				   like that, JOVE will notice. */
	ttyset(ON);
}

void
UnsetTerm(mesg)
char	*mesg;
{
	ttyset(OFF);
	putpad(KE, 1);
	putpad(VE, 1);
	putpad(TE, 1);
#ifdef ID_CHAR
	INSmode(0);
#endif
	Placur(ILI, 0);
	printf("%s", mesg);
	putpad(CE, 1);
	flusho();
}
#endif /* TERMCAP */

#ifdef JOB_CONTROL
PauseJove()
{
	UnsetTerm(ModBufs(0) ? "[There are modified buffers]" : NullStr);
	(void) kill(0, SIGTSTP);
	ResetTerm();
	ClAndRedraw();
}
#endif


#ifndef MAC
void
Push()
{
	int
#ifndef MSDOS
		pid,
		(*old_quit)() = signal(SIGQUIT, SIG_IGN),
#endif /* MSDOS */
   		(*old_int)() = signal(SIGINT, SIG_IGN);

#ifndef MSDOS
#ifdef IPROCS
	sighold(SIGCHLD);
#endif
	switch (pid = fork()) {
	case -1:
		complain("[Fork failed]");

	case 0:
		UnsetTerm(NullStr);
#ifdef IPROCS
		sigrelse(SIGCHLD);
#endif
		(void) signal(SIGTERM, SIG_DFL);
#else /* MSDOS */
	UnsetTerm(NullStr);
#endif /* MSDOS */
		(void) signal(SIGINT, SIG_DFL);
#ifdef UNIX
		(void) signal(SIGQUIT, SIG_DFL);
		execl(Shell, basename(Shell), (char *)0);
		message("[Execl failed]");
		_exit(1);
	}
    	dowait(pid, (int *) 0);
#ifdef IPROCS
	sigrelse(SIGCHLD);
#endif
#endif /* UNIX */
#ifdef MSDOS
	break_rst();
	if (spawnl(0, Shell, basename(Shell), (char *)0) == -1)
		message("[Spawn failed]"); 
#endif /* MSDOS */
    	ResetTerm();
    	ClAndRedraw();
#ifndef MSDOS
	(void) signal(SIGQUIT, old_quit);
#else /* MSDOS */
	break_off();
#ifdef CHDIR
	getCWD();
#endif	
#endif /* MSDOS */
    	(void) signal(SIGINT, old_int);
}
#endif /* MAC */

int	OKXonXoff = 0,		/* ^S and ^Q initially DON'T work */
	IntChar = CTL(']');

void
ttsize()
{
#ifdef UNIX
#   ifdef TIOCGWINSZ
	struct winsize win;

	if (ioctl (0, TIOCGWINSZ, &win) == 0) {
		if (win.ws_col)
			CO = win.ws_col;
		if (win.ws_row)
			LI = win.ws_row;
	}
#   else /* TIOCGWINSZ */
#	ifdef BTL_BLIT
#include <sys/jioctl.h>
	struct jwinsize jwin;

	if (ioctl(0, JWINSIZE, &jwin) == 0) {
		if (jwin.bytesx)
			CO = jwin.bytesx;
		if (jwin.bytesy)
			LI = jwin.bytesy;
	}
#	endif /* BTL_BLIT */
#   endif /* TIOCGWINSZ */
#endif /* UNIX */
	ILI = LI - 1;
}

#ifdef BIFF
biff_init()
{
	dw_biff = ((BiffChk) &&
#   ifndef BSD4_2
		   ((tt_name != 0) || (tt_name = ttyname(0))) &&
		   (stat(tt_name, &tt_stat) != -1) &&
#   else
		   (fstat(0, &tt_stat) != -1) &&
#   endif
		   (tt_stat.st_mode & S_IEXEC));	/* he's using biff */

}

biff(on)
{
	if (dw_biff == NO)
		return;
#   ifndef BSD4_2
	(void) chmod(tt_name, on ? tt_stat.st_mode :
				   (tt_stat.st_mode & ~S_IEXEC));
#   else
	(void) fchmod(0, on ? tt_stat.st_mode :
			      (tt_stat.st_mode & ~S_IEXEC));
#   endif
}

#endif /* BIFF */

void
ttinit()
{
#ifdef BIFF
	biff_init();
#endif
#ifdef TIOCSLTC
	(void) ioctl(0, TIOCGLTC, (struct sgttyb *) &ls1);
	ls2 = ls1;
	ls2.t_suspc = (char) -1;
	ls2.t_dsuspc = (char) -1;
	ls2.t_flushc = (char) -1;
	ls2.t_lnextc = (char) -1;
#endif

#ifdef TIOCGETC
	/* Change interupt and quit. */
	(void) ioctl(0, TIOCGETC, (struct sgttyb *) &tc1);
	tc2 = tc1;
	tc2.t_intrc = IntChar;
	tc2.t_quitc = (char) -1;
	if (OKXonXoff) {
		tc2.t_stopc = (char) -1;
		tc2.t_startc = (char) -1;
	}
#endif /* TIOCGETC */
	do_sgtty();
}

private int	done_ttinit = 0;

void
do_sgtty()
{
#ifdef UNIX
# ifdef SYSV
	(void) ioctl(0, TCGETA, (char *) &sg1);
# else
	(void) gtty(0, &sg1);
# endif /* SYSV */
	sg2 = sg1;

# ifdef LPASS8
	(void) ioctl(0, TIOCLGET, &lmword1);
	lmword2 = lmword1;
	if (MetaKey == YES)
		lmword2 |= LPASS8;
# endif

# ifdef SYSV
	TABS = !((sg1.c_oflag & TAB3) == TAB3);
	ospeed = sg1.c_cflag & CBAUD;

	if (OKXonXoff)
		sg2.c_iflag &= ~(IXON | IXOFF);
	sg2.c_iflag &= ~(INLCR|ICRNL|IGNCR);
	sg2.c_lflag &= ~(ISIG|ICANON|ECHO);
	sg2.c_oflag &= ~(OCRNL|ONLCR);
	sg2.c_cc[VMIN] = sizeof smbuf;
	sg2.c_cc[VTIME] = 1;
# else
	TABS = !(sg1.sg_flags & XTABS);
	ospeed = sg1.sg_ospeed;
#  ifdef BRLUNIX
	sg2.sg_flags &= ~(ECHO | CRMOD);
	sg2.sg_flags |= CBREAK;

	/* VT100 Kludge: leave STALL on for flow control if DC3DC1 (Yuck.) */
	sg2.sg_xflags &= ~((sg2.sg_xflags&DC3DC1 ? 0 : STALL) | PAGE);
#  else
	sg2.sg_flags &= ~(ECHO | CRMOD);
#  endif /* BRLUNIX */

#  ifdef PURDUE_EE
#   ifdef pdp11
	sg2.sg_flags |= RAW;
#   else
	sg2.sg_flags |= (MetaKey ? RAW : CBREAK);
#   endif
#  else
#   ifdef LPASS8
	sg2.sg_flags |= CBREAK;
#   else
	sg2.sg_flags |= (MetaKey ? RAW : CBREAK);
#   endif
#  endif /* PURDUE_EE */
# endif /* SYSV */
#endif /* UNIX */

#ifdef MSDOS
# ifndef IBMPC
	setmode(1, 0x8000);
# endif /* IBMPC */
	TABS = 0;
#endif /* MSDOS */
}

void
tty_reset()
{
	if (!done_ttinit)
		return;
	ttyset(OFF);	/* go back to original modes */
	ttinit();
	ttyset(ON);
}

/* If n is OFF reset to original modes */

void
ttyset(n)
{
	if (!done_ttinit && n == 0)	/* Try to reset before we've set! */
		return;
#ifdef UNIX
# ifdef SYSV
	(void) ioctl(0, TCSETAW, n == 0 ? (struct sgttyb *) &sg1 : (struct sgttyb *) &sg2);
# else
#  ifdef BRLUNIX
	(void) stty(0, n == 0 ? (struct sgttyb *) &sg1 : (struct sgttyb *) &sg2);
#  else
	(void) ioctl(0, TIOCSETN, n == 0 ? (struct sgttyb *) &sg1 : (struct sgttyb *) &sg2);
#  endif /* BRLUNIX */
# endif /* SYSV */

# ifdef TIOCSETC
	(void) ioctl(0, TIOCSETC, n == 0 ? (struct sgttyb *) &tc1 : (struct sgttyb *) &tc2);
# endif /* TIOCSETC */
# ifdef TIOCSLTC
	(void) ioctl(0, TIOCSLTC, n == 0 ? (struct sgttyb *) &ls1 : (struct sgttyb *) &ls2);
# endif /* TIOCSLTC */
# ifdef LPASS8
	(void) ioctl(0, TIOCLSET, n == 0 ? &lmword1 : &lmword2);
# endif
#endif /* UNIX */

#ifdef MSDOS
# ifndef IBMPC
	setmode(1, n == 0 ? 0x4000 : 0x8000);
# endif
#endif /* MSDOS */
	done_ttinit = 1;
#ifdef BIFF
	biff(!n);
#endif
}

int	this_cmd,
	last_cmd;

int	LastKeyStruck,
	MetaKey = 0;

int
getch()
{
	register int	c,
			peekc;
#ifdef IPROCS
	extern int	NumProcs;
#endif
	extern int	ModCount,
			Interactive;

	if (Inputp) {
		if ((c = *Inputp++) != 0)
			return LastKeyStruck = c;
		Inputp = 0;
	}

	if (InJoverc)
		return EOF;	/* somethings wrong if Inputp runs out while
				   we're reading a .joverc file. */

#ifndef MSDOS
	if (ModCount >= SyncFreq) {
		ModCount = 0;
		SyncRec();
	}
#endif /* MSDOS */

	/* If we're not interactive and we're not executing a macro,
	   AND there are no ungetc'd characters, we read from the
	   terminal (i.e., getch()).  And characters only get put
	   in macros from inside this if. */
	if (((peekc = c = Peekc()) == -1) && (Interactive || ((c = mac_getc()) == -1))) {
		/* So messages that aren't error messages don't
		   hang around forever. */
		if (!UpdMesg && !Asking) {	/* Don't erase if we are asking */
			if (mesgbuf[0] && !errormsg)
				message(NullStr);
		}
		redisplay();
#if defined(IPROCS) && defined(PIPEPROCS)
		if (NumProcs > 0) {
			sigrelse(INPUT_SIG);
			sigrelse(SIGCHLD);
		}
#endif
#ifdef UNIX
		inIOread = 1;
#endif
		if ((c = getchar()) == EOF)
			finish(SIGHUP);
#ifdef UNIX
		inIOread = 0;
#endif

#if defined(IPROCS) && defined(PIPEPROCS)
		if (NumProcs > 0) {
			sighold(INPUT_SIG);
			sighold(SIGCHLD);
		}
#endif
		if (!Interactive && InMacDefine)
			mac_putc(c);
	}
	if (peekc == -1)	/* don't add_stroke peekc's */
		add_stroke(c);
	return LastKeyStruck = c;
}

#ifdef UNIX
dorecover()
{
	execl(Recover, "recover", "-d", TmpFilePath, (char *) 0);
	printf("%s: execl failed!\n", Recover);
	flusho();
	_exit(-1);
}
#endif /* UNIX */
		
void
ShowVersion()
{
	extern char	*version;

	s_mess("Jonathan's Own Version of Emacs (%s)", version);
}

void
UNIX_cmdline(argc, argv)
char	*argv[];
{
	int	lineno = 0,
		nwinds = 1;
	Buffer	*b;

	ShowVersion();
	while (argc > 1) {
		if (argv[1][0] != '-' && argv[1][0] != '+') {
			int	force = (nwinds > 0 || lineno != 0);

#ifdef MSDOS
			strlwr(argv[1]);
#endif
			minib_add(argv[1], force ? YES : NO);
			b = do_find(nwinds > 0 ? curwind : (Window *) 0,
				    argv[1], force);
			if (force) {
				SetABuf(curbuf);
				SetBuf(b);
				if (lineno >= 0)
					SetLine(next_line(curbuf->b_first, lineno));
				else
					SetLine(curbuf->b_last);
				if (nwinds > 1)
					NextWindow();
				if (nwinds)
					nwinds -= 1;
			}
			lineno = 0;
		} else	switch (argv[1][1]) {
			case 'd':
				argv += 1;
				argc -= 1;
				break;

			case 'j':	/* Ignore .joverc in HOME */
				break;
#ifndef MAC
			case 'p':
				argv += 1;
				argc -= 1;
				if (argv[1] != 0) {
					SetBuf(do_find(curwind, argv[1], 0));
					ErrParse();
					nwinds = 0;
				}
				break;
#endif
			case 't':
 				/* check if syntax is -tTag or -t Tag */
 				if (argv[1][2] != 0) {
 					find_tag(&(argv[1][2]), YES);
 				} else {
					argv += 1;
					argc -= 1;
					if (argv[1] != 0)
						find_tag(argv[1], YES);
 				}
  				break;
  
			case 'w':
				if (argv[1][2] == '\0')
					nwinds += 1;
				else {
					int	n;

					(void) chr_to_int(&argv[1][2], 10, NO, &n);
					nwinds += -1 + n;
				}
				(void) div_wind(curwind, nwinds - 1);
				break;

			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				(void) chr_to_int(&argv[1][1], 10, NO, &lineno);
				lineno -= 1;
				break;
			case  0: 
				lineno = -1;	/* goto end of file ... */
				break;		/* just like some people's */
		}				/* favourite editor */
		argv += 1;
		argc -= 1;
	}
}

/* VARARGS1 */

void
error(fmt, va_alist)
char	*fmt;
va_dcl
{
	va_list	ap;

	if (fmt) {
		va_start(ap);
		format(mesgbuf, sizeof mesgbuf, fmt, ap);
		va_end(ap);
		UpdMesg = YES;
	}
	rbell();
	longjmp(mainjmp, ERROR);
}

/* VARARGS1 */

void
complain(fmt, va_alist)
char	*fmt;
va_dcl
{
	va_list	ap;

	if (fmt) {
		va_start(ap);
		format(mesgbuf, sizeof mesgbuf, fmt, ap);
		va_end(ap);
		UpdMesg = YES;
	}
	rbell();
	longjmp(mainjmp, COMPLAIN);
}

/* VARARGS1 */

void
confirm(fmt, va_alist)
char	*fmt;
va_dcl
{
	char	*yorn;
	va_list	ap;

	va_start(ap);
	format(mesgbuf, sizeof mesgbuf, fmt, ap);
	va_end(ap);
	yorn = ask((char *) 0, mesgbuf);
	if (*yorn != 'Y' && *yorn != 'y')
		longjmp(mainjmp, COMPLAIN);
}

int	RecDepth = 0;

void
Recur()
{
	char	bname[128];
	Mark	*m;

	sprintf(bname, "%s", curbuf->b_name);
	m = MakeMark(curline, curchar, M_FLOATER);

	RecDepth += 1;
	UpdModLine = YES;
	DoKeys(1);	/* 1 means not first time */
	UpdModLine = YES;
	RecDepth -= 1;
	SetBuf(do_select(curwind, bname));
	if (!is_an_arg())
		ToMark(m);
	DelMark(m);
}

jmp_buf	mainjmp;

#ifdef MAC
jmp_buf auxjmp;
#endif

int	iniargc;	/* main sets these for DoKeys() */
char	**iniargv;

void
DoKeys(nocmdline)
{
	int	c;
	jmp_buf	savejmp;

	push_env(savejmp);

	switch (setjmp(mainjmp)) {
	case 0:
		if (!nocmdline)
			UNIX_cmdline(iniargc, iniargv);
		break;

	case QUIT:
		if (RecDepth == 0) {
			if (ModMacs()) {
				rbell();
				if (CharUpcase(*ask("No",
"Some MACROS haven't been saved; leave anyway? ")) != 'Y')
					break;
			}
			if (ModBufs(0)) {
				rbell();
				if (CharUpcase(*ask("No",
"Some buffers haven't been saved; leave anyway? ")) != 'Y')
					break;
			}
#ifdef IPROCS
			KillProcs();
#endif
		}
		pop_env(savejmp);
		return;

	case ERROR:
		getDOT();	/* God knows what state linebuf was in */

	case COMPLAIN:
	    {
		extern int	DisabledRedisplay;

		gc_openfiles();	/* close any files we left open */
		errormsg = YES;
		unwind_macro_stack();
		Asking = 0;
		curwind->w_bufp = curbuf;
		DisabledRedisplay = NO;
		redisplay();
		break;
	    }
	}

	this_cmd = last_cmd = 0;

	for (;;) {
#ifdef MAC
		setjmp(auxjmp);
#endif
		if (this_cmd != ARG_CMD) {
			clr_arg_value();
			last_cmd = this_cmd;
			init_strokes();
		}
#ifdef MAC
		HiliteMenu(0);
		EventCmd = 0;
		menus_on();
#endif
		c = getch();
		if (c == -1)
			continue;
	 	dispatch(c);
	}
}

int	Crashing = 0;

char **
scanvec(args, str)
register char	**args,
		*str;
{
	while (*args) {
		if (strcmp(*args, str) == 0)
			return args;
		args += 1;
	}
	return 0;
}

#ifdef UNIX
int	UpdFreq = 30,
	inIOread = 0;

updmode()
{
	UpdModLine = YES;
	if (inIOread)
		redisplay();
#ifndef JOB_CONTROL
	(void) signal(SIGALRM, updmode);
#endif
	(void) alarm((unsigned) UpdFreq);
}
#endif /* UNIX */

#ifndef MSDOS
# if defined(TIOCGWINSZ) && defined(SIGWINCH)
extern win_reshape();
# endif
#else /* MSDOS */
# ifndef IBMPC
char	ttbuf[BUFSIZ];
# endif	/* IBMPC */
#endif /* MSDOS */

#if defined(TIOCGWINSZ) && defined(SIGWINCH)
win_reshape()
{
	register int oldsize;
	register int newsize, total;
	register Window *wp;

	(void) signal(SIGWINCH, SIG_IGN);

	/*
	 * Save old number of lines.
	 */
	oldsize = LI;

	/*
	 * Get new line/col info.
	 */
	ttsize();

	/*
	 * LI has changed, and now holds the
	 * new value.
	 */
	/* 
	 *  Go through the window list, changing each window size in
	 *  proportion to the resize. If a window becomes too small,
	 *  delete it. We keep track of all the excess lines (caused by
	 *  roundoff!), and give them to the current window, as a sop -
	 *  can't be more than one or two lines anyway. This seems fairer
	 *  than just resizing the current window.
	 */
	wp = fwind;
	total = 0;
	do {
		newsize = LI * wp->w_height / oldsize;

		if (newsize < 2) {
			total += wp->w_height;
			wp = wp->w_next;
			del_wind(wp->w_prev);
		} else {
			wp->w_height = newsize;
			total += newsize;
			wp = wp->w_next;
		}
	} while (wp != fwind);

	curwind->w_height += LI - total - 1;

	/* Make a new screen structure */
	make_scr();
	/* Do a 'hard' update on the screen - clear and redraw */
	cl_scr(1);
	flusho();
	redisplay();

	(void) signal(SIGWINCH, win_reshape);
}
#endif

void

#ifdef MAC	/* will get args from user, if option key held during launch */
main()
{
	int argc;
	char **argv;
#else
main(argc, argv)
char	*argv[];
{
#endif /* MAC */
	char	*cp;
#ifndef MSDOS
	char	ttbuf[MAXTTYBUF],
# ifndef VMUNIX
		s_iobuff[LBSIZE],
		s_genbuf[LBSIZE],
		s_linebuf[LBSIZE];
	/* The way I look at it, there ain't no way I is gonna run
	   out of stack space UNLESS I have some kind of infinite
	   recursive bug.  So why use up some valuable memory, when
	   there is plenty of space on the stack?  (This only matters
	   on wimpy pdp11's, of course.) */

	iobuff = s_iobuff;
	genbuf = s_genbuf;
	linebuf = s_linebuf;
# endif

#else /* MSDOS */
	char	*getenv();
#endif /* MSDOS */

	errormsg = 0;

#ifdef MAC
	MacInit();		/* initializes all */
	if(make_cache() == 0) exit(-1);
	argc = getArgs(&argv);
#endif /* MAC */

	iniargc = argc;
	iniargv = argv;

	if (setjmp(mainjmp)) {
		printf("\rAck! I can't deal with error \"%s\" now.\n\r", mesgbuf);
		finish(0);
	}

#ifdef MSDOS
	/* import the temporary file path from the environment and
	   fix the string, so that we can append a slash safely	*/

	if (((cp = getenv("TMP")) || (cp = getenv("TMPDIR"))) &&
	    (*cp != '\0')) {
		strcpy(TmpFilePath, cp);
		cp = &TmpFilePath[strlen(TmpFilePath)-1];
		if ((*cp == '/') || (*cp == '\\'))
			*cp = 0;
	}
	ShFlags[0] = switchar();
#endif /* MSDOS */

	getTERM();	/* Get terminal. */
	if (getenv("METAKEY"))
		MetaKey = 1;
	ttsize();
	InitCM();
#ifdef MAC
	InitEvents();
#endif

	d_cache_init();		/* initialize the disk buffer cache */
#ifndef MAC
	if ((cp = getenv("COMSPEC")) && (*cp != '\0')) {
		strcpy(Shell, cp);
	}
#endif
#ifdef MSDOS
	if ((cp = getenv("DESCRIBE")) && (*cp != '\0'))
	   strcpy(CmdDb, cp);
#endif /* MSDOS */

	make_scr();
	mac_init();	/* Initialize Macros */
	winit();	/* Initialize Window */
#ifdef IPROCS
	pinit();	/* Pipes/process initialization */
#endif
	SetBuf(do_select(curwind, Mainbuf));

#ifdef CHDIR
	{
		char	**argp;

		if ((argp = scanvec(argv, "-d")) && (argp[1][0] == '/'))
			setCWD(argp[1]);
		else
			getCWD();	/* After we setup curbuf in case we have to getwd() */
	}
#endif
	HomeDir = getenv("HOME");
	if (HomeDir == 0)
		HomeDir = "/";
	HomeLen = strlen(HomeDir);

#ifdef UNIX
# ifdef SYSV
	sprintf(Mailbox, "/usr/mail/%s", getenv("LOGNAME"));
# else
	sprintf(Mailbox, "/usr/spool/mail/%s", getenv("USER"));
# endif
#endif

#ifdef MSDOS
	if ((cp = getenv("JOVERC")) && (*cp != '\0'))
	   (void) joverc(cp);
#endif /* MSDOS */
	(void) joverc(Joverc);
	if (!scanvec(argv, "-j")) {
		char	tmpbuf[100];
		sprintf(tmpbuf, "%s/.joverc", HomeDir);
		(void) joverc(tmpbuf);
	}
#ifndef MSDOS
	if (scanvec(argv, "-r"))
		dorecover();
	if (scanvec(argv, "-rc"))
		FullRecover();
#endif 	/* MSDOS */
	ttinit();	/* initialize terminal (after ~/.joverc) */
#ifndef IBMPC
	settout(ttbuf);	/* not until we know baudrate */
#endif

#ifdef MSDOS
	(void) signal(SIGINT, SIG_IGN);
	break_off();	/* disable ctrl-c checking */
#endif /* MSDOS */
#ifdef UNIX
	(void) signal(SIGHUP, finish);
	(void) signal(SIGINT, finish);
	(void) signal(SIGBUS, finish);
	(void) signal(SIGSEGV, finish);
	(void) signal(SIGPIPE, finish);
	(void) signal(SIGTERM, SIG_IGN);
# if defined(TIOCGWINSZ) && defined(SIGWINCH)
	(void) signal(SIGWINCH, win_reshape);
# endif 
	/* set things up to update the modeline every UpdFreq seconds */
	(void) signal(SIGALRM, updmode);
	(void) alarm((unsigned) (60 - (time((time_t *) 0) % 60)));
#endif /* UNIX */

	ResetTerm();
	cl_scr(1);
	flusho();
	RedrawDisplay();	/* start the redisplay process. */
	DoKeys(0);
	finish(0);
}

#ifdef MSDOS

#include <dos.h>

static	char break_state;

/* set the break state to off */
private void
break_off()
{
	union REGS regs;

	regs.h.ah = 0x33;		/* break status */
	regs.h.al = 0x00;		/* request current state */
	intdos(&regs, &regs);
	break_state = regs.h.dl;
	bdos(0x33, 0, 1);	/* turn off break */
}

/* reset the break state */
private void
break_rst()
{
	bdos(0x33, break_state, 1);
}
#endif
