475 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			475 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* modify.c */
 | |
| 
 | |
| /* This file contains the low-level file modification functions:
 | |
|  *	delete(frommark, tomark)	- removes line or portions of lines
 | |
|  *	add(frommark, text)		- inserts new text
 | |
|  *	change(frommark, tomark, text)	- delete, then add
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #include "vi.h"
 | |
| 
 | |
| #ifdef DEBUG2
 | |
| # include <stdio.h>
 | |
| static FILE *dbg;
 | |
| 
 | |
| /*VARARGS1*/
 | |
| debout(msg, arg1, arg2, arg3, arg4, arg5)
 | |
| 	char	*msg, *arg1, *arg2, *arg3, *arg4, *arg5;
 | |
| {
 | |
| 	if (!dbg)
 | |
| 	{
 | |
| 		dbg = fopen("debug.out", "w");
 | |
| 		if (!dbg)
 | |
| 			return;
 | |
| 		setbuf(dbg, (FILE *)0);
 | |
| 	}
 | |
| 	fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5);
 | |
| }
 | |
| #endif /* DEBUG2 */
 | |
| 
 | |
| /* delete a range of text from the file */
 | |
| void delete(frommark, tomark)
 | |
| 	MARK		frommark;	/* first char to be deleted */
 | |
| 	MARK		tomark;		/* AFTER last char to be deleted */
 | |
| {
 | |
| 	int		i;		/* used to move thru logical blocks */
 | |
| 	REG char	*scan;		/* used to scan thru text of the blk */
 | |
| 	REG char	*cpy;		/* used when copying chars */
 | |
| 	BLK		*blk;		/* a text block */
 | |
| 	long		l;		/* a line number */
 | |
| 	MARK		m;		/* a traveling version of frommark */
 | |
| 
 | |
| #ifdef DEBUG2
 | |
| 	debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark));
 | |
| #endif
 | |
| 
 | |
| 	/* if not deleting anything, quit now */
 | |
| 	if (frommark == tomark)
 | |
