Path: utzoo!utgpu!cs.utexas.edu!uunet!zephyr.ens.tek.com!tektronix!nosun!qiclab!pdxgate!eecs.cs.pdx.edu!kirkenda From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Newsgroups: alt.sources Subject: Elvis 1.4, part 5 of 8 Message-ID: <829@pdxgate.UUCP> Date: 3 Dec 90 21:32:42 GMT Sender: news@pdxgate.UUCP Reply-To: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Organization: Portland State University, Portland, OR Lines: 3030 # --------------------------- 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@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 cut.c <<\eof X/* cut.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains function which manipulate the cut buffers. */ X X#include "config.h" X#include "vi.h" X#if TURBOC X#include /* needed for getpid */ X#endif X#if TOS X#include 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 ex.c <<\eof X/* ex.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the code for reading ex commands. */ X X#include "config.h" X#include 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 input.c <<\eof X/* input.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the input() function, which implements vi's INPUT mode. X * It also contains the code that supports digraphs. X */ X X#include 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 main.c <<\eof X/* main.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the main() function of vi */ X X#include "config.h" X#include X#include 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, ""); X if (has_KD) mapkey(has_KD, "j", WHEN_VICMD|WHEN_INMV, ""); X if (has_KL) mapkey(has_KL, "h", WHEN_VICMD|WHEN_INMV, ""); X if (has_KR) mapkey(has_KR, "l", WHEN_VICMD|WHEN_INMV, ""); X if (has_HM) mapkey(has_HM, "^", WHEN_VICMD|WHEN_INMV, ""); X if (has_EN) mapkey(has_EN, "$", WHEN_VICMD|WHEN_INMV, ""); X if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, ""); X if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, ""); X#if MSDOS X if (*o_pcbios) X { X mapkey("#R", "i", WHEN_VICMD|WHEN_INMV, ""); X mapkey("#S", "x", WHEN_VICMD|WHEN_INMV, ""); X mapkey("#s", "B", WHEN_VICMD|WHEN_INMV, "^"); X mapkey("#t", "W", WHEN_VICMD|WHEN_INMV, "^"); X } X#else X if (ERASEKEY != '\177') X { X mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, ""); 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@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@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 misc.c <<\eof X/* misc.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains functions which 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