home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Distributions / ucb / spencer_2bsd.tar.gz / 2bsd.tar / src / ex / ex_vmain.c < prev    next >
C/C++ Source or Header  |  1980-02-17  |  23KB  |  1,073 lines

  1. /* Copyright (c) 1979 Regents of the University of California */
  2. #include "ex.h"
  3. #include "ex_tty.h"
  4. #include "ex_vis.h"
  5.  
  6. /*
  7.  * This is the main routine for visual.
  8.  * We here decode the count and possible named buffer specification
  9.  * preceding a command and interpret a few of the commands.
  10.  * Commands which involve a target (i.e. an operator) are decoded
  11.  * in the routine operate in ex_voperate.c.
  12.  */
  13.  
  14. #define    forbid(a)    { if (a) goto fonfon; }
  15.  
  16. vmain()
  17. {
  18.     register int c, cnt, i;
  19.     char esave[TUBECOLS];
  20.     char *oglobp;
  21.     char d;
  22.     line *addr;
  23.     int ind;
  24.     int onumber, olist, (*OPline)(), (*OPutchar)();
  25.  
  26.     /*
  27.      * If we started as a vi command (on the command line)
  28.      * then go process initial commands (recover, next or tag).
  29.      */
  30.     if (initev) {
  31.         oglobp = globp;
  32.         globp = initev;
  33.         hadcnt = cnt = 0;
  34.         i = tchng;
  35.         addr = dot;
  36.         goto doinit;
  37.     }
  38.  
  39.     /*
  40.      * NB:
  41.      *
  42.      * The current line is always in the line buffer linebuf,
  43.      * and the cursor at the position cursor.  You should do
  44.      * a vsave() before moving off the line to make sure the disk
  45.      * copy is updated if it has changed, and a getDOT() to get
  46.      * the line back if you mung linebuf.  The motion
  47.      * routines in ex_vwind.c handle most of this.
  48.      */
  49.     for (;;) {
  50.         /*
  51.          * Decode a visual command.
  52.          * First sync the temp file if there has been a reasonable
  53.          * amount of change.  Clear state for decoding of next
  54.          * command.
  55.          */
  56.         TSYNC();
  57.         vglobp = 0;
  58.         vreg = 0;
  59.         hold = 0;
  60.         wcursor = 0;
  61.         Xhadcnt = hadcnt = 0;
  62.         Xcnt = cnt = 1;
  63.         splitw = 0;
  64.         if (i = holdupd) {
  65.             if (state == VISUAL)
  66.                 ignore(peekkey());
  67.             holdupd = 0;
  68. /*
  69.             if (LINE(0) < ZERO) {
  70.                 vclear();
  71.                 vcnt = 0;
  72.                 i = 3;
  73.             }
  74. */
  75.             if (state != VISUAL) {
  76.                 vcnt = 0;
  77.                 vsave();
  78.                 vrepaint(cursor);
  79.             } else if (i == 3)
  80.                 vredraw(WTOP);
  81.             else
  82.                 vsync(WTOP);
  83.             vfixcurs();
  84.         }
  85.  
  86.         /*
  87.          * Gobble up counts and named buffer specifications.
  88.          */
  89.         for (;;) {
  90.             if (isdigit(peekkey()) && peekkey() != '0') {
  91.                 hadcnt = 1;
  92.                 cnt = vgetcnt();
  93.                 forbid (cnt <= 0);
  94.             }
  95.             if (peekkey() != '"')
  96.                 break;
  97.             ignore(getkey()), c = getkey();
  98.             /*
  99.              * Buffer names be letters or digits.
  100.              * But not '0' as that is the source of
  101.              * an 'empty' named buffer spec in the routine
  102.              * kshift (see ex_temp.c).
  103.              */
  104.             forbid (c == '0' || !isalpha(c) && !isdigit(c));
  105.             vreg = c;
  106.         }
  107. reread:
  108.         /*
  109.          * Come to reread from below after some macro expansions.
  110.          * The call to map allows use of function key pads
  111.          * by performing a terminal dependent mapping of inputs,
  112.          * but notably not of two character inputs starting with
  113.          * an escape (as from a VT52 keypad).  This is useful for
  114.          * e.g. DM1520's however.
  115.          */
  116.         op = c = map(getkey());
  117.  
  118.         /*
  119.          * Begin to build an image of this command for possible
  120.          * later repeat in the buffer workcmd.  It will be copied
  121.          * to lastcmd by the routine setLAST
  122.          * if/when completely specified.
  123.          */
  124.         lastcp = workcmd;
  125.         if (!vglobp)
  126.             *lastcp++ = c;
  127.  
  128.         /*
  129.          * First level command decode.
  130.          */
  131.         switch (c) {
  132.  
  133.         /*
  134.          * ^L        Clear screen e.g. after transmission error.
  135.          */
  136.         case CTRL(l):
  137.             vclear();
  138.             vdirty(0, vcnt);
  139.             /* fall into... */
  140.  
  141.         /*
  142.          * ^R        Retype screen, getting rid of @ lines.
  143.          *        If in open, equivalent to ^L.
  144.          */
  145.         case CTRL(r):
  146.             if (state != VISUAL) {
  147.                 /*
  148.                  * Get a clean line, throw away the
  149.                  * memory of what is displayed now,
  150.                  * and move back onto the current line.
  151.                  */
  152.                 vclean();
  153.                 vcnt = 0;
  154.                 vmoveto(dot, cursor, 0);
  155.                 continue;
  156.             }
  157.             vredraw(WTOP);
  158.             /*
  159.              * Weird glitch -- when we enter visual
  160.              * in a very small window we may end up with
  161.              * no lines on the screen because the line
  162.              * at the top is too long.  This forces the screen
  163.              * to be expanded to make room for it (after
  164.              * we have printed @'s ick showing we goofed).
  165.              */
  166.             if (vcnt == 0)
  167.                 vrepaint(cursor);
  168.             vfixcurs();
  169.             continue;
  170.  
  171.         /*
  172.          * $        Escape just cancels the current command
  173.          *        with a little feedback.  It would be
  174.          *        nice to handle function pads sending ESC-X;
  175.          *        here, using something else to replace ESC.
  176.          */
  177.         case ESCAPE:
  178.             beep();
  179.             continue;
  180.  
  181.         /*
  182.          * .        Repeat the last (modifying) open/visual command.
  183.          */
  184.         case '.':
  185.             /*
  186.              * Check that there was a last command, and
  187.              * take its count and named buffer unless they
  188.              * were given anew.  Special case if last command
  189.              * referenced a numeric named buffer -- increment
  190.              * the number and go to a named buffer again.
  191.              * This allows a sequence like "1pu.u.u...
  192.              * to successively look for stuff in the kill chain
  193.              * much as one does in EMACS with C-Y and M-Y.
  194.              */
  195.             forbid (lastcmd[0] == 0);
  196.             if (hadcnt)
  197.                 lastcnt = cnt;
  198.             if (vreg)
  199.                 lastreg = vreg;
  200.             else if (isdigit(lastreg) && lastreg < '9')
  201.                 lastreg++;
  202.             vreg = lastreg;
  203.             cnt = lastcnt;
  204.             hadcnt = lasthad;
  205.             vglobp = lastcmd;
  206.             goto reread;
  207.  
  208.         /*
  209.          * ^U        Scroll up.  A count sticks around for
  210.          *        future scrolls as the scroll amount.
  211.          *        Attempt to hold the indentation from the
  212.          *        top of the screen (in logical lines).
  213.          *
  214.          * BUG:        A ^U near the bottom of the screen
  215.          *        on a dumb terminal (which can't roll back)
  216.          *        causes the screen to be cleared and then
  217.          *        redrawn almost as it was.  In this case
  218.          *        one should simply move the cursor.
  219.          */
  220.         case CTRL(u):
  221.             if (hadcnt)
  222.                 vSCROLL = cnt;
  223.             cnt = vSCROLL;
  224.             if (state == VISUAL)
  225.                 ind = vcline, cnt += ind;
  226.             else
  227.                 ind = 0;
  228.             vmoving = 0;
  229.             vup(cnt, ind, 1);
  230.             vnline(NOSTR);
  231.             continue;
  232.  
  233.         /*
  234.          * ^D        Scroll down.  Like scroll up.
  235.          */
  236.         case CTRL(d):
  237.             if (hadcnt)
  238.                 vSCROLL = cnt;
  239.             cnt = vSCROLL;
  240.             if (state == VISUAL)
  241.                 ind = vcnt - vcline - 1, cnt += ind;
  242.             else
  243.                 ind = 0;
  244.             vmoving = 0;
  245.             vdown(cnt, ind, 1);
  246.             vnline(NOSTR);
  247.             continue;
  248.  
  249.         /*
  250.          * m        Mark position in mark register given
  251.          *        by following letter.  Return is
  252.          *        accomplished via ' or `; former
  253.          *        to beginning of line where mark
  254.          *        was set, latter to column where marked.
  255.          */
  256.         case 'm':
  257.             /*
  258.              * Getesc is generally used when a character
  259.              * is read as a latter part of a command
  260.              * to allow one to hit rubout/escape to cancel
  261.              * what you have typed so far.  These characters
  262.              * are mapped to 0 by the subroutine.
  263.              */
  264.             c = getesc();
  265.             if (c == 0)
  266.                 continue;
  267.  
  268.             /*
  269.              * Markreg checks that argument is a letter
  270.              * and also maps ' and ` to the end of the range
  271.              * to allow '' or `` to reference the previous
  272.              * context mark.
  273.              */
  274.             c = markreg(c);
  275.             forbid (c == 0);
  276.             vsave();
  277.             names[c - 'a'] = (*dot &~ 01);
  278.             ncols[c - 'a'] = cursor;
  279.             anymarks = 1;
  280.             continue;
  281.  
  282.         /*
  283.          * ^F        Window forwards, with 2 lines of continuity.
  284.          *        Count gives new screen size.
  285.          */
  286.         case CTRL(f):
  287.             vsave();
  288.             if (hadcnt)
  289.                 vsetsiz(cnt);
  290.             if (vcnt > 2) {
  291.                 dot += (vcnt - vcline) - 2;
  292.                 vcnt = vcline = 0;
  293.             }
  294.             vzop(0, 0, '+');
  295.             continue;
  296.  
  297.         /*
  298.          * ^B        Window backwards, with 2 lines of continuity.
  299.          *        Inverse of ^F.
  300.          */
  301.         case CTRL(b):
  302.             vsave();
  303.             if (hadcnt)
  304.                 vsetsiz(cnt);
  305.             if (one + vcline != dot && vcnt > 2) {
  306.                 dot -= vcline - 2;
  307.                 vcnt = vcline = 0;
  308.             }
  309.             vzop(0, 0, '^');
  310.             continue;
  311.  
  312.         /*
  313.          * z        Screen adjustment, taking a following character:
  314.          *            z<CR>        current line to top
  315.          *            z<NL>        like z<CR>
  316.          *            z-        current line to bottom
  317.          *        also z+, z^ like ^F and ^B.
  318.          *        A preceding count is line to use rather
  319.          *        than current line.  A count between z and
  320.          *        specifier character changes the screen size
  321.          *        for the redraw.
  322.          *
  323.          */
  324.         case 'z':
  325.             if (state == VISUAL) {
  326.                 i = vgetcnt();
  327.                 if (i > 0)
  328.                     vsetsiz(i);
  329.                 c = getesc();
  330.                 if (c == 0)
  331.                     continue;
  332.             }
  333.             vsave();
  334.             vzop(hadcnt, cnt, c);
  335.             continue;
  336.  
  337.         /*
  338.          * Y        Yank lines, abbreviation for y_ or yy.
  339.          *        Yanked lines can be put later if no
  340.          *        changes intervene, or can be put in named
  341.          *        buffers and put anytime in this session.
  342.          */
  343.         case 'Y':
  344.             ungetkey('_');
  345.             c = 'y';
  346.             break;
  347.  
  348.         /*
  349.          * J        Join lines, 2 by default.  Count is number
  350.          *        of lines to join (no join operator sorry.)
  351.          */
  352.         case 'J':
  353.             forbid (dot == dol);
  354.             if (cnt == 1)
  355.                 cnt = 2;
  356.             if (cnt > (i = dol - dot + 1))
  357.                 cnt = i;
  358.             vsave();
  359.             setLAST();
  360.             cursor = strend(linebuf);
  361.             vremote(cnt, join, 0);
  362.             notenam = "join";
  363.             vmoving = 0;
  364.             killU();
  365.             vreplace(vcline, cnt, 1);
  366.             if (!*cursor && cursor > linebuf)
  367.                 cursor--;
  368.             if (notecnt == 2)
  369.                 notecnt = 0;
  370.             vrepaint(cursor);
  371.             continue;
  372.  
  373.         /*
  374.          * S        Substitute text for whole lines, abbrev for c_.
  375.          *        Count is number of lines to change.
  376.          */
  377.         case 'S':
  378.             ungetkey('_');
  379.             c = 'c';
  380.             break;
  381.  
  382.         /*
  383.          * O        Create a new line above current and accept new
  384.          *        input text, to an escape, there.
  385.          *        A count specifies, for dumb terminals when
  386.          *        slowopen is not set, the number of physical
  387.          *        line space to open on the screen.
  388.          *
  389.          * o        Like O, but opens lines below.
  390.          */
  391.         case 'O':
  392.         case 'o':
  393.             voOpen(c, cnt);
  394.             continue;
  395.  
  396.         /*
  397.          * C        Change text to end of line, short for c$.
  398.          */
  399.         case 'C':
  400.             if (*cursor) {
  401.                 ungetkey('$'), c = 'c';
  402.                 break;
  403.             }
  404.             goto appnd;
  405.  
  406.         /*
  407.          * A        Append at end of line, short for $a.
  408.          */
  409.         case 'A':
  410.             operate('$', 1);
  411. appnd:
  412.             c = 'a';
  413.             /* fall into ... */
  414.  
  415.         /*
  416.          * a        Appends text after cursor.  Text can continue
  417.          *        through arbitrary number of lines.
  418.          */
  419.         case 'a':
  420.             if (*cursor) {
  421.                 if (state == HARDOPEN)
  422.                     putchar(*cursor);
  423.                 cursor++;
  424.             }
  425.             goto insrt;
  426.  
  427.         /*
  428.          * I        Insert at beginning of whitespace of line,
  429.          *        short for ^i.
  430.          */
  431.         case 'I':
  432.             operate('^', 1);
  433.             c = 'i';
  434.             /* fall into ... */
  435.  
  436.         /*
  437.          * R        Replace characters, one for one, by input
  438.          *        (logically), like repeated r commands.
  439.          *
  440.          * BUG:        This is like the typeover mode of many other
  441.          *        editors, and is only rarely useful.  Its
  442.          *        implementation is a hack in a low level
  443.          *        routine and it doesn't work very well, e.g.
  444.          *        you can't move around within a R, etc.
  445.          */
  446.         case 'R':
  447.             /* fall into... */
  448.  
  449.         /*
  450.          * i        Insert text to an escape in the buffer.
  451.          *        Text is arbitrary.  This command reminds of
  452.          *        the i command in bare teco.
  453.          */
  454.         case 'i':
  455. insrt:
  456.             /*
  457.              * Common code for all the insertion commands.
  458.              * Save for redo, position cursor, prepare for append
  459.              * at command and in visual undo.  Note that nothing
  460.              * is doomed, unless R when all is, and save the
  461.              * current line in a the undo temporary buffer.
  462.              */
  463.             setLAST();
  464.             vcursat(cursor);
  465.             prepapp();
  466.             vnoapp();
  467.             doomed = c == 'R' ? 10000 : 0;
  468.             vundkind = VCHNG;
  469.             CP(vutmp, linebuf);
  470.  
  471.             /*
  472.              * If this is a repeated command, then suppress
  473.              * fake insert mode on dumb terminals which looks
  474.              * ridiculous and wastes lots of time even at 9600B.
  475.              */
  476.             if (vglobp)
  477.                 hold = HOLDQIK;
  478.             vappend(c, cnt, 0);
  479.             continue;
  480.  
  481.         /*
  482.          * ^?        An attention, normally a ^?, just beeps.
  483.          *        If you are a vi command within ex, then
  484.          *        two ATTN's will drop you back to command mode.
  485.          */
  486.         case ATTN:
  487.             beep();
  488.             if (initev || peekkey() != ATTN)
  489.                 continue;
  490.             /* fall into... */
  491.  
  492.         /*
  493.          * ^\        A quit always gets command mode.
  494.          */
  495.         case QUIT:
  496.             /*
  497.              * Have to be careful if we were called
  498.              *    g/xxx/vi
  499.              * since a return will just start up again.
  500.              * So we simulate an interrupt.
  501.              */
  502.             if (inglobal)
  503.                 onintr();
  504.             /* fall into... */
  505.  
  506.         /*
  507.          * q        Quit back to command mode, unless called as
  508.          *        vi on command line in which case dont do it
  509.          */
  510.         case 'q':    /* quit */
  511.             if (initev) {
  512.                 vsave();
  513.                 CATCH
  514.                     error("Q gets ex command mode, :q leaves vi");
  515.                 ENDCATCH
  516.                 splitw = 0;
  517.                 getDOT();
  518.                 vrepaint(cursor);
  519.                 continue;
  520.             }
  521.             /* fall into... */
  522.  
  523.         /*
  524.          * Q        Is like q, but always gets to command mode
  525.          *        even if command line invocation was as vi.
  526.          */
  527.         case 'Q':
  528.             vsave();
  529.             return;
  530.  
  531.         /*
  532.          * P        Put back text before cursor or before current
  533.          *        line.  If text was whole lines goes back
  534.          *        as whole lines.  If part of a single line
  535.          *        or parts of whole lines splits up current
  536.          *        line to form many new lines.
  537.          *        May specify a named buffer, or the delete
  538.          *        saving buffers 1-9.
  539.          *
  540.          * p        Like P but after rather than before.
  541.          */
  542.         case 'P':
  543.         case 'p':
  544.             vmoving = 0;
  545.             /*
  546.              * If previous delete was partial line, use an
  547.              * append or insert to put it back so as to
  548.              * use insert mode on intelligent terminals.
  549.              */
  550.             if (!vreg && DEL[0]) {
  551.                 forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
  552.                 vglobp = DEL;
  553.                 ungetkey(c == 'p' ? 'a' : 'i');
  554.                 goto reread;
  555.             }
  556.  
  557.             /*
  558.              * If a register wasn't specified, then make
  559.              * sure there is something to put back.
  560.              */
  561.             forbid (!vreg && unddol == dol);
  562.             vsave();
  563.             setLAST();
  564.             i = 0;
  565.             if (vreg && partreg(vreg) || !vreg && pkill[0]) {
  566.                 /*
  567.                  * Restoring multiple lines which were partial
  568.                  * lines; will leave cursor in middle
  569.                  * of line after shoving restored text in to
  570.                  * split the current line.
  571.                  */
  572.                 i++;
  573.                 if (c == 'p' && *cursor)
  574.                     cursor++;
  575.             } else {
  576.                 /*
  577.                  * In whole line case, have to back up dot
  578.                  * for P; also want to clear cursor so
  579.                  * cursor will eventually be positioned
  580.                  * at the beginning of the first put line.
  581.                  */
  582.                 cursor = 0;
  583.                 if (c == 'P') {
  584.                     dot--, vcline--;
  585.                     c = 'p';
  586.                 }
  587.             }
  588.             killU();
  589.  
  590.             /*
  591.              * The call to putreg can potentially
  592.              * bomb since there may be nothing in a named buffer.
  593.              * We thus put a catch in here.  If we didn't and
  594.              * there was an error we would end up in command mode.
  595.              */
  596.             CATCH
  597.                 vremote(1, vreg ? putreg : put, vreg);
  598.             ONERR
  599.                 if (vreg == -1) {
  600.                     splitw = 0;
  601.                     if (op == 'P')
  602.                         dot++, vcline++;
  603.                     goto pfixup;
  604.                 }
  605.             ENDCATCH
  606.             splitw = 0;
  607.             if (!i) {
  608.                 /*
  609.                  * Increment undap1, undap2 to make up
  610.                  * for their incorrect initialization in the
  611.                  * routine vremote before calling put/putreg.
  612.                  */
  613.                 undap1++, undap2++;
  614.                 vcline++;
  615.             }
  616.  
  617.             /*
  618.              * After a put want current line first line,
  619.              * and dot was made the last line put in code run
  620.              * so far.  This is why we increment vcline above,
  621.              * and decrease (usually) dot here.
  622.              */
  623.             dot = undap1;
  624.             vreplace(vcline, i, undap2 - undap1);
  625.             if (state != VISUAL) {
  626.                 /*
  627.                  * Special case in open mode.
  628.                  * Force action on the screen when a single
  629.                  * line is put even if it is identical to
  630.                  * the current line, e.g. on YP; otherwise
  631.                  * you can't tell anything happened.
  632.                  */
  633.                 vjumpto(dot, cursor, '.');
  634.                 continue;
  635.             }
  636. pfixup:
  637.             vrepaint(cursor);
  638.             vfixcurs();
  639.             continue;
  640.  
  641.         /*
  642.          * ^^        Return to previous context.  Like a 't
  643.          *        if that mark is set since tag sets that
  644.          *        mark if it stays in same file.  Else
  645.          *        like a :e #, and thus can be used after a
  646.          *        "No Write" diagnostic.
  647.          */
  648.         case CTRL(^):
  649.             if (hadcnt)
  650.                 vsetsiz(cnt);
  651.             addr = getmark('t');
  652.             if (addr != 0) {
  653.                 markit(addr);
  654.                 vupdown(addr - dot, NOSTR);
  655.                 continue;
  656.             }
  657.             oglobp = globp;
  658.             globp = "e #";
  659.             goto gogo;
  660.  
  661.         /*
  662.          * ^]        Takes word after cursor as tag, and then does
  663.          *        tag command.  Read ``go right to''.
  664.          */
  665.         case CTRL(]):
  666.             grabtag();
  667.             oglobp = globp;
  668.             globp = "tag";
  669.             goto gogo;
  670.             
  671.         /*
  672.          * ^G        Bring up a status line at the bottom of
  673.          *        the screen, like a :file command.
  674.          *
  675.          * BUG:        Was ^S but doesn't work in cbreak mode
  676.          */
  677.         case CTRL(g):
  678.             oglobp = globp;
  679.             globp = "file";
  680. gogo:
  681.             addr = dot;
  682.             vsave();
  683.             goto doinit;
  684.  
  685.         /*
  686.          * :        Read a command from the echo area and
  687.          *        execute it in command mode.
  688.          */
  689.         case ':':
  690.             if (hadcnt)
  691.                 vsetsiz(cnt);
  692.             vsave();
  693.             i = tchng;
  694.             addr = dot;
  695.             if (readecho(c)) {
  696.                 esave[0] = 0;
  697.                 goto fixup;
  698.             }
  699.             /*
  700.              * Use the visual undo buffer to store the global
  701.              * string for command mode, since it is idle right now.
  702.              */
  703.             oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
  704. doinit:
  705.             esave[0] = 0;
  706.             fixech();
  707.  
  708.             /*
  709.              * Have to finagle around not to lose last
  710.              * character after this command (when run from ex
  711.              * command mode).  This is clumsy.
  712.              */
  713.             d = peekc; ungetchar(0);
  714.             CATCH
  715.                 /*
  716.                  * Save old values of options so we can
  717.                  * notice when they change; switch into
  718.                  * cooked mode so we are interruptible.
  719.                  */
  720.                 onumber = value(NUMBER);
  721.                 olist = value(LIST);
  722.                 OPline = Pline;
  723.                 OPutchar = Putchar;
  724. #ifdef V6
  725.                 vcook();
  726. #endif
  727.                 commands(1, 1);
  728.                 if (dot == zero && dol > zero)
  729.                     dot = one;
  730. #ifdef V6
  731.                 vraw();
  732. #endif
  733.             ONERR
  734. #ifdef V6
  735.                 vraw();
  736. #endif
  737.                 copy(esave, vtube[WECHO], TUBECOLS);
  738.             ENDCATCH
  739.             fixol();
  740.             Pline = OPline;
  741.             Putchar = OPutchar;
  742.             ungetchar(d);
  743.             globp = oglobp;
  744.  
  745.             /*
  746.              * If we ended up with no lines in the buffer, make
  747.              * a line, and don't consider the buffer changed.
  748.              */
  749.             if (dot == zero) {
  750.                 fixzero();
  751.                 sync();
  752.             }
  753.             splitw = 0;
  754.  
  755.             /*
  756.              * Special case: did list/number options change?
  757.              */
  758.             if (onumber != value(NUMBER))
  759.                 setnumb(value(NUMBER));
  760.             if (olist != value(LIST))
  761.                 setlist(value(LIST));
  762.  
  763. fixup:
  764.             /*
  765.              * If a change occurred, other than
  766.              * a write which clears changes, then
  767.              * we should allow an undo even if .
  768.              * didn't move.
  769.              *
  770.              * BUG: You can make this wrong by
  771.              * tricking around with multiple commands
  772.              * on one line of : escape, and including
  773.              * a write command there, but its not
  774.              * worth worrying about.
  775.              */
  776.             if (tchng && tchng != i)
  777.                 vundkind = VMANY, cursor = 0;
  778.  
  779.             /*
  780.              * If we are about to do another :, hold off
  781.              * updating of screen.
  782.              */
  783.             if (vcnt < 0 && Peekkey == ':') {
  784.                 getDOT();
  785.                 continue;
  786.             }
  787.  
  788.             /*
  789.              * In the case where the file being edited is
  790.              * new; e.g. if the initial state hasn't been
  791.              * saved yet, then do so now.
  792.              */
  793.             if (unddol == truedol) {
  794.                 vundkind = VNONE;
  795.                 Vlines = lineDOL();
  796.                 if (!inglobal)
  797.                     savevis();
  798.                 addr = zero;
  799.                 vcnt = 0;
  800.                 if (esave[0] == 0)
  801.                     copy(esave, vtube[WECHO], TUBECOLS);
  802.             }
  803.  
  804.             /*
  805.              * If the current line moved reset the cursor position.
  806.              */
  807.             if (dot != addr) {
  808.                 vmoving = 0;
  809.                 cursor = 0;
  810.             }
  811.  
  812.             /*
  813.              * If current line is not on screen or if we are
  814.              * in open mode and . moved, then redraw.
  815.              */
  816.             i = vcline + (dot - addr);
  817.             if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
  818.                 if (state == CRTOPEN)
  819.                     vup1();
  820.                 if (vcnt > 0)
  821.                     vcnt = 0;
  822.                 vjumpto(dot, (char *) 0, '.');
  823.             } else {
  824.                 /*
  825.                  * Current line IS on screen.
  826.                  * If we did a [Hit return...] then
  827.                  * restore vcnt and clear screen if in visual
  828.                  */
  829.                 vcline = i;
  830.                 if (vcnt < 0) {
  831.                     vcnt = -vcnt;
  832.                     if (state == VISUAL)
  833.                         vclear();
  834.                     else if (state == CRTOPEN)
  835.                         vcnt = 0;
  836.                 }
  837.  
  838.                 /*
  839.                  * Limit max value of vcnt based on $
  840.                  */
  841.                 i = vcline + lineDOL() - lineDOT() + 1;
  842.                 if (i < vcnt)
  843.                     vcnt = i;
  844.                 
  845.                 /*
  846.                  * Dirty and repaint.
  847.                  */
  848.                 vdirty(0, LINES);
  849.                 vrepaint(cursor);
  850.             }
  851.  
  852.             /*
  853.              * If in visual, put back the echo area
  854.              * if it was clobberred.
  855.              */
  856.             if (state == VISUAL) {
  857.                 int sdc = destcol, sdl = destline;
  858.  
  859.                 splitw++;
  860.                 vigoto(WECHO, 0);
  861.                 for (i = 0; i < TUBECOLS - 1; i++) {
  862.                     if (esave[i] == 0)
  863.                         break;
  864.                     vputchar(esave[i]);
  865.                 }
  866.                 splitw = 0;
  867.                 vgoto(sdl, sdc);
  868.             }
  869.             continue;
  870.  
  871.         /*
  872.          * u        undo the last changing command.
  873.          */
  874.         case 'u':
  875.             vundo();
  876.             continue;
  877.  
  878.         /*
  879.          * U        restore current line to initial state.
  880.          */
  881.         case 'U':
  882.             vUndo();
  883.             continue;
  884.  
  885. fonfon:
  886.             beep();
  887.             continue;
  888.         }
  889.  
  890.         /*
  891.          * Rest of commands are decoded by the operate
  892.          * routine.
  893.          */
  894.         operate(c, cnt);
  895.     }
  896. }
  897.  
  898. /*
  899.  * Grab the word after the cursor so we can look for it as a tag.
  900.  */
  901. grabtag()
  902. {
  903.     register char *cp, *dp;
  904.  
  905.     cp = vpastwh(cursor);
  906.     if (*cp) {
  907.         dp = lasttag;
  908.         do {
  909.             if (dp < &lasttag[sizeof lasttag - 2])
  910.                 *dp++ = *cp;
  911.             cp++;
  912.         } while (isalpha(*cp) || isdigit(*cp) || *cp == '_');
  913.         *dp++ = 0;
  914.     }
  915. }
  916.  
  917. /*
  918.  * Before appending lines, set up addr1 and
  919.  * the command mode undo information.
  920.  */
  921. prepapp()
  922. {
  923.  
  924.     addr1 = dot + 1;
  925.     deletenone();
  926.     appendnone();
  927. }
  928.  
  929. /*
  930.  * Execute function f with the address bounds addr1
  931.  * and addr2 surrounding cnt lines starting at dot.
  932.  */
  933. vremote(cnt, f, arg)
  934.     int cnt, (*f)(), arg;
  935. {
  936.     register int oing = inglobal;
  937.  
  938.     addr1 = dot;
  939.     addr2 = dot + cnt - 1;
  940.     undap1 = undap2 = dot;
  941.     inglobal = 0;
  942.     (*f)(arg);
  943.     inglobal = oing;
  944.     vundkind = VMANY;
  945.     vmcurs = 0;
  946. }
  947.  
  948. /*
  949.  * Save the current contents of linebuf, if it has changed.
  950.  */
  951. vsave()
  952. {
  953.     char temp[LBSIZE];
  954.  
  955.     CP(temp, linebuf);
  956.     if (vundkind == VCHNG || vundkind == VCAPU) {
  957.         /*
  958.          * If the undo state is saved in the temporary buffer
  959.          * vutmp, then we sync this into the temp file so that
  960.          * we will be able to undo even after we have moved off
  961.          * the line.  It would be possible to associate a line
  962.          * with vutmp but we assume that vutmp is only associated
  963.          * with line dot (e.g. in case ':') above, so beware.
  964.          */
  965.         prepapp();
  966.         strcLIN(vutmp);
  967.         putmark(dot);
  968.         vremote(1, yank, 0);
  969.         vundkind = VMCHNG;
  970.         notecnt = 0;
  971.         undkind = UNDCHANGE;
  972.     }
  973.     /*
  974.      * Get the line out of the temp file and do nothing if it hasn't
  975.      * changed.  This may seem like a loss, but the line will
  976.      * almost always be in a read buffer so this may well avoid disk i/o.
  977.      */
  978.     getDOT();
  979.     if (strcmp(linebuf, temp) == 0)
  980.         return;
  981.     strcLIN(temp);
  982.     putmark(dot);
  983. }
  984.  
  985. #undef    forbid
  986. #define    forbid(a)    if (a) { beep(); return; }
  987.  
  988. /*
  989.  * Do a z operation.
  990.  * Code here is rather long, and very uninteresting.
  991.  */
  992. vzop(hadcnt, cnt, c)
  993.     bool hadcnt;
  994.     int cnt;
  995.     register int c;
  996. {
  997.     register line *addr;
  998.  
  999.     if (state != VISUAL) {
  1000.         /*
  1001.          * Z from open; always like a z=.
  1002.          * This code is a mess and should be cleaned up.
  1003.          */
  1004.         vmoveitup(1);
  1005.         vgoto(outline, 0);
  1006.         ostop(normf);
  1007.         setoutt();
  1008.         addr2 = dot;
  1009.         vclear();
  1010.         destline = WECHO;
  1011.         zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
  1012.         if (state == CRTOPEN)
  1013.             putnl();
  1014.         putNFL();
  1015.         termreset();
  1016.         Outchar = vputchar;
  1017.         ignore(ostart());
  1018.         vcnt = 0;
  1019.         outline = destline = 0;
  1020.         vjumpto(dot, cursor, 0);
  1021.         return;
  1022.     }
  1023.     if (hadcnt) {
  1024.         addr = zero + cnt;
  1025.         if (addr < one)
  1026.             addr = one;
  1027.         if (addr > dol)
  1028.             addr = dol;
  1029.         markit(addr);
  1030.     } else
  1031.         switch (c) {
  1032.  
  1033.         case '+':
  1034.             addr = dot + vcnt - vcline;
  1035.             break;
  1036.  
  1037.         case '^':
  1038.             addr = dot - vcline - 1;
  1039.             forbid (addr < one);
  1040.             c = '-';
  1041.             break;
  1042.  
  1043.         default:
  1044.             addr = dot;
  1045.             break;
  1046.         }
  1047.     switch (c) {
  1048.  
  1049.     case '.':
  1050.     case '-':
  1051.         break;
  1052.  
  1053.     case '^':
  1054.         forbid (addr <= one);
  1055.         break;
  1056.  
  1057.     case '+':
  1058.         forbid (addr >= dol);
  1059.         /* fall into ... */
  1060.  
  1061.     case CR:
  1062.     case NL:
  1063.         c = CR;
  1064.         break;
  1065.  
  1066.     default:
  1067.         beep();
  1068.         return;
  1069.     }
  1070.     vmoving = 0;
  1071.     vjumpto(addr, NOSTR, c);
  1072. }
  1073.