943 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			943 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* cmd2.c */
 | 
						|
 | 
						|
/* Author:
 | 
						|
 *	Steve Kirkendall
 | 
						|
 *	14407 SW Teal Blvd. #C
 | 
						|
 *	Beaverton, OR 97005
 | 
						|
 *	kirkenda@cs.pdx.edu
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
/* This file contains some of the commands - mostly ones that change text */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
#include "ctype.h"
 | 
						|
#include "vi.h"
 | 
						|
#include "regexp.h"
 | 
						|
#if TOS
 | 
						|
# include <stat.h>
 | 
						|
#else
 | 
						|
# if OSK
 | 
						|
#  include "osk.h"
 | 
						|
# else
 | 
						|
#  if AMIGA
 | 
						|
#   include "amistat.h"
 | 
						|
#  else
 | 
						|
#   include <sys/stat.h>
 | 
						|
#  endif
 | 
						|
# endif
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_substitute(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;	/* rest of the command line */
 | 
						|
{
 | 
						|
	char	*line;	/* a line from the file */
 | 
						|
	regexp	*re;	/* the compiled search expression */
 | 
						|
	char	*subst;	/* the substitution string */
 | 
						|
	char	*opt;	/* substitution options */
 | 
						|
	long	l;	/* a line number */
 | 
						|
	char	*s, *d;	/* used during subtitutions */
 | 
						|
	char	*conf;	/* used during confirmation */
 | 
						|
	long	chline;	/* # of lines changed */
 | 
						|
	long	chsub;	/* # of substitutions made */
 | 
						|
	static	optp;	/* boolean option: print when done? */
 | 
						|
	static	optg;	/* boolean option: substitute globally in line? */
 | 
						|
	static	optc;	/* boolean option: confirm before subst? */
 | 
						|
#ifndef CRUNCH
 | 
						|
	long	oldnlines;
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
	/* for now, assume this will fail */
 | 
						|
	rptlines = -1L;
 | 
						|
 | 
						|
	if (cmd == CMD_SUBAGAIN)
 | 
						|
	{
 | 
						|
#ifndef NO_MAGIC
 | 
						|
		if (*o_magic)
 | 
						|
			subst = "~";
 | 
						|
		else
 | 
						|
#endif
 | 
						|
		subst = "\\~";
 | 
						|
		re = regcomp("");
 | 
						|
 | 
						|
		/* if visual "&", then turn off the "p" and "c" options */
 | 
						|
		if (bang)
 | 
						|
		{
 | 
						|
			optp = optc = FALSE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else /* CMD_SUBSTITUTE */
 | 
						|
	{
 | 
						|
		/* make sure we got a search pattern */
 | 
						|
		if (*extra != '/' && *extra != '?')
 | 
						|
		{
 | 
						|
			msg("Usage: s/regular expression/new text/");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		/* parse & compile the search pattern */
 | 
						|
		subst = parseptrn(extra);
 | 
						|
		re = regcomp(extra + 1);
 | 
						|
	}
 | 
						|
 | 
						|
	/* abort if RE error -- error message already given by regcomp() */
 | 
						|
	if (!re)
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (cmd == CMD_SUBSTITUTE)
 | 
						|
	{
 | 
						|
		/* parse the substitution string & find the option string */
 | 
						|
		for (opt = subst; *opt && *opt != *extra; opt++)
 | 
						|
		{
 | 
						|
			if (*opt == '\\' && opt[1])
 | 
						|
			{
 | 
						|
				opt++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (*opt)
 | 
						|
		{
 | 
						|
			*opt++ = '\0';
 | 
						|
		}
 | 
						|
 | 
						|
		/* analyse the option string */
 | 
						|
		if (!*o_edcompatible)
 | 
						|
		{
 | 
						|
			optp = optg = optc = FALSE;
 | 
						|
		}
 | 
						|
		while (*opt)
 | 
						|
		{
 | 
						|
			switch (*opt++)
 | 
						|
			{
 | 
						|
			  case 'p':	optp = !optp;	break;
 | 
						|
			  case 'g':	optg = !optg;	break;
 | 
						|
			  case 'c':	optc = !optc;	break;
 | 
						|
			  case ' ':
 | 
						|
			  case '\t':			break;
 | 
						|
			  default:
 | 
						|
				msg("Subst options are p, c, and g -- not %c", opt[-1]);
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
 | 
						|
	if ((optc || optp) && mode == MODE_VI)
 | 
						|
	{
 | 
						|
		addch('\n');
 | 
						|
		exrefresh();
 | 
						|
	}
 | 
						|
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		/* reset the change counters */
 | 
						|
		chline = chsub = 0L;
 | 
						|
 | 
						|
		/* for each selected line */
 | 
						|
		for (l = markline(frommark); l <= markline(tomark); l++)
 | 
						|
		{
 | 
						|
			/* fetch the line */
 | 
						|
			line = fetchline(l);
 | 
						|
 | 
						|
			/* if it contains the search pattern... */
 | 
						|
			if (regexec(re, line, TRUE))
 | 
						|
			{
 | 
						|
				/* increment the line change counter */
 | 
						|
				chline++;
 | 
						|
 | 
						|
				/* initialize the pointers */
 | 
						|
				s = line;
 | 
						|
				d = tmpblk.c;
 | 
						|
 | 
						|
				/* do once or globally ... */
 | 
						|
				do
 | 
						|
				{
 | 
						|
#ifndef CRUNCH
 | 
						|
					/* confirm, if necessary */
 | 
						|
					if (optc)
 | 
						|
					{
 | 
						|
						for (conf = line; conf < re->startp[0]; conf++)
 | 
						|
							addch(*conf);
 | 
						|
						standout();
 | 
						|
						for ( ; conf < re->endp[0]; conf++)
 | 
						|
							addch(*conf);
 | 
						|
						standend();
 | 
						|
						for (; *conf; conf++)
 | 
						|
							addch(*conf);
 | 
						|
						addch('\n');
 | 
						|
						exrefresh();
 | 
						|
						if (getkey(0) != 'y')
 | 
						|
						{
 | 
						|
							/* copy accross the original chars */
 | 
						|
							while (s < re->endp[0])
 | 
						|
								*d++ = *s++;
 | 
						|
 | 
						|
							/* skip to next match on this line, if any */
 | 
						|
							goto Continue;
 | 
						|
						}
 | 
						|
					}
 | 
						|
#endif /* not CRUNCH */
 | 
						|
 | 
						|
					/* increment the substitution change counter */
 | 
						|
					chsub++;
 | 
						|
 | 
						|
					/* copy stuff from before the match */
 | 
						|
					while (s < re->startp[0])
 | 
						|
					{
 | 
						|
						*d++ = *s++;
 | 
						|
					}
 | 
						|
 | 
						|
					/* substitute for the matched part */
 | 
						|
					regsub(re, subst, d);
 | 
						|
					s = re->endp[0];
 | 
						|
					d += strlen(d);
 | 
						|
 | 
						|
Continue:
 | 
						|
					/* if this regexp could conceivably match
 | 
						|
					 * a zero-length string, then require at
 | 
						|
					 * least 1 unmatched character between
 | 
						|
					 * matches.
 | 
						|
					 */
 | 
						|
					if (re->minlen == 0)
 | 
						|
					{
 | 
						|
						if (!*s)
 | 
						|
							break;
 | 
						|
						*d++ = *s++;
 | 
						|
					}
 | 
						|
 | 
						|
				} while (optg && regexec(re, s, FALSE));
 | 
						|
 | 
						|
				/* copy stuff from after the match */
 | 
						|
				while (*d++ = *s++)	/* yes, ASSIGNMENT! */
 | 
						|
				{
 | 
						|
				}
 | 
						|
 | 
						|
#ifndef CRUNCH
 | 
						|
				/* NOTE: since the substitution text is allowed to have ^Ms which are
 | 
						|
				 * translated into newlines, it is possible that the number of lines
 | 
						|
				 * in the file will increase after each line has been substituted.
 | 
						|
				 * we need to adjust for this.
 | 
						|
				 */
 | 
						|
				oldnlines = nlines;
 | 
						|
#endif
 | 
						|
 | 
						|
				/* replace the old version of the line with the new */
 | 
						|
				d[-1] = '\n';
 | 
						|
				d[0] = '\0';
 | 
						|
				change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
 | 
						|
 | 
						|
#ifndef CRUNCH
 | 
						|
				l += nlines - oldnlines;
 | 
						|
				tomark += MARK_AT_LINE(nlines - oldnlines);
 | 
						|
#endif
 | 
						|
 | 
						|
				/* if supposed to print it, do so */
 | 
						|
				if (optp)
 | 
						|
				{
 | 
						|
					addstr(tmpblk.c);
 | 
						|
					exrefresh();
 | 
						|
				}
 | 
						|
 | 
						|
				/* move the cursor to that line */
 | 
						|
				cursor = MARK_AT_LINE(l);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* free the regexp */
 | 
						|
	free(re);
 | 
						|
 | 
						|
	/* if done from within a ":g" command, then finish silently */
 | 
						|
	if (doingglobal)
 | 
						|
	{
 | 
						|
		rptlines = chline;
 | 
						|
		rptlabel = "changed";
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Reporting */
 | 
						|
	if (chsub == 0)
 | 
						|
	{
 | 
						|
		msg("Substitution failed");
 | 
						|
	}
 | 
						|
	else if (chline >= *o_report)
 | 
						|
	{
 | 
						|
		msg("%ld substitutions on %ld lines", chsub, chline);
 | 
						|
	}
 | 
						|
	rptlines = 0L;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_delete(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	MARK	curs2;	/* an altered form of the cursor */
 | 
						|
 | 
						|
	/* choose your cut buffer */
 | 
						|
	if (*extra == '"')
 | 
						|
	{
 | 
						|
		extra++;
 | 
						|
	}
 | 
						|
	if (*extra)
 | 
						|
	{
 | 
						|
		cutname(*extra);
 | 
						|
	}
 | 
						|
 | 
						|
	/* make sure we're talking about whole lines here */
 | 
						|
	frommark = frommark & ~(BLKSIZE - 1);
 | 
						|
	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
 | 
						|
 | 
						|
	/* yank the lines */
 | 
						|
	cut(frommark, tomark);
 | 
						|
 | 
						|
	/* if CMD_DELETE then delete the lines */
 | 
						|
	if (cmd != CMD_YANK)
 | 
						|
	{
 | 
						|
		curs2 = cursor;
 | 
						|
		ChangeText
 | 
						|
		{
 | 
						|
			/* delete the lines */
 | 
						|
			delete(frommark, tomark);
 | 
						|
		}
 | 
						|
		if (curs2 > tomark)
 | 
						|
		{
 | 
						|
			cursor = curs2 - tomark + frommark;
 | 
						|
		}
 | 
						|
		else if (curs2 > frommark)
 | 
						|
		{
 | 
						|
			cursor = frommark;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_append(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	long	l;	/* line counter */
 | 
						|
 | 
						|
#ifndef CRUNCH
 | 
						|
	/* if '!' then toggle auto-indent */
 | 
						|
	if (bang)
 | 
						|
	{
 | 
						|
		*o_autoindent = !*o_autoindent;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		/* if we're doing a change, delete the old version */
 | 
						|
		if (cmd == CMD_CHANGE)
 | 
						|
		{
 | 
						|
			/* delete 'em */
 | 
						|
			cmd_delete(frommark, tomark, cmd, bang, extra);
 | 
						|
		}
 | 
						|
 | 
						|
		/* new lines start at the frommark line, or after it */
 | 
						|
		l = markline(frommark);
 | 
						|
		if (cmd == CMD_APPEND)
 | 
						|
		{
 | 
						|
 			l++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* get lines until no more lines, or "." line, and insert them */
 | 
						|
		while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
 | 
						|
		{
 | 
						|
			addch('\n');
 | 
						|
			if (!strcmp(tmpblk.c, "."))
 | 
						|
			{
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			strcat(tmpblk.c, "\n");
 | 
						|
			add(MARK_AT_LINE(l), tmpblk.c);
 | 
						|
			l++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* on the odd chance that we're calling this from vi mode ... */
 | 
						|
	redraw(MARK_UNSET, FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_put(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	/* choose your cut buffer */
 | 
						|
	if (*extra == '"')
 | 
						|
	{
 | 
						|
		extra++;
 | 
						|
	}
 | 
						|
	if (*extra)
 | 
						|
	{
 | 
						|
		cutname(*extra);
 | 
						|
	}
 | 
						|
 | 
						|
	/* paste it */
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		cursor = paste(frommark, TRUE, FALSE);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_join(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	long	l;
 | 
						|
	char	*scan;
 | 
						|
	int	len;	/* length of the new line */
 | 
						|
 | 
						|
	/* if only one line is specified, assume the following one joins too */
 | 
						|
	if (markline(frommark) == nlines)
 | 
						|
	{
 | 
						|
		msg("Nothing to join with this line");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (markline(frommark) == markline(tomark))
 | 
						|
	{
 | 
						|
		tomark += BLKSIZE;
 | 
						|
	}
 | 
						|
 | 
						|
	/* get the first line */
 | 
						|
	l = markline(frommark);
 | 
						|
	strcpy(tmpblk.c, fetchline(l));
 | 
						|
	len = strlen(tmpblk.c);
 | 
						|
 | 
						|
	/* build the longer line */
 | 
						|
	while (++l <= markline(tomark))
 | 
						|
	{
 | 
						|
		/* get the next line */
 | 
						|
		scan = fetchline(l);
 | 
						|
 | 
						|
		/* remove any leading whitespace */
 | 
						|
		while (*scan == '\t' || *scan == ' ')
 | 
						|
		{
 | 
						|
			scan++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* see if the line will fit */
 | 
						|
		if (strlen(scan) + len + 3 > BLKSIZE)
 | 
						|
		{
 | 
						|
			msg("Can't join -- the resulting line would be too long");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		/* catenate it, with a space (or two) in between */
 | 
						|
		if (!bang)
 | 
						|
		{
 | 
						|
			if (len >= 1)
 | 
						|
			{
 | 
						|
				if (tmpblk.c[len - 1] == '.'
 | 
						|
				 || tmpblk.c[len - 1] == '?'
 | 
						|
				 || tmpblk.c[len - 1] == '!')
 | 
						|
				{
 | 
						|
					 tmpblk.c[len++] = ' ';
 | 
						|
				}
 | 
						|
				tmpblk.c[len++] = ' ';
 | 
						|
			}
 | 
						|
		}
 | 
						|
		strcpy(tmpblk.c + len, scan);
 | 
						|
		len += strlen(scan);
 | 
						|
	}
 | 
						|
	tmpblk.c[len++] = '\n';
 | 
						|
	tmpblk.c[len] = '\0';
 | 
						|
 | 
						|
	/* make the change */
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		frommark &= ~(BLKSIZE - 1);
 | 
						|
		tomark &= ~(BLKSIZE - 1);
 | 
						|
		tomark += BLKSIZE;
 | 
						|
		change(frommark, tomark, tmpblk.c);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Reporting... */
 | 
						|
	rptlines = markline(tomark) - markline(frommark) - 1L;
 | 
						|
	rptlabel = "joined";
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_shift(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	long	l;	/* line number counter */
 | 
						|
	int	oldidx;	/* number of chars previously used for indent */
 | 
						|
	int	newidx;	/* number of chars in the new indent string */
 | 
						|
	int	oldcol;	/* previous indent amount */
 | 
						|
	int	newcol;	/* new indent amount */
 | 
						|
	char	*text;	/* pointer to the old line's text */
 | 
						|
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		/* for each line to shift... */
 | 
						|
		for (l = markline(frommark); l <= markline(tomark); l++)
 | 
						|
		{
 | 
						|
			/* get the line - ignore empty lines unless ! mode */
 | 
						|
			text = fetchline(l);
 | 
						|
			if (!*text && !bang)
 | 
						|
				continue;
 | 
						|
 | 
						|
			/* calc oldidx and oldcol */
 | 
						|
			for (oldidx = 0, oldcol = 0;
 | 
						|
			     text[oldidx] == ' ' || text[oldidx] == '\t';
 | 
						|
			     oldidx++)
 | 
						|
			{
 | 
						|
				if (text[oldidx] == ' ')
 | 
						|
				{
 | 
						|
					oldcol += 1;
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					oldcol += *o_tabstop - (oldcol % *o_tabstop);
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			/* calc newcol */
 | 
						|
			if (cmd == CMD_SHIFTR)
 | 
						|
			{
 | 
						|
				newcol = oldcol + (*o_shiftwidth & 0xff);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				newcol = oldcol - (*o_shiftwidth & 0xff);
 | 
						|
				if (newcol < 0)
 | 
						|
					newcol = 0;
 | 
						|
			}
 | 
						|
 | 
						|
			/* if no change, then skip to next line */
 | 
						|
			if (oldcol == newcol)
 | 
						|
				continue;
 | 
						|
 | 
						|
			/* build a new indent string */
 | 
						|
			newidx = 0;
 | 
						|
			if (*o_autotab)
 | 
						|
			{
 | 
						|
				while (newcol >= *o_tabstop)
 | 
						|
				{
 | 
						|
					tmpblk.c[newidx++] = '\t';
 | 
						|
					newcol -= *o_tabstop;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			while (newcol > 0)
 | 
						|
			{
 | 
						|
				tmpblk.c[newidx++] = ' ';
 | 
						|
				newcol--;
 | 
						|
			}
 | 
						|
			tmpblk.c[newidx] = '\0';
 | 
						|
 | 
						|
			/* change the old indent string into the new */
 | 
						|
			change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Reporting... */
 | 
						|
	rptlines = markline(tomark) - markline(frommark) + 1L;
 | 
						|
	if (cmd == CMD_SHIFTR)
 | 
						|
	{
 | 
						|
		rptlabel = ">ed";
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		rptlabel = "<ed";
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_read(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	int	fd, rc;	/* used while reading from the file */
 | 
						|
	char	*scan;	/* used for finding NUL characters */
 | 
						|
	int	hadnul;	/* boolean: any NULs found? */
 | 
						|
	int	addnl;	/* boolean: forced to add newlines? */
 | 
						|
	int	len;	/* number of chars in current line */
 | 
						|
	long	lines;	/* number of lines in current block */
 | 
						|
	struct stat statb;
 | 
						|
 | 
						|
	/* special case: if ":r !cmd" then let the filter() function do it */
 | 
						|
	if (extra[0] == '!')
 | 
						|
	{
 | 
						|
		filter(frommark, MARK_UNSET, extra + 1, TRUE);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* open the file */
 | 
						|
	fd = open(extra, O_RDONLY);
 | 
						|
	if (fd < 0)
 | 
						|
	{
 | 
						|
		msg("Can't open \"%s\"", extra);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
#ifndef CRUNCH
 | 
						|
	if (stat(extra, &statb) < 0)
 | 
						|
	{
 | 
						|
		msg("Can't stat \"%s\"", extra);
 | 
						|
	}
 | 
						|
# if TOS
 | 
						|
	if (statb.st_mode & S_IJDIR)
 | 
						|
# else
 | 
						|
#  if OSK
 | 
						|
	if (statb.st_mode & S_IFDIR)
 | 
						|
#  else
 | 
						|
	if ((statb.st_mode & S_IFMT) != S_IFREG)
 | 
						|
#  endif
 | 
						|
# endif
 | 
						|
	{
 | 
						|
		msg("\"%s\" is not a regular file", extra);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
#endif /* not CRUNCH */
 | 
						|
 | 
						|
	/* get blocks from the file, and add them */
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		/* insertion starts at the line following frommark */
 | 
						|
		tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
 | 
						|
		len = 0;
 | 
						|
		hadnul = addnl = FALSE;
 | 
						|
 | 
						|
		/* add an extra newline, so partial lines at the end of
 | 
						|
		 * the file don't trip us up
 | 
						|
		 */
 | 
						|
		add(tomark, "\n");
 | 
						|
 | 
						|
		/* for each chunk of text... */
 | 
						|
		while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
 | 
						|
		{
 | 
						|
			/* count newlines, convert NULs, etc. ... */
 | 
						|
			for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
 | 
						|
			{
 | 
						|
				/* break up long lines */
 | 
						|
				if (*scan != '\n' && len + 2 > BLKSIZE)
 | 
						|
				{
 | 
						|
					*scan = '\n';
 | 
						|
					addnl = TRUE;
 | 
						|
				}
 | 
						|
 | 
						|
				/* protect against NUL chars in file */
 | 
						|
				if (!*scan)
 | 
						|
				{
 | 
						|
					*scan = 0x80;
 | 
						|
					hadnul = TRUE;
 | 
						|
				}
 | 
						|
 | 
						|
				/* starting a new line? */
 | 
						|
				if (*scan == '\n')
 | 
						|
				{
 | 
						|
					/* reset length at newline */
 | 
						|
					len = 0;
 | 
						|
					lines++;
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					len++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			/* add the text */
 | 
						|
			*scan = '\0';
 | 
						|
			add(tomark, tmpblk.c);
 | 
						|
			tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
 | 
						|
		}
 | 
						|
 | 
						|
		/* if partial last line, then retain that first newline */
 | 
						|
		if (len > 0)
 | 
						|
		{
 | 
						|
			msg("Last line had no newline");
 | 
						|
			tomark += BLKSIZE; /* <- for the rptlines calc */
 | 
						|
		}
 | 
						|
		else /* delete that first newline */
 | 
						|
		{
 | 
						|
			delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* close the file */
 | 
						|
	close(fd);
 | 
						|
 | 
						|
	/* Reporting... */
 | 
						|
	rptlines = markline(tomark) - markline(frommark);
 | 
						|
	rptlabel = "read";
 | 
						|
	if (mode == MODE_EX)
 | 
						|
	{
 | 
						|
		cursor = (tomark & ~BLKSIZE) - BLKSIZE;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		cursor = frommark;
 | 
						|
	}
 | 
						|
 | 
						|
	if (addnl)
 | 
						|
		msg("Newlines were added to break up long lines");
 | 
						|
	if (hadnul)
 | 
						|
		msg("NULs were converted to 0x80");
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_undo(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	undo();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* print the selected lines */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_print(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	REG char	*scan;
 | 
						|
	REG long	l;
 | 
						|
	REG int		col;
 | 
						|
 | 
						|
	for (l = markline(frommark); l <= markline(tomark); l++)
 | 
						|
	{
 | 
						|
		/* display a line number, if CMD_NUMBER */
 | 
						|
		if (cmd == CMD_NUMBER)
 | 
						|
		{
 | 
						|
			sprintf(tmpblk.c, "%6ld  ", l);
 | 
						|
			qaddstr(tmpblk.c);
 | 
						|
			col = 8;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			col = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		/* get the next line & display it */
 | 
						|
		for (scan = fetchline(l); *scan; scan++)
 | 
						|
		{
 | 
						|
			/* expand tabs to the proper width */
 | 
						|
			if (*scan == '\t' && cmd != CMD_LIST)
 | 
						|
			{
 | 
						|
				do
 | 
						|
				{
 | 
						|
					qaddch(' ');
 | 
						|
					col++;
 | 
						|
				} while (col % *o_tabstop != 0);
 | 
						|
			}
 | 
						|
			else if (*scan > 0 && *scan < ' ' || *scan == '\177')
 | 
						|
			{
 | 
						|
				qaddch('^');
 | 
						|
				qaddch(*scan ^ 0x40);
 | 
						|
				col += 2;
 | 
						|
			}
 | 
						|
			else if ((*scan & 0x80) && cmd == CMD_LIST)
 | 
						|
			{
 | 
						|
				sprintf(tmpblk.c, "\\%03o", UCHAR(*scan));
 | 
						|
				qaddstr(tmpblk.c);
 | 
						|
				col += 4;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				qaddch(*scan);
 | 
						|
				col++;
 | 
						|
			}
 | 
						|
 | 
						|
			/* wrap at the edge of the screen */
 | 
						|
			if (!has_AM && col >= COLS)
 | 
						|
			{
 | 
						|
				addch('\n');
 | 
						|
				col -= COLS;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (cmd == CMD_LIST)
 | 
						|
		{
 | 
						|
			qaddch('$');
 | 
						|
		}
 | 
						|
		addch('\n');
 | 
						|
		exrefresh();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* move or copy selected lines */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_move(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	MARK	destmark;
 | 
						|
 | 
						|
	/* parse the destination linespec.  No defaults.  Line 0 is okay */
 | 
						|
	destmark = cursor;
 | 
						|
	if (!strcmp(extra, "0"))
 | 
						|
	{
 | 
						|
		destmark = 0L;
 | 
						|
	}
 | 
						|
	else if (linespec(extra, &destmark) == extra || !destmark)
 | 
						|
	{
 | 
						|
		msg("invalid destination address");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* flesh the marks out to encompass whole lines */
 | 
						|
	frommark &= ~(BLKSIZE - 1);
 | 
						|
	tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
 | 
						|
	destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
 | 
						|
 | 
						|
	/* make sure the destination is valid */
 | 
						|
	if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
 | 
						|
	{
 | 
						|
		msg("invalid destination address");
 | 
						|
	}
 | 
						|
 | 
						|
	/* Do it */
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		/* save the text to a cut buffer */
 | 
						|
		cutname('\0');
 | 
						|
		cut(frommark, tomark);
 | 
						|
 | 
						|
		/* if we're not copying, delete the old text & adjust destmark */
 | 
						|
		if (cmd != CMD_COPY)
 | 
						|
		{
 | 
						|
			delete(frommark, tomark);
 | 
						|
			if (destmark >= frommark)
 | 
						|
			{
 | 
						|
				destmark -= (tomark - frommark);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* add the new text */
 | 
						|
		paste(destmark, FALSE, FALSE);
 | 
						|
	}
 | 
						|
 | 
						|
	/* move the cursor to the last line of the moved text */
 | 
						|
	cursor = destmark + (tomark - frommark) - BLKSIZE;
 | 
						|
	if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
 | 
						|
	{
 | 
						|
		cursor = MARK_LAST;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Reporting... */
 | 
						|
	rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* execute EX commands from a file */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_source(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	/* must have a filename */
 | 
						|
	if (!*extra)
 | 
						|
	{
 | 
						|
		msg("\"source\" requires a filename");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	doexrc(extra);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifndef NO_AT
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_at(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	static	nest = FALSE;
 | 
						|
	int	result;
 | 
						|
	char	buf[MAXRCLEN];
 | 
						|
 | 
						|
	/* don't allow nested macros */
 | 
						|
	if (nest)
 | 
						|
	{
 | 
						|
		msg("@ macros can't be nested");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	nest = TRUE;
 | 
						|
 | 
						|
	/* require a buffer name */
 | 
						|
	if (*extra == '"')
 | 
						|
		extra++;
 | 
						|
	if (!*extra || !isascii(*extra) ||!islower(*extra))
 | 
						|
	{
 | 
						|
		msg("@ requires a cut buffer name (a-z)");
 | 
						|
	}
 | 
						|
 | 
						|
	/* get the contents of the buffer */
 | 
						|
	result = cb2str(*extra, buf, (unsigned)(sizeof buf));
 | 
						|
	if (result <= 0)
 | 
						|
	{
 | 
						|
		msg("buffer \"%c is empty", *extra);
 | 
						|
	}
 | 
						|
	else if (result >= sizeof buf)
 | 
						|
	{
 | 
						|
		msg("buffer \"%c is too large to execute", *extra);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* execute the contents of the buffer as ex commands */
 | 
						|
		exstring(buf, result, '\\');
 | 
						|
	}
 | 
						|
 | 
						|
	nest = FALSE;
 | 
						|
}
 | 
						|
#endif
 |