V9/cmd/emacs/emacs_cmds.c

Compare this file to the similar file:
Show the results in this format:

/* EMACS_MODES: c, !fill */
#include "emacs_io.h"
#include "emacs_gb.h"
#include "emacs_cmds.h"
#include <signal.h>
#ifdef ux3
#include <fcntl.h>
#endif

/* lext -- extend line up to position if necessary */

lext(line,col)
/* Keywords: picture-mode movement insertion */
{
	mvc (line,col);
	if (column<col) insertc(col-column,' ');
}

dput(c)
/* Keywords: insertion modes:10 overwrite-mode */
{
	if (NODEL) insertc(1,c);
	else put(c);
}

/* move forward COUNT characters */
/* Keywords: movement commands forwards character-at-a-time picture-mode:20 */
forw(count)

register int count;
{
	register int retval;

	if (PICMODE) {
		if (count>0) {
			lext(curln,count+column);
			return(1);
		} else {
			column += count;
			if (column<0) {
				beep();
				column=0;
				return(0);
			} else return(1);
		}
	}
	if (count < 0) {
		retval = findb(-count);
	} else {
		retval = findf(count);
	}
	if (retval == 0) beep(); /* couldn't go all the way */
	move(kline,kcol);
	return(retval);
}

/* backward COUNT characters */

back(count)
/* Keywords: movement commands forwards character-at-a-time */
register int count;

{
	forw(-count);			/* do it this way */
}

/* move to previous line, same collumn */

upl (count) 

/* Keywords: commands backwards:30 upward-movement movement text-lines */
register int count;

{

	if (curln-count < 1) {
		if (count == 1) {
			beep();
			return(0);
		} else curln = 1;
	} else curln -= count;
	if (PICMODE) lext(curln,column);
	else mvc(curln,column);
	return(1);
}
/* move down one line, same column */

downl (count)
/* Keywords: commands forwards:30 downward-movement movement text-lines */
register int count;

{
register int retval;

	if (((curln += count) > nlines)&& (PICMODE == 0)) {
		curln = nlines+NLRUN;
		beep();
		retval = 0;
	} else retval = 1;
	if (PICMODE) lext(curln,column);
	else mvc(curln,column);
	return(retval);
}


/* abort EMACS */

int aborts = 0;
eabort(sig)
int sig;
{
/* Keywords: internal-errors unix-interface commands */
	if (sig){
		signal(sig,SIG_DFL); /* Prevent looping */
	}
	signal (SIGIOT,SIG_DFL);
	cook();
	if (aborts>3) exit(-1);
	aborts++;
#ifdef MINFILES
	rmtemp();
#endif
#ifdef PC
	rmtemp();
	exit(-1);
#else
#ifdef bsd
	sigsetmask(0);			/* ARGH, set mask to allow abort! */
#endif
	abort();
	exit(-1);
#endif PC
}


/* exit EMACS */


quit()

{
/* Keywords: commands exit-processing user-interface:20 unix-interface:30 */
	clear();
	cook();
	statout();
#ifdef MINFILES
	rmtemp();			/* flush temp files */
#endif
#ifdef PC
	rmtemp();
#else
	flushproc();
#endif
	exit(0);
}

/* exit EMACS gracefully */

gquit()
/* Keywords: commands exit-processing user-interface:20 unix-interface:30 macro-hooks:10 */
{
	if (hooks[Exit_Emacs_Hook]) if (hook(Exit_Emacs_Hook) == 0) return;
	if(bclean()== 0)quit();
}

/* kill line */

/* if the count is one, and if there is text on the line beyond column,
 * only that text is killed 
 * if count is one, and column is at end of line, the end of line is killed */
/* if count is greater than one, the next count lines (and their end of
 * lines) are killed */

/* all killed text is put into the kill stack */


ekill (count)

register int count;

/* Keywords: commands deletion killstack:10 text-lines */

{
	register int l;
	int opic,onodel;
	
	if (numarg == 0) {
		if ((l=leng(curln)) > column) {
			return(delc(l-column));
		}
	}
	opic = PICMODE;
	onodel =NODEL;
	PICMODE=NODEL=0;		/* Allow line kills! */
 
	kline = curln+count;
	kcol = 0;
	l= tkill();
	PICMODE=opic;
	NODEL=onodel;
	return(l);
}

/* goto beginning of current line */

begin()
/* Keywords: text-lines commands movement backwards */
{
	move(curln,0);
}

/* goto end of line */

endl()
/* Keywords: text-lines commands movement backwards */
{
	mvc (curln,10000);		/* mvc will adjust line length */
}

mquote(arg)
int arg;
/* Keywords: quoting commands insertion */
{
	quote(arg,0200);
}
rquote(arg)
int arg;
/* Keywords: quoting commands insertion */
{
	quote(arg,0);
}
	
/* insert the next character, whatever it is */

/* note that newlines inserted ths way act just like unquoted newlines */

quote(count,metf)

register int metf;
register int count;

{
/* Keywords: quoting commands insertion */
	register int c;

	while(count--) {
		if ((VERBOSE)&& (MOREIN == 0)) prompt1("%d ^Q: ",count+1);
		
		c = getchar();
		c = c | metf;
		insertc(1,c);
		if (VERBOSE && (MOREIN == 0)) {
			unprompt();
		}
		disup();
	}
}

/* numchar -- convert argument to a character to insert */

numchar(count)
/* Keywords: quoting commands insertion argument-processing */
register int count;
{
	insertc(1,count);		/* insert the count */
}


/* deletes count characters going forward */

fdel(count)
/* Keywords: commands deletion forwards character-at-a-time */
{
	IGNORE(findf(count));
	return(tkill());
}

/* deletes count characters going backward */

bdel(count)

register int count;
/* Keywords: commands deletion backwards character-at-a-time */
{
	IGNORE(findb(count));
	return(tkill());
}

/* file write command */


int fright(arg)

int arg;
{
	register char *np;
/* Keywords: files commands buffers:20 filenames:10 writing */
	if ((np = expenv(getname("Write file? "))) != NULL) {
		return(wout(np,arg));
	}
	return(0);
}

/* fred -- read a file */

fred(arg)

int arg;
{
/* Keywords: files commands buffers:20 filenames:10 reading */
	
	register char *np;

	if ((np = expenv(getname("Read File? "))) != NULL) {
		return(readin(np,arg));
	} else return(0);
}

/* forward words -- leaves kline, kcol at spot that is count words
 *forward */


wordf(count)

register int count;

{
	kmark();

/* Keywords: commands:10 forwards movement:50 deletion:50 word-oriented-commands */
	
	while (count--) {
		if (skipf(WRDSEP) || skipf(WRDCHR)) {
			return;
		}
	}
}

/* skips kline, kcol forward until a character without type bit on is
 * found */


skipf(bit)

register int bit;
/* Keywords: forwards sentence-commands word-oriented-commands movement:20 commands:10 */
{
	while (bits[*klptr] & bit) {
		if (mfk()) return(1);
	}
	return(0);
}


/* move forward count words */

mfwrd(count)
/* Keywords: commands forwards movement word-oriented-commands */
register int count;

{

	wordf(count);
	move(kline,kcol);
}

/* kill next count words */



kfwrd(count)
/* Keywords: commands forwards deletion word-oriented-commands */
register int count;

{
	wordf(count);
	return(tkill());
}

/* skip kline, kcol back until a character without type bit is found */



skipb(bit)

register int bit;
/* Keywords: sentence-commands word-oriented-commands movement:20 backwards commands:10 */
{
	do {
		if (mbk()) return(1);
	} while (bits[*klptr] &bit);
	return(0);
}

/* backward count words, leaves pointer in kline, kcol */

wordb(count)

register int count;
/* Keywords: commands:10 backwards movement:10 deletion:20 word-oriented-commands */
{
	kmark();
	while (count--) {
		if ((skipb(WRDSEP) || skipb(WRDCHR))) {
			return;
		}
	}
	IGNORE(mfk());
}

/* move back count words */

mbwrd(count)

register int count;
/* Keywords: commands backwards movement word-oriented-commands */
{
	wordb(count);
	move(kline,kcol);
}

/* kill back count words */

kbwrd(count)


register int count;
/* Keywords: commands backwards deletion word-oriented-commands */
{
	wordb(count);
	return(tkill());
}

/* kmark -- set kline,kcol and klptr */

kmark()

/* Keywords: commands:5 movement:50 deletion:50 character-at-a-time:20 word-oriented-commands:20 sentence-commands:20 */

{
	kline = curln;
	kcol = column;
	klptr = mkline(kline)+kcol;
}


/* move kline,kcol forward one */

mfk()
/* Keywords: commands:5 movement:50 deletion:50 character-at-a-time:20 word-oriented-commands:20 sentence-commands:20 forwards */
{
	if ((*klptr++)!=EOL ) {
		++kcol;
		return(0);
	}
	if (kline<nlines) {
		++kline;
		klptr = mkline(kline);
		kcol=0;
		return(0);
	}
	return(1);
}

/* move kline, kcol back one character */

mbk()
/* Keywords: commands:5 movement:50 deletion:50 character-at-a-time:20 word-oriented-commands:20 sentence-commands:20 backwards */
{
	if ((kcol--)>0) {
		klptr--;
		
		return(0);
	}
	if (kline>1) {
		--kline;
		kcol = leng(kline);
		klptr = mkline(kline) +	kcol;
		return(0);
	}
	kcol = 0;
	return(1);
}

	/* forward to end of sentence */

	/* leaves resulting pointer in kline, kcol */

fsent () 

/* Keywords: commands:10 forwards movement:40 sentence-commands */

{
	kmark();

	while (mfk() == 0) {
		if ((bits[*klptr] & SENTE) && (bits[klptr[1]]&WHITE)) {
			return(1);
		}
	}
	return(0);
}

/* back one sentence, leaves pointer in kline, kcol */

bsent()

/* Keywords: commands:10 backwards movement:40 sentence-commands */

