V9/cmd/emacs/emacs_buf.c

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

/* EMACS_MODES: c !fill */
#include <signal.h>
#include <sys/types.h>
#ifndef PC
#include <sys/stat.h>
#endif PC
#include "emacs_io.h"
#include "emacs_gb.h"
#include "emacs_buf.h"



/* leng * length of a line in the buffer */

leng(line)

register int line;

{
/* Keywords: line-representation:10 length */
	
	register char *lp;
	register char *olp;

	olp = lp = mkline(line);
	while (*lp++ != EOL);
	return(lp-olp-1);
}

allout ()				/* break all links */

{
	register unsigned *outp;
	register int xptr;
	register int q;
/* Keywords: buffer-representation:50 line-representation:5 paging buffer-changing:10 */
	
	for (outp = ptrs+nlines; outp > ptrs; outp--) {
		xptr = *outp;
		if (xptr&01) {
			q = xptr-1;
			*outp = ((bblock[q>>BSHIFT]<<BRSHIFT) + ((q&BMASK)>>LSSHIFT))<<1;
		}
	}
}
	

/* mkline -- make a pointer to the current line */

/* pages in the line if out of core, makes a null string for an empty
line */

char *

ckline(line,len)
int line;
int len;
/* Keywords: buffer-representation:50 line-representation:50 paging:20 buffer-allocation */

{
	register char *lp;
	char *lp1;
	register char *q;
	register int fileadd;
	int x;
	int s;
	
	lp = mkline(line);
	if (len <= (LSMALL*(*(lp-1))-2)) return(lp);
	
					/* current line is too small */
	

	if ((len+2) >= MAXEL) {
		error(WARN,33,line,MAXEL-1); /* report problems */
		return(NULL);		/* line too long, let caller handle */
	}
	s = (len+1+LSMALL)>>LSSHIFT;
	x = ptrs[line]-1;
	
	fileadd = (bblock[x>>BSHIFT]<<BRSHIFT) + ((x&BMASK)>>LSSHIFT);
	
	if ((fileadd + bbuf[0][x]) == BUFEND) {
		if ((fileadd & BRMASK) == ((fileadd+s-1)&BRMASK)) {
			
					/* current line extends in place */
			BUFEND = fileadd + s;
			bbuf [0] [x] = s;
			TRACE(TRCKEX);
			TRACE(line);
			TRACE(s);

			return (&bbuf[0][x+1]);
		}
	}
	
					/* must copy to new buffer */
	
	if ((BUFEND&BRMASK) != ((BUFEND-1+s)&BRMASK)) {
		BUFEND = (BUFEND&BRMASK)+BFACT;
	}
	ptrs[0] = BUFEND<<1;
	BUFEND += s;
	TRACE(TRCKCP);
	TRACE(line);
	TRACE(s);
	xline = line;			/* Mark the real line in ptrs[0] */
	holdin(0,line);			/* get both lines in memory */
	q = &(bbuf[0][ptrs[line]]);	/* old line buffer */
	lp = lp1 = &(bbuf[0][ptrs[0]]);		/* new buffer */
	*(lp-1) = s;
	s = (*(q-1)*LSMALL);		/* old length */
	while (s--) if ((*lp++ = *q++) == EOL) break;
	ptrs[line] = ptrs[0];
	ptrs[0] = 0;
	modify(line);
	return(lp1);
}

char *

mkl(line)
register int line;

{
	register char *p;
	register int i;
	int block;
	int newf;
/* Keywords: buffer-representation paging line-representation buffer-allocation */
	
	while (line>nlines) {
		sputl(nlines+1,0,line);
		if (line >= NPTRS) {
			if (!growbuf(line)) return(NULL);
		}
		nlines++;
		ptrs[nlines] = 0;
	}

	if (INMEM(line)) return (&bbuf[0][ptrs[line]]);

	/* no current line */

	nmkline++;			/* count for stats */

	TRACE(TRMAKE);
	TRACE(line);

	if (BUFILE == NULL) {
		BUFILE = gtemp(curbf); /* get a temp file */
		BUFEND = BFACT;		/* remember 0 == NULL */
	}
	if (ptrs[line] == 0) {
		ptrs[line] = (BUFEND++)<<1; /* assign new buffer */
		newf = 1;
		TRACE(TRMKEM);
	} else newf = 0;
	block = ((ptrs[line]))>>(BRSHIFT+1);

/* first see if desired block is in memory already */

	for (i = fbkno; i < NBLOCK; i++) if (block == bblock[i]) {
		goto bfill;
	}

					/* now try to swap in */
	
	for (i = fbkno; i <= NBLOCK+1; i++) {
		if (++nxtflsh >= NBLOCK) nxtflsh = fbkno;
		if (bstat[nxtflsh] == 0) {
			bgrab(nxtflsh,block); /* get block in (won't fail)*/
			i = nxtflsh;
			if (line)lowpt[i]=hipt[i]=line;
			else lowpt[i]=hipt[i]=xline;
			goto bfill;
		} else bflush(nxtflsh);	/* try to force out block */
	}
	
/* can't find a buffer to flush */

	error(FATAL,34);		/* File buffers broken */

	
bfill:
	p= &bbuf[i][LSMALL*((ptrs[line]>>1)&BRESID)+1];
	ptrs[line] = p-bbuf[0];
	if (newf) {
		*(p-1) = 1;		/* line size */
		*(p) = EOL;
		modify(line);		/* make sure line is flushed */
	}
	if (line==0) line = xline;
	if (line<lowpt[i])lowpt[i]=line;
	if (line > hipt[i])hipt[i] = line;
	return(p);
}


/* gtemp -- get a temp file, and unlink it */

gtemp(fn)
int fn;
{
/* Keywords: temporary-files filenames:50 */
	
	char tfile [64];
	register int tf;
#ifdef PC
retry:	seprintf(tfile,"%s:emt.%d",BTEMPATH,fn);
	tf = creat(tfile,6);
	if (tf < 0) {
		ctfile();
		goto retry;
	}
#else
	seprintf(tfile,"%s/em.%o.%o",BTEMPATH,fn,getpid());
	while ((tf = creat(tfile,0600)) < 0) {
		if ((errno != 4) && (errno != 23)) break;
	}
	close(tf);
	while ((tf = open(tfile,2)) < 0) {
		if ((errno != 4) && (errno != 23)) break;
	}
	if (tf < 0) {
		error(FATAL,errno,"temp file");
	}
#ifdef MINFILES
	if (fn == NBUF) unlink(tfile);	/* lose the file */
#else
	unlink(tfile);	/* lose the file */
#endif
#endif
	return(tf);
}

#ifdef PC
rmtemp()
{
/* Keywords: temporary-files filenames:20 */
	
	char tfile[64];
	int i;
	btmpfile[curbf] = BUFILE;		/* set up for current buffer */
	for (i = 0; i < NBUF; i++) if (btmpfile[i]) close(btmpfile[i]);
	if (kfile) close(kfile);
	for (i = 0; i <= NBUF; i++) {
		seprintf(tfile,"%s:emt.%d",BTEMPATH,i);
		unlink(tfile);
	}
}

/* Called on out of space errors, asks for file to delete and continues */

mkspace()
{
	char *cp;
	char filebuf[64];
	char tmp[128];
/* Keywords: PC-only temporary-files deletion:10 user-interface:50 */
	
	if (error(WARN,76,BTEMPATH) == 0)  {
		cp = getname("File to delete on temp file disk?");
		if (cp) {
			mstrcpy(tmp,fnbuf); /* Preserve fnbuf */
			seprintf(filebuf,"%s:%s",BTEMPATH,cp);
			if (unlink(filebuf) == 0) {
				mstrcpy(fnbuf,tmp);
				return(1);
			}
			mstrcpy(fnbuf,tmp);
		}
	}
	return(0);
}
#endif PC

