Article View: comp.windows.news
Article #710NeWS version of elvis (the vi clone) part 4 of 9
From: Mike H.
Date: Fri, 11 Jan 1991 02:01
Date: Fri, 11 Jan 1991 02:01
1546 lines
34840 bytes
34840 bytes
#! /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 the files:
# date.c
# ex.c
# input.c
# This archive created: Thu Jan 10 17:21:08 1991
export PATH; PATH=/bin:$PATH
if test -f 'date.c'
then
echo shar: will not over-write existing file "'date.c'"
else
cat << \SHAR_EOF > 'date.c'
SHAR_EOF
fi # end of overwriting check
if test -f 'ex.c'
then
echo shar: will not over-write existing file "'ex.c'"
else
cat << \SHAR_EOF > 'ex.c'
/* ex.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the code for reading ex commands. */
#include "config.h"
#include <ctype.h>
#include "vi.h"
#ifndef isascii
# define isascii(c) !((c)&~0x7f)
#endif
/* This data type is used to describe the possible argument combinations */
typedef short ARGT;
#define FROM 1 /* allow a linespec */
#define TO 2 /* allow a second linespec */
#define BANG 4 /* allow a ! after the command name */
#define EXTRA 8 /* allow extra args after command name */
#define XFILE 16 /* expand wildcards in extra part */
#define NOSPC 32 /* no spaces allowed in the extra part */
#define DFLALL 64 /* default file range is 1,$ */
#define DFLNONE 128 /* no default file range */
#define NODFL 256 /* do not default to the current file name */
#define EXRCOK 512 /* can be in a .exrc file */
#define NL 1024 /* if mode!=MODE_EX, then write a newline first */
#define PLUS 2048 /* allow a line number, as in ":e +32 foo" */
#define ZERO 4096 /* allow 0 to be given as a line number */
#define FILES (XFILE + EXTRA) /* multiple extra files allowed */
#define WORD1 (EXTRA + NOSPC) /* one extra word allowed */
#define FILE1 (FILES + NOSPC) /* 1 file allowed, defaults to current file */
#define NAMEDF (FILE1 + NODFL) /* 1 file allowed, defaults to "" */
#define NAMEDFS (FILES + NODFL) /* multiple files allowed, default is "" */
#define RANGE (FROM + TO) /* range of linespecs allowed */
#define NONE 0 /* no args allowed at all */
/* This array maps ex command names to command codes. The order in which
* command names are listed below is significant -- ambiguous abbreviations
* are always resolved to be the first possible match. (e.g. "r" is taken
* to mean "read", not "rewind", because "read" comes before "rewind")
*/
static struct
{
char *name; /* name of the command */
CMD code; /* enum code of the command */
void (*fn)();/* function which executes the command */
ARGT argt; /* command line arguments permitted/needed/used */
}
cmdnames[] =
{ /* cmd name cmd code function arguments */
{"append", CMD_APPEND, cmd_append, FROM+ZERO },
#ifdef DEBUG
{"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL},
#endif
{"change", CMD_CHANGE, cmd_append, RANGE },
{"delete", CMD_DELETE, cmd_delete, RANGE+WORD1 },
{"edit", CMD_EDIT, cmd_edit, BANG+FILE1+PLUS },
{"file", CMD_FILE, cmd_file, NAMEDF },
{"global", CMD_GLOBAL, cmd_global, RANGE+BANG+EXTRA+DFLALL},
{"insert", CMD_INSERT, cmd_append, FROM },
{"join", CMD_INSERT, cmd_join, RANGE },
{"k", CMD_MARK, cmd_mark, FROM+WORD1 },
{"list", CMD_LIST, cmd_print, RANGE+NL },
{"move", CMD_MOVE, cmd_move, RANGE+EXTRA },
{"next", CMD_NEXT, cmd_next, BANG+NAMEDFS },
{"Next", CMD_PREVIOUS, cmd_next, BANG },
{"print", CMD_PRINT, cmd_print, RANGE+NL },
{"quit", CMD_QUIT, cmd_xit, BANG },
{"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF},
{"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA },
{"to", CMD_COPY, cmd_move, RANGE+EXTRA },
{"undo", CMD_UNDO, cmd_undo, NONE },
{"vglobal", CMD_VGLOBAL, cmd_global, RANGE+EXTRA+DFLALL},
{"write", CMD_WRITE, cmd_write, RANGE+BANG+FILE1+DFLALL},
{"xit", CMD_XIT, cmd_xit, BANG+NL },
{"yank", CMD_YANK, cmd_delete, RANGE+WORD1 },
{"!", CMD_BANG, cmd_shell, EXRCOK+RANGE+NAMEDFS+DFLNONE+NL},
{"<", CMD_SHIFTL, cmd_shift, RANGE },
{">", CMD_SHIFTR, cmd_shift, RANGE },
{"=", CMD_EQUAL, cmd_file, RANGE },
{"&", CMD_SUBAGAIN, cmd_substitute, RANGE },
#ifndef NO_AT
{"@", CMD_AT, cmd_at, EXTRA },
#endif
#ifndef NO_ABBR
{"abbreviate", CMD_ABBR, cmd_abbr, EXRCOK+EXTRA },
#endif
{"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS },
#ifndef NO_ERRLIST
{"cc", CMD_CC, cmd_make, BANG+FILES },
#endif
{"cd", CMD_CD, cmd_cd, EXRCOK+NAMEDF },
{"copy", CMD_COPY, cmd_move, RANGE+EXTRA },
#ifndef NO_DIGRAPH
{"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA},
#endif
#ifndef NO_ERRLIST
{"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF },
#endif
{"ex", CMD_EDIT, cmd_edit, BANG+FILE1 },
{"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA},
#ifndef NO_MKEXRC
{"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF },
#endif
{"number", CMD_NUMBER, cmd_print, RANGE+NL },
{"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 },
{"set", CMD_SET, cmd_set, EXRCOK+EXTRA },
{"shell", CMD_SHELL, cmd_shell, NL },
{"source", CMD_SOURCE, cmd_source, EXRCOK+NAMEDF },
{"tag", CMD_TAG, cmd_tag, BANG+WORD1 },
{"version", CMD_VERSION, cmd_version, EXRCOK+NONE },
{"visual", CMD_VISUAL, cmd_visual, NONE },
{"wq", CMD_WQUIT, cmd_xit, NL },
#ifdef DEBUG
{"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL},
{"validate", CMD_VALIDATE, cmd_validate, BANG+NL },
#endif
{"chdir", CMD_CD, cmd_cd, EXRCOK+NAMEDF },
#ifndef NO_ERRLIST
{"make", CMD_MAKE, cmd_make, BANG+NAMEDFS },
#endif
{"mark", CMD_MARK, cmd_mark, FROM+WORD1 },
{"previous", CMD_PREVIOUS, cmd_next, BANG },
{"rewind", CMD_REWIND, cmd_next, BANG },
{"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA},
#ifndef NO_ABBR
{"unabbreviate",CMD_UNABBR, cmd_abbr, EXRCOK+WORD1 },
#endif
{(char *)0}
};
/* This function parses a search pattern - given a pointer to a / or ?,
* it replaces the ending / or ? with a \0, and returns a pointer to the
* stuff that came after the pattern.
*/
char *parseptrn(ptrn)
REG char *ptrn;
{
REG char *scan;
for (scan = ptrn + 1;
*scan && *scan != *ptrn;
scan++)
{
/* allow backslashed versions of / and ? in the pattern */
if (*scan == '\\' && scan[1] != '\0')
{
scan++;
}
}
if (*scan)
{
*scan++ = '\0';
}
return scan;
}
/* This function parses a line specifier for ex commands */
char *linespec(s, markptr)
REG char *s; /* start of the line specifier */
MARK *markptr; /* where to store the mark's value */
{
long num;
REG char *t;
/* parse each ;-delimited clause of this linespec */
do
{
/* skip an initial ';', if any */
if (*s == ';')
{
s++;
}
/* skip leading spaces */
while (isascii(*s) && isspace(*s))
{
s++;
}
/* dot means current position */
if (*s == '.')
{
s++;
*markptr = cursor;
}
/* '$' means the last line */
else if (*s == '$')
{
s++;
*markptr = MARK_LAST;
}
/* digit means an absolute line number */
else if (isascii(*s) && isdigit(*s))
{
for (num = 0; isascii(*s) && isdigit(*s); s++)
{
num = num * 10 + *s - '0';
}
*markptr = MARK_AT_LINE(num);
}
/* appostrophe means go to a set mark */
else if (*s == '\'')
{
s++;
*markptr = m_tomark(cursor, 1L, (int)*s);
s++;
}
/* slash means do a search */
else if (*s == '/' || *s == '?')
{
/* put a '\0' at the end of the search pattern */
t = parseptrn(s);
/* search for the pattern */
*markptr &= ~(BLKSIZE - 1);
if (*s == '/')
{
pfetch(markline(*markptr));
if (plen > 0)
*markptr += plen - 1;
*markptr = m_fsrch(*markptr, s);
}
else
{
*markptr = m_bsrch(*markptr, s);
}
/* adjust command string pointer */
s = t;
}
/* if linespec was faulty, quit now */
if (!*markptr)
{
return s;
}
/* maybe add an offset */
t = s;
if (*t == '-' || *t == '+')
{
s++;
for (num = 0; *s >= '0' && *s <= '9'; s++)
{
num = num * 10 + *s - '0';
}
if (num == 0)
{
num = 1;
}
*markptr = m_updnto(*markptr, num, *t);
}
} while (*s == ';' || *s == '+' || *s == '-');
return s;
}
/* This function reads an ex command and executes it. */
void ex()
{
char cmdbuf[80];
REG int cmdlen;
static long oldline;
significant = FALSE;
oldline = markline(cursor);
while (mode == MODE_EX)
{
/* read a line */
cmdlen = vgets(':', cmdbuf, sizeof cmdbuf);
if (cmdlen < 0)
{
return;
}
/* if empty line, assume ".+1" */
if (cmdlen == 0)
{
strcpy(cmdbuf, ".+1");
qaddch('\r');
clrtoeol();
}
else
{
addch('\n');
}
refresh();
/* parse & execute the command */
doexcmd(cmdbuf);
/* handle autoprint */
if (significant || markline(cursor) != oldline)
{
significant = FALSE;
oldline = markline(cursor);
if (*o_autoprint && mode == MODE_EX)
{
cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
}
}
}
}
void doexcmd(cmdbuf)
char *cmdbuf; /* string containing an ex command */
{
REG char *scan; /* used to scan thru cmdbuf */
MARK frommark; /* first linespec */
MARK tomark; /* second linespec */
REG int cmdlen; /* length of the command name given */
CMD cmd; /* what command is this? */
ARGT argt; /* argument types for this command */
short forceit; /* bang version of a command? */
REG int cmdidx; /* index of command */
REG char *build; /* used while copying filenames */
int iswild; /* boolean: filenames use wildcards? */
int isdfl; /* using default line ranges? */
int didsub; /* did we substitute file names for % or # */
/* ex commands can't be undone via the shift-U command */
U_line = 0L;
/* ignore command lines that start with a double-quote */
if (*cmdbuf == '"')
{
return;
}
/* permit extra colons at the start of the line */
while (*cmdbuf == ':')
{
cmdbuf++;
}
/* parse the line specifier */
scan = cmdbuf;
if (nlines < 1)
{
/* no file, so don't allow addresses */
}
else if (*scan == '%')
{
/* '%' means all lines */
frommark = MARK_FIRST;
tomark = MARK_LAST;
scan++;
}
else if (*scan == '0')
{
frommark = tomark = MARK_UNSET;
scan++;
}
else
{
frommark = cursor;
scan = linespec(scan, &frommark);
tomark = frommark;
if (frommark && *scan == ',')
{
scan++;
scan = linespec(scan, &tomark);
}
if (!tomark)
{
/* faulty line spec -- fault already described */
return;
}
if (frommark > tomark)
{
msg("first address exceeds the second");
return;
}
}
isdfl = (scan == cmdbuf);
/* skip whitespace */
while (isascii(*scan) && isspace(*scan))
{
scan++;
}
/* if no command, then just move the cursor to the mark */
if (!*scan)
{
cursor = tomark;
return;
}
/* figure out how long the command name is */
if (isascii(*scan) && !isalpha(*scan))
{
cmdlen = 1;
}
else
{
for (cmdlen = 1;
!isascii(scan[cmdlen]) || isalpha(scan[cmdlen]);
cmdlen++)
{
}
}
/* lookup the command code */
for (cmdidx = 0;
cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
cmdidx++)
{
}
argt = cmdnames[cmdidx].argt;
cmd = cmdnames[cmdidx].code;
if (cmd == CMD_NULL)
{
#if OSK
msg("Unknown command \"%s\"", scan);
#else
msg("Unknown command \"%.*s\"", cmdlen, scan);
#endif
return;
}
/* if the command ended with a bang, set the forceit flag */
scan += cmdlen;
if ((argt & BANG) && *scan == '!')
{
scan++;
forceit = 1;
}
else
{
forceit = 0;
}
/* skip any more whitespace, to leave scan pointing to arguments */
while (isascii(*scan) && isspace(*scan))
{
scan++;
}
/* a couple of special cases for filenames */
if (argt & XFILE)
{
/* if names were given, process them */
if (*scan)
{
for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
{
switch (*scan)
{
case '%':
if (!*origname)
{
msg("No filename to substitute for %%");
return;
}
strcpy(build, origname);
while (*build)
{
build++;
}
didsub = TRUE;
break;
case '#':
if (!*prevorig)
{
msg("No filename to substitute for #");
return;
}
strcpy(build, prevorig);
while (*build)
{
build++;
}
didsub = TRUE;
break;
case '*':
case '?':
#if !(MSDOS || TOS)
case '[':
case '`':
case '{': /* } */
case '$':
case '~':
#endif
*build++ = *scan;
iswild = TRUE;
break;
default:
*build++ = *scan;
}
}
*build = '\0';
if (cmd == CMD_BANG
|| cmd == CMD_READ && tmpblk.c[0] == '!'
|| cmd == CMD_WRITE && tmpblk.c[0] == '!')
{
if (didsub)
{
if (mode != MODE_EX)
{
addch('\n');
}
addstr(tmpblk.c);
addch('\n');
exrefresh();
}
}
else
{
if (iswild && tmpblk.c[0] != '>')
{
scan = wildcard(tmpblk.c);
}
}
}
else /* no names given, maybe assume origname */
{
if (!(argt & NODFL))
{
strcpy(tmpblk.c, origname);
}
else
{
*tmpblk.c = '\0';
}
}
scan = tmpblk.c;
}
/* bad arguments? */
if (!(argt & EXRCOK) && nlines < 1L)
{
msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
return;
}
if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
{
msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
return;
}
if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
{
msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
return;
}
if (!(argt & TO) && tomark != frommark && nlines >= 1L)
{
msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
return;
}
if (!(argt & EXTRA) && *scan)
{
msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
return;
}
if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
{
build = scan;
#ifndef CRUNCH
if ((argt & PLUS) && *build == '+')
{
while (*build && !(isascii(*build) && isspace(*build)))
{
build++;
}
while (*build && isascii(*build) && isspace(*build))
{
build++;
}
}
#endif /* not CRUNCH */
for (; *build; build++)
{
if (isspace(*build))
{
msg("Too many %s to \"%s\" command.",
(argt & XFILE) ? "filenames" : "arguments",
cmdnames[cmdidx].name);
return;
}
}
}
/* some commands have special default ranges */
if (isdfl && (argt & DFLALL))
{
frommark = MARK_FIRST;
tomark = MARK_LAST;
}
else if (isdfl && (argt & DFLNONE))
{
frommark = tomark = 0L;
}
/* write a newline if called from visual mode */
if ((argt & NL) && mode != MODE_EX && !exwrote)
{
addch('\n');
exrefresh();
}
/* act on the command */
(*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
}
/* This function executes EX commands from a file. It returns 1 normally, or
* 0 if the file could not be opened for reading.
*/
int doexrc(filename)
char *filename; /* name of a ".exrc" file */
{
int fd; /* file descriptor */
int len; /* length of the ".exrc" file */
char buf[MAXRCLEN]; /* buffer, holds the entire .exrc file */
/* open the file, read it, and close */
fd = open(filename, O_RDONLY);
if (fd < 0)
{
return 0;
}
len = tread(fd, buf, MAXRCLEN);
close(fd);
/* execute the string */
exstring(buf, len);
return 1;
}
void exstring(buf, len)
char *buf; /* the commands to execute */
int len; /* the length of the string */
{
char *cmd; /* start of a command */
char *end; /* used to search for the end of cmd */
/* find & do each command */
for (cmd = buf; cmd < &buf[len]; cmd = end + 1)
{
/* find the end of the command */
for (end = cmd; end < &buf[len] && *end != '\n' && *end != '|'; end++)
{
}
*end = '\0';
/* do it */
doexcmd(cmd);
}
}
SHAR_EOF
fi # end of overwriting check
if test -f 'input.c'
then
echo shar: will not over-write existing file "'input.c'"
else
cat << \SHAR_EOF > 'input.c'
/* input.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the input() function, which implements vi's INPUT mode.
* It also contains the code that supports digraphs.
*/
#include <ctype.h>
#include "config.h"
#include "vi.h"
#ifndef NO_DIGRAPH
static struct _DIG
{
struct _DIG *next;
char key1;
char key2;
char dig;
char save;
} *digs;
char digraph(key1, key2)
char key1; /* the underlying character */
char key2; /* the second character */
{
int newkey;
REG struct _DIG *dp;
/* if digraphs are disabled, then just return the new char */
if (!*o_digraph)
{
return key2;
}
/* remember the new key, so we can return it if this isn't a digraph */
newkey = key2;
/* sort key1 and key2, so that their original order won't matter */
if (key1 > key2)
{
key2 = key1;
key1 = newkey;
}
/* scan through the digraph chart */
for (dp = digs;
dp && (dp->key1 != key1 || dp->key2 != key2);
dp = dp->next)
{
}
/* if this combination isn't in there, just use the new key */
if (!dp)
{
return newkey;
}
/* else use the digraph key */
return dp->dig;
}
/* this function lists or defines digraphs */
void do_digraph(bang, extra)
int bang;
char extra[];
{
int dig;
REG struct _DIG *dp;
struct _DIG *prev;
static int user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
char listbuf[8];
/* if "extra" is NULL, then we've reached the end of the built-ins */
if (!extra)
{
user_defined = TRUE;
return;
}
/* if no args, then display the existing digraphs */
if (*extra < ' ')
{
listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
listbuf[7] = '\0';
for (dig = 0, dp = digs; dp; dp = dp->next)
{
if (dp->save || bang)
{
dig += 7;
if (dig >= COLS)
{
addch('\n');
exrefresh();
dig = 7;
}
listbuf[3] = dp->key1;
listbuf[4] = dp->key2;
listbuf[6] = dp->dig;
qaddstr(listbuf);
}
}
addch('\n');
exrefresh();
return;
}
/* make sure we have at least two characters */
if (!extra[1])
{
msg("Digraphs must be composed of two characters");
return;
}
/* sort key1 and key2, so that their original order won't matter */
if (extra[0] > extra[1])
{
dig = extra[0];
extra[0] = extra[1];
extra[1] = dig;
}
/* locate the new digraph character */
for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
{
}
dig = extra[dig];
if (!bang && dig)
{
dig |= 0x80;
}
/* search for the digraph */
for (prev = (struct _DIG *)0, dp = digs;
dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
prev = dp, dp = dp->next)
{
}
/* deleting the digraph? */
if (!dig)
{
if (!dp)
{
#ifndef CRUNCH
msg("%c%c not a digraph", extra[0], extra[1]);
#endif
return;
}
if (prev)
prev->next = dp->next;
else
digs = dp->next;
free(dp);
return;
}
/* if necessary, create a new digraph struct for the new digraph */
if (dig && !dp)
{
dp = (struct _DIG *)malloc(sizeof *dp);
if (!dp)
{
msg("Out of space in the digraph table");
return;
}
if (prev)
prev->next = dp;
else
digs = dp;
dp->next = (struct _DIG *)0;
}
/* assign it the new digraph value */
dp->key1 = extra[0];
dp->key2 = extra[1];
dp->dig = dig;
dp->save = user_defined;
}
# ifndef NO_MKEXRC
void savedigs(fd)
int fd;
{
static char buf[] = "digraph! XX Y\n";
REG struct _DIG *dp;
for (dp = digs; dp; dp = dp->next)
{
if (dp->save)
{
buf[9] = dp->key1;
buf[10] = dp->key2;
buf[12] = dp->dig;
write(fd, buf, (unsigned)14);
}
}
}
# endif
#endif
#ifndef NO_ABBR
static struct _AB
{
struct _AB *next;
char *large; /* the expanded form */
char small[1]; /* the abbreviated form (appended to struct) */
}
*abbrev;
/* This functions lists or defines abbreviations */
void do_abbr(extra)
char *extra;
{
int smlen; /* length of the small form */
int lrg; /* index of the start of the large form */
REG struct _AB *ab; /* used to move through the abbrev list */
struct _AB *prev;
/* no arguments? */
if (!*extra)
{
/* list all current abbreviations */
for (ab = abbrev; ab; ab = ab->next)
{
qaddstr("abbr ");
qaddstr(ab->small);
qaddch(' ');
qaddstr(ab->large);
addch('\n');
exrefresh();
}
return;
}
/* else one or more arguments. Parse the first & look up in abbrev[] */
for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
{
}
for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
{
if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
{
break;
}
}
/* locate the start of the large form, if any */
for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
{
}
/* only one arg? */
if (!extra[lrg])
{
/* trying to undo an abbreviation which doesn't exist? */
if (!ab)
{
#ifndef CRUNCH
msg("\"%s\" not an abbreviation", extra);
#endif
return;
}
/* undo the abbreviation */
if (prev)
prev->next = ab->next;
else
abbrev = ab->next;
free(ab->large);
free(ab);
return;
}
/* multiple args - [re]define an abbreviation */
if (ab)
{
/* redefining - free the old large form */
free(ab->large);
}
else
{
/* adding a new definition - make a new struct */
ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
#ifndef CRUNCH
if (!ab)
{
msg("Out of memory -- Sorry");
return;
}
#endif
strncpy(ab->small, extra, smlen);
ab->small[smlen] = '\0';
ab->next = (struct _AB *)0;
if (prev)
prev->next = ab;
else
abbrev = ab;
}
/* store the new form */
ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
strcpy(ab->large, &extra[lrg]);
}
# ifndef NO_MKEXRC
/* This function is called from cmd_mkexrc() to save the abbreviations */
void saveabbr(fd)
int fd; /* fd to which the :abbr commands should be written */
{
REG struct _AB *ab;
for (ab = abbrev; ab; ab = ab->next)
{
twrite(fd, "abbr ", 5);
twrite(fd, ab->small, strlen(ab->small));
twrite(fd, " ", 1);
twrite(fd, ab->large, strlen(ab->large));
twrite(fd, "\n", 1);
}
}
# endif
/* This function should be called before each char is inserted. If the next
* char is non-alphanumeric and we're at the end of a word, then that word
* is checked against the abbrev[] array and expanded, if appropriate. Upon
* returning from this function, the new char still must be inserted.
*/
static MARK expandabbr(m, ch)
MARK m; /* the cursor position */
int ch; /* the character to insert */
{
char *word; /* where the word starts */
int len; /* length of the word */
REG struct _AB *ab;
/* if no abbreviations are in effect, or ch is aphanumeric, then
* don't do anything
*/
if (!abbrev || !isascii(ch) || isalnum(ch))
{
return m;
}
/* see where the preceding word starts */
pfetch(markline(m));
for (word = ptext + markidx(m), len = 0;
--word >= ptext && (!isascii(*word) || isalnum(*word));
len++)
{
}
word++;
/* if zero-length, then it isn't a word, really -- so nothing */
if (len == 0)
{
return m;
}
/* look it up in the abbrev list */
for (ab = abbrev; ab; ab = ab->next)
{
if (!strncmp(ab->small, word, len) && !ab->small[len])
{
break;
}
}
/* not an abbreviation? then do nothing */
if (!ab)
{
return m;
}
/* else replace the small form with the large form */
add(m, ab->large);
delete(m - len, m);
/* return with the cursor after the end of the large form */
return m - len + strlen(ab->large);
}
#endif
/* This function allows the user to replace an existing (possibly zero-length)
* chunk of text with typed-in text. It returns the MARK of the last character
* that the user typed in.
*/
MARK input(from, to, when, indentref)
MARK from; /* where to start inserting text */
MARK to; /* extent of text to delete */
int when; /* either WHEN_VIINP or WHEN_VIREP */
long indentref; /* line rel to current mark to use for autoindent
reference - MCH */
{
char key[2]; /* key char followed by '\0' char */
char *build; /* used in building a newline+indent string */
char *scan; /* used while looking at the indent chars of a line */
MARK m; /* some place in the text */
#ifndef NO_EXTENSIONS
int quit = FALSE; /* boolean: are we exiting after this? */
#endif
#ifdef DEBUG
/* if "from" and "to" are reversed, complain */
if (from > to)
{
msg("ERROR: input(%ld:%d, %ld:%d)",
markline(from), markidx(from),
markline(to), markidx(to));
return MARK_UNSET;
}
#endif
key[1] = 0;
/* if we're replacing text with new text, save the old stuff */
/* (Alas, there is no easy way to save text for replace mode) */
if (from != to)
{
cut(from, to);
}
ChangeText
{
/* if doing a dot command, then reuse the previous text */
if (doingdot)
{
/* delete the text that's there now */
if (from != to)
{
delete(from, to);
}
/* insert the previous text */
cutname('.');
cursor = paste(from, FALSE, TRUE) + 1L;
}
else /* interactive version */
{
/* if doing a change within the line... */
if (from != to && markline(from) == markline(to))
{
/* mark the end of the text with a "$" */
change(to - 1, to, "$");
}
else
{
/* delete the old text right off */
if (from != to)
{
delete(from, to);
}
to = from;
}
/* handle autoindent of the first line, maybe */
cursor = from;
if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
{
/* Only autoindent blank lines. */
pfetch(markline(cursor));
if (plen == 0)
{
/* Okay, we really want to autoindent */
pfetch(markline(cursor) + indentref);
for (scan = ptext, build = tmpblk.c;
*scan == ' ' || *scan == '\t';
)
{
*build++ = *scan++;
}
if (build > tmpblk.c)
{
*build = '\0';
add(cursor, tmpblk.c);
cursor += (build - tmpblk.c);
}
}
}
/* repeatedly add characters from the user */
for (;;)
{
/* Get a character */
redraw(cursor, TRUE);
#ifdef DEBUG
msg("cursor=%ld.%d, to=%ld.%d",
markline(cursor), markidx(cursor),
markline(to), markidx(to));
#endif
key[0] = getkey(when);
/* if whitespace & wrapmargin is set & we're
* past the warpmargin, then change the
* whitespace character into a newline
*/
if ((*key == ' ' || *key == '\t')
&& *o_wrapmargin != 0)
{
pfetch(markline(cursor));
if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
{
*key = '\n';
}
}
/* process it */
switch (*key)
{
#ifndef NO_EXTENSIONS
case 0: /* special movement mapped keys */
*key = getkey(0);
switch (*key)
{
case 'h': m = m_left(cursor, 0L); break;
case 'j':
case 'k': m = m_updnto(cursor, 0L, *key); break;
case 'l': m = cursor + 1; break;
case 'b': m = m_bword(cursor, 0L); break;
case 'w': m = m_fword(cursor, 0L); break;
case '^': m = m_front(cursor, 0L); break;
case '$': m = m_rear(cursor, 0L); break;
case ctrl('B'):
case ctrl('F'):
m = m_scroll(cursor, 0L, *key); break;
case 'x': m = v_xchar(cursor, 0L); break;
case 'i': m = to = from = cursor; break;
default: m = MARK_UNSET; break;
}
/* adjust the moved cursor */
m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
if (*key == '$' || (*key == 'l' && m <= cursor))
{
m++;
}
/* if the cursor is reasonable, use it */
if (m == MARK_UNSET)
{
Beep();
}
else
{
if (to > cursor)
{
delete(cursor, to);
redraw(cursor, TRUE);
}
from = to = cursor = m;
}
break;
case ctrl('Z'):
if (getkey(0) == ctrl('Z'))
{
quit = TRUE;
goto BreakBreak;
}
break;
#endif
case ctrl('['):
#ifndef NO_ABBR
cursor = expandabbr(cursor, ctrl('['));
#endif
goto BreakBreak;
case ctrl('U'):
if (markline(cursor) == markline(from))
{
cursor = from;
}
else
{
cursor &= ~(BLKSIZE - 1);
}
break;
case ctrl('D'):
case ctrl('T'):
if (to > cursor)
{
delete(cursor, to);
}
mark[27] = cursor;
cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
if (mark[27])
{
cursor = mark[27];
}
else
{
cursor = m_front(cursor, 0L);
}
to = cursor;
break;
case '\b':
if (cursor <= from)
{
Beep();
}
else if (markidx(cursor) == 0)
{
cursor -= BLKSIZE;
pfetch(markline(cursor));
cursor += plen;
}
else
{
cursor--;
}
break;
case ctrl('W'):
m = m_bword(cursor, 1L);
if (markline(m) == markline(cursor) && m >= from)
{
cursor = m;
if (from > cursor)
{
from = cursor;
}
}
else
{
Beep();
}
break;
case '\n':
#if OSK
case '\l':
#else
case '\r':
#endif
#ifndef NO_ABBR
cursor = expandabbr(cursor, '\n');
#endif
build = tmpblk.c;
*build++ = '\n';
if (*o_autoindent)
{
/* figure out indent for next line */
pfetch(markline(cursor));
for (scan = ptext; *scan == ' ' || *scan == '\t'; )
{
*build++ = *scan++;
}
/* remove indent from this line, if blank */
if (!*scan && plen > 0)
{
to = cursor &= ~(BLKSIZE - 1);
delete(cursor, cursor + plen);
}
}
*build = 0;
if (cursor >= to && when != WHEN_VIREP)
{
add(cursor, tmpblk.c);
}
else
{
change(cursor, to, tmpblk.c);
}
redraw(cursor, TRUE);
to = cursor = (cursor & ~(BLKSIZE - 1))
+ BLKSIZE
+ (int)(build - tmpblk.c) - 1;
break;
case ctrl('A'):
case ctrl('P'):
if (cursor < to)
{
delete(cursor, to);
}
if (*key == ctrl('A'))
{
cutname('.');
}
to = cursor = paste(cursor, FALSE, TRUE) + 1L;
break;
case ctrl('V'):
if (cursor >= to && when != WHEN_VIREP)
{
add(cursor, "^");
}
else
{
change(cursor, to, "^");
to = cursor + 1;
}
redraw(cursor, TRUE);
*key = getkey(0);
if (*key == '\n')
{
/* '\n' too hard to handle */
#if OSK
*key = '\l';
#else
*key = '\r';
#endif
}
change(cursor, cursor + 1, key);
cursor++;
if (cursor > to)
{
to = cursor;
}
break;
case ctrl('L'):
case ctrl('R'):
redraw(MARK_UNSET, FALSE);
break;
default:
if (cursor >= to && when != WHEN_VIREP)
{
#ifndef NO_ABBR
cursor = expandabbr(cursor, *key);
#endif
add(cursor, key);
cursor++;
to = cursor;
}
else
{
pfetch(markline(cursor));
if (markidx(cursor) == plen)
{
#ifndef NO_ABBR
cursor = expandabbr(cursor, *key);
#endif
add(cursor, key);
}
else
{
#ifndef NO_DIGRAPH
*key = digraph(ptext[markidx(cursor)], *key);
#endif
#ifndef NO_ABBR
cursor = expandabbr(cursor, *key);
#endif
change(cursor, cursor + 1, key);
}
cursor++;
}
#ifndef NO_SHOWMATCH
/* show matching "({[" if neceesary */
if (*o_showmatch && strchr(")}]", *key))
{
redraw(cursor, TRUE);
m = m_match(cursor - 1, 0L);
if (markline(m) >= topline
&& markline(m) <= botline)
{
redraw(m, TRUE);
refresh();
sleep(1);
}
}
#endif
} /* end switch(*key) */
} /* end for(;;) */
BreakBreak:;
/* delete any excess characters */
/* this includes any whitespace on a whitespace
only line if we are in autoindent mode - MCH */
if (*o_autoindent)
{
/* remove indent from this line, if blank */
pfetch(markline(cursor));
for (scan = ptext; *scan == ' ' || *scan == '\t'; )
*scan++;
if (!*scan && plen > 0)
{
to = cursor &= ~(BLKSIZE - 1);
delete(cursor, cursor + plen);
}
}
if (cursor < to)
{
delete(cursor, to);
}
} /* end if doingdot else */
} /* end ChangeText */
/* put the new text into a cut buffer for possible reuse */
if (!doingdot)
{
blksync();
cutname('.');
cut(from, cursor);
}
/* move to last char that we inputted, unless it was newline */
if (markidx(cursor) != 0)
{
cursor--;
}
redraw(cursor, FALSE);
#ifndef NO_EXTENSIONS
if (quit)
{
/* if this is a nested "do", then cut it short */
abortdo();
/* exit, unless we can't write out the file */
cursor = v_xit(cursor, 0L, 'Z');
}
#endif
rptlines = 0L;
return cursor;
}
SHAR_EOF
fi # end of overwriting check
# End of shell archive
exit 0
Technical Details
Message-ID:
<1991Jan11.020153.27223@wlbr.imsd.contel.com>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!rutgers!apple!voder!wlbr!roger.imsd.contel.com!mhShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!rutgers!apple!voder!wlbr!roger.imsd.contel.com!mh From: mh@roger.imsd.contel.com (Mike H.) Newsgroups: comp.windows.news Subject: NeWS version of elvis (the vi clone) part 4 of 9 Message-ID: <1991Jan11.020153.27223@wlbr.imsd.contel.com> Date: 11 Jan 91 02:01:53 GMT Sender: news@wlbr.imsd.contel.com (news) Distribution: comp Organization: Contel FSD, Westlake Village, CA Lines: 1546 Nntp-Posting-Host: roger.imsd.contel.com