Blame | Last modification | View Log | RSS feed
/*
* 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_voper.c 1.27 (gritter) 2/15/05";
#endif
#endif
/* from ex_voper.c 7.4 (Berkeley) 6/7/85 */
#include "ex.h"
#include "ex_re.h"
#include "ex_tty.h"
#include "ex_vis.h"
#ifdef MB
static int
cblank(char *cp)
{
if (mb_cur_max > 1 && *cp & 0200) {
int c;
return mbtowi(&c, cp, mb_cur_max) > 0 && iswspace(c);
} else
return isspace(*cp&0377);
}
#define blank() cblank(wcursor)
#else /* !MB */
#define cblank(cp) isspace(*cp&0377)
#define blank() xisspace(wcursor[0]&TRIM)
#endif /* !MB */
#define forbid(a) if (a) goto errlab;
cell vscandir[2] = { '/', 0 };
/*
* Decode an operator/operand type command.
* Eventually we switch to an operator subroutine in ex_vops.c.
* The work here is setting up a function variable to point
* to the routine we want, and manipulation of the variables
* wcursor and wdot, which mark the other end of the affected
* area. If wdot is zero, then the current line is the other end,
* and if wcursor is zero, then the first non-blank location of the
* other line is implied.
*/
void
operate(register int c, register int cnt)
{
register int i = 0;
void (*moveop)(int), (*deleteop)(int);
void (*opf)(int);
bool subop = 0;
char *oglobp, *ocurs;
register line *addr;
line *odot;
static int lastFKND, lastFCHR;
short d;
cell nullcell[1], qmarkcell[2], slashcell[2];
CLOBBGRD(opf);
CLOBBGRD(d);
qmarkcell[0] = '?';
slashcell[0] = '/';
nullcell[0] = qmarkcell[1] = slashcell[1] = 0;
moveop = vmove, deleteop = vdelete;
wcursor = cursor;
wdot = NOLINE;
notecnt = 0;
dir = 1;
switch (c) {
/*
* d delete operator.
*/
case 'd':
moveop = vdelete;
deleteop = (void (*)(int))beep;
break;
/*
* s substitute characters, like c\040, i.e. change space.
*/
case 's':
ungetkey(' ');
subop++;
/* fall into ... */
/*
* c Change operator.
*/
case 'c':
if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
subop++;
moveop = vchange;
deleteop = (void (*)(int))beep;
break;
/*
* ! Filter through a UNIX command.
*/
case '!':
moveop = vfilter;
deleteop = (void (*)(int))beep;
break;
/*
* y Yank operator. Place specified text so that it
* can be put back with p/P. Also yanks to named buffers.
*/
case 'y':
moveop = vyankit;
deleteop = (void (*)(int))beep;
break;
/*
* = Reformat operator (for LISP).
*/
#ifdef LISPCODE
case '=':
forbid(!value(LISP));
/* fall into ... */
#endif
/*
* > Right shift operator.
* < Left shift operator.
*/
case '<':
case '>':
moveop = vshftop;
deleteop = (void (*)(int))beep;
break;
/*
* r Replace character under cursor with single following
* character.
*/
case 'r':
vmacchng(1);
vrep(cnt);
return;
default:
goto nocount;
}
vmacchng(1);
/*
* Had an operator, so accept another count.
* Multiply counts together.
*/
if (xisdigit(peekkey()) && peekkey() != '0') {
cnt *= vgetcnt();
Xcnt = cnt;
forbid (cnt <= 0);
}
/*
* Get next character, mapping it and saving as
* part of command for repeat.
*/
c = map(getesc(),arrows);
if (c == 0)
return;
if (!subop)
*lastcp++ = c;
nocount:
opf = moveop;
switch (c) {
/*
* b Back up a word.
* B Back up a word, liberal definition.
*/
case 'b':
case 'B':
dir = -1;
/* fall into ... */
/*
* w Forward a word.
* W Forward a word, liberal definition.
*/
case 'W':
case 'w':
wdkind = c & ' ';
forbid(llfind(2, cnt, opf, 0) < 0);
vmoving = 0;
break;
/*
* E to end of following blank/nonblank word
*/
case 'E':
wdkind = 0;
goto ein;
/*
* e To end of following word.
*/
case 'e':
wdkind = 1;
ein:
forbid(llfind(3, cnt - 1, opf, 0) < 0);
vmoving = 0;
break;
/*
* ( Back an s-expression.
*/
case '(':
dir = -1;
/* fall into... */
/*
* ) Forward an s-expression.
*/
case ')':
forbid(llfind(0, cnt, opf, (line *) 0) < 0);
markDOT();
break;
/*
* { Back an s-expression, but don't stop on atoms.
* In text mode, a paragraph. For C, a balanced set
* of {}'s.
*/
case '{':
dir = -1;
/* fall into... */
/*
* } Forward an s-expression, but don't stop on atoms.
* In text mode, back paragraph. For C, back a balanced
* set of {}'s.
*/
case '}':
forbid(llfind(1, cnt, opf, (line *) 0) < 0);
markDOT();
break;
/*
* % To matching () or {}. If not at ( or { scan for
* first such after cursor on this line.
*/
case '%':
vsave();
i = lmatchp((line *) 0);
#ifdef TRACE
if (trace)
fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
getDOT();
forbid(!i);
if (opf != vmove)
if (dir > 0)
wcursor += skipright(linebuf, wcursor);
else
cursor += skipright(linebuf, cursor);
else
markDOT();
vmoving = 0;
break;
/*
* [ Back to beginning of defun, i.e. an ( in column 1.
* For text, back to a section macro.
* For C, back to a { in column 1 (~~ beg of function.)
*/
case '[':
dir = -1;
/* fall into ... */
/*
* ] Forward to next defun, i.e. a ( in column 1.
* For text, forward section.
* For C, forward to a } in column 1 (if delete or such)
* or if a move to a { in column 1.
*/
case ']':
if (!vglobp)
forbid(getkey() != c);
forbid (Xhadcnt);
vsave();
i = lbrack(c, opf);
getDOT();
forbid(!i);
markDOT();
if (ospeed > B300)
hold |= HOLDWIG;
break;
/*
* , Invert last find with f F t or T, like inverse
* of ;.
*/
case ',':
forbid (lastFKND == 0);
c = xisupper(lastFKND&TRIM)
? xtolower(lastFKND&TRIM) : xtoupper(lastFKND&TRIM);
i = lastFCHR;
if (vglobp == 0)
vglobp = nullcell;
subop++;
goto nocount;
/*
* 0 To beginning of real line.
*/
case '0':
wcursor = linebuf;
vmoving = 0;
break;
/*
* ; Repeat last find with f F t or T.
*/
case ';':
forbid (lastFKND == 0);
c = lastFKND;
i = lastFCHR;
subop++;
goto nocount;
/*
* F Find single character before cursor in current line.
* T Like F, but stops before character.
*/
case 'F': /* inverted find */
case 'T':
dir = -1;
/* fall into ... */
/*
* f Find single character following cursor in current line.
* t Like f, but stope before character.
*/
case 'f': /* find */
case 't':
if (!subop) {
i = getesc();
if (i == 0)
return;
*lastcp++ = i;
}
if (vglobp == 0)
lastFKND = c, lastFCHR = i;
for (; cnt > 0; cnt--)
forbid (find(i) == 0);
vmoving = 0;
switch (c) {
case 'T':
wcursor += skipright(linebuf, wcursor);
break;
case 't':
wcursor += skipleft(linebuf, wcursor);
case 'f':
fixup:
if (moveop != vmove)
wcursor += skipright(linebuf, wcursor);
break;
}
break;
/*
* | Find specified print column in current line.
*/
case '|':
if (Pline == numbline)
cnt += 8;
vmovcol = cnt;
vmoving = 1;
wcursor = vfindcol(cnt);
break;
/*
* ^ To beginning of non-white space on line.
*/
case '^':
wcursor = vskipwh(linebuf);
vmoving = 0;
break;
/*
* $ To end of line.
*/
case '$':
if (opf == vmove) {
vmoving = 1;
vmovcol = 20000;
} else
vmoving = 0;
if (cnt > 1) {
if (opf == vmove) {
wcursor = 0;
cnt--;
} else
wcursor = linebuf;
/* This is wrong at EOF */
wdot = dot + cnt;
break;
}
if (linebuf[0]) {
wcursor = strend(linebuf) - 1;
goto fixup;
}
wcursor = linebuf;
break;
/*
* h Back a character.
* ^H Back a character.
*/
case 'h':
case CTRL('h'):
dir = -1;
/* fall into ... */
/*
* space Forward a character.
*/
case 'l':
case ' ':
forbid (margin() || opf == vmove && edge());
while (cnt > 0 && !margin()) {
wcursor += dir>0 ? skipright(linebuf, wcursor) :
skipleft(linebuf, wcursor);
cnt--;
}
if (margin() && opf == vmove || wcursor < linebuf)
wcursor -= dir;
vmoving = 0;
break;
/*
* D Delete to end of line, short for d$.
*/
case 'D':
cnt = INF;
goto deleteit;
/*
* X Delete character before cursor.
*/
case 'X':
dir = -1;
/* fall into ... */
deleteit:
/*
* x Delete character at cursor, leaving cursor where it is.
*/
case 'x':
if (margin())
goto errlab;
vmacchng(1);
while (cnt > 0 && !margin()) {
wcursor += dir > 0 ? skipright(linebuf, wcursor) :
skipleft(linebuf, wcursor);
cnt--;
}
opf = deleteop;
vmoving = 0;
break;
default:
/*
* Stuttered operators are equivalent to the operator on
* a line, thus turn dd into d_.
*/
if (opf == vmove || c != workcmd[0]) {
errlab:
beep();
vmacp = 0;
return;
}
/* fall into ... */
/*
* _ Target for a line or group of lines.
* Stuttering is more convenient; this is mostly
* for aesthetics.
*/
case '_':
wdot = dot + cnt - 1;
vmoving = 0;
wcursor = 0;
break;
/*
* H To first, home line on screen.
* Count is for count'th line rather than first.
*/
case 'H':
wdot = (dot - vcline) + cnt - 1;
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
/*
* - Backwards lines, to first non-white character.
*/
case '-':
wdot = dot - cnt;
vmoving = 0;
wcursor = 0;
break;
/*
* ^P To previous line same column. Ridiculous on the
* console of the VAX since it puts console in LSI mode.
*/
case 'k':
case CTRL('p'):
wdot = dot - cnt;
if (vmoving == 0)
vmoving = 1, vmovcol = lcolumn(cursor);
wcursor = 0;
break;
/*
* L To last line on screen, or count'th line from the
* bottom.
*/
case 'L':
wdot = dot + vcnt - vcline - cnt;
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
/*
* M To the middle of the screen.
*/
case 'M':
wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
/*
* + Forward line, to first non-white.
*
* CR Convenient synonym for +.
*/
case '+':
case CR:
wdot = dot + cnt;
vmoving = 0;
wcursor = 0;
break;
/*
* ^N To next line, same column if possible.
*
* LF Linefeed is a convenient synonym for ^N.
*/
case CTRL('n'):
case 'j':
case NL:
wdot = dot + cnt;
if (vmoving == 0)
vmoving = 1, vmovcol = lcolumn(cursor);
wcursor = 0;
break;
/*
* n Search to next match of current pattern.
*/
case 'n':
vglobp = vscandir;
c = *vglobp++;
goto nocount;
/*
* N Like n but in reverse direction.
*/
case 'N':
vglobp = vscandir[0] == '/' ? qmarkcell : slashcell;
c = *vglobp++;
goto nocount;
/*
* ' Return to line specified by following mark,
* first white position on line.
*
* ` Return to marked line at remembered column.
*/
case '\'':
case '`':
d = c;
c = getesc();
if (c == 0)
return;
c = markreg(c);
forbid (c == 0);
wdot = getmark(c);
forbid (wdot == NOLINE);
forbid (Xhadcnt);
vmoving = 0;
wcursor = d == '`' ? ncols[c - 'a'] : 0;
if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor)))
markDOT();
if (wcursor) {
vsave();
getline(*wdot);
if (wcursor > strend(linebuf))
wcursor = 0;
getDOT();
}
if (ospeed > B300)
hold |= HOLDWIG;
break;
/*
* G Goto count'th line, or last line if no count
* given.
*/
case 'G':
if (!Xhadcnt)
cnt = lineDOL();
wdot = zero + cnt;
forbid (wdot < one || wdot > dol);
if (opf == vmove)
markit(wdot);
vmoving = 0;
wcursor = 0;
break;
/*
* / Scan forward for following re.
* ? Scan backward for following re.
*/
case '/':
case '?':
forbid (Xhadcnt);
vsave();
ocurs = cursor;
odot = dot;
wcursor = 0;
if (readecho(c))
return;
if (!vglobp)
vscandir[0] = genbuf[0];
oglobp = globp;
CP(vutmp, genbuf);
globp = vutmp;
d = peekc;
fromsemi:
ungetchar(0);
fixech();
CATCH
addr = address(cursor);
ONERR
slerr:
globp = oglobp;
dot = odot;
cursor = ocurs;
ungetchar(d);
splitw = 0;
vclean();
vjumpto(dot, ocurs, 0);
return;
ENDCATCH
if (globp == 0)
globp = "";
else if (peekc)
--globp;
if (*globp == ';') {
/* /foo/;/bar/ */
globp++;
dot = addr;
cursor = loc1;
goto fromsemi;
}
dot = odot;
ungetchar(d);
c = 0;
if (*globp == 'z')
globp++, c = '\n';
if (any(*globp, "^+-."))
c = *globp++;
i = 0;
while (xisdigit(*globp&TRIM))
i = i * 10 + *globp++ - '0';
if (any(*globp, "^+-."))
c = *globp++;
if (*globp) {
/* random junk after the pattern */
beep();
goto slerr;
}
globp = oglobp;
splitw = 0;
vmoving = 0;
wcursor = loc1;
if (i != 0)
vsetsiz(i);
if (opf == vmove) {
if (state == ONEOPEN || state == HARDOPEN)
outline = destline = WBOT;
if (addr != dot || loc1 != cursor)
markDOT();
if (loc1 > linebuf && *loc1 == 0)
loc1--;
if (c)
vjumpto(addr, loc1, c);
else {
vmoving = 0;
if (loc1) {
vmoving++;
vmovcol = column(loc1);
}
getDOT();
if (state == CRTOPEN && addr != dot)
vup1();
vupdown(addr - dot, NOSTR);
}
return;
}
lastcp[-1] = 'n';
getDOT();
wdot = addr;
break;
}
/*
* Apply.
*/
if (vreg && wdot == 0)
wdot = dot;
(*opf)(c);
wdot = NOLINE;
}
/*
* Find single character c, in direction dir from cursor.
*/
int
find(int c)
{
for(;;) {
if (edge())
return (0);
wcursor += dir>0 ? skipright(linebuf, wcursor) :
skipleft(linebuf, wcursor-1);
if (samechar(wcursor, c))
return (1);
}
}
/*
* Do a word motion with operator op, and cnt more words
* to go after this.
*/
int
word(register void (*op)(int), int cnt)
{
register int which = 0, i;
register char *iwc;
register line *iwdot = wdot;
if (dir == 1) {
iwc = wcursor;
which = wordch(wcursor);
while (wordof(which, wcursor)) {
if (cnt == 1 && op != vmove &&
wcursor[i = skipright(linebuf, wcursor)]
== 0) {
wcursor += i;
break;
}
if (!lnext())
return (0);
if (wcursor == linebuf)
break;
}
/* Unless last segment of a change skip blanks */
if (op != vchange || cnt > 1)
while (!margin() && blank())
wcursor += skipright(linebuf, wcursor);
else
if (wcursor == iwc && iwdot == wdot && *iwc)
wcursor += skipright(linebuf, wcursor);
if (op == vmove && margin()) {
if (wcursor == linebuf)
wcursor--;
else if (!lnext())
return (0);
}
} else {
if (!lnext())
return (0);
while (blank())
if (!lnext())
return (0);
if (!margin()) {
which = wordch(wcursor);
while (!margin() && wordof(which, wcursor))
wcursor--;
}
if (wcursor < linebuf || !wordof(which, wcursor))
wcursor += skipright(linebuf, wcursor);
}
return (1);
}
/*
* To end of word, with operator op and cnt more motions
* remaining after this.
*/
void
eend(register void (*op)(int))
{
register int which;
if (!lnext())
return;
while (blank())
if (!lnext())
return;
which = wordch(wcursor);
while (wordof(which, wcursor)) {
if (wcursor[1] == 0) {
wcursor++;
break;
}
if (!lnext())
return;
}
if (op != vchange && op != vdelete && wcursor > linebuf)
wcursor--;
}
/*
* Wordof tells whether the character at *wc is in a word of
* kind which (blank/nonblank words are 0, conservative words 1).
*/
int
wordof(int which, register char *wc)
{
if (cblank(wc))
return (0);
return (!wdkind || wordch(wc) == which);
}
/*
* Wordch tells whether character at *wc is a word character
* i.e. an alfa, digit, or underscore.
*/
int
wordch(char *wc)
{
int c;
#ifdef MB
if (mb_cur_max > 0 && *wc & 0200) {
mbtowi(&c, wc, mb_cur_max);
if (c & INVBIT)
return 1;
} else
#endif
c = wc[0]&0377;
return (xisalnum(c) || c == '_'
#ifdef BIT8
#ifdef ISO8859_1
/*
* We consider all ISO 8859-1 characters except for
* no-break-space as word characters.
*/
|| c&0200 && (!(c"E) && (c&TRIM) != 0240)
#endif
#endif
);
}
/*
* Edge tells when we hit the last character in the current line.
*/
int
edge(void)
{
if (linebuf[0] == 0)
return (1);
if (dir == 1)
return (wcursor[skipright(linebuf, wcursor)] == 0);
else
return (wcursor == linebuf);
}
/*
* Margin tells us when we have fallen off the end of the line.
*/
int
margin(void)
{
return (wcursor < linebuf || wcursor[0] == 0);
}