#ifdef MINFILES
rmtemp()
{
/* Keywords: temporary-files filenames:20 */
	char tfile [64];
	register int fn;
	btmpfile[curbf] = BUFILE;		/* set up for current buffer */
	for (fn = 0; fn < NBUF; fn++) {
		if (btmpfile[fn]) {
			rftmp(fn);
		}
	}
	rftmp(NBUF);
}
rftmp(fn)
register int fn;
{
/* Keywords: temporary-files filenames:20 MINFILES-version */
	char tfile[64];
	seprintf(tfile,"%s/em.%o.%o",BTEMPATH,fn,getpid());
	unlink(tfile);
}

otemp(fn)
int fn;
{
/* Keywords: temporary-files filenames:20 MINFILES-version */
	char tfile [64];
	register int tf;
	seprintf(tfile,"%s/em.%o.%o",BTEMPATH,fn,getpid());
	while ((tf = open(tfile,2)) < 0) {
		if ((errno != 4) && (errno != 23)) break;
	}
	if (tf < 0) {
		error(FATAL,errno,"temp file");
	}
	return(tf);
}
#endif 

/* utility program to insure that both lines are in memory before proceeding */

holdin(line1,line2)

register int line1,line2;
/* Keywords: paging line-representation:10 */

{

	while (OUTMEM(line1) || OUTMEM(line2)) {
		 mkline(line1);
		 mkline(line2);
	}
}

/* growbuf -- grow the amount of buffer storage */
growbuf(iline)

int iline;
/* Keywords: buffer-representation memory-allocation */

