Elvis 1.4, part 7 of 8

Steve Kirkendall kirkenda at eecs.cs.pdx.edu
Tue Dec 4 08:35:35 AEST 1990


# --------------------------- cut here ----------------------------
# This is a shar archive.  To unpack it, save it to a file, and delete
# anything above the "cut here" line.  Then run sh on the file.
#
# -rw-r--r--  1 kirkenda    16871 Dec  2 17:57 regexp.c
# -rw-r--r--  1 kirkenda      579 Dec  2 17:57 regexp.h
# -rw-r--r--  1 kirkenda     3614 Dec  2 17:57 regsub.c
# -rw-r--r--  1 kirkenda     8849 Dec  2 17:57 system.c
# -rw-r--r--  1 kirkenda     3767 Dec  2 17:57 tinytcap.c
# -rw-r--r--  1 kirkenda    16181 Dec  2 17:57 tio.c
# -rw-r--r--  1 kirkenda    12770 Dec  2 17:57 tmp.c
#

if test -f regexp.c -a "$1" != -f
then
echo Will not overwrite regexp.c
else
echo Extracting regexp.c
sed 's/^X//' >regexp.c <<\eof
X/* regexp.c */
X
X/* This file contains the code that compiles regular expressions and executes
X * them.  It supports the same syntax and features as vi's regular expression
X * code.  Specifically, the meta characters are:
X *	^	matches the beginning of a line
X *	$	matches the end of a line
X *	\<	matches the beginning of a word
X *	\>	matches the end of a word
X *	.	matches any single character
X *	[]	matches any character in a character class
X *	\(	delimits the start of a subexpression
X *	\)	delimits the end of a subexpression
X *	*	repeats the preceding 0 or more times
X * NOTE: You cannot follow a \) with a *.
X *
X * The physical structure of a compiled RE is as follows:
X *	- First, there is a one-byte value that says how many character classes
X *	  are used in this regular expression
X *	- Next, each character class is stored as a bitmap that is 256 bits
X *	  (32 bytes) long.
X *	- A mixture of literal characters and compiled meta characters follows.
X *	  This begins with M_BEGIN(0) and ends with M_END(0).  All meta chars
X *	  are stored as a \n followed by a one-byte code, so they take up two
X *	  bytes apiece.  Literal characters take up one byte apiece.  \n can't
X *	  be used as a literal character.
X *
X * If NO_MAGIC is defined, then a different set of functions is used instead.
X * That right, this file contains TWO versions of the code.
X */
X
X#include <setjmp.h>
X#include <ctype.h>
X#include "config.h"
X#include "vi.h"
X#include "regexp.h"
X
X
X
Xstatic char	*previous;	/* the previous regexp, used when null regexp is given */
X
X
X#ifndef NO_MAGIC
X/* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */
X
X/* These are used to classify or recognize meta-characters */
X#define META		'\0'
X#define BASE_META(m)	((m) - 256)
X#define INT_META(c)	((c) + 256)
X#define IS_META(m)	((m) >= 256)
X#define IS_CLASS(m)	((m) >= M_CLASS(0) && (m) <= M_CLASS(9))
X#define IS_START(m)	((m) >= M_START(0) && (m) <= M_START(9))
X#define IS_END(m)	((m) >= M_END(0) && (m) <= M_END(9))
X#define IS_CLOSURE(m)	((m) >= M_SPLAT && (m) <= M_QMARK)
X#define ADD_META(s,m)	(*(s)++ = META, *(s)++ = BASE_META(m))
X#define GET_META(s)	(*(s) == META ? INT_META(*++(s)) : *s)
X
X/* These are the internal codes used for each type of meta-character */
X#define M_BEGLINE	256		/* internal code for ^ */
X#define M_ENDLINE	257		/* internal code for $ */
X#define M_BEGWORD	258		/* internal code for \< */
X#define M_ENDWORD	259		/* internal code for \> */
X#define M_ANY		260		/* internal code for . */
X#define M_SPLAT		261		/* internal code for * */
X#define M_PLUS		262		/* internal code for \+ */
X#define M_QMARK		263		/* internal code for \? */
X#define M_CLASS(n)	(264+(n))	/* internal code for [] */
X#define M_START(n)	(274+(n))	/* internal code for \( */
X#define M_END(n)	(284+(n))	/* internal code for \) */
X
X/* These are used during compilation */
Xstatic int	class_cnt;	/* used to assign class IDs */
Xstatic int	start_cnt;	/* used to assign start IDs */
Xstatic int	end_stk[NSUBEXP];/* used to assign end IDs */
Xstatic int	end_sp;
Xstatic char	*retext;	/* points to the text being compiled */
X
X/* error-handling stuff */
Xjmp_buf	errorhandler;
X#define FAIL(why)	regerror(why); longjmp(errorhandler, 1)
X
X
X
X
X
X/* This function builds a bitmap for a particular class */
Xstatic char *makeclass(text, bmap)
X	REG char	*text;	/* start of the class */
X	REG char	*bmap;	/* the bitmap */
X{
X	REG int		i;
X	int		complement = 0;
X
X# if TRACE
X	printf("makeclass(\"%s\", 0x%lx)\n", text, (long)bmap);
X# endif
X
X	/* zero the bitmap */
X	for (i = 0; bmap && i < 32; i++)
X	{
X		bmap[i] = 0;
X	}
X
X	/* see if we're going to complement this class */
X	if (*text == '^')
X	{
X		text++;
X		complement = 1;
X	}
X
X	/* add in the characters */
X	while (*text && *text != ']')
X	{
X		/* is this a span of characters? */
X		if (text[1] == '-' && text[2])
X		{
X			/* spans can't be backwards */
X			if (text[0] > text[2])
X			{
X				FAIL("Backwards span in []");
X			}
X
X			/* add each character in the span to the bitmap */
X			for (i = text[0]; bmap && i <= text[2]; i++)
X			{
X				bmap[i >> 3] |= (1 << (i & 7));
X			}
X
X			/* move past this span */
X			text += 3;
X		}
X		else
X		{
X			/* add this single character to the span */
X			i = *text++;
X			if (bmap)
X			{
X				bmap[i >> 3] |= (1 << (i & 7));
X			}
X		}
X	}
X
X	/* make sure the closing ] is missing */
X	if (*text++ != ']')
X	{
X		FAIL("] missing");
X	}
X
X	/* if we're supposed to complement this class, then do so */
X	if (complement && bmap)
X	{
X		for (i = 0; i < 32; i++)
X		{
X			bmap[i] = ~bmap[i];
X		}
X	}
X
X	return text;
X}
X
X
X
X
X/* This function gets the next character or meta character from a string.
X * The pointer is incremented by 1, or by 2 for \-quoted characters.  For [],
X * a bitmap is generated via makeclass() (if re is given), and the
X * character-class text is skipped.
X */
Xstatic int gettoken(sptr, re)
X	char	**sptr;
X	regexp	*re;
X{
X	int	c;
X
X	c = **sptr;
X	++*sptr;
X	if (c == '\\')
X	{
X		c = **sptr;
X		++*sptr;
X		switch (c)
X		{
X		  case '<':
X			return M_BEGWORD;
X
X		  case '>':
X			return M_ENDWORD;
X
X		  case '(':
X			if (start_cnt >= NSUBEXP)
X			{
X				FAIL("Too many \\(s");
X			}
X			end_stk[end_sp++] = start_cnt;
X			return M_START(start_cnt++);
X
X		  case ')':
X			if (end_sp <= 0)
X			{
X				FAIL("Mismatched \\)");
X			}
X			return M_END(end_stk[--end_sp]);
X
X		  case '*':
X			return (*o_magic ? c : M_SPLAT);
X
X		  case '.':
X			return (*o_magic ? c : M_ANY);
X
X		  case '+':
X			return M_PLUS;
X
X		  case '?':
X			return M_QMARK;
X
X		  default:
X			return c;
X		}
X	}
X	else if (*o_magic)
X	{
X		switch (c)
X		{
X		  case '^':
X			if (*sptr == retext + 1)
X			{
X				return M_BEGLINE;
X			}
X			return c;
X
X		  case '$':
X			if (!**sptr)
X			{
X				return M_ENDLINE;
X			}
X			return c;
X
X		  case '.':
X			return M_ANY;
X
X		  case '*':
X			return M_SPLAT;
X
X		  case '[':
X			/* make sure we don't have too many classes */
X			if (class_cnt >= 10)
X			{
X				FAIL("Too many []s");
X			}
X
X			/* process the character list for this class */
X			if (re)
X			{
X				/* generate the bitmap for this class */
X				*sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt);
X			}
X			else
X			{
X				/* skip to end of the class */
X				*sptr = makeclass(*sptr, (char *)0);
X			}
X			return M_CLASS(class_cnt++);
X
X		  default:
X			return c;
X		}
X	}
X	else	/* unquoted nomagic */
X	{
X		switch (c)
X		{
X		  case '^':
X			if (*sptr == retext + 1)
X			{
X				return M_BEGLINE;
X			}
X			return c;
X
X		  case '$':
X			if (!**sptr)
X			{
X				return M_ENDLINE;
X			}
X			return c;
X
X		  default:
X			return c;
X		}
X	}
X	/*NOTREACHED*/
X}
X
X
X
X
X/* This function calculates the number of bytes that will be needed for a
X * compiled RE.  Its argument is the uncompiled version.  It is not clever
X * about catching syntax errors; that is done in a later pass.
X */
Xstatic unsigned calcsize(text)
X	char		*text;
X{
X	unsigned	size;
X	int		token;
X
X	retext = text;
X	class_cnt = 0;
X	start_cnt = 1;
X	end_sp = 0;
X	size = 5;
X	while ((token = gettoken(&text, (regexp *)0)) != 0)
X	{
X		if (IS_CLASS(token))
X		{
X			size += 34;
X		}
X		else if (IS_META(token))
X		{
X			size += 2;
X		}
X		else
X		{
X			size++;
X		}
X	}
X
X	return size;
X}
X
X
X
X/* This function compiles a regexp. */
Xregexp *regcomp(text)
X	char		*text;
X{
X	int		needfirst;
X	unsigned	size;
X	int		token;
X	int		peek;
X	char		*build;
X	regexp		*re;
X
X
X	/* prepare for error handling */
X	re = (regexp *)0;
X	if (setjmp(errorhandler))
X	{
X		if (re)
X		{
X			free(re);
X		}
X		return (regexp *)0;
X	}
X
X	/* if an empty regexp string was given, use the previous one */
X	if (*text == 0)
X	{
X		if (!previous)
X		{
X			FAIL("No previous RE");
X		}
X		text = previous;
X	}
X	else /* non-empty regexp given, so remember it */
X	{
X		if (previous)
X			free(previous);
X		previous = (char *)malloc((unsigned)(strlen(text) + 1));
X		if (previous)
X			strcpy(previous, text);
X	}
X
X	/* allocate memory */
X	class_cnt = 0;
X	start_cnt = 1;
X	end_sp = 0;
X	retext = text;
X	size = calcsize(text) + sizeof(regexp);
X#ifdef lint
X	re = ((regexp *)0) + size;
X#else
X	re = (regexp *)malloc((unsigned)size);
X#endif
X	if (!re)
X	{
X		FAIL("Not enough memory for this RE");
X	}
X
X	/* compile it */
X	build = &re->program[1 + 32 * class_cnt];
X	re->program[0] = class_cnt;
X	for (token = 0; token < NSUBEXP; token++)
X	{
X		re->startp[token] = re->endp[token] = (char *)0;
X	}
X	re->first = 0;
X	re->bol = 0;
X	re->minlen = 0;
X	needfirst = 1;
X	class_cnt = 0;
X	start_cnt = 1;
X	end_sp = 0;
X	retext = text;
X	for (token = M_START(0), peek = gettoken(&text, re);
X	     token;
X	     token = peek, peek = gettoken(&text, re))
X	{
X		/* special processing for the closure operator */
X		if (IS_CLOSURE(peek))
X		{
X			/* detect misuse of closure operator */
X			if (IS_START(token))
X			{
X				FAIL("* or \\+ or \\? follows nothing");
X			}
X			else if (IS_META(token) && token != M_ANY && !IS_CLASS(token))
X			{
X				FAIL("* or \\+ or \\? can only follow a normal character or . or []");
X			}
X
X			/* it is okay -- make it prefix instead of postfix */
X			ADD_META(build, peek);
X
X			/* take care of "needfirst" - is this the first char? */
X			if (needfirst && peek == M_PLUS && !IS_META(token))
X			{
X				re->first = token;
X			}
X			needfirst = 0;
X
X			/* we used "peek" -- need to refill it */
X			peek = gettoken(&text, re);
X			if (IS_CLOSURE(peek))
X			{
X				FAIL("* or \\+ or \\? doubled up");
X			}
X		}
X		else if (!IS_META(token))
X		{
X			/* normal char is NOT argument of closure */
X			if (needfirst)
X			{
X				re->first = token;
X				needfirst = 0;
X			}
X			re->minlen++;
X		}
X		else if (token == M_ANY || IS_CLASS(token))
X		{
X			/* . or [] is NOT argument of closure */
X			needfirst = 0;
X			re->minlen++;
X		}
X
X		/* the "token" character is not closure -- process it normally */
X		if (token == M_BEGLINE)
X		{
X			/* set the BOL flag instead of storing M_BEGLINE */
X			re->bol = 1;
X		}
X		else if (IS_META(token))
X		{
X			ADD_META(build, token);
X		}
X		else
X		{
X			*build++ = token;
X		}
X	}
X
X	/* end it with a \) which MUST MATCH the opening \( */
X	ADD_META(build, M_END(0));
X	if (end_sp > 0)
X	{
X		FAIL("Not enough \\)s");
X	}
X
X	return re;
X}
X
X
X
X/*---------------------------------------------------------------------------*/
X
X
X/* This function checks for a match between a character and a token which is
X * known to represent a single character.  It returns 0 if they match, or
X * 1 if they don't.
X */
Xint match1(re, ch, token)
X	regexp		*re;
X	REG char	ch;
X	REG int		token;
X{
X	if (!ch)
X	{
X		/* the end of a line can't match any RE of width 1 */
X		return 1;
X	}
X	if (token == M_ANY)
X	{
X		return 0;
X	}
X	else if (IS_CLASS(token))
X	{
X		if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7)))
X			return 0;
X	}
X	else if (ch == token
X		|| (*o_ignorecase && isupper(ch) && tolower(ch) == token))
X	{
X		return 0;
X	}
X	return 1;
X}
X
X
X
X/* This function checks characters up to and including the next closure, at
X * which point it does a recursive call to check the rest of it.  This function
X * returns 0 if everything matches, or 1 if something doesn't match.
X */
Xint match(re, str, prog, here)
X	regexp		*re;	/* the regular expression */
X	char		*str;	/* the string */
X	REG char	*prog;	/* a portion of re->program, an compiled RE */
X	REG char	*here;	/* a portion of str, the string to compare it to */
X{
X	REG int		token;
X	REG int		nmatched;
X	REG int		closure;
X
X	for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog))
X	{
X		switch (token)
X		{
X		/*case M_BEGLINE: can't happen; re->bol is used instead */
X		  case M_ENDLINE:
X			if (*here)
X				return 1;
X			break;
X
X		  case M_BEGWORD:
X			if (here != str &&
X			   (here[-1] == '_' ||
X			     isascii(here[-1]) && isalnum(here[-1])))
X				return 1;
X			break;
X
X		  case M_ENDWORD:
X			if (here[0] == '_' || isascii(here[0]) && isalnum(here[0]))
X				return 1;
X			break;
X
X		  case M_START(0):
X		  case M_START(1):
X		  case M_START(2):
X		  case M_START(3):
X		  case M_START(4):
X		  case M_START(5):
X		  case M_START(6):
X		  case M_START(7):
X		  case M_START(8):
X		  case M_START(9):
X			re->startp[token - M_START(0)] = (char *)here;
X			break;
X
X		  case M_END(0):
X		  case M_END(1):
X		  case M_END(2):
X		  case M_END(3):
X		  case M_END(4):
X		  case M_END(5):
X		  case M_END(6):
X		  case M_END(7):
X		  case M_END(8):
X		  case M_END(9):
X			re->endp[token - M_END(0)] = (char *)here;
X			if (token == M_END(0))
X			{
X				return 0;
X			}
X			break;
X
X		  default: /* literal, M_CLASS(n), or M_ANY */
X			if (match1(re, *here, token) != 0)
X				return 1;
X			here++;
X		}
X	}
X
X	/* C L O S U R E */
X
X	/* step 1: see what we have to match against, and move "prog" to point
X	 * the the remainder of the compiled RE.
X	 */
X	closure = token;
X	prog++, token = GET_META(prog);
X	prog++;
X
X	/* step 2: see how many times we can match that token against the string */
X	for (nmatched = 0;
X	     (closure != M_QMARK || nmatched < 1) && *here && match1(re, *here, token) == 0;
X	     nmatched++, here++)
X	{
X	}
X
X	/* step 3: try to match the remainder, and back off if it doesn't */
X	while (nmatched >= 0 && match(re, str, prog, here) != 0)
X	{
X		nmatched--;
X		here--;
X	}
X
X	/* so how did it work out? */
X	if (nmatched >= ((closure == M_PLUS) ? 1 : 0))
X		return 0;
X	return 1;
X}
X
X
X
X/* This function searches through a string for text that matches an RE. */
Xint regexec(re, str, bol)
X	regexp	*re;	/* the compiled regexp to search for */
X	char	*str;	/* the string to search through */
X	int	bol;	/* boolean: does str start at the beginning of a line? */
X{
X	char	*prog;	/* the entry point of re->program */
X	int	len;	/* length of the string */
X	REG char	*here;
X
X	/* if must start at the beginning of a line, and this isn't, then fail */
X	if (re->bol && !bol)
X	{
X		return 0;
X	}
X
X	len = strlen(str);
X	prog = re->program + 1 + 32 * re->program[0];
X
X	/* search for the RE in the string */
X	if (re->bol)
X	{
X		/* must occur at BOL */
X		if ((re->first
X			&& match1(re, *(char *)str, re->first))/* wrong first letter? */
X		 || len < re->minlen			/* not long enough? */
X		 || match(re, (char *)str, prog, str))	/* doesn't match? */
X			return 0;			/* THEN FAIL! */
X	}
X#ifndef CRUNCH
X	else if (!*o_ignorecase)
X	{
X		/* can occur anywhere in the line, noignorecase */
X		for (here = (char *)str;
X		     (re->first && re->first != *here)
X			|| match(re, (char *)str, prog, here);
X		     here++, len--)
X		{
X			if (len < re->minlen)
X				return 0;
X		}
X	}
X#endif
X	else
X	{
X		/* can occur anywhere in the line, ignorecase */
X		for (here = (char *)str;
X		     (re->first && match1(re, *here, (int)re->first))
X			|| match(re, (char *)str, prog, here);
X		     here++, len--)
X		{
X			if (len < re->minlen)
X				return 0;
X		}
X	}
X
X	/* if we didn't fail, then we must have succeeded */
X	return 1;
X}
X
X#else /* NO_MAGIC */
X
Xregexp *regcomp(exp)
X	char	*exp;
X{
X	char	*src;
X	char	*dest;
X	regexp	*re;
X	int	i;
X
X	/* allocate a big enough regexp structure */
X#ifdef lint
X	re = (regexp *)0;
X#else
X	re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp)));
X#endif
X	if (!re)
X	{
X		regerror("Could not malloc a regexp structure");
X		return (regexp *)0;
X	}
X
X	/* initialize all fields of the structure */
X	for (i = 0; i < NSUBEXP; i++)
X	{
X		re->startp[i] = re->endp[i] = (char *)0;
X	}
X	re->minlen = 0;
X	re->first = 0;
X	re->bol = 0;
X
X	/* copy the string into it, translating ^ and $ as needed */
X	for (src = exp, dest = re->program + 1; *src; src++)
X	{
X		switch (*src)
X		{
X		  case '^':
X			if (src == exp)
X			{
X				re->bol += 1;
X			}
X			else
X			{
X				*dest++ = '^';
X				re->minlen++;
X			}
X			break;
X
X		  case '$':
X			if (!src[1])
X			{
X				re->bol += 2;
X			}
X			else
X			{
X				*dest++ = '$';
X				re->minlen++;
X			}
X			break;
X
X		  case '\\':
X			if (src[1])
X			{
X				*dest++ = *++src;
X				re->minlen++;
X			}
X			else
X			{
X				regerror("extra \\ at end of regular expression");
X			}
X			break;
X
X		  default:
X			*dest++ = *src;
X			re->minlen++;
X		}
X	}
X	*dest = '\0';
X
X	return re;
X}
X
X
X/* This "helper" function checks for a match at a given location.  It returns
X * 1 if it matches, 0 if it doesn't match here but might match later on in the
X * string, or -1 if it could not possibly match
X */
Xstatic int reghelp(prog, string, bolflag)
X	struct regexp	*prog;
X	char		*string;
X	int		bolflag;
X{
X	char		*scan;
X	char		*str;
X
X	/* if ^, then require bolflag */
X	if ((prog->bol & 1) && !bolflag)
X	{
X		return -1;
X	}
X
X	/* if it matches, then it will start here */
X	prog->startp[0] = string;
X
X	/* compare, possibly ignoring case */
X	if (*o_ignorecase)
X	{
X		for (scan = &prog->program[1]; *scan; scan++, string++)
X			if (tolower(*scan) != tolower(*string))
X				return *string ? 0 : -1;
X	}
X	else
X	{
X		for (scan = &prog->program[1]; *scan; scan++, string++)
X			if (*scan != *string)
X				return *string ? 0 : -1;
X	}
X
X	/* if $, then require string to end here, too */
X	if ((prog->bol & 2) && *string)
X	{
X		return 0;
X	}
X
X	/* if we get to here, it matches */
X	prog->endp[0] = string;
X	return 1;
X}
X
X
X
Xint regexec(prog, string, bolflag)
X	struct regexp	*prog;
X	char		*string;
X	int		bolflag;
X{
X	int		rc;
X
X	/* keep trying to match it */
X	for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0))
X	{
X		string++;
X	}
X
X	/* did we match? */
X	return rc == 1;
X}
X#endif
eof
if test `wc -c <regexp.c` -ne 16871
then
echo regexp.c damaged!
fi
fi