{
	
	kmark();

	IGNORE(skipb(WRDSEP));
	while (mbk() == 0) {
		if (kcol == 0) {
			if (kline>1) {
				if (bits[*mkline(kline-1)] & SENTE) return(1);
			}
		}
		if ((bits[*klptr] & SENTE) && (bits[klptr[1]] & WHITE)) {
			IGNORE(skipf(WRDCHR));
			IGNORE(skipf(WRDSEP));
			return(1);
		}
	}
	return(0);
}

/* move back count sentences */

ssent(count)
register int count;

/* Keywords: commands backwards movement sentence-commands */

{
	while (count--) {
		if(bsent()) move(kline,kcol);
	}
}

/* move forward count sentences */


esent(count)
register int count;

/* Keywords: commands forwards movement sentence-commands */

{
	while (count--) {
		if (fsent() == 0) break;
		move(kline,kcol+1);
	}
}


/* move to top of file */

top()
/* Keywords: files:20 buffers:20 movement upward-movement commands */
{
	move(1,0);
}


/* move to end of file */

bot()

/* Keywords: files:20 buffers:20 movement downward-movement commands */

{
	mvc(nlines,10000);
}

/* new line handler */

/* First, finishes off comment on the current line (if any) */

/* Next, if the current buffer has a sub-process, the line is sent to the sub-process */

/* Then, if the next line is non-empty, it creates an empty next line */
/* next, the next line is tab adjusted (if in C mode ) */

/* now, any text on the current line beyond the current position, it is
 * moved to the end of the next line */

/* finally, the pointer is moved to the (tab adjusted) start of the next
 * line */

/* if count > 1, then count-1 blank lines will be inserted in between
 * the current line and the 'next' line */

nl(count)

/* Keywords: commands C-mode:30 insertion text-lines comments:50 shell-escape:10 */



register int count;

{

	if (RARE == 0) {
		if (comln == curln) putin(" */");
#ifndef PC
		if (curbf == procbuf){
			MARK *mp;
		
			mp = markptr(curbf);
			if ((mp->markl == curln) && (mp->markc <= column)) {

/* If this is the last line on which output was done from */
/* the sub-process, then send from mark and re-mark */
			
				sendproc (clptr+mp->markc, column+1-mp->markc);
				mp->markl = curln+1;
				mp->markc = 0;
			} else {
				sendproc(clptr,column+1);
			}
		}
#endif
	}
	comln = 0;
	if ((count != 1) || (NLINS) || (isblank(curln+1)==0) || (clptr[column] != EOL)) {
		openl(count);
	}
	if (TABMD&& (RARE == 0)) tabjust(curln+count);
	else move(curln+count,0);
}

/* sets mark at current position */

mark(mnumb)

/* Keywords: marking regions commands */

{
	MARK *markp;
	markp = markptr(mnumb);
	markp->markl = curln;
	markp->markc = column;
}

/* returns mark pointer */

MARK *markptr(mnumb)

register int  mnumb;

/* Keywords: commands:10 marking:50 regions deletion:50 movement:50 */
{
	
	if (mnumb >= NMARKS) {
		error(WARN,44);
		mnumb = curbf;
	}
	if ((mnumb == 1) && (numarg == 0)) mnumb = curbf;
	return(&marks[mnumb]);
}


/* kills text between current position and mark position */


mkill(mnumb)
int mnumb;
/* Keywords: commands deletion regions */
{
	register MARK *markp;
	
	markp = markptr(mnumb);
	kline = markp->markl;
	if (kline < 1) kline = 1;
	if (kline > nlines) kline = nlines;
	kcol = leng(kline);
	if (markp->markc < kcol) kcol = markp->markc;
	if (PICMODE) {
					/* This pain is to get the
					 * mark to work  in picture
					 * mode so that everything
					 * up to and including the
					 * marked position is killed */
		if (kcol > column) {
			kcol++;
		} else {
			forw(1);
		}
	}
	return(tkill());
}

/* retrieves text from the kill stack and inserts it (count times ) */


int yline;
int ycol;

yank(count)

/* Keywords: regions commands insertion killstack retrieval popping:10 */

register int count;
{
	register int result;
	register int unp;
	
	unp = unstart();
	mark(curbf);
	while (count--) {
		result = retrv();
	}
	unend(unp);
	yline = curln;
	ycol = column;			/* Save, so we know when not to re-yank */

	return(result);
}

/* kills the marked region, removes the top item from the kill stack,
 * and then inserts the next area from the kill stack */


reyank(count)		/* yank again */
register int count;
/* Keywords: regions commands insertion killstack retrieval deletion popping:10 */
{
	register int unp;

	if ((curln != yline) || (column != ycol)) {
		error (WARN,84);	/* Prevent some nastyness */
		return;
	} 
	kbapp = 0;
	unp = unstart();
	mkill(curbf);	/* flush last insertion */
	kpop();		/* first pop the mkill */
	kpop();		/* now pop that last insertion */
	yank(count);
	unend(unp);
}

/* search subroutine */

/* searches forward or backward for a string, starting at the 
 * given position.  If the string is found, 1 is returned, and the start
 * of the match is left in kline, kcol.  Return of 0 indicates no match */

/* Keywords: searching */

srch(sline,scol,sp,direct)

int sline;
int scol;
char *sp;
int direct;

{
	register char *lp;
	register char *cp;
	register int l;
	int c;
	
	lp = sp;
	while (c = *lp) *lp++ = casem[c&0177]; /* map string to allowed case */
	lp = mkline(sline)+scol;
	while (1) {
		cp = sp;
		l = sline;
		while (*cp) {
			if ((c = casem[(*lp++)&0177]) != *cp++) goto again;
			if (c == EOL) {
				if (l == nlines) {
					if (direct>0) return(0);
					goto again;
				}
				++l;
				lp = mkline(l);
			}
		}
		kline = sline;
		kcol = scol;
		return(1);

again:		if (brkflg) brkit();
		lp = mkline(sline)+scol;
		if (direct>0) {
			do {			
				if (*lp++ !=EOL) scol++;
				else {
					if (sline == nlines) return(0);
					sline++;
					scol = 0;
					lp = mkline(sline);
				}
			} while (casem[*lp&0177] != *sp);
		} else {

			do {
				if (--scol<0) {
					if (sline == 1) return(0);
					scol = leng(--sline);
					lp = mkline(sline)+scol;
				} else lp--;
			} while (casem[*lp&0177] != *sp);
		}
	}
}

fisrch(arg)
int arg;
/* Keywords: searching commands forwards key-bindings:10 */
{
	return(isrch(1,arg));
}
risrch(arg)
int arg;
/* Keywords: searching commands backwards key-bindings:10 */

{
	return(isrch(-1,arg));
}
/* incremental search command (either direction) */


isrch(d,arg)
int d;
int arg;

/* Keywords: commands:80 searching user-interface:60 key-bindings:50 */


{
	int oldln;
	int oldcol;
	register char *ilp;
	register int c;
	int lx;
	int ly;
	int nd;
	int missed;
	char sst[40];
	char *xp;

	ilp = sst;
	missed = 0;
	lx = oldln = curln;
	ly = oldcol = column;
	if (infrn < 0) {			/* if in a macro */
		xp = getname(NULL);		/* search argument */
		ilp = mstrcpy(ilp,xp);
	}
	while (1) {
		*ilp = 0;
		if (missed == 0) {
			psrch(d,sst);		/* prompt */
			if (infrn == 0) mflush(stdout);
			if (srch(curln,column,sst,d) == NULL) {
				move(lx,ly);
				if (infrn >= 0) {
					beep();
					prompt1("Failing Search: %s", sst);
				}
				missed = 1;
			} else {
				move(lx = kline,ly = kcol);
				if (ilp!=sst)strcpy (presst,sst);
				missed = 0;
			}
		}
		if (infrn < 0) return(missed == 0); /* macro invocation */
		disup();
		if ((missed == 0) && (incnt == 0)) {
			psrch(d,sst); /* restore prompt */
			mgo(nln,ncol); /* goback to display position */
		}
		c = getchar();
		if ((c == CTRLH)||(c == RUBOUT)) {
			if (ilp!=sst) ilp--;
			missed=0;
			move (oldln,oldcol);
			continue;	/* next loop */
		} 
		if (c == CTRLG) {
			move (oldln,oldcol);
			unprompt();
			beep();
			return(0);
		}
		if (srch_nl?((c == '\n') || (c == '\r')) : (c == ESC)) {
goout:			unprompt();
			return(missed == 0);
		}
		if (nd=issrch(c)) {
			if (nd == d) {
				if (ilp == sst) {
					ilp = mstrcpy(sst,presst);
					continue;
				} else {
					if (missed) {
						beep();
						continue;
					}
					forw(d);
				}
			} else {
				missed=0;
				d = nd;
			}
			continue;
		}
		if (isquote(c)) {
			c = getchar(); /* skip all other processing */
		} else {
			if (c == CTRLM) c = '\n';
			if ((c < 040) && (c != '\n')) {
				ungetch(c);
				goto goout;
			} 
		}
		if (missed) {
			beep();
			continue;
		}
		*ilp++ = c;
	}
}

/* psrch -- print search prompt */

psrch(dir,sstp)
register int dir;
char *sstp;

/* Keywords: mode-line prompting searching */

{
	if (dir>0) prompt1("Search: %s",sstp);
	else prompt1("Reverse Search: %s",sstp);
}

/* handle a separator character in auto fill mode */

/* if fill mode is on, and the current position is beyond FILLCOL, a
 * newline is inserted before the last word.  Otherwise, just insert the
 * character at the current position */


afsep(count,chr)

int count;
int chr;

/* Keywords: commands:40 modes:30 text-filling comments:20 C-mode:10 */

