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