if test -f regexp.h -a "$1" != -f
then
echo Will not overwrite regexp.h
else
echo Extracting regexp.h
sed 's/^X//' >regexp.h <<\eof
X/*
X * Definitions etc. for regexp(3) routines.
X *
X * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
X * not the System V one.
X */
X#define NSUBEXP  10
X
Xtypedef struct regexp {
X	char	*startp[NSUBEXP];
X	char	*endp[NSUBEXP];
X	int	minlen;		/* length of shortest possible match */
X	char	first;		/* first character, if known; else \0 */
X	char	bol;		/* boolean: must start at beginning of line? */
X	char	program[1];	/* Unwarranted chumminess with compiler. */
X} regexp;
X
Xextern regexp *regcomp();
Xextern int regexec();
Xextern void regsub();
Xextern void regerror();
eof
if test `wc -c <regexp.h` -ne 579
then
echo regexp.h damaged!
fi
fi

if test -f regsub.c -a "$1" != -f
then
echo Will not overwrite regsub.c
else
echo Extracting regsub.c
sed 's/^X//' >regsub.c <<\eof
X/* regsub.c */
X
X/* This file contains the regsub() function, which performs substitutions
X * after a regexp match has been found.
X */
X
X#include <ctype.h>
X#include "config.h"
X#include "vi.h"
X#include "regexp.h"
X
Xstatic char *previous;	/* a copy of the text from the previous substitution */
X
X/* perform substitutions after a regexp match */
Xvoid regsub(re, src, dst)
X	regexp		*re;
X	REG char	*src;
X	REG char	*dst;
X{
X	REG char	*cpy;
X	REG char	*end;
X	REG char	c;
X	char		*start;
X#ifndef CRUNCH
X	int		mod;
X
X	mod = 0;
X#endif
X
X	start = src;
X	while ((c = *src++) != '\0')
X	{
X#ifndef NO_MAGIC
X		/* recognize any meta characters */
X		if (c == '&' && *o_magic)
X		{
X			cpy = re->startp[0];
X			end = re->endp[0];
X		}
X		else if (c == '~' && *o_magic)
X		{
X			cpy = previous;
X			if (cpy)
X				end = cpy + strlen(cpy);
X		}
X		else
X#endif /* not NO_MAGIC */
X		if (c == '\\')
X		{
X			c = *src++;
X			switch (c)
X			{
X#ifndef NO_MAGIC
X			  case '0':
X			  case '1':
X			  case '2':
X			  case '3':
X			  case '4':
X			  case '5':
X			  case '6':
X			  case '7':
X			  case '8':
X			  case '9':
X				/* \0 thru \9 mean "copy subexpression" */
X				c -= '0';
X				cpy = re->startp[c];
X				end = re->endp[c];
X				break;
X# ifndef CRUNCH
X			  case 'U':
X			  case 'u':
X			  case 'L':
X			  case 'l':
X				/* \U and \L mean "convert to upper/lowercase" */
X				mod = c;
X				continue;
X
X			  case 'E':
X			  case 'e':
X				/* \E ends the \U or \L */
X				mod = 0;
X				continue;
X# endif /* not CRUNCH */
X			  case '&':
X				/* "\&" means "original text" */
X				if (*o_magic)
X				{
X					*dst++ = c;
X					continue;
X				}
X				cpy = re->startp[0];
X				end = re->endp[0];
X				break;
X
X			  case '~':
X				/* "\~" means "previous text, if any" */
X				if (*o_magic)
X				{
X					*dst++ = c;
X					continue;
X				}
X				cpy = previous;
X				if (cpy)
X					end = cpy + strlen(cpy);
X				break;
X#else /* NO_MAGIC */
X			  case '&':
X				/* "\&" means "original text" */
X				cpy = re->startp[0];
X				end = re->endp[0];
X				break;
X
X			  case '~':
X				/* "\~" means "previous text, if any" */
X				cpy = previous;
X				if (cpy)
X					end = cpy + strlen(cpy);
X				break;
X#endif /* NO_MAGIC */
X			  default:
X				/* ordinary char preceded by backslash */
X				*dst++ = c;
X				continue;
X			}
X		}
X		else
X		{
X			/* ordinary character, so just copy it */
X			*dst++ = c;
X			continue;
X		}
X
X		/* Note: to reach this point in the code, we must have evaded
X		 * all "continue" statements.  To do that, we must have hit
X		 * a metacharacter that involves copying.
X		 */
X
X		/* if there is nothing to copy, loop */
X		if (!cpy)
X			continue;
X
X		/* copy over a portion of the original */
X		while (cpy < end)
X		{
X#ifndef NO_MAGIC
X# ifndef CRUNCH
X			switch (mod)
X			{
X			  case 'U':
X			  case 'u':
X				/* convert to uppercase */
X				if (isascii(*cpy) && islower(*cpy))
X				{
X					*dst++ = toupper(*cpy);
X					cpy++;
X				}
X				else
X				{
X					*dst++ = *cpy++;
X				}
X				break;
X
X			  case 'L':
X			  case 'l':
X				/* convert to lowercase */
X				if (isascii(*cpy) && isupper(*cpy))
X				{
X					*dst++ = tolower(*cpy);
X					cpy++;
X				}
X				else
X				{
X					*dst++ = *cpy++;
X				}
X				break;
X
X			  default:
X				/* copy without any conversion */
X				*dst++ = *cpy++;
X			}
X
X			/* \u and \l end automatically after the first char */
X			if (mod && (mod == 'u' || mod == 'l'))
X			{
X				mod = 0;
X			}
X# else /* CRUNCH */
X			*dst++ = *cpy++;
X# endif /* CRUNCH */
X#else /* NO_MAGIC */
X			*dst++ = *cpy++;
X#endif /* NO_MAGIC */
X		}
X	}
X	*dst = '\0';
X
X	/* remember what text we inserted this time */
X	if (previous)
X		free(previous);
X	previous = (char *)malloc((unsigned)(strlen(start) + 1));
X	if (previous)
X		strcpy(previous, start);
X}
eof
if test `wc -c <regsub.c` -ne 3614
then
echo regsub.c damaged!
fi
fi

