mirror of
https://github.com/tildeclub/ex-vi.git
synced 2026-06-15 01:10:17 +00:00
was followed by an ex command that also yanked or deleted some text, a following 'p' vi command pasted the text affected by the former vi command. It now pastes the text of the last yank or delete even if that was an ex command.
1064 lines
23 KiB
C
1064 lines
23 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_vops.c 1.22 (gritter) 1/2/05";
|
|
#endif
|
|
#endif
|
|
|
|
/* from ex_vops.c 7.7 (Berkeley) 6/7/85 */
|
|
|
|
#include "ex.h"
|
|
#include "ex_tty.h"
|
|
#include "ex_vis.h"
|
|
|
|
/*
|
|
* This file defines the operation sequences which interface the
|
|
* logical changes to the file buffer with the internal and external
|
|
* display representations.
|
|
*/
|
|
|
|
/*
|
|
* Undo.
|
|
*
|
|
* Undo is accomplished in two ways. We often for small changes in the
|
|
* current line know how (in terms of a change operator) how the change
|
|
* occurred. Thus on an intelligent terminal we can undo the operation
|
|
* by another such operation, using insert and delete character
|
|
* stuff. The pointers vU[AD][12] index the buffer vutmp when this
|
|
* is possible and provide the necessary information.
|
|
*
|
|
* The other case is that the change involved multiple lines or that
|
|
* we have moved away from the line or forgotten how the change was
|
|
* accomplished. In this case we do a redisplay and hope that the
|
|
* low level optimization routines (which don't look for winning
|
|
* via insert/delete character) will not lose too badly.
|
|
*/
|
|
char *vUA1, *vUA2;
|
|
char *vUD1, *vUD2;
|
|
|
|
void
|
|
vUndo(void)
|
|
{
|
|
|
|
/*
|
|
* Avoid UU which clobbers ability to do u.
|
|
*/
|
|
if (vundkind == VCAPU || vUNDdot != dot) {
|
|
beep();
|
|
return;
|
|
}
|
|
CP(vutmp, linebuf);
|
|
vUD1 = linebuf; vUD2 = strend(linebuf);
|
|
putmk1(dot, vUNDsav);
|
|
getDOT();
|
|
vUA1 = linebuf; vUA2 = strend(linebuf);
|
|
vundkind = VCAPU;
|
|
if (state == ONEOPEN || state == HARDOPEN) {
|
|
vjumpto(dot, vUNDcurs, 0);
|
|
return;
|
|
}
|
|
vdirty(vcline, 1);
|
|
vsyncCL();
|
|
cursor = linebuf;
|
|
vfixcurs();
|
|
}
|
|
|
|
void
|
|
vundo (
|
|
int show /* if true update the screen */
|
|
)
|
|
{
|
|
register int cnt;
|
|
register line *addr;
|
|
register char *cp;
|
|
char temp[LBSIZE];
|
|
bool savenote;
|
|
int (*OO)(int);
|
|
short oldhold = hold;
|
|
|
|
switch (vundkind) {
|
|
|
|
case VMANYINS:
|
|
wcursor = 0;
|
|
addr1 = undap1;
|
|
addr2 = undap2 - 1;
|
|
vsave();
|
|
YANKreg('1');
|
|
notecnt = 0;
|
|
/* fall into ... */
|
|
|
|
case VMANY:
|
|
case VMCHNG:
|
|
vsave();
|
|
addr = dot - vcline;
|
|
notecnt = 1;
|
|
if (undkind == UNDPUT && undap1 == undap2) {
|
|
beep();
|
|
break;
|
|
}
|
|
/*
|
|
* Undo() call below basically replaces undap1 to undap2-1
|
|
* with dol through unddol-1. Hack screen image to
|
|
* reflect this replacement.
|
|
*/
|
|
if (show)
|
|
if (undkind == UNDMOVE)
|
|
vdirty(0, TLINES);
|
|
else
|
|
vreplace(undap1 - addr, undap2 - undap1,
|
|
undkind == UNDPUT ? 0 : unddol - dol);
|
|
savenote = notecnt;
|
|
undo(1);
|
|
if (show && (vundkind != VMCHNG || addr != dot))
|
|
killU();
|
|
vundkind = VMANY;
|
|
cnt = dot - addr;
|
|
if (cnt < 0 || cnt > vcnt || state != VISUAL) {
|
|
if (show)
|
|
vjumpto(dot, NOSTR, '.');
|
|
break;
|
|
}
|
|
if (!savenote)
|
|
notecnt = 0;
|
|
if (show) {
|
|
vcline = cnt;
|
|
vrepaint(vmcurs);
|
|
}
|
|
vmcurs = 0;
|
|
break;
|
|
|
|
case VCHNG:
|
|
case VCAPU:
|
|
vundkind = VCHNG;
|
|
CP(temp, vutmp);
|
|
CP(vutmp, linebuf);
|
|
doomed = column(vUA2 - 1) - column(vUA1 - 1);
|
|
strcLIN(temp);
|
|
cp = vUA1; vUA1 = vUD1; vUD1 = cp;
|
|
cp = vUA2; vUA2 = vUD2; vUD2 = cp;
|
|
if (!show)
|
|
break;
|
|
cursor = vUD1;
|
|
if (state == HARDOPEN) {
|
|
doomed = 0;
|
|
vsave();
|
|
vopen(dot, WBOT);
|
|
vnline(cursor);
|
|
break;
|
|
}
|
|
/*
|
|
* Pseudo insert command.
|
|
*/
|
|
vcursat(cursor);
|
|
OO = Outchar; Outchar = vinschar; hold |= HOLDQIK;
|
|
vprepins();
|
|
temp[vUA2 - linebuf] = 0;
|
|
for (cp = &temp[vUA1 - linebuf]; *cp;)
|
|
putchar(*cp++);
|
|
Outchar = OO; hold = oldhold;
|
|
endim();
|
|
physdc(cindent(), cindent() + doomed);
|
|
doomed = 0;
|
|
vdirty(vcline, 1);
|
|
vsyncCL();
|
|
if (cursor > linebuf && cursor >= strend(linebuf))
|
|
cursor += skipleft(linebuf, cursor);
|
|
vfixcurs();
|
|
break;
|
|
|
|
case VNONE:
|
|
beep();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Routine to handle a change inside a macro.
|
|
* Fromvis is true if we were called from a visual command (as
|
|
* opposed to an ex command). This has nothing to do with being
|
|
* in open/visual mode as :s/foo/bar is not fromvis.
|
|
*/
|
|
void
|
|
vmacchng(int fromvis)
|
|
{
|
|
line *savedot, *savedol;
|
|
char *savecursor;
|
|
char savelb[LBSIZE];
|
|
int nlines, more;
|
|
/* register line *a1, *a2; */
|
|
/* char ch; */ /* DEBUG */
|
|
|
|
if (!inopen)
|
|
return;
|
|
if (!vmacp)
|
|
vch_mac = VC_NOTINMAC;
|
|
#ifdef TRACE
|
|
if (trace)
|
|
fprintf(trace, "vmacchng, vch_mac=%d, linebuf='%s', *dot=%o\n", vch_mac, linebuf, *dot);
|
|
#endif
|
|
if (vmacp && fromvis)
|
|
vsave();
|
|
#ifdef TRACE
|
|
if (trace)
|
|
fprintf(trace, "after vsave, linebuf='%s', *dot=%o\n", linebuf, *dot);
|
|
#endif
|
|
switch(vch_mac) {
|
|
case VC_NOCHANGE:
|
|
vch_mac = VC_ONECHANGE;
|
|
break;
|
|
case VC_ONECHANGE:
|
|
/* Save current state somewhere */
|
|
#ifdef TRACE
|
|
vudump("before vmacchng hairy case");
|
|
#endif
|
|
savedot = dot; savedol = dol; savecursor = cursor;
|
|
CP(savelb, linebuf);
|
|
nlines = dol - zero;
|
|
while ((line *) endcore - truedol < nlines)
|
|
morelines();
|
|
copyw(truedol+1, zero+1, nlines);
|
|
truedol += nlines;
|
|
|
|
#ifdef TRACE
|
|
visdump("before vundo");
|
|
#endif
|
|
/* Restore state as it was at beginning of macro */
|
|
vundo(0);
|
|
#ifdef TRACE
|
|
visdump("after vundo");
|
|
vudump("after vundo");
|
|
#endif
|
|
|
|
/* Do the saveall we should have done then */
|
|
saveall();
|
|
#ifdef TRACE
|
|
vudump("after saveall");
|
|
#endif
|
|
|
|
/* Restore current state from where saved */
|
|
more = savedol - dol; /* amount we shift everything by */
|
|
if (more)
|
|
(*(more>0 ? copywR : copyw))(savedol+1, dol+1, truedol-dol);
|
|
unddol += more; truedol += more; undap2 += more;
|
|
|
|
truedol -= nlines;
|
|
copyw(zero+1, truedol+1, nlines);
|
|
dot = savedot; dol = savedol ; cursor = savecursor;
|
|
CP(linebuf, savelb);
|
|
vch_mac = VC_MANYCHANGE;
|
|
|
|
/* Arrange that no further undo saving happens within macro */
|
|
otchng = tchng; /* Copied this line blindly - bug? */
|
|
inopen = -1; /* no need to save since it had to be 1 or -1 before */
|
|
vundkind = VMANY;
|
|
#ifdef TRACE
|
|
vudump("after vmacchng");
|
|
#endif
|
|
break;
|
|
case VC_NOTINMAC:
|
|
case VC_MANYCHANGE:
|
|
/* Nothing to do for various reasons. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize undo information before an append.
|
|
*/
|
|
void
|
|
vnoapp(void)
|
|
{
|
|
|
|
vUD1 = vUD2 = cursor;
|
|
}
|
|
|
|
/*
|
|
* All the rest of the motion sequences have one or more
|
|
* cases to deal with. In the case wdot == 0, operation
|
|
* is totally within current line, from cursor to wcursor.
|
|
* If wdot is given, but wcursor is 0, then operation affects
|
|
* the inclusive line range. The hardest case is when both wdot
|
|
* and wcursor are given, then operation affects from line dot at
|
|
* cursor to line wdot at wcursor.
|
|
*/
|
|
|
|
/*
|
|
* Move is simple, except for moving onto new lines in hardcopy open mode.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
vmove(int unused)
|
|
{
|
|
register int cnt;
|
|
|
|
if (wdot) {
|
|
if (wdot < one || wdot > dol) {
|
|
beep();
|
|
return;
|
|
}
|
|
cnt = wdot - dot;
|
|
wdot = NOLINE;
|
|
if (cnt)
|
|
killU();
|
|
vupdown(cnt, wcursor);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* When we move onto a new line, save information for U undo.
|
|
*/
|
|
if (vUNDdot != dot) {
|
|
vUNDsav = *dot;
|
|
vUNDcurs = wcursor;
|
|
vUNDdot = dot;
|
|
}
|
|
|
|
/*
|
|
* In hardcopy open, type characters to left of cursor
|
|
* on new line, or back cursor up if its to left of where we are.
|
|
* In any case if the current line is ``rubbled'' i.e. has trashy
|
|
* looking overstrikes on it or \'s from deletes, we reprint
|
|
* so it is more comprehensible (and also because we can't work
|
|
* if we let it get more out of sync since column() won't work right.
|
|
*/
|
|
if (state == HARDOPEN) {
|
|
register char *cp;
|
|
if (rubble) {
|
|
register int c;
|
|
int oldhold = hold;
|
|
|
|
sethard();
|
|
cp = wcursor;
|
|
c = *cp;
|
|
*cp = 0;
|
|
hold |= HOLDDOL;
|
|
vreopen(WTOP, lineDOT(), vcline);
|
|
hold = oldhold;
|
|
*cp = c;
|
|
} else if (wcursor > cursor) {
|
|
vfixcurs();
|
|
for (cp = cursor; *cp && cp < wcursor;) {
|
|
int c, n;
|
|
nextc(c, cp, n);
|
|
cp += n;
|
|
c &= TRIM;
|
|
putchar(c ? c : ' ');
|
|
}
|
|
}
|
|
}
|
|
vsetcurs(wcursor);
|
|
}
|
|
|
|
/*
|
|
* Delete operator.
|
|
*
|
|
* Hard case of deleting a range where both wcursor and wdot
|
|
* are specified is treated as a special case of change and handled
|
|
* by vchange (although vchange may pass it back if it degenerates
|
|
* to a full line range delete.)
|
|
*/
|
|
void
|
|
vdelete(int c)
|
|
{
|
|
register char *cp;
|
|
register int i;
|
|
|
|
if (wdot) {
|
|
if (wcursor) {
|
|
vchange(EOF);
|
|
return;
|
|
}
|
|
if ((i = xdw()) < 0)
|
|
return;
|
|
if (state != VISUAL) {
|
|
vgoto(LINE(0), 0);
|
|
vputchar('@');
|
|
}
|
|
wdot = dot;
|
|
vremote(i, delete, 0);
|
|
notenam = "delete";
|
|
DEL[0] = 0;
|
|
killU();
|
|
vreplace(vcline, i, 0);
|
|
if (wdot > dol)
|
|
vcline--;
|
|
vrepaint(NOSTR);
|
|
return;
|
|
}
|
|
if (wcursor < linebuf)
|
|
wcursor = linebuf;
|
|
if (cursor == wcursor) {
|
|
beep();
|
|
return;
|
|
}
|
|
i = vdcMID();
|
|
cp = cursor;
|
|
setDEL();
|
|
CP(cp, wcursor);
|
|
if (cp > linebuf && (cp[0] == 0 || c == '#'))
|
|
cp--;
|
|
if (state == HARDOPEN) {
|
|
bleep(i, cp);
|
|
cursor = cp;
|
|
return;
|
|
}
|
|
physdc(column(cursor + skipleft(linebuf, cursor)), i);
|
|
DEPTH(vcline) = 0;
|
|
vreopen(LINE(vcline), lineDOT(), vcline);
|
|
vsyncCL();
|
|
vsetcurs(cp);
|
|
}
|
|
|
|
/*
|
|
* Change operator.
|
|
*
|
|
* In a single line we mark the end of the changed area with '$'.
|
|
* On multiple whole lines, we clear the lines first.
|
|
* Across lines with both wcursor and wdot given, we delete
|
|
* and sync then append (but one operation for undo).
|
|
*/
|
|
void
|
|
vchange(int c)
|
|
{
|
|
register char *cp;
|
|
register int i, ind, cnt;
|
|
line *addr;
|
|
|
|
if (wdot) {
|
|
/*
|
|
* Change/delete of lines or across line boundaries.
|
|
*/
|
|
if ((cnt = xdw()) < 0)
|
|
return;
|
|
getDOT();
|
|
if (wcursor && cnt == 1) {
|
|
/*
|
|
* Not really.
|
|
*/
|
|
wdot = 0;
|
|
if (c == EOF) {
|
|
vdelete(c);
|
|
return;
|
|
}
|
|
goto smallchange;
|
|
}
|
|
if (cursor && wcursor) {
|
|
/*
|
|
* Across line boundaries, but not
|
|
* necessarily whole lines.
|
|
* Construct what will be left.
|
|
*/
|
|
*cursor = 0;
|
|
strcpy(genbuf, linebuf);
|
|
getline(*wdot);
|
|
if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) {
|
|
getDOT();
|
|
beep();
|
|
return;
|
|
}
|
|
strcat(genbuf, wcursor);
|
|
if (c == EOF && *vpastwh(genbuf) == 0) {
|
|
/*
|
|
* Although this is a delete
|
|
* spanning line boundaries, what
|
|
* would be left is all white space,
|
|
* so take it all away.
|
|
*/
|
|
wcursor = 0;
|
|
getDOT();
|
|
op = 0;
|
|
notpart(lastreg);
|
|
notpart('1');
|
|
vdelete(c);
|
|
return;
|
|
}
|
|
ind = -1;
|
|
} else if (c == EOF && wcursor == 0) {
|
|
vdelete(c);
|
|
return;
|
|
} else
|
|
#ifdef LISPCODE
|
|
/*
|
|
* We are just substituting text for whole lines,
|
|
* so determine the first autoindent.
|
|
*/
|
|
if (value(LISP) && value(AUTOINDENT))
|
|
ind = lindent(dot);
|
|
else
|
|
#endif
|
|
ind = whitecnt(linebuf);
|
|
i = vcline >= 0 ? LINE(vcline) : WTOP;
|
|
|
|
/*
|
|
* Delete the lines from the buffer,
|
|
* and remember how the partial stuff came about in
|
|
* case we are told to put.
|
|
*/
|
|
addr = dot;
|
|
vremote(cnt, delete, 0);
|
|
setpk();
|
|
notenam = "delete";
|
|
if (c != EOF)
|
|
notenam = "change";
|
|
/*
|
|
* If DEL[0] were nonzero, put would put it back
|
|
* rather than the deleted lines.
|
|
*/
|
|
DEL[0] = 0;
|
|
if (cnt > 1)
|
|
killU();
|
|
|
|
/*
|
|
* Now hack the screen image coordination.
|
|
*/
|
|
vreplace(vcline, cnt, 0);
|
|
wdot = NOLINE;
|
|
noteit(0);
|
|
vcline--;
|
|
if (addr <= dol)
|
|
dot--;
|
|
|
|
/*
|
|
* If this is a across line delete/change,
|
|
* cursor stays where it is; just splice together the pieces
|
|
* of the new line. Otherwise generate a autoindent
|
|
* after a S command.
|
|
*/
|
|
if (ind >= 0) {
|
|
*genindent(ind) = 0;
|
|
vdoappend(genbuf);
|
|
} else {
|
|
vmcurs = cursor;
|
|
strcLIN(genbuf);
|
|
vdoappend(linebuf);
|
|
}
|
|
|
|
/*
|
|
* Indicate a change on hardcopies by
|
|
* erasing the current line.
|
|
*/
|
|
if (c != EOF && state != VISUAL && state != HARDOPEN) {
|
|
int oldhold = hold;
|
|
|
|
hold |= HOLDAT, vclrlin(i, dot), hold = oldhold;
|
|
}
|
|
|
|
/*
|
|
* Open the line (logically) on the screen, and
|
|
* update the screen tail. Unless we are really a delete
|
|
* go off and gather up inserted characters.
|
|
*/
|
|
vcline++;
|
|
if (vcline < 0)
|
|
vcline = 0;
|
|
vopen(dot, i);
|
|
vsyncCL();
|
|
noteit(1);
|
|
if (c != EOF) {
|
|
if (ind >= 0) {
|
|
cursor = linebuf;
|
|
linebuf[0] = 0;
|
|
vfixcurs();
|
|
} else {
|
|
ind = 0;
|
|
vcursat(cursor);
|
|
}
|
|
vappend('x', 1, ind);
|
|
return;
|
|
}
|
|
if (*cursor == 0 && cursor > linebuf)
|
|
cursor += skipleft(linebuf, cursor);
|
|
vrepaint(cursor);
|
|
return;
|
|
}
|
|
|
|
smallchange:
|
|
/*
|
|
* The rest of this is just low level hacking on changes
|
|
* of small numbers of characters.
|
|
*/
|
|
if (wcursor < linebuf)
|
|
wcursor = linebuf;
|
|
if (cursor == wcursor) {
|
|
beep();
|
|
return;
|
|
}
|
|
i = vdcMID();
|
|
cp = cursor;
|
|
if (state != HARDOPEN)
|
|
vfixcurs();
|
|
|
|
/*
|
|
* Put out the \\'s indicating changed text in hardcopy,
|
|
* or mark the end of the change with $ if not hardcopy.
|
|
*/
|
|
if (state == HARDOPEN)
|
|
bleep(i, cp);
|
|
else {
|
|
int c, n;
|
|
vcursbef(wcursor);
|
|
nextc(c, cursor, n);
|
|
if (colsc(c) > 1)
|
|
putchar(' ');
|
|
putchar('$');
|
|
i = cindent();
|
|
}
|
|
|
|
/*
|
|
* Remember the deleted text for possible put,
|
|
* and then prepare and execute the input portion of the change.
|
|
*/
|
|
cursor = cp;
|
|
setDEL();
|
|
CP(cursor, wcursor);
|
|
if (state != HARDOPEN) {
|
|
vcursaft(cursor - 1);
|
|
doomed = i - cindent();
|
|
} else {
|
|
/*
|
|
sethard();
|
|
wcursor = cursor;
|
|
cursor = linebuf;
|
|
vgoto(outline, value(NUMBER) << 3);
|
|
vmove();
|
|
*/
|
|
doomed = 0;
|
|
}
|
|
prepapp();
|
|
vappend('c', 1, 0);
|
|
}
|
|
|
|
/*
|
|
* Open new lines.
|
|
*
|
|
* Tricky thing here is slowopen. This causes display updating
|
|
* to be held off so that 300 baud dumb terminals don't lose badly.
|
|
* This also suppressed counts, which otherwise say how many blank
|
|
* space to open up. Counts are also suppressed on intelligent terminals.
|
|
* Actually counts are obsoleted, since if your terminal is slow
|
|
* you are better off with slowopen.
|
|
*/
|
|
void
|
|
voOpen (
|
|
int c, /* mjm: char --> int */
|
|
register int cnt
|
|
)
|
|
{
|
|
register int ind = 0, i;
|
|
short oldhold = hold;
|
|
#ifdef SIGWINCH
|
|
sigset_t set, oset;
|
|
#endif
|
|
|
|
if (value(SLOWOPEN) || value(REDRAW) && AL && DL)
|
|
cnt = 1;
|
|
#ifdef SIGWINCH
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGWINCH);
|
|
sigprocmask(SIG_BLOCK, &set, &oset);
|
|
#endif
|
|
vsave();
|
|
setLAST();
|
|
if (value(AUTOINDENT))
|
|
ind = whitecnt(linebuf);
|
|
if (c == 'O') {
|
|
vcline--;
|
|
dot--;
|
|
if (dot > zero)
|
|
getDOT();
|
|
}
|
|
if (value(AUTOINDENT)) {
|
|
#ifdef LISPCODE
|
|
if (value(LISP))
|
|
ind = lindent(dot + 1);
|
|
#endif
|
|
}
|
|
killU();
|
|
prepapp();
|
|
if (FIXUNDO)
|
|
vundkind = VMANY;
|
|
if (state != VISUAL)
|
|
c = WBOT + 1;
|
|
else {
|
|
c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
|
|
if (c < ZERO)
|
|
c = ZERO;
|
|
i = LINE(vcline + 1) - c;
|
|
if (i < cnt && c <= WBOT && (!AL || !DL))
|
|
vinslin(c, cnt - i, vcline);
|
|
}
|
|
*genindent(ind) = 0;
|
|
vdoappend(genbuf);
|
|
vcline++;
|
|
oldhold = hold;
|
|
hold |= HOLDROL;
|
|
vopen(dot, c);
|
|
hold = oldhold;
|
|
if (value(SLOWOPEN))
|
|
/*
|
|
* Oh, so lazy!
|
|
*/
|
|
vscrap();
|
|
else
|
|
vsync1(LINE(vcline));
|
|
cursor = linebuf;
|
|
linebuf[0] = 0;
|
|
vappend('o', 1, ind);
|
|
#ifdef SIGWINCH
|
|
sigprocmask(SIG_SETMASK, &oset, NULL);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* > < and = shift operators.
|
|
*
|
|
* Note that =, which aligns lisp, is just a ragged sort of shift,
|
|
* since it never distributes text between lines.
|
|
*/
|
|
char vshnam[2] = { 'x', 0 };
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
vshftop(int unused)
|
|
{
|
|
register line *addr;
|
|
register int cnt;
|
|
|
|
if ((cnt = xdw()) < 0)
|
|
return;
|
|
addr = dot;
|
|
vremote(cnt, vshift, 0);
|
|
vshnam[0] = op;
|
|
notenam = vshnam;
|
|
dot = addr;
|
|
vreplace(vcline, cnt, cnt);
|
|
if (state == HARDOPEN)
|
|
vcnt = 0;
|
|
vrepaint(NOSTR);
|
|
}
|
|
|
|
/*
|
|
* !.
|
|
*
|
|
* Filter portions of the buffer through unix commands.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
vfilter(int unused)
|
|
{
|
|
register line *addr;
|
|
register int cnt;
|
|
char *oglobp;
|
|
short d;
|
|
#ifdef BIT8
|
|
cell cuxb[UXBSIZE + 2];
|
|
#endif
|
|
|
|
if ((cnt = xdw()) < 0)
|
|
return;
|
|
if (vglobp)
|
|
#ifdef BIT8
|
|
vglobp = cuxb;
|
|
#else
|
|
vglobp = uxb;
|
|
#endif
|
|
if (readecho('!'))
|
|
return;
|
|
oglobp = globp; globp = genbuf + 1;
|
|
d = peekc; ungetchar(0);
|
|
CATCH
|
|
fixech();
|
|
unix0(0);
|
|
#ifdef BIT8
|
|
str2cell(cuxb, uxb);
|
|
#endif
|
|
ONERR
|
|
splitw = 0;
|
|
ungetchar(d);
|
|
vrepaint(cursor);
|
|
globp = oglobp;
|
|
return;
|
|
ENDCATCH
|
|
ungetchar(d); globp = oglobp;
|
|
addr = dot;
|
|
CATCH
|
|
vgoto(WECHO, 0); flusho();
|
|
vremote(cnt, filter, 2);
|
|
ONERR
|
|
vdirty(0, TLINES);
|
|
ENDCATCH
|
|
if (dot == zero && dol > zero)
|
|
dot = one;
|
|
splitw = 0;
|
|
notenam = "";
|
|
/*
|
|
* BUG: we shouldn't be depending on what undap2 and undap1 are,
|
|
* since we may be inside a macro. What's really wanted is the
|
|
* number of lines we read from the filter. However, the mistake
|
|
* will be an overestimate so it only results in extra work,
|
|
* it shouldn't cause any real screwups.
|
|
*/
|
|
vreplace(vcline, cnt, undap2 - undap1);
|
|
dot = addr;
|
|
if (dot > dol) {
|
|
dot--;
|
|
vcline--;
|
|
}
|
|
vrepaint(NOSTR);
|
|
}
|
|
|
|
/*
|
|
* Xdw exchanges dot and wdot if appropriate and also checks
|
|
* that wdot is reasonable. Its name comes from
|
|
* xchange dotand wdot
|
|
*/
|
|
int
|
|
xdw(void)
|
|
{
|
|
register char *cp;
|
|
register int cnt;
|
|
/*
|
|
register int notp = 0;
|
|
*/
|
|
|
|
if (wdot == NOLINE || wdot < one || wdot > dol) {
|
|
beep();
|
|
return (-1);
|
|
}
|
|
vsave();
|
|
setLAST();
|
|
if (dot > wdot || (dot == wdot && wcursor != 0 && cursor > wcursor)) {
|
|
register line *addr;
|
|
|
|
vcline -= dot - wdot;
|
|
addr = dot; dot = wdot; wdot = addr;
|
|
cp = cursor; cursor = wcursor; wcursor = cp;
|
|
}
|
|
/*
|
|
* If a region is specified but wcursor is at the begining
|
|
* of the last line, then we move it to be the end of the
|
|
* previous line (actually off the end).
|
|
*/
|
|
if (cursor && wcursor == linebuf && wdot > dot) {
|
|
wdot--;
|
|
getDOT();
|
|
if (vpastwh(linebuf) >= cursor)
|
|
wcursor = 0;
|
|
else {
|
|
getline(*wdot);
|
|
wcursor = strend(linebuf);
|
|
getDOT();
|
|
}
|
|
/*
|
|
* Should prepare in caller for possible dot == wdot.
|
|
*/
|
|
}
|
|
cnt = wdot - dot + 1;
|
|
if (vreg) {
|
|
vremote(cnt, YANKreg, vreg);
|
|
/*
|
|
if (notp)
|
|
notpart(vreg);
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Kill buffer code. If delete operator is c or d, then save
|
|
* the region in numbered buffers.
|
|
*
|
|
* BUG: This may be somewhat inefficient due
|
|
* to the way named buffer are implemented,
|
|
* necessitating some optimization.
|
|
*/
|
|
vreg = 0;
|
|
if (any(op, "cd")) {
|
|
vremote(cnt, YANKreg, '1');
|
|
/*
|
|
if (notp)
|
|
notpart('1');
|
|
*/
|
|
}
|
|
return (cnt);
|
|
}
|
|
|
|
/*
|
|
* Routine for vremote to call to implement shifts.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
vshift(int unused)
|
|
{
|
|
|
|
shift(op, 1);
|
|
}
|
|
|
|
/*
|
|
* Replace a single character with the next input character.
|
|
* A funny kind of insert.
|
|
*/
|
|
void
|
|
vrep(register int cnt)
|
|
{
|
|
register int i, c;
|
|
|
|
if (cnt > strlen(cursor)) {
|
|
beep();
|
|
return;
|
|
}
|
|
showmode('r');
|
|
i = column(cursor + cnt - 1);
|
|
vcursat(cursor);
|
|
doomed = i - cindent();
|
|
if (!vglobp) {
|
|
c = getesc();
|
|
if (c == 0) {
|
|
showmode(0);
|
|
vfixcurs();
|
|
return;
|
|
}
|
|
ungetkey(c);
|
|
}
|
|
CP(vutmp, linebuf);
|
|
if (FIXUNDO)
|
|
vundkind = VCHNG;
|
|
wcursor = cursor;
|
|
for (i = 0; i < cnt; i++)
|
|
wcursor += skipright(cursor, wcursor);
|
|
vUD1 = cursor; vUD2 = wcursor;
|
|
CP(cursor, wcursor);
|
|
prepapp();
|
|
vappend('r', cnt, 0);
|
|
*lastcp++ = INS[0];
|
|
setLAST();
|
|
}
|
|
|
|
/*
|
|
* Yank.
|
|
*
|
|
* Yanking to string registers occurs for free (essentially)
|
|
* in the routine xdw().
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
vyankit(int unused)
|
|
{
|
|
register int cnt;
|
|
|
|
if (wdot) {
|
|
if ((cnt = xdw()) < 0)
|
|
return;
|
|
vremote(cnt, yank, 0);
|
|
setpk();
|
|
notenam = "yank";
|
|
if (FIXUNDO)
|
|
vundkind = VNONE;
|
|
DEL[0] = 0;
|
|
wdot = NOLINE;
|
|
if (notecnt <= vcnt - vcline && notecnt < value(REPORT))
|
|
notecnt = 0;
|
|
vrepaint(cursor);
|
|
return;
|
|
}
|
|
takeout(DEL);
|
|
}
|
|
|
|
/*
|
|
* Set pkill variables so a put can
|
|
* know how to put back partial text.
|
|
* This is necessary because undo needs the complete
|
|
* line images to be saved, while a put wants to trim
|
|
* the first and last lines. The compromise
|
|
* is for put to be more clever.
|
|
*/
|
|
void
|
|
setpk(void)
|
|
{
|
|
|
|
if (wcursor) {
|
|
pkill[0] = cursor;
|
|
pkill[1] = wcursor;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Kill the last deleted part of a line so that "p" does not put it back.
|
|
* This is to be called from ex commands that delete some text.
|
|
*/
|
|
void
|
|
vkillDEL(void)
|
|
{
|
|
DEL[0] = 0;
|
|
}
|