4.3BSD/usr/contrib/jove/insert.c

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

/*************************************************************************
 * This program is copyright (C) 1985, 1986 by Jonathan Payne.  It is    *
 * provided to you without charge for use only on a licensed Unix        *
 * system.  You may copy JOVE provided that this notice is included with *
 * the copy.  You may not sell copies of this program or versions        *
 * modified for use on microcomputer systems, unless the copies are      *
 * included with a Unix system distribution and the source is provided.  *
 *************************************************************************/

#include "jove.h"

#include "ctype.h"

/* Make a newline after AFTER in buffer BUF, UNLESS after is 0,
   in which case we insert the newline before after. */

Line *
listput(buf, after)
register Buffer	*buf;
register Line	*after;
{
	register Line	*newline = nbufline();

	if (after == 0) {	/* Before the first line */
		newline->l_next = buf->b_first;
		newline->l_prev = 0;
		buf->b_first = newline;
	} else {
		newline->l_prev = after;
		newline->l_next = after->l_next;
		after->l_next = newline;
	}
	if (newline->l_next)
		newline->l_next->l_prev = newline;
	else
		if (buf)
			buf->b_last = newline;
	if (buf && buf->b_dot == 0)
		buf->b_dot = newline;
	return newline;
}	

/* Divide the current line and move the current line to the next one */

LineInsert()
{
	register int	num = exp;
	char	newline[LBSIZE];
	register Line	*newdot,
			*olddot;
	int	oldchar;

	exp = 1;
	olddot = curline;
	oldchar = curchar;

	newdot = curline;
	while (--num >= 0) {
		newdot = listput(curbuf, newdot);
		SavLine(newdot, NullStr);
	}

	modify();
	if (curchar != 0) {
		strcpy(newline, &linebuf[curchar]);
		linebuf[curchar] = '\0';	/* Shorten this line */
		SavLine(curline, linebuf);
		strcpy(linebuf, newline);
	} else {	/* Redisplay optimization */
		newdot->l_dline = curline->l_dline;
		SavLine(curline, NullStr);
	}

	makedirty(curline);
	curline = newdot;
	curchar = 0;
	makedirty(curline);
	IFixMarks(olddot, oldchar, curline, curchar);
}	

n_indent(goal)
register int	goal;
{
	if (goal < 0)
		return;
	DoTimes(Insert('\t'), (goal / tabstop));
	if (goal % tabstop)
		DoTimes(Insert(' '), (goal % tabstop));
	exp_p = 0;
	exp = 1;
}

SelfInsert()
{
#ifdef ABBREV
	if (MinorMode(Abbrev) && !ismword(LastKeyStruck) &&
	    !bolp() && ismword(linebuf[curchar - 1]))
		AbbrevExpand();
#endif
	if (MinorMode(OverWrite)) {
		register int	num,
				i;

		for (i = 0, num = exp, exp = 1; i < num; i++) {
			int	pos = calc_pos(linebuf, curchar);

			if (!eolp()) {
				if (linebuf[curchar] == '\t') {
					if ((pos + 1) == ((pos + tabstop) - (pos % tabstop)))
						DelNChar();
				} else
					DelNChar();
			}
			Insert(LastKeyStruck);
		}
	} else
		Insert(LastKeyStruck);

	if (MinorMode(Fill) && (curchar >= RMargin ||
			       (calc_pos(linebuf, curchar) >= RMargin)))
		DoJustify(curline, 0, curline,
			  curchar + strlen(&linebuf[curchar]), 1, LMargin);
}

Insert(c)
{
	if (exp <= 0)
		return;
	modify();
	makedirty(curline);
	ins_c(c, linebuf, curchar, exp, LBSIZE);
	IFixMarks(curline, curchar, curline, curchar + exp);
	curchar += exp;
}	

/* Tab in to the right place for C mode */

Tab()
{
#ifdef LISP
	if (MajorMode(LISPMODE)) {
		Mark	*m = bolp() ? 0 : MakeMark(curline, curchar, FLOATER);

		Bol();
		DelWtSpace();
		(void) lisp_indent();
		if (m) {
			ToMark(m);
			DelMark(m);
		}
		if (bolp())
			ToIndent();
		return;
	}
#endif
	if (MajorMode(CMODE) && strlen(linebuf) == 0)
		(void) c_indent(CIndIncrmt);
	else
		SelfInsert();
}