if test -f system.c -a "$1" != -f
then
echo Will not overwrite system.c
else
echo Extracting system.c
sed 's/^X//' >system.c <<\eof
X/* system.c  -- UNIX version */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains a new version of the system() function and related stuff.
X *
X * Entry points are:
X *	system(cmd)	- run a single shell command
X *	wildcard(names)	- expand wildcard characters in filanames
X *	filter(m,n,cmd)	- run text lines through a filter program
X *
X * This is probably the single least portable file in the program.  The code
X * shown here should work correctly if it links at all; it will work on UNIX
X * and any O.S./Compiler combination which adheres to UNIX forking conventions.
X */
X
X#include "config.h"
X#include "vi.h"
X#include <signal.h>
Xextern char	**environ;
X
X#if ANY_UNIX
X
X/* This is a new version of the system() function.  The only difference
X * between this one and the library one is: this one uses the o_shell option.
X */
Xint system(cmd)
X	char	*cmd;	/* a command to run */
X{
X	int	status;	/* exit status of the command */
X
X	/* warn the user if the file hasn't been saved yet */
X	if (*o_warn && tstflag(file, MODIFIED))
X	{
X		if (mode == MODE_VI)
X		{
X			mode = MODE_COLON;
X		}
X		msg("Warning: \"%s\" has been modified but not yet saved", origname);
X	}
X
X	signal(SIGINT, SIG_IGN);
X	switch (fork())
X	{
X	  case -1:						/* error */
X		msg("fork() failed");
X		status = -1;
X		break;
X
X	  case 0:						/* child */
X		/* for the child, close all files except stdin/out/err */
X		for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
X		{
X		}
X
X		signal(SIGINT, SIG_DFL);
X		if (cmd == o_shell)
X		{
X			execle(o_shell, o_shell, (char *)0, environ);
X		}
X		else
X		{
X			execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
X		}
X		msg("execle(\"%s\", ...) failed", o_shell);
X		exit(1); /* if we get here, the exec failed */
X
X	  default:						/* parent */
X		wait(&status);
X		signal(SIGINT, trapint);
X	}
X
X	return status;
X}
X
X/* This private function opens a pipe from a filter.  It is similar to the
X * system() function above, and to popen(cmd, "r").
X */
Xstatic int rpipe(cmd, in)
X	char	*cmd;	/* the filter command to use */
X	int	in;	/* the fd to use for stdin */
X{
X	int	r0w1[2];/* the pipe fd's */
X
X	/* make the pipe */
X	if (pipe(r0w1) < 0)
X	{
X		return -1;	/* pipe failed */
X	}
X
X	/* The parent process (elvis) ignores signals while the filter runs.
X	 * The child (the filter program) will reset this, so that it can
X	 * catch the signal.
X	 */
X	signal(SIGINT, SIG_IGN);
X
X	switch (fork())
X	{
X	  case -1:						/* error */
X		return -1;
X
X	  case 0:						/* child */
X		/* close the "read" end of the pipe */
X		close(r0w1[0]);
X
X		/* redirect stdout to go to the "write" end of the pipe */
X		close(1);
X		dup(r0w1[1]);
X		close(2);
X		dup(r0w1[1]);
X		close(r0w1[1]);
X
X		/* redirect stdin */
X		if (in != 0)
X		{
X			close(0);
X			dup(in);
X			close(in);
X		}
X
X		/* the filter should accept SIGINT signals */
X		signal(SIGINT, SIG_DFL);
X
X		/* exec the shell to run the command */
X		execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
X		exit(1); /* if we get here, exec failed */
X
X	  default:						/* parent */
X		/* close the "write" end of the pipe */	
X		close(r0w1[1]);
X
X		return r0w1[0];
X	}
X}
X
X#endif /* non-DOS */
X
X#if OSK
X
X/* This private function opens a pipe from a filter.  It is similar to the
X * system() function above, and to popen(cmd, "r").
X */
Xstatic int rpipe(cmd, in)
X	char	*cmd;	/* the filter command to use */
X	int	in;	/* the fd to use for stdin */
X{
X
X    char **argblk;
X	char *p, *buffer, *command;
X	int stdinp, stdoutp;
X	unsigned addstack = 0;
X	int words, len, loop = 1;
X	int fp, pipe_pid;
X	extern int os9forkc();
X	extern char *index();
X
X	command = cmd;
X	words = 0;
X	if ((buffer = (char*) malloc(strlen(cmd))) == (char*) 0)
X		return 0;
X
X	do {
X		if (!(p = index(command, ' '))) {
X			loop--;
X			len = strlen(command);
X		}
X		else
X			len = p - command;
X		words++;	
X		while (command[len] && command[len] == ' ')
X			len++;
X		if (!command[len])
X			break;
X		command = command + len;
X	}	
X	while (loop);
X	if ((argblk = (char **)malloc((words+1) * sizeof(char*))) == (char **)0)
X		return 0;
X	command = cmd;
X	words = 0;
X	do {
X		if (!(p = index(command, ' '))) {
X			loop--;
X			len = strlen(command);
X		}
X		else
X			len = p - command;
X		strncpy(buffer, command, len);
X		argblk[words++] = buffer;
X		buffer += len;
X		*buffer++ = '\0';
X		while (command[len] && command[len] == ' ')
X			len++;
X		if (!command[len])
X			break;
X		command = command + len;
X	} while (loop);
X	if (argblk[words - 1][0] == '#') 
X		addstack = 1024 * atoi(&argblk[--words][1]);
X	argblk[words] = 0;
X
X	stdoutp = dup(1);
X	close(1);               /* close stdout */
X	if ((fp = open("/pipe",S_IREAD)) < 0) {
X		dup(stdoutp);
X		close(stdoutp);
X		return 0;
X	}
X	if (in != 0) {
X		stdinp = dup(0);
X		close(0);
X		dup(in);
X		close(in);
X	}
X	pipe_pid = os9exec(os9forkc,argblk[0],argblk,environ,addstack,0,3);
X	if (pipe_pid == -1) {
X		fclose(fp);
X		dup(stdoutp);
X		close(stdoutp);
X		if (in != 0) {
X			close(0);
X			dup(stdinp);
X		}
X		return 0;
X	}
X	fp = (short)dup(1);     /* save pipe */
X	close(1);               /* get rid of the pipe */
X	dup(stdoutp);           /* restore old stdout */
X	close(stdoutp);         /* close path to stdout copy */
X	if (in != 0) {
X		close(0);
X		dup(stdinp);
X	}
X	return fp;
X}	
X#endif
X
X#if ANY_UNIX || OSK
X
X/* This function closes the pipe opened by rpipe(), and returns 0 for success */
Xstatic int rpclose(fd)
X	int	fd;
X{
X	int	status;
X
X	close(fd);
X	wait(&status);
X	signal(SIGINT, trapint);
X	return status;
X}
X
X#endif /* non-DOS */
X
X/* This function expands wildcards in a filename or filenames.  It does this
X * by running the "echo" command on the filenames via the shell; it is assumed
X * that the shell will expand the names for you.  If for any reason it can't
X * run echo, then it returns the names unmodified.
X */
X
X#if MSDOS || TOS
X#define	PROG	"wildcard "
X#define	PROGLEN	9
X#include <string.h>
X#else
X#define	PROG	"echo "
X#define	PROGLEN	5
X#endif
X
Xchar *wildcard(names)
X	char	*names;
X{
X	int	i, j, fd;
X	REG char *s, *d;
X
X
X	/* build the echo command */
X	if (names != tmpblk.c)
X	{
X		/* the names aren't in tmpblk.c, so we can do it the easy way */
X		strcpy(tmpblk.c, PROG);
X		strcat(tmpblk.c, names);
X	}
X	else
X	{
X		/* the names are already in tmpblk.c, so shift them to make
X		 * room for the word "echo "
X		 */
X		for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
X		{
X			*--d = *--s;
X		}
X		strncpy(names, PROG, PROGLEN);
X	}
X
X	/* run the command & read the resulting names */
X	fd = rpipe(tmpblk.c, 0);
X	if (fd < 0) return names;
X	i = 0;
X	do
X	{
X		j = tread(fd, tmpblk.c + i, BLKSIZE - i);
X		i += j;
X	} while (j > 0);
X
X	/* successful? */
X	if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
X	{
X		tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
X		return tmpblk.c;
X	}
X	else
X	{
X		return names;
X	}
X}
X
X/* This function runs a range of lines through a filter program, and replaces
X * the original text with the filtered version.  As a special case, if "to"
X * is MARK_UNSET, then it runs the filter program with stdin coming from
X * /dev/null, and inserts any output lines.
X */
Xint filter(from, to, cmd)
X	MARK	from, to;	/* the range of lines to filter */
X	char	*cmd;		/* the filter command */
X{
X	int	scratch;	/* fd of the scratch file */
X	int	fd;		/* fd of the pipe from the filter */
X	char	scrout[50];	/* name of the scratch out file */
X	MARK	new;		/* place where new text should go */
X	int	i;
X
X	/* write the lines (if specified) to a temp file */
X	if (to)
X	{
X		/* we have lines */
X#if MSDOS || TOS
X		strcpy(scrout, o_directory);
X		if ((i=strlen(scrout)) && strchr("\\/:", scrout[i-1]))
X			scrout[i++]=SLASH;
X		strcpy(scrout+i, SCRATCHOUT+3);
X#else
X		sprintf(scrout, SCRATCHOUT, o_directory);
X#endif
X		mktemp(scrout);
X		cmd_write(from, to, CMD_BANG, 0, scrout);
X
X		/* use those lines as stdin */
X		scratch = open(scrout, O_RDONLY);
X		if (scratch < 0)
X		{
X			unlink(scrout);
X			return -1;
X		}
X	}
X	else
X	{
X		scratch = 0;
X	}
X
X	/* start the filter program */
X	fd = rpipe(cmd, scratch);
X	if (fd < 0)
X	{
X		if (to)
X		{
X			close(scratch);
X			unlink(scrout);
X		}
X		return -1;
X	}
X
X	ChangeText
X	{
X		/* adjust MARKs for whole lines, and set "new" */
X		from &= ~(BLKSIZE - 1);
X		if (to)
X		{
X			to &= ~(BLKSIZE - 1);
X			to += BLKSIZE;
X			new = to;
X		}
X		else
X		{
X			new = from + BLKSIZE;
X		}
X
X		/* repeatedly read in new text and add it */
X		while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
X		{
X			tmpblk.c[i] = '\0';
X			add(new, tmpblk.c);
X			for (i = 0; tmpblk.c[i]; i++)
X			{
X				if (tmpblk.c[i] == '\n')
X				{
X					new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
X				}
X				else
X				{
X					new++;
X				}
X			}
X		}
X	}
X
X	/* delete old text, if any */
X	if (to)
X	{
X		delete(from, to);
X	}
X
X	/* Reporting... */
X	rptlabel = "more";
X	if (rptlines < 0)
X	{
X		rptlines = -rptlines;
X		rptlabel = "less";
X	}
X
X	/* cleanup */
X	rpclose(fd);
X	if (to)
X	{
X		close(scratch);
X		unlink(scrout);
X	}
X	return 0;
X}
eof
if test `wc -c <system.c` -ne 8849
then
echo system.c damaged!
fi
fi

