1775 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1775 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* cmd1.c */
 | 
						|
 | 
						|
/* Author:
 | 
						|
 *	Steve Kirkendall
 | 
						|
 *	14407 SW Teal Blvd. #C
 | 
						|
 *	Beaverton, OR 97005
 | 
						|
 *	kirkenda@cs.pdx.edu
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
/* This file contains some of the EX commands - mostly ones that deal with
 | 
						|
 * files, options, etc. -- anything except text.
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
#include "ctype.h"
 | 
						|
#include "vi.h"
 | 
						|
#include "regexp.h"
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
/* print the selected lines with info on the blocks */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_debug(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	REG char	*scan;
 | 
						|
	REG long	l;
 | 
						|
	REG int		i;
 | 
						|
	int		len;
 | 
						|
 | 
						|
	/* scan lnum[] to determine which block its in */
 | 
						|
	l = markline(frommark);
 | 
						|
	for (i = 1; l > lnum[i]; i++)
 | 
						|
	{
 | 
						|
	}
 | 
						|
 | 
						|
	do
 | 
						|
	{
 | 
						|
		/* fetch text of the block containing that line */
 | 
						|
		scan = blkget(i)->c;
 | 
						|
 | 
						|
		/* calculate its length */
 | 
						|
		if (scan[BLKSIZE - 1])
 | 
						|
		{
 | 
						|
			len = BLKSIZE;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			len = strlen(scan);
 | 
						|
		}
 | 
						|
 | 
						|
		/* print block stats */
 | 
						|
		msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
 | 
						|
			i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
 | 
						|
		msg("##### len=%d, buf=0x%lx, %sdirty",
 | 
						|
			len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
 | 
						|
		if (bang)
 | 
						|
		{
 | 
						|
			while (--len >= 0)
 | 
						|
			{
 | 
						|
				addch(*scan);
 | 
						|
				scan++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		exrefresh();
 | 
						|
 | 
						|
		/* next block */
 | 
						|
		i++;
 | 
						|
	} while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function checks a lot of conditions to make sure they aren't screwy */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_validate(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	char	*scan;
 | 
						|
	int	i;
 | 
						|
	int	nlcnt;	/* used to count newlines */
 | 
						|
	int	len;	/* counts non-NUL characters */
 | 
						|
 | 
						|
	/* check lnum[0] */
 | 
						|
	if (lnum[0] != 0L)
 | 
						|
	{
 | 
						|
		msg("lnum[0] = %ld", lnum[0]);
 | 
						|
	}
 | 
						|
 | 
						|
	/* check each block */
 | 
						|
	for (i = 1; lnum[i] <= nlines; i++)
 | 
						|
	{
 | 
						|
		scan = blkget(i)->c;
 | 
						|
		if (scan[BLKSIZE - 1])
 | 
						|
		{
 | 
						|
			msg("block %d has no NUL at the end", i);
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			for (nlcnt = len = 0; *scan; scan++, len++)
 | 
						|
			{
 | 
						|
				if (*scan == '\n')
 | 
						|
				{
 | 
						|
					nlcnt++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if (scan[-1] != '\n')
 | 
						|
			{
 | 
						|
				msg("block %d doesn't end with '\\n' (length %d)", i, len);
 | 
						|
			}
 | 
						|
			if (bang || nlcnt != lnum[i] - lnum[i - 1])
 | 
						|
			{
 | 
						|
				msg("block %d (line %ld?) has %d lines, but should have %ld",
 | 
						|
					i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		exrefresh();
 | 
						|
	}
 | 
						|
 | 
						|
	/* check lnum again */
 | 
						|
	if (lnum[i] != INFINITY)
 | 
						|
	{
 | 
						|
		msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
 | 
						|
			i, hdr.n[i], i, lnum[i]);
 | 
						|
	}
 | 
						|
 | 
						|
	msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
 | 
						|
	msg("V_from=%ld.%d, cursor=%ld.%d", markline(V_from), markidx(V_from), markline(cursor), markidx(cursor));
 | 
						|
}
 | 
						|
#endif /* DEBUG */
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_mark(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	/* validate the name of the mark */
 | 
						|
	if (*extra == '"')
 | 
						|
	{
 | 
						|
		extra++;
 | 
						|
	}
 | 
						|
	/* valid mark names are lowercase ascii characters */
 | 
						|
	if (!isascii(*extra) || !islower(*extra) || extra[1])
 | 
						|
	{
 | 
						|
		msg("Invalid mark name");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	mark[*extra - 'a'] = tomark;
 | 
						|
}
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_write(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	int		fd;
 | 
						|
	int		append;	/* boolean: write in "append" mode? */
 | 
						|
	REG long	l;
 | 
						|
	REG char	*scan;
 | 
						|
	REG int		i;
 | 
						|
 | 
						|
	/* if writing to a filter, then let filter() handle it */
 | 
						|
	if (*extra == '!')
 | 
						|
	{
 | 
						|
		filter(frommark, tomark, extra + 1, FALSE);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* if all lines are to be written, use tmpsave() */
 | 
						|
	if (frommark == MARK_FIRST && tomark == MARK_LAST && cmd == CMD_WRITE)
 | 
						|
	{
 | 
						|
		tmpsave(extra, bang);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* see if we're going to do this in append mode or not */
 | 
						|
	append = FALSE;
 | 
						|
	if (extra[0] == '>' && extra[1] == '>')
 | 
						|
	{
 | 
						|
		extra += 2;
 | 
						|
		append = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	/* either the file must not exist, or we must have a ! or be appending */
 | 
						|
	if (access(extra, 0) == 0 && !bang && !append)
 | 
						|
	{
 | 
						|
		msg("File already exists - Use :w! to overwrite");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* else do it line-by-line, like cmd_print() */
 | 
						|
	if (append)
 | 
						|
	{
 | 
						|
#ifdef O_APPEND
 | 
						|
		fd = open(extra, O_WRONLY|O_APPEND);
 | 
						|
#else
 | 
						|
		fd = open(extra, O_WRONLY);
 | 
						|
		if (fd >= 0)
 | 
						|
		{
 | 
						|
			lseek(fd, 0L, 2);
 | 
						|
		}
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		fd = -1; /* so we know the file isn't open yet */
 | 
						|
	}
 | 
						|
 | 
						|
	if (fd < 0)
 | 
						|
	{
 | 
						|
		fd = creat(extra, FILEPERMS);
 | 
						|
		if (fd < 0)
 | 
						|
		{
 | 
						|
			msg("Can't write to \"%s\"", extra);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	for (l = markline(frommark); l <= markline(tomark); l++)
 | 
						|
	{
 | 
						|
		/* get the next line */
 | 
						|
		scan = fetchline(l);
 | 
						|
		i = strlen(scan);
 | 
						|
		scan[i++] = '\n';
 | 
						|
 | 
						|
		/* print the line */
 | 
						|
		if (twrite(fd, scan, i) < i)
 | 
						|
		{
 | 
						|
			msg("Write failed");
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	rptlines = markline(tomark) - markline(frommark) + 1;
 | 
						|
	rptlabel = "written";
 | 
						|
	close(fd);
 | 
						|
}	
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_shell(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	static char	prevextra[80];
 | 
						|
 | 
						|
	/* special case: ":sh" means ":!sh" */
 | 
						|
	if (cmd == CMD_SHELL)
 | 
						|
	{
 | 
						|
		extra = o_shell;
 | 
						|
		frommark = tomark = 0L;
 | 
						|
	}
 | 
						|
 | 
						|
	/* if extra is "!", substitute previous command */
 | 
						|
	if (*extra == '!')
 | 
						|
	{
 | 
						|
		if (!*prevextra)
 | 
						|
		{
 | 
						|
			msg("No previous shell command to substitute for '!'");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		extra = prevextra;
 | 
						|
	}
 | 
						|
	else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
 | 
						|
	{
 | 
						|
		strcpy(prevextra, extra);
 | 
						|
	}
 | 
						|
 | 
						|
	/* warn the user if the file hasn't been saved yet */
 | 
						|
	if (*o_warn && tstflag(file, MODIFIED))
 | 
						|
	{
 | 
						|
		if (mode == MODE_VI)
 | 
						|
		{
 | 
						|
			mode = MODE_COLON;
 | 
						|
		}
 | 
						|
		msg("Warning: \"%s\" has been modified but not yet saved", origname);
 | 
						|
	}
 | 
						|
 | 
						|
	/* if no lines were specified, just run the command */
 | 
						|
	suspend_curses();
 | 
						|
	if (frommark == 0L)
 | 
						|
	{
 | 
						|
		system(extra);
 | 
						|
	}
 | 
						|
	else /* pipe lines from the file through the command */
 | 
						|
	{
 | 
						|
		filter(frommark, tomark, extra, TRUE);
 | 
						|
	}
 | 
						|
 | 
						|
	/* resume curses quietly for MODE_EX, but noisily otherwise */
 | 
						|
	resume_curses(mode == MODE_EX);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_global(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;	/* rest of the command line */
 | 
						|
{
 | 
						|
	char	*cmdptr;	/* the command from the command line */
 | 
						|
	char	cmdln[100];	/* copy of the command from the command line */
 | 
						|
	char	*line;		/* a line from the file */
 | 
						|
	long	l;		/* used as a counter to move through lines */
 | 
						|
	long	lqty;		/* quantity of lines to be scanned */
 | 
						|
	long	nchanged;	/* number of lines changed */
 | 
						|
	regexp	*re;		/* the compiled search expression */
 | 
						|
 | 
						|
	/* can't nest global commands */
 | 
						|
	if (doingglobal)
 | 
						|
	{
 | 
						|
		msg("Can't nest global commands.");
 | 
						|
		rptlines = -1L;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* ":g! ..." is the same as ":v ..." */
 | 
						|
	if (bang)
 | 
						|
	{
 | 
						|
		cmd = CMD_VGLOBAL;
 | 
						|
	}
 | 
						|
 | 
						|
	/* make sure we got a search pattern */
 | 
						|
	if (*extra != '/' && *extra != '?')
 | 
						|
	{
 | 
						|
		msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* parse & compile the search pattern */
 | 
						|
	cmdptr = parseptrn(extra);
 | 
						|
	if (!extra[1])
 | 
						|
	{
 | 
						|
		msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	re = regcomp(extra + 1);
 | 
						|
	if (!re)
 | 
						|
	{
 | 
						|
		/* regcomp found & described an error */
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* for each line in the range */
 | 
						|
	doingglobal = TRUE;
 | 
						|
	ChangeText
 | 
						|
	{
 | 
						|
		/* NOTE: we have to go through the lines in a forward order,
 | 
						|
		 * otherwise "g/re/p" would look funny.  *BUT* for "g/re/d"
 | 
						|
		 * to work, simply adding 1 to the line# on each loop won't
 | 
						|
		 * work.  The solution: count lines relative to the end of
 | 
						|
		 * the file.  Think about it.
 | 
						|
		 */
 | 
						|
		for (l = nlines - markline(frommark),
 | 
						|
			lqty = markline(tomark) - markline(frommark) + 1L,
 | 
						|
			nchanged = 0L;
 | 
						|
		     lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
 | 
						|
		     l--, lqty--)
 | 
						|
		{
 | 
						|
			/* fetch the line */
 | 
						|
			line = fetchline(nlines - l);
 | 
						|
 | 
						|
			/* if it contains the search pattern... */
 | 
						|
			if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
 | 
						|
			{
 | 
						|
				/* move the cursor to that line */
 | 
						|
				cursor = MARK_AT_LINE(nlines - l);
 | 
						|
 | 
						|
				/* do the ex command (without mucking up
 | 
						|
				 * the original copy of the command line)
 | 
						|
				 */
 | 
						|
				strcpy(cmdln, cmdptr);
 | 
						|
				rptlines = 0L;
 | 
						|
				doexcmd(cmdln);
 | 
						|
				nchanged += rptlines;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	doingglobal = FALSE;
 | 
						|
 | 
						|
	/* free the regexp */
 | 
						|
	free(re);
 | 
						|
 | 
						|
	/* Reporting...*/
 | 
						|
	rptlines = nchanged;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_file(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
#ifndef CRUNCH
 | 
						|
	/* if we're given a new filename, use it as this file's name */
 | 
						|
	if (extra && *extra)
 | 
						|
	{
 | 
						|
		strcpy(origname, extra);
 | 
						|
		storename(origname);
 | 
						|
		setflag(file, NOTEDITED);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	if (cmd == CMD_FILE)
 | 
						|
	{
 | 
						|
#ifndef CRUNCH
 | 
						|
		msg("\"%s\" %s%s%s %ld lines,  line %ld [%ld%%]",
 | 
						|
#else
 | 
						|
		msg("\"%s\" %s%s %ld lines,  line %ld [%ld%%]",
 | 
						|
#endif
 | 
						|
			*origname ? origname : "[NO FILE]",
 | 
						|
			tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
 | 
						|
#ifndef CRUNCH
 | 
						|
			tstflag(file, NOTEDITED) ?"[NOT EDITED]":"",
 | 
						|
#endif
 | 
						|
			tstflag(file, READONLY) ? "[READONLY]" : "",
 | 
						|
			nlines,
 | 
						|
			markline(frommark),
 | 
						|
			markline(frommark) * 100 / nlines);
 | 
						|
	}
 | 
						|
#ifndef CRUNCH
 | 
						|
	else if (markline(frommark) != markline(tomark))
 | 
						|
	{
 | 
						|
		msg("range \"%ld,%ld\" contains %ld lines",
 | 
						|
			markline(frommark),
 | 
						|
			markline(tomark),
 | 
						|
			markline(tomark) - markline(frommark) + 1L);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	else
 | 
						|
	{
 | 
						|
		msg("%ld", markline(frommark));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_edit(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	long	line = 1L;	/* might be set to prevline */
 | 
						|
#ifndef CRUNCH
 | 
						|
	char	*init = (char *)0;
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
	/* if ":vi", then switch to visual mode, and if no file is named
 | 
						|
	 * then don't switch files.
 | 
						|
	 */
 | 
						|
	if (cmd == CMD_VISUAL)
 | 
						|
	{
 | 
						|
		mode = MODE_VI;
 | 
						|
		msg("");
 | 
						|
		if (!*extra)
 | 
						|
		{
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Editing previous file?  Then start at previous line */
 | 
						|
	if (!strcmp(extra, prevorig))
 | 
						|
	{
 | 
						|
		line = prevline;
 | 
						|
	}
 | 
						|
 | 
						|
#ifndef CRUNCH
 | 
						|
	/* if we were given an explicit starting line, then start there */
 | 
						|
	if (*extra == '+')
 | 
						|
	{
 | 
						|
		for (init = ++extra; !isspace(*extra); extra++)
 | 
						|
		{
 | 
						|
		}
 | 
						|
		while (isspace(*extra))
 | 
						|
		{
 | 
						|
			*extra++ = '\0';
 | 
						|
		}
 | 
						|
		if (!*init)
 | 
						|
		{
 | 
						|
			init = "$";
 | 
						|
		}
 | 
						|
		if (!extra)
 | 
						|
		{
 | 
						|
			extra = origname;
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif /* not CRUNCH */
 | 
						|
 | 
						|
	/* switch files */
 | 
						|
	if (tmpabort(bang))
 | 
						|
	{
 | 
						|
		tmpstart(extra);
 | 
						|
		if (line <= nlines && line >= 1L)
 | 
						|
		{
 | 
						|
			cursor = MARK_AT_LINE(line);
 | 
						|
		}
 | 
						|
#ifndef CRUNCH
 | 
						|
		if (init)
 | 
						|
		{
 | 
						|
			doexcmd(init);
 | 
						|
		}
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		msg("Use edit! to abort changes, or w to save changes");
 | 
						|
 | 
						|
		/* so we can say ":e!#" next time... */
 | 
						|
		strcpy(prevorig, extra);
 | 
						|
		prevline = 1L;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* This code is also used for rewind -- GB */
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_next(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	int	i, j;
 | 
						|
	char	*scan;
 | 
						|
 | 
						|
	/* if extra stuff given, use ":args" to define a new args list */
 | 
						|
	if (cmd == CMD_NEXT && extra && *extra)
 | 
						|
	{
 | 
						|
		cmd_args(frommark, tomark, cmd, bang, extra);
 | 
						|
	}
 | 
						|
 | 
						|
	/* move to the next arg */
 | 
						|
	if (cmd == CMD_NEXT)
 | 
						|
	{
 | 
						|
		i = argno + 1;
 | 
						|
	}
 | 
						|
	else if (cmd == CMD_PREVIOUS)
 | 
						|
	{
 | 
						|
		i = argno - 1;
 | 
						|
	}
 | 
						|
	else /* cmd == CMD_REWIND */
 | 
						|
	{
 | 
						|
		i = 0;
 | 
						|
	}	
 | 
						|
	if (i < 0 || i >= nargs)
 | 
						|
	{
 | 
						|
		msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* find & isolate the name of the file to edit */
 | 
						|
	for (j = i, scan = args; j > 0; j--)
 | 
						|
	{
 | 
						|
		while(*scan++)
 | 
						|
		{
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* switch to the next file */
 | 
						|
	if (tmpabort(bang))
 | 
						|
	{
 | 
						|
		tmpstart(scan);
 | 
						|
		argno = i;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		msg("Use :%s! to abort changes, or w to save changes",
 | 
						|
			cmd == CMD_NEXT ? "next" :
 | 
						|
			cmd == CMD_PREVIOUS ? "previous" :
 | 
						|
					"rewind");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* also called from :wq -- always writes back in this case */
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_xit(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	static long	whenwarned;	/* when the user was last warned of extra files */
 | 
						|
	int		oldflag;
 | 
						|
 | 
						|
	/* if there are more files to edit, then warn user */
 | 
						|
	if (argno >= 0 && argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
 | 
						|
	{
 | 
						|
		msg("More files to edit -- Use \":n\" to go to next file");
 | 
						|
		whenwarned = changes;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (cmd == CMD_QUIT)
 | 
						|
	{
 | 
						|
		oldflag = *o_autowrite;
 | 
						|
		*o_autowrite = FALSE;
 | 
						|
		if (tmpabort(bang))
 | 
						|
		{
 | 
						|
			mode = MODE_QUIT;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			msg("Use q! to abort changes, or wq to save changes");
 | 
						|
		}
 | 
						|
		*o_autowrite = oldflag;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* else try to save this file */
 | 
						|
		oldflag = tstflag(file, MODIFIED);
 | 
						|
		if (cmd == CMD_WQUIT)
 | 
						|
			setflag(file, MODIFIED);
 | 
						|
		if (tmpend(bang))
 | 
						|
		{
 | 
						|
			mode = MODE_QUIT;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			msg("Could not save file -- use quit! to abort changes, or w filename");
 | 
						|
		}
 | 
						|
		if (!oldflag)
 | 
						|
			clrflag(file, MODIFIED);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_args(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	char	*scan;
 | 
						|
	int	col;
 | 
						|
	int	arg;
 | 
						|
	int	scrolled = FALSE;
 | 
						|
	int	width;
 | 
						|
 | 
						|
	/* if no extra names given, or just current name, then report the args
 | 
						|
	 * we have now.
 | 
						|
	 */
 | 
						|
	if (!extra || !*extra)
 | 
						|
	{
 | 
						|
		/* empty args list? */
 | 
						|
		if (nargs == 1 && !*args)
 | 
						|
		{
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		/* list the arguments */
 | 
						|
		for (scan = args, col = arg = 0;
 | 
						|
		     arg < nargs;
 | 
						|
		     scan += width + 1, col += width, arg++)
 | 
						|
		{
 | 
						|
			width = strlen(scan);
 | 
						|
			if (col + width >= COLS - 4)
 | 
						|
			{
 | 
						|
				addch('\n');
 | 
						|
				col = 0;
 | 
						|
				scrolled = TRUE;
 | 
						|
			}
 | 
						|
			else if (col > 0)
 | 
						|
			{
 | 
						|
				addch(' ');
 | 
						|
				col++;
 | 
						|
			}
 | 
						|
			if (arg == argno)
 | 
						|
			{
 | 
						|
				addch('[');
 | 
						|
				addstr(scan);
 | 
						|
				addch(']');
 | 
						|
				col += 2;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				addstr(scan);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* write a trailing newline */
 | 
						|
		if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
 | 
						|
		{
 | 
						|
			addch('\n');
 | 
						|
		}
 | 
						|
		exrefresh();	
 | 
						|
	}
 | 
						|
	else /* new args list given */
 | 
						|
	{
 | 
						|
		for (scan = args, nargs = 1; *extra; )
 | 
						|
		{
 | 
						|
			if (isspace(*extra))
 | 
						|
			{
 | 
						|
				*scan++ = '\0';
 | 
						|
				while (isspace(*extra))
 | 
						|
				{
 | 
						|
					extra++;
 | 
						|
				}
 | 
						|
				if (*extra)
 | 
						|
				{
 | 
						|
					nargs++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				*scan++ = *extra++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		*scan = '\0';
 | 
						|
 | 
						|
		/* reset argno to before the first, so :next will go to first */
 | 
						|
		argno = -1;
 | 
						|
 | 
						|
		if (nargs != 1)
 | 
						|
		{
 | 
						|
                        msg("%d files to edit", nargs);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_cd(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	char	*getenv();
 | 
						|
 | 
						|
#ifndef CRUNCH
 | 
						|
	/* if current file is modified, and no '!' was given, then error */
 | 
						|
	if (tstflag(file, MODIFIED) && !bang)
 | 
						|
	{
 | 
						|
		msg("File modified; use \"cd! %s\" to switch anyway", extra);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* default directory name is $HOME */
 | 
						|
	if (!*extra)
 | 
						|
	{
 | 
						|
		extra = getenv("HOME");
 | 
						|
		if (!extra)
 | 
						|
		{
 | 
						|
			msg("environment variable $HOME not set");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* go to the directory */
 | 
						|
	if (chdir(extra) < 0)
 | 
						|
	{
 | 
						|
		perror(extra);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_map(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	char	*mapto;
 | 
						|
	char	*build, *scan;
 | 
						|
#ifndef NO_FKEY
 | 
						|
	static char *fnames[NFKEYS] =
 | 
						|
	{
 | 
						|
		"#10", "#1", "#2", "#3", "#4",
 | 
						|
		"#5", "#6", "#7", "#8", "#9",
 | 
						|
# ifndef NO_SHIFT_FKEY
 | 
						|
		"#10s", "#1s", "#2s", "#3s", "#4s",
 | 
						|
		"#5s", "#6s", "#7s", "#8s", "#9s",
 | 
						|
#  ifndef NO_CTRL_FKEY
 | 
						|
		"#10c", "#1c", "#2c", "#3c", "#4c",
 | 
						|
		"#5c", "#6c", "#7c", "#8c", "#9c",
 | 
						|
#   ifndef NO_ALT_FKEY
 | 
						|
		"#10a", "#1a", "#2a", "#3a", "#4a",
 | 
						|
		"#5a", "#6a", "#7a", "#8a", "#9a",
 | 
						|
#   endif
 | 
						|
#  endif
 | 
						|
# endif
 | 
						|
	};
 | 
						|
	int	key;
 | 
						|
#endif
 | 
						|
 | 
						|
	/* "map" with no extra will dump the map table contents */
 | 
						|
	if (!*extra)
 | 
						|
	{
 | 
						|
#ifndef NO_ABBR
 | 
						|
		if (cmd == CMD_ABBR)
 | 
						|
		{
 | 
						|
			dumpkey(bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, TRUE);
 | 
						|
		}
 | 
						|
		else
 | 
						|
#endif
 | 
						|
		{
 | 
						|
			dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, FALSE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* "extra" is key to map, followed by what it maps to */
 | 
						|
 | 
						|
		/* handle quoting inside the "raw" string */
 | 
						|
		for (build = mapto = extra;
 | 
						|
		     *mapto && (*mapto != ' ' && *mapto != '\t');
 | 
						|
		     *build++ = *mapto++)
 | 
						|
		{
 | 
						|
			if (*mapto == ctrl('V') && mapto[1])
 | 
						|
			{
 | 
						|
				mapto++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* skip whitespace, and mark the end of the "raw" string */
 | 
						|
		while ((*mapto == ' ' || *mapto == '\t'))
 | 
						|
		{
 | 
						|
			*mapto++ = '\0';
 | 
						|
		}
 | 
						|
		*build = '\0';
 | 
						|
 | 
						|
		/* strip ^Vs from the "cooked" string */
 | 
						|
		for (scan = build = mapto; *scan; *build++ = *scan++)
 | 
						|
		{
 | 
						|
			if (*scan == ctrl('V') && scan[1])
 | 
						|
			{
 | 
						|
				scan++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		*build = '\0';
 | 
						|
 | 
						|
#ifndef NO_FKEY
 | 
						|
		/* if the mapped string is '#' and a number, then assume
 | 
						|
		 * the user wanted that function key
 | 
						|
		 */
 | 
						|
		if (extra[0] == '#' && isdigit(extra[1]))
 | 
						|
		{
 | 
						|
			key = atoi(extra + 1) % 10;
 | 
						|
# ifndef NO_SHIFT_FKEY
 | 
						|
			build = extra + strlen(extra) - 1;
 | 
						|
			if (*build == 's')
 | 
						|
				key += 10;
 | 
						|
#  ifndef NO_CTRL_FKEY
 | 
						|
			else if (*build == 'c')
 | 
						|
				key += 20;
 | 
						|
#   ifndef NO_ALT_FKEY
 | 
						|
			else if (*build == 'a')
 | 
						|
				key += 30;
 | 
						|
#   endif
 | 
						|
#  endif
 | 
						|
# endif
 | 
						|
			if (FKEY[key])
 | 
						|
				mapkey(FKEY[key], mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, fnames[key]);
 | 
						|
			else
 | 
						|
				msg("This terminal has no %s key", fnames[key]);
 | 
						|
		}
 | 
						|
		else
 | 
						|
#endif
 | 
						|
#ifndef NO_ABBR
 | 
						|
		if (cmd == CMD_ABBR || cmd == CMD_UNABBR)
 | 
						|
		{
 | 
						|
			mapkey(extra, mapto, bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, "abbr");
 | 
						|
		}
 | 
						|
		else
 | 
						|
#endif
 | 
						|
		{
 | 
						|
			mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_set(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	if (!*extra)
 | 
						|
	{
 | 
						|
		dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
 | 
						|
	}
 | 
						|
	else if (!strcmp(extra, "all"))
 | 
						|
	{
 | 
						|
		dumpopts(TRUE);	/* "TRUE" means "dump all" - even unset vars */
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		setopts(extra);
 | 
						|
 | 
						|
		/* That option may have affected the appearence of text */
 | 
						|
		changes++;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_tag(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	int	fd;	/* file descriptor used to read the file */
 | 
						|
	char	*scan;	/* used to scan through the tmpblk.c */
 | 
						|
#ifdef INTERNAL_TAGS
 | 
						|
	char	*cmp;	/* char of tag name we're comparing, or NULL */
 | 
						|
	char	*end;	/* marks the end of chars in tmpblk.c */
 | 
						|
#else
 | 
						|
	int	i;
 | 
						|
#endif
 | 
						|
#ifndef NO_MAGIC
 | 
						|
	char	wasmagic; /* preserves the original state of o_magic */
 | 
						|
#endif
 | 
						|
	static char prevtag[30];
 | 
						|
 | 
						|
	/* if no tag is given, use the previous tag */
 | 
						|
	if (!extra || !*extra)
 | 
						|
	{
 | 
						|
		if (!*prevtag)
 | 
						|
		{
 | 
						|
			msg("No previous tag");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		extra = prevtag;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		strncpy(prevtag, extra, sizeof prevtag);
 | 
						|
		prevtag[sizeof prevtag - 1] = '\0';
 | 
						|
	}
 | 
						|
 | 
						|
#ifndef INTERNAL_TAGS
 | 
						|
	/* use "ref" to look up the tag info for this tag */
 | 
						|
	sprintf(tmpblk.c, "ref -t %s%s %s", (*origname ? "-f" : ""),origname, prevtag);
 | 
						|
	fd = rpipe(tmpblk.c, 0);
 | 
						|
	if (fd < 0)
 | 
						|
	{
 | 
						|
		msg("Can't run \"%s\"", tmpblk.c);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* try to read the tag info */
 | 
						|
	for (scan = tmpblk.c;
 | 
						|
	     (i = tread(fd, scan, scan - tmpblk.c + BLKSIZE)) > 0;
 | 
						|
	     scan += i)
 | 
						|
	{
 | 
						|
	}
 | 
						|
	*scan = '\0';
 | 
						|
 | 
						|
	/* close the pipe.  abort if error */
 | 
						|
	if (rpclose(fd) != 0 || scan < tmpblk.c + 3)
 | 
						|
	{
 | 
						|
		msg("tag \"%s\" not found", extra);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
#else /* use internal code to look up the tag */
 | 
						|
	/* open the tags file */
 | 
						|
	fd = open(TAGS, O_RDONLY);
 | 
						|
	if (fd < 0)
 | 
						|
	{
 | 
						|
		msg("No tags file");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Hmmm... this would have been a lot easier with <stdio.h> */
 | 
						|
 | 
						|
	/* find the line with our tag in it */
 | 
						|
	for(scan = end = tmpblk.c, cmp = extra; ; scan++)
 | 
						|
	{
 | 
						|
		/* read a block, if necessary */
 | 
						|
		if (scan >= end)
 | 
						|
		{
 | 
						|
			end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
 | 
						|
			scan = tmpblk.c;
 | 
						|
			if (scan >= end)
 | 
						|
			{
 | 
						|
				msg("tag \"%s\" not found", extra);
 | 
						|
				close(fd);
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* if we're comparing, compare... */
 | 
						|
		if (cmp)
 | 
						|
		{
 | 
						|
			/* matched??? wow! */
 | 
						|
			if (!*cmp && *scan == '\t')
 | 
						|
			{
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if (*cmp++ != *scan)
 | 
						|
			{
 | 
						|
				/* failed! skip to newline */
 | 
						|
				cmp = (char *)0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* if we're skipping to newline, do it fast! */
 | 
						|
		if (!cmp)
 | 
						|
		{
 | 
						|
			while (scan < end && *scan != '\n')
 | 
						|
			{
 | 
						|
				scan++;
 | 
						|
			}
 | 
						|
			if (scan < end)
 | 
						|
			{
 | 
						|
				cmp = extra;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* found it! get the rest of the line into memory */
 | 
						|
	for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
 | 
						|
	{
 | 
						|
		*cmp++ = *scan++;
 | 
						|
	}
 | 
						|
	if (scan == end)
 | 
						|
	{
 | 
						|
		tread(fd, cmp, BLKSIZE - (int)(cmp - tmpblk.c));
 | 
						|
	}
 | 
						|
	else
 | 
						|
		*cmp = *scan;
 | 
						|
 | 
						|
	/* we can close the tags file now */
 | 
						|
	close(fd);
 | 
						|
#endif /* INTERNAL_TAGS */
 | 
						|
 | 
						|
	/* extract the filename from the line, and edit the file */
 | 
						|
	for (scan = tmpblk.c; *scan != '\t'; scan++)
 | 
						|
	{
 | 
						|
	}
 | 
						|
	*scan++ = '\0';
 | 
						|
	if (strcmp(origname, tmpblk.c) != 0)
 | 
						|
	{
 | 
						|
		if (!tmpabort(bang))
 | 
						|
		{
 | 
						|
			msg("Use :tag! to abort changes, or :w to save changes");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		tmpstart(tmpblk.c);
 | 
						|
	}
 | 
						|
 | 
						|
	/* move to the desired line (or to line 1 if that fails) */
 | 
						|
#ifndef NO_MAGIC
 | 
						|
	wasmagic = *o_magic;
 | 
						|
	*o_magic = FALSE;
 | 
						|
#endif
 | 
						|
	cursor = MARK_FIRST;
 | 
						|
	linespec(scan, &cursor);
 | 
						|
	if (cursor == MARK_UNSET)
 | 
						|
	{
 | 
						|
		cursor = MARK_FIRST;
 | 
						|
		msg("Tag's address is out of date");
 | 
						|
	}
 | 
						|
#ifndef NO_MAGIC
 | 
						|
	*o_magic = wasmagic;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* describe this version of the program */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_version(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	msg("%s", VERSION);
 | 
						|
#ifdef CREDIT
 | 
						|
	msg("%s", CREDIT);
 | 
						|
#endif
 | 
						|
#ifdef CREDIT2
 | 
						|
	msg("%s", CREDIT2);
 | 
						|
#endif
 | 
						|
#ifdef COMPILED_BY
 | 
						|
	msg("Compiled by %s", COMPILED_BY);
 | 
						|
#endif
 | 
						|
#ifdef COPYING
 | 
						|
	msg("%s", COPYING);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifndef NO_MKEXRC
 | 
						|
/* make a .exrc file which describes the current configuration */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_mkexrc(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	int	fd;
 | 
						|
 | 
						|
	/* the default name for the .exrc file EXRC */
 | 
						|
	if (!*extra)
 | 
						|
	{
 | 
						|
		extra = EXRC;
 | 
						|
	}
 | 
						|
 | 
						|
	/* create the .exrc file */
 | 
						|
	fd = creat(extra, FILEPERMS);
 | 
						|
	if (fd < 0)
 | 
						|
	{
 | 
						|
		msg("Couldn't create a new \"%s\" file", extra);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* save stuff */
 | 
						|
	saveopts(fd);
 | 
						|
	savemaps(fd, FALSE);
 | 
						|
#ifndef NO_ABBR
 | 
						|
	savemaps(fd, TRUE);
 | 
						|
#endif
 | 
						|
#ifndef NO_DIGRAPH
 | 
						|
	savedigs(fd);
 | 
						|
#endif
 | 
						|
#ifndef NO_COLOR
 | 
						|
	savecolor(fd);
 | 
						|
#endif
 | 
						|
 | 
						|
	/* close the file */
 | 
						|
	close(fd);
 | 
						|
	msg("Configuration saved");
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef NO_DIGRAPH
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_digraph(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	do_digraph(bang, extra);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#ifndef NO_ERRLIST 
 | 
						|
static char	errfile[256];	/* the name of a file containing an error */
 | 
						|
static long	errline;	/* the line number for an error */
 | 
						|
static int	errfd = -2;	/* fd of the errlist file */
 | 
						|
 | 
						|
/* This static function tries to parse an error message.
 | 
						|
 *
 | 
						|
 * For most compilers, the first word is taken to be the name of the erroneous
 | 
						|
 * file, and the first number after that is taken to be the line number where
 | 
						|
 * the error was detected.  The description of the error follows, possibly
 | 
						|
 * preceded by an "error ... :" or "warning ... :" label which is skipped.
 | 
						|
 *
 | 
						|
 * For Coherent, error messages look like "line#: filename: message".
 | 
						|
 *
 | 
						|
 * For non-error lines, or unparsable error lines, this function returns NULL.
 | 
						|
 * Normally, though, it alters errfile and errline, and returns a pointer to
 | 
						|
 * the description.
 | 
						|
 */
 | 
						|
static char *parse_errmsg(text)
 | 
						|
	REG char	*text;
 | 
						|
{
 | 
						|
	REG char	*cpy;
 | 
						|
	long		atol();
 | 
						|
# if COHERENT || TOS /* any Mark Williams compiler */
 | 
						|
	/* Get the line number.  If no line number, then ignore this line. */
 | 
						|
	errline = atol(text);
 | 
						|
	if (errline == 0L)
 | 
						|
		return (char *)0;
 | 
						|
 | 
						|
	/* Skip to the start of the filename */
 | 
						|
	while (*text && *text++ != ':')
 | 
						|
	{
 | 
						|
	}
 | 
						|
	if (!*text++)
 | 
						|
		return (char *)0;
 | 
						|
 | 
						|
	/* copy the filename to errfile */
 | 
						|
	for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
 | 
						|
	{
 | 
						|
	}
 | 
						|
	if (!*text++)
 | 
						|
		return (char *)0;
 | 
						|
	cpy[-1] = '\0';
 | 
						|
 | 
						|
	return text;
 | 
						|
# else /* not a Mark Williams compiler */
 | 
						|
	char		*errmsg;
 | 
						|
 | 
						|
	/* the error message is the whole line, by default */
 | 
						|
	errmsg = text;
 | 
						|
 | 
						|
	/* skip leading garbage */
 | 
						|
	while (*text && !isalnum(*text))
 | 
						|
	{
 | 
						|
		text++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* copy over the filename */
 | 
						|
	cpy = errfile;
 | 
						|
	while(isalnum(*text) || *text == '.')
 | 
						|
	{
 | 
						|
		*cpy++ = *text++;
 | 
						|
	}
 | 
						|
	*cpy = '\0';
 | 
						|
 | 
						|
	/* ignore the name "Error" and filenames that contain a '/' */
 | 
						|
	if (*text == '/' || !*errfile || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
 | 
						|
	{
 | 
						|
		return (char *)0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* skip garbage between filename and line number */
 | 
						|
	while (*text && !isdigit(*text))
 | 
						|
	{
 | 
						|
		text++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* if the number is part of a larger word, then ignore this line */
 | 
						|
	if (*text && isalpha(text[-1]))
 | 
						|
	{
 | 
						|
		return (char *)0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* get the error line */
 | 
						|
	errline = 0L;
 | 
						|
	while (isdigit(*text))
 | 
						|
	{
 | 
						|
		errline *= 10;
 | 
						|
		errline += (*text - '0');
 | 
						|
		text++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* any line which lacks a filename or line number should be ignored */
 | 
						|
	if (!errfile[0] || !errline)
 | 
						|
	{
 | 
						|
		return (char *)0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* locate the beginning of the error description */
 | 
						|
	while (*text && !isspace(*text))
 | 
						|
	{
 | 
						|
		text++;
 | 
						|
	}
 | 
						|
	while (*text)
 | 
						|
	{
 | 
						|
#  ifndef CRUNCH
 | 
						|
		/* skip "error #:" and "warning #:" clauses */
 | 
						|
		if (!strncmp(text + 1, "rror ", 5)
 | 
						|
		 || !strncmp(text + 1, "arning ", 7)
 | 
						|
		 || !strncmp(text + 1, "atal error", 10))
 | 
						|
		{
 | 
						|
			do
 | 
						|
			{
 | 
						|
				text++;
 | 
						|
			} while (*text && *text != ':');
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
#  endif
 | 
						|
 | 
						|
		/* anything other than whitespace or a colon is important */
 | 
						|
		if (!isspace(*text) && *text != ':')
 | 
						|
		{
 | 
						|
			errmsg = text;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/* else keep looking... */
 | 
						|
		text++;
 | 
						|
	}
 | 
						|
 | 
						|
	return errmsg;
 | 
						|
# endif /* not COHERENT */
 | 
						|
}
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_errlist(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	static long	endline;/* original number of lines in this file */
 | 
						|
	static long	offset;	/* offset of the next line in the errlist file */
 | 
						|
	int		i;
 | 
						|
	char		*errmsg;
 | 
						|
 | 
						|
	/* if a new errlist file is named, open it */
 | 
						|
	if (extra && extra[0])
 | 
						|
	{
 | 
						|
		/* close the old one */
 | 
						|
		if (errfd >= 0)
 | 
						|
		{
 | 
						|
			close(errfd);
 | 
						|
		}
 | 
						|
 | 
						|
		/* open the new one */
 | 
						|
		errfd = open(extra, O_RDONLY);
 | 
						|
		offset = 0L;
 | 
						|
		endline = nlines;
 | 
						|
	}
 | 
						|
	else if (errfd < 0)
 | 
						|
	{
 | 
						|
		/* open the default file */
 | 
						|
		errfd = open(ERRLIST, O_RDONLY);
 | 
						|
		offset = 0L;
 | 
						|
		endline = nlines;
 | 
						|
	}
 | 
						|
 | 
						|
	/* do we have an errlist file now? */
 | 
						|
	if (errfd < 0)
 | 
						|
	{
 | 
						|
		msg("There is no errlist file");
 | 
						|
		beep();
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* find the next error message in the file */
 | 
						|
	do
 | 
						|
	{
 | 
						|
		/* read the next line from the errlist */
 | 
						|
		lseek(errfd, offset, 0);
 | 
						|
		if (tread(errfd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
 | 
						|
		{
 | 
						|
			msg("No more errors");
 | 
						|
			beep();
 | 
						|
			close(errfd);
 | 
						|
			errfd = -2;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		for (i = 0; tmpblk.c[i] != '\n'; i++)
 | 
						|
		{
 | 
						|
		}
 | 
						|
		tmpblk.c[i++] = 0;
 | 
						|
 | 
						|
		/* look for an error message in the line */
 | 
						|
		errmsg = parse_errmsg(tmpblk.c);
 | 
						|
		if (!errmsg)
 | 
						|
		{
 | 
						|
			offset += i;
 | 
						|
		}
 | 
						|
 | 
						|
	} while (!errmsg);
 | 
						|
 | 
						|
	/* switch to the file containing the error, if this isn't it */
 | 
						|
	if (strcmp(origname, errfile))
 | 
						|
	{
 | 
						|
		if (!tmpabort(bang))
 | 
						|
		{
 | 
						|
			msg("Use :er! to abort changes, or :w to save changes");
 | 
						|
			beep();
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		tmpstart(errfile);
 | 
						|
		endline = nlines;
 | 
						|
	}
 | 
						|
	else if (endline == 0L)
 | 
						|
	{
 | 
						|
		endline = nlines;
 | 
						|
	}
 | 
						|
 | 
						|
	/* go to the line where the error was detected */
 | 
						|
	cursor = MARK_AT_LINE(errline + (nlines - endline));
 | 
						|
	if (cursor > MARK_LAST)
 | 
						|
	{
 | 
						|
		cursor = MARK_LAST;
 | 
						|
	}
 | 
						|
	if (mode == MODE_VI)
 | 
						|
	{
 | 
						|
		redraw(cursor, FALSE);
 | 
						|
	}
 | 
						|
 | 
						|
	/* display the error message */
 | 
						|
#ifdef CRUNCH
 | 
						|
	msg("%.70s", errmsg);
 | 
						|
#else
 | 
						|
	if (nlines > endline)
 | 
						|
	{
 | 
						|
		msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
 | 
						|
	}
 | 
						|
	else if (nlines < endline)
 | 
						|
	{
 | 
						|
		msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		msg("line %ld: %.65s", errline, errmsg);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* remember where the NEXT error line will start */
 | 
						|
	offset += i;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_make(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	BLK	buf;
 | 
						|
 | 
						|
	/* if the file hasn't been saved, then complain unless ! */
 | 
						|
	if (tstflag(file, MODIFIED) && !bang)
 | 
						|
	{
 | 
						|
		msg("\"%s\" not saved yet", origname);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* build the command */
 | 
						|
	sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
 | 
						|
	qaddstr(buf.c);
 | 
						|
	addch('\n');
 | 
						|
 | 
						|
	/* close the old errlist file, if any */
 | 
						|
	if (errfd >= 0)
 | 
						|
	{
 | 
						|
		close(errfd);
 | 
						|
		errfd = -3;
 | 
						|
	}
 | 
						|
 | 
						|
	/* run the command, with curses temporarily disabled */
 | 
						|
	suspend_curses();
 | 
						|
	system(buf.c);
 | 
						|
	resume_curses(mode == MODE_EX);
 | 
						|
	if (mode == MODE_COLON)
 | 
						|
		mode = MODE_VI;
 | 
						|
 | 
						|
	/* run the "errlist" command */
 | 
						|
	cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#ifndef NO_COLOR
 | 
						|
 | 
						|
/* figure out the number of text colors we use with this configuration */
 | 
						|
# ifndef NO_POPUP
 | 
						|
#  ifndef NO_VISIBLE
 | 
						|
#   define NCOLORS 7
 | 
						|
#  else
 | 
						|
#   define NCOLORS 6
 | 
						|
#  endif
 | 
						|
# else
 | 
						|
#  ifndef NO_VISIBLE
 | 
						|
#   define NCOLORS 6
 | 
						|
#  else
 | 
						|
#   define NCOLORS 5
 | 
						|
#  endif
 | 
						|
# endif
 | 
						|
 | 
						|
/* the attribute bytes used in each of "when"s */
 | 
						|
static char bytes[NCOLORS];
 | 
						|
 | 
						|
static struct
 | 
						|
{
 | 
						|
	char	*word;	/* a legal word */
 | 
						|
	int	type;	/* what type of word this is */
 | 
						|
	int	val;	/* some other value */
 | 
						|
}
 | 
						|
	words[] =
 | 
						|
{
 | 
						|
	{"normal",	1,	A_NORMAL},	/* all "when" names must come */
 | 
						|
	{"standout",	1,	A_STANDOUT},	/* at the top of the list.    */
 | 
						|
	{"bold",	1,	A_BOLD},	/* The first 3 must be normal,*/
 | 
						|
	{"underlined",	1,	A_UNDERLINE},	/* standout, and bold; the    */
 | 
						|
	{"italics",	1,	A_ALTCHARSET},	/* remaining names follow.    */
 | 
						|
#ifndef NO_POPUP
 | 
						|
	{"popup",	1,	A_POPUP},
 | 
						|
#endif
 | 
						|
#ifndef NO_VISIBLE
 | 
						|
	{"visible",	1,	A_VISIBLE},
 | 
						|
#endif
 | 
						|
 | 
						|
	{"black",	3,	0x00},		/* The color names start right*/
 | 
						|
	{"blue",	3,	0x01},		/* after the "when" names.    */
 | 
						|
	{"green",	3,	0x02},
 | 
						|
	{"cyan",	3,	0x03},
 | 
						|
	{"red",		3,	0x04},
 | 
						|
	{"magenta",	3,	0x05},
 | 
						|
	{"brown",	3,	0x06},
 | 
						|
	{"white",	3,	0x07},
 | 
						|
	{"yellow",	3,	0x0E}, /* bright brown */
 | 
						|
	{"gray",	3,	0x08}, /* bright black?  of course! */
 | 
						|
	{"grey",	3,	0x08},
 | 
						|
 | 
						|
	{"bright",	2,	0x08},
 | 
						|
	{"light",	2,	0x08},
 | 
						|
	{"blinking",	2,	0x80},
 | 
						|
	{"on",		0,	0},
 | 
						|
	{"n",		1,	A_NORMAL},
 | 
						|
	{"s",		1,	A_STANDOUT},
 | 
						|
	{"b",		1,	A_BOLD},
 | 
						|
	{"u",		1,	A_UNDERLINE},
 | 
						|
	{"i",		1,	A_ALTCHARSET},
 | 
						|
#ifndef NO_POPUP
 | 
						|
	{"p",		1,	A_POPUP},
 | 
						|
	{"menu",	1,	A_POPUP},
 | 
						|
#endif
 | 
						|
#ifndef NO_VISIBLE
 | 
						|
	{"v",		1,	A_VISIBLE},
 | 
						|
#endif
 | 
						|
	{(char *)0,	0,	0}
 | 
						|
};
 | 
						|
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_color(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark, tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	int	attrbyte;
 | 
						|
	int	cmode;
 | 
						|
	int	nowbg;	/* BOOLEAN: is the next color background? */
 | 
						|
 | 
						|
	REG char *scan;
 | 
						|
	REG	i;
 | 
						|
 | 
						|
 | 
						|
#ifndef CRUNCH
 | 
						|
	/* if no args are given, then report the current colors */
 | 
						|
	if (!*extra)
 | 
						|
	{
 | 
						|
		/* if no colors are set, then say so */
 | 
						|
		if (!bytes[0])
 | 
						|
		{
 | 
						|
			msg("no colors have been set");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		/* report all five color combinations */
 | 
						|
		for (i = 0; i < NCOLORS; i++)
 | 
						|
		{
 | 
						|
			qaddstr("color ");
 | 
						|
			qaddstr(words[i].word);
 | 
						|
			qaddch(' ');
 | 
						|
			if (bytes[i] & 0x80)
 | 
						|
				qaddstr("blinking ");
 | 
						|
			switch (bytes[i] & 0xf)
 | 
						|
			{
 | 
						|
			  case 0x08:	qaddstr("gray");	break;
 | 
						|
			  case 0x0e:	qaddstr("yellow");	break;
 | 
						|
			  case 0x0f:	qaddstr("bright white");break;
 | 
						|
			  default:
 | 
						|
				if (bytes[i] & 0x08)
 | 
						|
					qaddstr("light ");
 | 
						|
				qaddstr(words[(bytes[i] & 0x07) + NCOLORS].word);
 | 
						|
			}
 | 
						|
			qaddstr(" on ");
 | 
						|
			qaddstr(words[((bytes[i] >> 4) & 0x07) + NCOLORS].word);
 | 
						|
			addch('\n');
 | 
						|
			exrefresh();
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* The default background color is the same as "normal" chars.
 | 
						|
	 * There is no default foreground color.
 | 
						|
	 */
 | 
						|
	cmode = A_NORMAL;
 | 
						|
	attrbyte = bytes[0] & 0x70;
 | 
						|
	nowbg = FALSE;
 | 
						|
 | 
						|
	/* parse each word in the "extra" text */
 | 
						|
	for (scan = extra; *extra; extra = scan)
 | 
						|
	{
 | 
						|
		/* locate the end of the word */
 | 
						|
		while (*scan && *scan != ' ')
 | 
						|
		{
 | 
						|
			scan++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* skip whitespace at the end of the word */
 | 
						|
		while(*scan == ' ')
 | 
						|
		{
 | 
						|
			*scan++ = '\0';
 | 
						|
		}
 | 
						|
 | 
						|
		/* lookup the word */
 | 
						|
		for (i = 0; words[i].word && strcmp(words[i].word, extra); i++)
 | 
						|
		{
 | 
						|
		}
 | 
						|
 | 
						|
		/* if not a word, then complain */
 | 
						|
		if (!words[i].word)
 | 
						|
		{
 | 
						|
			msg("Invalid color name: %s", extra);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		/* process the word */
 | 
						|
		switch (words[i].type)
 | 
						|
		{
 | 
						|
		  case 1:
 | 
						|
			cmode = words[i].val;
 | 
						|
			break;
 | 
						|
 | 
						|
		  case 2:
 | 
						|
			attrbyte |= words[i].val;
 | 
						|
			break;
 | 
						|
 | 
						|
		  case 3:
 | 
						|
			if (nowbg)
 | 
						|
				attrbyte = ((attrbyte & ~0x70) | ((words[i].val & 0x07) << 4));
 | 
						|
			else
 | 
						|
				attrbyte |= words[i].val;
 | 
						|
			nowbg = TRUE;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* if nowbg isn't set now, then we were never given a foreground color */
 | 
						|
	if (!nowbg)
 | 
						|
	{
 | 
						|
		msg("usage: color [when] [\"bright\"] [\"blinking\"] foreground [background]");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* the first ":color" command MUST define the "normal" colors */
 | 
						|
	if (!bytes[0])
 | 
						|
		cmode = A_NORMAL;
 | 
						|
 | 
						|
	/* we should now have a cmode and an attribute byte... */
 | 
						|
 | 
						|
	/* set the color */
 | 
						|
	setcolor(cmode, attrbyte);
 | 
						|
 | 
						|
	/* remember what we just did */
 | 
						|
	bytes[cmode] = attrbyte;
 | 
						|
 | 
						|
	/* if the other colors haven't been set yet, then set them to defaults */
 | 
						|
	if (!bytes[1])
 | 
						|
	{
 | 
						|
		/* standout is the opposite of normal */
 | 
						|
		bytes[1] = ((attrbyte << 4) & 0x70 | (attrbyte >> 4) & 0x07);
 | 
						|
		setcolor(A_STANDOUT, bytes[1]);
 | 
						|
 | 
						|
		/* if "normal" isn't bright, then bold defaults to normal+bright
 | 
						|
		 * else bold defaults to bright white.
 | 
						|
		 */
 | 
						|
		bytes[2] = attrbyte | ((attrbyte & 0x08) ? 0x0f : 0x08);
 | 
						|
		setcolor(A_BOLD, bytes[2]);
 | 
						|
 | 
						|
		/* all others default to the "standout" colors, without blinking */
 | 
						|
		for (i = 3; i < NCOLORS; i++)
 | 
						|
		{
 | 
						|
			bytes[i] = (bytes[1] & 0x7f);
 | 
						|
			setcolor(words[i].val, bytes[i]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* force a redraw, so we see the new colors */
 | 
						|
	redraw(MARK_UNSET, FALSE);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void savecolor(fd)
 | 
						|
	int	fd;	/* file descriptor to write colors to */
 | 
						|
{
 | 
						|
	int	i;
 | 
						|
	char	buf[80];
 | 
						|
 | 
						|
	/* if no colors are set, then return */
 | 
						|
	if (!bytes[0])
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* save all five color combinations */
 | 
						|
	for (i = 0; i < NCOLORS; i++)
 | 
						|
	{
 | 
						|
		strcpy(buf, "color ");
 | 
						|
		strcat(buf, words[i].word);
 | 
						|
		strcat(buf, " ");
 | 
						|
		if (bytes[i] & 0x80)
 | 
						|
			strcat(buf, "blinking ");
 | 
						|
		switch (bytes[i] & 0xf)
 | 
						|
		{
 | 
						|
		  case 0x08:	strcat(buf, "gray");	break;
 | 
						|
		  case 0x0e:	strcat(buf, "yellow");	break;
 | 
						|
		  case 0x0f:	strcat(buf, "bright white");break;
 | 
						|
		  default:
 | 
						|
			if (bytes[i] & 0x08)
 | 
						|
				strcat(buf, "light ");
 | 
						|
			strcat(buf, words[(bytes[i] & 0x07) + NCOLORS].word);
 | 
						|
		}
 | 
						|
		strcat(buf, " on ");
 | 
						|
		strcat(buf, words[((bytes[i] >> 4) & 0x07) + NCOLORS].word);
 | 
						|
		strcat(buf, "\n");
 | 
						|
		twrite(fd, buf, (unsigned)strlen(buf));
 | 
						|
	}
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef SIGTSTP
 | 
						|
/* temporarily suspend elvis */
 | 
						|
/*ARGSUSED*/
 | 
						|
void cmd_suspend(frommark, tomark, cmd, bang, extra)
 | 
						|
	MARK	frommark;
 | 
						|
	MARK	tomark;
 | 
						|
	CMD	cmd;
 | 
						|
	int	bang;
 | 
						|
	char	*extra;
 | 
						|
{
 | 
						|
	void	(*func)();	/* stores the previous setting of SIGTSTP */
 | 
						|
 | 
						|
#if ANY_UNIX
 | 
						|
	/* the Bourne shell can't handle ^Z */
 | 
						|
	if (!strcmp(o_shell, "/bin/sh"))
 | 
						|
	{
 | 
						|
		msg("The /bin/sh shell doesn't support ^Z");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	move(LINES - 1, 0);
 | 
						|
	if (tstflag(file, MODIFIED))
 | 
						|
	{
 | 
						|
		addstr("Warning: \"");
 | 
						|
		addstr(origname);
 | 
						|
		addstr("\" modified but not yet saved");
 | 
						|
		clrtoeol();
 | 
						|
	}
 | 
						|
	refresh();
 | 
						|
	suspend_curses();
 | 
						|
	func = signal(SIGTSTP, SIG_DFL);
 | 
						|
	kill (0, SIGTSTP);
 | 
						|
 | 
						|
	/* the process stops and resumes here */
 | 
						|
 | 
						|
	signal(SIGTSTP, func);
 | 
						|
	resume_curses(TRUE);
 | 
						|
	if (mode == MODE_VI || mode == MODE_COLON)
 | 
						|
		redraw(MARK_UNSET, FALSE);
 | 
						|
	else
 | 
						|
		refresh ();
 | 
						|
}
 | 
						|
#endif
 |