home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / vcmd.c < prev    next >
C/C++ Source or Header  |  1995-05-26  |  18KB  |  1,008 lines

  1. /* vcmd.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    1500 SW Park #326
  6.  *    Portland OR, 97201
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains the functions that handle VI commands */
  12.  
  13.  
  14. #include "config.h"
  15. #include "ctype.h"
  16. #include "vi.h"
  17. #if MSDOS || OS2
  18. # include <process.h>
  19. # include <string.h>
  20. #endif
  21. #if TOS
  22. # include <osbind.h>
  23. # include <string.h>
  24. #endif
  25. #if OSK
  26. # include <stdio.h>
  27. #endif
  28.  
  29.  
  30. /* This function puts the editor in EX mode */
  31. MARK v_quit()
  32. {
  33.     move(LINES - 1, 0);
  34.     mode = MODE_EX;
  35.     return cursor;
  36. }
  37.  
  38. /* This function causes the screen to be redrawn */
  39. MARK v_redraw()
  40. {
  41.     redraw(MARK_UNSET, FALSE);
  42.     return cursor;
  43. }
  44.  
  45. /* This function executes a string of EX commands, and waits for a user keystroke
  46.  * before returning to the VI screen.  If that keystroke is another ':', then
  47.  * another EX command is read and executed.
  48.  */
  49. /*ARGSUSED*/
  50. MARK v_1ex(m, text)
  51.     MARK    m;    /* the current line */
  52.     char    *text;    /* the first command to execute */
  53. {
  54.     /* run the command.  be careful about modes & output */
  55.     exwrote = (mode == MODE_COLON);
  56.     doexcmd(text, '\\');
  57.     exrefresh();
  58.  
  59.     /* if mode is no longer MODE_VI, then we should quit right away! */
  60.     if (mode != MODE_VI && mode != MODE_COLON)
  61.         return cursor;
  62.  
  63.     /* The command did some output.  Wait for a keystoke. */
  64.     if (exwrote)
  65.     {
  66.         mode = MODE_VI;    
  67.         msg("[Hit <RETURN> to continue]");
  68.         if (getkey(0) == ':')
  69.         {    mode = MODE_COLON;
  70.             addch('\n');
  71.         }
  72.         else
  73.             redraw(MARK_UNSET, FALSE);
  74.     }
  75.  
  76.     return cursor;
  77. }
  78.  
  79. /* This function undoes the last change */
  80. /*ARGSUSED*/
  81. MARK v_undo(m)
  82.     MARK    m;    /* (ignored) */
  83. {
  84.     if (undo())
  85.     {
  86.         redraw(MARK_UNSET, FALSE);
  87.     }
  88.     return cursor;
  89. }
  90.  
  91. /* This function deletes the character(s) that the cursor is on */
  92. MARK v_xchar(m, cnt, cmd)
  93.     MARK    m;    /* where to start deletions */
  94.     long    cnt;    /* number of chars to delete */
  95.     int    cmd;    /* either 'x' or 'X' */
  96. {
  97.     DEFAULT(1);
  98.  
  99.     /* for 'X', adjust so chars are deleted *BEFORE* cursor */
  100.     if (cmd == 'X')
  101.     {
  102.         if (markidx(m) < cnt)
  103.             return MARK_UNSET;
  104.         m -= cnt;
  105.     }
  106.  
  107.     /* make sure we don't try to delete more thars than there are */
  108.     pfetch(markline(m));
  109.     if (markidx(m + cnt) > plen)
  110.     {
  111.         cnt = plen - markidx(m);
  112.     }
  113.     if (cnt == 0L)
  114.     {
  115.         return MARK_UNSET;
  116.     }
  117.  
  118.     /* do it */
  119.     ChangeText
  120.     {
  121.         cut(m, m + cnt);
  122.         delete(m, m + cnt);
  123.     }
  124.     return m;
  125. }
  126.  
  127. /* This function defines a mark */
  128. /*ARGSUSED*/
  129. MARK v_mark(m, count, key)
  130.     MARK    m;    /* where the mark will be */
  131.     long    count;    /* (ignored) */
  132.     int    key;    /* the ASCII label of the mark */
  133. {
  134.     if (key < 'a' || key > 'z')
  135.     {
  136.         msg("Marks must be from a to z");
  137.     }
  138.     else
  139.     {
  140.         mark[key - 'a'] = m;
  141.     }
  142.     return m;
  143. }
  144.  
  145. /* This function toggles upper & lower case letters */
  146. MARK v_ulcase(m, cnt)
  147.     MARK    m;    /* where to make the change */
  148.     long    cnt;    /* number of chars to flip */
  149. {
  150.     REG char     *pos;
  151.     REG int        j;
  152.  
  153.     DEFAULT(1);
  154.  
  155.     /* fetch the current version of the line */
  156.     pfetch(markline(m));
  157.  
  158.     /* for each position in the line */
  159.     for (j = 0, pos = &ptext[markidx(m)]; j < cnt && *pos; j++, pos++)
  160.     {
  161.         if (isupper(*pos))
  162.         {
  163.             tmpblk.c[j] = tolower(*pos);
  164.         }
  165.         else
  166.         {
  167.             tmpblk.c[j] = toupper(*pos);
  168.         }
  169.     }
  170.  
  171.     /* if the new text is different from the old, then change it */
  172.     if (strncmp(tmpblk.c, &ptext[markidx(m)], j))
  173.     {
  174.         ChangeText
  175.         {
  176.             tmpblk.c[j] = '\0';
  177.             change(m, m + j, tmpblk.c);
  178.         }
  179.     }
  180.  
  181.     return m + j;
  182. }
  183.  
  184.  
  185. MARK v_replace(m, cnt, key)
  186.     MARK    m;    /* first char to be replaced */
  187.     long    cnt;    /* number of chars to replace */
  188.     int    key;    /* what to replace them with */
  189. {
  190.     REG char    *text;
  191.     REG int        i;
  192.  
  193.     DEFAULT(1);
  194.  
  195.     /* map ^M to '\n' */
  196.     if (key == '\r')
  197.     {
  198.         key = '\n';
  199.     }
  200.  
  201.     /* make sure the resulting line isn't too long */
  202.     if (cnt > BLKSIZE - 2 - markidx(m))
  203.     {
  204.         cnt = BLKSIZE - 2 - markidx(m);
  205.     }
  206.  
  207.     /* build a string of the desired character with the desired length */
  208.     for (text = tmpblk.c, i = (key == '\n' ? 1 : cnt); i > 0; i--)
  209.     {
  210.         *text++ = key;
  211.     }
  212.     *text = '\0';
  213.  
  214.     /* make sure cnt doesn't extend past EOL */
  215.     pfetch(markline(m));
  216.     key = markidx(m);
  217.     if (key + cnt > plen)
  218.     {
  219.         cnt = plen - key;
  220.     }
  221.  
  222.     /* do the replacement */
  223.     ChangeText
  224.     {
  225.         change(m, m + cnt, tmpblk.c);
  226.     }
  227.  
  228.     if (*tmpblk.c == '\n')
  229.     {
  230.         return (m & ~(BLKSIZE - 1)) + BLKSIZE;
  231.     }
  232.     else
  233.     {
  234.         return m + cnt - 1;
  235.     }
  236. }
  237.  
  238. MARK v_overtype(m)
  239.     MARK        m;    /* where to start overtyping */
  240. {
  241.     MARK        end;    /* end of a substitution */
  242.     static long    width;    /* width of a single-line replace */
  243.  
  244.     /* the "doingdot" version of replace is really a substitution */
  245.     if (doingdot)
  246.     {
  247.         /* was the last one really repeatable? */
  248.         if (width < 0)
  249.         {
  250.             msg("Can't repeat a multi-line overtype command");
  251.             return MARK_UNSET;
  252.         }
  253.  
  254.         /* replacing nothing by nothing?  Don't bother */
  255.         if (width == 0)
  256.         {
  257.             return m;
  258.         }
  259.  
  260.         /* replace some chars by repeated text */
  261.         return v_subst(m, width);
  262.     }
  263.  
  264.     /* Normally, we input starting here, in replace mode */
  265.     ChangeText
  266.     {
  267.         end = input(m, m, WHEN_VIREP, 0);
  268.     }
  269.  
  270.     /* if we ended on the same line we started on, then this
  271.      * overtype is repeatable via the dot key.
  272.      */
  273.     if (markline(end) == markline(m) && end >= m - 1L)
  274.     {
  275.         width = end - m + 1L;
  276.     }
  277.     else /* it isn't repeatable */
  278.     {
  279.         width = -1L;
  280.     }
  281.  
  282.     return end;
  283. }
  284.  
  285.  
  286. /* This function selects which cut buffer to use */
  287. /*ARGSUSED*/
  288. MARK v_selcut(m, cnt, key)
  289.     MARK    m;
  290.     long    cnt;
  291.     int    key;
  292. {
  293.     cutname(key);
  294.     return m;
  295. }
  296.  
  297. /* This function pastes one or more copies of a cut buffer into the main
  298.  * edit buffer.
  299.  */
  300. MARK v_paste(m, cnt, cmd)
  301.     MARK    m;    /* where to paste the text */
  302.     long    cnt;    /* number of copies of cut buffer to paste */
  303.     int    cmd;    /* either 'p' or 'P' */
  304. {
  305.     MARK    dest;
  306.     int     i;
  307.   
  308.     ChangeText
  309.     {
  310.         /* paste the text, and find out where it ends */
  311.         dest = paste(m, cmd == 'p', TRUE);
  312.         if (cnt > 1)
  313.         {
  314.             for (i = 1; i != cnt; i++)
  315.             {
  316.                 dest = paste(dest, TRUE, TRUE);
  317.             }
  318.         }
  319.  
  320.         /* was that a line-mode paste? */
  321.         if (dest && markline(dest) != markline(m))
  322.         {
  323.             /* line-mode pastes leave the cursor at the front
  324.              * of the first pasted line.
  325.              */
  326.             dest = m;
  327.             if (cmd == 'p')
  328.             {
  329.                 dest += BLKSIZE;
  330.             }
  331.             force_flags |= FRNT;
  332.         }
  333.     }
  334.     return dest;
  335. }
  336.  
  337. /* This function yanks text into a cut buffer */
  338. MARK v_yank(m, n)
  339.     MARK    m, n;    /* range of text to yank */
  340. {
  341.     cut(m, n);
  342.     return m;
  343. }
  344.  
  345. /* This function deletes a range of text */
  346. MARK v_delete(m, n)
  347.     MARK    m, n;    /* range of text to delete */
  348. {
  349.     /* illegal to try and delete nothing */
  350.     if (n <= m)
  351.     {
  352.         return MARK_UNSET;
  353.     }
  354.  
  355.     /* Do it */
  356.     ChangeText
  357.     {
  358.         cut(m, n);
  359.         delete(m, n);
  360.     }
  361.     return m;
  362. }
  363.  
  364.  
  365. /* This starts input mode without deleting anything */
  366. MARK v_insert(m, cnt, key)
  367.     MARK    m;    /* where to start (sort of) */
  368.     long    cnt;    /* repeat how many times? */
  369.     int    key;    /* what command is this for? {a,A,i,I,o,O} */
  370. {
  371.     int    wasdot;
  372.     long    reps;
  373.     int    delta = 0;/* 1 to take autoindent from line below, -1 for above */
  374.  
  375.     DEFAULT(1);
  376.  
  377.     ChangeText
  378.     {
  379.         /* tweak the insertion point, based on command key */
  380.         switch (key)
  381.         {
  382.           case 'i':
  383.             break;
  384.  
  385.           case 'a':
  386.             pfetch(markline(m));
  387.             if (plen > 0)
  388.             {
  389.                 m++;
  390.             }
  391.             break;
  392.  
  393.           case 'I':
  394.             m = m_front(m, 1L);
  395.             break;
  396.  
  397.           case 'A':
  398.             pfetch(markline(m));
  399.             m = (m & ~(BLKSIZE - 1)) + plen;
  400.             break;
  401.  
  402.           case 'O':
  403.             m &= ~(BLKSIZE - 1);
  404.             add(m, "\n");
  405.             delta = 1;
  406.             break;
  407.  
  408.           case 'o':
  409.             m = (m & ~(BLKSIZE - 1)) + BLKSIZE;
  410.             add(m, "\n");
  411.             delta = -1;
  412.             break;
  413.         }
  414.  
  415.         /* insert the same text once or more */
  416.         for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE)
  417.         {
  418.             m = input(m, m, WHEN_VIINP, delta) + 1;
  419.         }
  420.         if (markidx(m) > 0)
  421.         {
  422.             m--;
  423.         }
  424.  
  425.         doingdot = wasdot;
  426.     }
  427.  
  428. #ifndef CRUNCH
  429. # ifndef NO_EXTENSIONS
  430.     if (key == 'i' && *o_inputmode && mode == MODE_VI)
  431.     {
  432.         msg("Now in command mode!  To return to input mode, hit <i>");
  433.     }
  434. # endif
  435. #endif
  436.  
  437.     return m;
  438. }
  439.  
  440. /* This starts input mode with some text deleted */
  441. MARK v_change(m, n)
  442.     MARK    m, n;    /* the range of text to change */
  443. {
  444.     int    lnmode;    /* is this a line-mode change? */
  445.  
  446.     /* swap them if they're in reverse order */
  447.     if (m > n)
  448.     {
  449.         MARK    tmp;
  450.         tmp = m;
  451.         m = n;
  452.         n = tmp;
  453.     }
  454.  
  455.     /* for line mode, retain the last newline char */
  456.     lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
  457.     if (lnmode)
  458.     {
  459.         n -= BLKSIZE;
  460.         pfetch(markline(n));
  461.         n = (n & ~(BLKSIZE - 1)) + plen;
  462.     }
  463.  
  464.     ChangeText
  465.     {
  466.         cut(m, n);
  467.         m = input(m, n, WHEN_VIINP, 0);
  468.     }
  469.  
  470.     return m;
  471. }
  472.  
  473. /* This function replaces a given number of characters with input */
  474. MARK v_subst(m, cnt)
  475.     MARK    m;    /* where substitutions start */
  476.     long    cnt;    /* number of chars to replace */
  477. {
  478.     DEFAULT(1);
  479.  
  480.     /* make sure we don't try replacing past EOL */
  481.     pfetch(markline(m));
  482.     if (markidx(m) + cnt > plen)
  483.     {
  484.         cnt = plen - markidx(m);
  485.     }
  486.  
  487.     /* Go for it! */
  488.     ChangeText
  489.     {
  490.         cut(m, m + cnt);
  491.         m = input(m, m + cnt, WHEN_VIINP, 0);
  492.     }
  493.     return m;
  494. }
  495.  
  496. /* This calls the ex "join" command to join some lines together */
  497. MARK v_join(m, cnt)
  498.     MARK    m;    /* the first line to be joined */
  499.     long    cnt;    /* number of other lines to join */
  500. {
  501.     MARK    joint;    /* where the lines were joined */
  502.  
  503.     DEFAULT(1);
  504.     if (cnt > 1)
  505.     {
  506.         cnt--;
  507.     }
  508.  
  509.     /* figure out where the joint will be */
  510.     pfetch(markline(m));
  511.     joint = (m & ~(BLKSIZE - 1)) + plen;
  512.  
  513.     /* join the lines */
  514.     cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, "");
  515.  
  516.     /* the cursor should be left at the joint */
  517.     return joint;
  518. }
  519.  
  520.  
  521. /* This calls the ex "<" command to shift some lines left */
  522. MARK v_lshift(m, n)
  523.     MARK    m, n;    /* range of lines to shift */
  524. {
  525.     /* adjust for inclusive endmarks in ex */
  526.     n -= BLKSIZE;
  527.  
  528.     cmd_shift(m, n, CMD_SHIFTL, FALSE, "");
  529.  
  530.     return m;
  531. }
  532.  
  533. /* This calls the ex ">" command to shift some lines right */
  534. MARK v_rshift(m, n)
  535.     MARK    m, n;    /* range of lines to shift */
  536. {
  537.     /* adjust for inclusive endmarks in ex */
  538.     n -= BLKSIZE;
  539.  
  540.     cmd_shift(m, n, CMD_SHIFTR, FALSE, "");
  541.  
  542.     return m;
  543. }
  544.  
  545. /* This filters some lines through a preset program, to reformat them */
  546. MARK v_reformat(m, n)
  547.     MARK    m, n;    /* range of lines to shift */
  548. {
  549.     /* adjust for inclusive endmarks in ex */
  550.     n -= BLKSIZE;
  551.  
  552.     /* run the filter command */
  553.     filter(m, n, o_equalprg, TRUE);
  554.  
  555.     redraw(MARK_UNSET, FALSE);
  556.     return m;
  557. }
  558.  
  559.  
  560. /* This runs some lines through a filter program */
  561. MARK v_filter(m, n)
  562.     MARK    m, n;    /* range of lines to shift */
  563. {
  564.     static char    cmdln[150];    /* a shell command line */
  565.  
  566. #ifndef NO_SAFER
  567.     if (*o_safer)
  568.     {
  569.         msg("visual ! command is unsafe");
  570.         return MARK_UNSET;
  571.     }
  572. #endif
  573.  
  574.     /* adjust for inclusive endmarks in ex */
  575.     n -= BLKSIZE;
  576.  
  577.     if (doingdot || vgets('!', cmdln, sizeof(cmdln)) > 0)
  578.     {
  579.         filter(m, n, cmdln, TRUE);
  580.     }
  581.  
  582.     redraw(MARK_UNSET, FALSE);
  583.     return m;
  584. }
  585.  
  586.  
  587. /* This function runs the ex "file" command to show the file's status */
  588. MARK v_status()
  589. {
  590.     cmd_file(cursor, cursor, CMD_FILE, 0, "");
  591.     return cursor;
  592. }
  593.  
  594.  
  595. /* This function runs the ":&" command to repeat the previous :s// */
  596. MARK v_again(m, n)
  597.     MARK    m, n;
  598. {
  599.     cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, "");
  600.     return cursor;
  601. }
  602.  
  603.  
  604.  
  605. /* This function switches to the previous file, if possible */
  606. MARK v_switch()
  607. {
  608.     if (!*prevorig)
  609.         msg("No previous file");
  610.     else
  611.     {    strcpy(tmpblk.c, prevorig);
  612.         cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c);
  613.     }
  614.     return cursor;
  615. }
  616.  
  617. /* This function does a tag search on a keyword */
  618. /*ARGSUSED*/
  619. MARK v_tag(keyword, m, cnt)
  620.     char    *keyword;
  621.     MARK    m;
  622.     long    cnt;
  623. {
  624.     /* move the cursor to the start of the tag name, where m is */
  625.     cursor = m;
  626.  
  627.     /* perform the tag search */
  628.     cmd_tag(cursor, cursor, CMD_TAG, 0, keyword);
  629.  
  630.     return cursor;
  631. }
  632.  
  633. #ifndef NO_EXTENSIONS
  634. /* This function looks up a keyword by calling the helpprog program */
  635. /*ARGSUSED*/
  636. MARK v_keyword(keyword, m, cnt)
  637.     char    *keyword;
  638.     MARK    m;
  639.     long    cnt;
  640. {
  641.     int    waswarn;
  642.     char    cmdline[130];
  643.  
  644.     move(LINES - 1, 0);
  645.     addstr("---------------------------------------------------------\n");
  646.     clrtoeol();
  647.     refresh();
  648.     sprintf(cmdline, "%s %s", o_keywordprg, keyword);
  649.     waswarn = *o_warn;
  650.     *o_warn = FALSE;
  651.     suspend_curses();
  652.     if (system(cmdline))
  653.     {
  654.         addstr("<<< failed >>>\n");
  655.     }
  656.     resume_curses(FALSE);
  657.     mode = MODE_VI;
  658.     redraw(MARK_UNSET, FALSE);
  659.     *o_warn = waswarn;
  660.  
  661.     return m;
  662. }
  663.  
  664.  
  665.  
  666. MARK v_increment(keyword, m, cnt)
  667.     char    *keyword;
  668.     MARK    m;
  669.     long    cnt;
  670. {
  671.     static    sign;
  672.     char    newval[12];
  673.  
  674.     DEFAULT(1);
  675.  
  676.     /* get one more keystroke, unless doingdot */
  677.     if (!doingdot)
  678.     {
  679.         sign = getkey(0);
  680.     }
  681.  
  682.     /* adjust the number, based on that second keystroke */
  683.     switch (sign)
  684.     {
  685.       case '+':
  686.       case '#':
  687.         cnt = atol(keyword) + cnt;
  688.         break;
  689.  
  690.       case '-':
  691.         cnt = atol(keyword) - cnt;
  692.         break;
  693.  
  694.       case '=':
  695.         break;
  696.  
  697.       default:
  698.         return MARK_UNSET;
  699.     }
  700.     sprintf(newval, "%ld", cnt);
  701.  
  702.     ChangeText
  703.     {
  704.         change(m, m + strlen(keyword), newval);
  705.     }
  706.  
  707.     return m;
  708. }
  709. #endif
  710.  
  711.  
  712. /* This function acts like the EX command "xit" */
  713. /*ARGSUSED*/
  714. MARK v_xit(m, cnt, key)
  715.     MARK    m;    /* ignored */
  716.     long    cnt;    /* ignored */
  717.     int    key;    /* must be a second 'Z' */
  718. {
  719.     /* if second char wasn't 'Z', fail */
  720.     if (key != 'Z')
  721.     {
  722.         return MARK_UNSET;
  723.     }
  724.  
  725.     /* move the cursor to the bottom of the screen */
  726.     move(LINES - 1, 0);
  727.     clrtoeol();
  728.  
  729.     /* do the xit command */
  730.     cmd_xit(m, m, CMD_XIT, FALSE, "");
  731.  
  732.     /* return the cursor */
  733.     return m;
  734. }
  735.  
  736.  
  737. /* This function undoes changes to a single line, if possible */
  738. MARK v_undoline(m)
  739.     MARK    m;    /* where we hope to undo the change */
  740. {
  741.     /* make sure we have the right line in the buffer */
  742.     if (markline(m) != U_line)
  743.     {
  744.         return MARK_UNSET;
  745.     }
  746.  
  747.     /* fix it */
  748.     ChangeText
  749.     {
  750.         strcat(U_text, "\n");
  751.         change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text);
  752.     }
  753.  
  754.     /* nothing in the buffer anymore */
  755.     U_line = -1L;
  756.  
  757.     /* return, with the cursor at the front of the line */
  758.     return m & ~(BLKSIZE - 1);
  759. }
  760.  
  761.  
  762. #ifndef NO_ERRLIST
  763. MARK v_errlist(m)
  764.     MARK    m;
  765. {
  766.     cmd_errlist(m, m, CMD_ERRLIST, FALSE, "");
  767.     return cursor;
  768. }
  769. #endif
  770.  
  771.  
  772. #ifndef NO_AT
  773. /*ARGSUSED*/
  774. MARK v_at(m, cnt, key)
  775.     MARK    m;
  776.     long    cnt;
  777.     int    key;
  778. {
  779.     int    size;
  780.  
  781.     size = cb2str(key, tmpblk.c, BLKSIZE);
  782.     if (size <= 0 || size == BLKSIZE)
  783.     {
  784.         return MARK_UNSET;
  785.     }
  786.  
  787.     execmap(0, tmpblk.c, FALSE);
  788.     return cursor;
  789. }
  790. #endif
  791.  
  792.  
  793. #ifdef SIGTSTP
  794. MARK v_suspend()
  795. {
  796.     cmd_suspend(MARK_UNSET, MARK_UNSET, CMD_SUSPEND, FALSE, "");
  797.     return MARK_UNSET;
  798. }
  799. #endif
  800.  
  801.  
  802. #ifndef NO_VISIBLE
  803. /*ARGSUSED*/
  804. MARK v_start(m, cnt, cmd)
  805.     MARK    m;    /* starting point for a v or V command */
  806.     long    cnt;    /* ignored */
  807.     int    cmd;    /* either 'v' or 'V' */
  808. {
  809.     if (V_from)
  810.     {
  811.         V_from = MARK_UNSET;
  812.     }
  813.     else
  814.     {
  815.         V_from = m;
  816.         V_linemd = isupper(cmd);
  817.     }
  818.     return m;
  819. }
  820. #endif
  821.  
  822. #ifndef NO_POPUP
  823. # define MENU_HEIGHT 11
  824. # define MENU_WIDTH  23
  825. MARK v_popup(m, n)
  826.     MARK        m, n;    /* the range of text to change */
  827. {
  828.     int        i;
  829.     int        y, x;    /* position where the window will pop up at */
  830.     int        key;    /* keystroke from the user */
  831.     int        sel;    /* index of the selected operation */
  832.     static int    dfltsel;/* default value of sel */
  833.     static char    *labels[11] =
  834.     {
  835.         "ESC cancel!         ",
  836.         " d  delete (cut)    ",
  837.         " y  yank (copy)     ",
  838.         " p  paste after     ",
  839.         " P  paste before    ",
  840.         " >  more indented   ",
  841.         " <  less indented   ",
  842.         " =  reformat        ",
  843.         " !  external filter ",
  844.         " ZZ save & exit     ",
  845.         " u  undo previous   "
  846.     };
  847.  
  848.     /* try to position the menu near the cursor */
  849.     x = physcol - (MENU_WIDTH / 2);
  850.     if (x < 0)
  851.         x = 0;
  852.     else if (x + MENU_WIDTH + 2 > COLS)
  853.         x = COLS - MENU_WIDTH - 2;
  854.     if (markline(cursor) < topline || markline(cursor) > botline)
  855.         y = 0;
  856.     else if (markline(cursor) + 1L + MENU_HEIGHT > botline)
  857.         y = (int)(markline(cursor) - topline) - MENU_HEIGHT;
  858.     else
  859.         y = (int)(markline(cursor) - topline) + 1L;
  860.  
  861.     /* draw the menu */
  862.     for (sel = 0; sel < MENU_HEIGHT; sel++)
  863.     {
  864.         move(y + sel, x);
  865.         do_POPUP();
  866.         if (sel == dfltsel)
  867.             qaddstr("-->");
  868.         else
  869.             qaddstr("   ");
  870.         qaddstr(labels[sel]);
  871.         do_SE();
  872.     }
  873.  
  874.     /* get a selection */
  875.     move(y + dfltsel, x + 4);
  876.     for (sel = dfltsel; (key = getkey(WHEN_POPUP)) != '\\' && key != '\r'; )
  877.     {
  878.         /* erase the selection arrow */
  879.         move(y + sel, x);
  880.         do_POPUP();
  881.         qaddstr("   ");
  882.         qaddstr(labels[sel]);
  883.         do_SE();
  884.  
  885.         /* process the user's keystroke */
  886.         if (key == 'j' && sel < MENU_HEIGHT - 1)
  887.         {
  888.             sel++;
  889.         }
  890.         else if (key == 'k' && sel > 0)
  891.         {
  892.             sel--;
  893.         }
  894.         else if (key == '\033')
  895.         {
  896.             sel = 0;
  897.             break;
  898.         }
  899.         else
  900.         {
  901.             for (i = 1; i < MENU_HEIGHT && labels[i][1] != key; i++)
  902.             {
  903.             }
  904.             if (i < MENU_HEIGHT)
  905.             {
  906.                 sel = i;
  907.                 break;
  908.             }
  909.         }
  910.  
  911.         /* redraw the arrow, possibly in a new place */
  912.         move(y + sel, x);
  913.         do_POPUP();
  914.         qaddstr("-->");
  915.         qaddstr(labels[sel]);
  916.         do_SE();
  917.         move(y + sel, x + 4);
  918.     }
  919.  
  920.     /* in most cases, the default selection is "paste after" */
  921.     dfltsel = 3;
  922.  
  923.     /* perform the appropriate action */
  924.     switch (sel)
  925.     {
  926.       case 0:
  927.         m = cursor;
  928.         dfltsel = 0;
  929.         break;
  930.  
  931.       case 1: /* delete (cut) */
  932.         m = v_delete(m, n);
  933.         break;
  934.  
  935.       case 2: /* yank (copy) */
  936.         m = v_yank(m, n);
  937.         break;
  938.  
  939.       case 3: /* paste after */
  940.         m = v_paste(n, 1L, 'P');
  941.         break;
  942.  
  943.       case 4: /* paste before */
  944.         m = v_paste(m, 1L, 'P');
  945.         dfltsel = 4;
  946.         break;
  947.  
  948.       case 5: /* more indented */
  949.         m = v_rshift(m, n);
  950.         dfltsel = 5;
  951.         break;
  952.  
  953.       case 6: /* less indented */
  954.         m = v_lshift(m, n);
  955.         dfltsel = 6;
  956.         break;
  957.  
  958.       case 7: /* reformat */
  959.         m = v_reformat(m, n);
  960.         dfltsel = 7;
  961.         break;
  962.  
  963.       case 8: /* external filter */
  964.         m = v_filter(m, n);
  965.         dfltsel = 0;
  966.         break;
  967.  
  968.       case 9: /* save & exit */
  969.         /* get confirmation first */
  970.         do
  971.         {
  972.             key = getkey(0);
  973.         } while (key != '\\' && key != 'Z' && key != '\r'    /* good */
  974.               && key != '\033');                 /* bad  */
  975.         if (key != '\033')
  976.         {
  977.             m = v_xit(m, 0L, 'Z');
  978.         }
  979.         break;
  980.  
  981.       case 10: /* undo previous */
  982.         m = v_undo(m);
  983.         dfltsel = 9;
  984.         break;
  985.     }
  986.  
  987.     /* arrange for the menu to be erased (except "save & exit" doesn't care)
  988.      */
  989.     if (mode == MODE_VI)
  990.         redraw(MARK_UNSET, FALSE);
  991.  
  992.     return m;
  993. }
  994. #endif /* undef NO_POPUP */
  995.  
  996. #ifndef NO_TAGSTACK
  997. MARK v_pop(m, cnt, cmd)
  998.     MARK    m;    /* original cursor position (ignored) */
  999.     long    cnt;    /* number of levels to pop */
  1000.     int    cmd;    /* command key -- ^T  (ignored) */
  1001. {
  1002.     DEFAULT(1L);
  1003.     sprintf(tmpblk.c, "%ld", cnt);
  1004.     cmd_pop(m, m, CMD_POP, FALSE, tmpblk.c);
  1005.     return cursor;
  1006. }
  1007. #endif
  1008.