{
	register int cc;
	int newcol;
	int oldcol;
	char lbuf[MAXEL];		/* KLUDGE! */
	register char *p1;
	register char *p2;
	
/* whitespace characters in a macro expansion must be quoted to be
 * self-inserting.  The following checks for non-zero character
 * count, input from macro, and a whitespace character to be
 * inserted.  Under these conditions, no inserting is done. */
	
	if (count && (infrn < 0)&& (WHITE&bits[chr])) return(1);


	
	if (FILLMD && (RARE == 0)) {
		insertc(count,chr);
		oldcol = column;
		cc = leng(curln) - column;
		mline=0;
		findpos(clptr,column);
		if (mline || (mcol>FILLCOL)|| (column > FILLCOL)) {

again:			newcol = column;
			
			do {
				column--;
			} while ((column) &&
				(((bits[clptr[column]] & WHITE) == 0) ||
				(clptr[column+1] == '.') ||
				(clptr[column+1] == '\'')));
			mline=0;
			findpos(clptr,column);
			if (mline || (mcol>FILLCOL)|| (column > FILLCOL)) goto again;



/* Now then, do the work.  We are sitting at the right column to break the line */
/* First, save the rest of the line.  Then, kill it, then, do a newline,  */
/* now, bring it back */
			
			
			if (column == 0) column = newcol; /* 1 word line */
			if (column == oldcol) return(1); /* Do absolutely nothing */
			p1 = clptr+column+1; /* Eat the whitespace character */
			
			p2 = lbuf;
			while ((*p2++= *p1++)!=EOL); /* copy line */
			sputl(curln,column,curln); /* Flag line is bad! */
			clptr[column]=EOL; /* truncate line */
			if (comln == curln) {
				comln = -1;
				nl(1);
				cment(1);
				*(clptr+column-3)=' ';
			} else nl(1);
			p1 = lbuf;
			while (*p1 != EOL) put(*p1++); /* restore line */
			forw(-cc);   /* go back to right place. */
		}
		return(1);
	}
	insertc(count,chr);
	return(1);
}

/* unix escape */

char *shell = "sh";			/* name of shell processor */

ux(arg)

int arg;
{
/* Keywords: commands buffers:20 unix-interface shell-escape */

	register char *sp;		

	if (SAVEMD) fsave(0);
	do {
		sp = (getname("command line? "));
	} while (sp && 	(unx(sp,(arg != 1))== 0));
	return(cstatus);
}

#ifndef PC

char *
nvmatch(s1, s2)
register char *s1, *s2;

/* Keywords: unix-interface shell-escape environment-variables */
{
	while(*s1 == *s2++)
		if(*s1++ == '=')
			return(s2);
	if(*s1 == '\0' && *(s2-1) == '=')
		return(s2);
	return(NULL);
}

/* Environment fixer-upper, adds definition of filename to the
 * environment and returns a pointer to it. */

extern char **environ;

char **
fixenv(defp)
register char *defp;
/* Keywords: unix-interface shell-escape environment-variables */

{
	register char **xp;
	register char **yp;
	int envd = 0;
	
	xp =((char **)  &bbuf[0][0]);		/* Overlay screen */
	yp = environ;
	
	while (*xp = *yp++) {
		if (nvmatch(defp,*xp++)) {
			xp[-1] = defp;
			envd=1;
		}
	}
	if (envd==0) {
		*xp++ = defp;
		*xp = NULL;
	}
	xp =((char **)  &bbuf[0][0]);		/* Overlay screen */
	return(xp);
}
#endif


/* general unix escape --  */

/* flag = 0 means just run it */
/* flag = 1 means run and feed it the buffer */
/* flag = 2 means run and replace .exec with the result */
/* flag = 3 means run and append result to .exec */
/* flag = 4 means run and bring results back in fnbuf */
/* flag = 5 means run and hook up input and output pipes */
/* flag = 6 means run it and hook up output to splfile, leave input in splfile */
/* flag = 7 means run it and hook up input from splfile, leave output in splfile */

#define UNEEDSIN 1
#define UNEEDSOUT 2
#define UDOTEXEC 4
#define UNOSCREEN 8
#define URETBUF 16
#define UPROCESS 32
#define USENDBUF 64
#define UREADBUF 128
#define UINITBUF 256
#define USPLIN 512
#define USPLOUT 1024

int unxflags[8] = {
	0,
	UNEEDSIN+USENDBUF,
	UNEEDSOUT+UDOTEXEC+UREADBUF+UINITBUF,
	UNEEDSOUT+UDOTEXEC+UREADBUF,
	UNEEDSOUT+UNOSCREEN+URETBUF,
	UNEEDSIN+UNEEDSOUT+UNOSCREEN+UPROCESS,
	UNEEDSIN+USPLOUT+UNOSCREEN,
	UNEEDSOUT+USPLIN+UNOSCREEN
};


unx(cmd,flag)
/* Keywords: commands:80 buffers:10 files:10 environment-variables:20 unix-interface shell-escape dired:10 sub-processes:40 encryption:20 */
register char *cmd;
register int flag;

{
#ifdef PC
	return(1);			/* Don't do this, just exit */
#else
	struct pipes {
		int rdpipe;
		int wrpipe;
	} piped1,piped2;
	FILE tinbuf[1];
	FILE *infile;
	int (*istat)();
	int (*qstat)();
	register int i;
	int c;
	int obuf;
	int pid;
	char **evp;
	char *obname;
	char evbuf[256];
	char *myshell;
	
	if (*cmd == 0) return(1);	/* Don't even bother with null commands */
	obname=fname();
	cstatus  = -1;			/* start off bad */
	flag = unxflags[flag];		/* Translate to bit-coded value */
	
	if ((flag & UPROCESS) && procpid) flushproc(); /* Zap old process */

	if (flag&UDOTEXEC) {		/* if .exec in the business */
		obuf = curbf;		
		if (chgbuf(".exec") == 0) return(0);
	}

	if ((USILENT == 0) && ((flag&UNOSCREEN) == 0)) {
		clear();
		putout("%s",cmd);
		istat = signal(SIGINT, SIG_IGN);
		qstat = signal(SIGQUIT, SIG_IGN);
		cook();
	}
	if (flag & UNEEDSIN) {
		if (pipe(&piped1)) {
			error (WARN,errno,cmd);
			goto done;
		}
	}
	
	if (flag & UNEEDSOUT) {
		if (pipe (&piped2)) {
			error (WARN,errno,cmd);
			if (flag & UNEEDSIN) {
				close(piped1.rdpipe);
				close(piped1.wrpipe);
			}
			goto done;
		}
	}
	if ((pid = fork()) == 0) {	/* child process */
		
		if (flag &UNEEDSIN) {
			close(0);
			dup(piped1.rdpipe);
		} else if (flag & USPLIN) {
			close(0);
			dup(splfile);
 		} else if ((USILENT)|| (flag & URETBUF)) {
			close(0);
			open("/dev/null",0);
		}
		if (flag &UNEEDSOUT) {	/* if writing to .exec */
			close(1);
			dup(piped2.wrpipe);
		} else if (flag & USPLOUT) {
			close(1);
			dup(splfile);

		} else if (USILENT) { /* silent, throw away output */
			close(1);
			open("/dev/null",2);
		}
		signal(SIGINT, SIG_DFL);
		signal(SIGQUIT,SIG_DFL);
		xclose(2);	/* close all irrelavent files */
		dup(1);		/* restore stderr */

		myshell = getenv("SHELL");
		if ((myshell == NULL)|| (*myshell == '\0')) myshell = "/bin/sh";
		umask(mymask);		/* Restore user's umask */
		seprintf(evbuf,"filename=%s",obname);
		evp = fixenv(evbuf);		/* Add filename definition */
		
		if (streq(cmd,shell)) execl(myshell,shell,"-i",0,evp);
		execle(myshell, shell, "-c", cmd, 0,evp);
		execle("/bin/sh",shell, "-c", cmd, 0,evp);
		_exit(127);
	}

/* parent process (the one that remains EMACS) */
	
	if (pid== -1)  {
		error(WARN,73,cmd);
		goto done;
	}
	if (flag &UPROCESS) {
		close (piped1.rdpipe);
		close (piped2.wrpipe);
		inproc = piped2.rdpipe;
		outproc = piped1.wrpipe;
#ifdef ux3
		fcntl(inproc,F_SETFL,O_NDELAY); /* Turn on non-blocking I/O */
		ioset(1);		/* Set timeout on terminal short */
#endif
		procbuf = curbf;
		procpid = pid;
		return(1);
	}
	if (flag & USPLIN) {
		close(splfile);
		splfile = piped2.rdpipe;
		close(piped2.wrpipe);
		return(1);
	}
	if (flag & USPLOUT) {
		close(splfile);
		splfile = piped1.wrpipe;
		close(piped1.rdpipe);
		return(1);
	}
	if (flag&USENDBUF) {		/* if sending the buffer */
		close(piped1.rdpipe);
		infile = fdopen(tinbuf,piped1.wrpipe,"w");
		for (i = 1; i <=nlines; i++) {
			cmd = mkline(i);
			while ((c = *cmd++) != EOL) {
				putc(c,infile);
			}
			if (i != nlines) putc(EOL,infile);
		}
		mclose(infile);
	}
	if (flag&(UREADBUF|URETBUF)) {		/* if writing */
		close(piped2.wrpipe);
		infile = fdopen(tinbuf,piped2.rdpipe,"r");
		if (flag&UINITBUF) bufinit();
		else if (flag&UREADBUF) {
			bot();	/* append */
			if (column != 0) nl(1);
			putin(cmd);
			nl(2);
		}
		if (flag&URETBUF) {
			cmd = fnbuf;
			i = 127;
			while (i--) {
				c = getc(infile);
				if ((c=='\0')||(c == '\n')||(c==' ')||(c=='\t')|| (c == EOF)) break;
				*cmd++ = c;
			}
			*cmd = 0;	/* EOS */
			mclose(infile);
		} else {
			i = ((flag & UINITBUF) != 0);
			readsub(infile,i,cmd,1-(USILENT|NOECHO));
			chbuf(obuf);
		}
	}
		/*  wait for child to finish */

	/* must wait for child else it becomes a <defunct> process */

	while ((i = wait(&cstatus)) != pid && i != -1);		

	if (flag&URETBUF) return(cstatus);
	/* child is now done, go back to normal EMACS */
	mailcnt = 0;
	
done:	if ((USILENT == 0) && ((flag&UNOSCREEN) == 0)) {
		uncook();
		signal(SIGINT, istat);
		signal(SIGQUIT, qstat);
		junked++;
	}
	return(contin());
#endif	
}

