511 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			511 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!
 | ||
|  */
 | ||
| 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 */
 | ||
| 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 */
 | ||
| }
 | ||
| 
 | ||
| /* 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));
 | ||
| }
 | ||
| 
 | ||
| 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);
 | ||
| }
 | 
