Article View: comp.windows.news
Article #709NeWS version of elvis (the vi clone) part 3 of 9
From: Mike H.
Date: Fri, 11 Jan 1991 01:57
Date: Fri, 11 Jan 1991 01:57
4650 lines
95581 bytes
95581 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:
# alias.c
# atari.c
# blk.c
# cmd1.c
# cmd2.c
# ctags.c
# curses.c
# cut.c
# This archive created: Thu Jan 10 17:21:07 1991
export PATH; PATH=/bin:$PATH
if test -f 'alias.c'
then
echo shar: will not over-write existing file "'alias.c'"
else
cat << \SHAR_EOF > 'alias.c'
/* alias.c */
/* Author:
* Peter Reinig
* Universitaet Kaiserslautern
* Postfach 3049
* 7650 Kaiserslautern
* W. Germany
* reinig@physik.uni-kl.de
*/
/* This tiny program executes elvis with the flags that are appropriate
* for a given command name. This program is used only on systems that
* don't allow UNIX-style file links.
*
* The benefit of this program is: instead of having 5 copies of elvis
* on your disk, you only need one copy of elvis and 4 copies of this
* little program.
*/
#include <stdio.h>
#include "config.h"
#if OSK
#define ELVIS "/dd/usr/cmds/elvis"
#else
#define ELVIS "elvis"
#endif
extern char **environ;
extern int errno;
extern char *malloc();
main(argc, argv)
int argc;
char *argv[];
{
int pid, i, j;
int letter;
char **argblk;
#if OSK
extern int chainc();
#endif
/* allocate enough space for a copy of the argument list, plus a
* terminating NULL, plus maybe an added flag.
*/
argblk = (char **) malloc((argc + 2) * sizeof(char *));
if (!argblk)
{
#if OSK
_errmsg(errno, "Can't get enough memory\n");
#else
perror(argv[0]);
#endif
exit(2);
}
/* find the last letter in the invocation name of this program */
i = strlen(argv[0]);
#if MSDOS || TOS
/* we almost certainly must bypass ".EXE" or ".TTP" from argv[0] */
if (i > 4 && argv[0][i - 4] == '.')
i -= 4;
#endif
letter = argv[0][i - 1];
/* copy argv to argblk, possibly inserting a flag such as "-R" */
argblk[0] = ELVIS;
i = j = 1;
switch (letter)
{
case 'w': /* "view" */
case 'W':
argblk[i++] = "-R";
break;
#if !OSK
case 'x': /* "ex" */
case 'X':
argblk[i++] = "-e";
break;
#endif
case 't': /* "input" */
case 'T':
argblk[i++] = "-i";
break;
}
while (j < argc)
{
argblk[i++] = argv[j++];
}
argblk[i] = (char *)0;
/* execute the real ELVIS program */
#if OSK
pid = os9exec(chainc, argblk[0], argblk, environ, 0, 0, 3);
fprintf(stderr, "%s: cannot execute\n", argblk[0]);
#else
(void)execvp(argblk[0], argblk);
perror(ELVIS);
#endif
}
SHAR_EOF
fi # end of overwriting check
if test -f 'atari.c'
then
echo shar: will not over-write existing file "'atari.c'"
else
cat << \SHAR_EOF > 'atari.c'
/* atari.c */
/* Author:
* Guntram Blohm
* Buchenstrasse 19
* 7904 Erbach, West Germany
* Tel. ++49-7305-6997
* sorry - no regular network connection
*/
/*
* This file contains the 'standard' functions which are not supported
* by Atari/Mark Williams, and some other TOS-only requirements.
*/
#include "config.h"
#include "vi.h"
#if TOS
#include <osbind.h>
/* vi uses mode==0 only ... */
int access(file, mode)
char *file;
{
int fd=Fopen(file, 0);
if (fd<0)
return -1;
Fclose(fd);
return 0;
}
char *mktemp(template)
char *template;
{
return template;
}
/* read -- text mode, compress \r\n to \n
* warning: might fail when maxlen==1 and at eol
*/
int tread(fd, buf, maxlen)
int fd;
char *buf;
int maxlen;
{
int i, j, nread=read(fd, buf, (unsigned)maxlen);
if (nread && buf[nread-1]=='\r')
{ nread--;
lseek(fd, -1l, 1);
}
for (i=j=0; j<nread; i++,j++)
{ if (buf[j]=='\r' && buf[j+1]=='\n')
j++;
buf[i]=buf[j];
}
return i;
}
int twrite(fd, buf, maxlen)
int fd;
char *buf;
int maxlen;
{
int i, j, nwritten=0, hadnl=0;
char writbuf[BLKSIZE];
for (i=j=0; j<maxlen; )
{
if ((writbuf[i++]=buf[j++])=='\n')
{ writbuf[i-1]='\r';
if (i<BLKSIZE)
writbuf[i++]='\n';
else
hadnl=1;
}
if (i==BLKSIZE)
{
write(fd, writbuf, (unsigned)i);
i=0;
}
if (hadnl)
{
writbuf[i++]='\n';
hadnl=0;
}
}
if (i)
write(fd, writbuf, (unsigned)i);
return j;
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'blk.c'
then
echo shar: will not over-write existing file "'blk.c'"
else
cat << \SHAR_EOF > 'blk.c'
/* blk.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the functions that get/put blocks from the temp file.
* It also contains the "do" and "undo" functions.
*/
#include "config.h"
#include "vi.h"
#ifndef NBUFS
# define NBUFS 5 /* must be at least 3 -- more is better */
#endif
extern long lseek();
/*------------------------------------------------------------------------*/
BLK hdr; /* buffer for the header block */
static int b4cnt; /* used to count context of beforedo/afterdo */
static struct _blkbuf
{
BLK buf; /* contents of a text block */
unsigned short logical; /* logical block number */
int dirty; /* must the buffer be rewritten? */
}
blk[NBUFS], /* buffers for text[?] blocks */
*toonew, /* buffer which shouldn't be recycled yet */
*newtoo, /* another buffer which should be recycled */
*recycle = blk; /* next block to be recycled */
/* This function wipes out all buffers */
void blkinit()
{
int i;
for (i = 0; i < NBUFS; i++)
{
blk[i].logical = 0;
blk[i].dirty = FALSE;
}
for (i = 0; i < MAXBLKS; i++)
{
hdr.n[i] = 0;
}
}
/* This function allocates a buffer and fills it with a given block's text */
BLK *blkget(logical)
int logical; /* logical block number to fetch */
{
REG struct _blkbuf *this; /* used to step through blk[] */
REG int i;
/* if logical is 0, just return the hdr buffer */
if (logical == 0)
{
return &hdr;
}
/* see if we have that block in mem already */
for (this = blk; this < &blk[NBUFS]; this++)
{
if (this->logical == logical)
{
newtoo = toonew;
toonew = this;
return &this->buf;
}
}
/* choose a block to be recycled */
do
{
this = recycle++;
if (recycle == &blk[NBUFS])
{
recycle = blk;
}
} while (this == toonew || this == newtoo);
/* if it contains a block, flush that block */
blkflush(this);
/* fill this buffer with the desired block */
this->logical = logical;
if (hdr.n[logical])
{
/* it has been used before - fill it from tmp file */
lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0);
if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
{
msg("Error reading back from tmp file!");
}
}
else
{
/* it is new - zero it */
for (i = 0; i < BLKSIZE; i++)
{
this->buf.c[i] = 0;
}
}
/* This isn't really a change, but it does potentially invalidate
* the kinds of shortcuts that the "changes" variable is supposed
* to protect us from... so count it as a change.
*/
changes++;
/* mark it as being "not dirty" */
this->dirty = 0;
/* return it */
newtoo = toonew;
toonew = this;
return &this->buf;
}
/* This function writes a block out to the temporary file */
void blkflush(this)
REG struct _blkbuf *this; /* the buffer to flush */
{
long seekpos; /* seek position of the new block */
unsigned short physical; /* physical block number */
/* if its empty (an orphan blkadd() maybe?) then make it dirty */
if (this->logical && !*this->buf.c)
{
blkdirty(&this->buf);
}
/* if it's an empty buffer or a clean version is on disk, quit */
if (!this->logical || hdr.n[this->logical] && !this->dirty)
{
return;
}
/* find a free place in the file */
#ifndef NO_RECYCLE
seekpos = allocate();
lseek(tmpfd, seekpos, 0);
#else
seekpos = lseek(tmpfd, 0L, 2);
#endif
physical = seekpos / BLKSIZE;
/* put the block there */
if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE)
{
msg("Trouble writing to tmp file");
}
this->dirty = FALSE;
/* update the header so it knows we put it there */
hdr.n[this->logical] = physical;
}
/* This function sets a block's "dirty" flag or deletes empty blocks */
void blkdirty(bp)
BLK *bp; /* buffer returned by blkget() */
{
REG int i, j;
REG char *scan;
REG int k;
/* find the buffer */
for (i = 0; i < NBUFS && bp != &blk[i].buf; i++)
{
}
#ifdef DEBUG
if (i >= NBUFS)
{
msg("blkdirty() called with unknown buffer at 0x%lx", bp);
return;
}
if (blk[i].logical == 0)
{
msg("blkdirty called with freed buffer");
return;
}
#endif
/* if this block ends with line# INFINITY, then it must have been
* allocated unnecessarily during tmpstart(). Forget it.
*/
if (lnum[blk[i].logical] == INFINITY)
{
#ifdef DEBUG
if (blk[i].buf.c[0])
{
msg("bkldirty called with non-empty extra BLK");
}
#endif
blk[i].logical = 0;
blk[i].dirty = FALSE;
return;
}
/* count lines in this block */
for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++)
{
if (*scan == '\n')
{
j++;
}
}
/* adjust lnum, if necessary */
k = blk[i].logical;
j += (lnum[k - 1] - lnum[k]);
if (j != 0)
{
nlines += j;
while (k < MAXBLKS && lnum[k] != INFINITY)
{
lnum[k++] += j;
}
}
/* if it still has text, mark it as dirty */
if (*bp->c)
{
blk[i].dirty = TRUE;
}
else /* empty block, so delete it */
{
/* adjust the cache */
k = blk[i].logical;
for (j = 0; j < NBUFS; j++)
{
if (blk[j].logical >= k)
{
blk[j].logical--;
}
}
/* delete it from hdr.n[] and lnum[] */
blk[i].logical = 0;
blk[i].dirty = FALSE;
while (k < MAXBLKS - 1)
{
hdr.n[k] = hdr.n[k + 1];
lnum[k] = lnum[k + 1];
k++;
}
hdr.n[MAXBLKS - 1] = 0;
lnum[MAXBLKS - 1] = INFINITY;
}
}
/* insert a new block into hdr, and adjust the cache */
BLK *blkadd(logical)
int logical; /* where to insert the new block */
{
REG int i;
/* adjust hdr and lnum[] */
for (i = MAXBLKS - 1; i > logical; i--)
{
hdr.n[i] = hdr.n[i - 1];
lnum[i] = lnum[i - 1];
}
hdr.n[logical] = 0;
lnum[logical] = lnum[logical - 1];
/* adjust the cache */
for (i = 0; i < NBUFS; i++)
{
if (blk[i].logical >= logical)
{
blk[i].logical++;
}
}
/* return the new block, via blkget() */
return blkget(logical);
}
/* This function forces all dirty blocks out to disk */
void blksync()
{
int i;
for (i = 0; i < NBUFS; i++)
{
/* blk[i].dirty = TRUE; */
blkflush(&blk[i]);
}
if (*o_sync)
{
sync();
}
}
/*------------------------------------------------------------------------*/
static MARK undocurs; /* where the cursor should go if undone */
static long oldnlines;
static long oldlnum[MAXBLKS];
/* This function should be called before each command that changes the text.
* It defines the state that undo() will reset the file to.
*/
void beforedo(forundo)
int forundo; /* boolean: is this for an undo? */
{
REG int i;
REG long l;
/* if this is a nested call to beforedo, quit! Use larger context */
if (b4cnt++ > 0)
{
return;
}
/* force all block buffers to disk */
blksync();
#ifndef NO_RECYCLE
/* perform garbage collection on blocks from tmp file */
garbage();
#endif
/* force the header out to disk */
lseek(tmpfd, 0L, 0);
if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE)
{
msg("Trouble writing header to tmp file ");
}
/* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */
if (forundo)
{
for (i = 0; i < MAXBLKS; i++)
{
l = lnum[i];
lnum[i] = oldlnum[i];
oldlnum[i] = l;
}
l = nlines;
nlines = oldnlines;
oldnlines = l;
}
else
{
for (i = 0; i < MAXBLKS; i++)
{
oldlnum[i] = lnum[i];
}
oldnlines = nlines;
}
/* save the cursor position */
undocurs = cursor;
/* upon return, the calling function continues and makes changes... */
}
/* This function marks the end of a (nested?) change to the file */
void afterdo()
{
if (--b4cnt)
{
/* after abortdo(), b4cnt may decribe nested beforedo/afterdo
* pairs incorrectly. If it is decremented to often, then
* keep b4cnt sane but don't do anything else.
*/
if (b4cnt < 0)
b4cnt = 0;
return;
}
/* make sure the cursor wasn't left stranded in deleted text */
if (markline(cursor) > nlines)
{
cursor = MARK_LAST;
}
/* NOTE: it is still possible that markidx(cursor) is after the
* end of a line, so the Vi mode will have to take care of that
* itself */
/* if a significant change has been made to this file, then set the
* MODIFIED flag.
*/
if (significant)
{
setflag(file, MODIFIED);
}
}
/* This function cuts short the current set of changes. It is called after
* a SIGINT.
*/
void abortdo()
{
/* finish the operation immediately. */
if (b4cnt > 0)
{
b4cnt = 1;
afterdo();
}
/* in visual mode, the screen is probably screwed up */
if (mode == MODE_COLON)
{
mode = MODE_VI;
}
if (mode == MODE_VI)
{
redraw(MARK_UNSET, FALSE);
}
}
/* This function discards all changes made since the last call to beforedo() */
int undo()
{
BLK oldhdr;
/* if beforedo() has never been run, fail */
if (!tstflag(file, MODIFIED))
{
msg("You haven't modified this file yet.");
return FALSE;
}
/* read the old header form the tmp file */
lseek(tmpfd, 0L, 0);
if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE)
{
msg("Trouble rereading the old header from tmp file");
}
/* "do" the changed version, so we can undo the "undo" */
cursor = undocurs;
beforedo(TRUE);
afterdo();
/* wipe out the block buffers - we can't assume they're correct */
blkinit();
/* use the old header -- and therefore the old text blocks */
hdr = oldhdr;
/* This is a change */
changes++;
return TRUE;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'cmd1.c'
then
echo shar: will not over-write existing file "'cmd1.c'"
else
cat << \SHAR_EOF > 'cmd1.c'
/* cmd1.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains some of the EX commands - mostly ones that deal with
* files, options, etc. -- anything except text.
*/
#include "config.h"
#include <ctype.h>
#include "vi.h"
#include "regexp.h"
#if MSDOS
#define DATE __DATE__
#endif
#ifdef DEBUG
/* print the selected lines with info on the blocks */
/*ARGSUSED*/
void cmd_debug(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
REG char *scan;
REG long l;
REG int i;
int len;
/* scan lnum[] to determine which block its in */
l = markline(frommark);
for (i = 1; l > lnum[i]; i++)
{
}
do
{
/* fetch text of the block containing that line */
scan = blkget(i)->c;
/* calculate its length */
if (scan[BLKSIZE - 1])
{
len = BLKSIZE;
}
else
{
len = strlen(scan);
}
/* print block stats */
msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
msg("##### len=%d, buf=0x%lx, %sdirty",
len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
if (bang)
{
while (--len >= 0)
{
addch(*scan);
scan++;
}
}
exrefresh();
/* next block */
i++;
} while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
}
/* This function checks a lot of conditions to make sure they aren't screwy */
/*ARGSUSED*/
void cmd_validate(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
char *scan;
int i;
int nlcnt; /* used to count newlines */
int len; /* counts non-NUL characters */
/* check lnum[0] */
if (lnum[0] != 0L)
{
msg("lnum[0] = %ld", lnum[0]);
}
/* check each block */
for (i = 1; lnum[i] <= nlines; i++)
{
scan = blkget(i)->c;
if (scan[BLKSIZE - 1])
{
msg("block %d has no NUL at the end", i);
}
else
{
for (nlcnt = len = 0; *scan; scan++, len++)
{
if (*scan == '\n')
{
nlcnt++;
}
}
if (scan[-1] != '\n')
{
msg("block %d doesn't end with '\\n' (length %d)", i, len);
}
if (bang || nlcnt != lnum[i] - lnum[i - 1])
{
msg("block %d (line %ld?) has %d lines, but should have %ld",
i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
}
}
exrefresh();
}
/* check lnum again */
if (lnum[i] != INFINITY)
{
msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
i, hdr.n[i], i, lnum[i]);
}
msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
}
#endif /* DEBUG */
/*ARGSUSED*/
void cmd_mark(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
/* validate the name of the mark */
if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
{
msg("Invalid mark name");
return;
}
mark[*extra - 'a'] = tomark;
}
/*ARGSUSED*/
void cmd_write(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
int fd;
int append; /* boolean: write in "append" mode? */
REG long l;
REG char *scan;
REG int i;
/* if all lines are to be written, use tmpsave() */
if (frommark == MARK_FIRST && tomark == MARK_LAST)
{
tmpsave(extra, bang);
return;
}
/* see if we're going to do this in append mode or not */
append = FALSE;
if (extra[0] == '>' && extra[1] == '>')
{
extra += 2;
append = TRUE;
}
/* either the file must not exist, or we must have a ! or be appending */
if (access(extra, 0) == 0 && !bang && !append)
{
msg("File already exists - Use :w! to overwrite");
return;
}
/* else do it line-by-line, like cmd_print() */
if (append)
{
#ifdef O_APPEND
fd = open(extra, O_WRONLY|O_APPEND);
#else
fd = open(extra, O_WRONLY);
if (fd >= 0)
{
lseek(fd, 0L, 2);
}
#endif
}
else
{
fd = -1; /* so we know the file isn't open yet */
}
if (fd < 0)
{
fd = creat(extra, FILEPERMS);
if (fd < 0)
{
msg("Can't write to \"%s\"", extra);
return;
}
}
for (l = markline(frommark); l <= markline(tomark); l++)
{
/* get the next line */
scan = fetchline(l);
i = strlen(scan);
scan[i++] = '\n';
/* print the line */
twrite(fd, scan, i);
}
close(fd);
}
/*ARGSUSED*/
void cmd_shell(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
static char prevextra[80];
/* special case: ":sh" means ":!sh" */
if (cmd == CMD_SHELL)
{
extra = o_shell;
frommark = tomark = 0L;
}
/* if extra is "!", substute previous command */
if (*extra == '!')
{
if (!*prevextra)
{
msg("No previous shell command to substitute for '!'");
return;
}
extra = prevextra;
}
else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
{
strcpy(prevextra, extra);
}
/* if no lines were specified, just run the command */
Suspend_curses();
if (frommark == 0L)
{
system(extra);
}
else /* pipe lines from the file through the command */
{
filter(frommark, tomark, extra);
}
/* resume curses quietly for MODE_EX, but noisily otherwise */
Resume_curses(mode == MODE_EX);
}
/*ARGSUSED*/
void cmd_global(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra; /* rest of the command line */
{
char *cmdptr; /* the command from the command line */
char cmdln[100]; /* copy of the command from the command line */
char *line; /* a line from the file */
long l; /* used as a counter to move through lines */
long lqty; /* quantity of lines to be scanned */
long nchanged; /* number of lines changed */
regexp *re; /* the compiled search expression */
/* can't nest global commands */
if (doingglobal)
{
msg("Can't nest global commands.");
rptlines = -1L;
return;
}
/* ":g! ..." is the same as ":v ..." */
if (bang)
{
cmd = CMD_VGLOBAL;
}
/* make sure we got a search pattern */
if (*extra != '/' && *extra != '?')
{
msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
return;
}
/* parse & compile the search pattern */
cmdptr = parseptrn(extra);
if (!extra[1])
{
msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
return;
}
re = regcomp(extra + 1);
if (!re)
{
/* regcomp found & described an error */
return;
}
/* for each line in the range */
doingglobal = TRUE;
ChangeText
{
/* NOTE: we have to go through the lines in a forward order,
* otherwise "g/re/p" would look funny. *BUT* for "g/re/d"
* to work, simply adding 1 to the line# on each loop won't
* work. The solution: count lines relative to the end of
* the file. Think about it.
*/
for (l = nlines - markline(frommark),
lqty = markline(tomark) - markline(frommark) + 1L,
nchanged = 0L;
lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
l--, lqty--)
{
/* fetch the line */
line = fetchline(nlines - l);
/* if it contains the search pattern... */
if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
{
/* move the cursor to that line */
cursor = MARK_AT_LINE(nlines - l);
/* do the ex command (without mucking up
* the original copy of the command line)
*/
strcpy(cmdln, cmdptr);
rptlines = 0L;
doexcmd(cmdln);
nchanged += rptlines;
}
}
}
doingglobal = FALSE;
/* free the regexp */
free(re);
/* Reporting...*/
rptlines = nchanged;
}
/*ARGSUSED*/
void cmd_file(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
#ifndef CRUNCH
/* if we're given a new filename, use it as this file's name */
if (extra && *extra)
{
strcpy(origname, extra);
}
#endif
if (cmd == CMD_FILE)
{
msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]",
*origname ? origname : "[NO FILE]",
tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
tstflag(file, READONLY) ? "[READONLY]" : "",
nlines,
markline(frommark),
markline(frommark) * 100 / nlines);
}
else if (markline(frommark) == markline(tomark))
{
msg("%ld", markline(frommark));
}
else
{
msg("range \"%ld,%ld\" contains %ld lines",
markline(frommark),
markline(tomark),
markline(tomark) - markline(frommark) + 1L);
}
}
/*ARGSUSED*/
void cmd_edit(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
long line = 1L; /* might be set to prevline */
/* Editing previous file? Then start at previous line */
if (!strcmp(extra, prevorig))
{
line = prevline;
}
#ifndef CRUNCH
/* if we were given an explicit starting line, then start there */
if (*extra == '+')
{
for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
{
line *= 10L;
line += (*extra - '0');
}
while (isascii(*extra) && isspace(*extra))
{
extra++;
}
}
#endif /* not CRUNCH */
/* switch files */
if (tmpabort(bang))
{
Tmpstart(extra);
if (line <= nlines && line >= 1L)
{
cursor = MARK_AT_LINE(line);
}
}
else
{
msg("Use edit! to abort changes, or w to save changes");
/* so we can say ":e!#" next time... */
strcpy(prevorig, extra);
prevline = 1L;
}
}
/* This code is also used for rewind -- GB */
/*ARGSUSED*/
void cmd_next(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
int i, j;
char *scan;
char *build;
/* if extra stuff given, use ":args" to define a new args list */
if (cmd == CMD_NEXT && extra && *extra)
{
cmd_args(frommark, tomark, cmd, bang, extra);
}
/* move to the next arg */
if (cmd == CMD_NEXT)
{
i = argno + 1;
}
else if (cmd == CMD_PREVIOUS)
{
i = argno - 1;
}
else /* cmd == CMD_REWIND */
{
i = 0;
}
if (i < 0 || i >= nargs)
{
msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
return;
}
/* find & isolate the name of the file to edit */
for (j = i, scan = args; j > 0; j--)
{
while(!isascii(*scan) || !isspace(*scan))
{
scan++;
}
while (isascii(*scan) && isspace(*scan))
{
scan++;
}
}
for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
{
*build++ = *scan++;
}
*build = '\0';
/* switch to the next file */
if (tmpabort(bang))
{
Tmpstart(tmpblk.c);
argno = i;
}
else
{
msg("Use :%s! to abort changes, or w to save changes",
cmd == CMD_NEXT ? "next" :
cmd == CMD_PREVIOUS ? "previous" :
"rewind");
}
}
/* also called from :wq -- always writes back in this case */
/*ARGSUSED*/
void cmd_xit(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
static long whenwarned; /* when the user was last warned of extra files */
int oldflag;
/* if there are more files to edit, then warn user */
if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
{
msg("More files to edit -- Use \":n\" to go to next file");
whenwarned = changes;
return;
}
if (cmd == CMD_QUIT)
{
if (tmpabort(bang))
{
mode = MODE_QUIT;
}
else
{
msg("Use q! to abort changes, or wq to save changes");
}
}
else
{
/* else try to save this file */
oldflag = tstflag(file, MODIFIED);
if (cmd == CMD_WQUIT)
setflag(file, MODIFIED);
if (tmpend(bang))
{
mode = MODE_QUIT;
}
else
{
msg("Could not save file -- use quit! to abort changes, or w filename");
}
if (!oldflag)
clrflag(file, MODIFIED);
}
}
/*ARGSUSED*/
void cmd_args(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
char *scan;
char *eow;
int col;
int arg;
int addcols;
int scrolled = 0;
/* if no extra names given, or just current name, then report the args
* we have now.
*/
if (!extra || !*extra)
{
for (scan = args, col=arg=0; *scan; )
{
while (*scan && isascii(*scan) && isspace(*scan))
scan++;
eow = scan;
while (*eow && (!isascii(*++eow) || !isspace(*eow)))
;
if (arg == argno)
addcols = 2;
else
addcols = 0;
if (col+addcols+(int)(eow-scan)+1>=COLS)
{
addch('\n');
scrolled=1;
col=0;
}
else if (arg)
{ qaddch(' ');
col++;
}
if (arg == argno)
qaddch('[');
while (scan < eow)
{ qaddch(*scan++);
col++;
}
if (arg == argno)
qaddch(']');
arg++;
col+=addcols;
}
/* write a trailing newline */
if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
addch('\n');
exrefresh();
}
else /* new args list given */
{
strcpy(args, extra);
argno = -1; /* before the first, so :next will go to first */
/* count the names */
for (nargs = 0, scan = args; *scan; nargs++)
{
while (*scan && (!isascii(*scan) || !isspace(*scan)))
{
scan++;
}
while (isascii(*scan) && isspace(*scan))
{
scan++;
}
}
msg("%d files to edit", nargs);
}
}
/*ARGSUSED*/
void cmd_cd(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
char *getenv();
/* default directory name is $HOME */
if (!*extra)
{
extra = getenv("HOME");
if (!extra)
{
msg("environment variable $HOME not set");
return;
}
}
/* go to the directory */
if (chdir(extra) < 0)
{
perror(extra);
}
}
/*ARGSUSED*/
void cmd_map(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
char *mapto;
/* "map" with no extra will dump the map table contents */
if (!*extra)
{
dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
}
else
{
/* "extra" is key to map, followed my what it maps to */
for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
{
}
while (*mapto == ' ' || *mapto == '\t')
{
*mapto++ = '\0';
}
mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
}
}
/*ARGSUSED*/
void cmd_set(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
if (!*extra)
{
dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
}
else if (!strcmp(extra, "all"))
{
dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */
}
else
{
setopts(extra);
/* That option may have affected the appearence of text */
changes++;
}
}
/*ARGSUSED*/
void cmd_tag(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
char *scan; /* used to scan through the tmpblk.c */
char *cmp; /* char of tag name we're comparing, or NULL */
char *end; /* marks the end of chars in tmpblk.c */
int fd; /* file descriptor used to read the file */
#ifndef NO_MAGIC
char wasmagic; /* preserves the original state of o_magic */
#endif
static char prevtag[30];
/* if no tag is given, use the previous tag */
if (!extra || !*extra)
{
if (!*prevtag)
{
msg("No previous tag");
return;
}
extra = prevtag;
}
else
{
strncpy(prevtag, extra, sizeof prevtag);
}
/* open the tags file */
fd = open(TAGS, O_RDONLY);
if (fd < 0)
{
msg("No tags file");
return;
}
/* Hmmm... this would have been a lot easier with <stdio.h> */
/* find the line with our tag in it */
for(scan = end = tmpblk.c, cmp = extra; ; scan++)
{
/* read a block, if necessary */
if (scan >= end)
{
end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
scan = tmpblk.c;
if (scan >= end)
{
msg("tag \"%s\" not found", extra);
close(fd);
return;
}
}
/* if we're comparing, compare... */
if (cmp)
{
/* matched??? wow! */
if (!*cmp && *scan == '\t')
{
break;
}
if (*cmp++ != *scan)
{
/* failed! skip to newline */
cmp = (char *)0;
}
}
/* if we're skipping to newline, do it fast! */
if (!cmp)
{
while (scan < end && *scan != '\n')
{
scan++;
}
if (scan < end)
{
cmp = extra;
}
}
}
/* found it! get the rest of the line into memory */
for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
{
*cmp++ = *scan++;
}
if (scan == end)
{
tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
}
/* we can close the tags file now */
close(fd);
/* extract the filename from the line, and edit the file */
for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
{
}
*cmp++ = '\0';
if (strcmp(origname, tmpblk.c) != 0)
{
if (!tmpabort(bang))
{
msg("Use :tag! to abort changes, or :w to save changes");
return;
}
Tmpstart(tmpblk.c);
}
/* move to the desired line (or to line 1 if that fails) */
#ifndef NO_MAGIC
wasmagic = *o_magic;
*o_magic = FALSE;
#endif
cursor = MARK_FIRST;
linespec(cmp, &cursor);
if (cursor == MARK_UNSET)
{
cursor = MARK_FIRST;
}
#ifndef NO_MAGIC
*o_magic = wasmagic;
#endif
}
/*ARGSUSED*/
void cmd_visual(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
mode = MODE_VI;
msg("");
}
/* describe this version of the program */
/*ARGSUSED*/
void cmd_version(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
#ifndef DATE
msg("%s", VERSION);
#else
msg("%s (%s)", VERSION, DATE);
#endif
#ifdef COMPILED_BY
msg("Compiled by %s", COMPILED_BY);
#endif
#ifdef CREDIT
msg("%s", CREDIT);
#endif
#ifdef COPYING
msg("%s", COPYING);
#endif
}
#ifndef NO_MKEXRC
/* make a .exrc file which describes the current configuration */
/*ARGSUSED*/
void cmd_mkexrc(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
int fd;
/* the default name for the .exrc file EXRC */
if (!*extra)
{
extra = EXRC;
}
/* create the .exrc file */
fd = creat(extra, FILEPERMS);
if (fd < 0)
{
msg("Couldn't create a new \"%s\" file", extra);
return;
}
/* save stuff */
savekeys(fd);
saveopts(fd);
#ifndef NO_DIGRAPH
savedigs(fd);
#endif
#ifndef NO_ABBR
saveabbr(fd);
#endif
/* close the file */
close(fd);
msg("Created a new \"%s\" file", extra);
}
#endif
#ifndef NO_DIGRAPH
/*ARGSUSED*/
void cmd_digraph(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
do_digraph(bang, extra);
}
#endif
#ifndef NO_ERRLIST
static char errfile[256]; /* the name of a file containing an error */
static long errline; /* the line number for an error */
/* This static function tries to parse an error message.
*
* For most compilers, the first word is taken to be the name of the erroneous
* file, and the first number after that is taken to be the line number where
* the error was detected. The description of the error follows, possibly
* preceded by an "error ... :" or "warning ... :" label which is skipped.
*
* For Coherent, error messages look like "line#: filename: message".
*
* For non-error lines, or unparsable error lines, this function returns NULL.
* Normally, though, it alters errfile and errline, and returns a pointer to
* the description.
*/
static char *parse_errmsg(text)
REG char *text;
{
REG char *cpy;
long atol();
# if COHERENT || TOS /* any Mark Williams compiler */
/* Get the line number. If no line number, then ignore this line. */
errline = atol(text);
if (errline == 0L)
return (char *)0;
/* Skip to the start of the filename */
while (*text && *text++ != ':')
{
}
if (!*text++)
return (char *)0;
/* copy the filename to errfile */
for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
{
}
if (!*text++)
return (char *)0;
cpy[-1] = '\0';
return text;
# else /* not a Mark Williams compiler */
char *errmsg;
/* the error message is the whole line, by default */
errmsg = text;
/* skip leading garbage */
while (*text && !(isascii(*text) && isalnum(*text)))
{
text++;
}
/* copy over the filename */
cpy = errfile;
while(isascii(*text) && isalnum(*text) || *text == '.')
{
*cpy++ = *text++;
}
*cpy = '\0';
/* ignore the name "Error" and filenames that contain a '/' */
if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
{
return (char *)0;
}
/* skip garbage between filename and line number */
while (*text && !(isascii(*text) && isdigit(*text)))
{
text++;
}
/* if the number is part of a larger word, then ignore this line */
if (*text && isascii(text[-1]) && isalpha(text[-1]))
{
return (char *)0;
}
/* get the error line */
errline = 0L;
while (isascii(*text) && isdigit(*text))
{
errline *= 10;
errline += (*text - '0');
text++;
}
/* any line which lacks a filename or line number should be ignored */
if (!errfile[0] || !errline)
{
return (char *)0;
}
/* locate the beginning of the error description */
while (*text && isascii(*text) && !isspace(*text))
{
text++;
}
while (*text)
{
# ifndef CRUNCH
/* skip "error #:" and "warning #:" clauses */
if (!strncmp(text + 1, "rror ", 5)
|| !strncmp(text + 1, "arning ", 7)
|| !strncmp(text + 1, "atal error", 10))
{
do
{
text++;
} while (*text && *text != ':');
continue;
}
# endif
/* anything other than whitespace or a colon is important */
if (!isascii(*text) || (!isspace(*text) && *text != ':'))
{
errmsg = text;
break;
}
/* else keep looking... */
text++;
}
return errmsg;
# endif /* not COHERENT */
}
/*ARGSUSED*/
void cmd_errlist(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
static long endline;/* original number of lines in this file */
static long offset; /* offset of the next line in the errlist file */
static int fd = -2;/* fd of the errlist file */
int i;
char *errmsg;
/* if a new errlist file is named, open it */
if (extra && extra[0])
{
/* close the old one */
if (fd >= 0)
{
close(fd);
}
fd = open(extra, O_RDONLY);
offset = 0L;
}
else if (fd < 0)
{
fd = open(ERRLIST, O_RDONLY);
offset = 0L;
}
/* do we have an errlist file now? */
if (fd < 0)
{
msg("There is no errlist file");
beep();
return;
}
/* find the next error message in the file */
do
{
/* read the next line from the errlist */
lseek(fd, offset, 0);
if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
{
msg("No more errors");
beep();
close(fd);
return;
}
for (i = 0; tmpblk.c[i] != '\n'; i++)
{
}
tmpblk.c[i++] = 0;
/* look for an error message in the line */
errmsg = parse_errmsg(tmpblk.c);
if (!errmsg)
{
offset += i;
}
} while (!errmsg);
/* switch to the file containing the error, if this isn't it */
if (strcmp(origname, errfile))
{
if (!tmpabort(bang))
{
msg("Use :er! to abort changes, or :w to save changes");
beep();
return;
}
Tmpstart(errfile);
endline = nlines;
}
else if (endline == 0L)
{
endline = nlines;
}
/* go to the line where the error was detected */
cursor = MARK_AT_LINE(errline + (nlines - endline));
if (cursor > MARK_LAST)
{
cursor = MARK_LAST;
}
if (mode == MODE_VI)
{
redraw(cursor, FALSE);
}
/* display the error message */
if (nlines > endline)
{
msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
}
else if (nlines < endline)
{
msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
}
else
{
msg("line %ld: %.65s", errline, errmsg);
}
/* remember where the NEXT error line will start */
offset += i;
}
/*ARGSUSED*/
void cmd_make(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
BLK buf;
/* if the file hasn't been saved, then complain unless ! */
if (tstflag(file, MODIFIED) && !bang)
{
msg("\"%s\" not saved yet", origname);
return;
}
/* build the command */
sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
qaddstr(buf.c);
addch('\n');
/* run the command, with curses temporarily disabled */
Suspend_curses();
system(buf.c);
Resume_curses(mode == MODE_EX);
if (mode == MODE_COLON)
mode = MODE_VI;
/* run the "errlist" command */
cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
}
#endif
#ifndef NO_ABBR
/*ARGSUSED*/
void cmd_abbr(frommark, tomark, cmd, bang, extra)
MARK frommark, tomark;
CMD cmd;
int bang;
char *extra;
{
do_abbr(extra);
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'cmd2.c'
then
echo shar: will not over-write existing file "'cmd2.c'"
else
cat << \SHAR_EOF > 'cmd2.c'
/* cmd2.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains some of the commands - mostly ones that change text */
#include <ctype.h>
#include "config.h"
#include "vi.h"
#include "regexp.h"
#if TOS
# include <stat.h>
#else
# if OSK
# include "osk.h"
# else
# include <sys/stat.h>
# endif
#endif
/*ARGSUSED*/
void cmd_substitute(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra; /* rest of the command line */
{
char *line; /* a line from the file */
regexp *re; /* the compiled search expression */
char *subst; /* the substitution string */
char *opt; /* substitution options */
long l; /* a line number */
char *s, *d; /* used during subtitutions */
char *conf; /* used during confirmation */
long chline; /* # of lines changed */
long chsub; /* # of substitutions made */
static optp; /* boolean option: print when done? */
static optg; /* boolean option: substitute globally in line? */
static optc; /* boolean option: confirm before subst? */
/* for now, assume this will fail */
rptlines = -1L;
if (cmd == CMD_SUBAGAIN)
{
#ifndef NO_MAGIC
if (*o_magic)
subst = "~";
else
#endif
subst = "\\~";
re = regcomp("");
/* if visual "&", then turn off the "p" and "c" options */
if (bang)
{
optp = optc = FALSE;
}
}
else
{
/* make sure we got a search pattern */
if (*extra != '/' && *extra != '?')
{
msg("Usage: s/regular expression/new text/");
return;
}
/* parse & compile the search pattern */
subst = parseptrn(extra);
re = regcomp(extra + 1);
}
/* abort if RE error -- error message already given by regcomp() */
if (!re)
{
return;
}
if (cmd == CMD_SUBSTITUTE)
{
/* parse the substitution string & find the option string */
for (opt = subst; *opt && *opt != *extra; opt++)
{
if (*opt == '\\' && opt[1])
{
opt++;
}
}
if (*opt)
{
*opt++ = '\0';
}
/* analyse the option string */
if (!*o_edcompatible)
{
optp = optg = optc = FALSE;
}
while (*opt)
{
switch (*opt++)
{
case 'p': optp = !optp; break;
case 'g': optg = !optg; break;
case 'c': optc = !optc; break;
case ' ':
case '\t': break;
default:
msg("Subst options are p, c, and g -- not %c", opt[-1]);
return;
}
}
}
/* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
if ((optc || optp) && mode == MODE_VI)
{
addch('\n');
exrefresh();
}
ChangeText
{
/* reset the change counters */
chline = chsub = 0L;
/* for each selected line */
for (l = markline(frommark); l <= markline(tomark); l++)
{
/* fetch the line */
line = fetchline(l);
/* if it contains the search pattern... */
if (regexec(re, line, TRUE))
{
/* increment the line change counter */
chline++;
/* initialize the pointers */
s = line;
d = tmpblk.c;
/* do once or globally ... */
do
{
#ifndef CRUNCH
/* confirm, if necessary */
if (optc)
{
for (conf = line; conf < re->startp[0]; conf++)
addch(*conf);
standout();
for ( ; conf < re->endp[0]; conf++)
addch(*conf);
standend();
for (; *conf; conf++)
addch(*conf);
addch('\n');
exrefresh();
if (getkey(0) != 'y')
{
/* copy accross the original chars */
while (s < re->endp[0])
*d++ = *s++;
/* skip to next match on this line, if any */
continue;
}
}
#endif /* not CRUNCH */
/* increment the substitution change counter */
chsub++;
/* this may be the first line to redraw */
redrawrange(l, l + 1L, l + 1L);
/* copy stuff from before the match */
while (s < re->startp[0])
{
*d++ = *s++;
}
/* substitute for the matched part */
regsub(re, subst, d);
s = re->endp[0];
d += strlen(d);
} while (optg && regexec(re, s, FALSE));
/* copy stuff from after the match */
while (*d++ = *s++) /* yes, ASSIGNMENT! */
{
}
/* replace the old version of the line with the new */
d[-1] = '\n';
d[0] = '\0';
change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
/* if supposed to print it, do so */
if (optp)
{
addstr(tmpblk.c);
exrefresh();
}
/* move the cursor to that line */
cursor = MARK_AT_LINE(l);
}
}
}
/* tweak for redrawing */
mustredraw = TRUE;
/* free the regexp */
free(re);
/* if done from within a ":g" command, then finish silently */
if (doingglobal)
{
rptlines = chline;
rptlabel = "changed";
return;
}
/* Reporting */
if (chsub == 0)
{
msg("Substitution failed");
}
else if (chline >= *o_report)
{
msg("%ld substitutions on %ld lines", chsub, chline);
}
}
/*ARGSUSED*/
void cmd_delete(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
MARK curs2; /* an altered form of the cursor */
/* choose your cut buffer */
if (*extra == '"')
{
extra++;
}
if (*extra)
{
cutname(*extra);
}
/* make sure we're talking about whole lines here */
frommark = frommark & ~(BLKSIZE - 1);
tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
/* yank the lines */
cut(frommark, tomark);
/* if CMD_DELETE then delete the lines */
if (cmd != CMD_YANK)
{
curs2 = cursor;
ChangeText
{
/* delete the lines */
delete(frommark, tomark);
}
if (curs2 > tomark)
{
cursor = curs2 - tomark + frommark;
}
else if (curs2 > frommark)
{
cursor = frommark;
}
}
}
/*ARGSUSED*/
void cmd_append(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
long l; /* line counter */
ChangeText
{
/* if we're doing a change, delete the old version */
if (cmd == CMD_CHANGE)
{
/* delete 'em */
cmd_delete(frommark, tomark, cmd, bang, extra);
}
/* new lines start at the frommark line, or after it */
l = markline(frommark);
if (cmd == CMD_APPEND)
{
l++;
}
/* get lines until no more lines, or "." line, and insert them */
while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
{
addch('\n');
if (!strcmp(tmpblk.c, "."))
{
break;
}
strcat(tmpblk.c, "\n");
add(MARK_AT_LINE(l), tmpblk.c);
l++;
}
}
/* on the odd chance that we're calling this from vi mode ... */
redraw(MARK_UNSET, FALSE);
}
/*ARGSUSED*/
void cmd_put(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
/* choose your cut buffer */
if (*extra == '"')
{
extra++;
}
if (*extra)
{
cutname(*extra);
}
/* paste it */
ChangeText
{
cursor = paste(frommark, TRUE, FALSE);
}
}
/*ARGSUSED*/
void cmd_join(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
long l;
char *scan;
int len; /* length of the new line */
/* if only one line is specified, assume the following one joins too */
if (markline(frommark) == nlines)
{
msg("Nothing to join with this line");
return;
}
if (markline(frommark) == markline(tomark))
{
tomark += BLKSIZE;
}
/* get the first line */
l = markline(frommark);
strcpy(tmpblk.c, fetchline(l));
len = strlen(tmpblk.c);
/* build the longer line */
while (++l <= markline(tomark))
{
/* get the next line */
scan = fetchline(l);
/* remove any leading whitespace */
while (*scan == '\t' || *scan == ' ')
{
scan++;
}
/* see if the line will fit */
if (strlen(scan) + len + 3 > BLKSIZE)
{
msg("Can't join -- the resulting line would be too long");
return;
}
/* catenate it, with a space (or two) in between */
if (len >= 1 &&
(tmpblk.c[len - 1] == '.'
|| tmpblk.c[len - 1] == '?'
|| tmpblk.c[len - 1] == '!'))
{
tmpblk.c[len++] = ' ';
}
tmpblk.c[len++] = ' ';
strcpy(tmpblk.c + len, scan);
len += strlen(scan);
}
tmpblk.c[len++] = '\n';
tmpblk.c[len] = '\0';
/* make the change */
ChangeText
{
frommark &= ~(BLKSIZE - 1);
tomark &= ~(BLKSIZE - 1);
tomark += BLKSIZE;
change(frommark, tomark, tmpblk.c);
}
/* Reporting... */
rptlines = markline(tomark) - markline(frommark) - 1L;
rptlabel = "joined";
}
/*ARGSUSED*/
void cmd_shift(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
long l; /* line number counter */
int oldidx; /* number of chars previously used for indent */
int newidx; /* number of chars in the new indent string */
int oldcol; /* previous indent amount */
int newcol; /* new indent amount */
char *text; /* pointer to the old line's text */
/* figure out how much of the screen we must redraw (for vi mode) */
if (markline(frommark) != markline(tomark))
{
mustredraw = TRUE;
redrawrange(markline(frommark), markline(tomark) + 1L, markline(tomark) + 1L);
}
ChangeText
{
/* for each line to shift... */
for (l = markline(frommark); l <= markline(tomark); l++)
{
/* get the line - ignore empty lines unless ! mode */
text = fetchline(l);
if (!*text && !bang)
continue;
/* calc oldidx and oldcol */
for (oldidx = 0, oldcol = 0;
text[oldidx] == ' ' || text[oldidx] == '\t';
oldidx++)
{
if (text[oldidx] == ' ')
{
oldcol += 1;
}
else
{
oldcol += *o_tabstop - (oldcol % *o_tabstop);
}
}
/* calc newcol */
if (cmd == CMD_SHIFTR)
{
newcol = oldcol + (*o_shiftwidth & 0xff);
}
else
{
newcol = oldcol - (*o_shiftwidth & 0xff);
if (newcol < 0)
newcol = 0;
}
/* if no change, then skip to next line */
if (oldcol == newcol)
continue;
/* build a new indent string */
newidx = 0;
while (newcol >= *o_tabstop)
{
tmpblk.c[newidx++] = '\t';
newcol -= *o_tabstop;
}
while (newcol > 0)
{
tmpblk.c[newidx++] = ' ';
newcol--;
}
tmpblk.c[newidx] = '\0';
/* change the old indent string into the new */
change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
}
}
/* Reporting... */
rptlines = markline(tomark) - markline(frommark) + 1L;
if (cmd == CMD_SHIFTR)
{
rptlabel = ">ed";
}
else
{
rptlabel = "<ed";
}
}
/*ARGSUSED*/
void cmd_read(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
int fd, rc; /* used while reading from the file */
char *scan; /* used for finding NUL characters */
int hadnul; /* boolean: any NULs found? */
int addnl; /* boolean: forced to add newlines? */
int len; /* number of chars in current line */
long lines; /* number of lines in current block */
struct stat statb;
/* special case: if ":r !cmd" then let the filter() function do it */
if (extra[0] == '!')
{
filter(frommark, MARK_UNSET, extra + 1);
return;
}
/* open the file */
fd = open(extra, O_RDONLY);
if (fd < 0)
{
msg("Can't open \"%s\"", extra);
return;
}
#ifndef CRUNCH
if (stat(extra, &statb) < 0)
{
msg("Can't stat \"%s\"", extra);
}
# if TOS
if (statb.st_mode & S_IJDIR)
# else
# if OSK
if (statb.st_mode & S_IFDIR)
# else
if ((statb.st_mode & S_IFMT) != S_IFREG)
# endif
# endif
{
msg("\"%s\" is not a regular file", extra);
return;
}
#endif /* not CRUNCH */
/* get blocks from the file, and add them */
ChangeText
{
/* insertion starts at the line following frommark */
tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
len = 0;
hadnul = addnl = FALSE;
/* add an extra newline, so partial lines at the end of
* the file don't trip us up
*/
add(tomark, "\n");
/* for each chunk of text... */
while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
{
/* count newlines, convert NULs, etc. ... */
for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
{
/* break up long lines */
if (*scan != '\n' && len + 2 > BLKSIZE)
{
*scan = '\n';
addnl = TRUE;
}
/* protect against NUL chars in file */
if (!*scan)
{
*scan = 0x80;
hadnul = TRUE;
}
/* starting a new line? */
if (*scan == '\n')
{
/* reset length at newline */
len = 0;
lines++;
}
else
{
len++;
}
}
/* add the text */
*scan = '\0';
add(tomark, tmpblk.c);
tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
}
/* if partial last line, then retain that first newline */
if (len > 0)
{
msg("Last line had no newline");
tomark += BLKSIZE; /* <- for the rptlines calc */
}
else /* delete that first newline */
{
delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
}
}
/* close the file */
close(fd);
/* Reporting... */
rptlines = markline(tomark) - markline(frommark);
rptlabel = "read";
if (addnl)
msg("Newlines were added to break up long lines");
if (hadnul)
msg("NULs were converted to 0x80");
}
/*ARGSUSED*/
void cmd_undo(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
undo();
}
/* print the selected lines */
/*ARGSUSED*/
void cmd_print(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
REG char *scan;
REG long l;
REG int col;
for (l = markline(frommark); l <= markline(tomark); l++)
{
/* display a line number, if CMD_NUMBER */
if (cmd == CMD_NUMBER)
{
sprintf(tmpblk.c, "%6ld ", l);
qaddstr(tmpblk.c);
col = 8;
}
else
{
col = 0;
}
/* get the next line & display it */
for (scan = fetchline(l); *scan; scan++)
{
/* expand tabs to the proper width */
if (*scan == '\t' && cmd != CMD_LIST)
{
do
{
qaddch(' ');
col++;
} while (col % *o_tabstop != 0);
}
else if (*scan >= 0 && *scan < ' ' || *scan == '\177')
{
qaddch('^');
qaddch(*scan ^ 0x40);
col += 2;
}
else if ((*scan & 0x80) && cmd == CMD_LIST)
{
sprintf(tmpblk.c, "\\%03o", *scan);
qaddstr(tmpblk.c);
col += 4;
}
else
{
qaddch(*scan);
col++;
}
/* wrap at the edge of the screen */
if (!has_AM && col >= COLS)
{
addch('\n');
col -= COLS;
}
}
if (cmd == CMD_LIST)
{
qaddch('$');
}
addch('\n');
exrefresh();
}
}
/* move or copy selected lines */
/*ARGSUSED*/
void cmd_move(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
MARK destmark;
/* parse the destination linespec. No defaults. Line 0 is okay */
destmark = cursor;
if (!strcmp(extra, "0"))
{
destmark = 0L;
}
else if (linespec(extra, &destmark) == extra || !destmark)
{
msg("invalid destination address");
return;
}
/* flesh the marks out to encompass whole lines */
frommark &= ~(BLKSIZE - 1);
tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
/* make sure the destination is valid */
if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
{
msg("invalid destination address");
}
/* Do it */
ChangeText
{
/* save the text to a cut buffer */
cutname('\0');
cut(frommark, tomark);
/* if we're not copying, delete the old text & adjust destmark */
if (cmd != CMD_COPY)
{
delete(frommark, tomark);
if (destmark >= frommark)
{
destmark -= (tomark - frommark);
}
}
/* add the new text */
paste(destmark, FALSE, FALSE);
}
/* move the cursor to the last line of the moved text */
cursor = destmark + (tomark - frommark) - BLKSIZE;
if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
{
cursor = MARK_LAST;
}
/* Reporting... */
rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
}
/* execute EX commands from a file */
/*ARGSUSED*/
void cmd_source(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
/* must have a filename */
if (!*extra)
{
msg("\"source\" requires a filename");
return;
}
doexrc(extra);
}
#ifndef NO_AT
/*ARGSUSED*/
void cmd_at(frommark, tomark, cmd, bang, extra)
MARK frommark;
MARK tomark;
CMD cmd;
int bang;
char *extra;
{
static nest = FALSE;
int result;
char buf[MAXRCLEN];
/* don't allow nested macros */
if (nest)
{
msg("@ macros can't be nested");
return;
}
nest = TRUE;
/* require a buffer name */
if (*extra == '"')
extra++;
if (!*extra || !isascii(*extra) ||!islower(*extra))
{
msg("@ requires a cut buffer name (a-z)");
}
/* get the contents of the buffer */
result = cb2str(*extra, buf, (unsigned)(sizeof buf));
if (result <= 0)
{
msg("buffer \"%c is empty", *extra);
}
else if (result >= sizeof buf)
{
msg("buffer \"%c is too large to execute", *extra);
}
else
{
/* execute the contents of the buffer as ex commands */
exstring(buf, result);
}
nest = FALSE;
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'ctags.c'
then
echo shar: will not over-write existing file "'ctags.c'"
else
cat << \SHAR_EOF > 'ctags.c'
/* ctags.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the complete source to the ctags program. */
/* Special abilities:
* Can also make a "refs" file for use by the "ref" program.
*/
/* Limitations:
* This version of ctags always writes its output to the file "tags".
* It assumes that every command-line argument (but "-r") is a C source file.
* It does not sort the list of tags, unless CFLAGS=-DSORT.
* It does not recognize duplicate definitions.
* It does not try to handle "static" functions in a clever way.
* It probably won't scan ANSI-C source code very well.
*/
/* Implementation:
* Lines are scanned one-at-a-time.
* The context of lines is tracked via a finite state machine.
* Contexts are:
* EXPECTFN - we're looking for a function name.
* ARGS - between function name and its opening {
* BODY - we found a function name, skip to end of body.
*
* Function tags are referenced by a search string, so that lines may be
* inserted or deleted without mucking up the tag search.
*
* Macro tags are referenced by their line number, because 1) they usually
* occur near the top of a file, so their line# won't change much; 2) They
* often contain characters that are hard to search for; and 3) Their #define
* line is likely to be altered.
*
* Each line of the resulting "tags" file describes one tag. Lines begin with
* the tag name, then a tab, then the file name, then a tab, and then either
* a line number or a slash-delimited search string.
*/
#include <ctype.h>
#include <stdio.h>
#include "config.h"
#define REFS "refs"
#if OSK
#define NUMFMT "%%.%ds\t%%s\t%%ld\n"
#define SRCHFMT "%%.%ds\t%%s\t/^%%s$/\n"
#define MAINFMT "M%%.%ds\t%%s\t/^%%s$/\n"
static char fmt[256];
#else
#define NUMFMT "%.*s\t%s\t%ld\n"
#define SRCHFMT "%.*s\t%s\t/^%s$/\n"
#define MAINFMT "M%.*s\t%s\t/^%s$/\n"
#endif
#ifdef VERBOSE
# define SAY(x) fprintf(stderr, "%s\n", x);
#else
# define SAY(x)
#endif
#define EXPECTFN 1
#define ARGS 2
#define BODY 3
extern char *fgets();
char *progname; /* argv[0], used for diagnostic output */
main(argc, argv)
int argc;
char **argv;
{
FILE *fp;
int i;
FILE *refs; /* used to write to the refs file */
#if MSDOS || TOS
char **wildexpand();
argv=wildexpand(&argc, argv);
#endif
/* notice the program name */
progname = argv[0];
/* create the "refs" file if first arg is "-r" */
if (argc > 1 && !strcmp(argv[1], "-r"))
{
/* delete the "-r" flag from the args list */
argc--;
argv++;
/* open the "refs" file for writing */
refs = fopen(REFS, "w");
if (!refs)
{
fprintf(stderr, "%s: could not create \"%s\"\n", progname, REFS);
exit(2);
}
}
else
{
refs = (FILE *)0;
}
/* process each file named on the command line, or complain if none */
if (argc > 1)
{
/* redirect stdout to go to the "tags" file */
if (!freopen("tags", "w", stdout))
{
fprintf(stderr, "%s: could not create \"%s\"\n", progname, TAGS);
exit(2);
}
for (i = 1; i < argc; i++)
{
/* process this named file */
fp = fopen(argv[i], "r");
if (!fp)
{
fprintf(stderr, "%s: could not read \"%s\"\n", progname, argv[i]);
continue;
}
ctags(fp, argv[i], refs);
fclose(fp);
}
#ifdef SORT
/* This is a hack which will sort the tags list. It should
* on UNIX and Minix. You may have trouble with csh. Note
* that the tags list only has to be sorted if you intend to
* use it with the real vi; elvis permits unsorted tags.
*/
fflush(stdout);
#if OSK
fclose(stdout);
system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
#else
system("sort tags >_tags$$; mv _tags$$ tags");
#endif
#endif
exit(0);
}
else
{
fprintf(stderr, "usage: %s *.[ch]\n", progname);
exit(2);
}
}
/* this function finds all tags in a given file */
ctags(fp, name, refs)
FILE *fp; /* stream of the file to scan */
char *name; /* name of the file being scanned */
FILE *refs; /* NULL, or where to write refs lines */
{
int context; /* context - either EXPECTFN, ARGS, or BODY */
long lnum; /* line number */
char text[1000]; /* a line of text from the file */
char *scan; /* used for searching through text */
int len; /* length of the line */
/* for each line of the file... */
for (context = EXPECTFN, lnum = 1; fgets(text, sizeof text, fp); lnum++)
{
#ifdef VERBOSE
switch(context)
{
case EXPECTFN: scan = "EXPECTFN"; break;
case ARGS: scan = "ARGS "; break;
case BODY: scan = "BODY "; break;
default: scan = "context?";
}
fprintf(stderr, "%s:%s", scan, text);
#endif
/* start of body? */
if (text[0] == '{')
{
context = BODY;
SAY("Start of BODY");
continue;
}
/* argument line, to be written to "refs" ? */
if (refs && context == ARGS)
{
if (text[0] != '\t')
{
putc('\t', refs);
}
fputs(text, refs);
SAY("Argument line");
continue;
}
/* ignore empty or indented lines */
if (text[0] <= ' ')
{
SAY("Empty or indented");
continue;
}
/* end of body? */
if (text[0] == '}')
{
context = EXPECTFN;
SAY("End of BODY");
continue;
}
/* ignore lines in the body of a function */
if (context != EXPECTFN)
{
SAY("BODY or ARGS");
continue;
}
/* strip the newline */
len = strlen(text);
text[--len] = '\0';
/* a preprocessor line? */
if (text[0] == '#')
{
/* find the preprocessor directive */
for (scan = &text[1]; isspace(*scan); scan++)
{
}
/* if it's a #define, make a tag out of it */
if (!strncmp(scan, "define", 6))
{
/* find the start of the symbol name */
for (scan += 6; isspace(*scan); scan++)
{
}
/* find the length of the symbol name */
for (len = 1;
isalnum(scan[len]) || scan[len] == '_';
len++)
{
}
#if OSK
sprintf(fmt, NUMFMT, len);
printf(fmt, scan, name, lnum);
#else
printf(NUMFMT, len, scan, name, lnum);
#endif
}
SAY("Preprocessor line");
continue;
}
/* an extern or static declaration? */
if (text[len - 1] == ';'
|| !strncmp(text, "extern", 6)
|| !strncmp(text, "EXTERN", 6)
|| !strncmp(text, "static", 6)
|| !strncmp(text, "PRIVATE", 7))
{
SAY("Extern or static");
continue;
}
/* if we get here & the first punctuation other than "*" is
* a "(" which is immediately preceded by a name, then
* assume the name is that of a function.
*/
for (scan = text; *scan; scan++)
{
if (ispunct(*scan)
&& !isspace(*scan) /* in BSD, spaces are punctuation?*/
&& *scan != '*' && *scan != '_' && *scan != '(')
{
SAY("Funny punctuation");
goto ContinueContinue;
}
if (*scan == '(')
{
/* permit 0 or 1 spaces between name & '(' */
if (scan > text && scan[-1] == ' ')
{
scan--;
}
/* find the start & length of the name */
for (len = 0, scan--;
scan >= text && (isalnum(*scan) || *scan == '_');
scan--, len++)
{
}
scan++;
/* did we find a function? */
if (len > 0)
{
/* found a function! */
if (len == 4 && !strncmp(scan, "main", 4))
{
#if OSK
sprintf(fmt, MAINFMT, strlen(name) - 2);
printf(fmt, name, name, text);
#else
printf(MAINFMT, strlen(name) - 2, name, name, text);
#endif
}
#if OSK
sprintf(fmt, SRCHFMT, len);
printf(fmt, scan, name, text);
#else
printf(SRCHFMT, len, scan, name, text);
#endif
context = ARGS;
/* add a line to refs, if needed */
if (refs)
{
fputs(text, refs);
putc('\n', refs);
}
goto ContinueContinue;
}
}
else
{
SAY("No parenthesis");
}
}
SAY("No punctuation");
ContinueContinue:;
}
}
#if MSDOS || TOS
#define WILDCARD_NO_MAIN
#include "wildcard.c"
#endif
SHAR_EOF
fi # end of overwriting check
if test -f 'curses.c'
then
echo shar: will not over-write existing file "'curses.c'"
else
cat << \SHAR_EOF > 'curses.c'
/* curses.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the functions & variables needed for a tiny subset of
* curses. The principle advantage of this version of curses is its
* extreme speed. Disadvantages are potentially larger code, few supported
* functions, limited compatibility with full curses, and only stdscr.
*/
#include "config.h"
#include "vi.h"
#if ANY_UNIX
# if UNIXV
# include <termio.h>
# undef TIOCWINSZ /* we can't handle it correctly yet */
# else
# include <sgtty.h>
# endif
#endif
#if TOS
# include <osbind.h>
#endif
#if OSK
# include <sgstat.h>
#endif
#include <signal.h>
extern char *getenv();
void starttcap();
/* variables, publicly available & used in the macros */
short ospeed; /* speed of the tty, eg B2400 */
#if OSK
char PC_; /* Pad char */
#else
char PC; /* Pad char */
#endif
WINDOW *stdscr; /* pointer into kbuf[] */
WINDOW kbuf[KBSIZ]; /* a very large output buffer */
int LINES; /* :li#: number of rows */
int COLS; /* :co#: number of columns */
int AM; /* :am: boolean: auto margins? */
int PT; /* :pt: boolean: physical tabs? */
char *BC = "\b"; /* :bc backspace character string */
char *VB; /* :vb=: visible bell */
char *UP; /* :up=: move cursor up */
char *SO; /* :so=: standout start */
char *SE; /* :se=: standout end */
char *US = ""; /* :us=: underline start */
char *UE = ""; /* :ue=: underline end */
char *MD = ""; /* :md=: bold start */
char *ME = ""; /* :me=: bold end */
char *AS; /* :as=: alternate (italic) start */
char *AE; /* :ae=: alternate (italic) end */
char *CM; /* :cm=: cursor movement */
char *CE; /* :ce=: clear to end of line */
char *CD; /* :cd=: clear to end of screen */
char *AL; /* :al=: add a line */
char *DL; /* :dl=: delete a line */
#if OSK
char *SR_; /* :sr=: scroll reverse */
#else
char *SR; /* :sr=: scroll reverse */
#endif
char *KS; /* :ks=: init string for cursor */
char *KE; /* :ke=: restore string for cursor */
char *KU; /* :ku=: key sequence sent by up arrow */
char *KD; /* :kd=: key sequence sent by down arrow */
char *KL; /* :kl=: key sequence sent by left arrow */
char *KR; /* :kr=: key sequence sent by right arrow */
char *HM; /* :HM=: key sequence sent by the <Home> key */
char *EN; /* :EN=: key sequence sent by the <End> key */
char *PU; /* :PU=: key sequence sent by the <PgUp> key */
char *PD; /* :PD=: key sequence sent by the <PgDn> key */
char *IM; /* :im=: insert mode start */
char *IC = ""; /* :ic=: insert the following character */
char *EI; /* :ei=: insert mode end */
char *DC; /* :dc=: delete a character */
char *TI; /* :ti=: terminal init */ /* GB */
char *TE; /* :te=: terminal exit */ /* GB */
#ifndef NO_CURSORSHAPE
char *CQ = (char *)0;/* :cQ=: normal cursor */
char *CX = (char *)1;/* :cX=: cursor used for EX command/entry */
char *CV = (char *)2;/* :cV=: cursor used for VI command mode */
char *CI = (char *)3;/* :cI=: cursor used for VI input mode */
char *CR = (char *)4;/* :cR=: cursor used for VI replace mode */
#endif
char *aend = ""; /* end an attribute -- either UE or ME */
char ERASEKEY; /* backspace key taken from ioctl structure */
#if ANY_UNIX
# if UNIXV
static struct termio oldtermio; /* original tty mode */
static struct termio newtermio; /* cbreak/noecho tty mode */
# else
static struct sgttyb oldsgttyb; /* original tty mode */
static struct sgttyb newsgttyb; /* cbreak/nl/noecho tty mode */
static int oldint; /* ^C or DEL, the "intr" character */
# ifdef TIOCSLTC
static int oldswitch; /* ^Z, the "suspend" character */
static int oldquote; /* ^V, the "quote next char" char */
# endif
# endif
#endif
#if OSK
static struct sgbuf oldsgttyb; /* orginal tty mode */
static struct sgbuf newsgttyb; /* noecho tty mode */
#endif
static char *capbuf; /* capability string buffer */
void initscr()
{
/* make sure TERM variable is set */
#if MSDOS
char *val;
if (! (val = getenv("TERM"))
|| !strcmp(val, "pcbios"))
#else
if (!getenv("TERM"))
#endif
{
#if ANY_UNIX
write(2, "Environment variable TERM must be set\n", (unsigned)38);
exit(1);
#endif
#if OSK
writeln(2, "Environment variable TERM must be set\n", (unsigned)38);
exit(1);
#endif
#if MSDOS || TOS
Getsize(0);
#endif
}
else
{
#if MSDOS
*o_pcbios=0;
#endif
/* start termcap stuff */
starttcap();
}
/* create stdscr and curscr */
stdscr = kbuf;
/* change the terminal mode to cbreak/noecho */
#if ANY_UNIX
# if UNIXV
ioctl(2, TCGETA, &oldtermio);
# else
ioctl(2, TIOCGETP, &oldsgttyb);
# endif
#endif
#if OSK
_gs_opt(0, &oldsgttyb);
#endif
Resume_curses(TRUE);
}
void endwin()
{
/* change the terminal mode back the way it was */
Suspend_curses();
}
static int curses_active = FALSE;
void suspend_curses()
{
#if ANY_UNIX && !UNIXV
struct tchars tbuf;
# ifdef TIOCSLTC
struct ltchars ltbuf;
# endif
#endif
#ifndef NO_CURSORSHAPE
if (has_CQ)
{
do_CQ();
}
#endif
if (has_TE) /* GB */
{
do_TE();
}
if (has_KE)
{
do_KE();
}
refresh();
/* change the terminal mode back the way it was */
#if ANY_UNIX
# if UNIXV
ioctl(2, TCSETAW, &oldtermio);
# else
ioctl(2, TIOCSETP, &oldsgttyb);
ioctl(2, TIOCGETC, &tbuf);
tbuf.t_intrc = oldint;
ioctl(2, TIOCSETC, &tbuf);
# ifdef TIOCSLTC
ioctl(2, TIOCGLTC, <buf);
ltbuf.t_suspc = oldswitch;
ltbuf.t_lnextc = oldquote;
ioctl(2, TIOCSLTC, <buf);
# endif
# endif
#endif
#if OSK
_ss_opt(0, &oldsgttyb);
#endif
curses_active = FALSE;
}
void resume_curses(quietly)
int quietly;
{
if (!curses_active)
{
/* change the terminal mode to cbreak/noecho */
#if ANY_UNIX
# if UNIXV
ospeed = (oldtermio.c_cflag & CBAUD);
ERASEKEY = oldtermio.c_cc[VERASE];
newtermio = oldtermio;
newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK);
newtermio.c_oflag &= ~OPOST;
newtermio.c_lflag &= ISIG;
newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */
newtermio.c_cc[VMIN] = 1;
newtermio.c_cc[VTIME] = 0;
# ifdef VSWTCH
newtermio.c_cc[VSWTCH] = 0;
# endif
ioctl(2, TCSETAW, &newtermio);
# else /* BSD or V7 or Coherent or Minix */
struct tchars tbuf;
# ifdef TIOCSLTC
struct ltchars ltbuf;
# endif
ospeed = oldsgttyb.sg_ospeed;
ERASEKEY = oldsgttyb.sg_erase;
newsgttyb = oldsgttyb;
newsgttyb.sg_flags |= CBREAK;
newsgttyb.sg_flags &= ~(CRMOD|ECHO|XTABS);
ioctl(2, TIOCSETP, &newsgttyb);
ioctl(2, TIOCGETC, &tbuf);
oldint = tbuf.t_intrc;
tbuf.t_intrc = ctrl('C'); /* always use ^C for interrupts */
ioctl(2, TIOCSETC, &tbuf);
# ifdef TIOCSLTC
ioctl(2, TIOCGLTC, <buf);
oldswitch = ltbuf.t_suspc;
ltbuf.t_suspc = 0; /* disable ^Z for elvis */
oldquote = ltbuf.t_lnextc;
ltbuf.t_lnextc = 0; /* disable ^V for elvis */
ioctl(2, TIOCSLTC, <buf);
# endif
# endif
#endif
#if OSK
newsgttyb = oldsgttyb;
newsgttyb.sg_echo = 0;
newsgttyb.sg_eofch = 0;
newsgttyb.sg_kbach = 0;
newsgttyb.sg_kbich = ctrl('C');
_ss_opt(0, &newsgttyb);
ospeed = oldsgttyb.sg_baud;
ERASEKEY = oldsgttyb.sg_bspch;
#endif
if (has_TI) /* GB */
{
do_TI();
}
if (has_KS)
{
do_KS();
}
curses_active = TRUE;
}
/* If we're supposed to quit quietly, then we're done */
if (quietly)
{
return;
}
signal(SIGINT, SIG_IGN);
move(LINES - 1, 0);
do_SO();
qaddstr("[Press <RETURN> to continue]");
do_SE();
refresh();
ttyread(kbuf, 20); /* in RAW mode, so <20 is very likely */
if (kbuf[0] == ':')
{
mode = MODE_COLON;
addch('\n');
refresh();
}
else
{
mode = MODE_VI;
redraw(MARK_UNSET, FALSE);
}
exwrote = FALSE;
#if TURBOC
signal(SIGINT, (void(*)()) trapint);
#else
signal(SIGINT, trapint);
#endif
}
static void lacking(s)
char *s;
{
write(2, "This termcap entry lacks the :", (unsigned)30);
write(2, s, (unsigned)2);
write(2, "=: capability\n", (unsigned)14);
#if OSK
write(2, "\l", 1);
#endif
exit(1);
}
void starttcap()
{
char *str;
static char cbmem[800];
#define MUSTHAVE(T,s) if (!(T = Tgetstr(s, &capbuf))) lacking(s)
#define MAYHAVE(T,s) if (str = Tgetstr(s, &capbuf)) T = str
#define PAIR(T,U,sT,sU) T=Tgetstr(sT,&capbuf);U=Tgetstr(sU,&capbuf);if (!T||!U)T=U=""
/* allocate memory for capbuf */
capbuf = cbmem;
/* get the termcap entry */
switch (Tgetent(kbuf, getenv("TERM")))
{
case -1:
write(2, "Can't read /etc/termcap\n", (unsigned)24);
#if OSK
write(2, "\l", 1);
#endif
exit(2);
case 0:
write(2, "Unrecognized TERM type\n", (unsigned)23);
#if OSK
write(2, "\l", 1);
#endif
exit(3);
}
/* get strings */
MUSTHAVE(UP, "up");
MAYHAVE(BC, "bc");
MAYHAVE(VB, "vb");
MUSTHAVE(CM, "cm");
PAIR(SO, SE, "so", "se");
PAIR(TI, TE, "ti", "te");
if (Tgetnum("ug") <= 0)
{
PAIR(US, UE, "us", "ue");
PAIR(MD, ME, "md", "me");
/* get italics, or have it default to underline */
PAIR(AS, AE, "as", "ae");
if (!*AS)
{
AS = US;
AE = UE;
}
}
MAYHAVE(AL, "al");
MAYHAVE(DL, "dl");
MUSTHAVE(CE, "ce");
MAYHAVE(CD, "cd");
#if OSK
MAYHAVE(SR_, "sr");
#else
MAYHAVE(SR, "sr");
#endif
PAIR(IM, EI, "im", "ei");
MAYHAVE(IC, "ic");
MAYHAVE(DC, "dc");
/* other termcap stuff */
AM = Tgetflag("am");
PT = Tgetflag("pt");
Getsize(0);
/* Key sequences */
PAIR(KS, KE, "ks", "ke");
MAYHAVE(KU, "ku"); /* up */
MAYHAVE(KD, "kd"); /* down */
MAYHAVE(KL, "kl"); /* left */
MAYHAVE(KR, "kr"); /* right */
MAYHAVE(PU, "kP"); /* PgUp */
MAYHAVE(PD, "kN"); /* PgDn */
MAYHAVE(HM, "kh"); /* Home */
MAYHAVE(EN, "kH"); /* End */
#ifndef CRUNCH
if (!PU) MAYHAVE(PU, "K2"); /* "3x3 pad" names for PgUp, etc. */
if (!PD) MAYHAVE(PD, "K5");
if (!HM) MAYHAVE(HM, "K1");
if (!EN) MAYHAVE(EN, "K4");
MAYHAVE(PU, "PU"); /* old XENIX names for PgUp, etc. */
MAYHAVE(PD, "PD"); /* (overrides others, if used.) */
MAYHAVE(HM, "HM");
MAYHAVE(EN, "EN");
#endif
#ifndef NO_CURSORSHAPE
/* cursor shapes */
CQ = Tgetstr("cQ", &capbuf);
if (has_CQ)
{
CX = Tgetstr("cX", &capbuf);
if (!CX) CX = CQ;
CV = Tgetstr("cV", &capbuf);
if (!CV) CV = CQ;
CI = Tgetstr("cI", &capbuf);
if (!CI) CI = CQ;
CR = Tgetstr("cR", &capbuf);
if (!CR) CR = CQ;
}
# ifndef CRUNCH
else
{
PAIR(CQ, CV, "ve", "vs");
CX = CI = CR = CQ;
}
# endif /* !CRUNCH */
#endif /* !NO_CURSORSHAPE */
#undef MUSTHAVE
#undef MAYHAVE
#undef PAIR
}
/* This function gets the window size. It uses the TIOCGWINSZ ioctl call if
* your system has it, or Tgetnum("li") and Tgetnum("co") if it doesn't.
* This function is called once during initialization, and thereafter it is
* called whenever the SIGWINCH signal is sent to this process.
*/
int getsize(signo)
int signo;
{
int lines;
int cols;
#ifdef TIOCGWINSZ
struct winsize size;
#endif
#ifdef SIGWINCH
/* reset the signal vector */
signal(SIGWINCH, getsize);
#endif
/* get the window size, one way or another. */
lines = cols = 0;
#ifdef TIOCGWINSZ
if (ioctl(2, TIOCGWINSZ, &size) >= 0)
{
lines = size.ws_row;
cols = size.ws_col;
}
#endif
if ((lines == 0 || cols == 0) && signo == 0)
{
LINES = CHECKBIOS(v_rows(), Tgetnum("li"));
COLS = CHECKBIOS(v_cols(), Tgetnum("co"));
}
if (lines >= 2 && cols >= 30)
{
LINES = lines;
COLS = cols;
}
/* Make sure we got values that we can live with */
if (LINES < 2 || COLS < 30)
{
write(2, "Screen too small\n", (unsigned)17);
#if OSK
write(2, "\l", 1);
#endif
endwin();
exit(2);
}
/* !!! copy the new values into Elvis' options */
{
extern char o_columns[], o_lines[];
*o_columns = COLS;
*o_lines = LINES;
}
return 0;
}
/* This is a function version of addch() -- it is used by tputs() */
int faddch(ch)
int ch;
{
addch(ch);
return 0;
}
/* These functions are equivelent to the macros of the same names... */
void qaddstr(str)
char *str;
{
REG char *s_, *d_;
#if MSDOS
if (o_pcbios[0])
{
while (*str)
qaddch(*str++);
return;
}
#endif
for (s_=(str), d_=stdscr; *d_++ = *s_++; )
{
}
stdscr = d_ - 1;
}
void attrset(a)
int a;
{
do_aend();
if (a == A_BOLD)
{
do_MD();
aend = ME;
}
else if (a == A_UNDERLINE)
{
do_US();
aend = UE;
}
else if (a == A_ALTCHARSET)
{
do_AS();
aend = AE;
}
else
{
aend = "";
}
}
void insch(ch)
int ch;
{
if (has_IM)
do_IM();
do_IC();
qaddch(ch);
if (has_EI)
do_EI();
}
#if MSDOS
static int alarmtime;
/* raw read - #defined to read (0, ...) on non-MSDOS.
* With MSDOS, am maximum of 1 byte is read.
* If more bytes should be read, just change the loop.
* The following code uses the IBM-PC-System-Timer, so probably wont't work
* on non-compatibles.
*/
/*ARGSUSED*/
ttyread(buf, len)
char *buf;
int len;
{
volatile char far *biostimer;
char oldtime;
int nticks = 0;
int pos = 0;
biostimer = (char far *)0x0040006cl;
oldtime = *biostimer;
while (!pos && (!alarmtime || nticks<alarmtime))
{ if (kbhit())
if ((buf[pos++] = getch()) == 0) /* function key */
buf[pos-1] = '#';
if (oldtime != *biostimer)
{ nticks++;
oldtime = *biostimer;
}
}
return pos;
}
alarm(time)
int time;
{
alarmtime = 2 * time; /* ticks are 1/18 sec. */
}
sleep(seconds)
unsigned seconds;
{
volatile char far *biostimer = (char far *)0x0040006cl;
char stop;
stop = *biostimer + 18 * seconds;
while (*biostimer != stop)
{
}
}
#endif
#if TOS
static int alarmtime;
static long timer;
static gettime()
{
timer = *(long *)(0x4ba);
}
/*ARGSUSED*/
ttyread(buf, len)
char *buf;
int len;
{
int pos=0;
long l;
long endtime;
Supexec(gettime);
endtime = timer+alarmtime;
while (!pos && (!alarmtime || timer<endtime))
{
if (Bconstat(2))
{
l = Bconin(2);
if ((buf[pos++]=l) == '\0')
{
buf[pos-1]='#';
buf[pos++]=l>>16;
}
}
Supexec(gettime);
}
return pos;
}
alarm(time)
int time;
{
alarmtime = 50 * time; /* ticks are 1/200 sec. */
}
ttywrite(buf, len)
char *buf;
int len;
{
while (len--)
Bconout(2, *buf++);
}
#endif
#if OSK
ttyread(buf, len)
char *buf;
int len;
{
REG int i;
if ((i = _gs_rdy(0)) > 0)
return read(0, buf, i < len ? i : len);
else
return read(0, buf, 1);
}
alarm(time)
int time;
{
#define TIME(secs) ((secs << 8) | 0x80000000)
static int alrmid;
if (time)
alrmid = alm_set(SIGQUIT, TIME(time));
else
alm_delete(alrmid);
}
#endif /* OSK */
SHAR_EOF
fi # end of overwriting check
if test -f 'cut.c'
then
echo shar: will not over-write existing file "'cut.c'"
else
cat << \SHAR_EOF > 'cut.c'
/* cut.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains function which manipulate the cut buffers. */
#include "config.h"
#include "vi.h"
#if TURBOC
#include <process.h> /* needed for getpid */
#endif
#if TOS
#include <osbind.h>
#define rename(a,b) Frename(0,a,b)
#endif
# define NANNONS 9 /* number of annonymous buffers */
static struct cutbuf
{
short *phys; /* pointer to an array of #s of BLKs containing text */
int nblks; /* number of blocks in phys[] array */
int start; /* offset into first block of start of cut */
int end; /* offset into last block of end of cut */
int fd; /* fd of tmp file, or -1 to use tmpfd */
char lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */
}
named[27], /* cut buffers "a through "z and ". */
annon[NANNONS]; /* annonymous cut buffers */
static char cbname; /* name chosen for next cut/paste operation */
#ifndef NO_RECYCLE
/* This function builds a list of all blocks needed in the current tmp file
* for the contents of cut buffers.
* !!! WARNING: if you have more than ~450000 bytes of text in all of the
* cut buffers, then this will fail disastrously, because buffer overflow
* is *not* allowed for.
*/
int cutneeds(need)
BLK *need; /* this is where we deposit the list */
{
struct cutbuf *cb; /* used to count through cut buffers */
int i; /* used to count through blocks of a cut buffer */
int n; /* total number of blocks in list */
n = 0;
/* first the named buffers... */
for (cb = named; cb < &named[27]; cb++)
{
if (cb->fd > 0)
continue;
for (i = cb->nblks; i-- > 0; )
{
need->n[n++] = cb->phys[i];
}
}
/* then the anonymous buffers */
for (cb = annon; cb < &annon[NANNONS]; cb++)
{
if (cb->fd > 0)
continue;
for (i = cb->nblks; i-- > 0; )
{
need->n[n++] = cb->phys[i];
}
}
return n;
}
#endif
/* This function frees a cut buffer */
static void cutfree(buf)
struct cutbuf *buf;
{
char cutfname[50];
int i;
/* return immediately if the buffer is already empty */
if (buf->nblks <= 0)
{
return;
}
/* else free up stuff */
buf->nblks = 0;
#ifdef DEBUG
if (!buf->phys)
msg("cutfree() tried to free an NULL buf->phys pointer.");
#endif
free((char *)buf->phys);
/* see if anybody else needs this tmp file */
if (buf->fd >= 0)
{
for (i = 0; i < 27; i++)
{
if (named[i].nblks > 0 && named[i].fd == buf->fd)
{
break;
}
}
}
/* if nobody else needs it, then discard the tmp file */
if (buf->fd >= 0 && i == 27)
{
close(buf->fd);
#if MSDOS || TOS
strcpy(cutfname, o_directory);
if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
cutfname[i++]=SLASH;
sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
#else
sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
#endif
unlink(cutfname);
}
}
/* This function is called when we are about to abort a tmp file. If any
* cut buffers still need the file, then a copy of the file should be
* created for use by the cut buffers.
*
* To minimize the number of extra files lying around, only named cut buffers
* are preserved in a file switch; the annonymous buffers just go away.
*/
void cutswitch(tmpname)
char *tmpname; /* name of the tmp file */
{
char cutfname[50]; /* used to build a new name for the tmp file */
int fd; /* a new fd for the current tmp file */
int i;
#if MSDOS || TOS
int j;
#endif
/* discard all annonymous cut buffers */
for (i = 0; i < NANNONS; i++)
{
cutfree(&annon[i]);
}
/* find the first named buffer that uses this tmp file */
for (i = 0; i < 27; i++)
{
if (named[i].nblks > 0 && named[i].fd < 0)
{
break;
}
}
/* if none of them use this tmp file, then we're done */
if (i == 27)
{
return;
}
/* else we'll need this file and an fd a little longer */
#if MSDOS || TOS
strcpy(cutfname, o_directory);
if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
cutfname[j++]=SLASH;
close(tmpfd);
fd = open(tmpname, O_RDONLY|O_BINARY);
close(fd);
sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
rename(tmpname, cutfname);
fd = open(cutfname, O_RDONLY|O_BINARY);
tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
#else
fd = dup(tmpfd);
# if OSK
sprintf(cutfname, CUTNAME, "", getpid(), fd);
if (!link(tmpname, &cutfname[1])) /* skip slash */
unlink(tmpname);
# else
sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
link(tmpname, cutfname) || unlink(tmpname);
# endif
#endif
/* have all cut buffers use the new fd instead */
for (; i < 27; i++)
{
if (named[i].nblks > 0 && named[i].fd < 0)
{
named[i].fd = fd;
}
}
}
/* This function should be called just before termination of vi */
void cutend()
{
int i;
/* free all named cut buffers, since they might be forcing an older
* tmp file to be retained.
*/
for (i = 0; i < 27; i++)
{
cutfree(&named[i]);
}
}
/* This function is used to select the cut buffer to be used next */
void cutname(name)
int name; /* a single character */
{
cbname = name;
}
/* This function copies a selected segment of text to a cut buffer */
void cut(from, to)
MARK from; /* start of text to cut */
MARK to; /* end of text to cut */
{
int first; /* logical number of first block in cut */
int last; /* logical number of last block used in cut */
long line; /* a line number */
int lnmode; /* boolean: will this be a line-mode cut? */
MARK delthru;/* end of text temporarily inserted for apnd */
REG struct cutbuf *cb;
REG long l;
REG int i;
REG char *scan;
char *blkc;
/* detect whether this must be a line-mode cut or char-mode cut */
if (markidx(from) == 0 && markidx(to) == 0)
lnmode = TRUE;
else
lnmode = FALSE;
/* by default, we don't "delthru" anything */
delthru = MARK_UNSET;
/* decide which cut buffer to use */
if (!cbname)
{
/* free up the last annonymous cut buffer */
cutfree(&annon[NANNONS - 1]);
/* shift the annonymous cut buffers */
for (i = NANNONS - 1; i > 0; i--)
{
annon[i] = annon[i - 1];
}
/* use the first annonymous cut buffer */
cb = annon;
cb->nblks = 0;
}
else if (cbname >= 'a' && cbname <= 'z')
{
cb = &named[cbname - 'a'];
cutfree(cb);
}
#ifndef CRUNCH
else if (cbname >= 'A' && cbname <= 'Z')
{
cb = &named[cbname - 'A'];
if (cb->nblks > 0)
{
/* resolve linemode/charmode differences */
if (!lnmode && cb->lnmode)
{
from &= ~(BLKSIZE - 1);
if (markidx(to) != 0 || to == from)
{
to = to + BLKSIZE - markidx(to);
}
lnmode = TRUE;
}
/* insert the old cut-buffer before the new text */
mark[28] = to;
delthru = paste(from, FALSE, TRUE);
if (delthru == MARK_UNSET)
{
return;
}
delthru++;
to = mark[28];
}
cutfree(cb);
}
#endif /* not CRUNCH */
else if (cbname == '.')
{
cb = &named[26];
cutfree(cb);
}
else
{
msg("Invalid cut buffer name: \"%c", cbname);
cbname = '\0';
return;
}
cbname = '\0';
cb->fd = -1;
/* detect whether we're doing a line mode cut */
cb->lnmode = lnmode;
/* ---------- */
/* Reporting... */
if (markidx(from) == 0 && markidx(to) == 0)
{
rptlines = markline(to) - markline(from);
rptlabel = "yanked";
}
/* ---------- */
/* make sure each block has a physical disk address */
blksync();
/* find the first block in the cut */
line = markline(from);
for (first = 1; line > lnum[first]; first++)
{
}
/* fetch text of the block containing that line */
blkc = scan = blkget(first)->c;
/* find the mark in the block */
for (l = lnum[first - 1]; ++l < line; )
{
while (*scan++ != '\n')
{
}
}
scan += markidx(from);
/* remember the offset of the start */
cb->start = scan - blkc;
/* ---------- */
/* find the last block in the cut */
line = markline(to);
for (last = first; line > lnum[last]; last++)
{
}
/* fetch text of the block containing that line */
if (last != first)
{
blkc = scan = blkget(last)->c;
}
else
{
scan = blkc;
}
/* find the mark in the block */
for (l = lnum[last - 1]; ++l < line; )
{
while (*scan++ != '\n')
{
}
}
if (markline(to) <= nlines)
{
scan += markidx(to);
}
/* remember the offset of the end */
cb->end = scan - blkc;
/* ------- */
/* remember the physical block numbers of all included blocks */
cb->nblks = last - first;
if (cb->end > 0)
{
cb->nblks++;
}
#ifdef lint
cb->phys = (short *)0;
#else
cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
#endif
for (i = 0; i < cb->nblks; i++)
{
cb->phys[i] = hdr.n[first++];
}
#ifndef CRUNCH
/* if we temporarily inserted text for appending, then delete that
* text now -- before the user sees it.
*/
if (delthru)
{
line = rptlines;
delete(from, delthru);
rptlines = line;
rptlabel = "yanked";
}
#endif /* not CRUNCH */
}
static void readcutblk(cb, blkno)
struct cutbuf *cb;
int blkno;
{
int fd; /* either tmpfd or cb->fd */
/* decide which fd to use */
if (cb->fd >= 0)
{
fd = cb->fd;
}
else
{
fd = tmpfd;
}
/* get the block */
lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
{
msg("Error reading back from tmp file for pasting!");
}
}
/* This function inserts text from a cut buffer, and returns the MARK where
* insertion ended. Return MARK_UNSET on errors.
*/
MARK paste(at, after, retend)
MARK at; /* where to insert the text */
int after; /* boolean: insert after mark? (rather than before) */
int retend; /* boolean: return end of text? (rather than start) */
{
REG struct cutbuf *cb;
REG int i;
/* decide which cut buffer to use */
if (cbname >= 'A' && cbname <= 'Z')
{
cb = &named[cbname - 'A'];
}
else if (cbname >= 'a' && cbname <= 'z')
{
cb = &named[cbname - 'a'];
}
else if (cbname >= '1' && cbname <= '9')
{
cb = &annon[cbname - '1'];
}
else if (cbname == '.')
{
cb = &named[26];
}
else if (!cbname)
{
cb = annon;
}
else
{
msg("Invalid cut buffer name: \"%c", cbname);
cbname = '\0';
return MARK_UNSET;
}
/* make sure it isn't empty */
if (cb->nblks == 0)
{
if (cbname)
msg("Cut buffer \"%c is empty", cbname);
else
msg("Cut buffer is empty");
cbname = '\0';
return MARK_UNSET;
}
cbname = '\0';
/* adjust the insertion MARK for "after" and line-mode cuts */
if (cb->lnmode)
{
at &= ~(BLKSIZE - 1);
if (after)
{
at += BLKSIZE;
}
}
else if (after)
{
/* careful! if markidx(at) == 0 we might be pasting into an
* empty line -- so we can't blindly increment "at".
*/
if (markidx(at) == 0)
{
pfetch(markline(at));
if (plen != 0)
{
at++;
}
}
else
{
at++;
}
}
/* put a copy of the "at" mark in the mark[] array, so it stays in
* sync with changes made via add().
*/
mark[27] = at;
/* simple one-block paste? */
if (cb->nblks == 1)
{
/* get the block */
readcutblk(cb, 0);
/* isolate the text we need within it */
if (cb->end)
{
tmpblk.c[cb->end] = '\0';
}
/* insert it */
ChangeText
{
add(at, &tmpblk.c[cb->start]);
}
}
else
{
/* multi-block paste */
ChangeText
{
i = cb->nblks - 1;
/* add text from the last block first */
if (cb->end > 0)
{
readcutblk(cb, i);
tmpblk.c[cb->end] = '\0';
add(at, tmpblk.c);
i--;
}
/* add intervening blocks */
while (i > 0)
{
readcutblk(cb, i);
add(at, tmpblk.c);
i--;
}
/* add text from the first cut block */
readcutblk(cb, 0);
add(at, &tmpblk.c[cb->start]);
}
}
/* Reporting... */
rptlines = markline(mark[27]) - markline(at);
rptlabel = "pasted";
/* return the mark at the beginning/end of inserted text */
if (retend)
{
return mark[27] - 1L;
}
return at;
}
#ifndef NO_AT
/* This function copies characters from a cut buffer into a string.
* It returns the number of characters in the cut buffer. If the cut
* buffer is too large to fit in the string (i.e. if cb2str() returns
* a number >= size) then the characters will not have been copied.
* It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
*/
int cb2str(name, buf, size)
int name; /* the name of a cut-buffer to get: a-z only! */
char *buf; /* where to put the string */
unsigned size; /* size of buf */
{
REG struct cutbuf *cb;
REG char *src;
REG char *dest;
/* decide which cut buffer to use */
if (name >= 'a' && name <= 'z')
{
cb = &named[name - 'a'];
}
else
{
return -1;
}
/* if the buffer is empty, return 0 */
if (cb->nblks == 0)
{
return 0;
}
/* !!! if not a single-block cut, then fail */
if (cb->nblks != 1)
{
return size;
}
/* if too big, return the size now, without doing anything */
if (cb->end - cb->start >= size)
{
return cb->end - cb->start;
}
/* get the block */
readcutblk(cb, 0);
/* isolate the string within that blk */
if (cb->start == 0)
{
tmpblk.c[cb->end] = '\0';
}
else
{
for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
{
*dest++ = *src++;
}
*dest = '\0';
}
/* copy the string into the buffer */
if (buf != tmpblk.c)
{
strcpy(buf, tmpblk.c);
}
/* return the length */
return cb->end - cb->start;
}
#endif
SHAR_EOF
fi # end of overwriting check
# End of shell archive
exit 0
Technical Details
Message-ID:
<1991Jan11.015719.27142@wlbr.imsd.contel.com>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!rutgers!sun-barr!lll-winken!ncis.tis.llnl.gov!blackbird!lonex.radc.af.mil!wlbr!roger.imsd.contel.com!mhShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!rutgers!sun-barr!lll-winken!ncis.tis.llnl.gov!blackbird!lonex.radc.af.mil!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 3 of 9 Message-ID: <1991Jan11.015719.27142@wlbr.imsd.contel.com> Date: 11 Jan 91 01:57:19 GMT Sender: news@wlbr.imsd.contel.com (news) Distribution: comp Organization: Contel FSD, Westlake Village, CA Lines: 4650 Nntp-Posting-Host: roger.imsd.contel.com