bux(arg)

int arg;

/* Keywords: commands buffers unix-interface shell-escape */

{
	register char *sp;
	register int flag;
	if (SAVEMD) fsave(0);
       	if (arg == 0) flag = 5;
	else if (arg == 1) flag = 2;
	else flag = 3;
	
	do {
		sp = (getname("command line? "));
	} while (sp && 	(unx(sp,flag)== 0));
	return(cstatus);
}

/* query replace */
/* prompts for strings and does conditional replacement */

qrep(gflag)
int gflag;
/* Keywords: commands query-replace */
{
	iqrep(0,gflag);			/* no regular expressions */
}

rqrep(gflag)
int gflag;
/* Keywords: commands query-replace regular-expressions */
{
	iqrep(1,gflag);			/* regular expression version */
}


iqrep(regular,gflag)

int regular;
int gflag;

/* Keywords: query-replace regular-expressions:20 user-interface key-bindings:20 commands:10 */


{

	char sstring[128];
	int dir = 1;
	register char *sp;
	int bsflag = 0;
	int ask = 1;
	int show = 1;
	int l = 1;
	int stop = 0;
	int upt;
	register int c;
	int oldln;
	int oldcol;
	
	if((sp = getname("From? "))== NULL) return;
	if (*sp == NULL) sp = presst;
	strcpy(sstring,sp);
	if ((sp = getname("To? ")) == NULL) return;
	if (!streq(sp,"%")) strcpy(rstring,sp); /* Copy over return string unless it is a single % */
	oldln = curln;
	oldcol = column;
	upt = unstart();
	while (1) {
		if (regular) { 
			if (rgsrch(curln,column,sstring,0,dir)== 0) break;
		} else {
			if (srch(curln,column,sstring,dir)== 0) break;
		}
		dir = 1;
		if ((l == 0) && (curln == kline) && (column == kcol)) {
			error(WARN,72,sstring,rstring);
			goto rdone;
		}
		
 		prompt1("From %s To %s",sstring,rstring);
		strcpy(presst,sstring);	/* save successful search */
		move(kline,kcol);
		if (show) {
			disup();
		}
		if (ask){
			c = getchar();
			donttime=0;
		} else c = 'y';
		switch(c) {

case '.':	stop = 1;		/* stop after this one */
		goto rep_it;		/* make this replacement. */
case ESC:	sp = getname("Replace with: ");
		if (sp) {
			strcpy(rstring,sp);
			goto rep_it;
		}
case CTRLG:	beep();
done:		unprompt();		/* wipe out help message */
		unend(upt);
		return;

case '<':		
		move (oldln,oldcol);	/* return to last replacement */
		goto done;


case 'R':	show = 0;
case 'r':	ask = 0;		/* do rest */
		NOBEL++;		/* Inhibit ding on zero length deletes */
		/* fall through to do this one too */
case ' ':
case 'y':
case 'Y':
rep_it:		kbapp = 0;
		oldln = curln;
		oldcol = column;
		if (regular) {
			l = (loc2-column);
		} else {
			l = (lng(sstring));
		}
		fdel(l);
		for (sp = rstring; *sp; sp++) {
			switch(*sp) {			
			case '\\':
				if (!bsflag) {
					bsflag = 1;
					continue;
				}
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
				if (bsflag && regular && regrep(*sp-'1')) {
					bsflag = 0;
					continue; /* \number style replace */
				}
/* fall through to default treatment of numbers	*/

			default:
				dput(*sp);
				bsflag = 0;
				break;
			case '&':
				if (bsflag) {
					dput(*sp);
					bsflag = 0;
					continue;
				} else {
					yank(1);
					unpop(1);
				}
			}
		}
		unins(oldln,oldcol); /* Make an undo entry */
		kpop();			/* clean up our stack */
		if (stop) goto done;
		if (show) {
			disup();	/* before next search */
			mflush(stdout);
		}
		break;
case CTRLH:
case RUBOUT:
case 'n':
case 'N':
		forw(1);
		break;
case 'p':
case 'b':	
		dir = -1;
		forw(-1);
		break;
	default:
		beep();
case '?':
		donttime=1;		/* Avoid time mode */
		prompt3("Query replace -- 'y' to replace, 'n' to skip, ^G to quit,'R' or 'r' for rest");

	}
	if (gflag>1) move(kline+1,0);	/* only do first one */
	}
rdone:	prompt1("Replace Done");
	if (ask==0) NOBEL--;		/* Re-enable warning on 0 length deletes */
	unend(upt);
}
/* length of a string */

lng(sp)

char *sp;
/* Keywords: string-handling length */
{
	register char *spo;

	spo = sp;
	while(*spo++);
	return(spo-sp-1);
}

/* capitalize next count characters */

capnxt(count)

register int count;

/* Keywords: commands character-at-a-time forwards capitalization */
{
	register int c;
	int oldover;
	while (count--) {
		c = (clptr[column]);
		if ((c >= 'a') && (c <= 'z')) {
			oldover=OVERW;
			OVERW=1;
			insertc(1,c-040);
			OVERW=oldover;
		} else if (forw(1)==0) break; /* check for EOF */
	}
}


/* lowercase  next count characters */

lownxt(count)

register int count;

/* Keywords: commands character-at-a-time forwards capitalization */
{
	register int c;
	int oldover;
	while (count--) {
		c = (clptr[column]);
		if ((c >= 'A') && (c <= 'Z')) {
			oldover=OVERW;
			OVERW=1;
			insertc(1,c+040);
			OVERW=oldover;
		} else if (forw(1)==0) break; /* check for EOF */
	}
}

/* capitalize first character of the next word */

capwrd(count)

register int count;

/* Keywords: commands word-oriented-commands forwards capitalization */
{
	while (count--) {
		kmark();
		IGNORE(skipf(WRDSEP));
		move(kline,kcol);	/* to next word */
		capnxt(1);
		mfwrd(1);
	}
}

/* exchange current position and the marked position */

exch(mnumb)

/* Keywords: commands regions marking movement */
{
	register int x;
	register int y;
	register MARK *markp;
	
	markp = markptr(mnumb);
	x = markp->markl;
	if (x < 1) x = 1;
	y = markp->markc;
	if ((x == curln) && (y == column)) {
		return(0);
	}
	if (x > nlines) x = nlines;
	mark(mnumb);
	mvc(x,y);
	return(1);
}

/* exchange next two chacters in the buffer and move forward one */

xpose(count)

register int count;

/* Keywords: commands forwards movement:10 transposition */
{
	int oldnodel,oldpic;

	register int undp;
	
	oldnodel=NODEL;
	oldpic=PICMODE;
	NODEL=0;
	PICMODE=0;
	kbapp = 0;
	undp = unstart();
	fdel(1);
	forw(count);
	yank(1);
	unend(undp);
	kpop();
	forw(-1);
	NODEL=oldnodel;
	PICMODE=oldpic;
}

/* changes a mode parameter.  Mode parameters are of three types,
 * string, boolean, or integer.

 * Integer modes set from the count argument, boolean modes turn on if
 * count is one, off otherwise, and string modes prompt for new value

 * chmode returns the previous value of the mode set */


chmode(count)
register int count;

/* Keywords: modes commands assignment */
{
	register char *mp;
	
	if ((mp=getname("Mode? ")) != NULL) {
		if (*mp == '\0') {
			modisp(numarg);
			return(0);
		}
		return(setmode(mp,count,1));
	}
	return(0);
}

char *MDHEAD = "EMACS_MODES:";

bfmodes()				/* set modes from buffer */
/* Keywords: modes buffers file-modes assignment:10 */
{
	register char *cp;
	register char *cp1;
	char mdbuf[20];
	int onoff;
	
	kline = 11;
	if (kline > nlines) kline = nlines;
	while ((kline > 0) && srch(kline,leng(kline),MDHEAD,-1)) {
			 /* if modes set */

		for (cp = mkline(kline)+kcol+12; *cp != EOL;) {
			cp1 = mdbuf;
			while ((*cp != EOL) && (*cp != '!') && ((bits[*cp] & WRDCHR) == 0)) cp++;
			if (*cp == '!') {
				onoff = 0; 
				cp++;
			} else onoff = 1;
			
			while (bits[*cp] & WRDCHR) {
				*cp1++ = *cp++;
			}
			if (*cp == '=') {
				cp++;
				cp = nscan(cp,&onoff);
			}
			
			*cp1 = 0;
			if (mdbuf[0]) setmode(mdbuf,onoff,0); /* Set up mode */
		}
		kline--;
	}
}
			

setmode(mp,count,fudge)
register char *mp;
int count;
int fudge;
/* Keywords: modes macro-programming:10 assignment time-handling:20 display-format:10 */
{
	register int i;
	extern char cbuf[];
	int retval;
	int mfield;
	
	for (i = 0; i < NMODES; i++) {
		if (streq(mdata[i].modename,mp)) {
			retval = *mdata[i].modeloc; /* old value */
			switch(mdata[i].modetype) {
			case ONOFF:
				if (fudge && (count != 1)) count = 0;
				*mdata[i].modeloc = count;
				break;
			case INT:
				*mdata[i].modeloc = count;
				break;
			}
			if ((mfield = mdata[i].moderset)&DSIZE) {
				SCRNLIN = SCRLINES+4;
				setsize();
			}
/* customize the behavior of ^H in word commands */
		
			if (mfield|CTYPE) {
				if (BACKP) {
					bits[CTRLH] = bits['_'] = WRDCHR;
					ctype[CTRLH] = BACKSP;
				} else {
					bits[CTRLH] = bits['_'] = WRDSEP;
					ctype[CTRLH] = CONTRL;
				}
				if (NOTABS) ctype[CTRLI] = CONTRL;
				else ctype[CTRLI] = TAB;
				if (bit8) metal = 0;
				else metal = 2;
			}
			if (mfield & CSE) {
				if (NOCASE) count = 'a'-'A';
				else count = 0;
				for (i = 'A'; i <= 'Z'; i++) {
					casem[i] = i+count;
				}
			}
			if (timemd == 0) *cbuf = 0; /* Nullify current time */
			if (mfield&DISPLAY) {
				fclear(); /* force re-display */
				if (PICMODE==0) hcol=0;
			}
			return(retval);
		}
	}
	IGNORE(error (WARN,45,mp));
	return(0);
}


