2.9BSD/usr/contrib/jove/jove_insert.c

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

/*
   Jonathan Payne at Lincoln-Sudbury Regional High School 5/25/83
  
   Insert routines: the routine to Yank from the kill buffer
   and to insert lines, and characters into the buffer.  */

#include "jove.h"

/* Make a newline after `after' or course in `buf' */

LINE *
listput(buf, after)
BUFFER	*buf;
LINE	*after;
{
	LINE	*newline = nbufline();

	if (after == 0) {	/* First line in this list */
		buf->b_zero = buf->b_dol = buf->b_dot = newline;
		newline->l_next = newline->l_prev = 0;
		return newline;
	}
	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_dol = newline;
	return newline;
}	

/* Global variables aren't that bad.  There is a point where one
 * can go too far in trying to eliminate global variables on a 
 * principle.  After all this isn't LISP or anything like that.
 */

LineInsert()
{
	register int	num = exp;
	char	newline[LBSIZE];
	LINE	*newdot,
		*olddot = curline;
	int	oldchar = curchar;
	int	atbegin = (!firstp(curline) && bolp());

	exp = 1;
	if (atbegin)	/* This is mostly to make redisplay seem smart
			   but it also decreases the amount of copying
			   from one buffer to another */
		BackChar();
	strcpy(newline, &linebuf[curchar]);

	newdot = curline;
	while (num--) {
		newdot = listput(curbuf, newdot);	/* Put after newdot */
		newdot->l_dline = putline("") | DIRTY;
	}
	linebuf[curchar] = '\0';	/* Shorten this line */
	SavLine(curline, linebuf);
	makedirty(curline);
	curline = newdot;
	curchar = 0;
	strcpy(linebuf, newline);
	makedirty(curline);
	if (atbegin)
		ForChar();
	SetModified(curbuf);
	IFixMarks(olddot, oldchar, curline, curchar);
}	

LineAI()
{
	LineInsert();
	whitesp(getline(curline->l_prev->l_dline, genbuf), linebuf);
}

whitesp(from, to)
register char	*from,
		*to;
{
	char	c;
	int	oldchar = curchar;

	while ((c = *from++) && (c == ' ' || c == '\t'))
		insert(c, to, curchar++, 1, LBSIZE);
	SetModified(curbuf);
	IFixMarks(curline, oldchar, curline, curchar);
}

len_error(flag)
{
	char	*mesg = "line too long";

	(flag == COMPLAIN) ? complain(mesg) : error(mesg);
}

/* Insert num number of c's at offset atchar in a linebuf of LBSIZE */

insert(c, buf, atchar, num, max)
char	c, *buf;
{
	register char	*pp, *pp1;
	register int	len = atchar + strlen(&buf[atchar]);
	int	numchars;	/* Number of characters to copy forward */

	if (len + num >= max)
		len_error(COMPLAIN);
	pp = &buf[len];
	pp1 = &buf[len + num];
	numchars = len - atchar;
	while (numchars-- >= 0)
		*pp1-- = *pp--;
	pp = &buf[atchar];
	while (num--)
		*pp++ = c;
}

/*
 * Three situations:
 *
 * (1) We are at the end of the line in which case we just insert the char.
 * (2) We are in the middle of the line on a normal character in which case
 *     we replace that character with 'c'.
 * (3) We are in the middle of a tab.  Here we insert the character unless
 *     we are at the end of the tab stop in which case cover the tab with
 *     'c'.
 */

OverWrite()
{
	int	i, num;

	for (i = 0, num = exp, exp = 1; i < num; i++) {
		if (!eolp())
			DelNChar();
		Insert(LastKeyStruck);
	}
}

SelfInsert()
{
	Insert(LastKeyStruck);
}

Insert(c)
{
	SetModified(curbuf);
	makedirty(curline);
	insert(c, linebuf, curchar, exp, LBSIZE);
	IFixMarks(curline, curchar, curline, curchar + exp);
	curchar += exp;
}	

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

CTab()
{
	if (strlen(linebuf) == 0)
		c_indent();
	Insert('\t');
}

QuotChar()
{
	int	c;

	if (c = (*Getchar)())
		Insert(c);
}

blankp(line)
char	*line;
{
	register char	c;

	while (c = *line++)
		if (c != ' ' && c != '\t')
			return 0;
	return 1;
}

/* 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.
 */

DoParen()
{
	BUFLOC	*bp;
	int	nx,
		c = LastKeyStruck;

	if (c != ')' && c != '}')
		complain((char *) 0);
	if (c == '}' && IsFlagSet(globflags, CMODE))
		if (blankp(linebuf)) {
			Bol();		/* Beginning of line and */
			DelWtSpace();	/* Delete white space */
			c_indent();	/* insert the white space */
		}
	
	Insert(c);
	if (IsFlagSet(globflags, MATCHING)) {
		redisplay();
		GotoDot();
		BackChar();
		if (NotInQuotes(linebuf, curchar)) {
			if (bp = m_paren(c, curwind->w_top)) {
				nx = in_window(curwind, bp->p_line);
				if (nx != -1) {
					BUFLOC	b;

					DOTsave(&b);
					SetDot(bp);
					redisplay();
					sleep(1);
					SetDot(&b);
				}
			}
		}
		ForChar();
	}
}

c_indent()
{
	BUFLOC	*bp;

	bp = m_paren('}', curbuf->b_zero);
	if (!bp)
		return;
	ignore(getright(bp->p_line, genbuf));
	whitesp(genbuf, linebuf);
}

AtMargin()
{
	int	open_kludge = 0;

	exp = 1;

	if (curline->l_next == 0) {
		OpenLine();
		open_kludge++;
	}
	DoJustify(curline, curline->l_next);
	if (open_kludge)
		DelNChar();
}

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

	if (exp == 1 && eolp() && curline->l_next &&
			(getline(curline->l_next->l_dline,
			genbuf)[0] == '\0') && curline->l_next->l_next &&
			(getline(curline->l_next->l_next->l_dline,
			genbuf)[0] == '\0')) {
		SetLine(curline->l_next);
		return;
	}
	LineInsert();
}

TextInsert()
{
	Insert(LastKeyStruck);
	if ((curchar > RMargin) && LastKeyStruck != ' ')
		AtMargin();
}

ins_str(str)
register char	*str;
{
	register char	c;
	int	pos = curchar;

	while (c = *str++) {
		if (c == '\n')
			continue;
		insert(c, linebuf, curchar++, 1, LBSIZE);
	}
	IFixMarks(curline, pos, curline, curchar);
	makedirty(curline);
}

linecopy(onto, atchar, from)
register char	*onto, *from;
register int	atchar;
{
	char	*base = onto;

	onto += atchar;

	while (*onto = *from++)
		if (onto++ >= &base[LBSIZE - 2])
			len_error(ERROR);
}

OpenLine()
{
	int	num = exp;

	LineInsert();	/* Open the lines... */
	DoTimes(BackChar, num);
}

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

	SetModified(curbuf);
	lsave();
	ignore(getright(atline, genbuf));
	strcpy(save, &genbuf[atchar]);

	ignore(getright(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;
	}

	ignore(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;
}

/* 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 linefree sets dline to 0. */

#define CHUNKSIZE	40

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 */
};

struct chunk	*fchunk = 0;

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;
	}
}

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

	f = (struct chunk *) malloc((unsigned) sizeof (struct chunk));
	if (f == 0)
		return 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;
}

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. */

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/{should be} 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;

	message("Garbage collecting ...");
	UpdateMesg();
	sleep(1);
 	for (cp = fchunk; cp; 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;
	}
}