if test -f tinytcap.c -a "$1" != -f
then
echo Will not overwrite tinytcap.c
else
echo Extracting tinytcap.c
sed 's/^X//' >tinytcap.c <<\eof
X/* tinytcap.c */
X
X/* This file contains functions which simulate the termcap functions, but which
X * can only describe the capabilities of the ANSI.SYS and NANSI.SYS drivers on
X * an MS-DOS system or the VT-52 emulator of an Atari-ST.  These functions
X * do *NOT* access a "termcap" database file.
X */
X
X#include "config.h"
X#if MSDOS || TOS || MINIX || COHERENT
X
X#define CAP(str) CAP2((str)[0], (str)[1])
X#define CAP2(a,b) (((a) << 8) + ((b) & 0xff))
X
X#if MSDOS
X# define VAL2(v,a)	(a)
X# define VAL3(v,a,n)	(nansi ? (n) : (a))
Xstatic int	nansi;
X#endif
X
X#if TOS
X# define VAL2(v,a)	(v)
X# define VAL3(v,a,n)	(v)
X#endif
X
X#if MINIX || COHERENT
X# define VAL2(v,a)	(a)
X# define VAL3(v,a,n)	(n)
X#endif
X
X
X/*ARGSUSED*/
Xint tgetent(bp, name)
X	char	*bp;	/* buffer for storing the entry -- ignored */
X	char	*name;	/* name of the entry */
X{
X#if MSDOS
X	nansi = strcmp(name, "ansi");
X#endif
X	return 1;
X}
X
Xint tgetnum(id)
X	char	*id;
X{
X	switch (CAP(id))
X	{
X	  case CAP2('l','i'):	return 25;
X	  case CAP2('c','o'):	return 80;
X	  case CAP2('s','g'):	return 0;
X	  case CAP2('u','g'):	return 0;
X	  default:		return -1;
X	}
X}
X
Xint tgetflag(id)
X	char	*id;
X{
X	switch (CAP(id))
X	{
X#if !MINIX || COHERENT
X	  case CAP2('a','m'):	return 1;
X#endif
X	  case CAP2('b','s'):	return 1;
X	  case CAP2('m','i'):	return 1;
X	  default:		return 0;
X	}
X}
X
X/*ARGSUSED*/
Xchar *tgetstr(id, bp)
X	char	*id;
X	char	**bp;	/* pointer to pointer to buffer - ignored */
X{
X	switch (CAP(id))
X	{
X	  case CAP2('c','e'):	return VAL2("\033K", "\033[K");
X	  case CAP2('c','l'):	return VAL2("\033E", "\033[2J");
X
X	  case CAP2('a','l'):	return VAL3("\033L", (char *)0, "\033[L");
X	  case CAP2('d','l'):	return VAL3("\033M", (char *)0, "\033[M");
X
X	  case CAP2('c','m'):	return VAL2("\033Y%i%+ %+ ", "\033[%i%d;%dH");
X	  case CAP2('d','o'):	return VAL2("\033B", "\033[B");
X	  case CAP2('n','d'):	return VAL2("\033C", "\033[C");
X	  case CAP2('u','p'):	return VAL2("\033A", "\033[A");
X	  case CAP2('t','i'):	return VAL2("\033e", "");
X	  case CAP2('t','e'):	return VAL2("", "");
X
X	  case CAP2('s','o'):	return VAL2("\033p", "\033[7m");
X	  case CAP2('s','e'):	return VAL2("\033q", "\033[m");
X	  case CAP2('u','s'):	return VAL2((char *)0, "\033[4m");
X	  case CAP2('u','e'):	return VAL2((char *)0, "\033[m");
X	  case CAP2('m','d'):	return VAL2((char *)0, "\033[1m");
X	  case CAP2('m','e'):	return VAL2((char *)0, "\033[m");
X
X#if MINIX || COHERENT
X	  case CAP2('k','u'):	return "\033[A";
X	  case CAP2('k','d'):	return "\033[B";
X	  case CAP2('k','l'):	return "\033[D";
X	  case CAP2('k','r'):	return "\033[C";
X	  case CAP2('k','P'):	return "\033[V";
X	  case CAP2('k','N'):	return "\033[U";
X	  case CAP2('k','h'):	return "\033[H";
X# if MINIX
X	  case CAP2('k','H'):	return "\033[Y";
X# else /* COHERENT */
X	  case CAP2('k','H'):	return "\033[24H";
X# endif
X#else /* MS-DOS or TOS */
X	  case CAP2('k','u'):	return "#H";
X	  case CAP2('k','d'):	return "#P";
X	  case CAP2('k','l'):	return "#K";
X	  case CAP2('k','r'):	return "#M";
X	  case CAP2('k','h'):	return "#G";
X	  case CAP2('k','H'):	return "#O";
X	  case CAP2('k','P'):	return "#I";
X	  case CAP2('k','N'):	return "#Q";
X#endif
X
X	  default:		return (char *)0;
X	}
X}
X
X/*ARGSUSED*/
Xchar *tgoto(cm, destcol, destrow)
X	char	*cm;	/* cursor movement string -- ignored */
X	int	destcol;/* destination column, 0 - 79 */
X	int	destrow;/* destination row, 0 - 24 */
X{
X	static char buf[30];
X
X#if MSDOS || MINIX || COHERENT
X	sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1);
X#endif
X#if TOS
X	sprintf(buf, "\033Y%c%c", ' ' + destrow, ' ' + destcol);
X#endif
X	return buf;
X}
X
X/*ARGSUSED*/
Xvoid tputs(cp, affcnt, outfn)
X	char	*cp;		/* the string to output */
X	int	affcnt;		/* number of affected lines -- ignored */
X	int	(*outfn)();	/* the output function */
X{
X	while (*cp)
X	{
X		(*outfn)(*cp);
X		cp++;
X	}
X}
X#endif
eof
if test `wc -c <tinytcap.c` -ne 3767
then
echo tinytcap.c damaged!
fi
fi

