510 lines
12 KiB
C
510 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);
|
||
}
|