QuotChar()
{
	int	c;
	extern int	alarmed;	/* If waitfor had to wait. */

	c = waitchar();
	if (alarmed)
		message(key_strokes);
	if (c == CTL(J))
		LineInsert();
	else if (c != CTL(@))
		Insert(c);
}

/* Insert the paren.  If in C mode and c is a '}' then insert the
   '}' in the "right" place for C indentation; that is indented 
   the same amount as the matching '{' is indented. */

int	PDelay = 5,	/* 1/2 a second */
	CIndIncrmt = 8;

DoParen()
{
	Bufpos	*bp = (Bufpos *) -1;
	int	nx,
		c = LastKeyStruck;

	if (!isclosep(c)) {
		SelfInsert();
		return;
	}

	if (MajorMode(CMODE) && c == '}' && blnkp(linebuf)) {
		DelWtSpace();
		bp = c_indent(0);
	}
#ifdef LISP
	if (MajorMode(LISPMODE) && c == ')' && blnkp(linebuf)) {
		DelWtSpace();
		bp = lisp_indent();
	}
#endif
	SelfInsert();
	if (MinorMode(ShowMatch) && !charp() && !in_macro()) {
		BackChar();	/* Back onto the ')' */
		if ((int) bp == -1)
			bp = m_paren(c, BACKWARD, NO, YES);
		ForChar();
		if (bp != 0) {
			nx = in_window(curwind, bp->p_line);
			if (nx != -1) {		/* is visible */
				Bufpos	b;

				DOTsave(&b);
				SetDot(bp);
				(void) SitFor(PDelay);
				SetDot(&b);
			} else
				s_mess("%s", lcontents(bp->p_line));
		}
		mp_error();	/* display error message */
	}
}

LineAI()
{
	DoNewline(TRUE);
}

Newline()
{
	DoNewline(MinorMode(Indent));
}	

DoNewline(indentp)
{
	Bufpos	save;
	int	indent;

	/* first we calculate the indent of the current line */
	DOTsave(&save);
	ToIndent();
	indent = calc_pos(linebuf, curchar);
	SetDot(&save);

	/* If there is more than 2 blank lines in a row then don't make
	   a newline, just move down one. */

#ifdef ABBREV
	if (MinorMode(Abbrev) && !ismword(LastKeyStruck) &&
	    !bolp() && ismword(linebuf[curchar - 1]))
		AbbrevExpand();
#endif
#ifdef LISP
	if (MajorMode(LISPMODE))
		DelWtSpace();
#endif
	else if (blnkp(linebuf))
		DelWtSpace();
		
	if (exp == 1 && eolp() && TwoBlank())
		SetLine(curline->l_next);
	else
		LineInsert();

	if (indentp)
#ifdef LISP
	    if (MajorMode(LISPMODE))
		(void) lisp_indent();
	    else
#endif
		n_indent((LMargin == 0) ? indent : LMargin);
}

ins_str(str, ok_nl)
register char	*str;
{
	register char	c;
	Bufpos	save;
	int	llen;

	DOTsave(&save);
	llen = strlen(linebuf);
	while (c = *str++) {
		if (c == '\n' || (ok_nl && llen >= LBSIZE - 2)) {
			IFixMarks(save.p_line, save.p_char, curline, curchar);
			modify();
			makedirty(curline);
			LineInsert();
			DOTsave(&save);
			llen = strlen(linebuf);
		}
		if (c != '\n') {
			ins_c(c, linebuf, curchar++, 1, LBSIZE);
			llen++;
		}
	}
	IFixMarks(save.p_line, save.p_char, curline, curchar);
	modify();
	makedirty(curline);
}

OpenLine()
{
	Bufpos	dot;

	DOTsave(&dot);
	LineInsert();	/* Open the lines... */
	SetDot(&dot);
}

/* Take the region FLINE/FCHAR to TLINE/TCHAR and insert it at
   ATLINE/ATCHAR in WHATBUF. */

Bufpos *
DoYank(fline, fchar, tline, tchar, atline, atchar, whatbuf)
Line	*fline,
	*tline,
	*atline;
