Elvis 1.4, part 5 of 8
Steve Kirkendall
kirkenda at eecs.cs.pdx.edu
Tue Dec 4 08:32:42 AEST 1990
# --------------------------- 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 8819 Dec 2 17:57 curses.h
# -rw-r--r-- 1 kirkenda 13222 Dec 2 17:57 cut.c
# -rw-r--r-- 1 kirkenda 15130 Dec 2 17:57 ex.c
# -rw-r--r-- 1 kirkenda 16368 Dec 2 17:57 input.c
# -rw-r--r-- 1 kirkenda 7778 Dec 2 17:57 main.c
# -rw-r--r-- 1 kirkenda 2166 Dec 2 17:57 misc.c
#
if test -f curses.h -a "$1" != -f
then
echo Will not overwrite curses.h
else
echo Extracting curses.h
sed 's/^X//' >curses.h <<\eof
X/* curses.h */
X
X/* Author:
X * Steve Kirkendall
X * 14407 SW Teal Blvd. #C
X * Beaverton, OR 97005
X * kirkenda at cs.pdx.edu
X */
X
X
X/* This is the header file for a small, fast, fake curses package */
X
X/* termcap stuff */
Xextern char *tgoto();
Xextern char *tgetstr();
Xextern void tputs();
X
X#if MSDOS
X/* BIOS interface used instead of termcap for MS-DOS */
Xextern int vmode;
Xextern void v_up();
Xextern void v_cb();
Xextern void v_cs();
Xextern void v_ce();
Xextern void v_cl();
Xextern void v_cd();
Xextern void v_al();
Xextern void v_dl();
Xextern void v_sr();
Xextern void v_move();
X#endif
X
X/* faddch() is a function. a pointer to it is passed to tputs() */
Xextern int faddch();
X
X/* data types */
X#define WINDOW char
X
X/* CONSTANTS & SYMBOLS */
X#define TRUE 1
X#define FALSE 0
X#define A_NORMAL 0
X#define A_STANDOUT 1
X#define A_BOLD 2
X#define A_UNDERLINE 3
X#define A_ALTCHARSET 4
X#if MSDOS
X#define KBSIZ (10*1024)
X#else
X#define KBSIZ (6*1024)
X#endif
X
X/* extern variables, defined in curses.c */
Xextern short ospeed; /* tty speed, eg B2400 */
X#if OSK
Xextern char PC_; /* Pad char */
Xextern char *BC; /* Backspace char string */
X#else
Xextern char PC; /* Pad char */
X#endif
Xextern WINDOW *stdscr; /* pointer into kbuf[] */
Xextern WINDOW kbuf[KBSIZ]; /* a very large output buffer */
Xextern int LINES; /* :li#: number of rows */
Xextern int COLS; /* :co#: number of columns */
Xextern int AM; /* :am: boolean: auto margins? */
Xextern int PT; /* :pt: boolean: physical tabs? */
Xextern char *VB; /* :vb=: visible bell */
Xextern char *UP; /* :up=: move cursor up */
Xextern char *SO; /* :so=: standout start */
Xextern char *SE; /* :se=: standout end */
Xextern char *US; /* :us=: underline start */
Xextern char *UE; /* :ue=: underline end */
Xextern char *MD; /* :md=: bold start */
Xextern char *ME; /* :me=: bold end */
Xextern char *AS; /* :as=: alternate (italic) start */
Xextern char *AE; /* :ae=: alternate (italic) end */
Xextern char *CM; /* :cm=: cursor movement */
Xextern char *CE; /* :ce=: clear to end of line */
Xextern char *CD; /* :cd=: clear to end of screen */
Xextern char *AL; /* :al=: add a line */
Xextern char *DL; /* :dl=: delete a line */
X#if OSK
Xextern char *SR_; /* :sr=: scroll reverse */
X#else
Xextern char *SR; /* :sr=: scroll reverse */
X#endif
Xextern char *KS; /* :ks=: init string for cursor */
Xextern char *KE; /* :ke=: restore string for cursor */
Xextern char *KU; /* :ku=: sequence sent by up key */
Xextern char *KD; /* :kd=: sequence sent by down key */
Xextern char *KL; /* :kl=: sequence sent by left key */
Xextern char *KR; /* :kr=: sequence sent by right key */
Xextern char *PU; /* :PU=: key sequence sent by PgUp key */
Xextern char *PD; /* :PD=: key sequence sent by PgDn key */
Xextern char *HM; /* :HM=: key sequence sent by Home key */
Xextern char *EN; /* :EN=: key sequence sent by End key */
Xextern char *IM; /* :im=: insert mode start */
Xextern char *IC; /* :ic=: insert following char */
Xextern char *EI; /* :ei=: insert mode end */
Xextern char *DC; /* :dc=: delete a character */
Xextern char *TI; /* :ti=: terminal init */ /* GB */
Xextern char *TE; /* :te=: terminal exit */ /* GB */
X#ifndef NO_CURSORSHAPE
Xextern char *CQ; /* :cQ=: normal cursor */
Xextern char *CX; /* :cX=: cursor used for EX command/entry */
Xextern char *CV; /* :cV=: cursor used for VI command mode */
Xextern char *CI; /* :cI=: cursor used for VI input mode */
Xextern char *CR; /* :cR=: cursor used for VI replace mode */
X#endif
Xextern char *aend; /* end an attribute -- either UE or ME */
Xextern char ERASEKEY; /* taken from the ioctl structure */
X
X/* Msdos-versions may use bios; others always termcap.
X * Will emit some 'code has no effect' warnings in unix.
X */
X
X#if MSDOS
Xextern char o_pcbios[1]; /* BAH! */
X#define CHECKBIOS(x,y) (*o_pcbios ? (x) : (y))
X#define VOIDBIOS(x,y) {if (*o_pcbios) {x;} else {y;}}
X#else
X#define CHECKBIOS(x,y) (y)
X#define VOIDBIOS(x,y) {y;}
X#endif
X
X#define do_VB() VOIDBIOS(;, tputs(VB, 1, faddch))
X#define do_UP() VOIDBIOS(v_up(), tputs(UP, 1, faddch))
X#define do_SO() VOIDBIOS((vmode=A_STANDOUT), tputs(SO, 1, faddch))
X#define do_SE() VOIDBIOS((vmode=A_NORMAL), tputs(SE, 1, faddch))
X#define do_US() VOIDBIOS((vmode=A_UNDERLINE), tputs(US, 1, faddch))
X#define do_UE() VOIDBIOS((vmode=A_NORMAL), tputs(UE, 1, faddch))
X#define do_MD() VOIDBIOS((vmode=A_BOLD), tputs(MD, 1, faddch))
X#define do_ME() VOIDBIOS((vmode=A_NORMAL), tputs(ME, 1, faddch))
X#define do_AS() VOIDBIOS((vmode=A_ALTCHARSET), tputs(AS, 1, faddch))
X#define do_AE() VOIDBIOS((vmode=A_NORMAL), tputs(AE, 1, faddch))
X#undef do_CM /* move */
X#define do_CE() VOIDBIOS(v_ce(), tputs(CE, 1, faddch))
X#define do_CD() VOIDBIOS(v_cd(), tputs(CD, 1, faddch))
X#define do_AL() VOIDBIOS(v_al(), tputs(AL, LINES, faddch))
X#define do_DL() VOIDBIOS(v_dl(), tputs(DL, LINES, faddch))
X#if OSK
X#define do_SR() VOIDBIOS(v_sr(), tputs(SR_, 1, faddch))
X#else
X#define do_SR() VOIDBIOS(v_sr(), tputs(SR, 1, faddch))
X#endif
X#define do_KS() VOIDBIOS(1, tputs(KS, 1, faddch))
X#define do_KE() VOIDBIOS(1, tputs(KE, 1, faddch))
X#define do_IM() VOIDBIOS(;, tputs(IM, 1, faddch))
X#define do_IC() VOIDBIOS(;, tputs(IC, 1, faddch))
X#define do_EI() VOIDBIOS(;, tputs(EI, 1, faddch))
X#define do_DC() VOIDBIOS(;, tputs(DC, COLS, faddch))
X#define do_TI() VOIDBIOS(;, (void)ttywrite(TI, (unsigned)strlen(TI)))
X#define do_TE() VOIDBIOS(;, (void)ttywrite(TE, (unsigned)strlen(TE)))
X#ifndef NO_CURSORSHAPE
X# define do_CQ() VOIDBIOS(v_cs(), tputs(CQ, 1, faddch))
X# define do_CX() VOIDBIOS(v_cs(), tputs(CX, 1, faddch))
X# define do_CV() VOIDBIOS(v_cs(), tputs(CV, 1, faddch))
X# define do_CI() VOIDBIOS(v_cb(), tputs(CI, 1, faddch))
X# define do_CR() VOIDBIOS(v_cb(), tputs(CR, 1, faddch))
X#endif
X#define do_aend() VOIDBIOS((vmode=A_NORMAL), tputs(aend, 1, faddch))
X
X#define has_AM CHECKBIOS(1, AM)
X#define has_PT CHECKBIOS(0, PT)
X#define has_VB CHECKBIOS((char *)0, VB)
X#define has_UP CHECKBIOS((char *)1, UP)
X#define has_SO CHECKBIOS((char)1, (*SO))
X#define has_SE CHECKBIOS((char)1, (*SE))
X#define has_US CHECKBIOS((char)1, (*US))
X#define has_UE CHECKBIOS((char)1, (*UE))
X#define has_MD CHECKBIOS((char)1, (*MD))
X#define has_ME CHECKBIOS((char)1, (*ME))
X#define has_AS CHECKBIOS((char)1, (*AS))
X#define has_AE CHECKBIOS((char)1, (*AE))
X#undef has_CM /* cursor move: don't need */
X#define has_CB CHECKBIOS(1, 0)
X#define has_CS CHECKBIOS(1, 0)
X#define has_CE CHECKBIOS((char *)1, CE)
X#define has_CD CHECKBIOS((char *)1, CD)
X#define has_AL CHECKBIOS((char *)1, AL)
X#define has_DL CHECKBIOS((char *)1, DL)
X#if OSK
X#define has_SR CHECKBIOS((char *)1, SR_)
X#else
X#define has_SR CHECKBIOS((char *)1, SR)
X#endif
X#define has_KS CHECKBIOS((char)1, (*KS))
X#define has_KE CHECKBIOS((char)1, (*KE))
X#define has_KU CHECKBIOS("#H", KU)
X#define has_KD CHECKBIOS("#P", KD)
X#define has_KL CHECKBIOS("#K", KL)
X#define has_KR CHECKBIOS("#M", KR)
X#define has_HM CHECKBIOS("#G", HM)
X#define has_EN CHECKBIOS("#O", EN)
X#define has_PU CHECKBIOS("#I", PU)
X#define has_PD CHECKBIOS("#Q", PD)
X#define has_IM CHECKBIOS((char)0, (*IM))
X#define has_IC CHECKBIOS((char)0, (*IC))
X#define has_EI CHECKBIOS((char)0, (*EI))
X#define has_DC CHECKBIOS((char *)0, DC)
X#define has_TI CHECKBIOS((char)0, (*TI))
X#define has_TE CHECKBIOS((char)0, (*TE))
X#ifndef NO_CURSORSHAPE
X#define has_CQ CHECKBIOS((char *)1, CQ)
X#endif
X
X/* (pseudo)-Curses-functions */
X
X#ifdef lint
X# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : (stdscr[-1] = '\n')))
X#else
X# if OSK
X# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\l') : (stdscr[-1] = stdscr[-1])))
X# else
X# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : 0))
X#endif
X#endif
X#define qaddch(ch) CHECKBIOS(v_put(ch), (*stdscr++ = (ch)))
X#if OSK
X#define addch(ch) if (qaddch(ch) == '\n') qaddch('\l'); else
X#else
X#define addch(ch) if (qaddch(ch) == '\n') qaddch('\r'); else
X#endif
X
Xextern void initscr();
Xextern void endwin();
Xextern void suspend_curses();
Xextern void resume_curses();
Xextern void attrset();
Xextern void insch();
Xextern void qaddstr();
X#define addstr(str) {qaddstr(str); _addCR;}
X#define move(y,x) VOIDBIOS(v_move(x,y), \
X tputs(tgoto(CM, x, y), 1, faddch))
X#define mvaddch(y,x,ch) {move(y,x); addch(ch);}
X#define refresh() VOIDBIOS(;, wrefresh(stdscr))
X#define wrefresh(w) if ((w) != kbuf) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (unsigned)((w) - kbuf)); (w) = kbuf;}) else
X#define wqrefresh(w) if ((w) - kbuf > 2000) VOIDBIOS((w) = kbuf, {ttywrite(kbuf, (unsigned)((w) - kbuf)); (w) = kbuf;}) else
X#define standout() do_SO()
X#define standend() do_SE()
X#define clrtoeol() do_CE()
X#define clrtobot() do_CD()
X#define insertln() do_AL()
X#define deleteln() do_DL()
X#define delch() do_DC()
X#define scrollok(w,b)
X#define raw()
X#define echo()
X#define cbreak()
X#define noraw()
X#define noecho()
X#define nocbreak()
eof
if test `wc -c <curses.h` -ne 8819
then
echo curses.h damaged!
fi
fi
if test -f cut.c -a "$1" != -f
then
echo Will not overwrite cut.c
else
echo Extracting cut.c
sed 's/^X//' >cut.c <<\eof
X/* cut.c */
X
X/* Author:
X * Steve Kirkendall
X * 14407 SW Teal Blvd. #C
X * Beaverton, OR 97005
X * kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains function which manipulate the cut buffers. */
X
X#include "config.h"
X#include "vi.h"
X#if TURBOC
X#include <process.h> /* needed for getpid */
X#endif
X#if TOS
X#include <osbind.h>
X#define rename(a,b) Frename(0,a,b)
X#endif
X
X# define NANNONS 9 /* number of annonymous buffers */
X
Xstatic struct cutbuf
X{
X short *phys; /* pointer to an array of #s of BLKs containing text */
X int nblks; /* number of blocks in phys[] array */
X int start; /* offset into first block of start of cut */
X int end; /* offset into last block of end of cut */
X int fd; /* fd of tmp file, or -1 to use tmpfd */
X char lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */
X}
X named[27], /* cut buffers "a through "z and ". */
X annon[NANNONS]; /* annonymous cut buffers */
X
Xstatic char cbname; /* name chosen for next cut/paste operation */
X
X
X#ifndef NO_RECYCLE
X/* This function builds a list of all blocks needed in the current tmp file
X * for the contents of cut buffers.
X * !!! WARNING: if you have more than ~450000 bytes of text in all of the
X * cut buffers, then this will fail disastrously, because buffer overflow
X * is *not* allowed for.
X */
Xint cutneeds(need)
X BLK *need; /* this is where we deposit the list */
X{
X struct cutbuf *cb; /* used to count through cut buffers */
X int i; /* used to count through blocks of a cut buffer */
X int n; /* total number of blocks in list */
X
X n = 0;
X
X /* first the named buffers... */
X for (cb = named; cb < &named[27]; cb++)
X {
X if (cb->fd > 0)
X continue;
X
X for (i = cb->nblks; i-- > 0; )
X {
X need->n[n++] = cb->phys[i];
X }
X }
X
X /* then the anonymous buffers */
X for (cb = annon; cb < &annon[NANNONS]; cb++)
X {
X if (cb->fd > 0)
X continue;
X
X for (i = cb->nblks; i-- > 0; )
X {
X need->n[n++] = cb->phys[i];
X }
X }
X
X return n;
X}
X#endif
X
X/* This function frees a cut buffer */
Xstatic void cutfree(buf)
X struct cutbuf *buf;
X{
X char cutfname[50];
X int i;
X
X /* return immediately if the buffer is already empty */
X if (buf->nblks <= 0)
X {
X return;
X }
X
X /* else free up stuff */
X buf->nblks = 0;
X#ifdef DEBUG
X if (!buf->phys)
X msg("cutfree() tried to free an NULL buf->phys pointer.");
X#endif
X free((char *)buf->phys);
X
X /* see if anybody else needs this tmp file */
X if (buf->fd >= 0)
X {
X for (i = 0; i < 27; i++)
X {
X if (named[i].nblks > 0 && named[i].fd == buf->fd)
X {
X break;
X }
X }
X }
X
X /* if nobody else needs it, then discard the tmp file */
X if (buf->fd >= 0 && i == 27)
X {
X close(buf->fd);
X#if MSDOS || TOS
X strcpy(cutfname, o_directory);
X if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1]))
X cutfname[i++]=SLASH;
X sprintf(cutfname+i, CUTNAME+3, getpid(), buf->fd);
X#else
X sprintf(cutfname, CUTNAME, o_directory, getpid(), buf->fd);
X#endif
X unlink(cutfname);
X }
X}
X
X/* This function is called when we are about to abort a tmp file. If any
X * cut buffers still need the file, then a copy of the file should be
X * created for use by the cut buffers.
X *
X * To minimize the number of extra files lying around, only named cut buffers
X * are preserved in a file switch; the annonymous buffers just go away.
X */
Xvoid cutswitch(tmpname)
X char *tmpname; /* name of the tmp file */
X{
X char cutfname[50]; /* used to build a new name for the tmp file */
X int fd; /* a new fd for the current tmp file */
X int i;
X#if MSDOS || TOS
X int j;
X#endif
X
X /* discard all annonymous cut buffers */
X for (i = 0; i < NANNONS; i++)
X {
X cutfree(&annon[i]);
X }
X
X /* find the first named buffer that uses this tmp file */
X for (i = 0; i < 27; i++)
X {
X if (named[i].nblks > 0 && named[i].fd < 0)
X {
X break;
X }
X }
X
X /* if none of them use this tmp file, then we're done */
X if (i == 27)
X {
X return;
X }
X
X /* else we'll need this file and an fd a little longer */
X#if MSDOS || TOS
X strcpy(cutfname, o_directory);
X if ((j = strlen(cutfname)) && !strchr(":/\\", cutfname[j-1]))
X cutfname[j++]=SLASH;
X close(tmpfd);
X fd = open(tmpname, O_RDONLY|O_BINARY);
X close(fd);
X sprintf(cutfname+j, CUTNAME+3, getpid(), fd);
X rename(tmpname, cutfname);
X fd = open(cutfname, O_RDONLY|O_BINARY);
X tmpfd = -1; /* we'll try to close this in tmp.c, but who cares? */
X#else
X fd = dup(tmpfd);
X# if OSK
X sprintf(cutfname, CUTNAME, "", getpid(), fd);
X if (!link(tmpname, &cutfname[1])) /* skip slash */
X unlink(tmpname);
X# else
X sprintf(cutfname, CUTNAME, o_directory, getpid(), fd);
X link(tmpname, cutfname) || unlink(tmpname);
X# endif
X#endif
X
X /* have all cut buffers use the new fd instead */
X for (; i < 27; i++)
X {
X if (named[i].nblks > 0 && named[i].fd < 0)
X {
X named[i].fd = fd;
X }
X }
X}
X
X/* This function should be called just before termination of vi */
Xvoid cutend()
X{
X int i;
X
X /* free all named cut buffers, since they might be forcing an older
X * tmp file to be retained.
X */
X for (i = 0; i < 27; i++)
X {
X cutfree(&named[i]);
X }
X}
X
X
X/* This function is used to select the cut buffer to be used next */
Xvoid cutname(name)
X int name; /* a single character */
X{
X cbname = name;
X}
X
X
X
X
X/* This function copies a selected segment of text to a cut buffer */
Xvoid cut(from, to)
X MARK from; /* start of text to cut */
X MARK to; /* end of text to cut */
X{
X int first; /* logical number of first block in cut */
X int last; /* logical number of last block used in cut */
X long line; /* a line number */
X int lnmode; /* boolean: will this be a line-mode cut? */
X MARK delthru;/* end of text temporarily inserted for apnd */
X REG struct cutbuf *cb;
X REG long l;
X REG int i;
X REG char *scan;
X char *blkc;
X
X /* detect whether this must be a line-mode cut or char-mode cut */
X if (markidx(from) == 0 && markidx(to) == 0)
X lnmode = TRUE;
X else
X lnmode = FALSE;
X
X /* by default, we don't "delthru" anything */
X delthru = MARK_UNSET;
X
X /* decide which cut buffer to use */
X if (!cbname)
X {
X /* free up the last annonymous cut buffer */
X cutfree(&annon[NANNONS - 1]);
X
X /* shift the annonymous cut buffers */
X for (i = NANNONS - 1; i > 0; i--)
X {
X annon[i] = annon[i - 1];
X }
X
X /* use the first annonymous cut buffer */
X cb = annon;
X cb->nblks = 0;
X }
X else if (cbname >= 'a' && cbname <= 'z')
X {
X cb = &named[cbname - 'a'];
X cutfree(cb);
X }
X#ifndef CRUNCH
X else if (cbname >= 'A' && cbname <= 'Z')
X {
X cb = &named[cbname - 'A'];
X if (cb->nblks > 0)
X {
X /* resolve linemode/charmode differences */
X if (!lnmode && cb->lnmode)
X {
X from &= ~(BLKSIZE - 1);
X if (markidx(to) != 0 || to == from)
X {
X to = to + BLKSIZE - markidx(to);
X }
X lnmode = TRUE;
X }
X
X /* insert the old cut-buffer before the new text */
X mark[28] = to;
X delthru = paste(from, FALSE, TRUE);
X if (delthru == MARK_UNSET)
X {
X return;
X }
X delthru++;
X to = mark[28];
X }
X cutfree(cb);
X }
X#endif /* not CRUNCH */
X else if (cbname == '.')
X {
X cb = &named[26];
X cutfree(cb);
X }
X else
X {
X msg("Invalid cut buffer name: \"%c", cbname);
X cbname = '\0';
X return;
X }
X cbname = '\0';
X cb->fd = -1;
X
X /* detect whether we're doing a line mode cut */
X cb->lnmode = lnmode;
X
X /* ---------- */
X
X /* Reporting... */
X if (markidx(from) == 0 && markidx(to) == 0)
X {
X rptlines = markline(to) - markline(from);
X rptlabel = "yanked";
X }
X
X /* ---------- */
X
X /* make sure each block has a physical disk address */
X blksync();
X
X /* find the first block in the cut */
X line = markline(from);
X for (first = 1; line > lnum[first]; first++)
X {
X }
X
X /* fetch text of the block containing that line */
X blkc = scan = blkget(first)->c;
X
X /* find the mark in the block */
X for (l = lnum[first - 1]; ++l < line; )
X {
X while (*scan++ != '\n')
X {
X }
X }
X scan += markidx(from);
X
X /* remember the offset of the start */
X cb->start = scan - blkc;
X
X /* ---------- */
X
X /* find the last block in the cut */
X line = markline(to);
X for (last = first; line > lnum[last]; last++)
X {
X }
X
X /* fetch text of the block containing that line */
X if (last != first)
X {
X blkc = scan = blkget(last)->c;
X }
X else
X {
X scan = blkc;
X }
X
X /* find the mark in the block */
X for (l = lnum[last - 1]; ++l < line; )
X {
X while (*scan++ != '\n')
X {
X }
X }
X if (markline(to) <= nlines)
X {
X scan += markidx(to);
X }
X
X /* remember the offset of the end */
X cb->end = scan - blkc;
X
X /* ------- */
X
X /* remember the physical block numbers of all included blocks */
X cb->nblks = last - first;
X if (cb->end > 0)
X {
X cb->nblks++;
X }
X#ifdef lint
X cb->phys = (short *)0;
X#else
X cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short)));
X#endif
X for (i = 0; i < cb->nblks; i++)
X {
X cb->phys[i] = hdr.n[first++];
X }
X
X#ifndef CRUNCH
X /* if we temporarily inserted text for appending, then delete that
X * text now -- before the user sees it.
X */
X if (delthru)
X {
X line = rptlines;
X delete(from, delthru);
X rptlines = line;
X rptlabel = "yanked";
X }
X#endif /* not CRUNCH */
X}
X
X
Xstatic void readcutblk(cb, blkno)
X struct cutbuf *cb;
X int blkno;
X{
X int fd; /* either tmpfd or cb->fd */
X
X /* decide which fd to use */
X if (cb->fd >= 0)
X {
X fd = cb->fd;
X }
X else
X {
X fd = tmpfd;
X }
X
X /* get the block */
X lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0);
X if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE)
X {
X msg("Error reading back from tmp file for pasting!");
X }
X}
X
X
X/* This function inserts text from a cut buffer, and returns the MARK where
X * insertion ended. Return MARK_UNSET on errors.
X */
XMARK paste(at, after, retend)
X MARK at; /* where to insert the text */
X int after; /* boolean: insert after mark? (rather than before) */
X int retend; /* boolean: return end of text? (rather than start) */
X{
X REG struct cutbuf *cb;
X REG int i;
X
X /* decide which cut buffer to use */
X if (cbname >= 'A' && cbname <= 'Z')
X {
X cb = &named[cbname - 'A'];
X }
X else if (cbname >= 'a' && cbname <= 'z')
X {
X cb = &named[cbname - 'a'];
X }
X else if (cbname >= '1' && cbname <= '9')
X {
X cb = &annon[cbname - '1'];
X }
X else if (cbname == '.')
X {
X cb = &named[26];
X }
X else if (!cbname)
X {
X cb = annon;
X }
X else
X {
X msg("Invalid cut buffer name: \"%c", cbname);
X cbname = '\0';
X return MARK_UNSET;
X }
X
X /* make sure it isn't empty */
X if (cb->nblks == 0)
X {
X if (cbname)
X msg("Cut buffer \"%c is empty", cbname);
X else
X msg("Cut buffer is empty");
X cbname = '\0';
X return MARK_UNSET;
X }
X cbname = '\0';
X
X /* adjust the insertion MARK for "after" and line-mode cuts */
X if (cb->lnmode)
X {
X at &= ~(BLKSIZE - 1);
X if (after)
X {
X at += BLKSIZE;
X }
X }
X else if (after)
X {
X /* careful! if markidx(at) == 0 we might be pasting into an
X * empty line -- so we can't blindly increment "at".
X */
X if (markidx(at) == 0)
X {
X pfetch(markline(at));
X if (plen != 0)
X {
X at++;
X }
X }
X else
X {
X at++;
X }
X }
X
X /* put a copy of the "at" mark in the mark[] array, so it stays in
X * sync with changes made via add().
X */
X mark[27] = at;
X
X /* simple one-block paste? */
X if (cb->nblks == 1)
X {
X /* get the block */
X readcutblk(cb, 0);
X
X /* isolate the text we need within it */
X if (cb->end)
X {
X tmpblk.c[cb->end] = '\0';
X }
X
X /* insert it */
X ChangeText
X {
X add(at, &tmpblk.c[cb->start]);
X }
X }
X else
X {
X /* multi-block paste */
X
X ChangeText
X {
X i = cb->nblks - 1;
X
X /* add text from the last block first */
X if (cb->end > 0)
X {
X readcutblk(cb, i);
X tmpblk.c[cb->end] = '\0';
X add(at, tmpblk.c);
X i--;
X }
X
X /* add intervening blocks */
X while (i > 0)
X {
X readcutblk(cb, i);
X add(at, tmpblk.c);
X i--;
X }
X
X /* add text from the first cut block */
X readcutblk(cb, 0);
X add(at, &tmpblk.c[cb->start]);
X }
X }
X
X /* Reporting... */
X rptlines = markline(mark[27]) - markline(at);
X rptlabel = "pasted";
X
X /* return the mark at the beginning/end of inserted text */
X if (retend)
X {
X return mark[27] - 1L;
X }
X return at;
X}
X
X
X
X
X#ifndef NO_AT
X
X/* This function copies characters from a cut buffer into a string.
X * It returns the number of characters in the cut buffer. If the cut
X * buffer is too large to fit in the string (i.e. if cb2str() returns
X * a number >= size) then the characters will not have been copied.
X * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers.
X */
Xint cb2str(name, buf, size)
X int name; /* the name of a cut-buffer to get: a-z only! */
X char *buf; /* where to put the string */
X unsigned size; /* size of buf */
X{
X REG struct cutbuf *cb;
X REG char *src;
X REG char *dest;
X
X /* decide which cut buffer to use */
X if (name >= 'a' && name <= 'z')
X {
X cb = &named[name - 'a'];
X }
X else
X {
X return -1;
X }
X
X /* if the buffer is empty, return 0 */
X if (cb->nblks == 0)
X {
X return 0;
X }
X
X /* !!! if not a single-block cut, then fail */
X if (cb->nblks != 1)
X {
X return size;
X }
X
X /* if too big, return the size now, without doing anything */
X if (cb->end - cb->start >= size)
X {
X return cb->end - cb->start;
X }
X
X /* get the block */
X readcutblk(cb, 0);
X
X /* isolate the string within that blk */
X if (cb->start == 0)
X {
X tmpblk.c[cb->end] = '\0';
X }
X else
X {
X for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; )
X {
X *dest++ = *src++;
X }
X *dest = '\0';
X }
X
X /* copy the string into the buffer */
X if (buf != tmpblk.c)
X {
X strcpy(buf, tmpblk.c);
X }
X
X /* return the length */
X return cb->end - cb->start;
X}
X#endif
eof
if test `wc -c <cut.c` -ne 13222
then
echo cut.c damaged!
fi
fi
if test -f ex.c -a "$1" != -f
then
echo Will not overwrite ex.c
else
echo Extracting ex.c
sed 's/^X//' >ex.c <<\eof
X/* ex.c */
X
X/* Author:
X * Steve Kirkendall
X * 14407 SW Teal Blvd. #C
X * Beaverton, OR 97005
X * kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains the code for reading ex commands. */
X
X#include "config.h"
X#include <ctype.h>
X#include "vi.h"
X
X#ifndef isascii
X# define isascii(c) !((c)&~0x7f)
X#endif
X
X/* This data type is used to describe the possible argument combinations */
Xtypedef short ARGT;
X#define FROM 1 /* allow a linespec */
X#define TO 2 /* allow a second linespec */
X#define BANG 4 /* allow a ! after the command name */
X#define EXTRA 8 /* allow extra args after command name */
X#define XFILE 16 /* expand wildcards in extra part */
X#define NOSPC 32 /* no spaces allowed in the extra part */
X#define DFLALL 64 /* default file range is 1,$ */
X#define DFLNONE 128 /* no default file range */
X#define NODFL 256 /* do not default to the current file name */
X#define EXRCOK 512 /* can be in a .exrc file */
X#define NL 1024 /* if mode!=MODE_EX, then write a newline first */
X#define PLUS 2048 /* allow a line number, as in ":e +32 foo" */
X#define ZERO 4096 /* allow 0 to be given as a line number */
X#define FILES (XFILE + EXTRA) /* multiple extra files allowed */
X#define WORD1 (EXTRA + NOSPC) /* one extra word allowed */
X#define FILE1 (FILES + NOSPC) /* 1 file allowed, defaults to current file */
X#define NAMEDF (FILE1 + NODFL) /* 1 file allowed, defaults to "" */
X#define NAMEDFS (FILES + NODFL) /* multiple files allowed, default is "" */
X#define RANGE (FROM + TO) /* range of linespecs allowed */
X#define NONE 0 /* no args allowed at all */
X
X/* This array maps ex command names to command codes. The order in which
X * command names are listed below is significant -- ambiguous abbreviations
X * are always resolved to be the first possible match. (e.g. "r" is taken
X * to mean "read", not "rewind", because "read" comes before "rewind")
X */
Xstatic struct
X{
X char *name; /* name of the command */
X CMD code; /* enum code of the command */
X void (*fn)();/* function which executes the command */
X ARGT argt; /* command line arguments permitted/needed/used */
X}
X cmdnames[] =
X{ /* cmd name cmd code function arguments */
X {"append", CMD_APPEND, cmd_append, FROM+ZERO },
X#ifdef DEBUG
X {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL},
X#endif
X {"change", CMD_CHANGE, cmd_append, RANGE },
X {"delete", CMD_DELETE, cmd_delete, RANGE+WORD1 },
X {"edit", CMD_EDIT, cmd_edit, BANG+FILE1+PLUS },
X {"file", CMD_FILE, cmd_file, NAMEDF },
X {"global", CMD_GLOBAL, cmd_global, RANGE+BANG+EXTRA+DFLALL},
X {"insert", CMD_INSERT, cmd_append, FROM },
X {"join", CMD_INSERT, cmd_join, RANGE },
X {"k", CMD_MARK, cmd_mark, FROM+WORD1 },
X {"list", CMD_LIST, cmd_print, RANGE+NL },
X {"move", CMD_MOVE, cmd_move, RANGE+EXTRA },
X {"next", CMD_NEXT, cmd_next, BANG+NAMEDFS },
X {"Next", CMD_PREVIOUS, cmd_next, BANG },
X {"print", CMD_PRINT, cmd_print, RANGE+NL },
X {"quit", CMD_QUIT, cmd_xit, BANG },
X {"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF},
X {"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA },
X {"to", CMD_COPY, cmd_move, RANGE+EXTRA },
X {"undo", CMD_UNDO, cmd_undo, NONE },
X {"vglobal", CMD_VGLOBAL, cmd_global, RANGE+EXTRA+DFLALL},
X {"write", CMD_WRITE, cmd_write, RANGE+BANG+FILE1+DFLALL},
X {"xit", CMD_XIT, cmd_xit, BANG+NL },
X {"yank", CMD_YANK, cmd_delete, RANGE+WORD1 },
X
X {"!", CMD_BANG, cmd_shell, EXRCOK+RANGE+NAMEDFS+DFLNONE+NL},
X {"<", CMD_SHIFTL, cmd_shift, RANGE },
X {">", CMD_SHIFTR, cmd_shift, RANGE },
X {"=", CMD_EQUAL, cmd_file, RANGE },
X {"&", CMD_SUBAGAIN, cmd_substitute, RANGE },
X#ifndef NO_AT
X {"@", CMD_AT, cmd_at, EXTRA },
X#endif
X
X#ifndef NO_ABBR
X {"abbreviate", CMD_ABBR, cmd_abbr, EXRCOK+EXTRA },
X#endif
X {"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS },
X#ifndef NO_ERRLIST
X {"cc", CMD_CC, cmd_make, BANG+FILES },
X#endif
X {"cd", CMD_CD, cmd_cd, EXRCOK+NAMEDF },
X {"copy", CMD_COPY, cmd_move, RANGE+EXTRA },
X#ifndef NO_DIGRAPH
X {"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA},
X#endif
X#ifndef NO_ERRLIST
X {"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF },
X#endif
X {"ex", CMD_EDIT, cmd_edit, BANG+FILE1 },
X {"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA},
X#ifndef NO_MKEXRC
X {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF },
X#endif
X {"number", CMD_NUMBER, cmd_print, RANGE+NL },
X {"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 },
X {"set", CMD_SET, cmd_set, EXRCOK+EXTRA },
X {"shell", CMD_SHELL, cmd_shell, NL },
X {"source", CMD_SOURCE, cmd_source, EXRCOK+NAMEDF },
X {"tag", CMD_TAG, cmd_tag, BANG+WORD1 },
X {"version", CMD_VERSION, cmd_version, EXRCOK+NONE },
X {"visual", CMD_VISUAL, cmd_visual, NONE },
X {"wq", CMD_WQUIT, cmd_xit, NL },
X
X#ifdef DEBUG
X {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL},
X {"validate", CMD_VALIDATE, cmd_validate, BANG+NL },
X#endif
X {"chdir", CMD_CD, cmd_cd, EXRCOK+NAMEDF },
X#ifndef NO_ERRLIST
X {"make", CMD_MAKE, cmd_make, BANG+NAMEDFS },
X#endif
X {"mark", CMD_MARK, cmd_mark, FROM+WORD1 },
X {"previous", CMD_PREVIOUS, cmd_next, BANG },
X {"rewind", CMD_REWIND, cmd_next, BANG },
X {"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA},
X#ifndef NO_ABBR
X {"unabbreviate",CMD_UNABBR, cmd_abbr, EXRCOK+WORD1 },
X#endif
X
X {(char *)0}
X};
X
X
X/* This function parses a search pattern - given a pointer to a / or ?,
X * it replaces the ending / or ? with a \0, and returns a pointer to the
X * stuff that came after the pattern.
X */
Xchar *parseptrn(ptrn)
X REG char *ptrn;
X{
X REG char *scan;
X
X for (scan = ptrn + 1;
X *scan && *scan != *ptrn;
X scan++)
X {
X /* allow backslashed versions of / and ? in the pattern */
X if (*scan == '\\' && scan[1] != '\0')
X {
X scan++;
X }
X }
X if (*scan)
X {
X *scan++ = '\0';
X }
X
X return scan;
X}
X
X
X/* This function parses a line specifier for ex commands */
Xchar *linespec(s, markptr)
X REG char *s; /* start of the line specifier */
X MARK *markptr; /* where to store the mark's value */
X{
X long num;
X REG char *t;
X
X /* parse each ;-delimited clause of this linespec */
X do
X {
X /* skip an initial ';', if any */
X if (*s == ';')
X {
X s++;
X }
X
X /* skip leading spaces */
X while (isascii(*s) && isspace(*s))
X {
X s++;
X }
X
X /* dot means current position */
X if (*s == '.')
X {
X s++;
X *markptr = cursor;
X }
X /* '$' means the last line */
X else if (*s == '$')
X {
X s++;
X *markptr = MARK_LAST;
X }
X /* digit means an absolute line number */
X else if (isascii(*s) && isdigit(*s))
X {
X for (num = 0; isascii(*s) && isdigit(*s); s++)
X {
X num = num * 10 + *s - '0';
X }
X *markptr = MARK_AT_LINE(num);
X }
X /* appostrophe means go to a set mark */
X else if (*s == '\'')
X {
X s++;
X *markptr = m_tomark(cursor, 1L, (int)*s);
X s++;
X }
X /* slash means do a search */
X else if (*s == '/' || *s == '?')
X {
X /* put a '\0' at the end of the search pattern */
X t = parseptrn(s);
X
X /* search for the pattern */
X *markptr &= ~(BLKSIZE - 1);
X if (*s == '/')
X {
X pfetch(markline(*markptr));
X if (plen > 0)
X *markptr += plen - 1;
X *markptr = m_fsrch(*markptr, s);
X }
X else
X {
X *markptr = m_bsrch(*markptr, s);
X }
X
X /* adjust command string pointer */
X s = t;
X }
X
X /* if linespec was faulty, quit now */
X if (!*markptr)
X {
X return s;
X }
X
X /* maybe add an offset */
X t = s;
X if (*t == '-' || *t == '+')
X {
X s++;
X for (num = 0; *s >= '0' && *s <= '9'; s++)
X {
X num = num * 10 + *s - '0';
X }
X if (num == 0)
X {
X num = 1;
X }
X *markptr = m_updnto(*markptr, num, *t);
X }
X } while (*s == ';' || *s == '+' || *s == '-');
X
X return s;
X}
X
X
X
X/* This function reads an ex command and executes it. */
Xvoid ex()
X{
X char cmdbuf[80];
X REG int cmdlen;
X static long oldline;
X
X significant = FALSE;
X oldline = markline(cursor);
X
X while (mode == MODE_EX)
X {
X /* read a line */
X cmdlen = vgets(':', cmdbuf, sizeof cmdbuf);
X if (cmdlen < 0)
X {
X return;
X }
X
X /* if empty line, assume ".+1" */
X if (cmdlen == 0)
X {
X strcpy(cmdbuf, ".+1");
X qaddch('\r');
X clrtoeol();
X }
X else
X {
X addch('\n');
X }
X refresh();
X
X /* parse & execute the command */
X doexcmd(cmdbuf);
X
X /* handle autoprint */
X if (significant || markline(cursor) != oldline)
X {
X significant = FALSE;
X oldline = markline(cursor);
X if (*o_autoprint && mode == MODE_EX)
X {
X cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
X }
X }
X }
X}
X
Xvoid doexcmd(cmdbuf)
X char *cmdbuf; /* string containing an ex command */
X{
X REG char *scan; /* used to scan thru cmdbuf */
X MARK frommark; /* first linespec */
X MARK tomark; /* second linespec */
X REG int cmdlen; /* length of the command name given */
X CMD cmd; /* what command is this? */
X ARGT argt; /* argument types for this command */
X short forceit; /* bang version of a command? */
X REG int cmdidx; /* index of command */
X REG char *build; /* used while copying filenames */
X int iswild; /* boolean: filenames use wildcards? */
X int isdfl; /* using default line ranges? */
X int didsub; /* did we substitute file names for % or # */
X
X
X /* ex commands can't be undone via the shift-U command */
X U_line = 0L;
X
X /* ignore command lines that start with a double-quote */
X if (*cmdbuf == '"')
X {
X return;
X }
X
X /* permit extra colons at the start of the line */
X while (*cmdbuf == ':')
X {
X cmdbuf++;
X }
X
X /* parse the line specifier */
X scan = cmdbuf;
X if (nlines < 1)
X {
X /* no file, so don't allow addresses */
X }
X else if (*scan == '%')
X {
X /* '%' means all lines */
X frommark = MARK_FIRST;
X tomark = MARK_LAST;
X scan++;
X }
X else if (*scan == '0')
X {
X frommark = tomark = MARK_UNSET;
X scan++;
X }
X else
X {
X frommark = cursor;
X scan = linespec(scan, &frommark);
X tomark = frommark;
X if (frommark && *scan == ',')
X {
X scan++;
X scan = linespec(scan, &tomark);
X }
X if (!tomark)
X {
X /* faulty line spec -- fault already described */
X return;
X }
X if (frommark > tomark)
X {
X msg("first address exceeds the second");
X return;
X }
X }
X isdfl = (scan == cmdbuf);
X
X /* skip whitespace */
X while (isascii(*scan) && isspace(*scan))
X {
X scan++;
X }
X
X /* if no command, then just move the cursor to the mark */
X if (!*scan)
X {
X cursor = tomark;
X return;
X }
X
X /* figure out how long the command name is */
X if (isascii(*scan) && !isalpha(*scan))
X {
X cmdlen = 1;
X }
X else
X {
X for (cmdlen = 1;
X !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]);
X cmdlen++)
X {
X }
X }
X
X /* lookup the command code */
X for (cmdidx = 0;
X cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
X cmdidx++)
X {
X }
X argt = cmdnames[cmdidx].argt;
X cmd = cmdnames[cmdidx].code;
X if (cmd == CMD_NULL)
X {
X#if OSK
X msg("Unknown command \"%s\"", scan);
X#else
X msg("Unknown command \"%.*s\"", cmdlen, scan);
X#endif
X return;
X }
X
X /* if the command ended with a bang, set the forceit flag */
X scan += cmdlen;
X if ((argt & BANG) && *scan == '!')
X {
X scan++;
X forceit = 1;
X }
X else
X {
X forceit = 0;
X }
X
X /* skip any more whitespace, to leave scan pointing to arguments */
X while (isascii(*scan) && isspace(*scan))
X {
X scan++;
X }
X
X /* a couple of special cases for filenames */
X if (argt & XFILE)
X {
X /* if names were given, process them */
X if (*scan)
X {
X for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
X {
X switch (*scan)
X {
X case '%':
X if (!*origname)
X {
X msg("No filename to substitute for %%");
X return;
X }
X strcpy(build, origname);
X while (*build)
X {
X build++;
X }
X didsub = TRUE;
X break;
X
X case '#':
X if (!*prevorig)
X {
X msg("No filename to substitute for #");
X return;
X }
X strcpy(build, prevorig);
X while (*build)
X {
X build++;
X }
X didsub = TRUE;
X break;
X
X case '*':
X case '?':
X#if !(MSDOS || TOS)
X case '[':
X case '`':
X case '{': /* } */
X case '$':
X case '~':
X#endif
X *build++ = *scan;
X iswild = TRUE;
X break;
X
X default:
X *build++ = *scan;
X }
X }
X *build = '\0';
X
X if (cmd == CMD_BANG
X || cmd == CMD_READ && tmpblk.c[0] == '!'
X || cmd == CMD_WRITE && tmpblk.c[0] == '!')
X {
X if (didsub)
X {
X if (mode != MODE_EX)
X {
X addch('\n');
X }
X addstr(tmpblk.c);
X addch('\n');
X exrefresh();
X }
X }
X else
X {
X if (iswild && tmpblk.c[0] != '>')
X {
X scan = wildcard(tmpblk.c);
X }
X }
X }
X else /* no names given, maybe assume origname */
X {
X if (!(argt & NODFL))
X {
X strcpy(tmpblk.c, origname);
X }
X else
X {
X *tmpblk.c = '\0';
X }
X }
X
X scan = tmpblk.c;
X }
X
X /* bad arguments? */
X if (!(argt & EXRCOK) && nlines < 1L)
X {
X msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
X return;
X }
X if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
X {
X msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
X return;
X }
X if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
X {
X msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
X return;
X }
X if (!(argt & TO) && tomark != frommark && nlines >= 1L)
X {
X msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
X return;
X }
X if (!(argt & EXTRA) && *scan)
X {
X msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
X return;
X }
X if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
X {
X build = scan;
X#ifndef CRUNCH
X if ((argt & PLUS) && *build == '+')
X {
X while (*build && !(isascii(*build) && isspace(*build)))
X {
X build++;
X }
X while (*build && isascii(*build) && isspace(*build))
X {
X build++;
X }
X }
X#endif /* not CRUNCH */
X for (; *build; build++)
X {
X if (isspace(*build))
X {
X msg("Too many %s to \"%s\" command.",
X (argt & XFILE) ? "filenames" : "arguments",
X cmdnames[cmdidx].name);
X return;
X }
X }
X }
X
X /* some commands have special default ranges */
X if (isdfl && (argt & DFLALL))
X {
X frommark = MARK_FIRST;
X tomark = MARK_LAST;
X }
X else if (isdfl && (argt & DFLNONE))
X {
X frommark = tomark = 0L;
X }
X
X /* write a newline if called from visual mode */
X if ((argt & NL) && mode != MODE_EX && !exwrote)
X {
X addch('\n');
X exrefresh();
X }
X
X /* act on the command */
X (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
X}
X
X
X/* This function executes EX commands from a file. It returns 1 normally, or
X * 0 if the file could not be opened for reading.
X */
Xint doexrc(filename)
X char *filename; /* name of a ".exrc" file */
X{
X int fd; /* file descriptor */
X int len; /* length of the ".exrc" file */
X char buf[MAXRCLEN]; /* buffer, holds the entire .exrc file */
X
X /* open the file, read it, and close */
X fd = open(filename, O_RDONLY);
X if (fd < 0)
X {
X return 0;
X }
X len = tread(fd, buf, MAXRCLEN);
X close(fd);
X
X /* execute the string */
X exstring(buf, len);
X
X return 1;
X}
X
Xvoid exstring(buf, len)
X char *buf; /* the commands to execute */
X int len; /* the length of the string */
X{
X char *cmd; /* start of a command */
X char *end; /* used to search for the end of cmd */
X
X /* find & do each command */
X for (cmd = buf; cmd < &buf[len]; cmd = end + 1)
X {
X /* find the end of the command */
X for (end = cmd; end < &buf[len] && *end != '\n' && *end != '|'; end++)
X {
X }
X *end = '\0';
X
X /* do it */
X doexcmd(cmd);
X }
X}
eof
if test `wc -c <ex.c` -ne 15130
then
echo ex.c damaged!
fi
fi
if test -f input.c -a "$1" != -f
then
echo Will not overwrite input.c
else
echo Extracting input.c
sed 's/^X//' >input.c <<\eof
X/* input.c */
X
X/* Author:
X * Steve Kirkendall
X * 14407 SW Teal Blvd. #C
X * Beaverton, OR 97005
X * kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains the input() function, which implements vi's INPUT mode.
X * It also contains the code that supports digraphs.
X */
X
X#include <ctype.h>
X#include "config.h"
X#include "vi.h"
X
X
X#ifndef NO_DIGRAPH
Xstatic struct _DIG
X{
X struct _DIG *next;
X char key1;
X char key2;
X char dig;
X char save;
X} *digs;
X
Xchar digraph(key1, key2)
X char key1; /* the underlying character */
X char key2; /* the second character */
X{
X int newkey;
X REG struct _DIG *dp;
X
X /* if digraphs are disabled, then just return the new char */
X if (!*o_digraph)
X {
X return key2;
X }
X
X /* remember the new key, so we can return it if this isn't a digraph */
X newkey = key2;
X
X /* sort key1 and key2, so that their original order won't matter */
X if (key1 > key2)
X {
X key2 = key1;
X key1 = newkey;
X }
X
X /* scan through the digraph chart */
X for (dp = digs;
X dp && (dp->key1 != key1 || dp->key2 != key2);
X dp = dp->next)
X {
X }
X
X /* if this combination isn't in there, just use the new key */
X if (!dp)
X {
X return newkey;
X }
X
X /* else use the digraph key */
X return dp->dig;
X}
X
X/* this function lists or defines digraphs */
Xvoid do_digraph(bang, extra)
X int bang;
X char extra[];
X{
X int dig;
X REG struct _DIG *dp;
X struct _DIG *prev;
X static int user_defined = FALSE; /* boolean: are all later digraphs user-defined? */
X char listbuf[8];
X
X /* if "extra" is NULL, then we've reached the end of the built-ins */
X if (!extra)
X {
X user_defined = TRUE;
X return;
X }
X
X /* if no args, then display the existing digraphs */
X if (*extra < ' ')
X {
X listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
X listbuf[7] = '\0';
X for (dig = 0, dp = digs; dp; dp = dp->next)
X {
X if (dp->save || bang)
X {
X dig += 7;
X if (dig >= COLS)
X {
X addch('\n');
X exrefresh();
X dig = 7;
X }
X listbuf[3] = dp->key1;
X listbuf[4] = dp->key2;
X listbuf[6] = dp->dig;
X qaddstr(listbuf);
X }
X }
X addch('\n');
X exrefresh();
X return;
X }
X
X /* make sure we have at least two characters */
X if (!extra[1])
X {
X msg("Digraphs must be composed of two characters");
X return;
X }
X
X /* sort key1 and key2, so that their original order won't matter */
X if (extra[0] > extra[1])
X {
X dig = extra[0];
X extra[0] = extra[1];
X extra[1] = dig;
X }
X
X /* locate the new digraph character */
X for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
X {
X }
X dig = extra[dig];
X if (!bang && dig)
X {
X dig |= 0x80;
X }
X
X /* search for the digraph */
X for (prev = (struct _DIG *)0, dp = digs;
X dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
X prev = dp, dp = dp->next)
X {
X }
X
X /* deleting the digraph? */
X if (!dig)
X {
X if (!dp)
X {
X#ifndef CRUNCH
X msg("%c%c not a digraph", extra[0], extra[1]);
X#endif
X return;
X }
X if (prev)
X prev->next = dp->next;
X else
X digs = dp->next;
X free(dp);
X return;
X }
X
X /* if necessary, create a new digraph struct for the new digraph */
X if (dig && !dp)
X {
X dp = (struct _DIG *)malloc(sizeof *dp);
X if (!dp)
X {
X msg("Out of space in the digraph table");
X return;
X }
X if (prev)
X prev->next = dp;
X else
X digs = dp;
X dp->next = (struct _DIG *)0;
X }
X
X /* assign it the new digraph value */
X dp->key1 = extra[0];
X dp->key2 = extra[1];
X dp->dig = dig;
X dp->save = user_defined;
X}
X
X# ifndef NO_MKEXRC
Xvoid savedigs(fd)
X int fd;
X{
X static char buf[] = "digraph! XX Y\n";
X REG struct _DIG *dp;
X
X for (dp = digs; dp; dp = dp->next)
X {
X if (dp->save)
X {
X buf[9] = dp->key1;
X buf[10] = dp->key2;
X buf[12] = dp->dig;
X write(fd, buf, (unsigned)14);
X }
X }
X}
X# endif
X#endif
X
X
X#ifndef NO_ABBR
Xstatic struct _AB
X{
X struct _AB *next;
X char *large; /* the expanded form */
X char small[1]; /* the abbreviated form (appended to struct) */
X}
X *abbrev;
X
X/* This functions lists or defines abbreviations */
Xvoid do_abbr(extra)
X char *extra;
X{
X int smlen; /* length of the small form */
X int lrg; /* index of the start of the large form */
X REG struct _AB *ab; /* used to move through the abbrev list */
X struct _AB *prev;
X
X /* no arguments? */
X if (!*extra)
X {
X /* list all current abbreviations */
X for (ab = abbrev; ab; ab = ab->next)
X {
X qaddstr("abbr ");
X qaddstr(ab->small);
X qaddch(' ');
X qaddstr(ab->large);
X addch('\n');
X exrefresh();
X }
X return;
X }
X
X /* else one or more arguments. Parse the first & look up in abbrev[] */
X for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++)
X {
X }
X for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next)
X {
X if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen])
X {
X break;
X }
X }
X
X /* locate the start of the large form, if any */
X for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++)
X {
X }
X
X /* only one arg? */
X if (!extra[lrg])
X {
X /* trying to undo an abbreviation which doesn't exist? */
X if (!ab)
X {
X#ifndef CRUNCH
X msg("\"%s\" not an abbreviation", extra);
X#endif
X return;
X }
X
X /* undo the abbreviation */
X if (prev)
X prev->next = ab->next;
X else
X abbrev = ab->next;
X free(ab->large);
X free(ab);
X
X return;
X }
X
X /* multiple args - [re]define an abbreviation */
X if (ab)
X {
X /* redefining - free the old large form */
X free(ab->large);
X }
X else
X {
X /* adding a new definition - make a new struct */
X ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab));
X#ifndef CRUNCH
X if (!ab)
X {
X msg("Out of memory -- Sorry");
X return;
X }
X#endif
X strncpy(ab->small, extra, smlen);
X ab->small[smlen] = '\0';
X ab->next = (struct _AB *)0;
X if (prev)
X prev->next = ab;
X else
X abbrev = ab;
X }
X
X /* store the new form */
X ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1));
X strcpy(ab->large, &extra[lrg]);
X}
X
X
X# ifndef NO_MKEXRC
X/* This function is called from cmd_mkexrc() to save the abbreviations */
Xvoid saveabbr(fd)
X int fd; /* fd to which the :abbr commands should be written */
X{
X REG struct _AB *ab;
X
X for (ab = abbrev; ab; ab = ab->next)
X {
X twrite(fd, "abbr ", 5);
X twrite(fd, ab->small, strlen(ab->small));
X twrite(fd, " ", 1);
X twrite(fd, ab->large, strlen(ab->large));
X twrite(fd, "\n", 1);
X }
X}
X# endif
X
X/* This function should be called before each char is inserted. If the next
X * char is non-alphanumeric and we're at the end of a word, then that word
X * is checked against the abbrev[] array and expanded, if appropriate. Upon
X * returning from this function, the new char still must be inserted.
X */
Xstatic MARK expandabbr(m, ch)
X MARK m; /* the cursor position */
X int ch; /* the character to insert */
X{
X char *word; /* where the word starts */
X int len; /* length of the word */
X REG struct _AB *ab;
X
X /* if no abbreviations are in effect, or ch is aphanumeric, then
X * don't do anything
X */
X if (!abbrev || !isascii(ch) || isalnum(ch))
X {
X return m;
X }
X
X /* see where the preceding word starts */
X pfetch(markline(m));
X for (word = ptext + markidx(m), len = 0;
X --word >= ptext && (!isascii(*word) || isalnum(*word));
X len++)
X {
X }
X word++;
X
X /* if zero-length, then it isn't a word, really -- so nothing */
X if (len == 0)
X {
X return m;
X }
X
X /* look it up in the abbrev list */
X for (ab = abbrev; ab; ab = ab->next)
X {
X if (!strncmp(ab->small, word, len) && !ab->small[len])
X {
X break;
X }
X }
X
X /* not an abbreviation? then do nothing */
X if (!ab)
X {
X return m;
X }
X
X /* else replace the small form with the large form */
X add(m, ab->large);
X delete(m - len, m);
X
X /* return with the cursor after the end of the large form */
X return m - len + strlen(ab->large);
X}
X#endif
X
X
X/* This function allows the user to replace an existing (possibly zero-length)
X * chunk of text with typed-in text. It returns the MARK of the last character
X * that the user typed in.
X */
XMARK input(from, to, when)
X MARK from; /* where to start inserting text */
X MARK to; /* extent of text to delete */
X int when; /* either WHEN_VIINP or WHEN_VIREP */
X{
X char key[2]; /* key char followed by '\0' char */
X char *build; /* used in building a newline+indent string */
X char *scan; /* used while looking at the indent chars of a line */
X MARK m; /* some place in the text */
X#ifndef NO_EXTENSIONS
X int quit = FALSE; /* boolean: are we exiting after this? */
X#endif
X
X#ifdef DEBUG
X /* if "from" and "to" are reversed, complain */
X if (from > to)
X {
X msg("ERROR: input(%ld:%d, %ld:%d)",
X markline(from), markidx(from),
X markline(to), markidx(to));
X return MARK_UNSET;
X }
X#endif
X
X key[1] = 0;
X
X /* if we're replacing text with new text, save the old stuff */
X /* (Alas, there is no easy way to save text for replace mode) */
X if (from != to)
X {
X cut(from, to);
X }
X
X ChangeText
X {
X /* if doing a dot command, then reuse the previous text */
X if (doingdot)
X {
X /* delete the text that's there now */
X if (from != to)
X {
X delete(from, to);
X }
X
X /* insert the previous text */
X cutname('.');
X cursor = paste(from, FALSE, TRUE) + 1L;
X }
X else /* interactive version */
X {
X /* if doing a change within the line... */
X if (from != to && markline(from) == markline(to))
X {
X /* mark the end of the text with a "$" */
X change(to - 1, to, "$");
X }
X else
X {
X /* delete the old text right off */
X if (from != to)
X {
X delete(from, to);
X }
X to = from;
X }
X
X /* handle autoindent of the first line, maybe */
X cursor = from;
X if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0)
X {
X /* Only autoindent blank lines. */
X pfetch(markline(cursor));
X if (plen == 0)
X {
X /* Okay, we really want to autoindent */
X pfetch(markline(cursor) - 1L);
X for (scan = ptext, build = tmpblk.c;
X *scan == ' ' || *scan == '\t';
X )
X {
X *build++ = *scan++;
X }
X if (build > tmpblk.c)
X {
X *build = '\0';
X add(cursor, tmpblk.c);
X cursor += (build - tmpblk.c);
X }
X }
X }
X
X /* repeatedly add characters from the user */
X for (;;)
X {
X /* Get a character */
X redraw(cursor, TRUE);
X#ifdef DEBUG
X msg("cursor=%ld.%d, to=%ld.%d",
X markline(cursor), markidx(cursor),
X markline(to), markidx(to));
X#endif
X key[0] = getkey(when);
X
X /* if whitespace & wrapmargin is set & we're
X * past the warpmargin, then change the
X * whitespace character into a newline
X */
X if ((*key == ' ' || *key == '\t')
X && *o_wrapmargin != 0)
X {
X pfetch(markline(cursor));
X if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff))
X {
X *key = '\n';
X }
X }
X
X /* process it */
X switch (*key)
X {
X#ifndef NO_EXTENSIONS
X case 0: /* special movement mapped keys */
X *key = getkey(0);
X switch (*key)
X {
X case 'h': m = m_left(cursor, 0L); break;
X case 'j':
X case 'k': m = m_updnto(cursor, 0L, *key); break;
X case 'l': m = cursor + 1; break;
X case 'b': m = m_bword(cursor, 0L); break;
X case 'w': m = m_fword(cursor, 0L); break;
X case '^': m = m_front(cursor, 0L); break;
X case '$': m = m_rear(cursor, 0L); break;
X case ctrl('B'):
X case ctrl('F'):
X m = m_scroll(cursor, 0L, *key); break;
X case 'x': m = v_xchar(cursor, 0L); break;
X case 'i': m = to = from = cursor; break;
X default: m = MARK_UNSET; break;
X }
X /* adjust the moved cursor */
X m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0));
X if (*key == '$' || (*key == 'l' && m <= cursor))
X {
X m++;
X }
X /* if the cursor is reasonable, use it */
X if (m == MARK_UNSET)
X {
X beep();
X }
X else
X {
X if (to > cursor)
X {
X delete(cursor, to);
X redraw(cursor, TRUE);
X }
X from = to = cursor = m;
X }
X break;
X
X case ctrl('Z'):
X if (getkey(0) == ctrl('Z'))
X {
X quit = TRUE;
X goto BreakBreak;
X }
X break;
X#endif
X
X case ctrl('['):
X#ifndef NO_ABBR
X cursor = expandabbr(cursor, ctrl('['));
X#endif
X goto BreakBreak;
X
X case ctrl('U'):
X if (markline(cursor) == markline(from))
X {
X cursor = from;
X }
X else
X {
X cursor &= ~(BLKSIZE - 1);
X }
X break;
X
X case ctrl('D'):
X case ctrl('T'):
X if (to > cursor)
X {
X delete(cursor, to);
X }
X mark[27] = cursor;
X cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, "");
X if (mark[27])
X {
X cursor = mark[27];
X }
X else
X {
X cursor = m_front(cursor, 0L);
X }
X to = cursor;
X break;
X
X case '\b':
X if (cursor <= from)
X {
X beep();
X }
X else if (markidx(cursor) == 0)
X {
X cursor -= BLKSIZE;
X pfetch(markline(cursor));
X cursor += plen;
X }
X else
X {
X cursor--;
X }
X break;
X
X case ctrl('W'):
X m = m_bword(cursor, 1L);
X if (markline(m) == markline(cursor) && m >= from)
X {
X cursor = m;
X if (from > cursor)
X {
X from = cursor;
X }
X }
X else
X {
X beep();
X }
X break;
X
X case '\n':
X#if OSK
X case '\l':
X#else
X case '\r':
X#endif
X#ifndef NO_ABBR
X cursor = expandabbr(cursor, '\n');
X#endif
X build = tmpblk.c;
X *build++ = '\n';
X if (*o_autoindent)
X {
X /* figure out indent for next line */
X pfetch(markline(cursor));
X for (scan = ptext; *scan == ' ' || *scan == '\t'; )
X {
X *build++ = *scan++;
X }
X
X /* remove indent from this line, if blank */
X if (!*scan && plen > 0)
X {
X to = cursor &= ~(BLKSIZE - 1);
X delete(cursor, cursor + plen);
X }
X }
X *build = 0;
X if (cursor >= to && when != WHEN_VIREP)
X {
X add(cursor, tmpblk.c);
X }
X else
X {
X change(cursor, to, tmpblk.c);
X }
X redraw(cursor, TRUE);
X to = cursor = (cursor & ~(BLKSIZE - 1))
X + BLKSIZE
X + (int)(build - tmpblk.c) - 1;
X break;
X
X case ctrl('A'):
X case ctrl('P'):
X if (cursor < to)
X {
X delete(cursor, to);
X }
X if (*key == ctrl('A'))
X {
X cutname('.');
X }
X to = cursor = paste(cursor, FALSE, TRUE) + 1L;
X break;
X
X case ctrl('V'):
X if (cursor >= to && when != WHEN_VIREP)
X {
X add(cursor, "^");
X }
X else
X {
X change(cursor, to, "^");
X to = cursor + 1;
X }
X redraw(cursor, TRUE);
X *key = getkey(0);
X if (*key == '\n')
X {
X /* '\n' too hard to handle */
X#if OSK
X *key = '\l';
X#else
X *key = '\r';
X#endif
X }
X change(cursor, cursor + 1, key);
X cursor++;
X if (cursor > to)
X {
X to = cursor;
X }
X break;
X
X case ctrl('L'):
X case ctrl('R'):
X redraw(MARK_UNSET, FALSE);
X break;
X
X default:
X if (cursor >= to && when != WHEN_VIREP)
X {
X#ifndef NO_ABBR
X cursor = expandabbr(cursor, *key);
X#endif
X add(cursor, key);
X cursor++;
X to = cursor;
X }
X else
X {
X pfetch(markline(cursor));
X if (markidx(cursor) == plen)
X {
X#ifndef NO_ABBR
X cursor = expandabbr(cursor, *key);
X#endif
X add(cursor, key);
X }
X else
X {
X#ifndef NO_DIGRAPH
X *key = digraph(ptext[markidx(cursor)], *key);
X#endif
X#ifndef NO_ABBR
X cursor = expandabbr(cursor, *key);
X#endif
X change(cursor, cursor + 1, key);
X }
X cursor++;
X }
X#ifndef NO_SHOWMATCH
X /* show matching "({[" if neceesary */
X if (*o_showmatch && strchr(")}]", *key))
X {
X redraw(cursor, TRUE);
X m = m_match(cursor - 1, 0L);
X if (markline(m) >= topline
X && markline(m) <= botline)
X {
X redraw(m, TRUE);
X refresh();
X sleep(1);
X }
X }
X#endif
X } /* end switch(*key) */
X } /* end for(;;) */
XBreakBreak:;
X
X /* delete any excess characters */
X if (cursor < to)
X {
X delete(cursor, to);
X }
X
X } /* end if doingdot else */
X
X } /* end ChangeText */
X
X /* put the new text into a cut buffer for possible reuse */
X if (!doingdot)
X {
X blksync();
X cutname('.');
X cut(from, cursor);
X }
X
X /* move to last char that we inputted, unless it was newline */
X if (markidx(cursor) != 0)
X {
X cursor--;
X }
X redraw(cursor, FALSE);
X
X#ifndef NO_EXTENSIONS
X if (quit)
X {
X /* if this is a nested "do", then cut it short */
X abortdo();
X
X /* exit, unless we can't write out the file */
X cursor = v_xit(cursor, 0L, 'Z');
X }
X#endif
X
X rptlines = 0L;
X return cursor;
X}
eof
if test `wc -c <input.c` -ne 16368
then
echo input.c damaged!
fi
fi
if test -f main.c -a "$1" != -f
then
echo Will not overwrite main.c
else
echo Extracting main.c
sed 's/^X//' >main.c <<\eof
X/* main.c */
X
X/* Author:
X * Steve Kirkendall
X * 14407 SW Teal Blvd. #C
X * Beaverton, OR 97005
X * kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains the main() function of vi */
X
X#include "config.h"
X#include <signal.h>
X#include <setjmp.h>
X#include "vi.h"
X
Xextern trapint(); /* defined below */
Xextern char *getenv();
Xjmp_buf jmpenv;
X
X#ifndef NO_DIGRAPH
Xstatic init_digraphs();
X#endif
X
X/*---------------------------------------------------------------------*/
X
Xvoid main(argc, argv)
X int argc;
X char *argv[];
X{
X int i;
X char *cmd = (char *)0;
X char *tag = (char *)0;
X char *err = (char *)0;
X char *str;
X#if MSDOS || TOS
X char firstarg[256];
X#else
X char *firstarg;
X#endif
X
X /* set mode to MODE_VI or MODE_EX depending on program name */
X switch (argv[0][strlen(argv[0]) - 1])
X {
X case 'x': /* "ex" */
X mode = MODE_EX;
X break;
X
X case 'w': /* "view" */
X mode = MODE_VI;
X *o_readonly = TRUE;
X break;
X#ifndef NO_EXTENSIONS
X case 't': /* "edit" or "input" */
X mode = MODE_VI;
X *o_inputmode = TRUE;
X break;
X#endif
X default: /* "vi" or "elvis" */
X mode = MODE_VI;
X }
X
X#ifndef DEBUG
X# ifdef SIGQUIT
X /* normally, we ignore SIGQUIT. SIGINT is trapped later */
X signal(SIGQUIT, SIG_IGN);
X# endif
X#endif
X
X /* temporarily ignore SIGINT */
X signal(SIGINT, SIG_IGN);
X
X /* start curses */
X initscr();
X cbreak();
X noecho();
X scrollok(stdscr, TRUE);
X
X /* initialize the options */
X initopts();
X
X /* map the arrow keys. The KU,KD,KL,and KR variables correspond to
X * the :ku=: (etc.) termcap capabilities. The variables are defined
X * as part of the curses package.
X */
X if (has_KU) mapkey(has_KU, "k", WHEN_VICMD|WHEN_INMV, "<Up>");
X if (has_KD) mapkey(has_KD, "j", WHEN_VICMD|WHEN_INMV, "<Down>");
X if (has_KL) mapkey(has_KL, "h", WHEN_VICMD|WHEN_INMV, "<Left>");
X if (has_KR) mapkey(has_KR, "l", WHEN_VICMD|WHEN_INMV, "<Right>");
X if (has_HM) mapkey(has_HM, "^", WHEN_VICMD|WHEN_INMV, "<Home>");
X if (has_EN) mapkey(has_EN, "$", WHEN_VICMD|WHEN_INMV, "<End>");
X if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, "<PgUp>");
X if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, "<PgDn>");
X#if MSDOS
X if (*o_pcbios)
X {
X mapkey("#R", "i", WHEN_VICMD|WHEN_INMV, "<Insrt>");
X mapkey("#S", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
X mapkey("#s", "B", WHEN_VICMD|WHEN_INMV, "^<left>");
X mapkey("#t", "W", WHEN_VICMD|WHEN_INMV, "^<right>");
X }
X#else
X if (ERASEKEY != '\177')
X {
X mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, "<Del>");
X }
X#endif
X
X#ifndef NO_DIGRAPH
X init_digraphs();
X#endif /* NO_DIGRAPH */
X
X /* process any flags */
X for (i = 1; i < argc && *argv[i] == '-'; i++)
X {
X switch (argv[i][1])
X {
X case 'R': /* readonly */
X *o_readonly = TRUE;
X break;
X
X case 'r': /* recover */
X msg("Use the `virec` program to recover lost files");
X endmsgs();
X refresh();
X endwin();
X exit(0);
X break;
X
X case 't': /* tag */
X if (argv[i][2])
X {
X tag = argv[i] + 2;
X }
X else
X {
X i++;
X tag = argv[i];
X }
X break;
X
X case 'v': /* vi mode */
X mode = MODE_VI;
X break;
X
X case 'e': /* ex mode */
X mode = MODE_EX;
X break;
X#ifndef NO_EXTENSIONS
X case 'i': /* input mode */
X *o_inputmode = TRUE;
X break;
X#endif
X#ifndef NO_ERRLIST
X case 'm': /* use "errlist" as the errlist */
X if (argv[i][2])
X {
X err = argv[i] + 2;
X }
X else if (i + 1 < argc)
X {
X i++;
X err = argv[i];
X }
X else
X {
X err = "";
X }
X break;
X#endif
X default:
X msg("Ignoring unknown flag \"%s\"", argv[i]);
X }
X }
X
X /* if we were given an initial ex command, save it... */
X if (i < argc && *argv[i] == '+')
X {
X if (argv[i][1])
X {
X cmd = argv[i++] + 1;
X }
X else
X {
X cmd = "$"; /* "vi + file" means start at EOF */
X i++;
X }
X }
X
X /* the remaining args are file names. */
X nargs = argc - i;
X if (nargs > 0)
X {
X#if ! ( MSDOS || TOS )
X firstarg = argv[i];
X#endif
X strcpy(args, argv[i]);
X while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args)
X {
X strcat(args, " ");
X strcat(args, argv[i]);
X }
X }
X#if ! ( MSDOS || TOS )
X else
X {
X firstarg = "";
X }
X#endif
X argno = 0;
X
X#if MSDOS || TOS
X if (nargs > 0)
X {
X strcpy(args, wildcard(args));
X nargs = 1;
X for (i = 0; args[i]; i++)
X {
X if (args[i] == ' ')
X {
X nargs++;
X }
X }
X for (i = 0; args[i] && args[i] != ' '; i++)
X {
X firstarg[i] = args[i];
X }
X firstarg[i] = '\0';
X }
X else
X {
X firstarg[0] = '\0';
X }
X#endif
X
X /* perform the .exrc files and EXINIT environment variable */
X#ifdef SYSEXRC
X doexrc(SYSEXRC);
X#endif
X#ifdef HMEXRC
X str = getenv("HOME");
X if (str)
X {
X sprintf(tmpblk.c, "%s%c%s", str, SLASH, HMEXRC);
X doexrc(tmpblk.c);
X }
X#endif
X doexrc(EXRC);
X#ifdef EXINIT
X str = getenv(EXINIT);
X if (str)
X {
X exstring(str, strlen(str));
X }
X#endif
X
X /* search for a tag (or an error) now, if desired */
X blkinit();
X if (tag)
X {
X cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag);
X }
X#ifndef NO_ERRLIST
X else if (err)
X {
X cmd_errlist(MARK_FIRST, MARK_FIRST, CMD_ERRLIST, 0, err);
X }
X#endif
X
X /* if no tag/err, or tag failed, then start with first arg */
X if (tmpfd < 0 && tmpstart(firstarg) == 0 && *origname)
X {
X ChangeText
X {
X }
X clrflag(file, MODIFIED);
X }
X
X /* now we do the immediate ex command that we noticed before */
X if (cmd)
X {
X doexcmd(cmd);
X }
X
X /* repeatedly call ex() or vi() (depending on the mode) until the
X * mode is set to MODE_QUIT
X */
X while (mode != MODE_QUIT)
X {
X if (setjmp(jmpenv))
X {
X /* Maybe we just aborted a change? */
X abortdo();
X }
X#if TURBOC
X signal(SIGINT, (void(*)()) trapint);
X#else
X signal(SIGINT, trapint);
X#endif
X
X switch (mode)
X {
X case MODE_VI:
X vi();
X break;
X
X case MODE_EX:
X ex();
X break;
X#ifdef DEBUG
X default:
X msg("mode = %d?", mode);
X mode = MODE_QUIT;
X#endif
X }
X }
X
X /* free up the cut buffers */
X cutend();
X
X /* end curses */
X#ifndef NO_CURSORSHAPE
X if (has_CQ)
X do_CQ();
X#endif
X endmsgs();
X move(LINES - 1, 0);
X clrtoeol();
X refresh();
X endwin();
X
X exit(0);
X /*NOTREACHED*/
X}
X
X
X/*ARGSUSED*/
Xint trapint(signo)
X int signo;
X{
X resume_curses(FALSE);
X abortdo();
X#if OSK
X sigmask(-1);
X#endif
X#if TURBO_C
X signal(signo, (void (*)())trapint);
X#else
X signal(signo, trapint);
X#endif
X longjmp(jmpenv, 1);
X
X return 0;
X}
X
X
X#ifndef NO_DIGRAPH
X
X/* This stuff us used to build the default digraphs table. */
Xstatic char digtable[][4] =
X{
X# if CS_IBMPC
X "C,\200", "u\"\1", "e'\2", "a^\3",
X "a\"\4", "a`\5", "a@\6", "c,\7",
X "e^\10", "e\"\211", "e`\12", "i\"\13",
X "i^\14", "i`\15", "A\"\16", "A@\17",
X "E'\20", "ae\21", "AE\22", "o^\23",
X "o\"\24", "o`\25", "u^\26", "u`\27",
X "y\"\30", "O\"\31", "U\"\32", "a'\240",
X "i'!", "o'\"", "u'#", "n~$",
X "N~%", "a-&", "o-'", "~?(",
X "~!-", "\"<.", "\">/",
X# if CS_SPECIAL
X "2/+", "4/,", "^+;", "^q<",
X "^c=", "^r>", "^t?", "pp]",
X "^^^", "oo_", "*a`", "*ba",
X "*pc", "*Sd", "*se", "*uf",
X "*tg", "*Ph", "*Ti", "*Oj",
X "*dk", "*Hl", "*hm", "*En",
X "*No", "eqp", "pmq", "ger",
X "les", "*It", "*iu", "*/v",
X "*=w", "sq{", "^n|", "^2}",
X "^3~", "^_\377",
X# endif /* CS_SPECIAL */
X# endif /* CS_IBMPC */
X# if CS_LATIN1
X "~!!", "a-*", "\">+", "o-:",
X "\"<>", "~??",
X
X "A`@", "A'A", "A^B", "A~C",
X "A\"D", "A at E", "AEF", "C,G",
X "E`H", "E'I", "E^J", "E\"K",
X "I`L", "I'M", "I^N", "I\"O",
X "-DP", "N~Q", "O`R", "O'S",
X "O^T", "O~U", "O\"V", "O/X",
X "U`Y", "U'Z", "U^[", "U\"\\",
X "Y'_",
X
X "a``", "a'a", "a^b", "a~c",
X "a\"d", "a at e", "aef", "c,g",
X "e`h", "e'i", "e^j", "e\"k",
X "i`l", "i'm", "i^n", "i\"o",
X "-dp", "n~q", "o`r", "o's",
X "o^t", "o~u", "o\"v", "o/x",
X "u`y", "u'z", "u^{", "u\"|",
X "y'~",
X# endif /* CS_LATIN1 */
X ""
X};
X
Xstatic init_digraphs()
X{
X int i;
X
X for (i = 0; *digtable[i]; i++)
X {
X do_digraph(FALSE, digtable[i]);
X }
X do_digraph(FALSE, (char *)0);
X}
X#endif /* NO_DIGRAPH */
eof
if test `wc -c <main.c` -ne 7778
then
echo main.c damaged!
fi
fi
if test -f misc.c -a "$1" != -f
then
echo Will not overwrite misc.c
else
echo Extracting misc.c
sed 's/^X//' >misc.c <<\eof
X/* misc.c */
X
X/* Author:
X * Steve Kirkendall
X * 14407 SW Teal Blvd. #C
X * Beaverton, OR 97005
X * kirkenda at cs.pdx.edu
X */
X
X
X/* This file contains functions which didn't seem happy anywhere else */
X
X#include "config.h"
X#include "vi.h"
X
X
X/* find a particular line & return a pointer to a copy of its text */
Xchar *fetchline(line)
X long line; /* line number of the line to fetch */
X{
X int i;
X REG char *scan; /* used to search for the line in a BLK */
X long l; /* line number counter */
X static BLK buf; /* holds ONLY the selected line (as string) */
X REG char *cpy; /* used while copying the line */
X static long nextline; /* } These four variables are used */
X static long chglevel; /* } to implement a shortcut when */
X static char *nextscan; /* } consecutive lines are fetched */
X static long nextlnum; /* } */
X
X /* can we do a shortcut? */
X if (changes == chglevel && line == nextline)
X {
X scan = nextscan;
X }
X else
X {
X /* scan lnum[] to determine which block its in */
X for (i = 1; line > lnum[i]; i++)
X {
X }
X nextlnum = lnum[i];
X
X /* fetch text of the block containing that line */
X scan = blkget(i)->c;
X
X /* find the line in the block */
X for (l = lnum[i - 1]; ++l < line; )
X {
X while (*scan++ != '\n')
X {
X }
X }
X }
X
X /* copy it into a block by itself, with no newline */
X for (cpy = buf.c; *scan != '\n'; )
X {
X *cpy++ = *scan++;
X }
X *cpy = '\0';
X
X /* maybe speed up the next call to fetchline() ? */
X if (line < nextlnum)
X {
X nextline = line + 1;
X chglevel = changes;
X nextscan = scan + 1;
X }
X else
X {
X nextline = 0;
X }
X
X /* Calls to fetchline() interfere with calls to pfetch(). Make sure
X * that pfetch() resets itself on its next invocation.
X */
X pchgs = 0L;
X
X /* Return a pointer to the line's text */
X return buf.c;
X}
X
X
X/* error message from the regexp code */
Xvoid regerror(txt)
X char *txt; /* an error message */
X{
X msg("RE error: %s", txt);
X}
X
X/* This function is equivelent to the pfetch() macro */
Xvoid pfetch(l)
X long l; /* line number of line to fetch */
X{
X if(l != pline || changes != pchgs)
X {
X pline = (l);
X ptext = fetchline(pline);
X plen = strlen(ptext);
X pchgs = changes;
X }
X}
eof
if test `wc -c <misc.c` -ne 2166
then
echo misc.c damaged!
fi
fi
exit 0
-------------------------------------------------------------------------------
Steve Kirkendall kirkenda at cs.pdx.edu Grad student at Portland State U.
More information about the Alt.sources
mailing list