514 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			514 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International
 | 
						||
 *	This software is quasi-public; it may be used freely with
 | 
						||
 *	like software, but may NOT be sold or made part of licensed
 | 
						||
 *	products without permission of the author.
 | 
						||
 */
 | 
						||
/*
 | 
						||
 * EEMAIN	ELLE Main Command Loop
 | 
						||
 */
 | 
						||
 | 
						||
#include "elle.h"
 | 
						||
 | 
						||
#include <stdio.h>
 | 
						||
#if !(V6)
 | 
						||
#include <signal.h>
 | 
						||
#else
 | 
						||
#include "eesigs.h"		/* Use this on V6 system */
 | 
						||
#endif /*V6*/
 | 
						||
 | 
						||
char *argfile[MAXARGFILES];	/* Filename args at startup */
 | 
						||
 | 
						||
extern int (*sbm_debug)();
 | 
						||
extern int (*sbv_debug)();
 | 
						||
int (*vfy_vec)();	/* If non-zero, routine to verify data
 | 
						||
			 * after each main-loop command */
 | 
						||
 | 
						||
main (argc, argv)
 | 
						||
int argc;
 | 
						||
char **argv;
 | 
						||
{
 | 
						||
	register int c;		/* Current command character */
 | 
						||
	register int i;
 | 
						||
	static int waitct;
 | 
						||
	extern int errsbm();
 | 
						||
#if SUN
 | 
						||
	extern int sun_rdevf;	/* from EESUN */
 | 
						||
#endif
 | 
						||
#ifdef STKMEM
 | 
						||
	char stackm[STKMEM];		/* Allocate some unused stack space */
 | 
						||
#endif /*STKMEM*/
 | 
						||
 | 
						||
	sbv_debug = errsbm;		/* Load with addrs of routine to */
 | 
						||
	sbm_debug = errsbm;		/* process SB and SBM errors. */
 | 
						||
 | 
						||
#ifdef STKMEM
 | 
						||
	sbm_init(&stackm[0],(SBMO)STKMEM);	/* Initialize mem alloc rtns */
 | 
						||
#endif /*STKMEM*/
 | 
						||
#if SUN
 | 
						||
	sun_main(&argc, argv);		/* On SUN, invoke window startup */
 | 
						||
#endif /*SUN*/
 | 
						||
 | 
						||
	setbuf(stdout, (char *)NULL);	/* Remove all stdio buffering */
 | 
						||
	setbuf(stderr, (char *)NULL);	/* in case of error reports. */
 | 
						||
 | 
						||
	waitct = 0;			/* debugging */
 | 
						||
	doargs(argc,argv);		/* Set up args */
 | 
						||
	initialize ();			/* Initialize the editor */
 | 
						||
 | 
						||
	if (argfile[0])			/* shell line arg */
 | 
						||
		find_file(argfile[0]);
 | 
						||
#if MAXARGFILES > 1
 | 
						||
	if(argfile[1])
 | 
						||
	  {	f_2winds();		/* Make 2 windows, go to 2nd */
 | 
						||
		i = 1;
 | 
						||
#if MAXARGFILES > 2
 | 
						||
		for (; i < MAXARGFILES; ++i)
 | 
						||
#endif /* > 2 files */
 | 
						||
			find_file(argfile[i]);	/* Get further file(s) */
 | 
						||
		f_othwind();		/* Move back to 1st window */
 | 
						||
	  }
 | 
						||
#endif /* > 1 file */
 | 
						||
 | 
						||
	redp(RD_SCREEN|RD_MODE);	/* Clear and show mode line */
 | 
						||
	setexit(0);			/* catch for ints, ^G throws */
 | 
						||
 | 
						||
/* -----------------------------------------------------------
 | 
						||
**			ELLE MAIN LOOP
 | 
						||
**
 | 
						||
*/
 | 
						||
	for (;;)
 | 
						||
	  {
 | 
						||
		/* First set up default arg unless last cmd specified it */
 | 
						||
		if(this_cmd != ARGCMD)
 | 
						||
		  {	exp = 1;		/* Default arg is 1 */
 | 
						||
			exp_p = 0;		/* Say no explicit arg */
 | 
						||
			last_cmd = this_cmd;
 | 
						||
		  }
 | 
						||
		this_cmd = 0;
 | 
						||
 | 
						||
		askclr();		/* If stuff asked, say to clear it */
 | 
						||
		if(cmd_wait())
 | 
						||
			waitct++;
 | 
						||
		else if(rd_type != 0)
 | 
						||
			redisplay();	/* Redisplay if needed and no input */
 | 
						||
#if SUN
 | 
						||
		sun_rdevf = 1;		/* Allow mouse events on this input */
 | 
						||
#endif
 | 
						||
		c = cmd_read();		/* Read an editor command */
 | 
						||
		sayclr();		/* Ask to clear echo area cleverly */
 | 
						||
 | 
						||
#if SUN
 | 
						||
		if(c != -1)		/* SUN may not have real input */
 | 
						||
#endif					/*    if mouse event happened. */
 | 
						||
			cmd_xct(c);	/* Execute the command char! */
 | 
						||
 | 
						||
		if(vfy_vec)		/* If debugging, */
 | 
						||
			(*vfy_vec)(1);	/* verify data structs right away */
 | 
						||
	  }
 | 
						||
}
 | 
						||
 | 
						||
