diff options
Diffstat (limited to 'ex_vmain.c')
-rw-r--r-- | ex_vmain.c | 1442 |
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; +} |