modval(mp)
char *mp;
/* Keywords: commands macro-programming modes */
{
	register int i;


	for (i = 0; i < NMODES; i++) if (streq(mdata[i].modename,mp)) return(*mdata[i].modeloc);
	return(0);
}


/* display all active modes.  Values displayed for integer and string
 * modes, all on boolean modes are displayed */

modisp(arg)
register int arg;
/* Keywords: informational-displays modes commands user-interface */
{
	register int i;
	char *mp;

	if ((arg < 0) && (mp = getname("Mode? "))) return(modval(mp));

	mtop();
	for (i = 0; i < NMODES; i++) {
		switch(mdata[i].modetype) {

		case ONOFF:
			if (*mdata[i].modeloc) putout("%s mode is on",mdata[i].modename);
			else if (arg) putout ("%s mode is off",mdata[i].modename);
			break;
		case INT:
			putout("%s = %d",mdata[i].modename,*mdata[i].modeloc);
			break;
		}
	}
	putout (endput);
	return(contin());
}

/* compare two strings */

streq(cp,cp1)

register char *cp;
register char *cp1;

/* Keywords: string-handling comparison */
{
	while (*cp) if (*cp++ != *cp1++) return(0);
	if (*cp1) return(0);
	return(1);
}

char *
mstrcpy(cp,cp1)

/* Keywords: assignment string-handling */
register char *cp;
register char *cp1;
{
	while (*cp++ = *cp1++);
	return(cp-1);
}

/* push the marked region onto the kill stack without killing it */


pickup(mnumb)

/* Keywords: regions commands killstack stacking */


{
	register MARK *markp;
	

	markp = markptr(mnumb);

	if ((markp->markl < curln) || ((markp->markl == curln) && (markp->markc < column))) {
		killstk(markp->markl,markp->markc,curln,column);
	} else {
		killstk(curln,column,markp->markl,markp->markc);
	}
}

/* go to beginning of count line */

absgoto(count)
register int count;
/* Keywords: commands movement text-lines */

{
	move((count<nlines)?count:nlines, 0);
}

/* begin a C coment.  comment begins in COMCOL, if current position is
 * not column 0, otherwise begins in column 0.  the next newline will end
 * the coment */

cment()

/* Keywords: C-mode commands comments */

{
	
	if (column) {
		disup();
		if (mcol<comcol) {
			while (mcol<comcol) {
				put('	');
				disup();
			}
		} else {
			put (' ');
		}
	}
	putin ("\057* ");		/* it's a slash, for stupid compilers! */
	comln = curln;
}

/* adjust the indentation of the current line to be consistent with that
 * in the last line.  */

tabjust(lno)
register int lno;
/* Keywords: insertion C-mode commands:10 */
{
	register int lln;
	register int tabno;
	char *llp;
	int c;

	tabno = 0;
	for (lln = lno-1; lln > 0; lln--) {
		llp = mkline(lln);
		while (llp[tabno] == '	') ++tabno;
		if ((llp[tabno]!= EOL) && (llp[tabno+1] != '*')) {
			while ((c = llp[tabno])!=EOL) {
				switch(c) {
				case '{':
					++tabno;
					break;
				default:
					++llp;
				}
			}
			goto tabout;
		}
		tabno = 0;
	}
tabout:
	move(lno,0);
	while ((c = clptr[column]) == '	') {
		tabno--;
		column++;
	}
	if ((c == '}') && tabno) tabno--;
	if (tabno) {
		if (tabno>0) tabc(tabno,'	');
		else {
			bdel(-tabno);
			kpop();
			unpop(1);
		}
	} else { 
		sputl(curln,column,curln);
		move(curln,column);
	}
}

/* } handler, re-sets the indentation back one level */

cbrak(count,brace)

register int count;
register int brace;
/* Keywords: C-mode insertion commands */
{
	if (TABMD &&(RARE == 0) && column && (*(clptr+column-1) == '	')){
		bdel(1);
		kpop();
		unpop(1);
	};
	insertc(count,brace);
}


/* returns 1 if next line contains no non blank (or tab) characters */

isblank(line)

register int line;
/* Keywords: commands:10 line-representation:50 */
{
	register char *lp;

	if ((line <= nlines) && (ptrs[line] == 0)) return(1);
	lp = mkline(line);
	while (*lp!=EOL) {
		if ((bits[*lp++] & WHITE) == 0) return(0);
	}
	return(1);
}

/* change working dir */

cwd()

/* Keywords: commands directories unix-interface */
{
#ifndef PC
	char *np;
	np = expenv(getname("Directory? "));
	if (np) {
		if (SAVEMD) fsave(0);
		if (*np == 0) np = getenv("HOME"); /* null means home */
		if(chdir(np)) {
			IGNORE(error(WARN,errno,np));
			return(0);
		}
		return(1);
	}
	return(0);
#endif PC
}

stats()
{
/* Keywords: commands informational-displays statistics */
	
#ifdef COMPRESS
	extern long coutc;
#endif
	mtop();
	putout ("%d chars from terminal",ninch);
	putout ("%D calls to mputc",nmput);
	putout ("%D chars written by mputc",noutc);
#ifdef COMPRESS
	putout ("%D chars after compression",coutc);
#endif
	putout ("%d terminal writes", ntwrite);
	putout ("%d calls to makeline",nmkline);
	putout ("%d buffer reads",nbread);
	putout ("%d file writes",nbwrite);
	putout ("%d file seeks", nbseek);
	putout ("%d characters of buffer left", (NBLOCK*BLEN)-macptr);
	IGNORE(contin());
}

uline(count)

/* Keywords: word-oriented-commands commands underlining */
register int count;

{
	while (count--) {
		kmark();
		IGNORE(skipf(WRDSEP));	/* skip to begginning of word */
		move(kline,kcol);
		while (bits[*klptr] & WRDCHR) {
			put('_');
			put(CTRLH);
			forw(1);
		}
	}
}

infile(fn)

/* Keywords: commands:40 files keyboard-macros:10 command-line-processing:10 command-files */
register char *fn;
{
	if (pushin(expenv(fn))) {
		edit(1);
		inpop();
		return(1);
	} else return(0);
}


inpsh(arg)
int arg;
{
	register char *fp;
/* Keywords: commands files keyboard-macros:10 command-files */

	if (fp=getname("Input file? ")) {
		if (infile(fp)==0 )  {
			if (arg > 0) IGNORE(error(WARN,errno,fp));
			return(0);
		}
	}
	return(1);
}

/* send the contents of the buffer as mail */

/* mtch -- see if current line matches header */

mtch(cp)
register char *cp;
/* Keywords: mail-processing string-handling:20 comparison:40 */

{
	register char *cl;
	cl = clptr;
	while (*cp) if (*cp++ != *cl++) return(0);
	return(1);
}

mailit()

/* Keywords: mail-processing unix-interface commands */


{
	char cmdbuf[256];
	register char *mp;
	register char *mp1;
	int mailstat;
	char *mailcom;
#ifdef POSTHACK
	char *s;
	register int c;
	extern char *strrchr();
#endif	
	mailstat = 0;
#ifndef PC
#ifdef bsd
#define DEFMAIL "/usr/lib/sendmail -t"
#else
#define DEFMAIL "mail"
#endif
	if ((mailcom=getenv("MAILER"))==NULL) mailcom=DEFMAIL;
	
	mp = mstrcpy(cmdbuf,mailcom);

#ifdef POSTHACK
	/* find the start of the mail command name */
	if ((s = strrchr(mailcom, '/')) != NULL) {
		++s;
	}
	else {
		s = mailcom;
	}
	/* if this is post or Berkeley mail */
	if (streq(s, "post") || streq(s, "Mail")) {
		top();
		while (curln < 10 && *clptr != EOL) {
			
			/* if there is a subject line in the message */
			if (mtch("Subject: ")) {
				
				/* add -s 'subject' to the mail command */
				strcpy(mp, " -s '");
				mp += 5;
				mp1 = clptr + 9; /* skip "Subject: " */
				while ((c = *mp1) != EOL) {
					if (c == '\'') { /* ' becomes '\'' */
						*mp++ = '\'';
						*mp++ = '\\';
						*mp++ = '\'';
						*mp++ = '\'';
					}
					else {
						*mp++ = c;
					}
					++mp1;
				}
				*mp++ = '\'';
				*mp = 0;
				break;
			}
			move(curln+1,0);
		}
	}
	/* get the list of addressees */
#endif
	top();
	while (curln < 10 && *clptr != EOL) {
		if (mtch("TO: ") || mtch("To: ") || mtch("CC: ") || mtch("Cc: ")) {
			if (clptr[1] == 'O') clptr[1] = 'o';
			if (clptr[1] == 'C') clptr[1] = 'c';
			column = 3;
			if (((mp-cmdbuf)+leng(curln)-column) > 250) {
				mailstat += unx(cmdbuf,1); /* partial list */
				mp = mstrcpy(cmdbuf,mailcom);
				move(curln,column);
			}
			mp1 = clptr+column;
			while (*mp1 != EOL) *mp++ = *mp1++; /* copy mailing list */
			*mp = 0;
		}
		move(curln+1,0);
	}
	mp1 = cmdbuf+4;
	while (*mp1) if (*mp1++ != ' ') { /* Check for non-null destination list */
		
		mailstat += unx(cmdbuf,1);		/* send to mail command */
		return(mailstat == 0);
	}
	IGNORE(error (WARN,46));
#endif
	return(0);
}

/* contin -- ask user to continue */


