summaryrefslogtreecommitdiff
path: root/ex_vmain.c
diff options
context:
space:
mode:
Diffstat (limited to 'ex_vmain.c')
-rw-r--r--ex_vmain.c1442
1 files changed, 1442 insertions, 0 deletions
diff --git a/ex_vmain.c b/ex_vmain.c
new file mode 100644
index 0000000..ac07f92
--- /dev/null
+++ b/ex_vmain.c
@@ -0,0 +1,1442 @@
+/*
+ * 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_vmain.c 1.29 (gritter) 2/17/05";
+#endif
+#endif
+
+/* from ex_vmain.c 7.7 (Berkeley) 6/7/85 */
+
+#include "ex.h"
+#include "ex_tty.h"
+#include "ex_vis.h"
+
+/*
+ * This is the main routine for visual.
+ * We here decode the count and possible named buffer specification
+ * preceding a command and interpret a few of the commands.
+ * Commands which involve a target (i.e. an operator) are decoded
+ * in the routine operate in ex_voperate.c.
+ */
+
+#define forbid(a) { if (a) goto fonfon; }
+
+void
+vmain(void)
+{
+ int c, cnt, i;
+ cell esave[TUBECOLS];
+ char *oglobp;
+ short d;
+ line *addr;
+ int ind, nlput;
+ int shouldpo = 0;
+ int onumber = 0, olist = 0;
+ void (*OPline)(int) = NULL;
+ int (*OPutchar)(int) = NULL;
+
+ CLOBBGRD(c);
+ CLOBBGRD(cnt);
+ CLOBBGRD(i);
+ CLOBBGRD(oglobp);
+ CLOBBGRD(addr);
+ CLOBBGRD(shouldpo);
+ CLOBBGRD(onumber);
+ CLOBBGRD(olist);
+ CLOBBGRD(OPline);
+ CLOBBGRD(OPutchar);
+
+ vch_mac = VC_NOTINMAC;
+
+ /*
+ * If we started as a vi command (on the command line)
+ * then go process initial commands (recover, next or tag).
+ */
+ if (initev) {
+ oglobp = globp;
+ globp = initev;
+ hadcnt = cnt = 0;
+ i = tchng;
+ addr = dot;
+ goto doinit;
+ }
+
+ /*
+ * NB:
+ *
+ * The current line is always in the line buffer linebuf,
+ * and the cursor at the position cursor. You should do
+ * a vsave() before moving off the line to make sure the disk
+ * copy is updated if it has changed, and a getDOT() to get
+ * the line back if you mung linebuf. The motion
+ * routines in ex_vwind.c handle most of this.
+ */
+ for (;;) {
+ /*
+ * Decode a visual command.
+ * First sync the temp file if there has been a reasonable
+ * amount of change. Clear state for decoding of next
+ * command.
+ */
+ TSYNC();
+ vglobp = 0;
+ vreg = 0;
+ hold = 0;
+ seenprompt = 1;
+ wcursor = 0;
+ Xhadcnt = hadcnt = 0;
+ Xcnt = cnt = 1;
+ splitw = 0;
+ if (i = holdupd) {
+ if (state == VISUAL)
+ ignore(peekkey());
+ holdupd = 0;
+/*
+ if (LINE(0) < ZERO) {
+ vclear();
+ vcnt = 0;
+ i = 3;
+ }
+*/
+ if (state != VISUAL) {
+ vcnt = 0;
+ vsave();
+ vrepaint(cursor);
+ } else if (i == 3)
+ vredraw(WTOP);
+ else
+ vsync(WTOP);
+ vfixcurs();
+ }
+
+ /*
+ * Gobble up counts and named buffer specifications.
+ */
+ for (;;) {
+looptop:
+#ifdef MDEBUG
+ if (trace)
+ fprintf(trace, "pc=%c",peekkey());
+#endif
+ if (xisdigit(peekkey()) && peekkey() != '0') {
+ hadcnt = 1;
+ cnt = vgetcnt();
+ forbid (cnt <= 0);
+ }
+ if (peekkey() != '"')
+ break;
+ ignore(getkey()), c = getkey();
+ /*
+ * Buffer names be letters or digits.
+ * But not '0' as that is the source of
+ * an 'empty' named buffer spec in the routine
+ * kshift (see ex_temp.c).
+ */
+ forbid (c == '0' || !xisalpha(c) && !xisdigit(c));
+ vreg = c;
+ }
+reread:
+ /*
+ * Come to reread from below after some macro expansions.
+ * The call to map allows use of function key pads
+ * by performing a terminal dependent mapping of inputs.
+ */
+#ifdef MDEBUG
+ if (trace)
+ fprintf(trace,"pcb=%c,",peekkey());
+#endif
+ op = getkey();
+ maphopcnt = 0;
+ do {
+ /*
+ * Keep mapping the char as long as it changes.
+ * This allows for double mappings, e.g., q to #,
+ * #1 to something else.
+ */
+ c = op;
+ op = map(c,arrows);
+#ifdef MDEBUG
+ if (trace)
+ fprintf(trace,"pca=%c,",c);
+#endif
+ /*
+ * Maybe the mapped to char is a count. If so, we have
+ * to go back to the "for" to interpret it. Likewise
+ * for a buffer name.
+ */
+ if ((xisdigit(c) && c!='0') || c == '"') {
+ ungetkey(c);
+ goto looptop;
+ }
+ if (!value(REMAP)) {
+ c = op;
+ break;
+ }
+ if (++maphopcnt > 256)
+ error(catgets(catd, 1, 225,
+ "Infinite macro loop"));
+ } while (c != op);
+
+ /*
+ * Begin to build an image of this command for possible
+ * later repeat in the buffer workcmd. It will be copied
+ * to lastcmd by the routine setLAST
+ * if/when completely specified.
+ */
+ lastcp = workcmd;
+ if (!vglobp)
+ *lastcp++ = c;
+
+ /*
+ * First level command decode.
+ */
+ if (c == ATTN)
+ goto case_ATTN;
+ switch (c) {
+
+ /*
+ * ^L Clear screen e.g. after transmission error.
+ */
+
+ /*
+ * ^R Retype screen, getting rid of @ lines.
+ * If in open, equivalent to ^L.
+ * On terminals where the right arrow key sends
+ * ^L we make ^R act like ^L, since there is no
+ * way to get ^L. These terminals (adm31, tvi)
+ * are intelligent so ^R is useless. Soroc
+ * will probably foul this up, but nobody has
+ * one of them.
+ */
+ case CTRL('l'):
+ case CTRL('r'):
+ if (c == CTRL('l') || (KR && *KR==CTRL('l'))) {
+ vclear();
+ vdirty(0, vcnt);
+ }
+ if (state != VISUAL) {
+ /*
+ * Get a clean line, throw away the
+ * memory of what is displayed now,
+ * and move back onto the current line.
+ */
+ vclean();
+ vcnt = 0;
+ vmoveto(dot, cursor, 0);
+ continue;
+ }
+ vredraw(WTOP);
+ /*
+ * Weird glitch -- when we enter visual
+ * in a very small window we may end up with
+ * no lines on the screen because the line
+ * at the top is too long. This forces the screen
+ * to be expanded to make room for it (after
+ * we have printed @'s ick showing we goofed).
+ */
+ if (vcnt == 0)
+ vrepaint(cursor);
+ vfixcurs();
+ continue;
+
+ /*
+ * $ Escape just cancels the current command
+ * with a little feedback.
+ */
+ case ESCAPE:
+ beep();
+ continue;
+
+ /*
+ * @ Macros. Bring in the macro and put it
+ * in vmacbuf, point vglobp there and punt.
+ */
+ case '@':
+ c = getesc();
+ if (c == 0)
+ continue;
+ if (c == '@')
+ c = lastmac;
+ if (xisupper(c))
+ c = xtolower(c);
+ forbid(!xislower(c));
+ lastmac = c;
+ vsave();
+ CATCH
+ char tmpbuf[BUFSIZ];
+
+ regbuf(c,tmpbuf,sizeof(vmacbuf));
+ macpush(tmpbuf, 1);
+ ONERR
+ lastmac = 0;
+ splitw = 0;
+ getDOT();
+ vrepaint(cursor);
+ continue;
+ ENDCATCH
+ vmacp = vmacbuf;
+ goto reread;
+
+ /*
+ * . Repeat the last (modifying) open/visual command.
+ */
+ case '.':
+ /*
+ * Check that there was a last command, and
+ * take its count and named buffer unless they
+ * were given anew. Special case if last command
+ * referenced a numeric named buffer -- increment
+ * the number and go to a named buffer again.
+ * This allows a sequence like "1pu.u.u...
+ * to successively look for stuff in the kill chain
+ * much as one does in EMACS with C-Y and M-Y.
+ */
+ forbid (lastcmd[0] == 0);
+ if (hadcnt)
+ lastcnt = cnt;
+ if (vreg)
+ lastreg = vreg;
+ else if (xisdigit(lastreg) && lastreg < '9')
+ lastreg++;
+ vreg = lastreg;
+ cnt = lastcnt;
+ hadcnt = lasthad;
+ vglobp = lastcmd;
+ goto reread;
+
+ /*
+ * ^U Scroll up. A count sticks around for
+ * future scrolls as the scroll amount.
+ * Attempt to hold the indentation from the
+ * top of the screen (in logical lines).
+ *
+ * BUG: A ^U near the bottom of the screen
+ * on a dumb terminal (which can't roll back)
+ * causes the screen to be cleared and then
+ * redrawn almost as it was. In this case
+ * one should simply move the cursor.
+ */
+ case CTRL('u'):
+ if (hadcnt)
+ vSCROLL = cnt;
+ cnt = vSCROLL;
+ if (state == VISUAL)
+ ind = vcline, cnt += ind;
+ else
+ ind = 0;
+ vmoving = 0;
+ vup(cnt, ind, 1);
+ vnline(NOSTR);
+ continue;
+
+ /*
+ * ^D Scroll down. Like scroll up.
+ */
+ case CTRL('d'):
+#ifdef TRACE
+ if (trace)
+ fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
+#endif
+ if (hadcnt)
+ vSCROLL = cnt;
+ cnt = vSCROLL;
+ if (state == VISUAL)
+ ind = vcnt - vcline - 1, cnt += ind;
+ else
+ ind = 0;
+ vmoving = 0;
+ vdown(cnt, ind, 1);
+#ifdef TRACE
+ if (trace)
+ fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
+#endif
+ vnline(NOSTR);
+#ifdef TRACE
+ if (trace)
+ fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
+#endif
+ continue;
+
+ /*
+ * ^E Glitch the screen down (one) line.
+ * Cursor left on same line in file.
+ */
+ case CTRL('e'):
+ if (state != VISUAL)
+ continue;
+ if (!hadcnt)
+ cnt = 1;
+ /* Bottom line of file already on screen */
+ forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
+ ind = vcnt - vcline - 1 + cnt;
+ vdown(ind, ind, 1);
+ vnline(cursor);
+ continue;
+
+ /*
+ * ^Y Like ^E but up
+ */
+ case CTRL('y'):
+ if (state != VISUAL)
+ continue;
+ if (!hadcnt)
+ cnt = 1;
+ forbid(lineDOT()-1<=vcline); /* line 1 already there */
+ ind = vcline + cnt;
+ vup(ind, ind, 1);
+ vnline(cursor);
+ continue;
+
+
+ /*
+ * m Mark position in mark register given
+ * by following letter. Return is
+ * accomplished via ' or `; former
+ * to beginning of line where mark
+ * was set, latter to column where marked.
+ */
+ case 'm':
+ /*
+ * Getesc is generally used when a character
+ * is read as a latter part of a command
+ * to allow one to hit rubout/escape to cancel
+ * what you have typed so far. These characters
+ * are mapped to 0 by the subroutine.
+ */
+ c = getesc();
+ if (c == 0)
+ continue;
+
+ /*
+ * Markreg checks that argument is a letter
+ * and also maps ' and ` to the end of the range
+ * to allow '' or `` to reference the previous
+ * context mark.
+ */
+ c = markreg(c);
+ forbid (c == 0);
+ vsave();
+ names[c - 'a'] = (*dot &~ 01);
+ ncols[c - 'a'] = cursor;
+ anymarks = 1;
+ continue;
+
+ /*
+ * ^F Window forwards, with 2 lines of continuity.
+ * Count repeats.
+ */
+ case CTRL('f'):
+ vsave();
+ if (vcnt > 2) {
+ addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
+ forbid(addr > dol);
+ dot = (line*)addr;
+ vcnt = vcline = 0;
+ }
+ vzop(0, 0, '+');
+ continue;
+
+ /*
+ * ^B Window backwards, with 2 lines of continuity.
+ * Inverse of ^F.
+ */
+ case CTRL('b'):
+ vsave();
+ if (one + vcline != dot && vcnt > 2) {
+ addr = dot - vcline + 2 - (cnt-1)*basWLINES;
+ forbid (addr <= zero);
+ dot = (line*)addr;
+ vcnt = vcline = 0;
+ }
+ vzop(0, 0, '^');
+ continue;
+
+ /*
+ * z Screen adjustment, taking a following character:
+ * z<CR> current line to top
+ * z<NL> like z<CR>
+ * z- current line to bottom
+ * also z+, z^ like ^F and ^B.
+ * A preceding count is line to use rather
+ * than current line. A count between z and
+ * specifier character changes the screen size
+ * for the redraw.
+ *
+ */
+ case 'z':
+ if (state == VISUAL) {
+ i = vgetcnt();
+ if (i > 0)
+ vsetsiz(i);
+ c = getesc();
+ if (c == 0)
+ continue;
+ }
+ vsave();
+ vzop(hadcnt, cnt, c);
+ continue;
+
+ /*
+ * Y Yank lines, abbreviation for y_ or yy.
+ * Yanked lines can be put later if no
+ * changes intervene, or can be put in named
+ * buffers and put anytime in this session.
+ */
+ case 'Y':
+ ungetkey('_');
+ c = 'y';
+ break;
+
+ /*
+ * J Join lines, 2 by default. Count is number
+ * of lines to join (no join operator sorry.)
+ */
+ case 'J':
+ forbid (dot == dol);
+ if (cnt == 1)
+ cnt = 2;
+ if (cnt > (i = dol - dot + 1))
+ cnt = i;
+ vsave();
+ vmacchng(1);
+ setLAST();
+ cursor = strend(linebuf);
+ vremote(cnt, join, 0);
+ notenam = "join";
+ vmoving = 0;
+ killU();
+ vreplace(vcline, cnt, 1);
+ if (!*cursor && cursor > linebuf)
+ cursor += skipleft(linebuf, cursor);
+ if (notecnt == 2)
+ notecnt = 0;
+ vrepaint(cursor);
+ continue;
+
+ /*
+ * S Substitute text for whole lines, abbrev for c_.
+ * Count is number of lines to change.
+ */
+ case 'S':
+ ungetkey('_');
+ c = 'c';
+ break;
+
+ /*
+ * O Create a new line above current and accept new
+ * input text, to an escape, there.
+ * A count specifies, for dumb terminals when
+ * slowopen is not set, the number of physical
+ * line space to open on the screen.
+ *
+ * o Like O, but opens lines below.
+ */
+ case 'O':
+ case 'o':
+ vmacchng(1);
+ voOpen(c, cnt);
+ continue;
+
+ /*
+ * C Change text to end of line, short for c$.
+ */
+ case 'C':
+ if (*cursor) {
+ ungetkey('$'), c = 'c';
+ break;
+ }
+ goto appnd;
+
+ /*
+ * ~ Switch case of letter under cursor
+ */
+ case '~':
+ vswitch(cnt);
+ continue;
+
+
+ /*
+ * A Append at end of line, short for $a.
+ */
+ case 'A':
+ operate('$', 1);
+appnd:
+ c = 'a';
+ /* fall into ... */
+
+ /*
+ * a Appends text after cursor. Text can continue
+ * through arbitrary number of lines.
+ */
+ case 'a':
+ if (*cursor) {
+ if (state == HARDOPEN) {
+ int c, n;
+ nextc(c, cursor, n);
+ putchar(c);
+ cursor += n;
+ } else
+ cursor += skipright(linebuf, cursor);
+ }
+ goto insrt;
+
+ /*
+ * I Insert at beginning of whitespace of line,
+ * short for ^i.
+ */
+ case 'I':
+ operate('^', 1);
+ c = 'i';
+ /* fall into ... */
+
+ /*
+ * R Replace characters, one for one, by input
+ * (logically), like repeated r commands.
+ *
+ * BUG: This is like the typeover mode of many other
+ * editors, and is only rarely useful. Its
+ * implementation is a hack in a low level
+ * routine and it doesn't work very well, e.g.
+ * you can't move around within a R, etc.
+ */
+ case 'R':
+ /* fall into... */
+
+ /*
+ * i Insert text to an escape in the buffer.
+ * Text is arbitrary. This command reminds of
+ * the i command in bare teco.
+ */
+ case 'i':
+insrt:
+ /*
+ * Common code for all the insertion commands.
+ * Save for redo, position cursor, prepare for append
+ * at command and in visual undo. Note that nothing
+ * is doomed, unless R when all is, and save the
+ * current line in a the undo temporary buffer.
+ */
+ vmacchng(1);
+ setLAST();
+ vcursat(cursor);
+ prepapp();
+ vnoapp();
+ doomed = c == 'R' ? 10000 : 0;
+ if(FIXUNDO)
+ vundkind = VCHNG;
+ vmoving = 0;
+ CP(vutmp, linebuf);
+
+ /*
+ * If this is a repeated command, then suppress
+ * fake insert mode on dumb terminals which looks
+ * ridiculous and wastes lots of time even at 9600B.
+ */
+ if (vglobp)
+ hold = HOLDQIK;
+ vappend(c, cnt, 0);
+ continue;
+
+ /*
+ * ^? An attention, normally a ^?, just beeps.
+ * If you are a vi command within ex, then
+ * two ATTN's will drop you back to command mode.
+ */
+case_ATTN:
+ beep();
+ if (initev || peekkey() != ATTN)
+ continue;
+ /* fall into... */
+
+ /*
+ * ^\ A quit always gets command mode.
+ */
+ case QUIT:
+ /*
+ * Have to be careful if we were called
+ * g/xxx/vi
+ * since a return will just start up again.
+ * So we simulate an interrupt.
+ */
+ if (inglobal)
+ onintr(SIGINT);
+ /* fall into... */
+
+#ifdef notdef
+ /*
+ * q Quit back to command mode, unless called as
+ * vi on command line in which case dont do it
+ */
+ case 'q': /* quit */
+ if (initev) {
+ vsave();
+ CATCH
+ error(catgets(catd, 1, 226,
+ "Q gets ex command mode, :q leaves vi"));
+ ENDCATCH
+ splitw = 0;
+ getDOT();
+ vrepaint(cursor);
+ continue;
+ }
+#endif
+ /* fall into... */
+
+ /*
+ * Q Is like q, but always gets to command mode
+ * even if command line invocation was as vi.
+ */
+ case 'Q':
+ vsave();
+ /*
+ * If we are in the middle of a macro, throw away
+ * the rest and fix up undo.
+ * This code copied from getbr().
+ */
+ if (vmacp) {
+ vmacp = 0;
+ if (inopen == -1) /* don't screw up undo for esc esc */
+ vundkind = VMANY;
+ inopen = 1; /* restore old setting now that macro done */
+ }
+ return;
+
+
+ /*
+ * ZZ Like :x
+ */
+ case 'Z':
+ forbid(getkey() != 'Z');
+ oglobp = globp;
+ globp = "x";
+ vclrech(0);
+ goto gogo;
+
+ /*
+ * P Put back text before cursor or before current
+ * line. If text was whole lines goes back
+ * as whole lines. If part of a single line
+ * or parts of whole lines splits up current
+ * line to form many new lines.
+ * May specify a named buffer, or the delete
+ * saving buffers 1-9.
+ *
+ * p Like P but after rather than before.
+ */
+ case 'P':
+ case 'p':
+ vmoving = 0;
+#ifdef notdef
+ forbid (!vreg && value(UNDOMACRO) && inopen < 0);
+#endif
+ /*
+ * If previous delete was partial line, use an
+ * append or insert to put it back so as to
+ * use insert mode on intelligent terminals.
+ */
+ if (!vreg && DEL[0]) {
+ forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
+ vglobp = DEL;
+ ungetkey(c == 'p' ? 'a' : 'i');
+ goto reread;
+ }
+
+ /*
+ * If a register wasn't specified, then make
+ * sure there is something to put back.
+ */
+ forbid (!vreg && unddol == dol);
+ /*
+ * If we just did a macro the whole buffer is in
+ * the undo save area. We don't want to put THAT.
+ */
+ forbid (vundkind == VMANY && undkind==UNDALL);
+ vsave();
+ vmacchng(1);
+ setLAST();
+ i = 0;
+ if (vreg && partreg(vreg) || !vreg && pkill[0]) {
+ /*
+ * Restoring multiple lines which were partial
+ * lines; will leave cursor in middle
+ * of line after shoving restored text in to
+ * split the current line.
+ */
+ i++;
+ if (c == 'p' && *cursor)
+ cursor += skipright(linebuf, cursor);
+ } else {
+ /*
+ * In whole line case, have to back up dot
+ * for P; also want to clear cursor so
+ * cursor will eventually be positioned
+ * at the beginning of the first put line.
+ */
+ cursor = 0;
+ if (c == 'P') {
+ dot--, vcline--;
+ c = 'p';
+ }
+ }
+ killU();
+
+ /*
+ * The call to putreg can potentially
+ * bomb since there may be nothing in a named buffer.
+ * We thus put a catch in here. If we didn't and
+ * there was an error we would end up in command mode.
+ */
+ addr = dol; /* old dol */
+ CATCH
+ vremote(1, vreg ? putreg : put, vreg);
+ ONERR
+ if (vreg == -1) {
+ splitw = 0;
+ if (op == 'P')
+ dot++, vcline++;
+ goto pfixup;
+ }
+ ENDCATCH
+ splitw = 0;
+ nlput = dol - addr + 1;
+ if (!i) {
+ /*
+ * Increment undap1, undap2 to make up
+ * for their incorrect initialization in the
+ * routine vremote before calling put/putreg.
+ */
+ if (FIXUNDO)
+ undap1++, undap2++;
+ vcline++;
+ nlput--;
+
+ /*
+ * After a put want current line first line,
+ * and dot was made the last line put in code
+ * run so far. This is why we increment vcline
+ * above and decrease dot here.
+ */
+ dot -= nlput - 1;
+ }
+#ifdef TRACE
+ if (trace)
+ fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
+#endif
+ vreplace(vcline, i, nlput);
+ if (state != VISUAL) {
+ /*
+ * Special case in open mode.
+ * Force action on the screen when a single
+ * line is put even if it is identical to
+ * the current line, e.g. on YP; otherwise
+ * you can't tell anything happened.
+ */
+ vjumpto(dot, cursor, '.');
+ continue;
+ }
+pfixup:
+ vrepaint(cursor);
+ vfixcurs();
+ continue;
+
+ /*
+ * ^^ Return to previous file.
+ * Like a :e #, and thus can be used after a
+ * "No Write" diagnostic.
+ */
+ case CTRL('^'):
+ forbid (hadcnt);
+ vsave();
+ ckaw();
+ oglobp = globp;
+ if (value(AUTOWRITE))
+ globp = "e! #";
+ else
+ globp = "e #";
+ goto gogo;
+
+ /*
+ * ^] Takes word after cursor as tag, and then does
+ * tag command. Read ``go right to''.
+ */
+ case CTRL(']'):
+ grabtag();
+ oglobp = globp;
+ globp = "tag";
+ goto gogo;
+
+ /*
+ * & Like :&
+ */
+ case '&':
+ oglobp = globp;
+ globp = "&";
+ goto gogo;
+
+ /*
+ * ^G Bring up a status line at the bottom of
+ * the screen, like a :file command.
+ *
+ * BUG: Was ^S but doesn't work in cbreak mode
+ */
+ case CTRL('g'):
+ oglobp = globp;
+ globp = "file";
+gogo:
+ addr = dot;
+ vsave();
+ goto doinit;
+
+#ifdef SIGTSTP
+ /*
+ * ^Z: suspend editor session and temporarily return
+ * to shell. Only works with Berkeley/IIASA process
+ * control in kernel.
+ */
+ case CTRL('z'):
+ forbid(dosusp == 0 || !ldisc);
+ vsave();
+ oglobp = globp;
+ globp = "stop";
+ goto gogo;
+#endif
+
+ /*
+ * : Read a command from the echo area and
+ * execute it in command mode.
+ */
+ case ':':
+ forbid (hadcnt);
+ vsave();
+ i = tchng;
+ addr = dot;
+ if (readecho(c)) {
+ esave[0] = 0;
+ goto fixup;
+ }
+ getDOT();
+ /*
+ * Use the visual undo buffer to store the global
+ * string for command mode, since it is idle right now.
+ */
+ oglobp = globp;
+ CP(vutmp, genbuf+1);
+ globp = vutmp;
+doinit:
+ esave[0] = 0;
+ fixech();
+
+ /*
+ * Have to finagle around not to lose last
+ * character after this command (when run from ex
+ * command mode). This is clumsy.
+ */
+ d = peekc; ungetchar(0);
+ if (shouldpo) {
+ /*
+ * So after a "Hit return..." ":", we do
+ * another "Hit return..." the next time
+ */
+ pofix();
+ shouldpo = 0;
+ }
+ CATCH
+ /*
+ * Save old values of options so we can
+ * notice when they change; switch into
+ * cooked mode so we are interruptible.
+ */
+ onumber = value(NUMBER);
+ olist = value(LIST);
+ OPline = Pline;
+ OPutchar = Putchar;
+ commands(1, 1);
+ if (dot == zero && dol > zero)
+ dot = one;
+ ONERR
+ copy(esave, vtube[WECHO],
+ TUBECOLS * sizeof *esave);
+ ENDCATCH
+ fixol();
+ Pline = OPline;
+ Putchar = OPutchar;
+ ungetchar(d);
+ if (globp && tflag < 0) {
+ tflag = 0;
+ goto doinit;
+ }
+ globp = oglobp;
+
+ /*
+ * If we ended up with no lines in the buffer, make
+ * a line, and don't consider the buffer changed.
+ */
+ if (dot == zero) {
+ fixzero();
+ /*synced();*/
+ }
+ splitw = 0;
+
+ /*
+ * Special case: did list/number options change?
+ */
+ if (onumber != value(NUMBER))
+ setnumb(value(NUMBER));
+ if (olist != value(LIST))
+ setlist(value(LIST));
+
+fixup:
+ /*
+ * If a change occurred, other than
+ * a write which clears changes, then
+ * we should allow an undo even if .
+ * didn't move.
+ *
+ * BUG: You can make this wrong by
+ * tricking around with multiple commands
+ * on one line of : escape, and including
+ * a write command there, but its not
+ * worth worrying about.
+ */
+ if (FIXUNDO && tchng && tchng != i)
+ vundkind = VMANY, cursor = 0;
+
+ /*
+ * If we are about to do another :, hold off
+ * updating of screen.
+ */
+ if (vcnt < 0 && Peekkey == ':') {
+ getDOT();
+ shouldpo = 1;
+ continue;
+ }
+ shouldpo = 0;
+
+ /*
+ * In the case where the file being edited is
+ * new; e.g. if the initial state hasn't been
+ * saved yet, then do so now.
+ */
+ if (unddol == truedol) {
+ vundkind = VNONE;
+ Vlines = lineDOL();
+ if (!inglobal)
+ savevis();
+ addr = zero;
+ vcnt = 0;
+ if (esave[0] == 0)
+ copy(esave, vtube[WECHO],
+ TUBECOLS * sizeof *esave);
+ }
+
+ /*
+ * If the current line moved reset the cursor position.
+ */
+ if (dot != addr) {
+ vmoving = 0;
+ cursor = 0;
+ }
+
+ /*
+ * If current line is not on screen or if we are
+ * in open mode and . moved, then redraw.
+ */
+ i = vcline + (dot - addr);
+ if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
+ if (state == CRTOPEN)
+ vup1();
+ if (vcnt > 0)
+ vcnt = 0;
+ vjumpto(dot, (char *) 0, '.');
+ } else {
+ /*
+ * Current line IS on screen.
+ * If we did a [Hit return...] then
+ * restore vcnt and clear screen if in visual
+ */
+ vcline = i;
+ if (vcnt < 0) {
+ vcnt = -vcnt;
+ if (state == VISUAL)
+ vclear();
+ else if (state == CRTOPEN) {
+ vcnt = 0;
+ }
+ }
+
+ /*
+ * Limit max value of vcnt based on $
+ */
+ i = vcline + lineDOL() - lineDOT() + 1;
+ if (i < vcnt)
+ vcnt = i;
+
+ /*
+ * Dirty and repaint.
+ */
+ vdirty(0, TLINES);
+ vrepaint(cursor);
+ }
+
+ /*
+ * If in visual, put back the echo area
+ * if it was clobberred.
+ */
+ if (state == VISUAL) {
+ int sdc = destcol, sdl = destline;
+
+ splitw++;
+ vigoto(WECHO, 0);
+ for (i = 0; i < TUBECOLS - 1; i++) {
+ if (esave[i] == 0)
+ break;
+ vputchar(esave[i]);
+ }
+ splitw = 0;
+ vgoto(sdl, sdc);
+ }
+ continue;
+
+ /*
+ * u undo the last changing command.
+ */
+ case 'u':
+ vundo(1);
+ continue;
+
+ /*
+ * U restore current line to initial state.
+ */
+ case 'U':
+ vUndo();
+ continue;
+
+fonfon:
+ beep();
+ vmacp = 0;
+ inopen = 1; /* might have been -1 */
+ continue;
+ }
+
+ /*
+ * Rest of commands are decoded by the operate
+ * routine.
+ */
+ operate(c, cnt);
+ }
+}
+
+/*
+ * Grab the word after the cursor so we can look for it as a tag.
+ */
+void
+grabtag(void)
+{
+ register char *cp, *dp;
+
+ cp = vpastwh(cursor);
+ if (*cp) {
+ dp = lasttag;
+ do {
+ if (dp < &lasttag[sizeof lasttag - 2])
+ *dp++ = *cp;
+ cp++;
+ } while (isalpha(*cp&0377) || isdigit(*cp&0377)
+ || *cp == '_'
+#ifdef LISPCODE
+ || (value(LISP) && *cp == '-')
+#endif /* LISPCODE */
+ );
+ *dp++ = 0;
+ }
+}
+
+/*
+ * Before appending lines, set up addr1 and
+ * the command mode undo information.
+ */
+void
+prepapp(void)
+{
+
+ addr1 = dot;
+ deletenone();
+ addr1++;
+ appendnone();
+}
+
+/*
+ * Execute function f with the address bounds addr1
+ * and addr2 surrounding cnt lines starting at dot.
+ */
+void
+vremote(int cnt, void (*f)(int), int arg)
+{
+ register int oing = inglobal;
+
+ addr1 = dot;
+ addr2 = dot + cnt - 1;
+ inglobal = 0;
+ if (FIXUNDO)
+ undap1 = undap2 = dot;
+ (*f)(arg);
+ inglobal = oing;
+ if (FIXUNDO)
+ vundkind = VMANY;
+ vmcurs = 0;
+}
+
+/*
+ * Save the current contents of linebuf, if it has changed.
+ */
+void
+vsave(void)
+{
+ char temp[LBSIZE];
+
+ CP(temp, linebuf);
+ if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
+ /*
+ * If the undo state is saved in the temporary buffer
+ * vutmp, then we sync this into the temp file so that
+ * we will be able to undo even after we have moved off
+ * the line. It would be possible to associate a line
+ * with vutmp but we assume that vutmp is only associated
+ * with line dot (e.g. in case ':') above, so beware.
+ */
+ prepapp();
+ CP(linebuf, vutmp);
+ putmark(dot);
+ vremote(1, yank, 0);
+ vundkind = VMCHNG;
+ notecnt = 0;
+ undkind = UNDCHANGE;
+ }
+ /*
+ * Get the line out of the temp file and do nothing if it hasn't
+ * changed. This may seem like a loss, but the line will
+ * almost always be in a read buffer so this may well avoid disk i/o.
+ */
+ getDOT();
+ if (strcmp(linebuf, temp) == 0)
+ return;
+ strcLIN(temp);
+ putmark(dot);
+}
+
+#undef forbid
+#define forbid(a) if (a) { beep(); return; }
+
+/*
+ * Do a z operation.
+ * Code here is rather long, and very uninteresting.
+ */
+void
+vzop(int hadcnt, int cnt, register int c)
+{
+ register line *addr;
+
+ if (state != VISUAL) {
+ /*
+ * Z from open; always like a z=.
+ * This code is a mess and should be cleaned up.
+ */
+ vmoveitup(1, 1);
+ vgoto(outline, 0);
+ ostop(normf);
+ setoutt();
+ addr2 = dot;
+ vclear();
+ destline = WECHO;
+ zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
+ if (state == CRTOPEN)
+ putnl();
+ putNFL();
+ termreset();
+ Outchar = vputchar;
+ ignore(ostart());
+ vcnt = 0;
+ outline = destline = 0;
+ vjumpto(dot, cursor, 0);
+ return;
+ }
+ if (hadcnt) {
+ addr = zero + cnt;
+ if (addr < one)
+ addr = one;
+ if (addr > dol)
+ addr = dol;
+ markit(addr);
+ } else
+ switch (c) {
+
+ case '+':
+ addr = dot + vcnt - vcline;
+ break;
+
+ case '^':
+ addr = dot - vcline - 1;
+ forbid (addr < one);
+ c = '-';
+ break;
+
+ default:
+ addr = dot;
+ break;
+ }
+ switch (c) {
+
+ case '.':
+ case '-':
+ break;
+
+ case '^':
+ forbid (addr <= one);
+ break;
+
+ case '+':
+ forbid (addr >= dol);
+ /* fall into ... */
+
+ case CR:
+ case NL:
+ c = CR;
+ break;
+
+ default:
+ beep();
+ return;
+ }
+ vmoving = 0;
+ vjumpto(addr, NOSTR, c);
+}
+
+cell *
+str2cell(cell *dst, register char *src)
+{
+ register cell *cp = dst;
+
+#ifdef MB
+ if (mb_cur_max > 1) {
+ int c, n;
+ do {
+ nextc(c, src, n);
+ src += n;
+ *cp++ = c;
+ } while (src[-n]);
+ } else
+#endif /* MB */
+ while (*cp++ = *src++ & 0377);
+ return dst;
+}
+
+char *
+cell2str(char *dst, register cell *src)
+{
+ register char *cp = dst;
+
+ while (*cp++ = *src++);
+ return dst;
+}
+
+cell *
+cellcpy(cell *dst, register cell *src)
+{
+ register cell *cp = dst;
+
+ while (*cp++ = *src++);
+ return dst;
+}
+
+size_t
+cellen(register cell *cp)
+{
+ register size_t sz = 0;
+
+ while (*cp++)
+ sz++;
+ return sz;
+}
+
+cell *
+cellcat(cell *dst, register cell *src)
+{
+ register cell *cp = dst;
+
+ while (*cp)
+ cp++;
+ cellcpy(cp, src);
+ return dst;
+}