elvis 1.3 - a clone of vi/ex, part 5 of 6
Steve Kirkendall
kirkenda at eecs.cs.pdx.edu
Sat Aug 25 03:59:09 AEST 1990
Archive-name: elvis1.3/part5
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# regexp.c
# regexp.h
# regsub.c
# sysdos.c
# system.c
# tinytcap.c
# tio.c
# tmp.c
# This archive created: Fri Aug 24 10:29:59 1990
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'regexp.c'
then
echo shar: "will not over-write existing file 'regexp.c'"
else
cat << \SHAR_EOF > 'regexp.c'
/* ALTERED VERSION */
/*
* regcomp and regexec -- regsub and regerror are elsewhere
*
* Copyright (c) 1986 by University of Toronto.
* Written by Henry Spencer. Not derived from licensed software.
*
* Permission is granted to anyone to use this software for any
* purpose on any computer system, and to redistribute it freely,
* subject to the following restrictions:
*
* 1. The author is not responsible for the consequences of use of
* this software, no matter how awful, even if they arise
* from defects in it.
*
* 2. The origin of this software must not be misrepresented, either
* by explicit claim or by omission.
*
* 3. Altered versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* Beware that some of this code is subtly aware of the way operator
* precedence is structured in regular expressions. Serious changes in
* regular-expression syntax might require a total rethink.
*
* The third parameter to regexec was added by Martin C. Atkins.
* Andy Tanenbaum also made some changes.
* Steve Kirkendall changed the syntax and added o_magic, o_ignorecase
*/
#include <ctype.h>
#include "config.h"
#include "vi.h"
#include "regexp.h"
#define NULL (char *)0
extern char *ustrchr(); /* version of strchr which uses o_ignorecase */
extern int ustrncmp(); /* version of strcmp which uses o_ignorecase */
/*
* The first byte of the regexp internal "program" is actually this magic
* number; the start node begins in the second byte.
*/
#define MAGIC 0234
#ifdef NO_MAGIC
# include "nomagic.c"
#else
/*
* The "internal use only" fields in regexp.h are present to pass info from
* compile to execute that permits the execute phase to run lots faster on
* simple cases. They are:
*
* regstart char that must begin a match; '\0' if none obvious
* reganch is the match anchored (at beginning-of-line only)?
* regmust string (pointer into program) that match must include, or NULL
* regmlen length of regmust string
*
* Regstart and reganch permit very fast decisions on suitable starting points
* for a match, cutting down the work a lot. Regmust permits fast rejection
* of lines that cannot possibly match. The regmust tests are costly enough
* that regcomp() supplies a regmust only if the r.e. contains something
* potentially expensive (at present, the only such thing detected is * or +
* at the start of the r.e., which can involve a lot of backup). Regmlen is
* supplied because the test in regexec() needs it and regcomp() is computing
* it anyway.
*/
/*
* Structure for regexp "program". This is essentially a linear encoding
* of a nondeterministic finite-state machine (aka syntax charts or
* "railroad normal form" in parsing technology). Each node is an opcode
* plus a "next" pointer, possibly plus an operand. "Next" pointers of
* all nodes except BRANCH implement concatenation; a "next" pointer with
* a BRANCH on both ends of it is connecting two alternatives. (Here we
* have one of the subtle syntax dependencies: an individual BRANCH (as
* opposed to a collection of them) is never concatenated with anything
* because of operator precedence.) The operand of some types of node is
* a literal string; for others, it is a node leading into a sub-FSM. In
* particular, the operand of a BRANCH node is the first node of the branch.
* (NB this is *not* a tree structure: the tail of the branch connects
* to the thing following the set of BRANCHes.) The opcodes are:
*/
/* definition number opnd? meaning */
#define END 0 /* no End of program. */
#define BOL 1 /* no Match "" at beginning of line. */
#define EOL 2 /* no Match "" at end of line. */
#define ANY 3 /* no Match any one character. */
#define ANYOF 4 /* str Match any character in this string. */
#define ANYBUT 5 /* str Match any character not in this string. */
#define BRANCH 6 /* node Match this alternative, or the next... */
#define BACK 7 /* no Match "", "next" ptr points backward. */
#define EXACTLY 8 /* str Match this string. */
#define NOTHING 9 /* no Match empty string. */
#define STAR 10 /* node Match this (simple) thing 0 or more times. */
#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
#define BOW 12 /* no Match "" at front of word */
#define EOW 13 /* no Match "" at rear of word */
#define OPEN 20 /* no Mark this point in input as start of #n. */
/* OPEN+1 is number 1, etc. */
#define CLOSE 30 /* no Analogous to OPEN. */
/*
* Opcode notes:
*
* BRANCH The set of branches constituting a single choice are hooked
* together with their "next" pointers, since precedence prevents
* anything being concatenated to any individual branch. The
* "next" pointer of the last BRANCH in a choice points to the
* thing following the whole choice. This is also where the
* final "next" pointer of each individual branch points; each
* branch starts with the operand node of a BRANCH node.
*
* BACK Normal "next" pointers all implicitly point forward; BACK
* exists to make loop structures possible.
*
* STAR,PLUS '?', and complex '*' and '+', are implemented as circular
* BRANCH structures using BACK. Simple cases (one character
* per match) are implemented with STAR and PLUS for speed
* and to minimize recursive plunges.
*
* OPEN,CLOSE ...are numbered at compile time.
*/
/*
* A node is one char of opcode followed by two chars of "next" pointer.
* "Next" pointers are stored as two 8-bit pieces, high order first. The
* value is a positive offset from the opcode of the node containing it.
* An operand, if any, simply follows the node. (Note that much of the
* code generation knows about this implicit relationship.)
*
* Using two bytes for the "next" pointer is vast overkill for most things,
* but allows patterns to get big without disasters.
*/
#define OP(p) (*(p))
#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
#define OPERAND(p) ((p) + 3)
/*
* Utility definitions.
*/
#define UCHARAT(p) UCHAR(*p)
#define FAIL(m) { regerror(m); return 0; }
#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
#define META "^$.[()|?+*\\"
/*
* Flags to be passed up and down.
*/
#define HASWIDTH 01 /* Known never to match null string. */
#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
#define SPSTART 04 /* Starts with * or +. */
#define WORST 0 /* Worst case. */
/*
* Global work variables for regcomp().
*/
static char *regstr; /* the RE being compiled */
static char *regparse; /* Input-scan pointer. */
static int regnpar; /* () count. */
static char regdummy;
static char *regcode; /* Code-emit pointer; ®dummy = don't. */
static long regsize; /* Code size. */
/*
* Forward declarations for regcomp()'s friends.
*/
#ifndef STATIC
#define STATIC static
#endif
STATIC char *reg();
STATIC char *regbranch();
STATIC char *regpiece();
STATIC char *regatom();
STATIC char *regnode();
STATIC char *regnext();
STATIC void regc();
STATIC void reginsert();
STATIC void regtail();
STATIC void regoptail();
/*
- regcomp - compile a regular expression into internal code
*
* We can't allocate space until we know how big the compiled form will be,
* but we can't compile it (and thus know how big it is) until we've got a
* place to put the code. So we cheat: we compile it twice, once with code
* generation turned off and size counting turned on, and once "for real".
* This also means that we don't allocate space until we are sure that the
* thing really will compile successfully, and we never have to move the
* code and thus invalidate pointers into it. (Note that it has to be in
* one piece because free() must be able to free it all.)
*
* Beware that the optimization-preparation code in here knows about some
* of the structure of the compiled regexp.
*/
regexp *
regcomp(exp)
char *exp;
{
register regexp *r;
register char *scan;
register char *longest;
register int len;
int flags;
if (exp == NULL)
FAIL("NULL argument");
/* Make the start address of this RE available to everybody */
regstr = exp;
/* First pass: determine size, legality. */
regparse = exp;
regnpar = 1;
regsize = 0L;
regcode = ®dummy;
regc(MAGIC);
if (reg(0, &flags) == NULL)
return (regexp *)0;
/* Small enough for pointer-storage convention? */
if (regsize >= 32767L) /* Probably could be 65535L. */
FAIL("regexp too big");
/* Allocate space. */
r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
if (!r)
FAIL("out of space");
/* Second pass: emit code. */
regparse = exp;
regnpar = 1;
regcode = r->program;
regc(MAGIC);
if (reg(0, &flags) == NULL)
return (regexp *)0;
/* Dig out information for optimizations. */
r->regstart = '\0'; /* Worst-case defaults. */
r->reganch = 0;
r->regmust = NULL;
r->regmlen = 0;
scan = r->program+1; /* First BRANCH. */
if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
scan = OPERAND(scan);
/* Starting-point info. */
if (OP(scan) == EXACTLY)
r->regstart = *OPERAND(scan);
else if (OP(scan) == BOL)
r->reganch++;
/*
* If there's something expensive in the r.e., find the
* longest literal string that must appear and make it the
* regmust. Resolve ties in favor of later strings, since
* the regstart check works with the beginning of the r.e.
* and avoiding duplication strengthens checking. Not a
* strong reason, but sufficient in the absence of others.
*/
if (flags&SPSTART) {
longest = NULL;
len = 0;
for (; scan != NULL; scan = regnext(scan))
if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
longest = OPERAND(scan);
len = strlen(OPERAND(scan));
}
r->regmust = longest;
r->regmlen = len;
}
}
return(r);
}
/*
- reg - regular expression, i.e. main body or parenthesized thing
*
* Caller must absorb opening parenthesis.
*
* Combining parenthesis handling with the base level of regular expression
* is a trifle forced, but the need to tie the tails of the branches to what
* follows makes it hard to avoid.
*/
static char *
reg(paren, flagp)
int paren; /* Parenthesized? */
int *flagp;
{
register char *ret;
register char *br;
register char *ender;
register int parno;
int flags;
*flagp = HASWIDTH; /* Tentatively. */
/* Make an OPEN node, if parenthesized. */
if (paren) {
if (regnpar >= NSUBEXP)
FAIL("too many ()");
parno = regnpar;
regnpar++;
ret = regnode(OPEN+parno);
} else
ret = NULL;
/* Pick up the branches, linking them together. */
br = regbranch(&flags);
if (br == NULL)
return(NULL);
if (ret != NULL)
regtail(ret, br); /* OPEN -> first. */
else
ret = br;
if (!(flags&HASWIDTH))
*flagp &= ~HASWIDTH;
*flagp |= flags&SPSTART;
while (regparse[0] == '\\' && regparse[1] == '|') {
regparse += 2;
br = regbranch(&flags);
if (br == NULL)
return(NULL);
regtail(ret, br); /* BRANCH -> BRANCH. */
if (!(flags&HASWIDTH))
*flagp &= ~HASWIDTH;
*flagp |= flags&SPSTART;
}
/* Make a closing node, and hook it on the end. */
ender = regnode((paren) ? CLOSE+parno : END);
regtail(ret, ender);
/* Hook the tails of the branches to the closing node. */
for (br = ret; br != NULL; br = regnext(br))
regoptail(br, ender);
/* Check for proper termination. */
if (paren && (*regparse++ != '\\' || *regparse++ != ')')) {
FAIL("unmatched \\(\\)");
} else if (!paren && *regparse != '\0') {
if (regparse[0] == '\\' && regparse[1] == ')') {
FAIL("unmatched \\(\\)");
} else
FAIL("junk on end"); /* "Can't happen". */
/* NOTREACHED */
}
return(ret);
}
/*
- regbranch - one alternative of an | operator
*
* Implements the concatenation operator.
*/
static char *
regbranch(flagp)
int *flagp;
{
register char *ret;
register char *chain;
register char *latest;
int flags;
*flagp = WORST; /* Tentatively. */
ret = regnode(BRANCH);
chain = NULL;
while (*regparse != '\0' && (regparse[0] != '\\' ||
regparse[1] != '|' && regparse[1] != ')')) {
latest = regpiece(&flags);
if (latest == NULL)
return(NULL);
*flagp |= flags&HASWIDTH;
if (chain == NULL) /* First piece. */
*flagp |= flags&SPSTART;
else
regtail(chain, latest);
chain = latest;
}
if (chain == NULL) /* Loop ran zero times. */
regnode(NOTHING);
return(ret);
}
/*
- regpiece - something followed by possible [*+?]
*
* Note that the branching code sequences used for ? and the general cases
* of * and + are somewhat optimized: they use the same NOTHING node as
* both the endmarker for their branch list and the body of the last branch.
* It might seem that this node could be dispensed with entirely, but the
* endmarker role is not redundant.
*/
static char *
regpiece(flagp)
int *flagp;
{
register char *ret;
register char op;
register char *next;
int flags;
ret = regatom(&flags);
if (ret == NULL)
return(NULL);
op = *regparse;
if (!ISMULT(op)) {
*flagp = flags;
return(ret);
}
if (!(flags&HASWIDTH) && op != '?')
FAIL("*+ operand could be empty");
*flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
if (op == '*' && (flags&SIMPLE))
reginsert(STAR, ret);
else if (op == '*') {
/* Emit x* as (x&|), where & means "self". */
reginsert(BRANCH, ret); /* Either x */
regoptail(ret, regnode(BACK)); /* and loop */
regoptail(ret, ret); /* back */
regtail(ret, regnode(BRANCH)); /* or */
regtail(ret, regnode(NOTHING)); /* null. */
} else if (op == '+' && (flags&SIMPLE))
reginsert(PLUS, ret);
else if (op == '+') {
/* Emit x+ as x(&|), where & means "self". */
next = regnode(BRANCH); /* Either */
regtail(ret, next);
regtail(regnode(BACK), ret); /* loop back */
regtail(next, regnode(BRANCH)); /* or */
regtail(ret, regnode(NOTHING)); /* null. */
} else if (op == '?') {
/* Emit x? as (x|) */
reginsert(BRANCH, ret); /* Either x */
regtail(ret, regnode(BRANCH)); /* or */
next = regnode(NOTHING); /* null. */
regtail(ret, next);
regoptail(ret, next);
}
regparse++;
if (ISMULT(*regparse))
FAIL("nested *?+");
return(ret);
}
/*
- regatom - the lowest level
*
* Optimization: gobbles an entire sequence of ordinary characters so that
* it can turn them into a single node, which is smaller to store and
* faster to run. Backslashed characters are exceptions, each becoming a
* separate node; the code is simpler that way and it's not worth fixing.
*
* !sk! Most of my changes are located here. I fixed the backslash bug, and
* modified things so that ()<>| are only special when preceded by a backslash
* Since I'm shoehorning this into code which didn't particularly care about
* backslashes, interfaces are a bit rough. This routine expects to be called
* with regparse pointing to a backslash, if there is one; but it exits with
* regparse pointing to the ()| AFTER the backslash, except for \< and \>.
* Yeah, really messy.
*/
static char *
regatom(flagp)
int *flagp;
{
register char *ret;
int flags;
register int len;
register char ender;
int more;
*flagp = WORST; /* Tentatively. */
/* The first character may be special */
switch (*regparse++) {
case '^':
return regnode(BOL);
case '$':
return regnode(EOL);
case '.':
if (*o_magic) {
ret = regnode(ANY);
*flagp |= HASWIDTH|SIMPLE;
return ret;
}
break;
case '[':
if (*o_magic) {
register int class;
register int classend;
if (*regparse == '^') { /* Complement of range. */
ret = regnode(ANYBUT);
regparse++;
} else
ret = regnode(ANYOF);
if (*regparse == ']' || *regparse == '-')
regc(*regparse++);
while (*regparse != '\0' && *regparse != ']') {
if (*regparse == '-') {
regparse++;
if (*regparse == ']' || *regparse == '\0')
regc('-');
else {
class = UCHARAT(regparse-2)+1;
classend = UCHARAT(regparse);
if (class > classend+1)
FAIL("invalid [] range");
for (; class <= classend; class++)
regc(class);
regparse++;
}
} else
regc(*regparse++);
}
regc('\0');
if (*regparse != ']')
FAIL("unmatched []");
regparse++;
*flagp |= HASWIDTH|SIMPLE;
return ret;
}
break;
case '\\':
if (*o_magic) {
switch (*regparse++) {
case '(':
ret = reg(1, &flags);
if (ret == NULL)
return(NULL);
*flagp |= flags&(HASWIDTH|SPSTART);
return ret;
case '\0':
case '|':
case ')':
FAIL("internal urp"); /* Supposed to be caught earlier. */
case '<':
return regnode(BOW);
case '>':
return regnode(EOW);
}
regparse--;
}
break;
case '?':
case '+':
case '*':
if (*o_magic)
{
FAIL("?+* follows nothing");
}
break;
}
/* The first char wasn't special, so start building an EXACTLY string */
regparse--;
for (len = 0, more = 1; regparse[len] && more; len++) {
switch (regparse[len]) {
case '^':
case '$':
len--;
more = 0;
break;
case '+':
case '*':
case '?':
case '.':
case '[':
if (*o_magic) {
len--;
more = 0;
}
break;
case '\\':
switch (regparse[++len]) {
case '<':
case '>':
case '(':
case ')':
case '|':
len -= 2; /* put the whole \< or \> back in string */
more = 0;
break;
case '\0':
FAIL("Trailing \\");
}
break;
}
}
if (len <= 0)
FAIL("internal disaster");
ender = *(regparse+len);
if (len > 1 && ISMULT(ender))
len--; /* Back off clear of ?+* operand. */
*flagp |= HASWIDTH;
if (len == 1)
*flagp |= SIMPLE;
ret = regnode(EXACTLY);
while (len > 0) {
if (*regparse == '\\') {
regparse++;
len--;
if (len == 0)
break;
}
regc(*regparse++);
len--;
}
regc('\0');
return(ret);
}
/*
- regnode - emit a node
*/
static char * /* Location. */
regnode(op)
char op;
{
register char *ret;
register char *ptr;
ret = regcode;
if (ret == ®dummy) {
regsize += 3;
return(ret);
}
ptr = ret;
*ptr++ = op;
*ptr++ = '\0'; /* Null "next" pointer. */
*ptr++ = '\0';
regcode = ptr;
return(ret);
}
/*
- regc - emit (if appropriate) a byte of code
*/
static void
regc(b)
char b;
{
if (regcode != ®dummy)
*regcode++ = b;
else
regsize++;
}
/*
- reginsert - insert an operator in front of already-emitted operand
*
* Means relocating the operand.
*/
static void
reginsert(op, opnd)
char op;
char *opnd;
{
register char *src;
register char *dst;
register char *place;
if (regcode == ®dummy) {
regsize += 3;
return;
}
src = regcode;
regcode += 3;
dst = regcode;
while (src > opnd)
*--dst = *--src;
place = opnd; /* Op node, where operand used to be. */
*place++ = op;
*place++ = '\0';
*place++ = '\0';
}
/*
- regtail - set the next-pointer at the end of a node chain
*/
static void
regtail(p, val)
char *p;
char *val;
{
register char *scan;
register char *temp;
register int offset;
if (p == ®dummy)
return;
/* Find last node. */
scan = p;
for (;;) {
temp = regnext(scan);
if (temp == NULL)
break;
scan = temp;
}
if (OP(scan) == BACK)
offset = scan - val;
else
offset = val - scan;
*(scan+1) = (offset>>8)&0377;
*(scan+2) = offset&0377;
}
/*
- regoptail - regtail on operand of first argument; nop if operandless
*/
static void
regoptail(p, val)
char *p;
char *val;
{
/* "Operandless" and "op != BRANCH" are synonymous in practice. */
if (p == NULL || p == ®dummy || OP(p) != BRANCH)
return;
regtail(OPERAND(p), val);
}
/*
* regexec and friends
*/
/*
* Global work variables for regexec().
*/
static char *reginput; /* String-input pointer. */
static char *regbol; /* Beginning of input, for ^ check. */
static char **regstartp; /* Pointer to startp array. */
static char **regendp; /* Ditto for endp. */
/*
* Forwards.
*/
STATIC int regtry();
STATIC int regmatch();
STATIC int regrepeat();
#ifdef DEBUG
int regnarrate = 0;
void regdump();
STATIC char *regprop();
#endif
/*
- regexec - match a regexp against a string
*/
int
regexec(prog, string, bolflag)
register regexp *prog;
register char *string;
int bolflag;
{
register char *s;
extern char *strchr();
/* Be paranoid... */
if (!prog || !string) {
regerror("NULL parameter");
return(0);
}
/* Check validity of program. */
if (UCHARAT(prog->program) != MAGIC) {
regerror("corrupted program");
return(0);
}
/* If there is a "must appear" string, look for it. */
if (prog->regmust != NULL) {
s = string;
while ((s = ustrchr(s, prog->regmust[0])) != NULL) {
if (ustrncmp(s, prog->regmust, prog->regmlen) == 0)
break; /* Found it. */
s++;
}
if (s == NULL) /* Not present. */
return(0);
}
/* Mark beginning of line for ^ . */
if(bolflag)
regbol = string;
else
regbol = NULL;
/* Simplest case: anchored match need be tried only once. */
if (prog->reganch)
return(regtry(prog, string));
/* Messy cases: unanchored match. */
s = string;
if (prog->regstart != '\0')
/* We know what char it must start with. */
while ((s = ustrchr(s, prog->regstart)) != NULL) {
if (regtry(prog, s))
return(1);
s++;
}
else
/* We don't -- general case. */
do {
if (regtry(prog, s))
return(1);
} while (*s++ != '\0');
/* Failure. */
return(0);
}
/*
- regtry - try match at specific point
*/
static int /* 0 failure, 1 success */
regtry(prog, string)
regexp *prog;
char *string;
{
register int i;
register char **sp;
register char **ep;
reginput = string;
regstartp = prog->startp;
regendp = prog->endp;
sp = prog->startp;
ep = prog->endp;
for (i = NSUBEXP; i > 0; i--) {
*sp++ = NULL;
*ep++ = NULL;
}
if (regmatch(prog->program + 1)) {
prog->startp[0] = string;
prog->endp[0] = reginput;
return(1);
} else
return(0);
}
/*
- regmatch - main matching routine
*
* Conceptually the strategy is simple: check to see whether the current
* node matches, call self recursively to see whether the rest matches,
* and then act accordingly. In practice we make some effort to avoid
* recursion, in particular by going through "ordinary" nodes (that don't
* need to know whether the rest of the match failed) by a loop instead of
* by recursion.
*/
static int /* 0 failure, 1 success */
regmatch(prog)
char *prog;
{
register char *scan; /* Current node. */
char *next; /* Next node. */
extern char *strchr();
scan = prog;
#ifdef DEBUG
if (scan != NULL && regnarrate)
wprintw(stdscr, "%s(\n", regprop(scan));
#endif
while (scan != NULL) {
#ifdef DEBUG
if (regnarrate)
wprintw(stdscr, "%s...\n", regprop(scan));
#endif
next = regnext(scan);
switch (OP(scan)) {
case BOL:
if (reginput != regbol)
return(0);
break;
case EOL:
if (*reginput != '\0' && *reginput != '\n')
return(0);
break;
case BOW:
if (reginput != regbol
&& (isalnum(reginput[-1]) || reginput[-1] == '_'))
return(0);
break;
case EOW:
if (isalnum(*reginput) || *reginput == '_')
return(0);
break;
case ANY:
if (*reginput == '\0' || *reginput == '\n')
return(0);
reginput++;
break;
case EXACTLY: {
register int len;
register char *opnd;
opnd = OPERAND(scan);
/* Inline the first character, for speed. */
if (!*o_ignorecase && *opnd != *reginput)
return(0);
len = strlen(opnd);
if (len > 1 && ustrncmp(opnd, reginput, len) != 0)
return(0);
reginput += len;
}
break;
case ANYOF:
if (*reginput == '\0' || *reginput == '\n'
|| strchr(OPERAND(scan), *reginput) == NULL)
return(0);
reginput++;
break;
case ANYBUT:
if (*reginput == '\0' || *reginput == '\n'
|| strchr(OPERAND(scan), *reginput) != NULL)
return(0);
reginput++;
break;
case NOTHING:
break;
case BACK:
break;
case OPEN+1:
case OPEN+2:
case OPEN+3:
case OPEN+4:
case OPEN+5:
case OPEN+6:
case OPEN+7:
case OPEN+8:
case OPEN+9: {
register int no;
register char *save;
no = OP(scan) - OPEN;
save = reginput;
if (regmatch(next)) {
/*
* Don't set startp if some later
* invocation of the same parentheses
* already has.
*/
if (regstartp[no] == NULL)
regstartp[no] = save;
return(1);
} else
return(0);
}
case CLOSE+1:
case CLOSE+2:
case CLOSE+3:
case CLOSE+4:
case CLOSE+5:
case CLOSE+6:
case CLOSE+7:
case CLOSE+8:
case CLOSE+9: {
register int no;
register char *save;
no = OP(scan) - CLOSE;
save = reginput;
if (regmatch(next)) {
/*
* Don't set endp if some later
* invocation of the same parentheses
* already has.
*/
if (regendp[no] == NULL)
regendp[no] = save;
return(1);
} else
return(0);
}
case BRANCH: {
register char *save;
if (OP(next) != BRANCH) /* No choice. */
next = OPERAND(scan); /* Avoid recursion. */
else {
do {
save = reginput;
if (regmatch(OPERAND(scan)))
return(1);
reginput = save;
scan = regnext(scan);
} while (scan != NULL && OP(scan) == BRANCH);
return(0);
/* NOTREACHED */
}
}
break;
case STAR:
case PLUS: {
register char nextch;
register int no;
register char *save;
register int min;
/*
* Lookahead to avoid useless match attempts
* when we know what character comes next.
*/
nextch = '\0';
if (OP(next) == EXACTLY)
nextch = *OPERAND(next);
min = (OP(scan) == STAR) ? 0 : 1;
save = reginput;
no = regrepeat(OPERAND(scan));
while (no >= min) {
/* If it could work, try it. */
if (nextch == '\0' || *reginput == nextch)
if (regmatch(next))
return(1);
/* Couldn't or didn't -- back up. */
no--;
reginput = save + no;
}
return(0);
}
case END:
return(1); /* Success! */
default:
regerror("memory corruption");
return(0);
}
scan = next;
}
/*
* We get here only if there's trouble -- normally "case END" is
* the terminating point.
*/
/*NOTREACHED*/
regerror("corrupted pointers");
return(0);
}
/*
- regrepeat - repeatedly match something simple, report how many
*/
static int
regrepeat(p)
char *p;
{
register int count = 0;
register char *scan;
register char *opnd;
scan = reginput;
opnd = OPERAND(p);
switch (OP(p)) {
case ANY:
while (*scan && *scan != '\n') {
count++;
scan++;
}
break;
case EXACTLY:
while (*opnd == *scan) {
count++;
scan++;
}
break;
case ANYOF:
while (*scan && *scan != '\n' && strchr(opnd, *scan) != NULL) {
count++;
scan++;
}
break;
case ANYBUT:
while (*scan && *scan != '\n' && strchr(opnd, *scan) == NULL) {
count++;
scan++;
}
break;
default: /* Oh dear. Called inappropriately. */
regerror("internal foulup");
count = 0; /* Best compromise. */
break;
}
reginput = scan;
return(count);
}
/*
- regnext - dig the "next" pointer out of a node
*/
static char *
regnext(p)
register char *p;
{
register int offset;
if (p == ®dummy)
return(NULL);
offset = NEXT(p);
if (offset == 0)
return(NULL);
if (OP(p) == BACK)
return(p-offset);
else
return(p+offset);
}
#ifdef DEBUG
STATIC char *regprop();
/*
- regdump - dump a regexp onto stdout in vaguely comprehensible form
*/
void
regdump(r)
regexp *r;
{
register char *s;
register char op = EXACTLY; /* Arbitrary non-END op. */
register char *next;
extern char *strchr();
s = r->program + 1;
while (op != END) { /* While that wasn't END last time... */
op = OP(s);
wprintw(stdscr, "%2d%s", (int)(s-r->program), regprop(s)); /* Where, what. */
next = regnext(s);
if (next == NULL) /* Next ptr. */
wprintw(stdscr, "(0)");
else
wprintw(stdscr, "(%d)", (int)(s-r->program)+(int)(next-s));
s += 3;
if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
/* Literal string, where present. */
while (*s != '\0') {
addch(*s);
s++;
}
s++;
}
addch('\n');
}
/* Header fields of interest. */
if (r->regstart != '\0')
wprintw(stdscr, "start `%c' ", r->regstart);
if (r->reganch)
wprintw(stdscr, "anchored ");
if (r->regmust != NULL)
wprintw(stdscr, "must have \"%s\"", r->regmust);
wprintw(stdscr, "\n");
}
/*
- regprop - printable representation of opcode
*/
static char *
regprop(op)
char *op;
{
register char *p;
static char buf[50];
(void) strcpy(buf, ":");
switch (OP(op)) {
case BOL:
p = "BOL";
break;
case EOL:
p = "EOL";
break;
case ANY:
p = "ANY";
break;
case ANYOF:
p = "ANYOF";
break;
case ANYBUT:
p = "ANYBUT";
break;
case BRANCH:
p = "BRANCH";
break;
case EXACTLY:
p = "EXACTLY";
break;
case NOTHING:
p = "NOTHING";
break;
case BACK:
p = "BACK";
break;
case END:
p = "END";
break;
case BOW:
p = "BOW";
break;
case EOW:
p = "EOW";
break;
case OPEN+1:
case OPEN+2:
case OPEN+3:
case OPEN+4:
case OPEN+5:
case OPEN+6:
case OPEN+7:
case OPEN+8:
case OPEN+9:
sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
p = NULL;
break;
case CLOSE+1:
case CLOSE+2:
case CLOSE+3:
case CLOSE+4:
case CLOSE+5:
case CLOSE+6:
case CLOSE+7:
case CLOSE+8:
case CLOSE+9:
sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
p = NULL;
break;
case STAR:
p = "STAR";
break;
case PLUS:
p = "PLUS";
break;
default:
regerror("corrupted opcode");
break;
}
if (p != NULL)
(void) strcat(buf, p);
return(buf);
}
#endif
/* Here is a function which performs string comparisons. Uses o_ignorecase */
int ustrncmp(str1, str2, len)
register char *str1, *str2; /* the strings to compare */
register int len; /* max # of chars we care about */
{
if (*o_ignorecase)
{
while (--len >= 0)
{
if (tolower(*str1) != tolower(*str2))
{
return tolower(*str2) - tolower(*str1);
}
str1++;
str2++;
}
return 0;
}
else
{
while (--len >= 0 && *str1++ == *str2++)
{
}
if (len < 0)
{
return 0;
}
str1--;
str2--;
return *str2 - *str1;
}
}
/* Here is a function which looks for a character in a string. */
char *ustrchr(str, ch)
register char *str; /* the string to look in */
register char ch; /* the character to look for */
{
if (*o_ignorecase)
{
for (ch = tolower(ch); *str && *str != '\n'; str++)
{
if (tolower(*str) == ch)
{
return str;
}
}
}
else
{
for (; *str && *str != '\n'; str++)
{
if (*str == ch)
{
return str;
}
}
}
return (char *)0;
}
#endif
SHAR_EOF
fi
if test -f 'regexp.h'
then
echo shar: "will not over-write existing file 'regexp.h'"
else
cat << \SHAR_EOF > 'regexp.h'
/*
* Definitions etc. for regexp(3) routines.
*
* Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
* not the System V one.
*/
#define NSUBEXP 10
typedef struct regexp {
char *startp[NSUBEXP];
char *endp[NSUBEXP];
char regstart; /* Internal use only. */
char reganch; /* Internal use only. */
char *regmust; /* Internal use only. */
int regmlen; /* Internal use only. */
char program[1]; /* Unwarranted chumminess with compiler. */
} regexp;
extern regexp *regcomp();
extern int regexec();
extern void regsub();
extern void regerror();
SHAR_EOF
fi
if test -f 'regsub.c'
then
echo shar: "will not over-write existing file 'regsub.c'"
else
cat << \SHAR_EOF > 'regsub.c'
/*
* regsub
*
* Copyright (c) 1986 by University of Toronto.
* Written by Henry Spencer. Not derived from licensed software.
*
* Permission is granted to anyone to use this software for any
* purpose on any computer system, and to redistribute it freely,
* subject to the following restrictions:
*
* 1. The author is not responsible for the consequences of use of
* this software, no matter how awful, even if they arise
* from defects in it.
*
* 2. The origin of this software must not be misrepresented, either
* by explicit claim or by omission.
*
* 3. Altered versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*/
#include "regexp.h"
#define NULL (char *)0
/*
* The first byte of the regexp internal "program" is actually this magic
* number; the start node begins in the second byte.
*/
#define MAGIC 0234
#define CHARBITS 0377
#ifndef CHARBITS
#define UCHARAT(p) ((int)*(unsigned char *)(p))
#else
#define UCHARAT(p) ((int)*(p)&CHARBITS)
#endif
/*
- regsub - perform substitutions after a regexp match
*/
void regsub(prog, source, dest)
regexp *prog;
char *source;
char *dest;
{
register char *src;
register char *dst;
register char c;
register int no;
register int len;
extern char *strncpy();
if (!prog || !source || !dest) {
regerror("NULL parm to regsub");
return;
}
if (UCHARAT(prog->program) != MAGIC) {
regerror("damaged regexp fed to regsub");
return;
}
src = source;
dst = dest;
while ((c = *src++) != '\0') {
if (c == '&')
no = 0;
else if (c == '\\' && '0' <= *src && *src <= '9')
no = *src++ - '0';
else
no = -1;
if (no < 0) { /* Ordinary character. */
if (c == '\\' && *src != '\0')
c = *src++;
*dst++ = c;
} else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
len = (int)(prog->endp[no] - prog->startp[no]);
strncpy(dst, prog->startp[no], len);
dst += len;
if (len !=0 && *(dst-1) == '\0') { /* strncpy hit NUL. */
regerror("damaged match string");
return;
}
}
}
*dst++ = '\0';
}
SHAR_EOF
fi
if test -f 'sysdos.c'
then
echo shar: "will not over-write existing file 'sysdos.c'"
else
cat << \SHAR_EOF > 'sysdos.c'
/* sysdos.c -- DOS version of system.c */
/* Author:
* Guntram Blohm
* Buchenstrasse 19
* 7904 Erbach, West Germany
* Tel. ++49-7305-6997
* sorry - no regular network connection
*/
/* This file is derived from Steve Kirkendall's system.c.
*
* Entry points are:
* system(cmd) - run a single shell command
* wildcard(names) - expand wildcard characters in filanames
*
* This file is for use with DOS and TOS. For OS/2, slight modifications
* might be sufficient. For UNIX, use system.c. For Amiga, completely
* rewrite this stuff.
*
* Another system function, filter, is the same on DOS and UNIX and thus
* can be found in the original system.c.
*/
#include "config.h"
#include "vi.h"
extern char **environ;
#if MSDOS
#include <process.h>
extern unsigned char _osmajor;
#endif
#if TOS
#include <osbind.h>
#endif
#if MSDOS || TOS
#include <string.h>
/*
* Calling command is a bit nasty because of the undocumented yet sometimes
* used feature to change the option char to something else than /.
* Versions 2.x and 3.x support this, 4.x doesn't.
*
* For Atari, some shells define a shortcut entry which is faster than
* shell -c. Also, Mark Williams uses a special ARGV environment variable
* to pass more than 128 chars to a called command.
* We try to support all of these features here.
*/
int system(cmd)
const char *cmd;
{
#if MSDOS
char *cmdswitch="/c";
if (_osmajor<4)
cmdswitch[0]=switchar();
return spawnle(P_WAIT, o_shell, o_shell, cmdswitch, cmd, (char *)0, environ);
#else
long ssp;
int (*shell)();
char line[130];
char env[4096], *ep=env;
int i;
/* does our shell have a shortcut, that we can use? */
ssp = Super(0L);
shell = *((int (**)())0x4F6);
Super(ssp);
if (shell)
return (*shell)(cmd);
/* else we'll have to call a shell ... */
for (i=0; environ[i] && strncmp(environ[i], "ARGV=", 5); i++)
{ strcpy(ep, environ[i]);
ep+=strlen(ep)+1;
}
if (environ[i])
{
strcpy(ep, environ[i]); ep+=strlen(ep)+1;
strcpy(ep, o_shell); ep+=strlen(ep)+1;
strcpy(ep, "-c"); ep+=3;
strcpy(ep, cmd); ep+=strlen(ep)+1;
}
*ep='\0';
strcpy(line+1, "-c ");
strncat(line+1, cmd, 126);
line[0]=strlen(line+1);
return Pexec(0, o_shell, line, env);
#endif
}
/* This private function opens a pipe from a filter. It is similar to the
* system() function above, and to popen(cmd, "r").
* sorry - i cant use cmdstate until rpclose, but get it from spawnle.
*/
static int cmdstate;
static char output[80];
int rpipe(cmd, in)
char *cmd; /* the filter command to use */
int in; /* the fd to use for stdin */
{
int fd, old0, old1, old2;
/* create the file that will collect the filter's output */
strcpy(output, o_directory);
if ((fd=strlen(output)) && !strchr("/\\:", output[fd-1]))
output[fd++]=SLASH;
strcpy(output+fd, SCRATCHIN+3);
mktemp(output);
close(creat(output, 0666));
if ((fd=open(output, O_RDWR))==-1)
{
unlink(output);
return -1;
}
/* save and redirect stdin, stdout, and stderr */
old0=dup(0);
old1=dup(1);
if (in)
{
dup2(in, 0);
close(in);
}
dup2(fd, 1);
/* call command */
cmdstate=system(cmd);
/* restore old std... */
dup2(old0, 0); close(old0);
dup2(old1, 1); close(old1);
/* rewind command output */
lseek(fd, 0L, 0);
return fd;
}
/* This function closes the pipe opened by rpipe(), and returns 0 for success */
int rpclose(fd)
int fd;
{
int status;
close(fd);
unlink(output);
return cmdstate;
}
#endif
SHAR_EOF
fi
if test -f 'system.c'
then
echo shar: "will not over-write existing file 'system.c'"
else
cat << \SHAR_EOF > 'system.c'
/* system.c -- UNIX version */
/* Author:
* Steve Kirkendall
* 16820 SW Tallac Way
* Beaverton, OR 97006
* kirkenda at jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
*/
/* This file contains a new version of the system() function and related stuff.
*
* Entry points are:
* system(cmd) - run a single shell command
* wildcard(names) - expand wildcard characters in filanames
* filter(m,n,cmd) - run text lines through a filter program
*
* This is probably the single least portable file in the program. The code
* shown here should work correctly if it links at all; it will work on UNIX
* and any O.S./Compiler combination which adheres to UNIX forking conventions.
*/
#include "config.h"
#include "vi.h"
#include <signal.h>
extern char **environ;
#if ANY_UNIX
/* This is a new version of the system() function. The only difference
* between this one and the library one is: this one uses the o_shell option.
*/
int system(cmd)
char *cmd; /* a command to run */
{
int status; /* exit status of the command */
/* warn the user if the file hasn't been saved yet */
if (*o_warn && tstflag(file, MODIFIED))
{
msg("Warning: \"%s\" has been modified but not yet saved", origname);
}
switch (fork())
{
case -1: /* error */
status = -1;
break;
case 0: /* child */
execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
exit(1); /* if we get here, the exec failed */
default: /* parent */
signal(SIGINT, SIG_IGN);
wait(&status);
signal(SIGINT, trapint);
}
return status;
}
/* This private function opens a pipe from a filter. It is similar to the
* system() function above, and to popen(cmd, "r").
*/
static int rpipe(cmd, in)
char *cmd; /* the filter command to use */
int in; /* the fd to use for stdin */
{
int r0w1[2];/* the pipe fd's */
/* make the pipe */
if (pipe(r0w1) < 0)
{
return -1; /* pipe failed */
}
switch (fork())
{
case -1: /* error */
return -1;
case 0: /* child */
/* close the "read" end of the pipe */
close(r0w1[0]);
/* redirect stdout to go to the "write" end of the pipe */
close(1);
dup(r0w1[1]);
close(2);
dup(r0w1[1]);
close(r0w1[1]);
/* redirect stdin */
if (in != 0)
{
close(0);
dup(in);
close(in);
}
/* exec the shell to run the command */
execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
exit(1); /* if we get here, exec failed */
default: /* parent */
signal(SIGINT, SIG_IGN); /* <- reset after the wait() */
/* close the "write" end of the pipe */
close(r0w1[1]);
return r0w1[0];
}
}
/* This function closes the pipe opened by rpipe(), and returns 0 for success */
static int rpclose(fd)
int fd;
{
int status;
close(fd);
wait(&status);
signal(SIGINT, trapint);
return status;
}
#endif /* non-DOS */
/* This function expands wildcards in a filename or filenames. It does this
* by running the "echo" command on the filenames via the shell; it is assumed
* that the shell will expand the names for you. If for any reason it can't
* run echo, then it returns the names unmodified.
*/
#if MSDOS || TOS
#define PROG "wildcard "
#define PROGLEN 9
#include <string.h>
#else
#define PROG "echo "
#define PROGLEN 5
#endif
char *wildcard(names)
char *names;
{
int i, j, fd;
/* build the echo command */
if (names != tmpblk.c)
{
/* the names aren't in tmpblk.c, so we can do it the easy way */
strcpy(tmpblk.c, PROG);
strcat(tmpblk.c, names);
}
else
{
register char *s, *d;
/* the names are already in tmpblk.c, so shift them to make
* room for the word "echo "
*/
for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
{
*--d = *--s;
}
strncpy(names, PROG, PROGLEN);
}
/* run the command & read the resulting names */
fd = rpipe(tmpblk.c, 0);
if (fd < 0) return names;
i = 0;
do
{
j = tread(fd, tmpblk.c + i, BLKSIZE - i);
i += j;
} while (j > 0);
/* successful? */
if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
{
tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
return tmpblk.c;
}
else
{
return names;
}
}
/* This function runs a range of lines through a filter program, and replaces
* the original text with the filtered version. As a special case, if "to"
* is MARK_UNSET, then it runs the filter program with stdin coming from
* /dev/null, and inserts any output lines.
*/
int filter(from, to, cmd)
MARK from, to; /* the range of lines to filter */
char *cmd; /* the filter command */
{
int scratch; /* fd of the scratch file */
int fd; /* fd of the pipe from the filter */
char scrout[50]; /* name of the scratch out file */
int i;
/* write the lines (if specified) to a temp file */
if (to)
{
/* we have lines */
#if MSDOS || TOS
strcpy(scrout, o_directory);
if ((i=strlen(scrout)) && strchr("\\/:", scrout[i-1]))
scrout[i++]=SLASH;
strcpy(scrout+i, SCRATCHOUT+3);
#else
sprintf(scrout, SCRATCHOUT, o_directory);
#endif
mktemp(scrout);
cmd_write(from, to, CMD_BANG, 0, scrout);
/* use those lines as stdin */
scratch = open(scrout, O_RDONLY);
if (scratch < 0)
{
unlink(scrout);
return -1;
}
}
else
{
scratch = 0;
}
/* start the filter program */
fd = rpipe(cmd, scratch);
if (fd < 0)
{
if (to)
{
close(scratch);
unlink(scrout);
}
return -1;
}
ChangeText
{
/* delete the original lines, if any. Lines! */
if (to)
{
from &= ~(BLKSIZE - 1);
to &= ~(BLKSIZE - 1);
to += BLKSIZE;
delete(from, to);
}
/* repeatedly read in new text and add it */
while ((i = tread(fd, tmpblk.c, BLKSIZE)) > 0)
{
tmpblk.c[i] = '\0';
add(from, tmpblk.c);
for (i = 0; tmpblk.c[i]; i++)
{
if (tmpblk.c[i] == '\n')
{
from = (from & ~(BLKSIZE - 1)) + BLKSIZE;
}
else
{
from++;
}
}
}
}
/* cleanup */
rpclose(fd);
if (to)
{
close(scratch);
unlink(scrout);
}
return 0;
}
SHAR_EOF
fi
if test -f 'tinytcap.c'
then
echo shar: "will not over-write existing file 'tinytcap.c'"
else
cat << \SHAR_EOF > 'tinytcap.c'
/* tinytcap.c */
/* This file contains functions which simulate the termcap functions, but which
* can only describe the capabilities of the ANSI.SYS and NANSI.SYS drivers on
* an MS-DOS system or the VT-52 emulator of an Atari-ST. These functions
* do *NOT* access a "termcap" database file.
*/
#include "config.h"
#if MSDOS || TOS
#define CAP(str) CAP2((str)[0], (str)[1])
#define CAP2(a,b) (((a) << 8) + ((b) & 0xff))
#if MSDOS
# define VAL2(v,a) (a)
# define VAL3(v,a,n) (nansi ? (n) : (a))
static int nansi = 0;
#endif
#if TOS
# define VAL2(v,a) (v)
# define VAL3(v,a,n) (v)
#endif
/*ARGSUSED*/
int tgetent(bp, name)
char *bp; /* buffer for storing the entry -- ignored */
char *name; /* name of the entry */
{
#if MSDOS
if (!strcmp(name, "ansi") || !strcmp(name, "nansi"))
{
nansi = (name[0] == 'n');
return 1;
}
#endif
#if TOS
if (!strcmp(name, "vt52"))
{
return 1;
}
#endif
return 0;
}
int tgetnum(id)
char *id;
{
switch (CAP(id))
{
case CAP2('l','i'): return 25;
case CAP2('c','o'): return 80;
case CAP2('s','g'): return 0;
case CAP2('u','g'): return 0;
default: return -1;
}
}
int tgetflag(id)
char *id;
{
switch (CAP(id))
{
case CAP2('a','m'): return 1;
case CAP2('b','s'): return 1;
case CAP2('m','i'): return 1;
default: return 0;
}
}
/*ARGSUSED*/
char *tgetstr(id, bp)
char *id;
char **bp; /* pointer to pointer to buffer - ignored */
{
switch (CAP(id))
{
case CAP2('c','e'): return VAL2("\033K", "\033[K");
case CAP2('c','l'): return VAL2("\033E", "\033[2J");
case CAP2('a','l'): return VAL3("\033L", (char *)0, "\033[L");
case CAP2('d','l'): return VAL3("\033M", (char *)0, "\033[M");
case CAP2('c','m'): return VAL2("\033Y%i%+ %+ ", "\033[%i%d;%dH");
case CAP2('d','o'): return VAL2("\033B", "\033[B");
case CAP2('n','d'): return VAL2("\033C", "\033[C");
case CAP2('u','p'): return VAL2("\033A", "\033[A");
case CAP2('t','i'): return VAL2("\033e", "");
case CAP2('t','e'): return VAL2("", "");
case CAP2('s','o'): return VAL2("\033p", "\033[7m");
case CAP2('s','e'): return VAL2("\033q", "\033[m");
case CAP2('u','s'): return VAL2((char *)0, "\033[4m");
case CAP2('u','e'): return VAL2((char *)0, "\033[m");
case CAP2('V','B'): return VAL2((char *)0, "\033[1m");
case CAP2('V','b'): return VAL2((char *)0, "\033[m");
case CAP2('k','u'): return "#H";
case CAP2('k','d'): return "#P";
case CAP2('k','l'): return "#K";
case CAP2('k','r'): return "#M";
case CAP2('H','M'): return "#G";
case CAP2('E','N'): return "#O";
case CAP2('P','U'): return "#I";
case CAP2('P','D'): return "#Q";
default: return (char *)0;
}
}
/*ARGSUSED*/
char *tgoto(cm, destcol, destrow)
char *cm; /* cursor movement string -- ignored */
int destcol;/* destination column, 0 - 79 */
int destrow;/* destination row, 0 - 24 */
{
static char buf[30];
#if MSDOS
sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1);
#endif
#if TOS
sprintf(buf, "\033Y%c%c", ' ' + destrow, ' ' + destcol);
#endif
return buf;
}
/*ARGSUSED*/
void tputs(cp, affcnt, outfn)
char *cp; /* the string to output */
int affcnt; /* number of affected lines -- ignored */
int (*outfn)(); /* the output function */
{
while (*cp)
{
(*outfn)(*cp);
cp++;
}
}
#endif
SHAR_EOF
fi
if test -f 'tio.c'
then
echo shar: "will not over-write existing file 'tio.c'"
else
cat << \SHAR_EOF > 'tio.c'
/* tio.c */
/* Author:
* Steve Kirkendall
* 16820 SW Tallac Way
* Beaverton, OR 97006
* kirkenda at jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
*/
/* This file contains terminal I/O functions */
#include "config.h"
#include <signal.h>
#include "vi.h"
/* This function reads in a line from the terminal. */
int vgets(prompt, buf, bsize)
char prompt; /* the prompt character, or '\0' for none */
char *buf; /* buffer into which the string is read */
int bsize; /* size of the buffer */
{
int len; /* how much we've read so far */
int ch; /* a character from the user */
int quoted; /* is the next char quoted? */
int tab; /* column position of cursor */
char widths[132]; /* widths of characters */
/* show the prompt */
move(LINES - 1, 0);
tab = 0;
if (prompt)
{
addch(prompt);
tab = 1;
}
clrtoeol();
refresh();
/* read in the line */
quoted = len = 0;
for (;;)
{
ch = getkey(quoted ? 0 : WHEN_EX);
/* some special conversions */
if (ch == ctrl('D') && len == 0)
ch = ctrl('[');
/* inhibit detection of special chars (except ^J) after a ^V */
if (quoted && ch != '\n')
{
ch |= 256;
}
/* process the character */
switch(ch)
{
case ctrl('V'):
qaddch('^');
qaddch('\b');
quoted = TRUE;
break;
case ctrl('['):
return -1;
case '\n':
case '\r':
clrtoeol();
goto BreakBreak;
case '\b':
if (len > 0)
{
len--;
addstr("\b\b\b\b\b\b\b\b" + 8 - widths[len]);
if (mode == MODE_EX)
{
clrtoeol();
}
tab -= widths[len];
}
else
{
return -1;
}
break;
default:
/* strip off quotation bit */
if (ch & 256)
{
ch &= ~256;
quoted = FALSE;
qaddch(' ');
qaddch('\b');
}
/* add & echo the char */
if (len < bsize - 1)
{
if (ch == '\t')
{
widths[len] = *o_tabstop - (tab % *o_tabstop);
addstr(" " + 8 - widths[len]);
tab += widths[len];
}
else if (ch > 0 && ch < ' ') /* > 0 by GB */
{
addch('^');
addch(ch + '@');
widths[len] = 2;
tab += 2;
}
else if (ch == '\177')
{
addch('^');
addch('?');
widths[len] = 2;
tab += 2;
}
else
{
addch(ch);
widths[len] = 1;
tab++;
}
buf[len++] = ch;
}
else
{
beep();
}
}
}
BreakBreak:
refresh();
buf[len] = '\0';
return len;
}
/* ring the terminal's bell */
beep()
{
if (*o_vbell)
{
do_VB();
refresh();
}
else if (*o_errorbells)
{
ttywrite("\007", 1);
}
}
static manymsgs; /* This variable keeps msgs from overwriting each other */
/* Write a message in an appropriate way. This should really be a varargs
* function, but there is no such thing as vwprintw. Hack!!! Also uses a
* little sleaze in the way it saves messages for repetition later.
*
* msg((char *)0) - repeats the previous message
* msg("") - clears the message line
* msg("%s %d", ...) - does a printf onto the message line
*/
/*VARARGS1*/
msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
char *fmt;
long arg1, arg2, arg3, arg4, arg5, arg6, arg7;
{
static char pmsg[80]; /* previous message */
char *start; /* start of current message */
if (mode != MODE_VI)
{
sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
qaddstr(pmsg);
addch('\n');
exrefresh();
}
else
{
/* redrawing previous message? */
if (!fmt)
{
move(LINES - 1, 0);
standout();
qaddch(' ');
addstr(pmsg);
qaddch(' ');
standend();
clrtoeol();
return;
}
/* just blanking out message line? */
if (!*fmt)
{
if (!*pmsg) return;
*pmsg = '\0';
move(LINES - 1, 0);
clrtoeol();
return;
}
/* wait for keypress between consecutive msgs */
if (manymsgs)
{
qaddstr("[More...]");
wqrefresh(stdscr);
getkey(0);
}
/* real message */
move(LINES - 1, 0);
standout();
qaddch(' ');
sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
qaddstr(pmsg);
qaddch(' ');
standend();
clrtoeol();
refresh();
}
manymsgs = TRUE;
}
/* This function calls refresh() if the option exrefresh is set */
exrefresh()
{
char *scan;
/* If this ex command wrote ANYTHING set exwrote so vi's : command
* can tell that it must wait for a user keystroke before redrawing.
*/
for (scan=kbuf; scan<stdscr; scan++)
if (*scan == '\n')
exwrote = TRUE;
#if MICROSOFT /* avoid compiler bug */
scan = stdscr;
#define stdscr scan
#endif
/* now we do the refresh thing */
if (*o_exrefresh)
{
refresh();
}
else
{
wqrefresh(stdscr);
}
#if MICROSOFT
#undef stdscr
stdscr = scan;
#endif
manymsgs = FALSE;
}
/* This variable holds a single ungotten key, or 0 for no key */
static int ungotten;
ungetkey(key)
int key;
{
ungotten = key;
}
/* This array describes mapped key sequences */
static struct _keymap
{
char *name; /* name of the key, or NULL */
char rawin[LONGKEY]; /* the unmapped version of input */
char cooked[80]; /* the mapped version of input */
int len; /* length of the unmapped version */
int when; /* when is this key mapped? */
}
mapped[MAXMAPS];
#if !MSDOS && !TOS
static int dummy(){} /* for timeout */
#endif
/* This function reads in a keystroke for VI mode. It automatically handles
* key mapping.
*/
int getkey(when)
int when; /* which bits must be ON? */
{
static char keybuf[100]; /* array of already-read keys */
static int nkeys; /* total number of keys in keybuf */
static int next; /* index of next key to return */
static char *cooked; /* rawin, or pointer to converted key */
static int oldwhen; /* "when" from last time */
static int oldleft;
static long oldtop;
static long oldnlines;
static char *cshape; /* current cursor shape */
register char *kptr; /* &keybuf[next] */
register struct _keymap *km; /* used to count through keymap */
register int i, j, k;
/* if this key is needed for delay between multiple error messages,
* then reset the manymsgs flag and abort any mapped key sequence.
*/
if (manymsgs)
{
manymsgs = FALSE;
cooked = (char *)0;
ungotten = 0;
}
/* if we have an ungotten key, use it */
if (ungotten != 0)
{
k = ungotten;
ungotten = 0;
return k;
}
/* if we're doing a mapped key, get the next char */
if (cooked && *cooked)
{
return *cooked++;
}
/* if keybuf is empty, fill it */
if (next == nkeys)
{
#ifndef NO_CURSORSHAPE
/* make sure the cursor is the right shape */
if (has_CQ)
{
cooked = cshape;
switch (when)
{
case WHEN_EX: cooked = CX; break;
case WHEN_VICMD: cooked = CV; break;
case WHEN_VIINP: cooked = CI; break;
case WHEN_VIREP: cooked = CR; break;
}
if (cooked != cshape)
{
cshape = cooked;
switch (when)
{
case WHEN_EX: do_CX(); break;
case WHEN_VICMD: do_CV(); break;
case WHEN_VIINP: do_CI(); break;
case WHEN_VIREP: do_CR(); break;
}
}
cooked = (char *)0;
}
#endif
#ifndef NO_SHOWMODE
/* if "showmode" then say which mode we're in */
if (*o_showmode
&& mode == MODE_VI
&& (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines))
{
oldwhen = when;
oldtop = topline;
oldleft = leftcol;
oldnlines = nlines;
if (when & WHEN_VICMD)
{
redraw(cursor, FALSE);
move(LINES - 1, COLS - 10);
standout();
addstr("Command");
standend();
redraw(cursor, FALSE);
}
else if (when & WHEN_VIINP)
{
redraw(cursor, TRUE);
move(LINES - 1, COLS - 10);
standout();
addstr(" Input ");
standend();
redraw(cursor, TRUE);
}
else if (when & WHEN_VIREP)
{
redraw(cursor, TRUE);
move(LINES - 1, COLS - 10);
standout();
addstr("Replace");
standend();
redraw(cursor, TRUE);
}
}
else
#endif
/* redraw if getting a VI command */
if (when & WHEN_VICMD)
{
redraw(cursor, FALSE);
}
/* read the rawin keystrokes */
refresh();
while ((nkeys = ttyread(keybuf, sizeof keybuf)) <= 0)
{
/* terminal was probably resized */
*o_lines = LINES;
*o_columns = COLS;
if (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))
{
redraw(MARK_UNSET, FALSE);
redraw(cursor, (when & WHEN_VICMD) == 0);
refresh();
}
}
next = 0;
}
/* see how many mapped keys this might be */
kptr = &keybuf[next];
for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
{
if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
{
if (km->len > nkeys - next)
{
if (!strncmp(km->rawin, kptr, nkeys - next))
{
j++;
}
}
else
{
if (!strncmp(km->rawin, kptr, km->len))
{
j++;
k = i;
}
}
}
}
/* if more than one, try to read some more */
while (j > 1)
{
#if ANY_UNIX
signal(SIGALRM, dummy);
#endif
alarm((unsigned)*o_keytime);
i = nkeys;
if ((k = ttyread(keybuf + nkeys, sizeof keybuf - nkeys)) >= 0)
{
nkeys += k;
}
alarm(0);
/* if we couldn't read any more, pretend 0 mapped keys */
if (i == nkeys)
{
j = 0;
}
else /* else we got some more - try again */
{
for (i = j = 0, k = -1, km = mapped; i < MAXMAPS; i++, km++)
{
if ((km->when & when) && km->len > 0 && *km->rawin == *kptr)
{
if (km->len > nkeys - next)
{
if (!strncmp(km->rawin, kptr, nkeys - next))
{
j++;
}
}
else
{
if (!strncmp(km->rawin, kptr, km->len))
{
j++;
k = i;
}
}
}
}
}
}
/* if unambiguously mapped key, use it! */
if (j == 1 && k >= 0)
{
next += mapped[k].len;
cooked = mapped[k].cooked;
#ifndef NO_EXTENSIONS
if ((when & (WHEN_VIINP|WHEN_VIREP))
&& (mapped[k].when & WHEN_INMV))
{
return 0; /* special case, means "a movement char follows" */
}
else
#endif
{
return *cooked++;
}
}
else
/* assume key is unmapped, but still translate weird erase key to '\b' */
if (keybuf[next] == ERASEKEY && when != 0)
{
next++;
return '\b';
}
else
{
return keybuf[next++];
}
}
/* This function maps or unmaps a key */
mapkey(rawin, cooked, when, name)
char *rawin; /* the input key sequence, before mapping */
char *cooked;/* after mapping */
short when; /* bitmap of when mapping should happen */
char *name; /* name of the key, if any */
{
int i, j;
/* if the mapped version starts with the word "visual" then set WHEN_INMV */
if (!strncmp(cooked, "visual ", 7))
{
when |= WHEN_INMV;
cooked += 7;
}
/* if WHEN_INMV is set, then WHEN_VIINP and WHEN_VIREP must be set */
if (when & WHEN_INMV)
{
when |= (WHEN_VIINP | WHEN_VIREP);
}
/* see if the key sequence was mapped before */
j = strlen(rawin);
for (i = 0; i < MAXMAPS; i++)
{
if (mapped[i].len == j
&& !strncmp(mapped[i].rawin, rawin, j)
&& (mapped[i].when & when))
{
break;
}
}
/* if not already mapped, then try to find a new slot to use */
if (i == MAXMAPS)
{
for (i = 0; i < MAXMAPS && mapped[i].len > 0; i++)
{
}
}
/* no room for the new key? */
if (i == MAXMAPS)
{
msg("No room left in the key map table");
return;
}
/* map the key */
if (cooked && *cooked)
{
/* Map the key */
mapped[i].len = j;
strncpy(mapped[i].rawin, rawin, j);
strcpy(mapped[i].cooked, cooked);
mapped[i].when = when;
mapped[i].name = name;
}
else /* unmap the key */
{
mapped[i].len = 0;
}
}
/* Dump keys of a given type - WHEN_VICMD dumps the ":map" keys, and
* WHEN_VIINP|WHEN_VIREP dumps the ":map!" keys
*/
dumpkey(when)
{
int i, len, mlen;
char *scan;
char *mraw;
for (i = 0; i < MAXMAPS; i++)
{
/* skip unused entries, or entries that don't match "when" */
if (mapped[i].len <= 0 || !(mapped[i].when & when))
{
continue;
}
/* dump the key label, if any */
len = 8;
if (mapped[i].name)
{
qaddstr(mapped[i].name);
len -= strlen(mapped[i].name);
}
do
{
qaddch(' ');
} while (len-- > 0);
/* dump the raw version */
len = 0;
mlen = mapped[i].len;
mraw = mapped[i].rawin;
for (scan = mraw; scan < mraw + mlen; scan++)
{
if (UCHAR(*scan) < ' ' || *scan == '\177')
{
qaddch('^');
qaddch(*scan ^ '@');
len += 2;
}
else
{
qaddch(*scan);
len++;
}
}
do
{
qaddch(' ');
} while (++len < 8);
/* dump the mapped version */
if ((mapped[i].when & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
{
qaddstr("visual ");
}
for (scan = mapped[i].cooked; *scan; scan++)
{
if (UCHAR(*scan) < ' ' || *scan == '\177')
{
qaddch('^');
qaddch(*scan ^ '@');
}
else
{
qaddch(*scan);
}
}
addch('\n');
exrefresh();
}
}
/* This function saves the current configuration of mapped keys to a file */
savekeys(fd)
int fd; /* file descriptor to save them to */
{
int i;
char buf[80];
/* now write a map command for each key other than the arrows */
for (i = 0; i < MAXMAPS; i++)
{
/* ignore keys that came from termcap */
if (mapped[i].name)
{
continue;
}
/* If this isn't used, ignore it */
if (mapped[i].len <= 0)
{
continue;
}
/* write the map command */
if (mapped[i].when & WHEN_INMV)
{
sprintf(buf, "map%s %.*s visual %s\n",
(mapped[i].when & WHEN_VICMD) ? "" : "!",
mapped[i].len, mapped[i].rawin,
mapped[i].cooked);
twrite(fd, buf, strlen(buf));
}
else
{
if (mapped[i].when & WHEN_VICMD)
{
sprintf(buf, "map %.*s %s\n",
mapped[i].len, mapped[i].rawin,
mapped[i].cooked);
twrite(fd, buf, strlen(buf));
}
if (mapped[i].when & (WHEN_VIINP | WHEN_VIREP))
{
sprintf(buf, "map! %.*s %s\n",
mapped[i].len, mapped[i].rawin,
mapped[i].cooked);
twrite(fd, buf, strlen(buf));
}
}
}
}
SHAR_EOF
fi
if test -f 'tmp.c'
then
echo shar: "will not over-write existing file 'tmp.c'"
else
cat << \SHAR_EOF > 'tmp.c'
/* tmpfile.c */
/* Author:
* Steve Kirkendall
* 16820 SW Tallac Way
* Beaverton, OR 97006
* kirkenda at jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
*/
/* This file contains functions which create & readback a TMPFILE */
#include "config.h"
#include "vi.h"
#if TOS
#include <stat.h>
#else
#include <sys/stat.h>
#endif
/* The FAIL() macro prints an error message and then exits. */
#define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9)
/* This is the name of the temp file */
static char tmpname[80];
/* This function creates the temp file and copies the original file into it.
* Returns if successful, or stops execution if it fails.
*/
int tmpstart(filename)
char *filename; /* name of the original file */
{
int origfd; /* fd used for reading the original file */
struct stat statb; /* stat buffer, used to examine inode */
register BLK *this; /* pointer to the current block buffer */
register BLK *next; /* pointer to the next block buffer */
int inbuf; /* number of characters in a buffer */
int nread; /* number of bytes read */
#ifdef FASTLOAD
int kin; /* number of bytes in kbuf */
int kout; /* index into kbuf of where to take bytes */
#endif
register int j, k;
int i;
/* switching to a different file certainly counts as a change */
changes++;
redraw(MARK_UNSET, FALSE);
/* open the original file for reading */
*origname = '\0';
if (filename && *filename)
{
strcpy(origname, filename);
origfd = open(origname, O_RDONLY);
if (origfd < 0 && errno != ENOENT)
{
FAIL("Can't open \"%s\"", origname);
}
if (origfd >= 0)
{
#if TURBOC || TOS
if (stat(origname, &statb) < 0)
#else
if (fstat(origfd, &statb) < 0)
#endif
{
FAIL("Can't stat \"%s\"", origname);
}
#if TOS
if (origfd >= 0 && (statb.st_mode & S_IJDIR))
#else
if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG)
#endif
{
FAIL("\"%s\" is not a regular file", origname);
}
}
else
{
stat(".", &statb);
}
if (origfd >= 0)
{
origtime = statb.st_mtime;
#if MSDOS
if (*o_readonly || !(statb.st_mode & S_IWRITE))
#endif
#if TOS
if (*o_readonly || (statb.st_mode & S_IJRON))
#endif
#if OS9
if we don't have write permission...
#endif
#if ANY_UNIX
if (*o_readonly || !(statb.st_mode &
(statb.st_uid != geteuid() ? 0022 : 0200)))
#endif
{
setflag(file, READONLY);
}
}
else
{
origtime = 0L;
}
}
else
{
setflag(file, NOFILE);
origfd = -1;
origtime = 0L;
stat(".", &statb);
}
/* make a name for the tmp file */
#if MSDOS || TOS
/* MS-Dos doesn't allow multiple slashes, but supports drives
* with current directories.
* This relies on TMPNAME beginning with "%s\\"!!!!
*/
strcpy(tmpname, o_directory);
if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1]))
tmpname[i++]=SLASH;
sprintf(tmpname+i, TMPNAME+3, statb.st_ino, statb.st_dev);
#else
sprintf(tmpname, TMPNAME, o_directory, statb.st_ino, statb.st_dev);
#endif
/* make sure nobody else is editing the same file */
if (access(tmpname, 0) == 0)
{
FAIL("\"%s\" is busy", filename);
}
/* create the temp file */
close(creat(tmpname, 0600));
tmpfd = open(tmpname, O_RDWR | O_BINARY);
if (tmpfd < 0)
{
FAIL("Can't create temporary file, errno=%d", errno);
return 1;
}
/* allocate space for the header in the file */
write(tmpfd, hdr.c, BLKSIZE);
#ifndef NO_RECYCLE
/* initialize the block allocator */
/* This must already be done here, before the first attempt
* to write to the new file! GB */
garbage();
#endif
/* initialize lnum[] */
for (i = 1; i < MAXBLKS; i++)
{
lnum[i] = INFINITY;
}
lnum[0] = 0;
/* if there is no original file, then create a 1-line file */
if (origfd < 0)
{
hdr.n[0] = 0; /* invalid inode# denotes new file */
this = blkget(1); /* get the new text block */
strcpy(this->c, "\n"); /* put a line in it */
lnum[1] = 1; /* block 1 ends with line 1 */
nlines = 1; /* there is 1 line in the file */
if (*origname)
{
msg("\"%s\" [NEW FILE] 1 line", origname);
}
else
{
msg("\"[NO FILE]\" 1 line");
}
}
else /* there is an original file -- read it in */
{
hdr.n[0] = statb.st_ino;
nlines = 0;
/* preallocate 1 "next" buffer */
i = 1;
next = blkget(i);
inbuf = 0;
/* loop, moving blocks from orig to tmp */
#ifdef FASTLOAD
kin = kout = 0;
#endif
for (;;)
{
/* "next" buffer becomes "this" buffer */
this = next;
/* read [more] text into this block */
do
{
#ifdef FASTLOAD
if (kout >= kin)
{
kout = 0;
kin = tread(origfd, kbuf, KBSIZ);
}
nread = kin - kout;
if (nread > BLKSIZE - 1 - inbuf)
nread = BLKSIZE - 1 - inbuf;
if (nread > 0)
{
memcpy(&this->c[inbuf], &kbuf[kout], nread);
kout += nread;
}
#else
nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf);
#endif
if (nread < 0)
{
close(origfd);
close(tmpfd);
tmpfd = -1;
unlink(tmpname);
FAIL("Error reading \"%s\"", origname);
}
/* convert NUL characters to something else */
for (k = inbuf; k < inbuf + nread; k++)
{
if (!this->c[k])
{
setflag(file, HADNUL);
this->c[k] = 0x80;
}
}
inbuf += nread;
/* if the buffer is empty, quit */
if (inbuf == 0)
{
goto FoundEOF;
}
} while (0 /* nread > 0 && inbuf < BLKSIZE - 2 */ );
#if MSDOS || TOS
/* BAH! MS text mode read fills inbuf, then compresses eliminating \r
but leaving garbage at end of buf. The same is true for TURBOC. GB. */
memset(this->c + inbuf, '\0', BLKSIZE - inbuf);
#endif
/* search backward for last newline */
for (k = inbuf; --k >= 0 && this->c[k] != '\n';)
{
}
if (k++ < 0)
{
if (inbuf >= BLKSIZE - 1)
{
k = 80;
}
else
{
k = inbuf;
}
}
/* allocate next buffer */
next = blkget(++i);
/* move fragmentary last line to next buffer */
inbuf -= k;
for (j = 0; k < BLKSIZE; j++, k++)
{
next->c[j] = this->c[k];
this->c[k] = 0;
}
/* if necessary, add a newline to this buf */
for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; )
{
}
if (this->c[k] != '\n')
{
setflag(file, ADDEDNL);
this->c[k + 1] = '\n';
}
/* count the lines in this block */
for (k = 0; k < BLKSIZE && this->c[k]; k++)
{
if (this->c[k] == '\n')
{
nlines++;
}
}
lnum[i - 1] = nlines;
}
FoundEOF:
/* if this is a zero-length file, add 1 line */
if (nlines == 0)
{
this = blkget(1); /* get the new text block */
strcpy(this->c, "\n"); /* put a line in it */
lnum[1] = 1; /* block 1 ends with line 1 */
nlines = 1; /* there is 1 line in the file */
}
/* report the number of lines in the file */
msg("\"%s\" %s %ld line%s",
origname,
(tstflag(file, READONLY) ? "[READONLY]" : ""),
nlines,
nlines == 1 ? "" : "s");
}
/* initialize the cursor to start of line 1 */
cursor = MARK_FIRST;
/* close the original file */
close(origfd);
return 0;
}
/* This function copies the temp file back onto an original file.
* Returns TRUE if successful, or FALSE if the file could NOT be saved.
*/
tmpsave(filename, bang)
char *filename; /* the name to save it to */
int bang; /* forced write? */
{
int fd; /* fd of the file we're writing to */
register int len; /* length of a text block */
register BLK *this; /* a text block */
long bytes; /* byte counter */
register int i;
/* if no filename is given, assume the original file name */
if (!filename || !*filename)
{
filename = origname;
}
/* if still no file name, then fail */
if (!*filename)
{
msg("Don't know a name for this file -- NOT WRITTEN");
return FALSE;
}
/* open the file */
if (*filename == '>' && filename[1] == '>')
{
filename += 2;
while (*filename == ' ' || *filename == '\t')
{
filename++;
}
#ifdef O_APPEND
fd = open(filename, O_WRONLY|O_APPEND);
#else
fd = open(filename, O_WRONLY);
lseek(fd, 0L, 2);
#endif
}
else
{
/* either the file must not exist, or it must be the original
* file, or we must have a bang
*/
if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang)
{
msg("File already exists - Use :w! to overwrite");
return FALSE;
}
fd = creat(filename, 0666);
}
if (fd < 0)
{
msg("Can't write to \"%s\" -- NOT WRITTEN", filename);
return FALSE;
}
/* write each text block to the file */
bytes = 0L;
for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++)
{
for (len = 0; len < BLKSIZE && this->c[len]; len++)
{
}
twrite(fd, this->c, len);
bytes += len;
}
/* reset the "modified" flag */
clrflag(file, MODIFIED);
/* report lines & characters */
#if MSDOS || TOS
bytes += nlines; /* for the inserted carriage returns */
#endif
if (strncmp(filename, o_directory, strlen(o_directory)))
{
msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes);
}
/* close the file */
close(fd);
return TRUE;
}
/* This function deletes the temporary file. If the file has been modified
* and "bang" is FALSE, then it returns FALSE without doing anything; else
* it returns TRUE.
*
* If the "autowrite" option is set, then instead of returning FALSE when
* the file has been modified and "bang" is false, it will call tmpend().
*/
tmpabort(bang)
int bang;
{
/* if there is no file, return successfully */
if (tmpfd < 0)
{
return TRUE;
}
/* see if we must return FALSE -- can't quit */
if (!bang && tstflag(file, MODIFIED))
{
/* if "autowrite" is set, then act like tmpend() */
if (*o_autowrite)
return tmpend(bang);
else
return FALSE;
}
/* delete the tmp file */
cutswitch(tmpname);
close(tmpfd);
tmpfd = -1;
unlink(tmpname);
strcpy(prevorig, origname);
prevline = markline(cursor);
*origname = '\0';
origtime = 0L;
blkinit();
nlines = 0;
initflags();
return TRUE;
}
/* This function saves the file if it has been modified, and then deletes
* the temporary file. Returns TRUE if successful, or FALSE if the file
* needs to be saved but can't be. When it returns FALSE, it will not have
* deleted the tmp file, either.
*/
tmpend(bang)
int bang;
{
/* save the file if it has been modified */
if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang)
{
return FALSE;
}
/* delete the tmp file */
tmpabort(TRUE);
return TRUE;
}
/* If the tmp file has been changed, then this function will force those
* changes to be written to the disk, so that the tmp file will survive a
* system crash or power failure.
*/
#if MSDOS || TOS || OS9
sync()
{
# if OS9
/* OS-9 doesn't need an explicit sync operation, but the linker
* demands something called sync(), so this is a dummy function.
*/
#else
/* MS-DOS and TOS don't flush their buffers until the file is closed,
* so here we close the tmp file and then immediately reopen it.
*/
close(tmpfd);
tmpfd = open(tmpname, O_RDWR | O_BINARY);
#endif
}
#endif
SHAR_EOF
fi
exit 0
# End of shell archive
-------------------------------------------------------------------------------
Steve Kirkendall kirkenda at cs.pdx.edu uunet!tektronix!psueea!eecs!kirkenda
More information about the Alt.sources
mailing list