contin()
{
/* Keywords: informational-displays:20 user-interface:10 prompting:40 */
	register int c;
	if (infrn < 0) return(1);	/* continue always in a macro */
	prompt1("Continue?");
 	c = getchar();
	prompt1("");
	if ((c == 'y') || (c == ' ') || (c == '\n') || (c == '\015')) return(1);
	if ('n' == c) return(0);
	if (c == CTRLZ) {
		gquit();
		return(1);
	}
	ungetch(c);
	return(0);
}

/* buffer length and current position */


/* If invoked from tty, prints buffer status info on the terminal.  If */
/* invoked from a macro, returns a value dependent on its argument */

#define VLIN 0				/* return line number */
#define VCOL 1				/* return column number */
#define VDLIN 2				/* display line */
#define VDCOL 3				/* display column */
#define VMINLN 4			/* First file line on screen */
#define VMAXLN 5			/* ditto last */
#define VWTOP 6				/* First screen line of window */
#define VWBOT 7				/* ditto last */

buflng(arg)

int arg;
{
/* Keywords: commands macro-programming:40 buffers statistics */
	
	long flng;
	long fpos;	
	char xbuf[128];
	extern int minln;
	extern int maxln;
	register int i;
	if (infrn < 0) {
				/* If from macro, return useful info */
		switch(arg) {
			
		case VCOL: return(column);
		case VLIN: return(curln);
		case VDCOL:
		case VDLIN:
			if (findline(curln) == 0) return(-1);
			mline=nln;
			if (findpos(clptr,column) == 0) return(-1);
			if (arg == VDLIN) return(nln-wbase);
			else return(mcol);
		case VMINLN: return(minln);
		case VMAXLN: return(maxln);
		case VWTOP: return(wbase);
		case VWBOT: return(SCRLINES-1);
		}
	}
	flng = 0L;
	for (i = 1; i <= nlines; i++) {
		if (i != 1) flng++;	/* linefeed from last line */
		if (i == curln) fpos = flng + column;
		flng += leng(i);
	}
	clptr=mkline(curln);
	seprintf(xbuf,"next char: %o, line: %d/%d:  pos:  %D/%D column: %d",
		clptr[column],curln,nlines-1,fpos,flng,column);
	prompt1(xbuf);
	return(0);
}

filler(arg)
/* Keywords: regions:50 text-filling commands */
int arg;
{
	register int i;
	register char *cp;
	register int j;
	int unp;
	
	unp = unstart();	
	if (arg != 1) {
					/* fill region only */
		register MARK *markp;
		markp = markptr(curbf);
		if ((i = markp->markl) > curln) fillreg(curln,i,1);
		else fillreg(i,curln,1);
	} else {
		undel();
		killstk(1,0,nlines,0);
		kpop();
		for (i = 1; i < nlines;i++ ) {
			cp = mkline(i);
			if ((*cp != '.') && (*cp != '\'') && (*cp != EOL)) {
				j = i;
				while (i < nlines) {
					cp = mkline(i);
					if ((*cp == '.') || (*cp == '\'') || (*cp == EOL)) break;
					i++;
				}
				fillreg(j,i,0);
				i = curln+1;
			}
		}
		unins(1,0);
	}
	unend(unp);
}

fillreg(i,j,u)
register int i;
register int j;
int u;
{
/* Keywords: commands:10 text-filling regions:20 undo:10 */

	int onlin,ofill;
	onlin = NLINS;
	ofill = FILLMD;
	FILLMD = NLINS = 1;	/* Turn on "rigid_newline" mode */
	move(i,0);
	if (u) {

		/* Make an undo record for this region */
		
		undel();
		killstk(i,0,j,0);
		kpop();
	}
	while (curln < j) {
		endl();
		do {
			i = curln;
			afsep(0,0);/* Note that we don't really put anything in */
			if (i != curln) j++;
		} while (i != curln);
		if (curln < j-1) {
			fdel(1);
			kpop();
			unpop(1);
			while ((clptr[column] == ' ') || (clptr[column] == '	')) {
				fdel(1);
				kpop();
				unpop(1);
			}
			put(' ');
			j--;
		} else break;
	}
	if (u) {
		move(j,0);
		unins(i,0);		/* Make a second undo record */
	}
	NLINS = onlin;	
	FILLMD = ofill;
}

/* macro the region */

macro(arg)
int arg;
{
/* Keywords: key-bindings memory-allocation:10 parsing symbol-handling commands macro-hooks:20 */

	register int mychar;	
	register int balance;
	register int machr;
	int oldpic;
	int compos;
	int hookp;
	int hash,ename;
#ifdef u370
	int beepbeep();
#else	
	extern int beep();
#endif
	
	oldpic=PICMODE;
	PICMODE=0;
	bot();
	back(2);
	if (clptr[column] != CTRLZ) {

/* File does not end in ^Z, Try to catch a potential disaster */
		
		error(WARN,80,fname());
		PICMODE=oldpic;
		return(0);
	}
	if (arg != 1) {
		for (mychar = 0; mychar < NCHARS; mychar++) {
			if (map_it[mychar] > ISIZE) {
				map_it[mychar] = 0;
			}
			if (mychar < NMAC) machash[mychar] = 0;
			if (mychar < NHOOKS) hooks[mychar] = 0;

		}
		macptr = 0;
		fbkno = 0;
	}
	top();
		
	while (curln<nlines) {

		balance = 0;
		machr = clptr[column]&0377;
		if (machr == ESC) machr = (clptr[++column]&0377)+0200;
		if (machr == CTRLX) {
			machr = (clptr[++column]&0377)+0400;
		}
		if (machr == CTRLZ) {
			hookp =  (clptr[++column]&0377);
			if (hookp > NHOOKS) {
				hookp = 0; /* Just ignore the bad ones */
				--column; 
			}
		}
		forw(1);
		if (macptr == 0) pshchr(0); /* Can't use the first location! */
		compos = macptr;
		pshchr(0);		/* Leave room for linkage chain */
		pshchr(0);
		hash = 0;
		ename = 1;
		if (clptr[column] == CTRLBACK) { /* if comment */
			pshchr(0);
			++column;
			while (clptr[column] != EOL) {
				if ((clptr[column] == ' ')||(clptr[column] == '	')) ename = 0;
				if (ename) hash += clptr[column];
				pshchr(clptr[column++]);
			}
			pshchr(0);
			forw(1);	/* skip newline */
		}
		hash %= NMAC;
		bbuf[0][compos] = machash[hash];
		bbuf[0][compos+1] = machash[hash]>>8;
		machash[hash] = compos;
		if (machr == CTRLZ) hooks[hookp] = macptr; /* Define hook */
		else {
					/* Define character command */

			map_it[machr] = macptr+ISIZE;
		}
		while (curln < nlines) {
			mychar = clptr[column]&0377;
			if (column == 0) {
				while ((mychar==' ') || (mychar=='	')) {
					column++;
					mychar = clptr[column]&0377;
				}
			}
			if (mychar != CTRLBACK) {
				if (mychar == MTA({)) balance++;
				else if (mychar == MTA(})) balance--;
				pshchr(mychar);
				forw(1);
				if ((mychar == CTRLZ) && (clptr[column] == EOL)) {
					if (balance) {
						error(WARN,47,curln);
					}
					goto macdone; /* end of macro definition */
				}
			} else {
				move(curln+1,0); /* skip comment */
			}
		}
		error (WARN,48);
		pshchr(CTRLG);		/* escape from bad macro */

macdone:	forw(1);		/* on to next macro */
	}
	PICMODE=oldpic;
	if (hooks[Load_Macro_Hook]) {
		hook(Load_Macro_Hook);
		hooks[Load_Macro_Hook] = 0;
	}
	return(1);
}

ldmac(arg)
int arg;
/* Keywords: commands files reading macro-programming:20 */
{
	register int oldbuf;
	register int tmpbuf;
	char *obname;
	int oldbin;	
	int err;
	
	if (arg>0) err = 1;
	else {
		err = 0;
		arg = 1;
	}
	obname = fname();		/* Old file name */
	oldbuf = curbf;
	chgbuf("...");			/* temporary buffer */
	strcpy(fname(),obname);		/* Restore file name */
#ifdef PC
	oldbin = BINMODE;
	BINMODE = 1;
#endif PC	
	if (fred(err)) {			/* read file */
		macro(arg);
		err = 1;
	} else {
		err = 0;
	}
#ifdef PC
	BINMODE=oldbin;
#endif PC
	tmpbuf = curbf;
	chbuf(oldbuf);
	klbfr(tmpbuf);			/* kill temp buffer */
	return(err);
}
/* macro programming commands */

/* these commands perform various utility functions for macro commands */

bfchr()					/* get a character from the buffer */
{
/* Keywords: macro-programming quoting character-at-a-time buffers:40 */
	return(clptr[column]&0377);	/* Trim sign bits! */
}

litchr()				/* literal character */

{
/* Keywords: macro-programming quoting */
	return(getchar());
}

compar(ctype)				/* general comparison */

/* comparison type definitions (pecular arangement for convenient */
/* specification via ^U */


#define CLE 0				/* <= */
#define CEQ 1				/* = */
#define CLS 2				/* < */
#define CGT 3				/* > */
#define CNE 4				/* != */
#define CGE 5				/* >= */
#define COR 6				/* OR */
#define CAND 7				/* AND */
#define CNOT 8				/* NOT */
#define CFALSE 9			/* FALSE */
#define CTRUE 10			/* TRUE */
#define CPLUS 11			/* arg1 + arg2 */
#define CMINUS 12			/* arg1 - arg2 */
#define CTIMES 13			/* arg1 * arg2 */
#define CDIV 14				/* arg1 / arg2 */
#define CREM 15				/* arg1 % arg2 */
#define CDTOS 16			/* arg1 convert to string on kill stack */
#define CSTOD 17			/* convert ks to decimal and return */
#define CBAND 18			/* Bitwise and */
#define CBOR 19				/* Bitwise or */
#define CXOR 20				/* Bitwise xor */
#define CUNGET 21			/* Unget character */
#define CPENDING 22			/* number of characters pending */

/* Keywords: macro-programming arithmetic:50 comparison:50 */