char *prof_file;	/* Can specify user profile filename */
 | 
						||
 | 
						||
doargs(argc,argv)
 | 
						||
int argc;
 | 
						||
char **argv;
 | 
						||
{	register int cnt, c;
 | 
						||
	register char **av;
 | 
						||
	extern int tibfmsk;
 | 
						||
	int argfiles = 0;
 | 
						||
	int argsignored = 0;
 | 
						||
 | 
						||
	av = argv;
 | 
						||
	cnt = argc;
 | 
						||
 | 
						||
#if V6	/* V6 doesn't have environment thus no TERM var */
 | 
						||
	/* Hack to force terminal type; analyze pgm name to get
 | 
						||
	 * possible ".type" suffix.
 | 
						||
	 */
 | 
						||
	if(cnt && (c = strlen(*av)))
 | 
						||
	  while(--c >= 0)
 | 
						||
	  {	switch(av[0][c])
 | 
						||
		  {	case '.':
 | 
						||
				tv_stype = &av[0][c+1];
 | 
						||
			case '/':
 | 
						||
				break;
 | 
						||
			default: continue;
 | 
						||
		  }
 | 
						||
		break;
 | 
						||
	  }
 | 
						||
#endif /*V6*/
 | 
						||
 | 
						||
	while(--cnt > 0)
 | 
						||
	  {	++av;
 | 
						||
		if(*av[0] != '-')	/* If not switch, */
 | 
						||
		  {			/* assume it's an input filename */
 | 
						||
			if (argfiles < MAXARGFILES)
 | 
						||
				argfile[argfiles++] = *av;
 | 
						||
			else
 | 
						||
				++argsignored;
 | 
						||
			continue;
 | 
						||
		  }
 | 
						||
		c = upcase(av[0][1]);
 | 
						||
		switch(c)		/* Switches without args */
 | 
						||
		  {	case 'I':	/* Allow debug ints */
 | 
						||
				dbg_isw = 1;
 | 
						||
				continue;
 | 
						||
			case '8':		/* Ask for 8-bit input */
 | 
						||
				tibfmsk = 0377;
 | 
						||
				continue;
 | 
						||
			case '7':		/* Ask for 7-bit input */
 | 
						||
				tibfmsk = 0177;
 | 
						||
				continue;
 | 
						||
#if IMAGEN
 | 
						||
			case 'R':	/* Debug redisplay stuff */
 | 
						||
				dbg_redp = 1;
 | 
						||
				continue;
 | 
						||
#endif /*IMAGEN*/
 | 
						||
		  }
 | 
						||
		if(--cnt <= 0)
 | 
						||
			goto stop;
 | 
						||
		++av;
 | 
						||
		switch(c)		/* Switches with args */
 | 
						||
		  {	case 'T':	/* Terminal type */
 | 
						||
				tv_stype = *av;
 | 
						||
				break;	
 | 
						||
			case 'P':
 | 
						||
				prof_file = *av;
 | 
						||
			default:
 | 
						||
				goto stop;
 | 
						||
		  }
 | 
						||
		continue;
 | 
						||
	stop:	printf("ELLE: bad switch: %s\n",*av);
 | 
						||
		exit(1);
 | 
						||
	  }
 | 
						||
	if (argsignored > 0)
 | 
						||
	  {	printf("ELLE: more than %d file args, %d ignored.\n",
 | 
						||
			MAXARGFILES, argsignored);
 | 
						||
		sleep(2);	/* Complain but continue after pause */
 | 
						||
	  }
 | 
						||
}
 | 
						||
 | 
						||