Buffer	*whatbuf;
{
	register Line	*newline;
	static Bufpos	bp;
	char	save[LBSIZE],
		buf[LBSIZE];
	Line	*startline = atline;
	int	startchar = atchar;

	lsave();
	if (whatbuf)
		modify();
	(void) ltobuf(atline, genbuf);
	strcpy(save, &genbuf[atchar]);

	(void) ltobuf(fline, buf);
	if (fline == tline)
		buf[tchar] = '\0';

	linecopy(genbuf, atchar, &buf[fchar]);
	atline->l_dline = putline(genbuf);
	makedirty(atline);

	fline = fline->l_next;
	while (fline != tline->l_next) {
		newline = listput(whatbuf, atline);
		newline->l_dline = fline->l_dline;
		makedirty(newline);
		fline = fline->l_next;
		atline = newline;
		atchar = 0;
	}

	(void) getline(atline->l_dline, genbuf);
	atchar += tchar;
	linecopy(genbuf, atchar, save);
	atline->l_dline = putline(genbuf);
	makedirty(atline);
	IFixMarks(startline, startchar, atline, atchar);
	bp.p_line = atline;
	bp.p_char = atchar;
	this_cmd = YANKCMD;
	getDOT();			/* Whatever used to be in linebuf */
	return &bp;
}

YankPop()
{
	Line	*line,
		*last;
	Mark	*mp = CurMark();
	Bufpos	*dot;
	int	dir = -1;	/* Direction to rotate the ring */

	if (last_cmd != YANKCMD)
		complain("Yank something first!");

	lfreelist(reg_delete(mp->m_line, mp->m_char, curline, curchar));

	/* Now must find a recently killed region. */

	if (exp < 0)
		dir = 1;

	killptr += dir;
	for (;;) {
		if (killptr < 0)
			killptr = NUMKILLS - 1;
		else if (killptr >= NUMKILLS)
			killptr = 0;
		if (killbuf[killptr])
			break;
		killptr += dir;
	}

	this_cmd = YANKCMD;

	line = killbuf[killptr];
	last = lastline(line);
	dot = DoYank(line, 0, last, length(last), curline, curchar, curbuf);
	MarkSet(CurMark(), curline, curchar);
	SetDot(dot);
}

/* This is an attempt to reduce the amount of memory taken up by each line.
   Without this each malloc of a line uses sizeof (line) + sizeof(HEADER)
   where line is 3 words and HEADER is 1 word.
   This is going to allocate memory in chucks of CHUNKSIZE * sizeof (line)
   and divide each chuck into lineS.  A line is free in a chunk when its
   line->l_dline == 0, so freeline sets dline to 0. */

#define CHUNKSIZE	300

struct chunk {
	int	c_nlines;	/* Number of lines in this chunk (so they
				   don't all have to be CHUNKSIZE long). */
	Line	*c_block;	/* Chunk of memory */
	struct chunk	*c_nextfree;	/* Next chunk of lines */
};

static struct chunk	*fchunk = 0;
static Line	*ffline = 0;	/* First free line */

freeline(line)
register Line	*line;
{
	line->l_dline = 0;
	line->l_next = ffline;
	if (ffline)
		ffline->l_prev = line;
	line->l_prev = 0;
	ffline = line;
}

lfreelist(first)
register Line	*first;
{
	if (first)
		lfreereg(first, lastline(first));
}

/* Append region from line1 to line2 onto the free list of lines */

lfreereg(line1, line2)
register Line	*line1,
		*line2;
{
	register Line	*next,
			*last = line2->l_next;

	while (line1 != last) {
		next = line1->l_next;
		freeline(line1);
		line1 = next;
	}
}

static
newchunk()
{
	register Line	*newline;
	register int	i;
	struct chunk	*f;
	int	nlines = CHUNKSIZE;

	f = (struct chunk *) emalloc(sizeof (struct chunk));
	if (f == 0)
		return 0;

	if ((f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines))) == 0) {
		while (nlines > 0) {
			f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines));
			if (f->c_block != 0)
				break;
			nlines /= 2;
		}
	}

	if (nlines <= 0)
		return 0;

	f->c_nlines = nlines;
	for (i = 0, newline = f->c_block; i < nlines; newline++, i++)
		freeline(newline);
	f->c_nextfree = fchunk;
	fchunk = f;
	return 1;
}