register int ctype;
{
	register int r1;
	register int r2;
	char *sp;
	char sbuf[20];
	int res;
	
	if (ctype == CFALSE) return(0);
	if (ctype == CTRUE) return(1);
	if (ctype == CSTOD) {
		sp = getname("Number?");
		nscan(sp,&res);
		return(res);
	}
	if (ctype == CPENDING) {
		ttfill();		/* Read any pending tty input */
		return(ttcnt);
	}
	r1 = edit(0);			/* first arg */

	if (ctype == CNOT) return(r1 == 0);
	if (ctype == CDTOS) {
		seprintf(sbuf,"%d",r1);
		stkstr(sbuf);
		return(r1);
	}
	if (ctype == CUNGET) {
		pushin(0);		/* Force to tty */
		ungetch(r1);
		inpop();
		return(1);
	}
	r2 = edit(0);			/* second arg */
	
	switch(ctype) {
		
	case CLE: return(r1<=r2);
	case CEQ: return(r1==r2);
	case CLS: return(r1<r2);
	case CGT: return(r1>r2);
	case CNE: return(r1!=r2);
	case CGE: return(r1>=r2);
	case CAND: return(r1 && r2);
	case COR: return(r1 || r2);
	case CPLUS: return(r1+r2);
	case CMINUS: return(r1-r2);
	case CTIMES: return (r1*r2);
	case CDIV: if (r2) return(r1/r2);
		else return(0);
	case CREM: if (r2) return(r1%r2);
		else return(0);
	case CBAND: return(r1&r2);
	case CBOR: return(r1|r2);
	case CXOR: return (r1^r2);
default:return(0);
	}
}


pscan(lev)				/* parenthesis scan */
/* Keywords: macro-programming parsing control-flow:50 */
register int lev;				/* returns when lev+paren level = 0 */
{
	register int c;
	register int ctlx;
	ctlx = 0;
	while (lev) {
		c = Mgetchar();
		if (c == CTRLX) {
			ctlx++;
			continue;
		}
		if (!ctlx && ((c == (CTRLQ)) || (c == MTA(q)) || (c == (CTRLQ+META)))) {
			c = Mgetchar();
			continue;
		}
		ctlx = 0;
		if (c == MTA({)) lev++;
		else if (c == MTA(})) lev--;
	}
}


cond()					/* conditional execute */
/* Keywords: control-flow parsing :5 conditionals macro-programming */
{
	register int c;
	
	MACEXIT;			/* Only in a macro */
	
	if ((c = Mgetchar()) != MTA({)) {
					/* must be M-{ */
		return(error(NORM,49));
	}
	while ((c = Mgetchar()) != MTA(})) {
		while ((c != MTA({)) && (c !=MTA(}))) c = Mgetchar();
		if (c == MTA(})) return(0); /* failed */
		if (edit(0)) {
			c = edit(1); /* scan command */
			pscan(1);	/* skip to end of cond */
			return(c);
		} else pscan(1);	/* skip to end of block */
	}
	return(0);			/* cond failed */
}

/* macro case statement:  syntax is ^x!M-{<expr>
 *					M-{<char><sequence>M-}
 *					...
 *				      M-}
 */


mcase()



/* Keywords: cases parsing:5 control-flow macro-programming */
{
	register int cchar;
	register int c;

	MACEXIT;	

	while ((cchar =Mgetchar()) != MTA({));
	cchar = edit(0);		/* match expression */
	
	while ((c = Mgetchar()) != MTA(})) {
		while ((c != MTA({)) && (c !=MTA(}))) c = Mgetchar();
		if (c == MTA(})) return(0); /* failed */
		c = Mgetchar();
		if ((c == cchar) || (c == 0377)) {
			c = edit(1); /* scan command */
			pscan(1);	/* skip to end of case */
			return(c);
		} else pscan(1);	/* skip to end of block */
	}
	return(0);			/* case failed */
}
	
/* mbegin -- begin block in macro */

mbegin()


/* Keywords: macro-programming parsing:20 control-flow */

{
	return(edit(1));		/* push command */
}

/* iteration handler */

