mirror of https://github.com/tildeclub/ex-vi.git
1098 lines
40 KiB
C
1098 lines
40 KiB
C
/*
|
|
* This code contains changes by
|
|
* Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
|
|
*
|
|
* Conditions 1, 2, and 4 and the no-warranty notice below apply
|
|
* to these changes.
|
|
*
|
|
*
|
|
* Copyright (c) 1980, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*
|
|
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* Redistributions of source code and documentation must retain the
|
|
* above copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed or owned by Caldera
|
|
* International, Inc.
|
|
* Neither the name of Caldera International, Inc. nor the names of
|
|
* other contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
|
|
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef lint
|
|
#ifdef DOSCCS
|
|
static char sccsid[] = "@(#)ex_vops2.c 1.34 (gritter) 1/12/05";
|
|
#endif
|
|
#endif
|
|
|
|
/* from ex_vops2.c 6.8 (Berkeley) 6/7/85 */
|
|
|
|
#include "ex.h"
|
|
#include "ex_tty.h"
|
|
#include "ex_vis.h"
|
|
|
|
/*
|
|
* Low level routines for operations sequences,
|
|
* and mostly, insert mode (and a subroutine
|
|
* to read an input line, including in the echo area.)
|
|
*/
|
|
extern char *vUA1, *vUA2; /* mjm: extern; also in ex_vops.c */
|
|
extern char *vUD1, *vUD2; /* mjm: extern; also in ex_vops.c */
|
|
|
|
/*
|
|
* Obleeperate characters in hardcopy
|
|
* open with \'s.
|
|
*/
|
|
void
|
|
bleep(register int i, char *cp)
|
|
{
|
|
|
|
i -= column(cp);
|
|
do
|
|
putchar('\\' | QUOTE);
|
|
while (--i >= 0);
|
|
rubble = 1;
|
|
}
|
|
|
|
/*
|
|
* Common code for middle part of delete
|
|
* and change operating on parts of lines.
|
|
*/
|
|
int
|
|
vdcMID(void)
|
|
{
|
|
register char *cp;
|
|
|
|
squish();
|
|
setLAST();
|
|
if (FIXUNDO)
|
|
vundkind = VCHNG, CP(vutmp, linebuf);
|
|
if (wcursor < cursor)
|
|
cp = wcursor, wcursor = cursor, cursor = cp;
|
|
vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
|
|
return (column(wcursor + skipleft(linebuf, wcursor)));
|
|
}
|
|
|
|
/*
|
|
* Take text from linebuf and stick it
|
|
* in the VBSIZE buffer BUF. Used to save
|
|
* deleted text of part of line.
|
|
*/
|
|
void
|
|
takeout(cell *BUF)
|
|
{
|
|
register char *cp;
|
|
|
|
if (wcursor < linebuf)
|
|
wcursor = linebuf;
|
|
if (cursor == wcursor) {
|
|
beep();
|
|
return;
|
|
}
|
|
if (wcursor < cursor) {
|
|
cp = wcursor;
|
|
wcursor = cursor;
|
|
cursor = cp;
|
|
}
|
|
setBUF(BUF);
|
|
if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
|
|
beep();
|
|
}
|
|
|
|
/*
|
|
* Are we at the end of the printed representation of the
|
|
* line? Used internally in hardcopy open.
|
|
*/
|
|
int
|
|
ateopr(void)
|
|
{
|
|
register int i, c;
|
|
register cell *cp = vtube[destline] + destcol;
|
|
|
|
for (i = WCOLS - destcol; i > 0; i--) {
|
|
c = *cp++;
|
|
if (c == 0)
|
|
return (1);
|
|
if (c != ' ' && (c & QUOTE) == 0)
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
showmode(int mode)
|
|
{
|
|
int sdc = destcol, sdl = destline;
|
|
char *ocurs, *str;
|
|
|
|
if (value(SHOWMODE) == 0 || TCOLUMNS <= 20 || state == ONEOPEN
|
|
|| state == HARDOPEN || vmacp != NULL)
|
|
return;
|
|
ocurs = cursor;
|
|
fixech();
|
|
vgoto(WECHO, TCOLUMNS - 20);
|
|
switch (mode) {
|
|
case 0: str = catgets(catd, 1, 227,
|
|
" ");
|
|
break;
|
|
case 'A': /*FALLTHROUGH*/
|
|
case 'a': str = catgets(catd, 1, 228,
|
|
"AAPPEND MODE");
|
|
break;
|
|
case 'C': /*FALLTHROUGH*/
|
|
case 'c': str = catgets(catd, 1, 229,
|
|
"CCHANGE MODE");
|
|
break;
|
|
case 'O': /*FALLTHROUGH*/
|
|
case 'o': str = catgets(catd, 1, 230,
|
|
"OOPEN MODE");
|
|
break;
|
|
case 'R': str = catgets(catd, 1, 231,
|
|
"RREPLACE MODE");
|
|
break;
|
|
case 'r': str = catgets(catd, 1, 232,
|
|
"rREPLACE 1 CHAR");
|
|
break;
|
|
default: str = catgets(catd, 1, 233,
|
|
"IINSERT MODE");
|
|
}
|
|
if (value(TERSE))
|
|
putchar(str[0]);
|
|
else
|
|
printf("%s",&str[1]);
|
|
vgoto(sdl, sdc);
|
|
cursor = ocurs;
|
|
splitw = 0;
|
|
}
|
|
|
|
/*
|
|
* Append.
|
|
*
|
|
* This routine handles the top level append, doing work
|
|
* as each new line comes in, and arranging repeatability.
|
|
* It also handles append with repeat counts, and calculation
|
|
* of autoindents for new lines.
|
|
*/
|
|
bool vaifirst;
|
|
bool gobbled;
|
|
char *ogcursor;
|
|
|
|
/*
|
|
* The addtext() and addto() routines combined, accepting a single
|
|
* cell character.
|
|
*/
|
|
void
|
|
addc(cell c)
|
|
{
|
|
register cell *cp = INS;
|
|
|
|
if (vglobp)
|
|
return;
|
|
if ((cp[0] & (QUOTE|TRIM)) != OVERBUF) {
|
|
if (cellen(cp) + 2 >= VBSIZE) {
|
|
cp[0] = OVERBUF;
|
|
lastcmd[0] = 0;
|
|
} else {
|
|
while (*cp)
|
|
cp++;
|
|
*cp++ = c;
|
|
*cp++ = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
vappend(int ch, int cnt, int indent)
|
|
/* int ch; /\* mjm: char --> int */
|
|
{
|
|
register int i = 0;
|
|
register char *gcursor;
|
|
bool escape;
|
|
int repcnt, savedoomed;
|
|
short oldhold = hold;
|
|
#ifdef SIGWINCH
|
|
sigset_t set, oset;
|
|
#endif
|
|
|
|
/*
|
|
* Before a move in hardopen when the line is dirty
|
|
* or we are in the middle of the printed representation,
|
|
* we retype the line to the left of the cursor so the
|
|
* insert looks clean.
|
|
*/
|
|
if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
|
|
rubble = 1;
|
|
gcursor = cursor;
|
|
i = *gcursor;
|
|
*gcursor = ' ';
|
|
wcursor = gcursor;
|
|
vmove(0);
|
|
*gcursor = i;
|
|
}
|
|
vaifirst = indent == 0;
|
|
|
|
showmode(ch);
|
|
|
|
/*
|
|
* Handle replace character by (eventually)
|
|
* limiting the number of input characters allowed
|
|
* in the vgetline routine.
|
|
*/
|
|
if (ch == 'r')
|
|
repcnt = 2;
|
|
else
|
|
repcnt = 0;
|
|
|
|
/*
|
|
* If an autoindent is specified, then
|
|
* generate a mixture of blanks to tabs to implement
|
|
* it and place the cursor after the indent.
|
|
* Text read by the vgetline routine will be placed in genbuf,
|
|
* so the indent is generated there.
|
|
*/
|
|
if (value(AUTOINDENT) && indent != 0) {
|
|
gcursor = genindent(indent);
|
|
*gcursor = 0;
|
|
vgotoCL(qcolumn(cursor + skipright(cursor, linebuf), genbuf));
|
|
} else {
|
|
gcursor = genbuf;
|
|
*gcursor = 0;
|
|
if (ch == 'o')
|
|
vfixcurs();
|
|
}
|
|
|
|
/*
|
|
* Prepare for undo. Pointers delimit inserted portion of line.
|
|
*/
|
|
vUA1 = vUA2 = cursor;
|
|
|
|
/*
|
|
* If we are not in a repeated command and a ^@ comes in
|
|
* then this means the previous inserted text.
|
|
* If there is none or it was too long to be saved,
|
|
* then beep() and also arrange to undo any damage done
|
|
* so far (e.g. if we are a change.)
|
|
*/
|
|
if ((vglobp && *vglobp == 0) || peekbr()) {
|
|
if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
|
|
beep();
|
|
if (!splitw)
|
|
ungetkey('u');
|
|
doomed = 0;
|
|
hold = oldhold;
|
|
showmode(0);
|
|
return;
|
|
}
|
|
/*
|
|
* Unread input from INS.
|
|
* An escape will be generated at end of string.
|
|
* Hold off n^^2 type update on dumb terminals.
|
|
*/
|
|
vglobp = INS;
|
|
hold |= HOLDQIK;
|
|
} else if (vglobp == 0)
|
|
/*
|
|
* Not a repeated command, get
|
|
* a new inserted text for repeat.
|
|
*/
|
|
INS[0] = 0;
|
|
|
|
/*
|
|
* For wrapmargin to hack away second space after a '.'
|
|
* when the first space caused a line break we keep
|
|
* track that this happened in gobblebl, which says
|
|
* to gobble up a blank silently.
|
|
*/
|
|
gobblebl = 0;
|
|
|
|
#ifdef SIGWINCH
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGWINCH);
|
|
sigprocmask(SIG_BLOCK, &set, &oset);
|
|
#endif
|
|
/*
|
|
* Text gathering loop.
|
|
* New text goes into genbuf starting at gcursor.
|
|
* cursor preserves place in linebuf where text will eventually go.
|
|
*/
|
|
if (*cursor == 0 || state == CRTOPEN)
|
|
hold |= HOLDROL;
|
|
for (;;) {
|
|
if (ch == 'r' && repcnt == 0)
|
|
escape = 0;
|
|
else {
|
|
gcursor = vgetline(repcnt, gcursor, &escape, ch);
|
|
|
|
/*
|
|
* After an append, stick information
|
|
* about the ^D's and ^^D's and 0^D's in
|
|
* the repeated text buffer so repeated
|
|
* inserts of stuff indented with ^D as backtab's
|
|
* can work.
|
|
*/
|
|
if (HADUP)
|
|
addc('^');
|
|
else if (HADZERO)
|
|
addc('0');
|
|
while (CDCNT > 0)
|
|
#ifndef BIT8
|
|
addc('\204'), CDCNT--;
|
|
#else
|
|
addc(OVERBUF), CDCNT--;
|
|
#endif
|
|
|
|
if (gobbled)
|
|
addc(' ');
|
|
addtext(ogcursor);
|
|
}
|
|
repcnt = 0;
|
|
|
|
/*
|
|
* Smash the generated and preexisting indents together
|
|
* and generate one cleanly made out of tabs and spaces
|
|
* if we are using autoindent.
|
|
*/
|
|
if (!vaifirst && value(AUTOINDENT)) {
|
|
i = fixindent(indent);
|
|
if (!HADUP)
|
|
indent = i;
|
|
gcursor = strend(genbuf);
|
|
}
|
|
|
|
/*
|
|
* Limit the repetition count based on maximum
|
|
* possible line length; do output implied
|
|
* by further count (> 1) and cons up the new line
|
|
* in linebuf.
|
|
*/
|
|
cnt = vmaxrep(ch, cnt);
|
|
CP(gcursor + skipright(ogcursor, gcursor), cursor);
|
|
do {
|
|
CP(cursor, genbuf);
|
|
if (cnt > 1) {
|
|
int oldhold = hold;
|
|
|
|
Outchar = vinschar;
|
|
hold |= HOLDQIK;
|
|
printf("%s", genbuf);
|
|
hold = oldhold;
|
|
Outchar = vputchar;
|
|
}
|
|
cursor += gcursor - genbuf;
|
|
} while (--cnt > 0);
|
|
endim();
|
|
vUA2 = cursor;
|
|
if (escape != '\n')
|
|
CP(cursor, gcursor + skipright(ogcursor, gcursor));
|
|
|
|
/*
|
|
* If doomed characters remain, clobber them,
|
|
* and reopen the line to get the display exact.
|
|
*/
|
|
if (state != HARDOPEN) {
|
|
DEPTH(vcline) = 0;
|
|
savedoomed = doomed;
|
|
if (doomed > 0) {
|
|
register int cind = cindent();
|
|
|
|
physdc(cind, cind + doomed);
|
|
doomed = 0;
|
|
}
|
|
i = vreopen(LINE(vcline), lineDOT(), vcline);
|
|
#ifdef TRACE
|
|
if (trace)
|
|
fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
|
|
#endif
|
|
if (ch == 'R')
|
|
doomed = savedoomed;
|
|
}
|
|
|
|
/*
|
|
* All done unless we are continuing on to another line.
|
|
*/
|
|
if (escape != '\n')
|
|
break;
|
|
|
|
/*
|
|
* Set up for the new line.
|
|
* First save the current line, then construct a new
|
|
* first image for the continuation line consisting
|
|
* of any new autoindent plus the pushed ahead text.
|
|
*/
|
|
showmode(0);
|
|
killU();
|
|
addc(gobblebl ? ' ' : '\n');
|
|
vsave();
|
|
cnt = 1;
|
|
if (value(AUTOINDENT)) {
|
|
#ifdef LISPCODE
|
|
if (value(LISP))
|
|
indent = lindent(dot + 1);
|
|
else
|
|
#endif
|
|
if (!HADUP && vaifirst)
|
|
indent = whitecnt(linebuf);
|
|
vaifirst = 0;
|
|
strcLIN(vpastwh(gcursor + 1));
|
|
gcursor = genindent(indent);
|
|
*gcursor = 0;
|
|
if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
|
|
gcursor = genbuf;
|
|
CP(gcursor, linebuf);
|
|
} else {
|
|
CP(genbuf, gcursor + skipright(ogcursor, gcursor));
|
|
gcursor = genbuf;
|
|
}
|
|
|
|
/*
|
|
* If we started out as a single line operation and are now
|
|
* turning into a multi-line change, then we had better yank
|
|
* out dot before it changes so that undo will work
|
|
* correctly later.
|
|
*/
|
|
if (FIXUNDO && vundkind == VCHNG) {
|
|
vremote(1, yank, 0);
|
|
undap1--;
|
|
}
|
|
|
|
/*
|
|
* Now do the append of the new line in the buffer,
|
|
* and update the display. If slowopen
|
|
* we don't do very much.
|
|
*/
|
|
vdoappend(genbuf);
|
|
vundkind = VMANYINS;
|
|
vcline++;
|
|
if (state != VISUAL)
|
|
vshow(dot, NOLINE);
|
|
else {
|
|
i += LINE(vcline - 1);
|
|
vopen(dot, i);
|
|
if (value(SLOWOPEN))
|
|
vscrap();
|
|
else
|
|
vsync1(LINE(vcline));
|
|
}
|
|
strcLIN(gcursor);
|
|
*gcursor = 0;
|
|
cursor = linebuf;
|
|
vgotoCL(qcolumn(cursor + skipleft(ogcursor, cursor), genbuf));
|
|
showmode(ch);
|
|
}
|
|
|
|
/*
|
|
* All done with insertion, position the cursor
|
|
* and sync the screen.
|
|
*/
|
|
showmode(0);
|
|
hold = oldhold;
|
|
if (cursor > linebuf)
|
|
cursor += skipleft(linebuf, cursor);
|
|
if (state != HARDOPEN)
|
|
vsyncCL();
|
|
else if (cursor > linebuf)
|
|
back1();
|
|
doomed = 0;
|
|
wcursor = cursor;
|
|
vmove(0);
|
|
#ifdef SIGWINCH
|
|
sigprocmask(SIG_SETMASK, &oset, NULL);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Subroutine for vgetline to back up a single character position,
|
|
* backwards around end of lines (vgoto can't hack columns which are
|
|
* less than 0 in general).
|
|
*/
|
|
void
|
|
back1(void)
|
|
{
|
|
|
|
vgoto(destline - 1, WCOLS + destcol - 1);
|
|
}
|
|
|
|
#define gappend(c) { \
|
|
int _c = c; \
|
|
xgappend(_c, &gcursor); \
|
|
}
|
|
|
|
static void
|
|
xgappend(int c, char **gp)
|
|
{
|
|
if (*gp >= &genbuf[MAXBSIZE-mb_cur_max-1]) {
|
|
beep();
|
|
return;
|
|
}
|
|
#ifdef MB
|
|
if (mb_cur_max > 1 && !(c & INVBIT)) {
|
|
char mb[MB_LEN_MAX];
|
|
int i, n;
|
|
n = wctomb(mb, c);
|
|
for (i = 0; i < n; i++)
|
|
*(*gp)++ = mb[i];
|
|
} else
|
|
#endif /* MB */
|
|
*(*gp)++ = c & 0377;
|
|
}
|
|
|
|
/*
|
|
* Get a line into genbuf after gcursor.
|
|
* Cnt limits the number of input characters
|
|
* accepted and is used for handling the replace
|
|
* single character command. Aescaped is the location
|
|
* where we stick a termination indicator (whether we
|
|
* ended with an ESCAPE or a newline/return.
|
|
*
|
|
* We do erase-kill type processing here and also
|
|
* are careful about the way we do this so that it is
|
|
* repeatable. (I.e. so that your kill doesn't happen,
|
|
* when you repeat an insert if it was escaped with \ the
|
|
* first time you did it. commch is the command character
|
|
* involved, including the prompt for readline.
|
|
*/
|
|
char *
|
|
vgetline(int cnt, char *gcursor, bool *aescaped, int commch)
|
|
{
|
|
register int c, ch;
|
|
register char *cp;
|
|
int x, y, iwhite, backsl=0;
|
|
cell *iglobp;
|
|
char cstr[2];
|
|
int (*OO)() = Outchar;
|
|
|
|
/*
|
|
* Clear the output state and counters
|
|
* for autoindent backwards motion (counts of ^D, etc.)
|
|
* Remember how much white space at beginning of line so
|
|
* as not to allow backspace over autoindent.
|
|
*/
|
|
*aescaped = 0;
|
|
ogcursor = gcursor;
|
|
flusho();
|
|
CDCNT = 0;
|
|
HADUP = 0;
|
|
HADZERO = 0;
|
|
gobbled = 0;
|
|
iwhite = whitecnt(genbuf);
|
|
iglobp = vglobp;
|
|
|
|
/*
|
|
* Carefully avoid using vinschar in the echo area.
|
|
*/
|
|
if (splitw)
|
|
Outchar = vputchar;
|
|
else {
|
|
Outchar = vinschar;
|
|
vprepins();
|
|
}
|
|
for (;;) {
|
|
backsl = 0;
|
|
if (gobblebl)
|
|
gobblebl--;
|
|
if (cnt != 0) {
|
|
cnt--;
|
|
if (cnt == 0)
|
|
goto vadone;
|
|
}
|
|
c = getkey();
|
|
if (c != ATTN)
|
|
c &= (QUOTE|TRIM);
|
|
ch = c;
|
|
maphopcnt = 0;
|
|
if (vglobp == 0 && Peekkey == 0 && commch != 'r')
|
|
while ((ch = map(c, immacs)) != c) {
|
|
c = ch;
|
|
if (!value(REMAP))
|
|
break;
|
|
if (++maphopcnt > 256)
|
|
error(catgets(catd, 1, 234,
|
|
"Infinite macro loop"));
|
|
}
|
|
if (!iglobp) {
|
|
|
|
/*
|
|
* Erase-kill type processing.
|
|
* Only happens if we were not reading
|
|
* from untyped input when we started.
|
|
* Map users erase to ^H, kill to -1 for switch.
|
|
*/
|
|
if (c == tty.c_cc[VERASE])
|
|
c = CTRL('h');
|
|
else if (c == tty.c_cc[VKILL])
|
|
c = -1;
|
|
if (c == ATTN)
|
|
goto case_ATTN;
|
|
switch (c) {
|
|
|
|
/*
|
|
* ^? Interrupt drops you back to visual
|
|
* command mode with an unread interrupt
|
|
* still in the input buffer.
|
|
*
|
|
* ^\ Quit does the same as interrupt.
|
|
* If you are a ex command rather than
|
|
* a vi command this will drop you
|
|
* back to command mode for sure.
|
|
*/
|
|
case QUIT:
|
|
case_ATTN:
|
|
ungetkey(c);
|
|
goto vadone;
|
|
|
|
/*
|
|
* ^H Backs up a character in the input.
|
|
*
|
|
* BUG: Can't back around line boundaries.
|
|
* This is hard because stuff has
|
|
* already been saved for repeat.
|
|
*/
|
|
case CTRL('h'):
|
|
bakchar:
|
|
cp = gcursor + skipleft(ogcursor, gcursor);
|
|
if (cp < ogcursor) {
|
|
if (splitw) {
|
|
/*
|
|
* Backspacing over readecho
|
|
* prompt. Pretend delete but
|
|
* don't beep.
|
|
*/
|
|
ungetkey(c);
|
|
goto vadone;
|
|
}
|
|
beep();
|
|
continue;
|
|
}
|
|
goto vbackup;
|
|
|
|
/*
|
|
* ^W Back up a white/non-white word.
|
|
*/
|
|
case CTRL('w'):
|
|
wdkind = 1;
|
|
for (cp = gcursor; cp > ogcursor
|
|
&& isspace(cp[-1]&0377); cp--)
|
|
continue;
|
|
for (c = wordch(cp - 1);
|
|
cp > ogcursor && wordof(c, cp - 1); cp--)
|
|
continue;
|
|
goto vbackup;
|
|
|
|
/*
|
|
* users kill Kill input on this line, back to
|
|
* the autoindent.
|
|
*/
|
|
case -1:
|
|
cp = ogcursor;
|
|
vbackup:
|
|
if (cp == gcursor) {
|
|
beep();
|
|
continue;
|
|
}
|
|
endim();
|
|
*cp = 0;
|
|
c = cindent();
|
|
vgotoCL(qcolumn(cursor +
|
|
skipleft(linebuf, cursor), genbuf));
|
|
if (doomed >= 0)
|
|
doomed += c - cindent();
|
|
gcursor = cp;
|
|
continue;
|
|
|
|
/*
|
|
* \ Followed by erase or kill
|
|
* maps to just the erase or kill.
|
|
*/
|
|
case '\\':
|
|
x = destcol, y = destline;
|
|
putchar('\\');
|
|
vcsync();
|
|
c = getkey();
|
|
if (c == tty.c_cc[VERASE]
|
|
|| c == tty.c_cc[VKILL])
|
|
{
|
|
vgoto(y, x);
|
|
if (doomed >= 0)
|
|
doomed++;
|
|
goto def;
|
|
}
|
|
ungetkey(c), c = '\\';
|
|
backsl = 1;
|
|
break;
|
|
|
|
/*
|
|
* ^Q Super quote following character
|
|
* Only ^@ is verboten (trapped at
|
|
* a lower level) and \n forces a line
|
|
* split so doesn't really go in.
|
|
*
|
|
* ^V Synonym for ^Q
|
|
*/
|
|
case CTRL('q'):
|
|
case CTRL('v'):
|
|
x = destcol, y = destline;
|
|
putchar('^');
|
|
vgoto(y, x);
|
|
c = getkey();
|
|
if (c != NL) {
|
|
if (doomed >= 0)
|
|
doomed++;
|
|
goto def;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we get a blank not in the echo area
|
|
* consider splitting the window in the wrapmargin.
|
|
*/
|
|
if (c != NL && !splitw) {
|
|
if (c == ' ' && gobblebl) {
|
|
gobbled = 1;
|
|
continue;
|
|
}
|
|
if (value(WRAPMARGIN) &&
|
|
(outcol >= OCOLUMNS - value(WRAPMARGIN) ||
|
|
backsl && outcol==0) &&
|
|
commch != 'r') {
|
|
/*
|
|
* At end of word and hit wrapmargin.
|
|
* Move the word to next line and keep going.
|
|
*/
|
|
wdkind = 1;
|
|
gappend(c);
|
|
if (backsl)
|
|
gappend(getkey());
|
|
*gcursor = 0;
|
|
/*
|
|
* Find end of previous word if we are past it.
|
|
*/
|
|
for (cp=gcursor; cp>ogcursor
|
|
&& isspace(cp[-1]&0377); cp--)
|
|
;
|
|
if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
|
|
/*
|
|
* Find beginning of previous word.
|
|
*/
|
|
for (; cp>ogcursor && !isspace(cp[-1]&0377); cp--)
|
|
;
|
|
if (cp <= ogcursor) {
|
|
/*
|
|
* There is a single word that
|
|
* is too long to fit. Just
|
|
* let it pass, but beep for
|
|
* each new letter to warn
|
|
* the luser.
|
|
*/
|
|
c = *--gcursor;
|
|
*gcursor = 0;
|
|
beep();
|
|
goto dontbreak;
|
|
}
|
|
/*
|
|
* Save it for next line.
|
|
*/
|
|
macpush(cp, 0);
|
|
cp--;
|
|
}
|
|
macpush("\n", 0);
|
|
/*
|
|
* Erase white space before the word.
|
|
*/
|
|
while (cp > ogcursor && isspace(cp[-1]&0377))
|
|
cp--; /* skip blank */
|
|
gobblebl = 3;
|
|
goto vbackup;
|
|
}
|
|
dontbreak:;
|
|
}
|
|
|
|
/*
|
|
* Word abbreviation mode.
|
|
*/
|
|
cstr[0] = c;
|
|
if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
|
|
int wdtype, abno;
|
|
|
|
cstr[1] = 0;
|
|
wdkind = 1;
|
|
cp = gcursor + skipleft(ogcursor, gcursor);
|
|
for (wdtype = wordch(cp - 1);
|
|
cp > ogcursor && wordof(wdtype, cp - 1); cp--)
|
|
;
|
|
*gcursor = 0;
|
|
for (abno=0; abbrevs[abno].mapto; abno++) {
|
|
if (!abbrevs[abno].hadthis &&
|
|
eq(cp, abbrevs[abno].cap)) {
|
|
abbrevs[abno].hadthis++;
|
|
macpush(cstr, 0);
|
|
macpush(abbrevs[abno].mapto, 0);
|
|
goto vbackup;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef BIT8
|
|
if (c == OVERBUF)
|
|
goto btrp;
|
|
#endif
|
|
switch (c) {
|
|
|
|
/*
|
|
* ^M Except in repeat maps to \n.
|
|
*/
|
|
case CR:
|
|
if (vglobp)
|
|
goto def;
|
|
c = '\n';
|
|
/* presto chango ... */
|
|
|
|
/*
|
|
* \n Start new line.
|
|
*/
|
|
case NL:
|
|
*aescaped = c;
|
|
goto vadone;
|
|
|
|
/*
|
|
* escape End insert unless repeat and more to repeat.
|
|
*/
|
|
case ESCAPE:
|
|
if (lastvgk)
|
|
goto def;
|
|
goto vadone;
|
|
|
|
/*
|
|
* ^D Backtab.
|
|
* ^T Software forward tab.
|
|
*
|
|
* Unless in repeat where this means these
|
|
* were superquoted in.
|
|
*/
|
|
case CTRL('d'):
|
|
case CTRL('t'):
|
|
if (vglobp)
|
|
goto def;
|
|
/* fall into ... */
|
|
|
|
/*
|
|
* ^D|QUOTE Is a backtab (in a repeated command).
|
|
*/
|
|
#ifndef BIT8
|
|
case CTRL('d') | QUOTE:
|
|
#else
|
|
btrp:
|
|
#endif
|
|
*gcursor = 0;
|
|
cp = vpastwh(genbuf);
|
|
c = whitecnt(genbuf);
|
|
if (ch == CTRL('t')) {
|
|
/*
|
|
* ^t just generates new indent replacing
|
|
* current white space rounded up to soft
|
|
* tab stop increment.
|
|
*/
|
|
if (cp != gcursor)
|
|
/*
|
|
* BUG: Don't hack ^T except
|
|
* right after initial
|
|
* white space.
|
|
*/
|
|
continue;
|
|
cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
|
|
ogcursor = cp;
|
|
goto vbackup;
|
|
}
|
|
/*
|
|
* ^D works only if we are at the (end of) the
|
|
* generated autoindent. We count the ^D for repeat
|
|
* purposes.
|
|
*/
|
|
if (c == iwhite && c != 0)
|
|
if (cp == gcursor) {
|
|
iwhite = backtab(c);
|
|
CDCNT++;
|
|
ogcursor = cp = genindent(iwhite);
|
|
goto vbackup;
|
|
} else if (&cp[1] == gcursor &&
|
|
(*cp == '^' || *cp == '0')) {
|
|
/*
|
|
* ^^D moves to margin, then back
|
|
* to current indent on next line.
|
|
*
|
|
* 0^D moves to margin and then
|
|
* stays there.
|
|
*/
|
|
HADZERO = *cp == '0';
|
|
ogcursor = cp = genbuf;
|
|
HADUP = 1 - HADZERO;
|
|
CDCNT = 1;
|
|
endim();
|
|
back1();
|
|
vputchar(' ');
|
|
goto vbackup;
|
|
}
|
|
if (vglobp && vglobp - iglobp >= 2 &&
|
|
(vglobp[-2] == '^' || vglobp[-2] == '0')
|
|
&& gcursor == ogcursor + 1)
|
|
goto bakchar;
|
|
continue;
|
|
|
|
default:
|
|
/*
|
|
* Possibly discard control inputs.
|
|
*/
|
|
if (!vglobp && junk(c)) {
|
|
beep();
|
|
continue;
|
|
}
|
|
def:
|
|
if (!backsl) {
|
|
/* int cnt; */
|
|
putchar(c);
|
|
flush();
|
|
}
|
|
if (gcursor > &genbuf[LBSIZE - 2])
|
|
error(catgets(catd, 1, 235, "Line too long"));
|
|
gappend(c & TRIM);
|
|
vcsync();
|
|
if (value(SHOWMATCH) && !iglobp)
|
|
if (c == ')' || c == '}')
|
|
lsmatch(gcursor);
|
|
continue;
|
|
}
|
|
}
|
|
vadone:
|
|
*gcursor = 0;
|
|
if (Outchar != termchar)
|
|
Outchar = OO;
|
|
endim();
|
|
return (gcursor);
|
|
}
|
|
|
|
char *vsplitpt;
|
|
|
|
/*
|
|
* Append the line in buffer at lp
|
|
* to the buffer after dot.
|
|
*/
|
|
void
|
|
vdoappend(char *lp)
|
|
{
|
|
register int oing = inglobal;
|
|
|
|
vsplitpt = lp;
|
|
inglobal = 1;
|
|
ignore(append(vgetsplit, dot));
|
|
inglobal = oing;
|
|
}
|
|
|
|
/*
|
|
* Subroutine for vdoappend to pass to append.
|
|
*/
|
|
int
|
|
vgetsplit(void)
|
|
{
|
|
|
|
if (vsplitpt == 0)
|
|
return (EOF);
|
|
strcLIN(vsplitpt);
|
|
vsplitpt = 0;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Vmaxrep determines the maximum repetitition factor
|
|
* allowed that will yield total line length less than
|
|
* LBSIZE characters and also does hacks for the R command.
|
|
*/
|
|
int
|
|
vmaxrep(int ch, register int cnt)
|
|
{
|
|
register int len, replen;
|
|
|
|
if (cnt > LBSIZE - 2)
|
|
cnt = LBSIZE - 2;
|
|
replen = strlen(genbuf);
|
|
if (ch == 'R') {
|
|
len = strlen(cursor);
|
|
if (replen < len)
|
|
len = replen;
|
|
#ifdef MB
|
|
if (mb_cur_max > 1) {
|
|
char *cp, *gp;
|
|
int c, g;
|
|
for (gp = genbuf, g = 0; *gp; g++)
|
|
gp += wskipright(genbuf, gp);
|
|
for (cp = cursor, c = 0; c < g; c++)
|
|
cp += wskipright(cursor, cp);
|
|
CP(cursor, cp);
|
|
} else
|
|
#endif /* MB */
|
|
CP(cursor, cursor + len);
|
|
vUD2 += len;
|
|
}
|
|
len = strlen(linebuf);
|
|
if (len + cnt * replen <= LBSIZE - 2)
|
|
return (cnt);
|
|
cnt = (LBSIZE - 2 - len) / replen;
|
|
if (cnt == 0) {
|
|
vsave();
|
|
error(catgets(catd, 1, 236, "Line too long"));
|
|
}
|
|
return (cnt);
|
|
}
|