home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Source Code 1992 March
/
Source_Code_CD-ROM_Walnut_Creek_March_1992.iso
/
usenet
/
altsrcs
/
2
/
2195
< prev
next >
Wrap
Internet Message Format
|
1990-12-28
|
78KB
From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall)
Newsgroups: alt.sources
Subject: Elvis 1.4, part 4 of 8
Message-ID: <828@pdxgate.UUCP>
Date: 3 Dec 90 21:31:07 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 9230 Dec 2 17:57 blk.c
# -rw-r--r-- 1 kirkenda 23975 Dec 2 17:57 cmd1.c
# -rw-r--r-- 1 kirkenda 16797 Dec 2 17:57 cmd2.c
# -rw-r--r-- 1 kirkenda 7951 Dec 2 17:57 config.h
# -rw-r--r-- 1 kirkenda 14196 Dec 2 17:57 curses.c
#
if test -f blk.c -a "$1" != -f
then
echo Will not overwrite blk.c
else
echo Extracting blk.c
sed 's/^X//' >blk.c <<\eof
X/* blk.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 the functions that get/put blocks from the temp file.
X * It also contains the "do" and "undo" functions.
X */
X
X#include "config.h"
X#include "vi.h"
X
X#ifndef NBUFS
X# define NBUFS 5 /* must be at least 3 -- more is better */
X#endif
X
Xextern long lseek();
X
X/*------------------------------------------------------------------------*/
X
XBLK hdr; /* buffer for the header block */
X
Xstatic int b4cnt; /* used to count context of beforedo/afterdo */
Xstatic struct _blkbuf
X{
X BLK buf; /* contents of a text block */
X unsigned short logical; /* logical block number */
X int dirty; /* must the buffer be rewritten? */
X}
X blk[NBUFS], /* buffers for text[?] blocks */
X *toonew, /* buffer which shouldn't be recycled yet */
X *newtoo, /* another buffer which should be recycled */
X *recycle = blk; /* next block to be recycled */
X
X
X
X
X
X/* This function wipes out all buffers */
Xvoid blkinit()
X{
X int i;
X
X for (i = 0; i < NBUFS; i++)
X {
X blk[i].logical = 0;
X blk[i].dirty = FALSE;
X }
X for (i = 0; i < MAXBLKS; i++)
X {
X hdr.n[i] = 0;
X }
X}
X
X/* This function allocates a buffer and fills it with a given block's text */
XBLK *blkget(logical)
X int logical; /* logical block number to fetch */
X{
X REG struct _blkbuf *this; /* used to step through blk[] */
X REG int i;
X
X /* if logical is 0, just return the hdr buffer */
X if (logical == 0)
X {
X return &hdr;
X }
X
X /* see if we have that block in mem already */
X for (this = blk; this < &blk[NBUFS]; this++)
X {
X if (this->logical == logical)
X {
X newtoo = toonew;
X toonew = this;
X return &this->buf;
X }
X }
X
X /* choose a block to be recycled */
X do
X {
X this = recycle++;
X if (recycle == &blk[NBUFS])
X {
X recycle = blk;
X }
X } while (this == toonew || this == newtoo);
X
X /* if it contains a block, flush that block */
X blkflush(this);
X
X /* fill this buffer with the desired block */
X this->logical = logical;
X if (hdr.n[logical])
X {
X /* it has been used before - fill it from tmp file */
X lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
X if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
X {
X msg("Error reading back from tmp file!");
X }
X }
X else
X {
X /* it is new - zero it */
X for (i = 0; i < BLKSIZE; i++)
X {
X this->buf.c[i] = 0;
X }
X }
X
X /* This isn't really a change, but it does potentially invalidate
X * the kinds of shortcuts that the "changes" variable is supposed
X * to protect us from... so count it as a change.
X */
X changes++;
X
X /* mark it as being "not dirty" */
X this->dirty = 0;
X
X /* return it */
X newtoo = toonew;
X toonew = this;
X return &this->buf;
X}
X
X
X
X/* This function writes a block out to the temporary file */
Xvoid blkflush(this)
X REG struct _blkbuf *this; /* the buffer to flush */
X{
X long seekpos; /* seek position of the new block */
X unsigned short physical; /* physical block number */
X
X /* if its empty (an orphan blkadd() maybe?) then make it dirty */
X if (this->logical && !*this->buf.c)
X {
X blkdirty(&this->buf);
X }
X
X /* if it's an empty buffer or a clean version is on disk, quit */
X if (!this->logical || hdr.n[this->logical] && !this->dirty)
X {
X return;
X }
X
X /* find a free place in the file */
X#ifndef NO_RECYCLE
X seekpos = allocate();
X lseek(tmpfd, seekpos, 0);
X#else
X seekpos = lseek(tmpfd, 0L, 2);
X#endif
X physical = seekpos / BLKSIZE;
X
X /* put the block there */
X if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
X {
X msg("Trouble writing to tmp file");
X }
X this->dirty = FALSE;
X
X /* update the header so it knows we put it there */
X hdr.n[this->logical] = physical;
X}
X
X
X/* This function sets a block's "dirty" flag or deletes empty blocks */
Xvoid blkdirty(bp)
X BLK *bp; /* buffer returned by blkget() */
X{
X REG int i, j;
X REG char *scan;
X REG int k;
X
X /* find the buffer */
X for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
X {
X }
X#ifdef DEBUG
X if (i >= NBUFS)
X {
X msg("blkdirty() called with unknown buffer at 0x%lx", bp);
X return;
X }
X if (blk[i].logical == 0)
X {
X msg("blkdirty called with freed buffer");
X return;
X }
X#endif
X
X /* if this block ends with line# INFINITY, then it must have been
X * allocated unnecessarily during tmpstart(). Forget it.
X */
X if (lnum[blk[i].logical] == INFINITY)
X {
X#ifdef DEBUG
X if (blk[i].buf.c[0])
X {
X msg("bkldirty called with non-empty extra BLK");
X }
X#endif
X blk[i].logical = 0;
X blk[i].dirty = FALSE;
X return;
X }
X
X /* count lines in this block */
X for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
X {
X if (*scan == '\n')
X {
X j++;
X }
X }
X
X /* adjust lnum, if necessary */
X k = blk[i].logical;
X j += (lnum[k - 1] - lnum[k]);
X if (j != 0)
X {
X nlines += j;
X while (k < MAXBLKS && lnum[k] != INFINITY)
X {
X lnum[k++] += j;
X }
X }
X
X /* if it still has text, mark it as dirty */
X if (*bp->c)
X {
X blk[i].dirty = TRUE;
X }
X else /* empty block, so delete it */
X {
X /* adjust the cache */
X k = blk[i].logical;
X for (j = 0; j < NBUFS; j++)
X {
X if (blk[j].logical >= k)
X {
X blk[j].logical--;
X }
X }
X
X /* delete it from hdr.n[] and lnum[] */
X blk[i].logical = 0;
X blk[i].dirty = FALSE;
X while (k < MAXBLKS - 1)
X {
X hdr.n[k] = hdr.n[k + 1];
X lnum[k] = lnum[k + 1];
X k++;
X }
X hdr.n[MAXBLKS - 1] = 0;
X lnum[MAXBLKS - 1] = INFINITY;
X }
X}
X
X
X/* insert a new block into hdr, and adjust the cache */
XBLK *blkadd(logical)
X int logical; /* where to insert the new block */
X{
X REG int i;
X
X /* adjust hdr and lnum[] */
X for (i = MAXBLKS - 1; i > logical; i--)
X {
X hdr.n[i] = hdr.n[i - 1];
X lnum[i] = lnum[i - 1];
X }
X hdr.n[logical] = 0;
X lnum[logical] = lnum[logical - 1];
X
X /* adjust the cache */
X for (i = 0; i < NBUFS; i++)
X {
X if (blk[i].logical >= logical)
X {
X blk[i].logical++;
X }
X }
X
X /* return the new block, via blkget() */
X return blkget(logical);
X}
X
X
X/* This function forces all dirty blocks out to disk */
Xvoid blksync()
X{
X int i;
X
X for (i = 0; i < NBUFS; i++)
X {
X /* blk[i].dirty = TRUE; */
X blkflush(&blk[i]);
X }
X if (*o_sync)
X {
X sync();
X }
X}
X
X/*------------------------------------------------------------------------*/
X
Xstatic MARK undocurs; /* where the cursor should go if undone */
Xstatic long oldnlines;
Xstatic long oldlnum[MAXBLKS];
X
X
X/* This function should be called before each command that changes the text.
X * It defines the state that undo() will reset the file to.
X */
Xvoid beforedo(forundo)
X int forundo; /* boolean: is this for an undo? */
X{
X REG int i;
X REG long l;
X
X /* if this is a nested call to beforedo, quit! Use larger context */
X if (b4cnt++ > 0)
X {
X return;
X }
X
X /* force all block buffers to disk */
X blksync();
X
X#ifndef NO_RECYCLE
X /* perform garbage collection on blocks from tmp file */
X garbage();
X#endif
X
X /* force the header out to disk */
X lseek(tmpfd, 0L, 0);
X if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
X {
X msg("Trouble writing header to tmp file ");
X }
X
X /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
X if (forundo)
X {
X for (i = 0; i < MAXBLKS; i++)
X {
X l = lnum[i];
X lnum[i] = oldlnum[i];
X oldlnum[i] = l;
X }
X l = nlines;
X nlines = oldnlines;
X oldnlines = l;
X }
X else
X {
X for (i = 0; i < MAXBLKS; i++)
X {
X oldlnum[i] = lnum[i];
X }
X oldnlines = nlines;
X }
X
X /* save the cursor position */
X undocurs = cursor;
X
X /* upon return, the calling function continues and makes changes... */
X}
X
X/* This function marks the end of a (nested?) change to the file */
Xvoid afterdo()
X{
X if (--b4cnt)
X {
X /* after abortdo(), b4cnt may decribe nested beforedo/afterdo
X * pairs incorrectly. If it is decremented to often, then
X * keep b4cnt sane but don't do anything else.
X */
X if (b4cnt < 0)
X b4cnt = 0;
X
X return;
X }
X
X /* make sure the cursor wasn't left stranded in deleted text */
X if (markline(cursor) > nlines)
X {
X cursor = MARK_LAST;
X }
X /* NOTE: it is still possible that markidx(cursor) is after the
X * end of a line, so the Vi mode will have to take care of that
X * itself */
X
X /* if a significant change has been made to this file, then set the
X * MODIFIED flag.
X */
X if (significant)
X {
X setflag(file, MODIFIED);
X }
X}
X
X/* This function cuts short the current set of changes. It is called after
X * a SIGINT.
X */
Xvoid abortdo()
X{
X /* finish the operation immediately. */
X if (b4cnt > 0)
X {
X b4cnt = 1;
X afterdo();
X }
X
X /* in visual mode, the screen is probably screwed up */
X if (mode == MODE_COLON)
X {
X mode = MODE_VI;
X }
X if (mode == MODE_VI)
X {
X redraw(MARK_UNSET, FALSE);
X }
X}
X
X/* This function discards all changes made since the last call to beforedo() */
Xint undo()
X{
X BLK oldhdr;
X
X /* if beforedo() has never been run, fail */
X if (!tstflag(file, MODIFIED))
X {
X msg("You haven't modified this file yet.");
X return FALSE;
X }
X
X /* read the old header form the tmp file */
X lseek(tmpfd, 0L, 0);
X if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
X {
X msg("Trouble rereading the old header from tmp file");
X }
X
X /* "do" the changed version, so we can undo the "undo" */
X cursor = undocurs;
X beforedo(TRUE);
X afterdo();
X
X /* wipe out the block buffers - we can't assume they're correct */
X blkinit();
X
X /* use the old header -- and therefore the old text blocks */
X hdr = oldhdr;
X
X /* This is a change */
X changes++;
X
X return TRUE;
X}
eof
if test `wc -c <blk.c` -ne 9230
then
echo blk.c damaged!
fi
fi
if test -f cmd1.c -a "$1" != -f
then
echo Will not overwrite cmd1.c
else
echo Extracting cmd1.c
sed 's/^X//' >cmd1.c <<\eof
X/* cmd1.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 some of the EX commands - mostly ones that deal with
X * files, options, etc. -- anything except text.
X */
X
X#include "config.h"
X#include <ctype.h>
X#include "vi.h"
X#include "regexp.h"
X
X#if MSDOS
X#define DATE __DATE__
X#endif
X
X#ifdef DEBUG
X/* print the selected lines with info on the blocks */
X/*ARGSUSED*/
Xvoid cmd_debug(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X REG char *scan;
X REG long l;
X REG int i;
X int len;
X
X /* scan lnum[] to determine which block its in */
X l = markline(frommark);
X for (i = 1; l > lnum[i]; i++)
X {
X }
X
X do
X {
X /* fetch text of the block containing that line */
X scan = blkget(i)->c;
X
X /* calculate its length */
X if (scan[BLKSIZE - 1])
X {
X len = BLKSIZE;
X }
X else
X {
X len = strlen(scan);
X }
X
X /* print block stats */
X msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
X i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
X msg("##### len=%d, buf=0x%lx, %sdirty",
X len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
X if (bang)
X {
X while (--len >= 0)
X {
X addch(*scan);
X scan++;
X }
X }
X exrefresh();
X
X /* next block */
X i++;
X } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
X}
X
X
X/* This function checks a lot of conditions to make sure they aren't screwy */
X/*ARGSUSED*/
Xvoid cmd_validate(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X char *scan;
X int i;
X int nlcnt; /* used to count newlines */
X int len; /* counts non-NUL characters */
X
X /* check lnum[0] */
X if (lnum[0] != 0L)
X {
X msg("lnum[0] = %ld", lnum[0]);
X }
X
X /* check each block */
X for (i = 1; lnum[i] <= nlines; i++)
X {
X scan = blkget(i)->c;
X if (scan[BLKSIZE - 1])
X {
X msg("block %d has no NUL at the end", i);
X }
X else
X {
X for (nlcnt = len = 0; *scan; scan++, len++)
X {
X if (*scan == '\n')
X {
X nlcnt++;
X }
X }
X if (scan[-1] != '\n')
X {
X msg("block %d doesn't end with '\\n' (length %d)", i, len);
X }
X if (bang || nlcnt != lnum[i] - lnum[i - 1])
X {
X msg("block %d (line %ld?) has %d lines, but should have %ld",
X i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
X }
X }
X exrefresh();
X }
X
X /* check lnum again */
X if (lnum[i] != INFINITY)
X {
X msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
X i, hdr.n[i], i, lnum[i]);
X }
X
X msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
X}
X#endif /* DEBUG */
X
X
X/*ARGSUSED*/
Xvoid cmd_mark(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X /* validate the name of the mark */
X if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
X {
X msg("Invalid mark name");
X return;
X }
X
X mark[*extra - 'a'] = tomark;
X}
X
X/*ARGSUSED*/
Xvoid cmd_write(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X int fd;
X int append; /* boolean: write in "append" mode? */
X REG long l;
X REG char *scan;
X REG int i;
X
X /* if all lines are to be written, use tmpsave() */
X if (frommark == MARK_FIRST && tomark == MARK_LAST)
X {
X tmpsave(extra, bang);
X return;
X }
X
X /* see if we're going to do this in append mode or not */
X append = FALSE;
X if (extra[0] == '>' && extra[1] == '>')
X {
X extra += 2;
X append = TRUE;
X }
X
X /* either the file must not exist, or we must have a ! or be appending */
X if (access(extra, 0) == 0 && !bang && !append)
X {
X msg("File already exists - Use :w! to overwrite");
X return;
X }
X
X /* else do it line-by-line, like cmd_print() */
X if (append)
X {
X#ifdef O_APPEND
X fd = open(extra, O_WRONLY|O_APPEND);
X#else
X fd = open(extra, O_WRONLY);
X if (fd >= 0)
X {
X lseek(fd, 0L, 2);
X }
X#endif
X }
X else
X {
X fd = -1; /* so we know the file isn't open yet */
X }
X
X if (fd < 0)
X {
X fd = creat(extra, FILEPERMS);
X if (fd < 0)
X {
X msg("Can't write to \"%s\"", extra);
X return;
X }
X }
X for (l = markline(frommark); l <= markline(tomark); l++)
X {
X /* get the next line */
X scan = fetchline(l);
X i = strlen(scan);
X scan[i++] = '\n';
X
X /* print the line */
X twrite(fd, scan, i);
X }
X close(fd);
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_shell(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X static char prevextra[80];
X
X /* special case: ":sh" means ":!sh" */
X if (cmd == CMD_SHELL)
X {
X extra = o_shell;
X frommark = tomark = 0L;
X }
X
X /* if extra is "!", substute previous command */
X if (*extra == '!')
X {
X if (!*prevextra)
X {
X msg("No previous shell command to substitute for '!'");
X return;
X }
X extra = prevextra;
X }
X else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
X {
X strcpy(prevextra, extra);
X }
X
X /* if no lines were specified, just run the command */
X suspend_curses();
X if (frommark == 0L)
X {
X system(extra);
X }
X else /* pipe lines from the file through the command */
X {
X filter(frommark, tomark, extra);
X }
X
X /* resume curses quietly for MODE_EX, but noisily otherwise */
X resume_curses(mode == MODE_EX);
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_global(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra; /* rest of the command line */
X{
X char *cmdptr; /* the command from the command line */
X char cmdln[100]; /* copy of the command from the command line */
X char *line; /* a line from the file */
X long l; /* used as a counter to move through lines */
X long lqty; /* quantity of lines to be scanned */
X long nchanged; /* number of lines changed */
X regexp *re; /* the compiled search expression */
X
X /* can't nest global commands */
X if (doingglobal)
X {
X msg("Can't nest global commands.");
X rptlines = -1L;
X return;
X }
X
X /* ":g! ..." is the same as ":v ..." */
X if (bang)
X {
X cmd = CMD_VGLOBAL;
X }
X
X /* make sure we got a search pattern */
X if (*extra != '/' && *extra != '?')
X {
X msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
X return;
X }
X
X /* parse & compile the search pattern */
X cmdptr = parseptrn(extra);
X if (!extra[1])
X {
X msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
X return;
X }
X re = regcomp(extra + 1);
X if (!re)
X {
X /* regcomp found & described an error */
X return;
X }
X
X /* for each line in the range */
X doingglobal = TRUE;
X ChangeText
X {
X /* NOTE: we have to go through the lines in a forward order,
X * otherwise "g/re/p" would look funny. *BUT* for "g/re/d"
X * to work, simply adding 1 to the line# on each loop won't
X * work. The solution: count lines relative to the end of
X * the file. Think about it.
X */
X for (l = nlines - markline(frommark),
X lqty = markline(tomark) - markline(frommark) + 1L,
X nchanged = 0L;
X lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
X l--, lqty--)
X {
X /* fetch the line */
X line = fetchline(nlines - l);
X
X /* if it contains the search pattern... */
X if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
X {
X /* move the cursor to that line */
X cursor = MARK_AT_LINE(nlines - l);
X
X /* do the ex command (without mucking up
X * the original copy of the command line)
X */
X strcpy(cmdln, cmdptr);
X rptlines = 0L;
X doexcmd(cmdln);
X nchanged += rptlines;
X }
X }
X }
X doingglobal = FALSE;
X
X /* free the regexp */
X free(re);
X
X /* Reporting...*/
X rptlines = nchanged;
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_file(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X#ifndef CRUNCH
X /* if we're given a new filename, use it as this file's name */
X if (extra && *extra)
X {
X strcpy(origname, extra);
X }
X#endif
X if (cmd == CMD_FILE)
X {
X msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]",
X *origname ? origname : "[NO FILE]",
X tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
X tstflag(file, READONLY) ? "[READONLY]" : "",
X nlines,
X markline(frommark),
X markline(frommark) * 100 / nlines);
X }
X else if (markline(frommark) == markline(tomark))
X {
X msg("%ld", markline(frommark));
X }
X else
X {
X msg("range \"%ld,%ld\" contains %ld lines",
X markline(frommark),
X markline(tomark),
X markline(tomark) - markline(frommark) + 1L);
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_edit(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X long line = 1L; /* might be set to prevline */
X
X /* Editing previous file? Then start at previous line */
X if (!strcmp(extra, prevorig))
X {
X line = prevline;
X }
X
X#ifndef CRUNCH
X /* if we were given an explicit starting line, then start there */
X if (*extra == '+')
X {
X for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
X {
X line *= 10L;
X line += (*extra - '0');
X }
X while (isascii(*extra) && isspace(*extra))
X {
X extra++;
X }
X }
X#endif /* not CRUNCH */
X
X /* switch files */
X if (tmpabort(bang))
X {
X tmpstart(extra);
X if (line <= nlines && line >= 1L)
X {
X cursor = MARK_AT_LINE(line);
X }
X }
X else
X {
X msg("Use edit! to abort changes, or w to save changes");
X
X /* so we can say ":e!#" next time... */
X strcpy(prevorig, extra);
X prevline = 1L;
X }
X}
X
X/* This code is also used for rewind -- GB */
X
X/*ARGSUSED*/
Xvoid cmd_next(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X int i, j;
X char *scan;
X char *build;
X
X /* if extra stuff given, use ":args" to define a new args list */
X if (cmd == CMD_NEXT && extra && *extra)
X {
X cmd_args(frommark, tomark, cmd, bang, extra);
X }
X
X /* move to the next arg */
X if (cmd == CMD_NEXT)
X {
X i = argno + 1;
X }
X else if (cmd == CMD_PREVIOUS)
X {
X i = argno - 1;
X }
X else /* cmd == CMD_REWIND */
X {
X i = 0;
X }
X if (i < 0 || i >= nargs)
X {
X msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
X return;
X }
X
X /* find & isolate the name of the file to edit */
X for (j = i, scan = args; j > 0; j--)
X {
X while(!isascii(*scan) || !isspace(*scan))
X {
X scan++;
X }
X while (isascii(*scan) && isspace(*scan))
X {
X scan++;
X }
X }
X for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
X {
X *build++ = *scan++;
X }
X *build = '\0';
X
X /* switch to the next file */
X if (tmpabort(bang))
X {
X tmpstart(tmpblk.c);
X argno = i;
X }
X else
X {
X msg("Use :%s! to abort changes, or w to save changes",
X cmd == CMD_NEXT ? "next" :
X cmd == CMD_PREVIOUS ? "previous" :
X "rewind");
X }
X}
X
X/* also called from :wq -- always writes back in this case */
X
X/*ARGSUSED*/
Xvoid cmd_xit(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X static long whenwarned; /* when the user was last warned of extra files */
X int oldflag;
X
X /* if there are more files to edit, then warn user */
X if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
X {
X msg("More files to edit -- Use \":n\" to go to next file");
X whenwarned = changes;
X return;
X }
X
X if (cmd == CMD_QUIT)
X {
X if (tmpabort(bang))
X {
X mode = MODE_QUIT;
X }
X else
X {
X msg("Use q! to abort changes, or wq to save changes");
X }
X }
X else
X {
X /* else try to save this file */
X oldflag = tstflag(file, MODIFIED);
X if (cmd == CMD_WQUIT)
X setflag(file, MODIFIED);
X if (tmpend(bang))
X {
X mode = MODE_QUIT;
X }
X else
X {
X msg("Could not save file -- use quit! to abort changes, or w filename");
X }
X if (!oldflag)
X clrflag(file, MODIFIED);
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_args(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X char *scan;
X char *eow;
X int col;
X int arg;
X int addcols;
X int scrolled = 0;
X
X /* if no extra names given, or just current name, then report the args
X * we have now.
X */
X if (!extra || !*extra)
X {
X for (scan = args, col=arg=0; *scan; )
X {
X while (*scan && isascii(*scan) && isspace(*scan))
X scan++;
X eow = scan;
X while (*eow && (!isascii(*++eow) || !isspace(*eow)))
X ;
X if (arg == argno)
X addcols = 2;
X else
X addcols = 0;
X if (col+addcols+(int)(eow-scan)+1>=COLS)
X {
X addch('\n');
X scrolled=1;
X col=0;
X }
X else if (arg)
X { qaddch(' ');
X col++;
X }
X if (arg == argno)
X qaddch('[');
X while (scan < eow)
X { qaddch(*scan++);
X col++;
X }
X if (arg == argno)
X qaddch(']');
X arg++;
X col+=addcols;
X }
X /* write a trailing newline */
X if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
X addch('\n');
X exrefresh();
X }
X else /* new args list given */
X {
X strcpy(args, extra);
X argno = -1; /* before the first, so :next will go to first */
X
X /* count the names */
X for (nargs = 0, scan = args; *scan; nargs++)
X {
X while (*scan && (!isascii(*scan) || !isspace(*scan)))
X {
X scan++;
X }
X while (isascii(*scan) && isspace(*scan))
X {
X scan++;
X }
X }
X msg("%d files to edit", nargs);
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_cd(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X char *getenv();
X
X /* default directory name is $HOME */
X if (!*extra)
X {
X extra = getenv("HOME");
X if (!extra)
X {
X msg("environment variable $HOME not set");
X return;
X }
X }
X
X /* go to the directory */
X if (chdir(extra) < 0)
X {
X perror(extra);
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_map(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X char *mapto;
X
X /* "map" with no extra will dump the map table contents */
X if (!*extra)
X {
X dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
X }
X else
X {
X /* "extra" is key to map, followed my what it maps to */
X for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
X {
X }
X while (*mapto == ' ' || *mapto == '\t')
X {
X *mapto++ = '\0';
X }
X
X mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_set(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X if (!*extra)
X {
X dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
X }
X else if (!strcmp(extra, "all"))
X {
X dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */
X }
X else
X {
X setopts(extra);
X
X /* That option may have affected the appearence of text */
X changes++;
X }
X}
X
X/*ARGSUSED*/
Xvoid cmd_tag(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X char *scan; /* used to scan through the tmpblk.c */
X char *cmp; /* char of tag name we're comparing, or NULL */
X char *end; /* marks the end of chars in tmpblk.c */
X int fd; /* file descriptor used to read the file */
X#ifndef NO_MAGIC
X char wasmagic; /* preserves the original state of o_magic */
X#endif
X static char prevtag[30];
X
X /* if no tag is given, use the previous tag */
X if (!extra || !*extra)
X {
X if (!*prevtag)
X {
X msg("No previous tag");
X return;
X }
X extra = prevtag;
X }
X else
X {
X strncpy(prevtag, extra, sizeof prevtag);
X }
X
X /* open the tags file */
X fd = open(TAGS, O_RDONLY);
X if (fd < 0)
X {
X msg("No tags file");
X return;
X }
X
X /* Hmmm... this would have been a lot easier with <stdio.h> */
X
X /* find the line with our tag in it */
X for(scan = end = tmpblk.c, cmp = extra; ; scan++)
X {
X /* read a block, if necessary */
X if (scan >= end)
X {
X end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
X scan = tmpblk.c;
X if (scan >= end)
X {
X msg("tag \"%s\" not found", extra);
X close(fd);
X return;
X }
X }
X
X /* if we're comparing, compare... */
X if (cmp)
X {
X /* matched??? wow! */
X if (!*cmp && *scan == '\t')
X {
X break;
X }
X if (*cmp++ != *scan)
X {
X /* failed! skip to newline */
X cmp = (char *)0;
X }
X }
X
X /* if we're skipping to newline, do it fast! */
X if (!cmp)
X {
X while (scan < end && *scan != '\n')
X {
X scan++;
X }
X if (scan < end)
X {
X cmp = extra;
X }
X }
X }
X
X /* found it! get the rest of the line into memory */
X for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
X {
X *cmp++ = *scan++;
X }
X if (scan == end)
X {
X tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
X }
X
X /* we can close the tags file now */
X close(fd);
X
X /* extract the filename from the line, and edit the file */
X for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
X {
X }
X *cmp++ = '\0';
X if (strcmp(origname, tmpblk.c) != 0)
X {
X if (!tmpabort(bang))
X {
X msg("Use :tag! to abort changes, or :w to save changes");
X return;
X }
X tmpstart(tmpblk.c);
X }
X
X /* move to the desired line (or to line 1 if that fails) */
X#ifndef NO_MAGIC
X wasmagic = *o_magic;
X *o_magic = FALSE;
X#endif
X cursor = MARK_FIRST;
X linespec(cmp, &cursor);
X if (cursor == MARK_UNSET)
X {
X cursor = MARK_FIRST;
X }
X#ifndef NO_MAGIC
X *o_magic = wasmagic;
X#endif
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_visual(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X mode = MODE_VI;
X msg("");
X}
X
X
X
X
X
X/* describe this version of the program */
X/*ARGSUSED*/
Xvoid cmd_version(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X#ifndef DATE
X msg("%s", VERSION);
X#else
X msg("%s (%s)", VERSION, DATE);
X#endif
X#ifdef COMPILED_BY
X msg("Compiled by %s", COMPILED_BY);
X#endif
X#ifdef CREDIT
X msg("%s", CREDIT);
X#endif
X#ifdef COPYING
X msg("%s", COPYING);
X#endif
X}
X
X
X#ifndef NO_MKEXRC
X/* make a .exrc file which describes the current configuration */
X/*ARGSUSED*/
Xvoid cmd_mkexrc(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X int fd;
X
X /* the default name for the .exrc file EXRC */
X if (!*extra)
X {
X extra = EXRC;
X }
X
X /* create the .exrc file */
X fd = creat(extra, FILEPERMS);
X if (fd < 0)
X {
X msg("Couldn't create a new \"%s\" file", extra);
X return;
X }
X
X /* save stuff */
X savekeys(fd);
X saveopts(fd);
X#ifndef NO_DIGRAPH
X savedigs(fd);
X#endif
X#ifndef NO_ABBR
X saveabbr(fd);
X#endif
X
X /* close the file */
X close(fd);
X msg("Created a new \"%s\" file", extra);
X}
X#endif
X
X#ifndef NO_DIGRAPH
X/*ARGSUSED*/
Xvoid cmd_digraph(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X do_digraph(bang, extra);
X}
X#endif
X
X
X#ifndef NO_ERRLIST
Xstatic char errfile[256]; /* the name of a file containing an error */
Xstatic long errline; /* the line number for an error */
X
X/* This static function tries to parse an error message.
X *
X * For most compilers, the first word is taken to be the name of the erroneous
X * file, and the first number after that is taken to be the line number where
X * the error was detected. The description of the error follows, possibly
X * preceded by an "error ... :" or "warning ... :" label which is skipped.
X *
X * For Coherent, error messages look like "line#: filename: message".
X *
X * For non-error lines, or unparsable error lines, this function returns NULL.
X * Normally, though, it alters errfile and errline, and returns a pointer to
X * the description.
X */
Xstatic char *parse_errmsg(text)
X REG char *text;
X{
X REG char *cpy;
X long atol();
X# if COHERENT || TOS /* any Mark Williams compiler */
X /* Get the line number. If no line number, then ignore this line. */
X errline = atol(text);
X if (errline == 0L)
X return (char *)0;
X
X /* Skip to the start of the filename */
X while (*text && *text++ != ':')
X {
X }
X if (!*text++)
X return (char *)0;
X
X /* copy the filename to errfile */
X for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
X {
X }
X if (!*text++)
X return (char *)0;
X cpy[-1] = '\0';
X
X return text;
X# else /* not a Mark Williams compiler */
X char *errmsg;
X
X /* the error message is the whole line, by default */
X errmsg = text;
X
X /* skip leading garbage */
X while (*text && !(isascii(*text) && isalnum(*text)))
X {
X text++;
X }
X
X /* copy over the filename */
X cpy = errfile;
X while(isascii(*text) && isalnum(*text) || *text == '.')
X {
X *cpy++ = *text++;
X }
X *cpy = '\0';
X
X /* ignore the name "Error" and filenames that contain a '/' */
X if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
X {
X return (char *)0;
X }
X
X /* skip garbage between filename and line number */
X while (*text && !(isascii(*text) && isdigit(*text)))
X {
X text++;
X }
X
X /* if the number is part of a larger word, then ignore this line */
X if (*text && isascii(text[-1]) && isalpha(text[-1]))
X {
X return (char *)0;
X }
X
X /* get the error line */
X errline = 0L;
X while (isascii(*text) && isdigit(*text))
X {
X errline *= 10;
X errline += (*text - '0');
X text++;
X }
X
X /* any line which lacks a filename or line number should be ignored */
X if (!errfile[0] || !errline)
X {
X return (char *)0;
X }
X
X /* locate the beginning of the error description */
X while (*text && isascii(*text) && !isspace(*text))
X {
X text++;
X }
X while (*text)
X {
X# ifndef CRUNCH
X /* skip "error #:" and "warning #:" clauses */
X if (!strncmp(text + 1, "rror ", 5)
X || !strncmp(text + 1, "arning ", 7)
X || !strncmp(text + 1, "atal error", 10))
X {
X do
X {
X text++;
X } while (*text && *text != ':');
X continue;
X }
X# endif
X
X /* anything other than whitespace or a colon is important */
X if (!isascii(*text) || (!isspace(*text) && *text != ':'))
X {
X errmsg = text;
X break;
X }
X
X /* else keep looking... */
X text++;
X }
X
X return errmsg;
X# endif /* not COHERENT */
X}
X
X/*ARGSUSED*/
Xvoid cmd_errlist(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X static long endline;/* original number of lines in this file */
X static long offset; /* offset of the next line in the errlist file */
X static int fd = -2;/* fd of the errlist file */
X int i;
X char *errmsg;
X
X /* if a new errlist file is named, open it */
X if (extra && extra[0])
X {
X /* close the old one */
X if (fd >= 0)
X {
X close(fd);
X }
X
X fd = open(extra, O_RDONLY);
X offset = 0L;
X }
X else if (fd < 0)
X {
X fd = open(ERRLIST, O_RDONLY);
X offset = 0L;
X }
X
X /* do we have an errlist file now? */
X if (fd < 0)
X {
X msg("There is no errlist file");
X beep();
X return;
X }
X
X /* find the next error message in the file */
X do
X {
X /* read the next line from the errlist */
X lseek(fd, offset, 0);
X if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
X {
X msg("No more errors");
X beep();
X close(fd);
X return;
X }
X for (i = 0; tmpblk.c[i] != '\n'; i++)
X {
X }
X tmpblk.c[i++] = 0;
X
X /* look for an error message in the line */
X errmsg = parse_errmsg(tmpblk.c);
X if (!errmsg)
X {
X offset += i;
X }
X
X } while (!errmsg);
X
X /* switch to the file containing the error, if this isn't it */
X if (strcmp(origname, errfile))
X {
X if (!tmpabort(bang))
X {
X msg("Use :er! to abort changes, or :w to save changes");
X beep();
X return;
X }
X tmpstart(errfile);
X endline = nlines;
X }
X else if (endline == 0L)
X {
X endline = nlines;
X }
X
X /* go to the line where the error was detected */
X cursor = MARK_AT_LINE(errline + (nlines - endline));
X if (cursor > MARK_LAST)
X {
X cursor = MARK_LAST;
X }
X if (mode == MODE_VI)
X {
X redraw(cursor, FALSE);
X }
X
X /* display the error message */
X if (nlines > endline)
X {
X msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
X }
X else if (nlines < endline)
X {
X msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
X }
X else
X {
X msg("line %ld: %.65s", errline, errmsg);
X }
X
X /* remember where the NEXT error line will start */
X offset += i;
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_make(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X BLK buf;
X
X /* if the file hasn't been saved, then complain unless ! */
X if (tstflag(file, MODIFIED) && !bang)
X {
X msg("\"%s\" not saved yet", origname);
X return;
X }
X
X /* build the command */
X sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
X qaddstr(buf.c);
X addch('\n');
X
X /* run the command, with curses temporarily disabled */
X suspend_curses();
X system(buf.c);
X resume_curses(mode == MODE_EX);
X if (mode == MODE_COLON)
X mode = MODE_VI;
X
X /* run the "errlist" command */
X cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
X}
X#endif
X
X
X#ifndef NO_ABBR
X/*ARGSUSED*/
Xvoid cmd_abbr(frommark, tomark, cmd, bang, extra)
X MARK frommark, tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X do_abbr(extra);
X}
X#endif
eof
if test `wc -c <cmd1.c` -ne 23975
then
echo cmd1.c damaged!
fi
fi
if test -f cmd2.c -a "$1" != -f
then
echo Will not overwrite cmd2.c
else
echo Extracting cmd2.c
sed 's/^X//' >cmd2.c <<\eof
X/* cmd2.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 some of the commands - mostly ones that change text */
X
X#include <ctype.h>
X#include "config.h"
X#include "vi.h"
X#include "regexp.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/*ARGSUSED*/
Xvoid cmd_substitute(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra; /* rest of the command line */
X{
X char *line; /* a line from the file */
X regexp *re; /* the compiled search expression */
X char *subst; /* the substitution string */
X char *opt; /* substitution options */
X long l; /* a line number */
X char *s, *d; /* used during subtitutions */
X char *conf; /* used during confirmation */
X long chline; /* # of lines changed */
X long chsub; /* # of substitutions made */
X static optp; /* boolean option: print when done? */
X static optg; /* boolean option: substitute globally in line? */
X static optc; /* boolean option: confirm before subst? */
X
X
X /* for now, assume this will fail */
X rptlines = -1L;
X
X if (cmd == CMD_SUBAGAIN)
X {
X#ifndef NO_MAGIC
X if (*o_magic)
X subst = "~";
X else
X#endif
X subst = "\\~";
X re = regcomp("");
X
X /* if visual "&", then turn off the "p" and "c" options */
X if (bang)
X {
X optp = optc = FALSE;
X }
X }
X else
X {
X /* make sure we got a search pattern */
X if (*extra != '/' && *extra != '?')
X {
X msg("Usage: s/regular expression/new text/");
X return;
X }
X
X /* parse & compile the search pattern */
X subst = parseptrn(extra);
X re = regcomp(extra + 1);
X }
X
X /* abort if RE error -- error message already given by regcomp() */
X if (!re)
X {
X return;
X }
X
X if (cmd == CMD_SUBSTITUTE)
X {
X /* parse the substitution string & find the option string */
X for (opt = subst; *opt && *opt != *extra; opt++)
X {
X if (*opt == '\\' && opt[1])
X {
X opt++;
X }
X }
X if (*opt)
X {
X *opt++ = '\0';
X }
X
X /* analyse the option string */
X if (!*o_edcompatible)
X {
X optp = optg = optc = FALSE;
X }
X while (*opt)
X {
X switch (*opt++)
X {
X case 'p': optp = !optp; break;
X case 'g': optg = !optg; break;
X case 'c': optc = !optc; break;
X case ' ':
X case '\t': break;
X default:
X msg("Subst options are p, c, and g -- not %c", opt[-1]);
X return;
X }
X }
X }
X
X /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
X if ((optc || optp) && mode == MODE_VI)
X {
X addch('\n');
X exrefresh();
X }
X
X ChangeText
X {
X /* reset the change counters */
X chline = chsub = 0L;
X
X /* for each selected line */
X for (l = markline(frommark); l <= markline(tomark); l++)
X {
X /* fetch the line */
X line = fetchline(l);
X
X /* if it contains the search pattern... */
X if (regexec(re, line, TRUE))
X {
X /* increment the line change counter */
X chline++;
X
X /* initialize the pointers */
X s = line;
X d = tmpblk.c;
X
X /* do once or globally ... */
X do
X {
X#ifndef CRUNCH
X /* confirm, if necessary */
X if (optc)
X {
X for (conf = line; conf < re->startp[0]; conf++)
X addch(*conf);
X standout();
X for ( ; conf < re->endp[0]; conf++)
X addch(*conf);
X standend();
X for (; *conf; conf++)
X addch(*conf);
X addch('\n');
X exrefresh();
X if (getkey(0) != 'y')
X {
X /* copy accross the original chars */
X while (s < re->endp[0])
X *d++ = *s++;
X
X /* skip to next match on this line, if any */
X continue;
X }
X }
X#endif /* not CRUNCH */
X
X /* increment the substitution change counter */
X chsub++;
X
X /* this may be the first line to redraw */
X redrawrange(l, l + 1L, l + 1L);
X
X /* copy stuff from before the match */
X while (s < re->startp[0])
X {
X *d++ = *s++;
X }
X
X /* substitute for the matched part */
X regsub(re, subst, d);
X s = re->endp[0];
X d += strlen(d);
X
X } while (optg && regexec(re, s, FALSE));
X
X /* copy stuff from after the match */
X while (*d++ = *s++) /* yes, ASSIGNMENT! */
X {
X }
X
X /* replace the old version of the line with the new */
X d[-1] = '\n';
X d[0] = '\0';
X change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
X
X /* if supposed to print it, do so */
X if (optp)
X {
X addstr(tmpblk.c);
X exrefresh();
X }
X
X /* move the cursor to that line */
X cursor = MARK_AT_LINE(l);
X }
X }
X }
X
X /* tweak for redrawing */
X mustredraw = TRUE;
X
X /* free the regexp */
X free(re);
X
X /* if done from within a ":g" command, then finish silently */
X if (doingglobal)
X {
X rptlines = chline;
X rptlabel = "changed";
X return;
X }
X
X /* Reporting */
X if (chsub == 0)
X {
X msg("Substitution failed");
X }
X else if (chline >= *o_report)
X {
X msg("%ld substitutions on %ld lines", chsub, chline);
X }
X}
X
X
X
X
X/*ARGSUSED*/
Xvoid cmd_delete(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X MARK curs2; /* an altered form of the cursor */
X
X /* choose your cut buffer */
X if (*extra == '"')
X {
X extra++;
X }
X if (*extra)
X {
X cutname(*extra);
X }
X
X /* make sure we're talking about whole lines here */
X frommark = frommark & ~(BLKSIZE - 1);
X tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
X
X /* yank the lines */
X cut(frommark, tomark);
X
X /* if CMD_DELETE then delete the lines */
X if (cmd != CMD_YANK)
X {
X curs2 = cursor;
X ChangeText
X {
X /* delete the lines */
X delete(frommark, tomark);
X }
X if (curs2 > tomark)
X {
X cursor = curs2 - tomark + frommark;
X }
X else if (curs2 > frommark)
X {
X cursor = frommark;
X }
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_append(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X long l; /* line counter */
X
X ChangeText
X {
X /* if we're doing a change, delete the old version */
X if (cmd == CMD_CHANGE)
X {
X /* delete 'em */
X cmd_delete(frommark, tomark, cmd, bang, extra);
X }
X
X /* new lines start at the frommark line, or after it */
X l = markline(frommark);
X if (cmd == CMD_APPEND)
X {
X l++;
X }
X
X /* get lines until no more lines, or "." line, and insert them */
X while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
X {
X addch('\n');
X if (!strcmp(tmpblk.c, "."))
X {
X break;
X }
X
X strcat(tmpblk.c, "\n");
X add(MARK_AT_LINE(l), tmpblk.c);
X l++;
X }
X }
X
X /* on the odd chance that we're calling this from vi mode ... */
X redraw(MARK_UNSET, FALSE);
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_put(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X /* choose your cut buffer */
X if (*extra == '"')
X {
X extra++;
X }
X if (*extra)
X {
X cutname(*extra);
X }
X
X /* paste it */
X ChangeText
X {
X cursor = paste(frommark, TRUE, FALSE);
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_join(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X long l;
X char *scan;
X int len; /* length of the new line */
X
X /* if only one line is specified, assume the following one joins too */
X if (markline(frommark) == nlines)
X {
X msg("Nothing to join with this line");
X return;
X }
X if (markline(frommark) == markline(tomark))
X {
X tomark += BLKSIZE;
X }
X
X /* get the first line */
X l = markline(frommark);
X strcpy(tmpblk.c, fetchline(l));
X len = strlen(tmpblk.c);
X
X /* build the longer line */
X while (++l <= markline(tomark))
X {
X /* get the next line */
X scan = fetchline(l);
X
X /* remove any leading whitespace */
X while (*scan == '\t' || *scan == ' ')
X {
X scan++;
X }
X
X /* see if the line will fit */
X if (strlen(scan) + len + 3 > BLKSIZE)
X {
X msg("Can't join -- the resulting line would be too long");
X return;
X }
X
X /* catenate it, with a space (or two) in between */
X if (len >= 1 &&
X (tmpblk.c[len - 1] == '.'
X || tmpblk.c[len - 1] == '?'
X || tmpblk.c[len - 1] == '!'))
X {
X tmpblk.c[len++] = ' ';
X }
X tmpblk.c[len++] = ' ';
X strcpy(tmpblk.c + len, scan);
X len += strlen(scan);
X }
X tmpblk.c[len++] = '\n';
X tmpblk.c[len] = '\0';
X
X /* make the change */
X ChangeText
X {
X frommark &= ~(BLKSIZE - 1);
X tomark &= ~(BLKSIZE - 1);
X tomark += BLKSIZE;
X change(frommark, tomark, tmpblk.c);
X }
X
X /* Reporting... */
X rptlines = markline(tomark) - markline(frommark) - 1L;
X rptlabel = "joined";
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_shift(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X long l; /* line number counter */
X int oldidx; /* number of chars previously used for indent */
X int newidx; /* number of chars in the new indent string */
X int oldcol; /* previous indent amount */
X int newcol; /* new indent amount */
X char *text; /* pointer to the old line's text */
X
X /* figure out how much of the screen we must redraw (for vi mode) */
X if (markline(frommark) != markline(tomark))
X {
X mustredraw = TRUE;
X redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
X }
X
X ChangeText
X {
X /* for each line to shift... */
X for (l = markline(frommark); l <= markline(tomark); l++)
X {
X /* get the line - ignore empty lines unless ! mode */
X text = fetchline(l);
X if (!*text && !bang)
X continue;
X
X /* calc oldidx and oldcol */
X for (oldidx = 0, oldcol = 0;
X text[oldidx] == ' ' || text[oldidx] == '\t';
X oldidx++)
X {
X if (text[oldidx] == ' ')
X {
X oldcol += 1;
X }
X else
X {
X oldcol += *o_tabstop - (oldcol % *o_tabstop);
X }
X }
X
X /* calc newcol */
X if (cmd == CMD_SHIFTR)
X {
X newcol = oldcol + (*o_shiftwidth & 0xff);
X }
X else
X {
X newcol = oldcol - (*o_shiftwidth & 0xff);
X if (newcol < 0)
X newcol = 0;
X }
X
X /* if no change, then skip to next line */
X if (oldcol == newcol)
X continue;
X
X /* build a new indent string */
X newidx = 0;
X while (newcol >= *o_tabstop)
X {
X tmpblk.c[newidx++] = '\t';
X newcol -= *o_tabstop;
X }
X while (newcol > 0)
X {
X tmpblk.c[newidx++] = ' ';
X newcol--;
X }
X tmpblk.c[newidx] = '\0';
X
X /* change the old indent string into the new */
X change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
X }
X }
X
X /* Reporting... */
X rptlines = markline(tomark) - markline(frommark) + 1L;
X if (cmd == CMD_SHIFTR)
X {
X rptlabel = ">ed";
X }
X else
X {
X rptlabel = "<ed";
X }
X}
X
X
X/*ARGSUSED*/
Xvoid cmd_read(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X int fd, rc; /* used while reading from the file */
X char *scan; /* used for finding NUL characters */
X int hadnul; /* boolean: any NULs found? */
X int addnl; /* boolean: forced to add newlines? */
X int len; /* number of chars in current line */
X long lines; /* number of lines in current block */
X struct stat statb;
X
X /* special case: if ":r !cmd" then let the filter() function do it */
X if (extra[0] == '!')
X {
X filter(frommark, MARK_UNSET, extra + 1);
X return;
X }
X
X /* open the file */
X fd = open(extra, O_RDONLY);
X if (fd < 0)
X {
X msg("Can't open \"%s\"", extra);
X return;
X }
X
X#ifndef CRUNCH
X if (stat(extra, &statb) < 0)
X {
X msg("Can't stat \"%s\"", extra);
X }
X# if TOS
X if (statb.st_mode & S_IJDIR)
X# else
X# if OSK
X if (statb.st_mode & S_IFDIR)
X# else
X if ((statb.st_mode & S_IFMT) != S_IFREG)
X# endif
X# endif
X {
X msg("\"%s\" is not a regular file", extra);
X return;
X }
X#endif /* not CRUNCH */
X
X /* get blocks from the file, and add them */
X ChangeText
X {
X /* insertion starts at the line following frommark */
X tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
X len = 0;
X hadnul = addnl = FALSE;
X
X /* add an extra newline, so partial lines at the end of
X * the file don't trip us up
X */
X add(tomark, "\n");
X
X /* for each chunk of text... */
X while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
X {
X /* count newlines, convert NULs, etc. ... */
X for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
X {
X /* break up long lines */
X if (*scan != '\n' && len + 2 > BLKSIZE)
X {
X *scan = '\n';
X addnl = TRUE;
X }
X
X /* protect against NUL chars in file */
X if (!*scan)
X {
X *scan = 0x80;
X hadnul = TRUE;
X }
X
X /* starting a new line? */
X if (*scan == '\n')
X {
X /* reset length at newline */
X len = 0;
X lines++;
X }
X else
X {
X len++;
X }
X }
X
X /* add the text */
X *scan = '\0';
X add(tomark, tmpblk.c);
X tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
X }
X
X /* if partial last line, then retain that first newline */
X if (len > 0)
X {
X msg("Last line had no newline");
X tomark += BLKSIZE; /* <- for the rptlines calc */
X }
X else /* delete that first newline */
X {
X delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
X }
X }
X
X /* close the file */
X close(fd);
X
X /* Reporting... */
X rptlines = markline(tomark) - markline(frommark);
X rptlabel = "read";
X
X if (addnl)
X msg("Newlines were added to break up long lines");
X if (hadnul)
X msg("NULs were converted to 0x80");
X}
X
X
X
X/*ARGSUSED*/
Xvoid cmd_undo(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X undo();
X}
X
X
X/* print the selected lines */
X/*ARGSUSED*/
Xvoid cmd_print(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X REG char *scan;
X REG long l;
X REG int col;
X
X for (l = markline(frommark); l <= markline(tomark); l++)
X {
X /* display a line number, if CMD_NUMBER */
X if (cmd == CMD_NUMBER)
X {
X sprintf(tmpblk.c, "%6ld ", l);
X qaddstr(tmpblk.c);
X col = 8;
X }
X else
X {
X col = 0;
X }
X
X /* get the next line & display it */
X for (scan = fetchline(l); *scan; scan++)
X {
X /* expand tabs to the proper width */
X if (*scan == '\t' && cmd != CMD_LIST)
X {
X do
X {
X qaddch(' ');
X col++;
X } while (col % *o_tabstop != 0);
X }
X else if (*scan >= 0 && *scan < ' ' || *scan == '\177')
X {
X qaddch('^');
X qaddch(*scan ^ 0x40);
X col += 2;
X }
X else if ((*scan & 0x80) && cmd == CMD_LIST)
X {
X sprintf(tmpblk.c, "\\%03o", *scan);
X qaddstr(tmpblk.c);
X col += 4;
X }
X else
X {
X qaddch(*scan);
X col++;
X }
X
X /* wrap at the edge of the screen */
X if (!has_AM && col >= COLS)
X {
X addch('\n');
X col -= COLS;
X }
X }
X if (cmd == CMD_LIST)
X {
X qaddch('$');
X }
X addch('\n');
X exrefresh();
X }
X}
X
X
X/* move or copy selected lines */
X/*ARGSUSED*/
Xvoid cmd_move(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X MARK destmark;
X
X /* parse the destination linespec. No defaults. Line 0 is okay */
X destmark = cursor;
X if (!strcmp(extra, "0"))
X {
X destmark = 0L;
X }
X else if (linespec(extra, &destmark) == extra || !destmark)
X {
X msg("invalid destination address");
X return;
X }
X
X /* flesh the marks out to encompass whole lines */
X frommark &= ~(BLKSIZE - 1);
X tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
X destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
X
X /* make sure the destination is valid */
X if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
X {
X msg("invalid destination address");
X }
X
X /* Do it */
X ChangeText
X {
X /* save the text to a cut buffer */
X cutname('\0');
X cut(frommark, tomark);
X
X /* if we're not copying, delete the old text & adjust destmark */
X if (cmd != CMD_COPY)
X {
X delete(frommark, tomark);
X if (destmark >= frommark)
X {
X destmark -= (tomark - frommark);
X }
X }
X
X /* add the new text */
X paste(destmark, FALSE, FALSE);
X }
X
X /* move the cursor to the last line of the moved text */
X cursor = destmark + (tomark - frommark) - BLKSIZE;
X if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
X {
X cursor = MARK_LAST;
X }
X
X /* Reporting... */
X rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
X}
X
X
X
X/* execute EX commands from a file */
X/*ARGSUSED*/
Xvoid cmd_source(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X /* must have a filename */
X if (!*extra)
X {
X msg("\"source\" requires a filename");
X return;
X }
X
X doexrc(extra);
X}
X
X
X#ifndef NO_AT
X/*ARGSUSED*/
Xvoid cmd_at(frommark, tomark, cmd, bang, extra)
X MARK frommark;
X MARK tomark;
X CMD cmd;
X int bang;
X char *extra;
X{
X static nest = FALSE;
X int result;
X char buf[MAXRCLEN];
X
X /* don't allow nested macros */
X if (nest)
X {
X msg("@ macros can't be nested");
X return;
X }
X nest = TRUE;
X
X /* require a buffer name */
X if (*extra == '"')
X extra++;
X if (!*extra || !isascii(*extra) ||!islower(*extra))
X {
X msg("@ requires a cut buffer name (a-z)");
X }
X
X /* get the contents of the buffer */
X result = cb2str(*extra, buf, (unsigned)(sizeof buf));
X if (result <= 0)
X {
X msg("buffer \"%c is empty", *extra);
X }
X else if (result >= sizeof buf)
X {
X msg("buffer \"%c is too large to execute", *extra);
X }
X else
X {
X /* execute the contents of the buffer as ex commands */
X exstring(buf, result);
X }
X
X nest = FALSE;
X}
X#endif
eof
if test `wc -c <cmd2.c` -ne 16797
then
echo cmd2.c damaged!
fi
fi
if test -f config.h -a "$1" != -f
then
echo Will not overwrite config.h
else
echo Extracting config.h
sed 's/^X//' >config.h <<\eof
X/*
X * vi configuration file
X * We try to automatically configure to various compilers and operating
X * systems. Extend the autoconf section as needed.
X */
X
X/*************************** autoconf section ************************/
X
X/* standard unix V (?) */
X#ifdef M_SYSV
X# define UNIXV 1
X#endif
X
X/* xelos system, University of Ulm */
X#ifdef xelos
X# define UNIXV 1
X#endif
X
X/* BSD UNIX? */
X#ifdef bsd
X# define BSD 1
X#endif
X
X/* Microsoft C: sorry, Watcom does the same thing */
X#ifdef M_I86
X# ifndef M_SYSV
X# define MSDOS 1
X# define MICROSOFT 1
X# define COMPILED_BY "Microsoft C 5.10"
X# endif
X#endif
X
X/* Borlands Turbo C */
X#ifdef __TURBOC__
X# define MSDOS 1
X# define TURBOC 1
X# define COMPILED_BY "Turbo C 2.00"
X#endif
X
X/* Tos Mark-Williams */
X#ifdef M68000
X# define TOS 1
X# define COMPILED_BY "Mark Williams C"
X#endif
X
X/* OS9/68000 */
X#ifdef OSK
X# define COMPILED_BY "Microware C V2.3 Edition 40"
X#endif
X
X/*************************** end of autoconf section ************************/
X
X/* All undefined symbols are defined to zero here, to allow for older */
X/* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */
X
X/*************************** operating systems *****************************/
X
X#ifndef BSD
X# define BSD 0 /* UNIX - Berkeley 4.x */
X#endif
X
X#ifndef UNIXV
X# define UNIXV 0 /* UNIX - AT&T SYSV */
X#endif
X
X#ifndef UNIX7
X# define UNIX7 0 /* UNIX - version 7 */
X#endif
X
X#ifndef MSDOS
X# define MSDOS 0 /* PC */
X#endif
X
X#ifndef TOS
X# define TOS 0 /* Atari ST */
X#endif
X
X#ifndef AMIGA
X# define AMIGA 0 /* Commodore Amiga */
X#endif
X
X#ifndef OSK
X# define OSK 0 /* OS-9 / 68k */
X#endif
X
X#ifndef COHERENT
X# define COHERENT 0 /* Coherent */
X#endif
X
X /* Minix has no predefines */
X#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT
X# define MINIX 1
X#else
X# define MINIX 0
X#endif
X
X /* generic combination of Unices */
X#if UNIXV || UNIX7 || BSD || MINIX || COHERENT
X# define ANY_UNIX 1
X#else
X# define ANY_UNIX 0
X#endif
X
X/*************************** compilers **************************************/
X
X#ifndef MICROSOFT
X# define MICROSOFT 0
X#endif
X
X#ifndef TURBOC
X# define TURBOC 0
X#endif
X
X/******************************* Credit ************************************/
X
X#if MSDOS
X# define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel"
X#endif
X
X#if TOS
X# define CREDIT "Ported to Atari/TOS by Guntram Blohm & Martin Patzel"
X#endif
X
X#if OSK
X# define CREDIT "Ported to Microware OS9/68k by Peter Reinig"
X#endif
X
X#if COHERENT
X# define CREDIT "Ported to Coherent by Esa Ahola"
X#endif
X
X/*************************** functions depending on OS *********************/
X
X/* Only MSDOS, TOS, and OS9 need a special function for reading from the
X * keyboard. All others just read from file descriptor 0.
X */
X#if !MSDOS && !TOS && !OSK
X# define ttyread(buf, len) read(0, buf, (unsigned)len) /* raw read */
X#endif
X#if !TOS
X# define ttywrite(buf, len) write(1, buf, (unsigned)(len)) /* raw write */
X#endif
X
X/* The strchr() function is an official standard now, so everybody has it
X * except Unix version 7 (which is old) and BSD Unix (which is academic).
X * Those guys use something called index() to do the same thing.
X */
X#if BSD || UNIX7 || OSK
X# define strchr index
X#endif
Xextern char *strchr();
X
X/* BSD uses bcopy() instead of memcpy() */
X#if BSD
X#define memcpy(dest, src, siz) bcopy(src, dest, siz)
X#endif
X
X/* text versa binary mode for read/write */
X#if !TOS
X#define tread(fd,buf,n) read(fd,buf,(unsigned)(n))
X#define twrite(fd,buf,n) write(fd,buf,(unsigned)(n))
X#endif
X
X/**************************** Compiler quirks *********************************/
X
X/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */
X#if UNIX7 || TOS
X# define void int
X#endif
X
X/* as far as I know, all compilers except version 7 support unsigned char */
X/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */
X#if UNIX7 || MINIX
X# define UCHAR(c) ((c) & 0xff)
X# define uchar char
X#else
X# define UCHAR(c) ((unsigned char)(c))
X# define uchar unsigned char
X#endif
X
X/* Some compilers prefer to have malloc declared as returning a (void *) */
X#if BSD
Xextern void *malloc();
X#else
Xextern char *malloc();
X#endif
X
X/* Most compilers could benefit from using the "register" storage class */
X#if 1
X# define REG register
X#endif
X
X/******************* Names of files and environment vars **********************/
X
X#if ANY_UNIX
X# ifndef TMPDIR
X# if MINIX
X# define TMPDIR "/usr/tmp" /* Keep elvis' temp files off RAM disk! */
X# else
X# define TMPDIR "/tmp" /* directory where temp files live */
X# endif
X# endif
X# define TMPNAME "%s/elv%x%04x%03x" /* temp file */
X# define CUTNAME "%s/elv_%04x%03x" /* cut buffer's temp file */
X# ifndef EXRC
X# define EXRC ".exrc" /* init file in current directory */
X# endif
X# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */
X# ifndef EXINIT
X# define EXINIT "EXINIT"
X# endif
X# ifndef SHELL
X# define SHELL "/bin/sh" /* default shell */
X# endif
X# if COHERENT
X# ifndef REDIRECT
X# define REDIRECT ">" /* Coherent CC writes errors to stdout */
X# endif
X# endif
X#endif
X
X#if MSDOS || TOS
X/* do not change TMPNAME, CUTNAME and SCRATCH*: they MUST begin with '%s\\'! */
X# ifndef TMPDIR
X# define TMPDIR "C:\\tmp" /* directory where temp files live */
X# endif
X# define TMPNAME "%s\\elv%x%04x.%03x" /* temp file */
X# define CUTNAME "%s\\elv_%04x.%03x" /* cut buffer's temp file */
X# if MSDOS
X# if MICROSOFT
X# define CC_COMMAND "cl -c" /* C compiler */
X# else /* TURBO_C */
X# define CC_COMMAND "tc" /* C compiler */
X# endif
X# endif
X# define SCRATCHIN "%s\\siXXXXXX" /* DOS ONLY - output of filter program */
X# define SCRATCHOUT "%s\\soXXXXXX" /* temp file used as input to filter */
X# define SLASH '\\'
X# ifndef SHELL
X# if TOS
X# define SHELL "shell.ttp" /* default shell */
X# else
X# define SHELL "command.com" /* default shell */
X# endif
X# endif
X# define NEEDSYNC TRUE /* assume ":se sync" by default */
X# define REDIRECT ">" /* shell's redirection of stderr */
X# ifndef MAXMAPS
X# define MAXMAPS 40
X# endif
X# ifndef EXINIT
X# define EXINIT "EXINIT"
X# endif
X#endif
X
X#if OSK
X# ifndef TMPDIR
X# define TMPDIR "/dd/tmp" /* directory where temp files live */
X# endif
X# define TMPNAME "%s/elv%x%04x%03x" /* temp file */
X# define CUTNAME "%s/elv_%04x%03x" /* cut buffer's temp file */
X# ifndef CC_COMMAND
X# define CC_COMMAND "cc -r" /* name of the compiler */
X# endif
X# ifndef EXRC
X# define EXRC ".exrc" /* init file in current directory */
X# endif
X# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */
X# ifndef SHELL
X# define SHELL "shell" /* default shell */
X# endif
X# define FILEPERMS (S_IREAD|S_IWRITE) /* file permissions used for creat() */
X# define REDIRECT ">>-" /* shell's redirection of stderr */
X#endif
X
X#ifndef TAGS
X# define TAGS "tags" /* tags file */
X#endif
X
X#ifndef TMPNAME
X# define TMPNAME "%s/elv%x%04x.%03x" /* temp file */
X#endif
X
X#ifndef CUTNAME
X# define CUTNAME "%s/elv_%04x.%03x" /* cut buffer's temp file */
X#endif
X
X#ifndef EXRC
X# define EXRC "elvis.rc"
X#endif
X
X#ifndef HMEXRC
X# if !MSDOS && !TOS
X# define HMEXRC EXRC
X# endif
X#endif
X
X#ifndef KEYWORDPRG
X# define KEYWORDPRG "ref"
X#endif
X
X#ifndef SCRATCHOUT
X# define SCRATCHIN "%s/SIXXXXXX"
X# define SCRATCHOUT "%s/SOXXXXXX"
X#endif
X
X#ifndef ERRLIST
X# define ERRLIST "errlist"
X#endif
X
X#ifndef SLASH
X# define SLASH '/'
X#endif
X
X#ifndef SHELL
X# define SHELL "shell"
X#endif
X
X#ifndef REG
X# define REG
X#endif
X
X#ifndef NEEDSYNC
X# define NEEDSYNC FALSE
X#endif
X
X#ifndef FILEPERMS
X# define FILEPERMS 0666
X#endif
X
X#ifndef CC_COMMAND
X# define CC_COMMAND "cc -c"
X#endif
X
X#ifndef MAKE_COMMAND
X# define MAKE_COMMAND "make"
X#endif
X
X#ifndef REDIRECT
X# define REDIRECT "2>"
X#endif
X
X#ifndef MAXMAPS
X# define MAXMAPS 20 /* number of :map keys */
X#endif
X#ifndef MAXDIGS
X# define MAXDIGS 30 /* number of :digraph combos */
X#endif
X#ifndef MAXABBR
X# define MAXABBR 20 /* number of :abbr entries */
X#endif
eof
if test `wc -c <config.h` -ne 7951
then
echo config.h damaged!
fi
fi
if test -f curses.c -a "$1" != -f
then
echo Will not overwrite curses.c
else
echo Extracting curses.c
sed 's/^X//' >curses.c <<\eof
X/* curses.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 the functions & variables needed for a tiny subset of
X * curses. The principle advantage of this version of curses is its
X * extreme speed. Disadvantages are potentially larger code, few supported
X * functions, limited compatibility with full curses, and only stdscr.
X */
X
X#include "config.h"
X#include "vi.h"
X
X#if ANY_UNIX
X# if UNIXV
X# include <termio.h>
X# undef TIOCWINSZ /* we can't handle it correctly yet */
X# else
X# include <sgtty.h>
X# endif
X#endif
X
X#if TOS
X# include <osbind.h>
X#endif
X
X#if OSK
X# include <sgstat.h>
X#endif
X
X#include <signal.h>
X
Xextern char *getenv();
Xstatic void starttcap();
X
X/* variables, publicly available & used in the macros */
Xshort ospeed; /* speed of the tty, eg B2400 */
X#if OSK
Xchar PC_; /* Pad char */
Xchar *BC; /* backspace character string */
X#else
Xchar PC; /* Pad char */
X#endif
XWINDOW *stdscr; /* pointer into kbuf[] */
XWINDOW kbuf[KBSIZ]; /* a very large output buffer */
Xint LINES; /* :li#: number of rows */
Xint COLS; /* :co#: number of columns */
Xint AM; /* :am: boolean: auto margins? */
Xint PT; /* :pt: boolean: physical tabs? */
Xchar *VB; /* :vb=: visible bell */
Xchar *UP; /* :up=: move cursor up */
Xchar *SO; /* :so=: standout start */
Xchar *SE; /* :se=: standout end */
Xchar *US = ""; /* :us=: underline start */
Xchar *UE = ""; /* :ue=: underline end */
Xchar *MD = ""; /* :md=: bold start */
Xchar *ME = ""; /* :me=: bold end */
Xchar *AS; /* :as=: alternate (italic) start */
Xchar *AE; /* :ae=: alternate (italic) end */
Xchar *CM; /* :cm=: cursor movement */
Xchar *CE; /* :ce=: clear to end of line */
Xchar *CD; /* :cd=: clear to end of screen */
Xchar *AL; /* :al=: add a line */
Xchar *DL; /* :dl=: delete a line */
X#if OSK
Xchar *SR_; /* :sr=: scroll reverse */
X#else
Xchar *SR; /* :sr=: scroll reverse */
X#endif
Xchar *KS; /* :ks=: init string for cursor */
Xchar *KE; /* :ke=: restore string for cursor */
Xchar *KU; /* :ku=: key sequence sent by up arrow */
Xchar *KD; /* :kd=: key sequence sent by down arrow */
Xchar *KL; /* :kl=: key sequence sent by left arrow */
Xchar *KR; /* :kr=: key sequence sent by right arrow */
Xchar *HM; /* :HM=: key sequence sent by the <Home> key */
Xchar *EN; /* :EN=: key sequence sent by the <End> key */
Xchar *PU; /* :PU=: key sequence sent by the <PgUp> key */
Xchar *PD; /* :PD=: key sequence sent by the <PgDn> key */
Xchar *IM; /* :im=: insert mode start */
Xchar *IC = ""; /* :ic=: insert the following character */
Xchar *EI; /* :ei=: insert mode end */
Xchar *DC; /* :dc=: delete a character */
Xchar *TI; /* :ti=: terminal init */ /* GB */
Xchar *TE; /* :te=: terminal exit */ /* GB */
X#ifndef NO_CURSORSHAPE
Xchar *CQ = (char *)0;/* :cQ=: normal cursor */
Xchar *CX = (char *)1;/* :cX=: cursor used for EX command/entry */
Xchar *CV = (char *)2;/* :cV=: cursor used for VI command mode */
Xchar *CI = (char *)3;/* :cI=: cursor used for VI input mode */
Xchar *CR = (char *)4;/* :cR=: cursor used for VI replace mode */
X#endif
Xchar *aend = ""; /* end an attribute -- either UE or ME */
Xchar ERASEKEY; /* backspace key taken from ioctl structure */
X
X#if ANY_UNIX
X# if UNIXV
Xstatic struct termio oldtermio; /* original tty mode */
Xstatic struct termio newtermio; /* cbreak/noecho tty mode */
X# else
Xstatic struct sgttyb oldsgttyb; /* original tty mode */
Xstatic struct sgttyb newsgttyb; /* cbreak/nl/noecho tty mode */
Xstatic int oldint; /* ^C or DEL, the "intr" character */
X# ifdef TIOCSLTC
Xstatic int oldswitch; /* ^Z, the "suspend" character */
Xstatic int oldquote; /* ^V, the "quote next char" char */
X# endif
X# endif
X#endif
X
X#if OSK
Xstatic struct sgbuf oldsgttyb; /* orginal tty mode */
Xstatic struct sgbuf newsgttyb; /* noecho tty mode */
X#endif
X
Xstatic char *capbuf; /* capability string buffer */
X
X
Xvoid initscr()
X{
X /* make sure TERM variable is set */
X#if MSDOS
X char *val;
X if (! (val = getenv("TERM"))
X || !strcmp(val, "pcbios"))
X#else
X if (!getenv("TERM"))
X#endif
X {
X#if ANY_UNIX
X write(2, "Environment variable TERM must be set\n", (unsigned)38);
X exit(1);
X#endif
X#if OSK
X writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
X exit(1);
X#endif
X#if MSDOS || TOS
X getsize(0);
X#endif
X }
X else
X {
X#if MSDOS
X *o_pcbios=0;
X#endif
X /* start termcap stuff */
X starttcap();
X }
X
X /* create stdscr and curscr */
X stdscr = kbuf;
X
X /* change the terminal mode to cbreak/noecho */
X#if ANY_UNIX
X# if UNIXV
X ioctl(2, TCGETA, &oldtermio);
X# else
X ioctl(2, TIOCGETP, &oldsgttyb);
X# endif
X#endif
X
X#if OSK
X _gs_opt(0, &oldsgttyb);
X#endif
X resume_curses(TRUE);
X}
X
X
Xvoid endwin()
X{
X /* change the terminal mode back the way it was */
X suspend_curses();
X}
X
X
Xstatic int curses_active = FALSE;
X
Xvoid suspend_curses()
X{
X#if ANY_UNIX && !UNIXV
X struct tchars tbuf;
X# ifdef TIOCSLTC
X struct ltchars ltbuf;
X# endif
X#endif
X#ifndef NO_CURSORSHAPE
X if (has_CQ)
X {
X do_CQ();
X }
X#endif
X if (has_TE) /* GB */
X {
X do_TE();
X }
X if (has_KE)
X {
X do_KE();
X }
X refresh();
X
X /* change the terminal mode back the way it was */
X#if ANY_UNIX
X# if UNIXV
X ioctl(2, TCSETAW, &oldtermio);
X# else
X ioctl(2, TIOCSETP, &oldsgttyb);
X
X ioctl(2, TIOCGETC, &tbuf);
X tbuf.t_intrc = oldint;
X ioctl(2, TIOCSETC, &tbuf);
X
X# ifdef TIOCSLTC
X ioctl(2, TIOCGLTC, <buf);
X ltbuf.t_suspc = oldswitch;
X ltbuf.t_lnextc = oldquote;
X ioctl(2, TIOCSLTC, <buf);
X# endif
X# endif
X#endif
X#if OSK
X _ss_opt(0, &oldsgttyb);
X#endif
X curses_active = FALSE;
X}
X
Xvoid resume_curses(quietly)
X int quietly;
X{
X if (!curses_active)
X {
X /* change the terminal mode to cbreak/noecho */
X#if ANY_UNIX
X# if UNIXV
X ospeed = (oldtermio.c_cflag & CBAUD);
X ERASEKEY = oldtermio.c_cc[VERASE];
X newtermio = oldtermio;
X newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
X newtermio.c_oflag &= ~OPOST;
X newtermio.c_lflag &= ISIG;
X newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
X newtermio.c_cc[VMIN] = 1;
X newtermio.c_cc[VTIME] = 0;
X# ifdef VSWTCH
X newtermio.c_cc[VSWTCH] = 0;
X# endif
X ioctl(2, TCSETAW, &newtermio);
X# else /* BSD or V7 or Coherent or Minix */
X struct tchars tbuf;
X# ifdef TIOCSLTC
X struct ltchars ltbuf;
X# endif
X
X ospeed = oldsgttyb.sg_ospeed;
X ERASEKEY = oldsgttyb.sg_erase;
X newsgttyb = oldsgttyb;
X newsgttyb.sg_flags |= CBREAK;
X newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
X ioctl(2, TIOCSETP, &newsgttyb);
X
X ioctl(2, TIOCGETC, &tbuf);
X oldint = tbuf.t_intrc;
X tbuf.t_intrc = ctrl('C'); /* always use ^C for interrupts */
X ioctl(2, TIOCSETC, &tbuf);
X
X# ifdef TIOCSLTC
X ioctl(2, TIOCGLTC, <buf);
X oldswitch = ltbuf.t_suspc;
X ltbuf.t_suspc = 0; /* disable ^Z for elvis */
X oldquote = ltbuf.t_lnextc;
X ltbuf.t_lnextc = 0; /* disable ^V for elvis */
X ioctl(2, TIOCSLTC, <buf);
X# endif
X
X# endif
X#endif
X#if OSK
X newsgttyb = oldsgttyb;
X newsgttyb.sg_echo = 0;
X newsgttyb.sg_eofch = 0;
X newsgttyb.sg_kbach = 0;
X newsgttyb.sg_kbich = ctrl('C');
X _ss_opt(0, &newsgttyb);
X ospeed = oldsgttyb.sg_baud;
X ERASEKEY = oldsgttyb.sg_bspch;
X#endif
X
X if (has_TI) /* GB */
X {
X do_TI();
X }
X if (has_KS)
X {
X do_KS();
X }
X
X curses_active = TRUE;
X }
X
X /* If we're supposed to quit quietly, then we're done */
X if (quietly)
X {
X return;
X }
X
X signal(SIGINT, SIG_IGN);
X
X move(LINES - 1, 0);
X do_SO();
X qaddstr("[Press <RETURN> to continue]");
X do_SE();
X refresh();
X ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */
X if (kbuf[0] == ':')
X {
X mode = MODE_COLON;
X addch('\n');
X refresh();
X }
X else
X {
X mode = MODE_VI;
X redraw(MARK_UNSET, FALSE);
X }
X exwrote = FALSE;
X
X#if TURBOC
X signal(SIGINT, (void(*)()) trapint);
X#else
X signal(SIGINT, trapint);
X#endif
X}
X
Xstatic void lacking(s)
X char *s;
X{
X write(2, "This termcap entry lacks the :", (unsigned)30);
X write(2, s, (unsigned)2);
X write(2, "=: capability\n", (unsigned)14);
X#if OSK
X write(2, "\l", 1);
X#endif
X exit(1);
X}
X
Xstatic void starttcap()
X{
X char *str;
X static char cbmem[800];
X#define MUSTHAVE(T,s) if (!(T = tgetstr(s, &capbuf))) lacking(s)
X#define MAYHAVE(T,s) if (str = tgetstr(s, &capbuf)) T = str
X#define PAIR(T,U,sT,sU) T=tgetstr(sT,&capbuf);U=tgetstr(sU,&capbuf);if (!T||!U)T=U=""
X
X /* allocate memory for capbuf */
X capbuf = cbmem;
X
X /* get the termcap entry */
X switch (tgetent(kbuf, getenv("TERM")))
X {
X case -1:
X write(2, "Can't read /etc/termcap\n", (unsigned)24);
X#if OSK
X write(2, "\l", 1);
X#endif
X exit(2);
X
X case 0:
X write(2, "Unrecognized TERM type\n", (unsigned)23);
X#if OSK
X write(2, "\l", 1);
X#endif
X exit(3);
X }
X
X /* get strings */
X MUSTHAVE(UP, "up");
X MAYHAVE(VB, "vb");
X MUSTHAVE(CM, "cm");
X PAIR(SO, SE, "so", "se");
X PAIR(TI, TE, "ti", "te");
X if (tgetnum("ug") <= 0)
X {
X PAIR(US, UE, "us", "ue");
X PAIR(MD, ME, "md", "me");
X
X /* get italics, or have it default to underline */
X PAIR(AS, AE, "as", "ae");
X if (!*AS)
X {
X AS = US;
X AE = UE;
X }
X }
X MAYHAVE(AL, "al");
X MAYHAVE(DL, "dl");
X MUSTHAVE(CE, "ce");
X MAYHAVE(CD, "cd");
X#if OSK
X MAYHAVE(SR_, "sr");
X#else
X MAYHAVE(SR, "sr");
X#endif
X PAIR(IM, EI, "im", "ei");
X MAYHAVE(IC, "ic");
X MAYHAVE(DC, "dc");
X
X /* other termcap stuff */
X AM = tgetflag("am");
X PT = tgetflag("pt");
X getsize(0);
X
X /* Key sequences */
X PAIR(KS, KE, "ks", "ke");
X MAYHAVE(KU, "ku"); /* up */
X MAYHAVE(KD, "kd"); /* down */
X MAYHAVE(KL, "kl"); /* left */
X MAYHAVE(KR, "kr"); /* right */
X MAYHAVE(PU, "kP"); /* PgUp */
X MAYHAVE(PD, "kN"); /* PgDn */
X MAYHAVE(HM, "kh"); /* Home */
X MAYHAVE(EN, "kH"); /* End */
X#ifndef CRUNCH
X if (!PU) MAYHAVE(PU, "K2"); /* "3x3 pad" names for PgUp, etc. */
X if (!PD) MAYHAVE(PD, "K5");
X if (!HM) MAYHAVE(HM, "K1");
X if (!EN) MAYHAVE(EN, "K4");
X
X MAYHAVE(PU, "PU"); /* old XENIX names for PgUp, etc. */
X MAYHAVE(PD, "PD"); /* (overrides others, if used.) */
X MAYHAVE(HM, "HM");
X MAYHAVE(EN, "EN");
X#endif
X
X#ifndef NO_CURSORSHAPE
X /* cursor shapes */
X CQ = tgetstr("cQ", &capbuf);
X if (has_CQ)
X {
X CX = tgetstr("cX", &capbuf);
X if (!CX) CX = CQ;
X CV = tgetstr("cV", &capbuf);
X if (!CV) CV = CQ;
X CI = tgetstr("cI", &capbuf);
X if (!CI) CI = CQ;
X CR = tgetstr("cR", &capbuf);
X if (!CR) CR = CQ;
X }
X# ifndef CRUNCH
X else
X {
X PAIR(CQ, CV, "ve", "vs");
X CX = CI = CR = CQ;
X }
X# endif /* !CRUNCH */
X#endif /* !NO_CURSORSHAPE */
X
X#undef MUSTHAVE
X#undef MAYHAVE
X#undef PAIR
X}
X
X
X/* This function gets the window size. It uses the TIOCGWINSZ ioctl call if
X * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't.
X * This function is called once during initialization, and thereafter it is
X * called whenever the SIGWINCH signal is sent to this process.
X */
Xint getsize(signo)
X int signo;
X{
X int lines;
X int cols;
X#ifdef TIOCGWINSZ
X struct winsize size;
X#endif
X
X#ifdef SIGWINCH
X /* reset the signal vector */
X signal(SIGWINCH, getsize);
X#endif
X
X /* get the window size, one way or another. */
X lines = cols = 0;
X#ifdef TIOCGWINSZ
X if (ioctl(2, TIOCGWINSZ, &size) >= 0)
X {
X lines = size.ws_row;
X cols = size.ws_col;
X }
X#endif
X if ((lines == 0 || cols == 0) && signo == 0)
X {
X LINES = CHECKBIOS(v_rows(), tgetnum("li"));
X COLS = CHECKBIOS(v_cols(), tgetnum("co"));
X }
X if (lines >= 2 && cols >= 30)
X {
X LINES = lines;
X COLS = cols;
X }
X
X /* Make sure we got values that we can live with */
X if (LINES < 2 || COLS < 30)
X {
X write(2, "Screen too small\n", (unsigned)17);
X#if OSK
X write(2, "\l", 1);
X#endif
X endwin();
X exit(2);
X }
X
X /* !!! copy the new values into Elvis' options */
X {
X extern char o_columns[], o_lines[];
X
X *o_columns = COLS;
X *o_lines = LINES;
X }
X
X return 0;
X}
X
X
X/* This is a function version of addch() -- it is used by tputs() */
Xint faddch(ch)
X int ch;
X{
X addch(ch);
X
X return 0;
X}
X
X/* These functions are equivelent to the macros of the same names... */
X
Xvoid qaddstr(str)
X char *str;
X{
X REG char *s_, *d_;
X
X#if MSDOS
X if (o_pcbios[0])
X {
X while (*str)
X qaddch(*str++);
X return;
X }
X#endif
X for (s_=(str), d_=stdscr; *d_++ = *s_++; )
X {
X }
X stdscr = d_ - 1;
X}
X
Xvoid attrset(a)
X int a;
X{
X do_aend();
X if (a == A_BOLD)
X {
X do_MD();
X aend = ME;
X }
X else if (a == A_UNDERLINE)
X {
X do_US();
X aend = UE;
X }
X else if (a == A_ALTCHARSET)
X {
X do_AS();
X aend = AE;
X }
X else
X {
X aend = "";
X }
X}
X
X
Xvoid insch(ch)
X int ch;
X{
X if (has_IM)
X do_IM();
X do_IC();
X qaddch(ch);
X if (has_EI)
X do_EI();
X}
X
X#if MSDOS
X
Xstatic int alarmtime;
X
X/* raw read - #defined to read (0, ...) on non-MSDOS.
X * With MSDOS, am maximum of 1 byte is read.
X * If more bytes should be read, just change the loop.
X * The following code uses the IBM-PC-System-Timer, so probably wont't work
X * on non-compatibles.
X */
X/*ARGSUSED*/
Xttyread(buf, len)
X char *buf;
X int len;
X{
X volatile char far *biostimer;
X char oldtime;
X int nticks = 0;
X int pos = 0;
X
X biostimer = (char far *)0x0040006cl;
X oldtime = *biostimer;
X
X while (!pos && (!alarmtime || nticks<alarmtime))
X { if (kbhit())
X if ((buf[pos++] = getch()) == 0) /* function key */
X buf[pos-1] = '#';
X if (oldtime != *biostimer)
X { nticks++;
X oldtime = *biostimer;
X }
X }
X return pos;
X}
X
Xalarm(time)
X int time;
X{
X alarmtime = 2 * time; /* ticks are 1/18 sec. */
X}
X
Xsleep(seconds)
X unsigned seconds;
X{
X volatile char far *biostimer = (char far *)0x0040006cl;
X char stop;
X
X stop = *biostimer + 18 * seconds;
X while (*biostimer != stop)
X {
X }
X}
X#endif
X
X#if TOS
X
Xstatic int alarmtime;
Xstatic long timer;
X
Xstatic gettime()
X{
X timer = *(long *)(0x4ba);
X}
X
X/*ARGSUSED*/
Xttyread(buf, len)
X char *buf;
X int len;
X{
X int pos=0;
X long l;
X long endtime;
X
X Supexec(gettime);
X endtime = timer+alarmtime;
X
X while (!pos && (!alarmtime || timer<endtime))
X {
X if (Bconstat(2))
X {
X l = Bconin(2);
X if ((buf[pos++]=l) == '\0')
X {
X buf[pos-1]='#';
X buf[pos++]=l>>16;
X }
X }
X Supexec(gettime);
X }
X return pos;
X}
X
Xalarm(time)
X int time;
X{
X alarmtime = 50 * time; /* ticks are 1/200 sec. */
X}
X
Xttywrite(buf, len)
X char *buf;
X int len;
X{
X while (len--)
X Bconout(2, *buf++);
X}
X#endif
X
X#if OSK
Xttyread(buf, len)
X char *buf;
X int len;
X{
X REG int i;
X if ((i = _gs_rdy(0)) > 0)
X return read(0, buf, i < len ? i : len);
X else
X return read(0, buf, 1);
X}
X
Xalarm(time)
X int time;
X{
X#define TIME(secs) ((secs << 8) | 0x80000000)
X static int alrmid;
X
X if (time)
X alrmid = alm_set(SIGQUIT, TIME(time));
X else
X alm_delete(alrmid);
X}
X#endif /* OSK */
eof
if test `wc -c <curses.c` -ne 14196
then
echo curses.c damaged!
fi
fi
exit 0
-------------------------------------------------------------------------------
Steve Kirkendall kirkenda@cs.pdx.edu Grad student at Portland State U.