home *** CD-ROM | disk | FTP | other *** search
- From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall)
- Newsgroups: alt.sources
- Subject: Elvis 1.4, part 7 of 8
- Message-ID: <831@pdxgate.UUCP>
- Date: 3 Dec 90 21:35:35 GMT
-
- # --------------------------- 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@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@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@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@cs.pdx.edu Grad student at Portland State U.
-