int f_throw();		/* throw function */
 | 
						||
int bite_bag();		/* Error handling routine */
 | 
						||
int hup_exit();		/* Hangup handling routine */
 | 
						||
 | 
						||
struct majmode ifunmode = { "Fundamental" };
 | 
						||
 | 
						||
initialize ()				/* Initialization */
 | 
						||
{
 | 
						||
#if SUN
 | 
						||
	extern int sun_winfd;
 | 
						||
#endif
 | 
						||
	cur_mode = fun_mode = &ifunmode;	/* Set current major mode */
 | 
						||
	unrchf = pgoal = -1;
 | 
						||
	if(!homedir)
 | 
						||
	  {
 | 
						||
#if V6
 | 
						||
		extern char *logdir();
 | 
						||
		homedir = logdir();
 | 
						||
#else /* V7 */
 | 
						||
		homedir = getenv("HOME");
 | 
						||
#endif /*-V6*/
 | 
						||
	  }
 | 
						||
 | 
						||
	sbx_tset((chroff)0,0);		/* Create swapout file */
 | 
						||
					/* (Temporary hack, fix up later) */
 | 
						||
	hoard();			/* Hoard a FD for write purposes */
 | 
						||
 | 
						||
	redp_init();			/* Set up the display routines */
 | 
						||
	init_buf();			/* Set up initial buffers */
 | 
						||
	set_profile(prof_file);		/* Set up user profile */
 | 
						||
 | 
						||
#if SUN
 | 
						||
	if(sun_winfd) sun_init();
 | 
						||
#endif /*SUN*/
 | 
						||
 | 
						||
	/* Set up signal handlers */
 | 
						||
#if 0					/* not really used */
 | 
						||
	signal (SIGQUIT, f_throw);	/* Quit - on ^G */
 | 
						||
#endif
 | 
						||
#if !(MINIX)
 | 
						||
	signal (SIGSYS, bite_bag);	/* Bad arg to Sys call */
 | 
						||
#endif
 | 
						||
	signal (SIGSEGV, bite_bag);	/* Segmentation Violation */
 | 
						||
#if !(COHERENT)
 | 
						||
	signal (SIGILL, bite_bag);	/* Illegal Instruction interrupt */
 | 
						||
	signal (SIGBUS, bite_bag);	/* Bus Error interrupt */
 | 
						||
#endif /*-COHERENT*/
 | 
						||
#if !(TOPS20)				/* T20 just detaches job */
 | 
						||
	signal (SIGHUP, hup_exit);	/* Terminal Hangup interrupt */
 | 
						||
#endif /*-TOPS20*/
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
/* NOTE: This routine is not actually used, because ELLE does not
 | 
						||
 * allow interrupts to do anything.
 | 
						||
 */
 | 
						||
/* EFUN: "Error Throw" */
 | 
						||
f_throw ()			       /* abort whatever is going on */
 | 
						||
{
 | 
						||
	ring_bell ();
 | 
						||
	curs_lin = -1000;		/* make t_curpos do something */
 | 
						||
	redp(RD_MOVE);		/* crock: cursor seems to move, so fix it */
 | 
						||
	signal(SIGQUIT, f_throw);	/* rearm signal */
 | 
						||
/*	unwind_stack(main); */
 | 
						||
	reset(1);			/* throw to main loop */
 | 
						||
}
 | 
						||
 | 
						||
/* RING_BELL - General-purpose feeper when something goes wrong with
 | 
						||
 *	a function.
 | 
						||
 */
 | 
						||
ring_bell()
 | 
						||
{	t_bell();		/* Tell user something's wrong */
 | 
						||
 | 
						||
#if FX_SKMAC
 | 
						||
        f_ekmac();		/* Stop collecting keyboard macro if any */
 | 
						||
#endif /*FX_SKMAC*/
 | 
						||
}
 | 
						||
 | 
						||
/* EFUN: "Return to Superior"
 | 
						||
 *	Behavior here is somewhat system-dependent.  If it is possible to
 | 
						||
 * suspend the process and continue later, we do not ask about modified
 | 
						||
 * buffers.  Otherwise, we do.  Questioning can always be forced by using
 | 
						||
 * the prefix ^U.
 | 
						||
 *	Note that here we try to be very careful about not letting the user
 | 
						||
 * exit while buffers are still modified, since UNIX flushes the process
 | 
						||
 * if we exit.  Also, the code here conspires with sel_mbuf to rotate
 | 
						||
 * through all modified buffers, complaining about a different one each time,
 | 
						||
 * so that the user need not even know how to select a buffer!
 | 
						||
 */
 | 
						||
void f_retsup()
 | 
						||
{	register char *reply;
 | 
						||
	register int c;
 | 
						||
	register struct buffer *b, *b2;
 | 
						||
	extern struct buffer *sel_mbuf();
 | 
						||
	extern int tsf_pause;
 | 
						||
 | 
						||
	/* If we have capability of pausing and later continuing, do that,
 | 
						||
	 * except if CTRL-U forces us into question/save/quit behavior.
 | 
						||
	 */
 | 
						||
	if(tsf_pause && (exp_p != 4))
 | 
						||
	  {	clean_exit();		/* Return TTY to normal mode */
 | 
						||
		ts_pause();		/* Pause this inferior */
 | 
						||
		set_tty();		/* Continued, return to edit mode */
 | 
						||
		redp(RD_SCREEN);
 | 
						||
		return;
 | 
						||
	  }
 | 
						||
 | 
						||
	/* Sigh, do more typical "Are you sure" questioning prior to
 | 
						||
	 * killing the editor permanently.
 | 
						||
	 */
 | 
						||
	b = cur_buf;
 | 
						||
	if((b = sel_mbuf(b)) || (b = sel_mbuf((struct buffer *)0)) )
 | 
						||
	  {	if(b2 = sel_mbuf(b))
 | 
						||
			reply = ask(
 | 
						||
		"Quit: buffers %s, %s,... still have changes - forget them? ",
 | 
						||
				b->b_name, b2->b_name);
 | 
						||
		else
 | 
						||
			reply = ask(
 | 
						||
		"Quit: buffer %s still has changes - forget them? ",
 | 
						||
				b->b_name);
 | 
						||
		
 | 
						||
	  }
 | 
						||
	else
 | 
						||
	  {
 | 
						||
#if IMAGEN	/* Do not ask further if nothing modified */
 | 
						||
		barf("Bye");
 | 
						||
		clean_exit();
 | 
						||
		exit(0);
 | 
						||
#else
 | 
						||
		reply = ask("Quit? ");
 | 
						||
#endif /*-IMAGEN*/
 | 
						||
	  }
 | 
						||
 | 
						||
	if (reply == 0)
 | 
						||
		return;			/* Aborted, just return */
 | 
						||
 | 
						||
	c = upcase(*reply);		/* Get 1st char of reply */
 | 
						||
	chkfree(reply);
 | 
						||
 | 
						||
	switch(c)
 | 
						||
	  {	case 'Y':
 | 
						||
#if IMAGEN
 | 
						||
			barf("Bye");
 | 
						||
#endif /*IMAGEN*/
 | 
						||
			clean_exit();
 | 
						||
			exit(0);
 | 
						||
#if 0
 | 
						||
		case 'S':		/* Suspend command for debugging */
 | 
						||
			bkpt();
 | 
						||
			return;
 | 
						||
#endif /*COMMENT*/
 | 
						||
		default:		/* Complain */
 | 
						||
			ring_bell();
 | 
						||
		case 'N':
 | 
						||
			if(b)	/* B set if we have any modified buffers */
 | 
						||
			  {	sel_buf(b);
 | 
						||
				if(b->b_fn)
 | 
						||
					saynow("Use ^X ^S to save buffer");
 | 
						||
				else	saynow("Use ^X ^W to write out buffer");
 | 
						||
			  }
 | 
						||
	  }
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
#if FX_WFEXIT
 | 
						||
/* EFUN: "Write File Exit" (not EMACS) - from IMAGEN config */
 | 
						||
void f_wfexit()
 | 
						||
{
 | 
						||
	exp_p = 1;		/* Ensure f_savefiles asks no questions */
 | 
						||
	if (! f_savefiles())	/* Save all modified buffers, but */
 | 
						||
		return;		/*  stay here if any save fails */
 | 
						||
	saynow("Bye");
 | 
						||
	clean_exit();
 | 
						||
	exit(0);
 | 
						||
}
 | 
						||
#endif /*FX_WFEXIT*/
 | 
						||
 | 
						||
/* Subprocess-handling stuff; put here for time being. */
 | 
						||
 | 
						||
/* EFUN: "Push to Inferior" */
 | 
						||
#if TOPS20
 | 
						||
#include <frkxec.h>	/* Support for KCC forkexec() call */
 | 
						||
#endif
 | 
						||
f_pshinf()
 | 
						||
{
 | 
						||
	register int res;
 | 
						||
	register int (*sav2)(), (*sav3)();
 | 
						||
	int pid, status;
 | 
						||
	char *shellname;
 | 
						||
#if IMAGEN
 | 
						||
	char fullshell[64];
 | 
						||
#endif /*IMAGEN*/
 | 
						||
 | 
						||
	sav2 = signal(SIGINT, SIG_IGN);		/* Ignore TTY interrupts */
 | 
						||
	sav3 = signal(SIGQUIT, SIG_IGN);	/* Ditto TTY "quit"s */
 | 
						||
	clean_exit();				/* Restore normal TTY modes */
 | 
						||
 | 
						||
#if TOPS20
 | 
						||
    {
 | 
						||
	struct frkxec fx;
 | 
						||
	fx.fx_flags = FX_WAIT | FX_T20_PGMNAME;
 | 
						||
	fx.fx_name = "SYS:EXEC.EXE";
 | 
						||
	fx.fx_argv = fx.fx_envp = NULL;
 | 
						||
	if (forkexec(&fx) < 0)
 | 
						||
		writerr("Cannot run EXEC");
 | 
						||
    }
 | 
						||
#else /*-TOPS20*/
 | 
						||
	switch(pid = fork())
 | 
						||
	  {	case -1:
 | 
						||
			writerr("Cannot fork");
 | 
						||
			break;
 | 
						||
		case 0:		/* We're the child */
 | 
						||
			for(res = 3; res < 20;)	/* Don't let inf hack fd's */
 | 
						||
				close(res++);
 | 
						||
#if V6
 | 
						||
			execl("/bin/sh","-sh",0);
 | 
						||
#else
 | 
						||
			signal(SIGINT, SIG_DFL);	/* V7 shell wants this?? */
 | 
						||
			signal(SIGQUIT, SIG_DFL);	/*	*/
 | 
						||
#if IMAGEN
 | 
						||
			if((shellname = getenv("SHELL")) == 0)
 | 
						||
				shellname = "sh";
 | 
						||
			strcpy(fullshell, "/bin/");
 | 
						||
			strcat(fullshell, shellname);
 | 
						||
			shellname = fullshell;
 | 
						||
#else
 | 
						||
			if((shellname = getenv("SHELL")) == 0)
 | 
						||
				shellname = "/bin/sh";
 | 
						||
#endif /*-IMAGEN*/
 | 
						||
 | 
						||
			if((shellname = getenv("SHELL")) == 0)
 | 
						||
				shellname = "/bin/sh";
 | 
						||
			execl(shellname, shellname, 0);
 | 
						||
#endif /*-V6*/
 | 
						||
			writerr("No shell!");
 | 
						||
			exit(1);
 | 
						||
			break;
 | 
						||
		default:
 | 
						||
			while((res = wait(&status)) != pid && res != -1);
 | 
						||
			break;
 | 
						||
	  }
 | 
						||
#endif /*-TOPS20*/
 | 
						||
 | 
						||
	signal(SIGINT, sav2);		/* Restore signal settings */
 | 
						||
	signal(SIGQUIT, sav3);
 | 
						||
	set_tty();			/* Restore editor TTY modes */
 | 
						||
	redp(RD_SCREEN|RD_MODE);	/* Done, redisplay */
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
#if 0
 | 
						||
/* Miscellaneous utility routines - memory alloc/free and string hacking.
 | 
						||
 * If this page becomes overly large, it can be split off into a separate
 | 
						||
 * file called E_MISC.
 | 
						||
 */
 | 
						||
char *
 | 
						||
strdup(s)
 | 
						||
char *s;	/* Note that STRCPY's return val must be its 1st arg */
 | 
						||
{	char *strcpy();
 | 
						||
	return(strcpy(memalloc((SBMO)(strlen(s)+1)), s));
 | 
						||
}
 | 
						||
#endif
 | 
						||
 | 
						||
char *
 | 
						||
memalloc(size)
 | 
						||
SBMO size;
 | 
						||
{	register SBMA ptr;
 | 
						||
	extern SBMA sbx_malloc();
 | 
						||
 | 
						||
	if ((ptr = (SBMA)sbx_malloc(size)) != 0)
 | 
						||
		return((char *)ptr);
 | 
						||
	barf("ELLE: No memory left");
 | 
						||
	askerr();
 | 
						||
	return(0);		/* If we dare to continue... */
 | 
						||
}
 | 
						||
 | 
						||
chkfree (ptr)
 | 
						||
SBMA ptr;
 | 
						||
{
 | 
						||
	if(!free(ptr))
 | 
						||
	  {	errbarf("Something overwrote an allocated block!");
 | 
						||
		askerr();
 | 
						||
	  }
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
/* USTRCMP - Uppercase String Compare.
 | 
						||
 *	Returns 0 if mismatch,
 | 
						||
 *		1 if full match,
 | 
						||
 *		-1 if str1 runs out first (partial match)
 | 
						||
 */
 | 
						||
ustrcmp(str1,str2)
 | 
						||
char *str1, *str2;
 | 
						||
{	register char *s1, *s2;
 | 
						||
	register int c;
 | 
						||
	s1 = str1; s2 = str2;
 | 
						||
	while(c = *s1++)
 | 
						||
	  {	if(c != *s2 && upcase(c) != upcase(*s2))
 | 
						||
			return(0);
 | 
						||
		s2++;
 | 
						||
	  }
 | 
						||
	return(c == *s2 ? 1 : -1);
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
/* WRITERR(str) - Output string to standard error output.
 | 
						||
**	This is a separate routine to save a little space on calls.
 | 
						||
*/
 | 
						||
writerr(str)
 | 
						||
char *str;
 | 
						||
{	return(writez(2, str));
 | 
						||
}
 | 
						||
 | 
						||
/* WRITEZ(fd, str) - Miscellaneous general-purpose string output.
 | 
						||
 */
 | 
						||
writez(fd,acp)
 | 
						||
int fd;
 | 
						||
char *acp;
 | 
						||
{	register char *cp;
 | 
						||
	cp = acp;
 | 
						||
	while(*cp++);
 | 
						||
	write(fd,acp,cp-acp-1);
 | 
						||
}
 |