/* * 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); }