mirror of https://github.com/tildeclub/ex-vi.git
1069 lines
23 KiB
C
1069 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.26 (gritter) 1/13/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;) {
|
|
int c, n;
|
|
nextc(c, cp, n);
|
|
cp += n;
|
|
putchar(c);
|
|
}
|
|
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, d, n;
|
|
vcursbef(wcursor);
|
|
d = skipleft(linebuf, wcursor);
|
|
nextc(c, &wcursor[d], 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;
|
|
}
|