| 	{
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* This is a change */
 | |
| 	changes++;
 | |
| 	significant = TRUE;
 | |
| 
 | |
| 	/* supply clues to the redraw module */
 | |
| 	redrawrange(markline(frommark), markline(tomark), markline(frommark));
 | |
| 
 | |
| 	/* adjust marks 'a through 'z and '' as needed */
 | |
| 	l = markline(tomark);
 | |
| 	for (i = 0; i < NMARKS; i++)
 | |
| 	{
 | |
| 		if (mark[i] < frommark)
 | |
| 		{
 | |
| 			continue;
 | |
| 		}
 | |
| 		else if (mark[i] < tomark)
 | |
| 		{
 | |
| 			mark[i] = MARK_UNSET;
 | |
| 		}
 | |
| 		else if (markline(mark[i]) == l)
 | |
| 		{
 | |
| 			if (markline(frommark) == l)
 | |
| 			{
 | |
| 				mark[i] -= markidx(tomark) - markidx(frommark);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				mark[i] -= markidx(tomark);
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			mark[i] -= MARK_AT_LINE(l - markline(frommark));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Reporting... */
 | |
| 	if (markidx(frommark) == 0 && markidx(tomark) == 0)
 | |
| 	{
 | |
| 		rptlines = markline(tomark) - markline(frommark);
 | |
| 		rptlabel = "deleted";
 | |
| 	}
 | |
| 
 | |
| 	/* find the block containing frommark */
 | |
| 	l = markline(frommark);
 | |
| 	for (i = 1; lnum[i] < l; i++)
 | |
| 	{
 | |
| 	}
 | |
| 
 | |
| 	/* process each affected block... */
 | |
| 	for (m = frommark;
 | |
| 	     m < tomark && lnum[i] < INFINITY;
 | |
| 	     m = MARK_AT_LINE(lnum[i - 1] + 1))
 | |
| 	{
 | |
| 		/* fetch the block */
 | |
| 		blk = blkget(i);
 | |
| 
 | |
| 		/* find the mark in the block */
 | |
| 		scan = blk->c;
 | |
| 		for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--)
 | |
| 		{
 | |
| 			while (*scan++ != '\n')
 | |
| 			{
 | |
| 			}
 | |
| 		}
 | |
| 		scan += markidx(m);
 | |
| 
 | |
| 		/* figure out where the changes to this block end */
 | |
| 		if (markline(tomark) > lnum[i])
 | |
| 		{
 | |
| 			cpy = blk->c + BLKSIZE;
 | |
| 		}
 | |
| 		else if (markline(tomark) == markline(m))
 | |
| 		{
 | |
| 			cpy = scan - markidx(m) + markidx(tomark);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			cpy = scan;
 | |
| 			for (l = markline(tomark) - markline(m);
 | |
| 			     l > 0;
 | |
| 			     l--)
 | |
| 			{
 | |
| 				while (*cpy++ != '\n')
 | |
| 				{
 | |
| 				}
 | |
| 			}
 | |
| 			cpy += markidx(tomark);
 | |
| 		}
 | |
| 
 | |
| 		/* delete the stuff by moving chars within this block */
 | |
| 		while (cpy < blk->c + BLKSIZE)
 | |
| 		{
 | |
| 			*scan++ = *cpy++;
 | |
| 		}
 | |
| 		while (scan < blk->c + BLKSIZE)
 | |
| 		{
 | |
| 			*scan++ = '\0';
 | |
| 		}
 | |
| 
 | |
| 		/* adjust tomark to allow for lines deleted from this block */
 | |
| 		tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m));
 | |
| 
 | |
| 		/* if this block isn't empty now, then advance i */
 | |
| 		if (*blk->c)
 | |
| 		{
 | |
| 			i++;
 | |
| 		}
 | |
| 
 | |
| 		/* the buffer has changed.  Update hdr and lnum. */
 | |
| 		blkdirty(blk);
 | |
| 	}
 | |
| 
 | |
| 	/* must have at least 1 line */
 | |
| 	if (nlines == 0)
 | |
| 	{
 | |
| 		blk = blkadd(1);
 | |
| 		blk->c[0] = '\n';
 | |
| 		blkdirty(blk);
 | |
| 		cursor = MARK_FIRST;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /* add some text at a specific place in the file */
 | |
| void add(atmark, newtext)
 | |
| 	MARK		atmark;		/* where to insert the new text */
 | |
| 	char		*newtext;	/* NUL-terminated string to insert */
 | |
| {
 | |
| 	REG char	*scan;		/* used to move through string */
 | |
| 	REG char	*build;		/* used while copying chars */
 | |
| 	int		addlines;	/* number of lines we're adding */
 | |
| 	int		lastpart;	/* size of last partial line */
 | |
| 	BLK		*blk;		/* the block to be modified */
 | |
| 	int		blkno;		/* the logical block# of (*blk) */
 | |
| 	REG char	*newptr;	/* where new text starts in blk */
 | |
| 	BLK		buf;		/* holds chars from orig blk */
 | |
| 	BLK		linebuf;	/* holds part of line that didn't fit */
 | |
| 	BLK		*following;	/* the BLK following the last BLK */
 | |
| 	int		i;
 | |
| 	long		l;
 | |
| 
 | |
| #ifdef DEBUG2
 | |
| 	debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext);
 | |
| #endif
 | |
| #ifdef lint
 | |
| 	buf.c[0] = 0;
 | |
| #endif
 | |
| 	/* if not adding anything, return now */
 | |
| 	if (!*newtext)
 | |
| 	{
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* This is a change */
 | |
| 	changes++;
 | |
| 	significant = TRUE;
 | |
| 
 | |
| 	/* count the number of lines in the new text */
 | |
| 	for (scan = newtext, lastpart = addlines = 0; *scan; )
 | |
| 	{
 | |
| 		if (*scan++ == '\n')
 | |
| 		{
 | |
| 			addlines++;
 | |
| 			lastpart = 0;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			lastpart++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Reporting... */
 | |
| 	if (lastpart == 0 && markidx(atmark) == 0)
 | |
| 	{
 | |
| 		rptlines = addlines;
 | |
| 		rptlabel = "added";
 | |
| 	}
 | |
| 
 | |
| 	/* extract the line# from atmark */
 | |
| 	l = markline(atmark);
 | |
| 
 | |
| 	/* supply clues to the redraw module */
 | |
| 	if ((markidx(atmark) == 0 && lastpart == 0) || addlines == 0)
 | |
| 	{
 | |
| 		redrawrange(l, l, l + addlines);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* make sure the last line gets redrawn -- it was
 | |
| 		 * split, so its appearance has changed
 | |
| 		 */
 | |
| 		redrawrange(l, l + 1L, l + addlines + 1L);
 | |
| 	}
 | |
| 
 | |
| 	/* adjust marks 'a through 'z and '' as needed */
 | |
| 	for (i = 0; i < NMARKS; i++)
 | |
| 	{
 | |
| 		if (mark[i] < atmark)
 | |
| 		{
 | |
| 			/* earlier line, or earlier in same line: no change */
 | |
| 			continue;
 | |
| 		}
 | |
| 		else if (markline(mark[i]) > l)
 | |
| 		{
 | |
| 			/* later line: move down a whole number of lines */
 | |
| 			mark[i] += MARK_AT_LINE(addlines);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* later in same line */
 | |
| 			if (addlines > 0)
 | |
| 			{
 | |
| 				/* multi-line add, which split this line:
 | |
| 				 * move down, and possibly left or right,
 | |
| 				 * depending on where the split was and how
 | |
| 				 * much text was inserted after the last \n
 | |
| 				 */
 | |
| 				mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				/* totally within this line: move right */
 | |
| 				mark[i] += lastpart;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* get the block to be modified */
 | |
| 	for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++)
 | |
| 	{
 | |
| 	}
 | |
| 	blk = blkget(blkno);
 | |
| 	buf = *blk;
 | |
| 
 | |
| 	/* figure out where the new text starts */
 | |
| 	for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1;
 | |
| 	     l > 0;
 | |
| 	     l--)
 | |
| 	{
 | |
| 		while (*newptr++ != '\n')
 | |
| 		{
 | |
| 		}
 | |
| 	}
 | |
| 	newptr += markidx(atmark);
 | |
| 
 | |
| 	/* keep start of old block */
 | |
| 	build = blk->c + (int)(newptr - buf.c);
 | |
| 
 | |
| 	/* fill this block (or blocks) from the newtext string */
 | |
| 	while (*newtext)
 | |
| 	{
 | |
| 		while (*newtext && build < blk->c + BLKSIZE - 1)
 | |
| 		{
 | |
| 			*build++ = *newtext++;
 | |
| 		}
 | |
| 		if (*newtext)
 | |
| 		{
 | |
| 			/* save the excess */
 | |
| 			for (scan = linebuf.c + BLKSIZE;
 | |
| 			     build > blk->c && build[-1] != '\n';
 | |
| 			     )
 | |
| 			{
 | |
| 				*--scan = *--build;
 | |
| 			}
 | |
| 
 | |
| 			/* write the block */
 | |
| 			while (build < blk->c + BLKSIZE)
 | |
| 			{
 | |
| 				*build++ = '\0';
 | |
| 			}
 | |
| 			blkdirty(blk);
 | |
| 
 | |
| 			/* add another block */
 | |
| 			blkno++;
 | |
| 			blk = blkadd(blkno);
 | |
| 
 | |
| 			/* copy in the excess from last time */
 | |
| 			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
 | |
| 			{
 | |
| 				*build++ = *scan++;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* fill this block(s) from remainder of orig block */
 | |
| 	while (newptr < buf.c + BLKSIZE && *newptr)
 | |
| 	{
 | |
| 		while (newptr < buf.c + BLKSIZE
 | |
| 		    && *newptr
 | |
| 		    && build < blk->c + BLKSIZE - 1)
 | |
| 		{
 | |
| 			*build++ = *newptr++;
 | |
| 		}
 | |
| 		if (newptr < buf.c + BLKSIZE && *newptr)
 | |
| 		{
 | |
| 			/* save the excess */
 | |
| 			for (scan = linebuf.c + BLKSIZE;
 | |
| 			     build > blk->c && build[-1] != '\n';
 | |
| 			     )
 | |
| 			{
 | |
| 				*--scan = *--build;
 | |
| 			}
 | |
| 
 | |
| 			/* write the block */
 | |
| 			while (build < blk->c + BLKSIZE)
 | |
| 			{
 | |
| 				*build++ = '\0';
 | |
| 			}
 | |
| 			blkdirty(blk);
 | |
| 
 | |
| 			/* add another block */
 | |
| 			blkno++;
 | |
| 			blk = blkadd(blkno);
 | |
| 
 | |
| 			/* copy in the excess from last time */
 | |
| 			for (build = blk->c; scan < linebuf.c + BLKSIZE; )
 | |
| 			{
 | |
| 				*build++ = *scan++;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* see if we can combine our last block with the following block */
 | |
| 	if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6))
 | |
| 	{
 | |
| 		/* hey, we probably can!  Get the following block & see... */
 | |
| 		following = blkget(blkno + 1);
 | |
| 		if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1)
 | |
| 		{
 | |
| 			/* we can!  Copy text from following to blk */
 | |
| 			for (scan = following->c; *scan; )
 | |
| 			{
 | |
| 				*build++ = *scan++;
 | |
| 			}
 | |
| 			while (build < blk->c + BLKSIZE)
 | |
| 			{
 | |
| 				*build++ = '\0';
 | |
| 			}
 | |
| 			blkdirty(blk);
 | |
| 
 | |
| 			/* pretend the following was the last blk */
 | |
| 			blk = following;
 | |
| 			build = blk->c;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* that last block is dirty by now */
 | |
| 	while (build < blk->c + BLKSIZE)
 | |
| 	{
 | |
| 		*build++ = '\0';
 | |
| 	}
 | |
| 	blkdirty(blk);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* change the text of a file */
 | |
| void change(frommark, tomark, newtext)
 | |
| 	MARK	frommark, tomark;
 | |
| 	char	*newtext;
 | |
| {
 | |
| 	int	i;
 | |
| 	long	l;
 | |
| 	char	*text;
 | |
| 	BLK	*blk;
 | |
| 
 | |
| #ifdef DEBUG2
 | |
| 	debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext);
 | |
| #endif
 | |
| 
 | |
| 	/* optimize for single-character replacement */
 | |
| 	if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n')
 | |
| 	{
 | |
| 		/* find the block containing frommark */
 | |
| 		l = markline(frommark);
 | |
| 		for (i = 1; lnum[i] < l; i++)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		/* get the block */
 | |
| 		blk = blkget(i);
 | |
| 
 | |
| 		/* find the line within the block */
 | |
| 		for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++)
 | |
| 		{
 | |
| 			if (*text == '\n')
 | |
| 			{
 | |
| 				i--;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* replace the char */
 | |
| 		text += markidx(frommark);
 | |
| 		if (*text == newtext[0])
 | |
| 		{
 | |
| 			/* no change was needed - same char */
 | |
| 			return;
 | |
| 		}
 | |
| 		else if (*text != '\n')
 | |
| 		{
 | |
| 			/* This is a change */
 | |
| 			changes++;
 | |
| 			significant = TRUE;
 | |
| 			ChangeText
 | |
| 			{
 | |
| 				*text = newtext[0];
 | |
| 				blkdirty(blk);
 | |
| 			}
 | |
| 			redrawrange(markline(frommark), markline(tomark), markline(frommark));
 | |
| 			return;
 | |
| 		}
 | |
| 		/* else it is a complex change involving newline... */
 | |
| 	}
 | |
| 
 | |
| 	/* couldn't optimize, so do delete & add */
 | |
| 	ChangeText
 | |
| 	{
 | |
| 		delete(frommark, tomark);
 | |
| 		add(frommark, newtext);
 | |
| 		rptlabel = "changed";
 | |
| 	}
 | |
| }
 | 