if test -f tio.c -a "$1" != -f
then
echo Will not overwrite tio.c
else
echo Extracting tio.c
sed 's/^X//' >tio.c <<\eof
X/* tio.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains terminal I/O functions */
X
X#include "config.h"
X#if BSD || COHERENT
X# include <setjmp.h>
X#endif
X#include <signal.h>
X#include "vi.h"
X
X
X/* This function reads in a line from the terminal. */
Xint vgets(prompt, buf, bsize)
X	char	prompt;	/* the prompt character, or '\0' for none */
X	char	*buf;	/* buffer into which the string is read */
X	int	bsize;	/* size of the buffer */
X{
X	int	len;	/* how much we've read so far */
X	int	ch;	/* a character from the user */
X	int	quoted;	/* is the next char quoted? */
X	int	tab;	/* column position of cursor */
X	char	widths[132];	/* widths of characters */
X#ifndef NO_DIGRAPH
X	int	erased;	/* 0, or first char of a digraph */
X#endif
X
X	/* show the prompt */
X	move(LINES - 1, 0);
X	tab = 0;
X	if (prompt)
X	{
X		addch(prompt);
X		tab = 1;
X	}
X	clrtoeol();
X	refresh();
X
X	/* read in the line */
X#ifndef NO_DIGRAPH
X	erased =
X#endif
X	quoted = len = 0;
X	for (;;)
X	{
X		ch = getkey(quoted ? 0 : WHEN_EX);
X
X		/* some special conversions */
X		if (ch == ctrl('D') && len == 0)
X			ch = ctrl('[');
X#ifndef NO_DIGRAPH
X		if (*o_digraph && erased != 0 && ch != '\b')
X		{
X			ch = digraph(erased, ch);
X			erased = 0;
X		}
X#endif
X
X		/* inhibit detection of special chars (except ^J) after a ^V */
X		if (quoted && ch != '\n')
X		{
X			ch |= 256;
X		}
X
X		/* process the character */
X		switch(ch)
X		{
X		  case ctrl('V'):
X			qaddch('^');
X			qaddch('\b');
X			quoted = TRUE;
X			break;
X
X		  case ctrl('['):
X			return -1;
X
X		  case '\n':
X#if OSK
X		  case '\l':
X#else
X		  case '\r':
X#endif
X			clrtoeol();
X			goto BreakBreak;
X
X		  case '\b':
X			if (len > 0)
X			{
X				len--;
X#ifndef NO_DIGRAPH
X				erased = buf[len];
X#endif
X				for (ch = widths[len]; ch > 0; ch--)
X					addch('\b');
X				if (mode == MODE_EX)
X				{
X					clrtoeol();
X				}
X				tab -= widths[len];
X			}
X			else
X			{
X				return -1;
X			}
X			break;
X
X		  default:
X			/* strip off quotation bit */
X			if (ch & 256)
X			{
X				ch &= ~256;
X				quoted = FALSE;
X				qaddch(' ');
X				qaddch('\b');
X			}
X			/* add & echo the char */
X			if (len < bsize - 1)
X			{
X				if (ch == '\t')
X				{
X					widths[len] = *o_tabstop - (tab % *o_tabstop);
X					addstr("        " + 8 - widths[len]);
X					tab += widths[len];
X				}
X				else if (ch > 0 && ch < ' ') /* > 0 by GB */
X				{
X					addch('^');
X					addch(ch + '@');
X					widths[len] = 2;
X					tab += 2;
X				}
X				else if (ch == '\177')
X				{
X					addch('^');
X					addch('?');
X					widths[len] = 2;
X					tab += 2;
X				}
X				else
X				{
X					addch(ch);
X					widths[len] = 1;
X					tab++;
X				}
X				buf[len++] = ch;
X			}
X			else
X			{
X				beep();
X			}
X		}
X	}
XBreakBreak:
X	refresh();
X	buf[len] = '\0';
X	return len;
X}
X
X
X/* ring the terminal's bell */
Xvoid beep()
X{
X	if (*o_vbell)
X	{
X		do_VB();
X		refresh();
X	}
X	else if (*o_errorbells)
X	{
X		ttywrite("\007", 1);
X	}
X}
X
Xstatic int	manymsgs; /* This variable keeps msgs from overwriting each other */
Xstatic char	pmsg[80]; /* previous message (waiting to be displayed) */
X
X
Xstatic int showmsg()
X{
X	/* if there is no message to show, then don't */
X	if (!manymsgs)
X		return FALSE;
X
X	/* display the message */
X	move(LINES - 1, 0);
X	if (*pmsg)
X	{
X		standout();
X		qaddch(' ');
X		qaddstr(pmsg);
X		qaddch(' ');
X		standend();
X	}
X	clrtoeol();
X
X	manymsgs = FALSE;
X	return TRUE;
X}
X
X
Xvoid endmsgs()
X{
X	if (manymsgs)
X	{
X		showmsg();
X		addch('\n');
X	}
X}
X
X/* Write a message in an appropriate way.  This should really be a varargs
X * function, but there is no such thing as vwprintw.  Hack!!!
X *
X * In MODE_EX or MODE_COLON, the message is written immediately, with a
X * newline at the end.
X *
X * In MODE_VI, the message is stored in a character buffer.  It is not
X * displayed until getkey() is called.  msg() will call getkey() itself,
X * if necessary, to prevent messages from being lost.
X *
X * msg("")		- clears the message line
X * msg("%s %d", ...)	- does a printf onto the message line
X */
X/*VARARGS1*/
Xvoid msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
X	char	*fmt;
X	long	arg1, arg2, arg3, arg4, arg5, arg6, arg7;
X{
X	if (mode != MODE_VI)
X	{
X		sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
X		qaddstr(pmsg);
X		addch('\n');
X		exrefresh();
X	}
X	else
X	{
X		/* wait for keypress between consecutive msgs */
X		if (manymsgs)
X		{
X			getkey(WHEN_MSG);
X		}
X
X		/* real message */
X		sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
X		manymsgs = TRUE;
X	}
X}
X
X
X/* This function calls refresh() if the option exrefresh is set */
Xvoid exrefresh()
X{
X	char	*scan;
X
X	/* If this ex command wrote ANYTHING set exwrote so vi's  :  command
X	 * can tell that it must wait for a user keystroke before redrawing.
X	 */
X	for (scan=kbuf; scan<stdscr; scan++)
X		if (*scan == '\n')
X			exwrote = TRUE;
X
X#if MICROSOFT			/* avoid compiler bug */
X	scan = stdscr;
X#define	stdscr	scan
X#endif
X	/* now we do the refresh thing */
X	if (*o_exrefresh)
X	{
X		refresh();
X	}
X	else
X	{
X		wqrefresh(stdscr);
X	}
X#if MICROSOFT
X#undef	stdscr
X	stdscr = scan;
X#endif
X	if (mode != MODE_VI)
X	{
X		manymsgs = FALSE;
X	}
X}
X
X
X
X/* These are used for typeahead, and also for fudging the visual @ command */
Xstatic char	keybuf[100];	/* array of already-read keys */
Xstatic int	nkeys;		/* total number of keys in keybuf */
Xstatic int	next;		/* index of next key to return */
X#ifndef NO_AT
Xstatic int	atnext;		/* index of next key for "@", or 0 normally */
Xint fromcutbuf(cbname)
X	int	cbname;
X{
X	int	len;
X
X	/* fail if we're already doing an @ macro */
X	if (atnext > 0 && keybuf[atnext])
X	{
X		msg("Can't nest @ commands");
X		return FALSE;
X	}
X
X	/* use the empty portion of keybuf[] to get chars from the cut buffer */
X	len = cb2str(cbname, keybuf + nkeys, sizeof keybuf - nkeys);
X	if (len < 0)
X	{
X		msg("Invalid cut buffer name.  Must be a-z");
X		return FALSE;
X	}
X	if (len == 0)
X	{
X		msg("Cut buffer \"%c is empty", cbname);
X		return FALSE;
X	}
X	else if (len >= sizeof keybuf - nkeys)
X	{
X		msg("Cut buffer \"%c is too large to execute", cbname);
X		return FALSE;
X	}
X
X	/* prepare to "read" those keys on subsequent getkey() calls */
X	atnext = nkeys;
X	return TRUE;
X}
X#endif
X
X/* This array describes mapped key sequences */
Xstatic struct _keymap
X{
X	char	*name;		/* name of the key, or NULL */
X	char	rawin[LONGKEY];	/* the unmapped version of input */
X	char	cooked[80];	/* the mapped version of input */
X	int	len;		/* length of the unmapped version */
X	int	when;		/* when is this key mapped? */
X}
X	mapped[MAXMAPS];
X
X#if !MSDOS && !TOS
X# if BSD || COHERENT
Xstatic jmp_buf env_timeout;
Xstatic int dummy()
X{
X	longjmp(env_timeout, 1);
X	return 0;
X}
X# else 
Xstatic int dummy()
X{
X}
X# endif
X#endif
X
X/* This function reads in a keystroke for VI mode.  It automatically handles
X * key mapping.
X */
Xint getkey(when)
X	int		when;		/* which bits must be ON? */
X{
X	static char	*cooked;	/* rawin, or pointer to converted key */ 
X	static int	oldwhen;	/* "when" from last time */
X	static int	oldleft;
X	static long	oldtop;
X	static long	oldnlines;
X	static char	*cshape;	/* current cursor shape */
X	REG char	*kptr;		/* &keybuf[next] */
X	REG struct _keymap *km;	/* used to count through keymap */
X	REG int		i, j, k;
X
X#ifdef DEBUG
X	watch();
X#endif
X
X	/* if this key is needed for delay between multiple error messages,
X	 * then reset the manymsgs flag and abort any mapped key sequence.
X	 */
X	if (showmsg())
X	{
X		if (when == WHEN_MSG)
X		{
X			qaddstr("[More...]");
X			refresh();
X			cooked = (char *)0;
X		}
X		else if (when == WHEN_VIINP || when == WHEN_VIREP)
X		{
X			redraw(cursor, TRUE);
X		}
X	}
X
X#ifndef NO_AT
X	/* if we're in the middle of a visual @ macro, take atnext */
X	if (atnext > 0)
X	{
X		if (keybuf[atnext])
X		{
X			return keybuf[atnext++];
X		}
X		atnext = 0;
X	}
X#endif
X
X	/* if we're doing a mapped key, get the next char */
X	if (cooked && *cooked)
X	{
X		return *cooked++;
X	}
X
X	/* if keybuf is empty, fill it */
X	if (next == nkeys)
X	{
X#ifndef NO_CURSORSHAPE
X		/* make sure the cursor is the right shape */
X		if (has_CQ)
X		{
X			cooked = cshape;
X			switch (when)
X			{
X			  case WHEN_EX:		cooked = CX;	break;
X			  case WHEN_VICMD:	cooked = CV;	break;
X			  case WHEN_VIINP:	cooked = CI;	break;
X			  case WHEN_VIREP:	cooked = CR;	break;
X			}
X			if (cooked != cshape)
X			{
X				cshape = cooked;
X				switch (when)
X				{
X				  case WHEN_EX:		do_CX();	break;
X				  case WHEN_VICMD:	do_CV();	break;
X				  case WHEN_VIINP:	do_CI();	break;
X				  case WHEN_VIREP:	do_CR();	break;
X				}
X			}
X			cooked = (char *)0;
X		}
X#endif
X
X#ifndef NO_SHOWMODE
X		/* if "showmode" then say which mode we're in */
X		if (*o_smd
X		 && mode == MODE_VI
X		 && (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines))
X		{
X			oldwhen = when;
X			oldtop = topline;
X			oldleft = leftcol;
X			oldnlines = nlines;
X
X			if (when & WHEN_VICMD)
X			{
X				redraw(cursor, FALSE);
X				move(LINES - 1, COLS - 10);
X				standout();
X				addstr("Command");
X				standend();
X				redraw(cursor, FALSE);
X			}
X			else if (when & WHEN_VIINP)
X			{
X				redraw(cursor, TRUE);
X				move(LINES - 1, COLS - 10);
X				standout();
X				addstr(" Input ");
X				standend();
X				redraw(cursor, TRUE);
X			}
X			else if (when & WHEN_VIREP)
X			{
X				redraw(cursor, TRUE);
X				move(LINES - 1, COLS - 10);
X				standout();
X				addstr("Replace");
X				standend();
X				redraw(cursor, TRUE);
X			}
X		}
X		else
X#endif
X
X		/* redraw if getting a VI command */
X		if (when & WHEN_VICMD)
X		{
X			redraw(cursor, FALSE);
X		}
X
X		/* read the rawin keystrokes */
X		refresh();
X		while ((nkeys = ttyread(keybuf, sizeof keybuf)) < 0)
X		{
X			/* terminal was probably resized */
X			*o_lines = LINES;
X			*o_columns = COLS;
X			if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
X			{
X				redraw(MARK_UNSET, FALSE);
X				redraw(cursor, (when & WHEN_VICMD) == 0);
X				refresh();
X			}
X		}
X		next = 0;
X
X		/* if nkeys == 0 then we've reached EOF of an ex script. */
X		if (nkeys == 0)
X		{
X			tmpabort(TRUE);
X			move(LINES - 1, 0);
X			clrtoeol();
X			refresh();
X			endwin();
X			exit(1);
X		}
X	}
X
X	/* see how many mapped keys this might be */
X	kptr = &keybuf[next];
X	for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
X	{
X		if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
X		{
X			if (km->len > nkeys - next)
X			{
X				if (!strncmp(km->rawin, kptr, nkeys - next))
X				{
X					j++;
X				}
X			}
X			else
X			{
X				if (!strncmp(km->rawin, kptr, km->len))
X				{
X					j++;
X					k = i;
X				}
X			}
X		}
X	}
X
X	/* if more than one, try to read some more */
X	while (j > 1)
X	{
X#if BSD || COHERENT
X		if (setjmp(env_timeout))
X		{
X			/* we timed out - assume no mapping */
X			j = 0;
X			break;
X		}
X#endif
X#if ANY_UNIX
X		signal(SIGALRM, dummy);
X#endif
X#if OSK
X		signal(SIGQUIT, dummy);
X#endif
X		alarm((unsigned)*o_keytime);
X		i = nkeys;
X		if ((k = ttyread(keybuf + nkeys, sizeof keybuf - nkeys)) >= 0)
X		{
X			nkeys += k;
X		}
X		alarm(0);
X#if OSK
X# ifndef DEBUG
X		signal(SIGQUIT, SIG_IGN);
X# endif
X#endif
X
X		/* if we couldn't read any more, pretend 0 mapped keys */
X		if (i == nkeys)
X		{
X			j = 0;
X		}
X		else /* else we got some more - try again */
X		{
X			for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
X			{
X				if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
X				{
X					if (km->len > nkeys - next)
X					{
X						if (!strncmp(km->rawin, kptr, nkeys - next))
X						{
X							j++;
X						}
X					}
X					else
X					{
X						if (!strncmp(km->rawin, kptr, km->len))
X						{
X							j++;
X							k = i;
X						}
X					}
X				}
X			}
X		}
X	}
X
X	/* if unambiguously mapped key, use it! */
X	if (j == 1 && k >= 0)
X	{
X		next += mapped[k].len;
X		cooked = mapped[k].cooked;
X#ifndef NO_EXTENSIONS
X		if ((when & (WHEN_VIINP|WHEN_VIREP))
X		 && (mapped[k].when & WHEN_INMV))
X		{
X			return 0; /* special case, means "a movement char follows" */
X		}
X		else
X#endif
X		{
X			return *cooked++;
X		}
X	}
X	else
X	/* assume key is unmapped, but still translate weird erase key to '\b' */
X	if (keybuf[next] == ERASEKEY && when != 0)
X	{
X		next++;
X		return '\b';
X	}
X	else if (keybuf[next] == '\0')
X	{
X		next++;
X		return ('A' & 0x1f);
X	}
X	else
X	{
X		return keybuf[next++];
X	}
X}
X
X
X/* This function maps or unmaps a key */
Xvoid mapkey(rawin, cooked, when, name)
X	char	*rawin;	/* the input key sequence, before mapping */
X	char	*cooked;/* after mapping */
X	short	when;	/* bitmap of when mapping should happen */
X	char	*name;	/* name of the key, if any */
X{
X	int	i, j;
X
X#ifndef NO_EXTENSIONS
X	/* if the mapped version starts with the word "visual" then set WHEN_INMV */
X	if (!strncmp(cooked, "visual ", 7))
X	{
X		when |= WHEN_INMV;
X		cooked += 7;
X	}
X	/* if WHEN_INMV is set, then WHEN_VIINP and WHEN_VIREP must be set */
X	if (when & WHEN_INMV)
X	{
X		when |= (WHEN_VIINP | WHEN_VIREP);
X	}
X#endif
X
X	/* see if the key sequence was mapped before */
X	j = strlen(rawin);
X	for (i = 0; i < MAXMAPS; i++)
X	{
X		if (mapped[i].len == j
X		 && !strncmp(mapped[i].rawin, rawin, j)
X		 && (mapped[i].when & when))
X		{
X			break;
X		}
X	}
X
X	/* if not already mapped, then try to find a new slot to use */
X	if (i == MAXMAPS)
X	{
X		for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
X		{
X		}
X	}
X
X	/* no room for the new key? */
X	if (i == MAXMAPS)
X	{
X		msg("No room left in the key map table");
X		return;
X	}
X
X	/* map the key */
X	if (cooked && *cooked)
X	{
X		/* Map the key */
X		mapped[i].len = j;
X		strncpy(mapped[i].rawin, rawin, j);
X		strcpy(mapped[i].cooked, cooked);
X		mapped[i].when = when;
X		mapped[i].name = name;
X	}
X	else /* unmap the key */
X	{
X		mapped[i].len = 0;
X	}
X}
X
X/* Dump keys of a given type - WHEN_VICMD dumps the ":map" keys, and
X * WHEN_VIINP|WHEN_VIREP dumps the ":map!" keys
X */
Xvoid dumpkey(when)
X	int	when;	/* WHEN_XXXX of mappings to be dumped */
X{
X	int	i, len, mlen;
X	char	*scan;
X	char	*mraw;
X
X	for (i = 0; i < MAXMAPS; i++)
X	{
X		/* skip unused entries, or entries that don't match "when" */
X		if (mapped[i].len <= 0 || !(mapped[i].when & when))
X		{
X			continue;
X		}
X
X		/* dump the key label, if any */
X		len = 8;
X		if (mapped[i].name)
X		{
X			qaddstr(mapped[i].name);
X			len -= strlen(mapped[i].name);
X		}
X		do
X		{
X			qaddch(' ');
X		} while (len-- > 0);
X
X		/* dump the raw version */
X		len = 0;
X		mlen = mapped[i].len;
X		mraw = mapped[i].rawin;
X		for (scan = mraw; scan < mraw + mlen; scan++)
X		{
X			if (UCHAR(*scan) < ' ' || *scan == '\177')
X			{
X				qaddch('^');
X				qaddch(*scan ^ '@');
X				len += 2;
X			}
X			else
X			{
X				qaddch(*scan);
X				len++;
X			}
X		}
X		do
X		{
X			qaddch(' ');
X		} while (++len < 8);
X
X		/* dump the mapped version */
X#ifndef NO_EXTENSIONS
X		if ((mapped[i].when & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
X		{
X			qaddstr("visual ");
X		}
X#endif
X		for (scan = mapped[i].cooked; *scan; scan++)
X		{
X			if (UCHAR(*scan) < ' ' || *scan == '\177')
X			{
X				qaddch('^');
X				qaddch(*scan ^ '@');
X			}
X			else
X			{
X				qaddch(*scan);
X			}
X		}
X
X		addch('\n');
X		exrefresh();
X	}
X}
X
X
X
X#ifndef MKEXRC
X/* This function saves the current configuration of mapped keys to a file */
Xvoid savekeys(fd)
X	int	fd;	/* file descriptor to save them to */
X{
X	int	i;
X	char	buf[80];
X
X	/* now write a map command for each key other than the arrows */
X	for (i = 0; i < MAXMAPS; i++)
X	{
X		/* ignore keys that came from termcap */
X		if (mapped[i].name)
X		{
X			continue;
X		}
X
X		/* If this isn't used, ignore it */
X		if (mapped[i].len <= 0)
X		{
X			continue;
X		}
X
X		/* write the map command */
X#ifndef NO_EXTENSIONS
X		if (mapped[i].when & WHEN_INMV)
X		{
X#if OSK
X			char	fmt[80];
X			sprintf(fmt, "map%%s %%.%ds %%s\n", mapped[i].len);
X			sprintf(buf, fmt,
X				(mapped[i].when & WHEN_VICMD) ? "" : "!",
X#else
X			sprintf(buf, "map%s %.*s visual %s\n",
X				(mapped[i].when & WHEN_VICMD) ? "" : "!",
X				mapped[i].len,
X#endif
X				mapped[i].rawin,
X				mapped[i].cooked);
X			twrite(fd, buf, strlen(buf));
X		}
X		else
X#endif
X		{
X			if (mapped[i].when & WHEN_VICMD)
X			{
X#if OSK
X				char	fmt[80];
X				sprintf(fmt, "map %%.%ds %%s\n", mapped[i].len);
X				sprintf(buf, fmt,
X#else
X				sprintf(buf, "map %.*s %s\n", mapped[i].len,
X#endif
X					mapped[i].rawin,
X					mapped[i].cooked);
X				twrite(fd, buf, strlen(buf));
X			}
X			if (mapped[i].when & (WHEN_VIINP | WHEN_VIREP))
X			{
X#if OSK
X				char	fmt[80];
X				sprintf(fmt, "map! %%.%ds %%s\n", mapped[i].len);
X				sprintf(buf, fmt,
X#else
X				sprintf(buf, "map! %.*s %s\n", mapped[i].len,
X#endif
X					mapped[i].rawin,
X					mapped[i].cooked);
X				twrite(fd, buf, strlen(buf));
X			}
X		}
X	}
X}
X#endif
eof
if test `wc -c <tio.c` -ne 16181
then
echo tio.c damaged!
fi
fi

if test -f tmp.c -a "$1" != -f
then
echo Will not overwrite tmp.c
else
echo Extracting tmp.c
sed 's/^X//' >tmp.c <<\eof
X/* tmpfile.c */
X
X/* Author:
X *	Steve Kirkendall
X *	14407 SW Teal Blvd. #C
X *	Beaverton, OR 97005
X *	kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains functions which create & readback a TMPFILE */
X
X
X#include "config.h"
X#include <ctype.h>
X#include "vi.h"
X#if TOS
X# include <stat.h>
X#else
X# if OSK
X#  include "osk.h"
X# else
X#  include <sys/stat.h>
X# endif
X#endif
X
X
X#ifndef NO_MODELINE
Xstatic void do_modeline(l, stop)
X	long	l;	/* line number to start at */
X	long	stop;	/* line number to stop at */
X{
X	char	*str;	/* used to scan through the line */
X	char	*start;	/* points to the start of the line */
X	char	buf[80];
X
X	/* if modelines are disabled, then do nothing */
X	if (!*o_modeline)
X	{
X		return;
X	}
X
X	/* for each line... */
X	for (l = 1; l <= stop; l++)
X	{
X		/* for each position in the line.. */
X		for (str = fetchline(l); *str; str++)
X		{
X			/* if it is the start of a modeline command... */
X			if ((str[0] == 'e' && str[1] == 'x'
X			  || str[0] == 'v' && str[1] == 'i')
X			  && str[2] == ':')
X			{
X				start = str += 3;
X
X				/* find the end */
X				while (*str && *str != ':')
X				{
X					str++;
X				}
X
X				/* if it is a well-formed modeline, execute it */
X				if (*str && str - start < sizeof buf)
X				{
X					strncpy(buf, start, (int)(str - start));
X					buf[str - start] = '\0';
X					doexcmd(buf);
X					break;
X				}
X			}
X		}
X	}
X}
X#endif
X
X
X/* The FAIL() macro prints an error message and then exits. */
X#define FAIL(why,arg)	mode = MODE_EX; msg(why, arg); endwin(); exit(9)
X
X/* This is the name of the temp file */
Xstatic char	tmpname[80];
X
X/* This function creates the temp file and copies the original file into it.
X * Returns if successful, or stops execution if it fails.
X */
Xint tmpstart(filename)
X	char		*filename; /* name of the original file */
X{
X	int		origfd;	/* fd used for reading the original file */
X	struct stat	statb;	/* stat buffer, used to examine inode */
X	REG BLK		*this;	/* pointer to the current block buffer */
X	REG BLK		*next;	/* pointer to the next block buffer */
X	int		inbuf;	/* number of characters in a buffer */
X	int		nread;	/* number of bytes read */
X	REG int		j, k;
X	int		i;
X	int		sum;	/* used for calculating a checksum for this */
X	char		*scan;
X	long		nbytes;
X
X	/* switching to a different file certainly counts as a change */
X	changes++;
X	redraw(MARK_UNSET, FALSE);
X
X	/* open the original file for reading */
X	*origname = '\0';
X	if (filename && *filename)
X	{
X		strcpy(origname, filename);
X		origfd = open(origname, O_RDONLY);
X		if (origfd < 0 && errno != ENOENT)
X		{
X			msg("Can't open \"%s\"", origname);
X			return tmpstart("");
X		}
X		if (origfd >= 0)
X		{
X			if (stat(origname, &statb) < 0)
X			{
X				FAIL("Can't stat \"%s\"", origname);
X			}
X#if TOS
X			if (origfd >= 0 && (statb.st_mode & S_IJDIR))
X#else
X# if OSK
X			if (origfd >= 0 && (statb.st_mode & S_IFDIR))
X# else
X			if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
X# endif
X#endif
X			{
X				msg("\"%s\" is not a regular file", origname);
X				return tmpstart("");
X			}
X		}
X		else
X		{
X			stat(".", &statb);
X		}
X		if (origfd >= 0)
X		{
X			origtime = statb.st_mtime;
X#if MSDOS || OSK
X			if (*o_readonly || !(statb.st_mode & S_IWRITE))
X#endif
X#if TOS
X			if (*o_readonly || (statb.st_mode & S_IJRON))
X#endif
X#if ANY_UNIX
X			if (*o_readonly || !(statb.st_mode &
X				  (statb.st_uid != geteuid() ? 0022 : 0200)))
X#endif
X			{
X				setflag(file, READONLY);
X			}
X		}
X		else
X		{
X			origtime = 0L;
X		}
X	}
X	else
X	{
X		setflag(file, NOFILE);
X		origfd = -1;
X		origtime = 0L;
X		stat(".", &statb);
X	}
X
X	/* generate a checksum from the file's name */
X	for (sum = 0, scan = origname + strlen(origname);
X	     --scan >= origname && (isascii(*scan) && isalnum(*scan) || *scan == '.');
X	     sum = sum + *scan)
X	{
X	}
X	sum &= 0xf;
X
X	/* make a name for the tmp file */
X#if MSDOS || TOS
X	/* MS-Dos doesn't allow multiple slashes, but supports drives
X	 * with current directories.
X	 * This relies on TMPNAME beginning with "%s\\"!!!!
X	 */
X	strcpy(tmpname, o_directory);
X	if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
X		tmpname[i++]=SLASH;
X	sprintf(tmpname+i, TMPNAME+3, sum, statb.st_ino, statb.st_dev);
X#else
X	sprintf(tmpname, TMPNAME, o_directory, sum, statb.st_ino, statb.st_dev);
X#endif
X
X	/* make sure nobody else is editing the same file */
X	if (access(tmpname, 0) == 0)
X	{
X		if (*origname)
X		{
X			msg("\"%s\" is busy", filename);
X			return tmpstart("");
X		}
X		FAIL("\"%s\" is busy", filename);
X	}
X
X	/* create the temp file */
X#if ANY_UNIX
X	close(creat(tmpname, 0600));		/* only we can read it */
X#else
X	close(creat(tmpname, FILEPERMS));	/* anybody body can read it, alas */
X#endif
X	tmpfd = open(tmpname, O_RDWR | O_BINARY);
X	if (tmpfd < 0)
X	{
X		FAIL("Can't create temporary file, errno=%d", errno);
X		return 1;
X	}
X
X	/* allocate space for the header in the file */
X	write(tmpfd, hdr.c, (unsigned)BLKSIZE);
X
X#ifndef NO_RECYCLE
X	/* initialize the block allocator */
X	/* This must already be done here, before the first attempt
X	 * to write to the new file! GB */
X	garbage();
X#endif
X
X	/* initialize lnum[] */
X	for (i = 1; i < MAXBLKS; i++)
X	{
X		lnum[i] = INFINITY;
X	}
X	lnum[0] = 0;
X
X	/* if there is no original file, then create a 1-line file */
X	if (origfd < 0)
X	{
X		hdr.n[0] = 0;	/* invalid inode# denotes new file */
X
X		this = blkget(1); 	/* get the new text block */
X		strcpy(this->c, "\n");	/* put a line in it */
X
X		lnum[1] = 1L;	/* block 1 ends with line 1 */
X		nlines = 1L;	/* there is 1 line in the file */
X		nbytes = 1L;
X
X		if (*origname)
X		{
X			msg("\"%s\" [NEW FILE]  1 line, 1 char", origname);
X		}
X		else
X		{
X			msg("\"[NO FILE]\"  1 line, 1 char");
X		}
X	}
X	else /* there is an original file -- read it in */
X	{
X		hdr.n[0] = statb.st_ino;
X		nbytes = nlines = 0;
X
X		/* preallocate 1 "next" buffer */
X		i = 1;
X		next = blkget(i);
X		inbuf = 0;
X
X		/* loop, moving blocks from orig to tmp */
X		for (;;)
X		{
X			/* "next" buffer becomes "this" buffer */
X			this = next;
X
X			/* read [more] text into this block */
X			nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
X			if (nread < 0)
X			{
X				close(origfd);
X				close(tmpfd);
X				tmpfd = -1;
X				unlink(tmpname);
X				FAIL("Error reading \"%s\"", origname);
X			}
X
X			/* convert NUL characters to something else */
X			for (k = inbuf; k < inbuf + nread; k++)
X			{
X				if (!this->c[k])
X				{
X					setflag(file, HADNUL);
X					this->c[k] = 0x80;
X				}
X			}
X			inbuf += nread;
X
X			/* if the buffer is empty, quit */
X			if (inbuf == 0)
X			{
X				goto FoundEOF;
X			}
X
X#if MSDOS || TOS
X/* BAH! MS text mode read fills inbuf, then compresses eliminating \r
X   but leaving garbage at end of buf. The same is true for TURBOC. GB. */
X
X			memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
X#endif
X
X			/* search backward for last newline */
X			for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
X			{
X			}
X			if (k++ < 0)
X			{
X				if (inbuf >= BLKSIZE - 1)
X				{
X					k = 80;
X				}
X				else
X				{
X					k = inbuf;
X				}
X			}
X
X			/* allocate next buffer */
X			next = blkget(++i);
X
X			/* move fragmentary last line to next buffer */
X			inbuf -= k;
X			for (j = 0; k < BLKSIZE; j++, k++)
X			{
X				next->c[j] = this->c[k];
X				this->c[k] = 0;
X			}
X
X			/* if necessary, add a newline to this buf */
X			for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
X			{
X			}
X			if (this->c[k] != '\n')
X			{
X				setflag(file, ADDEDNL);
X				this->c[k + 1] = '\n';
X			}
X
X			/* count the lines in this block */
X			for (k = 0; k < BLKSIZE && this->c[k]; k++)
X			{
X				if (this->c[k] == '\n')
X				{
X					nlines++;
X				}
X				nbytes++;
X			}
X			lnum[i - 1] = nlines;
X		}
XFoundEOF:
X
X		/* if this is a zero-length file, add 1 line */
X		if (nlines == 0)
X		{
X			this = blkget(1); 	/* get the new text block */
X			strcpy(this->c, "\n");	/* put a line in it */
X
X			lnum[1] = 1;	/* block 1 ends with line 1 */
X			nlines = 1;	/* there is 1 line in the file */
X			nbytes = 1;
X		}
X
X#if MSDOS || TOS
X		/* each line has an extra CR that we didn't count yet */
X		nbytes += nlines;
X#endif
X
X		/* report the number of lines in the file */
X		msg("\"%s\" %s %ld line%s, %ld char%s",
X			origname,
X			(tstflag(file, READONLY) ? "[READONLY]" : ""),
X			nlines,
X			nlines == 1 ? "" : "s",
X			nbytes,
X			nbytes == 1 ? "" : "s");
X	}
X
X	/* initialize the cursor to start of line 1 */
X	cursor = MARK_FIRST;
X
X	/* close the original file */
X	close(origfd);
X
X	/* any other messages? */
X	if (tstflag(file, HADNUL))
X	{
X		msg("This file contained NULs.  They've been changed to \\x80 chars");
X	}
X	if (tstflag(file, ADDEDNL))
X	{
X		msg("Newline characters have been inserted to break up long lines");
X	}
X
X#ifndef NO_MODELINE
X	if (nlines > 10)
X	{
X		do_modeline(1L, 5L);
X		do_modeline(nlines - 4L, nlines);
X	}
X	else
X	{
X		do_modeline(1L, nlines);
X	}
X#endif
X	return 0;
X}
X
X
X
X/* This function copies the temp file back onto an original file.
X * Returns TRUE if successful, or FALSE if the file could NOT be saved.
X */
Xint tmpsave(filename, bang)
X	char	*filename;	/* the name to save it to */
X	int	bang;		/* forced write? */
X{
X	int		fd;	/* fd of the file we're writing to */
X	REG int		len;	/* length of a text block */
X	REG BLK		*this;	/* a text block */
X	long		bytes;	/* byte counter */
X	REG int		i;
X
X	/* if no filename is given, assume the original file name */
X	if (!filename || !*filename)
X	{
X		filename = origname;
X	}
X
X	/* if still no file name, then fail */
X	if (!*filename)
X	{
X		msg("Don't know a name for this file -- NOT WRITTEN");
X		return FALSE;
X	}
X
X	/* can't rewrite a READONLY file */
X	if (!strcmp(filename, origname) && *o_readonly && !bang)
X	{
X		msg("\"%s\" [READONLY] -- NOT WRITTEN", filename);
X		return FALSE;
X	}
X
X	/* open the file */
X	if (*filename == '>' && filename[1] == '>')
X	{
X		filename += 2;
X		while (*filename == ' ' || *filename == '\t')
X		{
X			filename++;
X		}
X#ifdef O_APPEND
X		fd = open(filename, O_WRONLY|O_APPEND);
X#else
X		fd = open(filename, O_WRONLY);
X		lseek(fd, 0L, 2);
X#endif
X	}
X	else
X	{
X		/* either the file must not exist, or it must be the original
X		 * file, or we must have a bang
X		 */
X		if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang)
X		{
X			msg("File already exists - Use :w! to overwrite");
X			return FALSE;
X		}
X		fd = creat(filename, FILEPERMS);
X	}
X	if (fd < 0)
X	{
X		msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
X		return FALSE;
X	}
X
X	/* write each text block to the file */
X	bytes = 0L;
X	for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
X	{
X		for (len = 0; len < BLKSIZE && this->c[len]; len++)
X		{
X		}
X		twrite(fd, this->c, len);
X		bytes += len;
X	}
X
X	/* reset the "modified" flag */
X	clrflag(file, MODIFIED);
X	significant = FALSE;
X
X	/* report lines & characters */
X#if MSDOS || TOS
X	bytes += nlines; /* for the inserted carriage returns */
X#endif
X	if (strncmp(filename, o_directory, strlen(o_directory)))
X	{
X		msg("Wrote \"%s\"  %ld lines, %ld characters", filename, nlines, bytes);
X	}
X
X	/* close the file */
X	close(fd);
X
X	return TRUE;
X}
X
X
X/* This function deletes the temporary file.  If the file has been modified
X * and "bang" is FALSE, then it returns FALSE without doing anything; else
X * it returns TRUE.
X *
X * If the "autowrite" option is set, then instead of returning FALSE when
X * the file has been modified and "bang" is false, it will call tmpend().
X */
Xint tmpabort(bang)
X	int	bang;
X{
X	/* if there is no file, return successfully */
X	if (tmpfd < 0)
X	{
X		return TRUE;
X	}
X
X	/* see if we must return FALSE -- can't quit */
X	if (!bang && tstflag(file, MODIFIED))
X	{
X		/* if "autowrite" is set, then act like tmpend() */
X		if (*o_autowrite)
X			return tmpend(bang);
X		else
X			return FALSE;
X	}
X
X	/* delete the tmp file */
X	cutswitch(tmpname);
X	close(tmpfd);
X	tmpfd = -1;
X	unlink(tmpname);
X	strcpy(prevorig, origname);
X	prevline = markline(cursor);
X	*origname = '\0';
X	origtime = 0L;
X	blkinit();
X	nlines = 0;
X	initflags();
X	return TRUE;
X}
X
X/* This function saves the file if it has been modified, and then deletes
X * the temporary file. Returns TRUE if successful, or FALSE if the file
X * needs to be saved but can't be.  When it returns FALSE, it will not have
X * deleted the tmp file, either.
X */
Xint tmpend(bang)
X	int	bang;
X{
X	/* save the file if it has been modified */
X	if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
X	{
X		return FALSE;
X	}
X
X	/* delete the tmp file */
X	tmpabort(TRUE);
X
X	return TRUE;
X}
X
X
X/* If the tmp file has been changed, then this function will force those
X * changes to be written to the disk, so that the tmp file will survive a
X * system crash or power failure.
X */
X#if MSDOS || TOS || OSK
Xsync()
X{
X# if OSK
X	/* OS9 doesn't need an explicit sync operation, but the linker
X	 * demands something called sync(), so this is a dummy function.
X	 */
X#else
X	/* MS-DOS and TOS don't flush their buffers until the file is closed,
X	 * so here we close the tmp file and then immediately reopen it.
X	 */
X	close(tmpfd);
X	tmpfd = open(tmpname, O_RDWR | O_BINARY);
X#endif
X}
X#endif
eof
if test `wc -c <tmp.c` -ne 12770
then
echo tmp.c damaged!
fi
fi

exit 0
-------------------------------------------------------------------------------
Steve Kirkendall     kirkenda at cs.pdx.edu      Grad student at Portland State U.



More information about the Alt.sources mailing list