iter()
{
/* Keywords: macro-programming control-flow parsing:10 loops */

	register int c;
	register char *savptr;
	
	MACEXIT;
	
	while ((c =Mgetchar()) != MTA({)) {
		if ((c != '	') && (c != ' ') && (c != EOL)) {
			return(error (NORM,50));
		}
	}
	savptr = inget();
	while (edit(0)) {		/* test condition */
		
		c = edit(1);		/* execute body */
		
		inset(savptr);		/* backup */
		
	}
	pscan(1);			/* skip body */
	return(c);
}

/* gparam -- get a parameter and push into the kill stack */

/* with an argument of 1, it takes the parameter in line, with */
/* other arguments, it takes the parameter from the tty, prompting with */
/* the in line parameter4 */


gparam(arg)				 /* get a parameter */

int arg;				/* with 1 arg, from macro text, else from tty */


/* Keywords: macro-programming string-handling:20 string-variables:50 filenames:10 prompting:40 reading:20 killstack:20 */
{
	char pbuf[128];
	register char *pb;
	register int pbch;
	
	MACEXIT;
	for (pb = pbuf;;) {
		*pb++ = pbch = Mgetchar();
		if (pbch == CTRLQ){
			--pb;
			*pb++ = Mgetchar();
		}
		if ((pbch == EOL)|| (pbch==CTRLZ)) break;
		if (pb>=(pbuf+128)) pb--; /* Truncate pb */
	}
	*(--pb) = 0;			/* pickup prompt message */
	return(gpm(arg,pbuf));		/* pickup up parameter */
}

/* gkarm -- like gparm, except input is from the kill stack. */


gkarm(arg)

/* Keywords: macro-programming string-handling:20 string-variables:50 filenames:10 prompting:40 reading:20 killstack:20 */
int arg;
{
	char *pb;
	
	pb = getname("");		/* pick up parameter */
	
	return(gpm(arg,pb));
}


gpm(arg,pbuf)
register int arg;
register char *pbuf;

/* Keywords: macro-programming string-handling:20 string-variables:50 filenames:10 prompting:40 reading:20 killstack:20 */
{
	register char *pb;
	char pmpt[128];
	int pbch;
	
	if (arg == 3) {
		eprintf("%s",pbuf);	/* Raw print, shouldn't mung screen */
		
		return(1);
	}
	if (arg != 1)	{
		pushin(NULL);
		if (arg <= 0) {
					/* single character */
			prompt1("%s",pbuf);
			if (arg < 0) {
				mgo(nln,ncol);
				pbch = getchar();
				unprompt();
			}
			inpop();
			return(pbch);
		}
		strcpy(pmpt,pbuf);	/* In case prompt is in pbuf already */
		pb = getname(pmpt);
		if (pb == NULL) pb = "";
		inpop();
	} else pb = pbuf;
	stkstr(pb);
	return(lng(pb));
}


char *
maclook(mp)
char *mp;

/* Keywords: macro-programming:20 symbol-handling macro-invocation global-variables:50 */
{
	int hash,ohash;
	register char *mp1,*mp2;
	
/* now look up macro name */
	
	mp1 = mp;
	hash = 0;
	while (*mp1) hash += *mp1++;
	hash = hash %NMAC;
	ohash = hash;
	hash = machash[hash];
	while (hash) {
		for (mp1=mp,mp2= &bbuf[0][hash+3]; *mp1++ == *mp2++; ) {
			if ((*mp1 == 0) && ((*mp2 == 0)||(*mp2 == ' ')|| (*mp2 == '	'))) {
				while (*mp2++);
				return(mp2);
			}
		}
		hash = (bbuf[0][hash]&0377) + (bbuf[0][hash+1] <<8);
	}
	return(NULL);
}

/* macro call by name */


macex(arg,ivchar)

int arg;
int ivchar;

/* Keywords: macro-invocation macro-programming modes:10 reading:40 */
{
	char mbuf[100];
	register char *mp1;
	register char *mp3;
	register int c;
	
	mp1 = mbuf;
	if (infrn < 0) {
		while ((c = Mgetchar()) != EOL) *mp1++ = c;
		*mp1 = 0;
		mp3 = mbuf;
	} else {
		mp3 = getname ("Macro name? ");
	}
	if (mp3 == NULL) return(0);	/* aborted */
	if (mp1 = maclook(mp3)) {
run:		return(xmac(mp1-bbuf[0],arg,ivchar));
	} else {
		if (AUTOLOAD && (infrn < 0)) {

/* Autoload mode,  If an undefined macro is called from a macro, it */
/* tries to load it from a standard library */

			char buf[128];
#ifdef PC
			seprintf (buf,"%s",mp3);
#else
			seprintf(buf,"~EMACS/macros/%s",mp3);
#endif
			stkstr(buf);
			if (ldmac(-1) &&(mp1 = maclook(mp3))) {
loaded:				prompt(ECHOL,"Autoloaded %s",buf);
				goto run;
			}
#ifdef PC
			seprintf(buf,"c:%s",mp3);
#else
			seprintf(buf,"$EMACS_LIB/%s",mp3);
#endif
			stkstr(buf);
			if (ldmac(-1) && (mp1 = maclook(mp3))) goto loaded;
		}
		error (WARN,51,mp3);
		return(NULL);
	}
}
hook(hookp)
int hookp;

/* Keywords: macro-programming macro-invoction:50 macro-hooks */
{
	register int oldhook;
	int result;
	char xfnbuf[FNLEN];
	
	strcpy(xfnbuf,fnbuf);		/* Preserve temp buffer, in case it's wiped by the macro */
	
	oldhook = hooks[hookp];
	hooks[hookp] = 0;			/* Prevent recursion */
	result = xmac (oldhook,0,0);
	hooks[hookp] = oldhook;
	strcpy(fnbuf,xfnbuf);
	return(result);
}
/* xmac -- execute a macro */


xmac(macp,arg,ivchar)

register int macp;
register int arg;
int ivchar;
{
/* Keywords: macro-programming macro-invocation local-variables:60 argument-processing:20 */
	
	int mvars[NMVAR];		/* macro variables */
	int *omarg;			/* old pointer to mvars */
	
	omarg = marg;
	marg = mvars;
	mvars[1]=arg;			/* record macro argument */
	mvars[0] = ivchar;		/* invoking character */
	pshmac(macp);
	if (etrace) putout("Entering macro: %s",hmap(macp));

	arg = edit(1);

	if (etrace) putout ("Exiting macro: %s",hmap(macp));
	marg = omarg;
	inpop();
	return(arg);
}

/* setvar -- set a local variable */

setvar(arg)
register int arg;
{
/* Keywords: assignment macro-programming local-variables */
	register int oldvar;

	if ((arg <NMVAR) && (arg >=0)) {
		oldvar = marg[arg];
		marg[arg] = edit(0); /* set new value */
		return(oldvar);
	} else {
		beep();
		return(0);
	}
}

/* getsharg -- get argument from shell command line */

/* with argument of 1, gets one argument and pops. */
/* with other argument, returns argument but does not pop */

/* Value returned is 1 unless there are no more command line options. */


getsharg(arg)
int arg;
{
/* Keywords: argument-processing:10 command-line-processing macro-programming */
	extern char **sharg;
	extern int nsharg;
	
	if (nsharg) {
		
		stkstr(*sharg);
		if (arg==1) {
			nsharg--;
			sharg++;
		}
		return(1);
	}
	return(0);
}


	
/* string operations */

/* compares same argument conventions as other comparisons */

#define CAPP 7				/* Arg 2 appended to arg1 */
#define CINDEX 8			/* index of arg 2 in arg1 */
#define SBINARY 8
#define CSUBSTR 9			/* string indexed by next 2 commands */
#define CLENGTH 10			/* String length */
#define SUNARY 10
#define CPTR 11
#define CSTRING 12
#define CPRINTF 13			/* formatted I/O */

cmpst(arg)

register int arg;

/* Keywords: macro-programming comparison string-variables string-handling:30 conversions:20 */
{
	char buf[256];
	
	register char *bp1;
	register char *bp2;
	char *bp3;
	int i,j;
	
	if (arg <= SUNARY)bp1 = getname("");		/* get first string */ 
	
	if (arg <= SBINARY) {
		strcpy(buf,bp1); 
		bp1 = buf;		
		bp2 = getname("");
	}
	
	if (arg <= CGE) {
		while ((*bp1 == *bp2) && *bp1) {
			bp1++;
			bp2++;
		}
	}
	switch (arg) {
		
	case CLE: return(*bp1<=*bp2);
	case CEQ: return(*bp1==*bp2);
	case CLS: return(*bp1<*bp2);
	case CGT: return(*bp1>*bp2);
	case CNE: return(*bp1!=*bp2);
	case CGE: return(*bp1>=*bp2);
	case CAPP:
		strcpy(bp2+lng(bp2),bp1);
		stkstr(bp2);
		return(1);
	case CINDEX:
		i = 0;
		bp3 = bp2;
		while (*(bp2=bp3+i)) {
			bp1 = buf;
			while (*bp1++ == *bp2++) {
				if (*bp1 == 0) return(i);
			}
			i++;
		}
		return(-1);
	case CSUBSTR: 
		i = edit(0);
		j = edit(0);
		bp1[j]=0;
		stkstr(bp1+i);
		return(1);
	case CLENGTH: return(lng(bp1));
	case CPTR: return(kgptr());
	case CSTRING:
		i = edit(0);
		kput(i);
		return(1);
	case CPRINTF:
					/* Printf like formatting. */
		{
			int pts[20],len;
			char buf2[256];
			bp1=bp2=getname(""); /* Format string */

/* First, collect all of the parameters */
			
			i = 0;
			while(*bp1) {
				if (bp1[0] == '%') {
					if (bp1[1] == '%') bp1++;
					else pts[i++] = kgptr(); /* Collect one */
				}
				bp1++;
			}
			strcpy(buf2,bp2);
			bp2 = buf2;
			bp1 = buf;
			while (*bp2) {
				if (*bp2 == '%') {
					bp2 = nscan(bp2+1,&j);
					if (*bp2 == '%') {
						*bp1++ = *bp2++;
						continue;
					}
					kput(pts[--i]);
					bp3 = getname(""); /* get the param */
					len = lng(bp3);
					
					switch(*bp2++) {
						
					case 'l': bp1 = mstrcpy(bp1,bp3);
						j = j-len;
						if (j>0) while (j--) *bp1++ = ' ';
						break;
					case 'r':
						j = j-len;
						if (j>0) while (j--) *bp1++ = ' ';
						 bp1 = mstrcpy(bp1,bp3);
						break;
					case 'c':
						len = (j-len);
						if (len>= 0) {
							j = len/2;
							len = len-j;
							while (j--) *bp1++ = ' ';
						}
						bp1 = mstrcpy(bp1,bp3);
						if (len> 0) while (len--) *bp1++ = ' ';
						break;
					}
				} else *bp1++ = *bp2++;
			}
			*bp1 = 0;
			stkstr(buf);
			return(1);
		}
	default:  return(0);
	}
}


/* Global variable -- global variables are dynamically bound by
 * names.  They are 2 or 4 bytes long and always stored in order of
 * lsB first.   */

glob(arg)
int arg;
{
/* Keywords: global-variables assignment:50 macro-programming */
	int size;
	char *name;
	char *where;
	int hash;
	
	
	if (name= getname ("Global variable name? ")) {
		if ((where = maclook(name)) == NULL) {
			if (arg == 0) return(0);
			where = name;
			hash = 0;
			while (*where) hash += *where++;
			hash = hash %NMAC;
			pshchr(machash[hash]);
			pshchr(machash[hash]>>8);
			machash[hash] = macptr-2;
			where = name;
			pshchr(0);
			while (*where) pshchr(*where++);
			pshchr(0);
			for (size=0; size<sizeof(size); size++) pshchr(0);
			where = maclook(name);
		}
		switch(arg) {

		case 0:
			return(1);
			break;
		case 1:
			hash = 0;
 			for (size=0; size<sizeof(size); size++) {
				hash+= ((*where++)&0377) << (size*8);
			}
			return(hash);
		case 2:
			hash = edit(0);
			for (size=0; size<sizeof(size); size++) {
				*where++ = hash;
				hash = hash >>8;
			}
			break;
		}
	}
	return(0);
}
/* envexp -- expand environment variables in a string */

/* expansion takes place in the caller's string buffer */
/* pointer is returned for convenience only */

char *pwpath = "/etc/passwd";

char *
expenv(str)
register char *str;
/* Keywords: environment-variables unix-interface user-interface:20 shell-escape:10 */
{
	char strtemp[128];
	char vartemp [64];
	register char *cp1;
	char *cp2;
	register int c;
	int oc;
	
	if (str == NULL) return(NULL);
		
	cp1 = strtemp;
	cp2 = str;
	while (*cp1++ = *str) {
		if ((*str== '`')||(*str=='*')||(*str=='{')||(*str=='[')||((*str++)=='?')) {
			seprintf(strtemp,"exec echo %s",cp2);
			unx(strtemp,4);			/* expand through the shell */
			return(fnbuf);
		}
	}
	cp1 = strtemp;
	str = fnbuf;			/* always copy back into file name */
	while (c = *cp1++) {
		if ((c == '$') || (c == '~')) {

/* Environment variable or `logdir` */
			
			oc = c;
			cp2 = vartemp;
			while (bits[c=((*cp1++)&0377)]&WRDCHR) {
				*cp2++ = c;
			}
			cp1--;		/* backspace pointer */
			*cp2 = 0;
			if (oc == '$') {
				cp2 = getenv(vartemp); /* environment variable */
			} else {
#ifndef PC
/* Home Directory */
			
				if (*vartemp == 0) {
					cp2 = getenv("HOME"); /* Bare ~ means home */
				} else if (streq(vartemp,"EMACS")) {
					cp2 = em_dir;
				} else if (streq(vartemp,"exptools") &&
					(cp2 = getenv("TOOLS")) && *cp2) {
					;
				} else {
					FILE pwfile[1];
					FILE *pwptr;
					char *vp;
					
					cp2 = NULL;
					pwptr = xopen(pwfile,pwpath,"r");
					if (pwptr) {
						while ((c = getc(pwptr))!= EOF) {
							if (c == '\n') {
								vp = vartemp;
/* Try to match the specified user ID */
								while (*vp++ == (c = getc(pwptr)));
								if ((vp[-1] == 0) && (c == ':')) {
/* Found it, now skip 4 colons, and copy next field into vartemp; */
									c = 0;
									while (c < 4) if (getc(pwptr) == ':') c++;
									cp2 = vartemp;
									while ((c = getc(pwptr)) != ':') *cp2++=c;
									*cp2++=0;
									cp2=vartemp;
									break; /* Break out of password scan */
								}
							}
						}
						mclose(pwptr);
					} else error(WARN,errno,pwpath);
				}
#endif PC
			}
			if (cp2 != NULL) {	
				str = mstrcpy(str,cp2);
			} else {
				*str++ = oc;
				str = mstrcpy(str,vartemp);
			}
		} else {
			*str++ = c;
		}
	}
	*str++ = 0;
	return(fnbuf);
}
/*
 * envget	takes a string and expands it into the string
 *		stored in the shell variable by the same name.
 *		If the shell variable did not exist, then envget
 *		returns NULL.
 */

envget()
/* Keywords: commands environment-variables */

{
	char * ptr;
	
	if (ptr = getenv(getname("Environment variable: ")))  {
		stkstr(ptr);
		return(1);
	}
	else {
		stkstr("");
		return(0);
	}
}

stopjob ()
{
	char x;
	clear();
	cook();
#if (defined (bsd) || defined (v8))
	kill(mypid,SIGSTOP);
#else
	eprintf ("Type ^Z to suspend emacs and <return> to continue");
	read(0,&x,1);
#endif
	uncook();
	clear();
}

#ifdef u370
beepbeep()
{
	extern int beep();
}
#endif