/* New BUFfer LINE */

Line *
nbufline()
{
	register Line	*newline;

	if (ffline == 0)	/* No free list */
		if (newchunk() == 0)
			complain("[Out of lines] ");
	newline = ffline;
	ffline = ffline->l_next;
	if (ffline)
		ffline->l_prev = 0;
	return newline;
}

/* Remove the free lines, in chunk c, from the free list because they are
   no longer free. */

static
remfreelines(c)
register struct chunk	*c;
{
	register Line	*lp;
	register int	i;

	for (lp = c->c_block, i = 0; i < c->c_nlines; i++, lp++) {
		if (lp->l_prev)
			lp->l_prev->l_next = lp->l_next;
		else
			ffline = lp->l_next;
		if (lp->l_next)
			lp->l_next->l_prev = lp->l_prev;
	}
}

/* This is used to garbage collect the chunks of lines when malloc fails
   and we are NOT looking for a new buffer line.  This goes through each
   chunk, and if every line in a given chunk is not allocated, the entire
   chunk is `free'd by "free()". */

GCchunks()
{
	register struct chunk	*cp;
	struct chunk	*prev = 0,
			*next = 0;
	register int	i;
	register Line	*newline;

 	for (cp = fchunk; cp != 0; cp = next) {
		for (i = 0, newline = cp->c_block; i < cp->c_nlines; newline++, i++)
			if (newline->l_dline != 0)
				break;

 		next = cp->c_nextfree;

		if (i == cp->c_nlines) {		/* Unlink it!!! */
			if (prev)
				prev->c_nextfree = cp->c_nextfree;
			else
				fchunk = cp->c_nextfree;
			remfreelines(cp);
			free((char *) cp->c_block);
			free((char *) cp);
		} else
			prev = cp;
	}
}

#ifdef LISP

/* Grind S-Expr */

GSexpr()
{
	Bufpos	dot,
		end;

	if (linebuf[curchar] != '(')
		complain((char *) 0);
	DOTsave(&dot);
	FSexpr();
	DOTsave(&end);
	exp = 1;
	SetDot(&dot);
	for (;;) {
		if (curline == end.p_line)
			break;
		line_move(FORWARD, NO);
		if (!blnkp(linebuf)) {
			DelWtSpace();
			(void) lisp_indent();
		}
	}
	SetDot(&dot);
}

/* lisp_indent() indents a new line in Lisp Mode, according to where
   the matching close-paren would go if we typed that (sort of). */

Bufpos *
lisp_indent()
{
	Bufpos	*bp,
		savedot;
	int	goal;

	bp = m_paren(')', BACKWARD, NO, YES);

	if (bp == 0)
		return 0;

	/*
	 * Otherwise, we indent to the first argument of
	 * the current s-expression.  This is done by
	 * starting at the matching paren, skipping
	 * to a word (atom), skipping over it, and
	 * skipping to the next one.
	 *
	 * We want to end up
	 *
	 *	(atom atom atom ...
	 *	      ^ here.
	 */

	DOTsave(&savedot);
	SetDot(bp);
	DoTimes(ForChar(), 1);
	if (linebuf[curchar] != '(') {
		static char	*specials[] = {
			"def",
			"let",
			"lambda",
			"fluid-let",
			"macro",
			"lexpr",
			"nlambda",
			"dolist",
			"caseq",
			"selectq",
			"while",
			"prog",
			0
		};
		int	i = 0;

		while (specials[i]) {
			char	*cp1 = specials[i],
				*cp2 = &linebuf[curchar];
			int	n = strlen(cp1);

			while (--n >= 0)
				if (Upper(*cp1++) != Upper(*cp2++))
					break;
			if (n < 0)
				break;	/* Matched. */
			i++;
		}
		if (specials[i] == 0) {
			if (index(&linebuf[curchar], ' ') != 0) {
			    WITH_TABLE(curbuf->b_major)
				ForWord();
			    END_TABLE();
				while (linebuf[curchar] == ' ')
					curchar++;
			}
		} else
			curchar++;
	}
	goal = calc_pos(linebuf, curchar);
	SetDot(&savedot);
	n_indent(goal);

	return bp;
}
#endif LISP