{
	register unsigned *ndadd;
	register int line;

	
	line = (iline&BRKMSK)+01000;		/* round up to next multiple
					 * of 512 */
	
	ndadd = &ptrs[line];		/* new need address */
#ifdef PC
	if (line >= MPTRS) {
#else
	if (brk(ndadd) != 0) {
#endif PC
		line = iline+4;		/* try for less */
		ndadd = &ptrs[line];
#ifdef PC
		if (line >= MPTRS) {
#else
		if (brk(ndadd) != 0) {
#endif PC
			error(NORM,35); /* Too Many lines */
			return(0);
		}
	}
	NPTRS = line;
	return(1);

}



		
/* bgrab -- fill a specific buffer with a specific block */

bgrab(x,blkno)
/* Keywords: buffer-representation paging paging-I/O encryption:10 */

register int x;
int blkno;
{
	register int base;
	register unsigned *p;
	register unsigned *endp;
#ifndef pdp11
	unsigned bsblock;
#else
	unsigned short bsblock;
#endif
	int rstat;
	long seekpos;
	
	TRACE(TRBGRAB);
	TRACE(x);
	TRACE(blkno);
	if (bblock[x]) {		/* if block is occupied */
		if (bstat[x]) {
			if (bflush(x)== 0) return(0);
		}
		base = x*BLEN+1;
		bsblock = bblock[x]<<(1+BRSHIFT);
		if (hipt[x] > nlines) hipt[x]=nlines; /* Make sure it's in bounds! */
		if (lowpt[x] < 0) lowpt[x] = 0; /* likewise */
		endp = &ptrs[lowpt[x]];		
		for (p= &ptrs[hipt[x]]; p>=endp; p--) {
			if((*p &01) && (((*p - base)&(~BMASK)) == NULL)) {
				*p = bsblock + ((*p - base)>>(LSSHIFT-1));
			}
		}
			/* break all links to block */

 	}
	bblock[x] = blkno;
	if (blkno&& (blkno<=mostwrit)) {
		if (blkno != sblk) { /* if we must seek */
			TRACE(TRSEEK);
			TRACE(sblk);
			TRACE(blkno);
			seekpos = (long) BLEN * (long) blkno;
			nbseek++;
			lseek(BUFILE,seekpos,0);
		}
		nbread++;
		TRACE(TRREAD);
		TRACE(x);
		TRACE(blkno);
		while ((rstat =read(BUFILE,bbuf[x],BLEN)) != BLEN) {
			if (errno != 4) break;
		}
		if (rstat != BLEN) error(FATAL,errno,"reading buffer");
#ifdef CRYPTO
		if (bbuf[x][0]&0200) {

/* First byte of buffer is always a count, and thus shouldn't have the */
/* 0200 bit on.  This insures that enciphered bufferes are deciphered */

			cryptic(&bbuf[x][0]);
			bbuf[x][0] &= 0177; /* Make sure high order bit is off */
		}
#endif
		sblk = blkno+1;
	}
	return(1);
}
#ifdef CRYPTO
cryptic(cp)
register long *cp;			/* Types deliberately mismatched */
{
/* Keywords: buffer-representation encryption paging:10 */
	
	long x;
	register long *endp;
	
	endp = cp + BLEN/sizeof(x);
	while (cp < endp) *cp++ ^= bufkey;
}
#endif
/* bflush -- flush contents of a buffer */ 

bflush(x)
/* Keywords: buffer-representation paging paging-I/O encryption:10 */

register int x;
{
	
	long seekpos;
	if (bstat[x]) {
retry:
		if (sblk != bblock[x]) {
			seekpos = (long) bblock[x] * (long) BLEN;
			TRACE(TRSEEK);
			TRACE(sblk);
			TRACE(bblock[x]);
			nbseek++;
			lseek(BUFILE,seekpos,0);
		}
#ifdef CRYPTO
		if (crypt) {
			cryptic(&bbuf[x][0]);
			bbuf[x][0] |= 0200;
		}
#endif CRYPTO
		while(write(BUFILE,bbuf[x],BLEN) != BLEN) {
#ifdef PC
			if (mkspace()) {
				bblock[x] = -1; /* Don't know where we are */
				goto retry;
			}
			return(0);
#else
			if (errno != 4)	error(FATAL,errno,"buffer file");
#endif			
		}
#ifdef CRYPTO
		if (bbuf[x][0]&0200) {
			cryptic(&bbuf[x][0]);
			bbuf[x][0] &= 0177; /* Make sure high order bit is off */
		}
#endif CRYPTO
		nbwrite++;
		TRACE(TRWRIT);
		TRACE(x);
		TRACE(bblock[x]);
		sblk = bblock[x]+1;
		if (bblock[x] > mostwrit) mostwrit = bblock[x];
		bstat[x] = 0;
	}
	return(1);
}

/* initializes line buffer free list, ptrs array to empty */



bufinit()
/* Keywords: buffer-changing:10 buffer-representation buffer-allocation:50 */

{
	register int i;
	register unsigned *p;

/* now set up buffer stuff */


	for (p = &ptrs[NPTRS-1]; p >= ptrs; p--) { /* clear ptrs */
		*p = 0;
	}

	/* set up free buffer line list */

	for (i = fbkno; i < NBLOCK; i++) bstat[i] = bblock[i] = 0;
	nxtflsh= NBLOCK;			/* initial buffer to use */
	nlines = 1;
	mostwrit = 0;
	sblk = 0;
	curln = 1;
	column = 0;
	curblk = 0;
	if (BUFILE) BUFEND = BFACT;	/* re-use buffer */
}
#ifdef PC

/* 	change temp file -- change place where temp files are put. */


ctfile()
{
/* Keywords: PC-only buffer-representation temporary-files filenames:10 */

	char *cp;
	cp = getname("Tempfile Directory? ");
	if (cp) {
		*BTEMPATH = *cp;
	}
	if (mostwrit == 0) {
					/* Re-allocate current buffer */
		close(BUFILE);
		BUFILE = gtemp(curbf);
	}
}

#endif PC
/* wout -- write out file */

wout(name,aflag)
/* Keywords: writing buffer-representation:50 commands:10 PC-only:10 macro-hooks:10 */
/* Keywords: encryption:20 unix-interface:10 dired:20 user-interface:10 */

char *name;
int aflag;
{
	FILE outbuf[1];
	register FILE *outfile;
	register int i;
	int nosave;			/* return value from backup link */
	char backb[128];		/* buffer for name of backup file */
	char *backp;
	char *bp;
	register char *cp;
#ifndef PC
	struct stat inode;		/* status buffer */
#endif
	int status;
	int xstat;	
	
	if (hooks[Pre_Write_Hook]) {
		stkstr(name);
		if (hook(Pre_Write_Hook)==0) {
			unprompt();
			return(0);
		}
		retrvs(fnbuf,FNLEN);
		name=fnbuf;
	}

#ifdef DIRED
	if (diron) return(dclean());
#endif
	if (*name == NULL) return(0);
	bp = name;
	cp = backp = backb;
	while (*cp = *bp++) {
		if (*cp++ == '/') backp = cp;
	}
	strcpy(backp,"EMACS_save");
	nosave = 1;
	prompt1("Wait");
#ifndef PC
	mflush(stdout);
	status = stat(name,&inode);		/* get old file modes */
					/* don't care if file not there */
/*	if ((status==0) && (((inode.st_mode &0200) == 0) || (inode.st_uid != myuid))) {*/
	if (status == 0) {
		if (inode.st_mode & S_IFDIR)  {/* Writing a directory, yuck */
			error(WARN,81,name,name,filename);
			return(0);
		}
		if (access(name,02) != 0) {
			if (gyn("File not explicitly writeable, write anyway?")<=0) return(0);
		}
	}
#endif PC
	if ((aflag>1)||(aflag<-1)) {
		outfile = xopen (outbuf,name,"ba");
		if (status == 0) goto wfile1; /* if file was there */
	}
#ifndef PC
	if ((status == 0) && streq(filename,name) && 
	(mtime[bfnumb()]) && (mtime[bfnumb()] != inode.st_mtime)) {
			
		error(WARN,78,name);
		return(0);
	} 
	/* don't prompt if user wants file links saved */
	if (savelink) goto wfile;
	if ((status== 0) && (inode.st_nlink != 1)) {

		i = gyn("File %s is linked to another, write to both (y) or only to %s ?",name,name);
		if (i < 0) return(0);
		if (i > 0) goto wfile;
	}
	
	nosave = streq(name,backb);	/* see if we are trying to link backup */
	if (nosave==0) {
		nosave = link (name,backb);

		/* make backup link to file in case */
		/* we crash.  Old copy will be in  
		 * EMACS_save if crash occurs during write */
		if (nosave == 0) unlink(name);	/* remove old file */
	}
#endif PC
wfile:	outfile = xopen (outbuf,name,"bw");
wfile1:	if (outfile == NULL) {
		error(WARN,errno,name);
		return(0);
	}
	prompt1("Writing");
	mflush(stdout);

#ifdef CRYPTO
	if (crypt) {
		char crbuf[32];
		seprintf(crbuf,"crypt %s",cryptkey);
		splfile = outfile ->_frn;
		if (unx(crbuf,6)<0) return(0);
		outfile->_frn = splfile;
	}
#endif	

	for (i = 1; i <= nlines; i++) {
		if (i != 1) {
#ifdef PC
			if (BINMODE == 0) putc(CTRLM,outfile);
#endif PC
			putc('\n',outfile);
		}
		cp = mkline(i);
		while (*cp != EOL) {
			if (PICMODE && (*cp == ' ')) {
				register int x;
				for (x = 1; cp[x]==' '; x++);
				if (cp[x] == EOL) break;
				while (x--) {
					putc(*cp,outfile);
					cp++;
				}
			} else {
				putc(*cp,outfile);
				cp++;
			}
		}
	}
	if (EOFNL) {
		cp = mkline(nlines);
		if (*cp!=EOL) {
			putc('\n', outfile);
#ifdef PC
			if (BINMODE == 0) putc(CTRLM,outfile);
#endif PC
		}
	}
#ifdef PC
	if (BINMODE == 0) putc('\032',outfile);
	mclose(outfile);
#else
	if (mclose(outfile)) {		/* Error occurred during write */
					/* Try to put back old file */
		if (nosave==0) { /* error writing saved file */
			unlink(name);
			link(backb,name); /* Put everything back */
			unlink(backb);
			prompt1("No write: %s", name);
			return(0);
		}
		prompt1("File errors: %s", name);
		mtime[bfnumb()] = 0; 
		return(0);
	}

#ifdef CRYPTO
	if (crypt) wait(&xstat);	/* Eliminate zombies */
#endif
	if (nosave == 0) unlink(backb);  /* remove backup link */

	if (status == 0) {
		chmod(name,inode.st_mode);/* set modes right */
		chown(name,inode.st_uid,inode.st_gid); /* set owner and group */
	} else {
		chmod(name,0666 & ~mymask);/* Modes by umask */
	}
#endif PC
	if ((aflag<2)&&(aflag>-2)) {
		prompt1("Written: %s", name);
	}
	else prompt1("Appended: %s", name);
 	NSCHAR=0;
	if (aflag>0) {
		strcpy(filename,name);
		bufmod = 0;
	}
#ifndef PC
	if (streq(filename,name)) {
		
		if (stat(name,&inode) != 0) {
			error(WARN,errno,name);
		} else {
			mtime[bfnumb()] = inode.st_mtime;
		}
	}
#endif
	dispmod();
	move(curln,column);		/* Restore state information */
	return(1);
}

/* read in a file into the buffer */
/* re-initializes buffer first */


readin(fn,reinit)

char *fn;
int reinit;
{

/* Keywords: reading encryption:20 PC-only:10 dired:10 unix-interface:10 commands:10 macro-hooks:10 */

	FILE *filein;
	FILE finbuf[1];
#ifndef PC
	struct stat inode;
	int xstat;
#endif
#ifdef DIRED
	extern char dired_args[];
#endif

	if (fn[0] == 0) fn =  filename;			/* default to previous file */
	if (fn[0] == 0) return(0);		/* no default */

	if (VERBOSE) {
		prompt1 ("Wait");
		mflush(stdout);
	}
	if (hooks[Pre_Read_Hook]) {
		stkstr(fn);
		if (hook(Pre_Read_Hook) == 0) {
			unprompt();
			return(0);
		}
		retrvs(fnbuf,FNLEN);
		fn = fnbuf;
	}
	filein = xopen( finbuf,fn,"br");
	if (filein == NULL) {
		unprompt();
		if (reinit>0) error(WARN,errno,fn);
		if (filename[0] == 0) {
			strcpy(filename,fn); /* copy name */
			dispmod();
		}
		return(0);
	}
#ifndef PC
	if (fstat(filein->_frn,&inode) != 0) {
		error(WARN,errno,fn);
		return(0);
	}
	if (inode.st_size >= MAXFS) { /* Check for grossly huge file */
		error(WARN,77,fn);
		return(0);
	}
#ifndef DIRED
	if (inode.st_mode & S_IFDIR) {
					/* Reading a directory, check to be sure */
		if (error(WARN,82,fn)) return(0);
					/* go ahead and read it if the user wants to */
	}
#endif
#endif
#ifdef DIRED
	if (inode.st_mode & S_IFDIR) {
		char dircom[128];
		diron = 1;
		splfile = filein ->_frn;
		seprintf(dircom,"ls %s %s", dired_args,fn);
		if (unx(dircom,7) < 0) return(0);
		filein->_frn = splfile;
	} else diron = 0;
#endif
	if (VERBOSE) {
		prompt1("Reading");
		mflush(stdout);
	}
	if (reinit == -1) reinit = 1;
	if (reinit == 1) bufinit();
#ifdef CRYPTO
	if (crypt) {
		char crbuf[32];
		seprintf(crbuf,"crypt %s",cryptkey);
		splfile = filein->_frn;
		if (unx(crbuf ,7)<0) return(0);
		filein->_frn = splfile;
	}
#endif	
	readsub(filein,reinit,fn,0);		/* do the reading */
#ifdef CRYPTO
	if (crypt) wait(&xstat);			/* Eliminate zombies */
#endif	
#ifdef DIRED
	if (diron) wait(&xstat);	/* Eliminate zombies */
#endif	
#ifndef PC
	if (streq(filename,fn)) {
		struct stat ninode;
		mtime[bfnumb()] = inode.st_mtime;
		if (stat(fn,&ninode) != 0) {
			error(WARN,errno,fn);
		} else {
			if ((ninode.st_mtime != inode.st_mtime)||
			    (ninode.st_ino   != inode.st_ino))  {
				error(WARN,79,fn);
			}
		}
	}
#endif
	unprompt();
	ttfill();			/* try to read ahead*/
	if (hooks[Post_Read_Hook]) hook (Post_Read_Hook);
	return(1);
}


readsub(filein,reinit,fn,cpyflag)
/* Keywords: reading buffer-representation:5 unix-interface:20 dired:30 file-modes:10 */

int reinit;
#ifndef pdp11
register
#endif
int cpyflag;
register FILE *filein;
char *fn;
{
	register int c;
	int i;
	char fbuf[MAXEL];
	register char *fp;
	register char *endbuf;
	int warnl;
	
	i = curln;
	if (reinit != 1) {
		RARE = 1;			/* turn off trouble */
		while ((c = getc(filein)) != EOF) {
#ifdef PC
			if (BINMODE == 0){
				if (c == '\032') break;
				if (c == '\n') bdel(1); /* Wipe out spurious cr */
				
			}
#endif PC
			if (cpyflag) putchar(c); /* output character */
			put(c);
			if (c == EOL) clptr = mkline(curln);
		}
	} else {
		if (BUFILE == NULL) {
			BUFILE = gtemp(curbf); /* get a temp file */
		}
		BUFEND = BFACT;		/* remember 0 == NULL */
		fp = &fbuf[0];	
		endbuf = fbuf+MAXEL-2;
		warnl = -1;			/* last "line too long" */
		while ((c = getc(filein)) != EOF) {
#ifdef PC
			if (BINMODE == 0){
				if (c == '\032') break;
				if (c == '\n') fp--; /* Wipe out trailing CR */
			}
#endif PC
			if (cpyflag) putchar(c); /* echo to terminal */

			if (c == '\n') {
				*fp = EOL;
				if (addline(fbuf,fp-fbuf,i++)==0) goto eof;
				fp = &fbuf[0];
			} else {
#ifdef DIRED
				if (diron) while (fp-fbuf < 4) *fp++ = ' '; /* make room for markings */

#endif
				*fp++ = c;
				if (fp >= endbuf) {
					fp--;
					if (i == warnl) continue;
					warnl = i;
					if(error(NORM,33,i,MAXEL-1)) goto eof;
					continue;
				}
			}
		}
eof:		*fp = EOL;
		addline(fbuf,fp-fbuf,i);
		move(1,0);
		strncpy(filename,fn,FNLEN); /* Copy name written */
		bufmod = 0;		/* buffer up to date */
	}
	mclose(filein);
	if (cpyflag) mflush(stdout);	/* Force output to come out */
	NSCHAR = 0;
	bfmodes();	/* set buffer modes */
	fclear();	/* file is now bad */
	RARE = 0;	/* normal modes back on */
}

/* Express access to the buffer.  This code plunks in one line at
 * the end of the buffer, manipulating all appropriate data, much
 * swifter than the usual ckline-bgrab sequence.  What is assumed by
 * this function is that it is called after bufinit and no other
 * intervening buffer manipulations.  It leaves the ptrs array set
 * up with no lines in memory, but with the last n blocks of the
 * file in bbuf and properly initialized.   */




addline(cp,len,line)
register char *cp;
int len;
int line;
{
/* Keywords: buffer-representation:50 line-representation:20 reading:10 buffer-allocation:20 */

	register char *lp;
	register int x;
	register int s;

	if (line >= NPTRS) {
		if (!growbuf(line)) {
			error(NORM,35);
			return(0);
		}
	}
	nlines=line;
	
	s = (len+1+LSMALL)>>LSSHIFT;
	if (((BUFEND-1) & BRMASK) != ((BUFEND+s-1)&BRMASK)) {

/* Need to go into the next block */
		
		nxtflsh++;
		if (nxtflsh>=NBLOCK) nxtflsh=fbkno;
		x = nxtflsh;
		bflush(x);
		BUFEND=((BUFEND-1)&BRMASK)+BFACT;
		bstat[x]=1;
		lowpt[x]=hipt[x]=line;
		bblock[x]=(BUFEND>>BRSHIFT);
	}
	ptrs[nlines] = BUFEND<<1;
	lp = &bbuf[nxtflsh][((BUFEND&BRESID)<<LSSHIFT)];
	*lp++ = s;			/* save size */
	while ((*lp++ = *cp++) != EOL); /* copy the line */
	BUFEND += s;
	return(1);
}



/* findf -- go forward to find row and columns */

/* leaves kline, kcol pointing at the character count characters ahead
of curln, column */


findf(count)

register int count;
/* Keywords: buffer-representation:10 movement:20 commands:10 forwards */

{
	kmark();
	while (count--) {
		if (*klptr++ != EOL) kcol++;
		else {
			if (kline == nlines) return(0);
			kcol = 0;
			++kline;
			klptr = mkline(kline);
		}
	}
	return(1);
}

/* findb -- go back n chars */

/* leaves kline, kcol pointing at the character count characters before
the current character */
/* Keywords: buffer-representation:10 movement:20 commands:10 backwards */
findb(count)

register int count;

{
	kline = curln;
	kcol = column;
	
	while (count) {
		if (count > kcol) {
			count-=(kcol+1);	/*plus one for the nl */
			kcol = 0;
			if (kline == 1) return(0);
			kline--;
			kcol = leng(kline);
		} else {
			kcol -= count;
			return(1);
		}
	}
	return(1);
}


/* mark a line as modified (also re-displays mode line if necessary) */


modify(line)
/* Keywords: buffer-representation:10 dired:20 mode-line:10 deletion:10 insertion:10 */

register int line;

{
	
	if (INMEM(line)) {
		bstat[(ptrs[line])>>BSHIFT] = 1;
	}
#ifdef DIRED
	if (diron) {
		register char *lp;
		lp = mkline(line);
		if ((column > 4) && (column < 14)) {
			lp[1] = 'M';
		} else if ((column > 18) && (column < 36)) {
			lp[2] = 'O';
		}
		sputl(line,1,line);	/* refresh */
	}
#endif
	if (bufmod == 0) {
		bufmod++;
		dispmod();
	}
}

/* stack text segment on kill stack */

/* the kill stack holds up to NKILLP segments, or a total of KBSIZE
 * characters.  killstk adds the segment delimited by its arguments to
 * this stack */

killstk(a,b,c,d)
/* Keywords: killstack stacking picture-mode:10 deletion:10 */

register int a;
register int b;
int c;
int d;

{
	register char *ap;
	
	if (kbapp) {			/* if appending */
		kend--;			/* backspace over the \0 */
	} else {
		if (nkp == NKILLP)  {	/* no more kill pointers available */
			mvdown();
		}
		nkp++;
		kstk[nkp] = kend;		/* start of saved text */
	}
	kget(kend);

	if (PICMODE) {
		int x;
		if (b>d) {
			x=d;
			d=b;
			b=x;
		}
		while (a <= c) {
			ap = mkline(a);
			a++;
			for (x = 0; x < d; x++) {
				if (x<b) {
					if (*ap != EOL) ap++;
				} else {
					if (*ap != EOL) {
						kbuf[kptr++] = *ap++;
					} else {
						kbuf[kptr++] = ' ';
					}
					kbmod=1;
					if (kptr>=KBSIZE) knext();
				}        
			}         
			if (a<=c) {
				kbmod=1;
				kbuf[kptr++] = EOL;
				if (kptr>=KBSIZE) knext();
			}        
		}         
	} else {    
		ap = mkline(a);
		while ((a < c) || (b < d) ) {
			if ((kbuf[kptr] = ap[b++]) == EOL) {
				++a;     
				ap = mkline(a);
				b = 0;  
			}        
			kbmod = 1;
			if (++kptr>=KBSIZE) {
				knext();
			}        
		}         
	}          
	kbapp = 0;			/* don't turn on append for now */
	kbuf[kptr++] = 0;
	kbmod = 1; 
	if (kptr>=KBSIZE) {
		knext();  
	}          
	kend = (long)kptr + kbase;
}
stkstr(sp)
/* Keywords: killstack stacking macro-programming:50 */

register char *sp;
{
	register char c;
	
	if (kbapp) {			/* if appending */
		kend--;			/* backspace over the \0 */
	} else {
		if (nkp == NKILLP)  {	/* no more kill pointers available */
			mvdown();
		}
		nkp++;
		kstk[nkp] = kend;		/* start of saved text */
	}
	kget(kend);
	do {
		c = kbuf[kptr++] = *sp++;
		kbmod = 1;
		if (kptr>=KBSIZE) knext();
	} while (c);
	kend = (long)kptr + kbase;
	kbapp = 0;
}

kput(ptr)
unsigned int ptr;
{
/* Keywords: killstack stacking string-variables macro-programming:20 */
	
	long real_ptr;
	int x;

	if (sizeof(x) < 4) {		/* If int's are only 16 bits, adjust ptr */
		
		real_ptr = kend & 0177777;
		if (real_ptr > ptr) real_ptr = ((long) ptr) + (kend & 037777600000);
		else real_ptr = ((long) ptr) + (kend & 037777600000) -65536;
	} else real_ptr = ptr;

	if ((real_ptr < 0) || (real_ptr >= kend)) {
		error (WARN,82);
		real_ptr = 0;
	}
	if (nkp == NKILLP)  {	/* no more kill pointers available */
		mvdown();
	}
	nkp++;
	kstk[nkp] = real_ptr;		/* start of saved text */
}
kgptr()
{
/* Keywords: killstack stacking insertion:10 retrieval:10 string-variables:10 */
	if (nkp>-1) {
		nkp--;
		return(kstk[nkp+1]);
	} else {
		return(0);			/* Error, too many pops */
	}
}

	

/* kapp -- append to kill buffer */

kapp()
{
/* Keywords: commands killstack appending */
	
	kbapp = 2;			/* force next kill to append */
}

/*knext -- next kill buffer*/


knext()
{
	long nbase;
/* Keywords: killstack paging */
	
	nbase = kbase + (long) KBSIZE;
	if (nbase >= KBLIM) nbase = 0;
	kget(nbase);
}


/* kget get next kill buffer */

kget(nbase)
long nbase;
{
/* Keywords: killstack paging paging-I/O */
	
	register long xbase;
	int st;
	
#ifdef	univac						/* MZ */
	xbase = nbase & 0xffffffe00;	/* Univac uses one's compliment. */
#else					/* MZ */
	xbase = nbase &( (long) -KBSIZE);
#endif					/* MZ */

	if (xbase == kbase) {
		kptr = nbase - kbase;
		return;
	}
	if (kfile == 0) {
		kfile = gtemp(NBUF);
	}
	if (kbmod) {
retry:		lseek(kfile,kbase,0);
		while(write(kfile,kbuf,KBSIZE) != KBSIZE) {
#ifdef PC
			if (mkspace()) goto retry;
			break;
#else
			if (errno != 4)	error(FATAL,errno,"kill buffer");
#endif
		}
		if (kbase >= kbwrt) kbwrt = kbase+KBSIZE;
		kbmod = 0;
	}
	if (nbase < kbwrt) {
		lseek(kfile,xbase,0);
		while((st=read(kfile,kbuf,KBSIZE) != KBSIZE)) {
			if (errno != 4) {
				error(FATAL,errno,"reading kill buffer");
				return;
			}
		}
	}
	kbase = xbase;
	kptr = nbase-xbase;
	return;
}

/* mvdown pushes the bottom text segment off of the kill stack */



mvdown()		/* move down kill stack */
/* Keywords: killstack stacking */

{
	register int i;
	for (i = 0; i <= nkp; i++) kstk[i] = kstk[i+1];
	nkp--;
}

/* retrv retrieves the top text segment from the kill stack and inserts
  * it into the buffer at the current location */


retrv()	/* retrieve from kill stack */
/* Keywords: commands killstack picture-mode:20 insertion:10 retrieval */

{
	register char c;
	register int oldcol,oldln;
	
	if (nkp <0) return(0);		/* nothing in the kill stack */
	RARE = 1;			/* turn off trouble */
	oldln = curln;
	oldcol = column;
	kget(kstk[nkp]);
	while (c= kbuf[kptr]) {
		if (PICMODE) {
			if (c==EOL) {
				move(++curln,0);
				lext(curln,oldcol);
			} else {
				insertc(1,c);
			}
		} else {
			if(put(c)==0) break;
		}
		if (++kptr >= KBSIZE) {
			knext();
		}
	}
	RARE = 0;			/* normal modes back on */
	unins(oldln,oldcol);
	return(1);				/* done */
}

retrvs(strp,mc)			/* retrieve from kill stack */
/* Keywords: commands killstack macro-programming retrieval */

register char *strp;
register int mc;
{
	register int nc;

	nc = 0;


	kget(kstk[nkp]);
	while (*strp++ = kbuf[kptr]) {
		if (++nc >= mc) {
			*(--strp) = 0; /* finish off string */
			error(WARN,63); /* file name too big */
			break;		/* break loop */
		}
		if (++kptr >= KBSIZE) {
			knext();
		}
	}
	kpop();				/* pop the kill stack */
	if (etrace) {
		putout("Retrieved %s from kill stack",strp-nc-1);
	}
	return(nc);				/* done */
}

/* kpop pops the top item off of the kill stack */
kpop()
/* Keywords: commands killstack stacking popping */

{
	if (nkp>-1) {
		nkp--;
		return(1);
	} else return(0);
}

/* fsave saves the buffer if it has been modified since last read or write */


fsave(arg)
register int arg;
{
/* Keywords: commands buffers files writing:50 saving dired:10 */
	
	if ((arg == 0) && (streq(bbfname[curbf],".exec"))) return(0);

#ifdef DIRED
	if (diron) return(dclean());
#endif
	if (READONLY) {
		if (arg) error(WARN,71);
		return(0);
	}
	if (bufmod && (*filename== 0)) {
		if (infrn == 0) return(fright(1)); /* no file name */
		error(WARN,65,bufname); /* Don't read the file name in a macro */
		return(0);
	}

	if (bufmod) {
		return(wout(filename,1));
	} else {
		if (arg == 0) return(1);
		prompt1("File %s is up to date",filename);
		return(1);
	}
}

/* fname, bname, and modded return the current file, buffer, and buffer
 * modified flags */


char *
fname()
{
	return(filename);
}
char *
bname()
{
	return(bufname);
}
int
bfnumb()
{
	return(curbf);
}
int
modded()
{
	if (bufmod) return(1);
	else return(0);
}

/* chbuf moves to a new buffer denoted by the buffer number of its
 * argument */

/* each buffer has a filename, buffername, modified flag, current
 * position, and size,  The ptrs array of a non-active buffer is stored in
 * the temp file for the buffer.
 */

chbuf(x)

register int x;
/* Keywords: buffers:50 buffer-representation buffer-changing unix-interface macro-hooks:10 */

{
	register int i;
	register int rx;
	unsigned cpointer;

	
	if (SAVEMD) IGNORE(fsave(0)); /* save buffer */
	if (hooks[Leave_Buffer_Hook]) hook(Leave_Buffer_Hook);
	for (i = fbkno; i < NBLOCK; i++) bflush(i);
	allout();
	NSCHAR = 0;
	btmpfile[curbf] = BUFILE;
	btmpfree[curbf] = BUFEND;
	bcurln[curbf] = curln;
	bcolumn[curbf] = column;
	if (BUFILE) {
		while (1) {
			lseek(BUFILE,(long)((long) BUFEND * (long) LSMALL),0);
			rx = write(BUFILE,(char *)ptrs,(nlines+1)*(sizeof cpointer));
			if (rx == (nlines+1)*(sizeof cpointer)) break;
#ifdef PC
			mkspace();
#endif
		}
	}
	bnlines[curbf] = nlines;
#ifdef MINFILES
	if (BUFILE) close(BUFILE);
	if (btmpfile[x]) btmpfile[x] = otemp(x);
#endif	
	curbf = x;
	bufinit();
	
	BUFEND = btmpfree[x];
	mostwrit = ((BUFEND-1)/BFACT);
	BUFILE = btmpfile[x];
	nlines = bnlines[x];
	if (BUFILE) {
		while (1) {
			lseek(BUFILE, (long)((long) BUFEND * (long) LSMALL),  0);
			rx = read(BUFILE,(char *)ptrs, (nlines+1)*(sizeof cpointer));
			if (rx ==(nlines+1)*(sizeof cpointer)) break;
			if (errno != 4) error (FATAL,errno,"Reading line pointers");
		}
	}
	
	bfmodes();			/* set buffer modes */
	move(bcurln[x],bcolumn[x]);
	if (bnlines[x] == 0) bufmod = 0; /* EMPTY BUFFERS CAN'T HAVE BEEN MODIFIED!!!! */
	if (hooks[Enter_Buffer_Hook]) hook(Enter_Buffer_Hook);
}

/* chgbuf changes buffers by name.  If no buffer of the right name
 * exists, one is made */


chgbuf(sp)
/* Keywords: buffers commands:10 buffer-representation:20 */

char *sp;

{
	int i;
	i = finbuf(sp,1);
	if (i >= 0) {
		chbuf(i);
		return(1);
	}
	return(0);
}


finbuf(sp,crflg)
/* Keywords: commands:20 buffers:50 files:50 reading:20 buffer-changing */

char *sp;
int crflg;
{
	register int ep = -1;
	register int i;
	
	if (*sp == 0) {
		error(WARN,36);
		return(-1);
		}
	for (i = 0; i < NBUF; i++) {
		if (streq(sp,bbfname[i])) {
			return(i);
		}
		if ((ep < 0) && (bbfname[i] [0] == 0)) ep = i;
	}
	i = aint(sp);			/* try numeric buffer name */
	if ((i < NBUF) && (i >= 0)) {
		if (bbfname[i] [0] == 0) {
			inibuf(i);
			seprintf(bbfname[i],"%d",i); /* name buffer */
		}
		return(i);
	}
	if (crflg == 0) return(-1);	/* Don't create new buffer */
	if (crflg < 0) {		/* Complain about buffer */
		error(WARN,39,sp);
		return(-1);
	}
	
/* Buffer does not now exist, create it */
	
	if (ep>=0) {
		inibuf(ep);
		if (streq(sp,"...")) seprintf(bbfname[ep],"%d",ep);
		else strcpy(bbfname[ep],sp);
		return(ep);
	} else {
		error(WARN,37,NBUF);
		return(-1);
	}
}

/* inibuf -- initialize buffer data */

inibuf(ep)
register int ep;				/* buffer number  */
/* Keywords: buffer-representation buffers commands:10 */

{
	bbfname[ep] [0] = 0;
	btmpfree[ep] = 0;
	bcurln[ep] = bcolumn[ep] = 0;
	bnlines[ep] = 0;
	bbfmod[ep] = 0;
	bfilname[ep] [0] = 0;
	mtime[ep] = 0;
}

/* edbuf is the find file command, it asks for a filename, finds a
 * buffer containing the file if it exists, otherwise it makes a new buffer
 * to hold the file */

edbuf(arg)
/* Keywords: commands buffers buffer-changing reading:10 files dired:20 */

int arg;
{
	register int i;
	int retv;
	int exists;
	char *sp;
#ifdef DIRED
	char nbuf[256];
#endif
	exists = 0;
	sp = expenv(getname("Filename to Find? "));
	if (sp == NULL) return(0);
#ifdef DIRED
	if (*sp != '/') {
		catstr(nbuf,filename,sp); /* relative path */
		if ((nbuf[0] == '.') && (nbuf[1] == '/')) sp = nbuf+2;
		else sp = nbuf;
	}
#endif
	for (i = 0; i < NBUF; i++) {
		if (streq(bfilname[i], sp)) {
			chbuf(i);
			if (arg > 1) return(readin(sp,1));
			return(1);
		}
		if (streq(bbfname[i], sp)) {
			exists = 1;
		}
	}
	if (exists) retv = chgbuf("...");
	else retv = chgbuf(sp);
	if (retv) retv=readin(sp,arg);
	return(retv);
}

/* cpbuf is the change buffer command, it prompts for a buffer name and
 * mmoves to  it unless the name is '*', in which case a list of active
 * buffers is displayed */

cpbuf(arg)
/* Keywords: commands buffer-changing files:10 */
int arg;
{
	int i;
	i = fndbuf(arg);
	if (arg && (i >= 0)) chbuf(i);
	return(i>= 0);
}
buflist()				/* list active buffers */
/* Keywords: commands buffers user-interface informational-displays */

{
	register int i;
	char c;
	
	mtop();
	putout("Buffers used:");
	putout ("");
	for(i = 0; i < NBUF; i++) {
		if (bbfname[i] [0] ) {
			if (i == curbf) c = '*';
			else c = ' ';
			putout ("%c (%d) %s  %c  %s",
			c,i,bbfname[i],mdchar[bbfmod[i]], bfilname[i]);
		}
	}
	putout (endput);
	return(contin());
}


/* fndbuf finds/creates a buffer with a given name */


fndbuf(crflg)
int crflg;
{
/* Keywords: commands:20 buffers files:20 windows:10 */
	

	register char *bn;
	
again:	bn = getname("Buffer Name? ");
	
	if (bn == NULL) return(-1);
	if ((*bn == 0) || (*bn == '*')) {
		if(buflist()) return(-1);
		else goto again;
	}
	return(finbuf(bn,crflg));
}	

/* bufmove -- pipe text from one buffer to another */

/* sends the text in the region to a specified buffer */

bfsend(arg)

int arg;
{
	register int i;
	register int j;
	
/* Keywords: commands buffers regions insertion:20 buffer-changing:50 */
	
	i = fndbuf(1);
	if (i >= 0) {
		pickup(arg);		/* pick up the region */
		j = curbf;
		chbuf(i);
		if (i == procbuf) {
			bot(); /* Force insertion to the bottom */
			mark(curbf); /* Force mark to end of buffer */
		}
		retrv();
		kpop();
#ifndef PC
		if (i == procbuf) {
			exch(curbf);
			while (curln < nlines) {
				sendproc (mkline(curln)+column,leng(curln)+1-column);
				curln++;
				column=0;
			}
			sendproc (mkline(curln)+column,leng(curln)-column);
			bot();
		}
#endif
		chbuf(j);		/* back to old buffer */
	}
}

/* rnbuf -- rename buffer or buffer file */


rnbuf(arg)

int arg;
/* Keywords: commands buffers:50 files:50 naming */

{
	register char *sp;
	int bn;
	
	sp = expenv(getname("New Name? "));
	if ((sp== NULL) || ((arg == 1) && (*sp == NULL))) return;
	if (arg == 1) {			/* change buffer name */
		bn = finbuf(sp,0);
		if ((bn >= 0) && (bn != curbf)) {
					/* (allow change to current name */
			error(WARN,38,sp); /* Buffer name in use */
			return;
		}
		strcpy(bbfname[curbf],sp);
	} else {
		strcpy(bfilname[curbf],sp);
		mtime[curbf]=0;		/* Buffer wasn't read from this file */

	}
	dispmod();
}

/* rmbuf prompts for a buffer name and removes the buffer */


rmbuf()
/* Keywords: buffers commands deletion */

{
	register int i;
	
	if ((i = fndbuf(-1)) >= 0) {
		klbfr(i);
		return(1);
	} 
	return(0);
}
klbfr(i)

register int i;
/* Keywords: commands:10 buffers buffer-representation:20 deletion unix-interface:10 shell-escape:20 */

{
	if (i == curbf) {
		IGNORE(error (WARN,40));
		return;
	}
#ifndef PC
	if (i == procbuf) flushproc();
#endif
	if (i == windbuf()) onewind();		/* killing other window */

	if (btmpfile[i]) {
#ifdef MINFILES
		rftmp(i);
#else
		close(btmpfile[i]);
#endif
		btmpfile[i] = 0;
	}
	inibuf(i);
}

/* clean up unwritten buffers.  bclean checks for unwritten buffers, and
 * if any are found, writes them out if the user wishes */

bclean()
/* Keywords: dired:20 saving:70 exit-processing files:30 user-interface:10 */

{
	register int i;
	
	bbfmod[curbf] = bufmod;
	bnlines[curbf] = nlines;
#ifdef DIRED
	if (diron) fsave(0);
#endif	
	for (i = 0; i < NBUF; i++) {
		if ((bbfmod[i]) && (bbfname[i] [0]) &&
			(bnlines[i] > 1) &&
			(READONLY == 0) &&
			(streq(bbfname[i],".exec") == 0)){


			if (SAVEMD) {
				chbuf(i);
				fsave(0);
				
			} else {
				switch (gyn("buffer %s modified since last write to file %s, write?",
					&(bbfname[i][0]),&(bfilname[i][0]))) {
				case 0:
					break; /* no  */
				case 1:
					/* yes answer */
					chbuf(i);
					if(fsave(1)<= 0) return(-1);
					break;
				case -1:
				default:
					unprompt(); /* clean up msg */
					return(-1);
				}
			}
		}
	}
	return(0);
}

crash(arg)					/* handle crashes */

int arg;				/* sometimes is reason for crash */
{
/* Keywords: internal-errors saving unix-interface:30 files:20 */
#ifndef PC	
	register int i;
	register char *home;
	register int x = 0;

	signal(SIGHUP,SIG_IGN);
	signal(SIGINT,SIG_IGN);		/* go into our shell */

	if (arg == SIGHUP) {
		no_io = 1;	/* Make sure we do no I/O */
		close(0);
		close(1);
		close(2);
		open("/dev/null",2);
		dup(0);
		dup(0);
	}
	if (crashes++) eabort(0);	/* one crash per customer */
	home = getenv("HOME");

	bbfmod[curbf] = bufmod;

	for (i = 0; i < NBUF; i++) {
		if ((bbfmod[i]) && (bbfname[i] [0])){
			seprintf(bfilname[i],"%s/emacs%d",home,i);
			chbuf(i);
			mtime[curbf] = 0; /* Don't complain about previous file! */
			IGNORE(fsave(0));
			x++;		/* flag that we saved one */
		}
	}
	if (x) {
		USILENT++;
		infrn = -1;		/* Make sure that unx doesn't ask questions */
		unx("echo $LOGTTY your emacs buffers are in $HOME/emacs[0-9]* | mail $LOGNAME &",0);
	}
#endif
	quit();
}

/* collect -- garbage collection of buffer file */

collect()

/* Keywords: buffer-allocation buffer-representation killstack:20 */
{
	int oldcol;
	int oldln;
	int onlns;
	
	prompt1("Garbage collecting buffer");
	mflush(stdout);
	
	oldln = curln;
	oldcol = column;
	
	top();
	onlns = kline = nlines;
	kcol = leng(kline);
	tkill();			/* kill all text into kill buffer */
	
	bufinit();			/* reinit buffer storage */
	top();
	retrv();
	if (onlns != curln) {
		error(WARN,75);		/* buffer too large for emacs */
	}
	kpop();
	unpop(2);			/* Ignore for undo */
	move(oldln,oldcol);
	unprompt();
}

/* pshchr -- push a character into the macro buffer */

pshchr(ch)

register int ch;
/* Keywords: memory-allocation:10 macro-programming:10 paging:20 */
/* Keywords: buffer-allocation:20 buffer-representation:20 */
{
	if (macptr >= fbkno*BLEN) {
		if ((fbkno+2<NBLOCK) && bgrab(fbkno++,0)){
			/* grab another buffer */
			if (nxtflsh < fbkno) nxtflsh = fbkno;
			clptr=mkline(curln); /* Restore line pointer */
		} else error(FATAL,41); /* too many macros */
	}
	bbuf[0][macptr++] = ch;
}

pbfname()				/* put buffer name in kill stack*/
{
/* Keywords: commands macro-programming naming buffers */
	stkstr(bname());
}
pfnname()				/* put file name in kill stack */
/* Keywords: commands macro-programming naming files */
{
	stkstr(fname());
}

pvname(count)				/* put version in kill stack */
int count;
{
/* Keywords: commands macro-programming naming versions */
	if (count) stkstr(version);
	else stkstr(serial);
}

recurse(arg)				/* call emacs recursively */
int arg;
/* Keywords: user-interface:10 macro-programming:90 key-bindings:10 mode-line:5 recursive-editing commands:50 */
{
	long svkst[NKILLP+1];		/* kill buffer save area */
	int svnkp;
	register int i,c;
	char *omyname;
	char nmbuf[64];
	int eresult;
	

	if (arg == 0) {
		register char *map;
		map = getname(""); /* Termination map */
		pushin(NULL);		/* back to the tty */
		while (1) {
			disup();
			c = (getchar()&0177);

/* Map is a bit map of 4 bit bytes, each represented as a hex digit */
			
			i = map[c>>2];
			if (i>'9') i = 9+(i&017);
			else i = i - '0';
			if (8 & (i<<(c&3))) put(c);
			else {
				inpop();
				return(c);
			}
		}
	}
	for (i = 0; i <= NKILLP; i++) svkst[i] = kstk[i];
	svnkp = nkp;
	omyname = myname;
	pushin(NULL);		/* back to the tty */
	seprintf(nmbuf,"%s*",myname);
	myname = nmbuf;
	dispmod();
	eresult = edit(1);
	myname = omyname; /* pop name */
	dispmod();
	inpop();			/* pop input */
	for (i = 0; i <= NKILLP; i++) kstk[i] = svkst[i];
	nkp = svnkp;
	return(eresult);
}

kdup(arg)				/* duplicate argument */

int arg;				/* level to duplicate */
/* Keywords: commands macro-programming killstack string-variables:20 stacking */
{
	register long dupkp;
	
	if (arg > nkp) return(0);
	dupkp = kstk[nkp-arg];
	if (nkp == NKILLP) mvdown(); /* make room */
	kstk[++nkp] = dupkp;
	return(1);
}

kflush(count)				/* flush kill stack */

register int count;
/* Keywords: commands macro-programming killstack string-variables:20 popping */
{
	while (count--) {
		if (kpop() == 0) return(0);
	}
	return(1);
}

kexch(count)				/* exchange kill stack */
/* Keywords: commands macro-programming killstack string-variables:20 */
{
	register long dupkp;
	
	if (count<(nkp+1)) {
		dupkp = kstk[nkp];
		kstk[nkp] = kstk[nkp-count];
		kstk[nkp-count] = dupkp;
		return(1);
	} else return(0);
}

/* unmod -- mark or change buffer modified flag */

unmod(arg)
/* Keywords: mode-line:20 buffers:80 file-modes:10 files:20 macro-programming:50 */
{
	if (arg == 1) bufmod = 0;
	else if (arg > 1) bufmod = 1;
	dispmod();
	return(bufmod);
}

#ifdef PC
#define KBDFILE "c:emk"
#define KBDMODE 1
#else
#define KBDFILE "$HOME/.emacs_kbd"
#define KBDMODE 0666
#endif
strtkbd()
{
/* Keywords: commands macro-programming:20 filenames:10 unix-interface:40 keyboard-macros */
	if (kbdfile) endkbd();
	kbdfile = creat(expenv(KBDFILE),KBDMODE);
	return(kbdfile);
}
endkbd()
{
/* Keywords: commands macro-programming:20 filenames:10 unix-interface:40 keyboard-macros */
	if (kbdfile) close(kbdfile);
	kbdfile=0;
}
exkbd(arg)
register int arg;
/* Keywords: commands macro-programming:20 filenames:10 unix-interface:40 keyboard-macros */
{
	while (arg-- > 0) infile(KBDFILE);
}
setkey()
/* Keywords: commands files file-modes:50 encryption */
{
#ifdef CRYPTO
	char *kp;
	kp = getname("Encryption Key? ");
	if (kp && *kp) {
		strcpy(cryptkey,kp);
		crypt = 1;
	} else crypt = 0;
#else
	beep();
#endif
}
#ifndef PC
access(path, amode)
char *path;
int amode;
/* Keywords: file-modes files reading:20 writing:20 unix-interface */
{
	struct stat stb;
	register int uid;

	/*** This nonsense would not be necessary if ***/
	/*** the saccess in sys2.c would do the check based on ***/
	/*** the effective uid and gid instead of the real ones ***/

	if( stat(path, &stb) < 0 ) {
		return(-1);
	}

	if( (uid=geteuid())==0 || amode==0 ) {
		/*** super user or existence check only ***/

		return(0);
	}

	if( uid == stb.st_uid ) {
		/*** uid's match ***/

		amode <<= 6;
	} else {
		if( getegid() == stb.st_gid ) {
			/*** gid's match ***/

			amode <<= 3;
		}
	}

	if( (stb.st_mode&amode) == amode ) {
		return(0);
	}
	return(-1);
}

#endif PC


undoit(n,doit)

/* Keywords: undo insertion:50 deletion:50 killstack:20 */

int n;
int doit;
{
	int unline;
	int uncol;
	long unparm;
	int untype;
	

	uncol = undostack[--n] & (MAXEL-1);
	unline = undostack[n]>>MAXELSH;
	move(unline,uncol);
	unparm = undostack[--n]>>UNDSHIFT;
	untype = undostack[n] & UNDMASK;

	switch(untype) {
		
	case UNINS:
		if (doit) {
			kline = unparm >>MAXELSH;
			kcol = unparm & (MAXEL-1);
			tkill();
			kpop();
		}
		break;
	case UNBAD:			/* Multiple undo that exceeds limits */
	
		if (error(WARN,85)) return(n);

	case UNMUL:			/* Multi-segment undo */
		while (unparm>0) {
			untype = undoit(n,doit);
			uncol = n-untype;
			if (uncol<0) uncol += NUNDO;
			unparm -= uncol/2;
			n = untype;
		}
		break;
	case UNDEL:
		if (doit) {
			if (nkp == NKILLP)  {	/* no more kill pointers available */
			mvdown();
			}
			nkp++;
			kstk[nkp] = unparm;
			retrv();
		}
		break;
	}
	if (n<= 0) n = NUNDO;
	return(n);
}

unadd()
{
/* Keywords: undo stacking */
	
	undostack[undop++] = ((long) curln) * MAXEL + column;
}

unins(stline,stcol)

/* Keywords: undo insertion stacking */

{
	long n;
	if (undop >= NUNDO) {
		undop = 0;
		unseg++;
	}
	n = ((long) stline) * MAXEL + stcol;
	undostack[undop++] = UNINS+ (n<<UNDSHIFT);
	unadd();
}

undel()

/* Keywords: undo deletion stacking */

{
	if (undop >= NUNDO) {
		undop = 0;
		unseg++;
	}
	undostack[undop++] = UNDEL+ (kend<<UNDSHIFT);
	unadd();
}

unstart()
{
	return (undop+unseg*NUNDO);
}
unend(unp)
register int unp;
{
	int untype;
	
	unp = (undop+unseg*NUNDO)-unp;
	if (unp >= NUNDO/2) {
		untype= UNBAD;
		unp = NUNDO/2-2;
	} else untype = UNMUL;
	unp = unp / 2;
	if (unp == 1) return;		/* No point in storing extra indirection */
	if (undop >= NUNDO) undop = 0;
	undostack[undop++] = untype + (unp<<UNDSHIFT);
	unadd();
}

undo(arg)

/* Keywords: commands undo */

{
	int undp;
	int unp;
	
	unp = unstart();			/* Set up for undoing undo */
	undp = undop;
	while (arg--) {
/* Only the last undo really undoes anything */
		
		undp = undoit(undp,(arg == 0));
	}
	unend(unp);
}
unpop(n)
{
	undop -= 2*n;
	if (undop < 0) undop += NUNDO;
}