home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 2 / crawlyvol2.bin / apps / text_ed / elv16b2 / st / vcmd.c < prev    next >
C/C++ Source or Header  |  1992-05-13  |  17KB  |  978 lines

  1. /* vcmd.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  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
  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 = 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)) + cnt * 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 text from a cut buffer */
  298. /*ARGSUSED*/
  299. MARK v_paste(m, cnt, cmd)
  300.     MARK    m;    /* where to paste the text */
  301.     long    cnt;    /* (ignored) */
  302.     int    cmd;    /* either 'p' or 'P' */
  303. {
  304.     MARK    dest;
  305.  
  306.     ChangeText
  307.     {
  308.         /* paste the text, and find out where it ends */
  309.         dest = paste(m, cmd == 'p', TRUE);
  310.  
  311.         /* was that a line-mode paste? */
  312.         if (dest && markline(dest) != markline(m))
  313.         {
  314.             /* line-mode pastes leave the cursor at the front
  315.              * of the first pasted line.
  316.              */
  317.             dest = m;
  318.             if (cmd == 'p')
  319.             {
  320.                 dest += BLKSIZE;
  321.             }
  322.             force_flags |= FRNT;
  323.         }
  324.     }
  325.     return dest;
  326. }
  327.  
  328. /* This function yanks text into a cut buffer */
  329. MARK v_yank(m, n)
  330.     MARK    m, n;    /* range of text to yank */
  331. {
  332.     cut(m, n);
  333.     return m;
  334. }
  335.  
  336. /* This function deletes a range of text */
  337. MARK v_delete(m, n)
  338.     MARK    m, n;    /* range of text to delete */
  339. {
  340.     /* illegal to try and delete nothing */
  341.     if (n <= m)
  342.     {
  343.         return MARK_UNSET;
  344.     }
  345.  
  346.     /* Do it */
  347.     ChangeText
  348.     {
  349.         cut(m, n);
  350.         delete(m, n);
  351.     }
  352.     return m;
  353. }
  354.  
  355.  
  356. /* This starts input mode without deleting anything */
  357. MARK v_insert(m, cnt, key)
  358.     MARK    m;    /* where to start (sort of) */
  359.     long    cnt;    /* repeat how many times? */
  360.     int    key;    /* what command is this for? {a,A,i,I,o,O} */
  361. {
  362.     int    wasdot;
  363.     long    reps;
  364.     int    delta = 0;/* 1 to take autoindent from line below, -1 for above */
  365.  
  366.     DEFAULT(1);
  367.  
  368.     ChangeText
  369.     {
  370.         /* tweak the insertion point, based on command key */
  371.         switch (key)
  372.         {
  373.           case 'i':
  374.             break;
  375.  
  376.           case 'a':
  377.             pfetch(markline(m));
  378.             if (plen > 0)
  379.             {
  380.                 m++;
  381.             }
  382.             break;
  383.  
  384.           case 'I':
  385.             m = m_front(m, 1L);
  386.             break;
  387.  
  388.           case 'A':
  389.             pfetch(markline(m));
  390.             m = (m & ~(BLKSIZE - 1)) + plen;
  391.             break;
  392.  
  393.           case 'O':
  394.             m &= ~(BLKSIZE - 1);
  395.             add(m, "\n");
  396.             delta = 1;
  397.             break;
  398.  
  399.           case 'o':
  400.             m = (m & ~(BLKSIZE - 1)) + BLKSIZE;
  401.             add(m, "\n");
  402.             delta = -1;
  403.             break;
  404.         }
  405.  
  406.         /* insert the same text once or more */
  407.         for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE)
  408.         {
  409.             m = input(m, m, WHEN_VIINP, delta) + 1;
  410.         }
  411.         if (markidx(m) > 0)
  412.         {
  413.             m--;
  414.         }
  415.  
  416.         doingdot = wasdot;
  417.     }
  418.  
  419. #ifndef CRUNCH
  420. # ifndef NO_EXTENSIONS
  421.     if (key == 'i' && *o_inputmode && mode == MODE_VI)
  422.     {
  423.         msg("Now in command mode!  To return to input mode, hit <i>");
  424.     }
  425. # endif
  426. #endif
  427.  
  428.     return m;
  429. }
  430.  
  431. /* This starts input mode with some text deleted */
  432. MARK v_change(m, n)
  433.     MARK    m, n;    /* the range of text to change */
  434. {
  435.     int    lnmode;    /* is this a line-mode change? */
  436.  
  437.     /* swap them if they're in reverse order */
  438.     if (m > n)
  439.     {
  440.         MARK    tmp;
  441.         tmp = m;
  442.         m = n;
  443.         n = tmp;
  444.     }
  445.  
  446.     /* for line mode, retain the last newline char */
  447.     lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
  448.     if (lnmode)
  449.     {
  450.         n -= BLKSIZE;
  451.         pfetch(markline(n));
  452.         n = (n & ~(BLKSIZE - 1)) + plen;
  453.     }
  454.  
  455.     ChangeText
  456.     {
  457.         cut(m, n);
  458.         m = input(m, n, WHEN_VIINP, 0);
  459.     }
  460.  
  461.     return m;
  462. }
  463.  
  464. /* This function replaces a given number of characters with input */
  465. MARK v_subst(m, cnt)
  466.     MARK    m;    /* where substitutions start */
  467.     long    cnt;    /* number of chars to replace */
  468. {
  469.     DEFAULT(1);
  470.  
  471.     /* make sure we don't try replacing past EOL */
  472.     pfetch(markline(m));
  473.     if (markidx(m) + cnt > plen)
  474.     {
  475.         cnt = plen - markidx(m);
  476.     }
  477.  
  478.     /* Go for it! */
  479.     ChangeText
  480.     {
  481.         cut(m, m + cnt);
  482.         m = input(m, m + cnt, WHEN_VIINP, 0);
  483.     }
  484.     return m;
  485. }
  486.  
  487. /* This calls the ex "join" command to join some lines together */
  488. MARK v_join(m, cnt)
  489.     MARK    m;    /* the first line to be joined */
  490.     long    cnt;    /* number of other lines to join */
  491. {
  492.     MARK    joint;    /* where the lines were joined */
  493.  
  494.     DEFAULT(1);
  495.  
  496.     /* figure out where the joint will be */
  497.     pfetch(markline(m));
  498.     joint = (m & ~(BLKSIZE - 1)) + plen;
  499.  
  500.     /* join the lines */
  501.     cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, "");
  502.  
  503.     /* the cursor should be left at the joint */
  504.     return joint;
  505. }
  506.  
  507.  
  508. /* This calls the ex "<" command to shift some lines left */
  509. MARK v_lshift(m, n)
  510.     MARK    m, n;    /* range of lines to shift */
  511. {
  512.     /* adjust for inclusive endmarks in ex */
  513.     n -= BLKSIZE;
  514.  
  515.     cmd_shift(m, n, CMD_SHIFTL, FALSE, (char *)0);
  516.  
  517.     return m;
  518. }
  519.  
  520. /* This calls the ex ">" command to shift some lines right */
  521. MARK v_rshift(m, n)
  522.     MARK    m, n;    /* range of lines to shift */
  523. {
  524.     /* adjust for inclusive endmarks in ex */
  525.     n -= BLKSIZE;
  526.  
  527.     cmd_shift(m, n, CMD_SHIFTR, FALSE, (char *)0);
  528.  
  529.     return m;
  530. }
  531.  
  532. /* This filters some lines through a preset program, to reformat them */
  533. MARK v_reformat(m, n)
  534.     MARK    m, n;    /* range of lines to shift */
  535. {
  536.     /* adjust for inclusive endmarks in ex */
  537.     n -= BLKSIZE;
  538.  
  539.     /* run the filter command */
  540.     filter(m, n, o_equalprg, TRUE);
  541.  
  542.     redraw(MARK_UNSET, FALSE);
  543.     return m;
  544. }
  545.  
  546.  
  547. /* This runs some lines through a filter program */
  548. MARK v_filter(m, n)
  549.     MARK    m, n;    /* range of lines to shift */
  550. {
  551.     char    cmdln[150];    /* a shell command line */
  552.  
  553.     /* adjust for inclusive endmarks in ex */
  554.     n -= BLKSIZE;
  555.  
  556.     if (vgets('!', cmdln, (int) sizeof(cmdln)) > 0)
  557.     {
  558.         filter(m, n, cmdln, TRUE);
  559.     }
  560.  
  561.     redraw(MARK_UNSET, FALSE);
  562.     return m;
  563. }
  564.  
  565.  
  566. /* This function runs the ex "file" command to show the file's status */
  567. MARK v_status()
  568. {
  569.     cmd_file(cursor, cursor, CMD_FILE, 0, "");
  570.     return cursor;
  571. }
  572.  
  573.  
  574. /* This function runs the ":&" command to repeat the previous :s// */
  575. MARK v_again(m, n)
  576.     MARK    m, n;
  577. {
  578.     cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, "");
  579.     return cursor;
  580. }
  581.  
  582.  
  583.  
  584. /* This function switches to the previous file, if possible */
  585. MARK v_switch()
  586. {
  587.     if (!*prevorig)
  588.         msg("No previous file");
  589.     else
  590.     {    strcpy(tmpblk.c, prevorig);
  591.         cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c);
  592.     }
  593.     return cursor;
  594. }
  595.  
  596. /* This function does a tag search on a keyword */
  597. /*ARGSUSED*/
  598. MARK v_tag(keyword, m, cnt)
  599.     char    *keyword;
  600.     MARK    m;
  601.     long    cnt;
  602. {
  603.     /* move the cursor to the start of the tag name, where m is */
  604.     cursor = m;
  605.  
  606.     /* perform the tag search */
  607.     cmd_tag(cursor, cursor, CMD_TAG, 0, keyword);
  608.  
  609.     return cursor;
  610. }
  611.  
  612. #ifndef NO_EXTENSIONS
  613. /* This function looks up a keyword by calling the helpprog program */
  614. /*ARGSUSED*/
  615. MARK v_keyword(keyword, m, cnt)
  616.     char    *keyword;
  617.     MARK    m;
  618.     long    cnt;
  619. {
  620.     int    waswarn;
  621.     char    cmdline[130];
  622.  
  623.     move(LINES - 1, 0);
  624.     addstr("---------------------------------------------------------\n");
  625.     clrtoeol();
  626.     refresh();
  627.     sprintf(cmdline, "%s %s", o_keywordprg, keyword);
  628.     waswarn = *o_warn;
  629.     *o_warn = FALSE;
  630.     suspend_curses();
  631.     if (system(cmdline))
  632.     {
  633.         addstr("<<< failed >>>\n");
  634.     }
  635.     resume_curses(FALSE);
  636.     mode = MODE_VI;
  637.     redraw(MARK_UNSET, FALSE);
  638.     *o_warn = waswarn;
  639.  
  640.     return m;
  641. }
  642.  
  643.  
  644.  
  645. MARK v_increment(keyword, m, cnt)
  646.     char    *keyword;
  647.     MARK    m;
  648.     long    cnt;
  649. {
  650.     static    sign;
  651.     char    newval[12];
  652.     long    atol();
  653.  
  654.     DEFAULT(1);
  655.  
  656.     /* get one more keystroke, unless doingdot */
  657.     if (!doingdot)
  658.     {
  659.         sign = getkey(0);
  660.     }
  661.  
  662.     /* adjust the number, based on that second keystroke */
  663.     switch (sign)
  664.     {
  665.       case '+':
  666.       case '#':
  667.         cnt = atol(keyword) + cnt;
  668.         break;
  669.  
  670.       case '-':
  671.         cnt = atol(keyword) - cnt;
  672.         break;
  673.  
  674.       case '=':
  675.         break;
  676.  
  677.       default:
  678.         return MARK_UNSET;
  679.     }
  680.     sprintf(newval, "%ld", cnt);
  681.  
  682.     ChangeText
  683.     {
  684.         change(m, m + strlen(keyword), newval);
  685.     }
  686.  
  687.     return m;
  688. }
  689. #endif
  690.  
  691.  
  692. /* This function acts like the EX command "xit" */
  693. /*ARGSUSED*/
  694. MARK v_xit(m, cnt, key)
  695.     MARK    m;    /* ignored */
  696.     long    cnt;    /* ignored */
  697.     int    key;    /* must be a second 'Z' */
  698. {
  699.     /* if second char wasn't 'Z', fail */
  700.     if (key != 'Z')
  701.     {
  702.         return MARK_UNSET;
  703.     }
  704.  
  705.     /* move the cursor to the bottom of the screen */
  706.     move(LINES - 1, 0);
  707.     clrtoeol();
  708.  
  709.     /* do the xit command */
  710.     cmd_xit(m, m, CMD_XIT, FALSE, "");
  711.  
  712.     /* return the cursor */
  713.     return m;
  714. }
  715.  
  716.  
  717. /* This function undoes changes to a single line, if possible */
  718. MARK v_undoline(m)
  719.     MARK    m;    /* where we hope to undo the change */
  720. {
  721.     /* make sure we have the right line in the buffer */
  722.     if (markline(m) != U_line)
  723.     {
  724.         return MARK_UNSET;
  725.     }
  726.  
  727.     /* fix it */
  728.     ChangeText
  729.     {
  730.         strcat(U_text, "\n");
  731.         change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text);
  732.     }
  733.  
  734.     /* nothing in the buffer anymore */
  735.     U_line = -1L;
  736.  
  737.     /* return, with the cursor at the front of the line */
  738.     return m & ~(BLKSIZE - 1);
  739. }
  740.  
  741.  
  742. #ifndef NO_ERRLIST
  743. MARK v_errlist(m)
  744.     MARK    m;
  745. {
  746.     cmd_errlist(m, m, CMD_ERRLIST, FALSE, "");
  747.     return cursor;
  748. }
  749. #endif
  750.  
  751.  
  752. #ifndef NO_AT
  753. /*ARGSUSED*/
  754. MARK v_at(m, cnt, key)
  755.     MARK    m;
  756.     long    cnt;
  757.     int    key;
  758. {
  759.     int    size;
  760.  
  761.     size = cb2str(key, tmpblk.c, BLKSIZE);
  762.     if (size <= 0 || size == BLKSIZE)
  763.     {
  764.         return MARK_UNSET;
  765.     }
  766.  
  767.     execmap(0, tmpblk.c, FALSE);
  768.     return cursor;
  769. }
  770. #endif
  771.  
  772.  
  773. #ifdef SIGTSTP
  774. MARK v_suspend()
  775. {
  776.     cmd_suspend(MARK_UNSET, MARK_UNSET, CMD_SUSPEND, FALSE, "");
  777.     return MARK_UNSET;
  778. }
  779. #endif
  780.  
  781.  
  782. #ifndef NO_VISIBLE
  783. /*ARGSUSED*/
  784. MARK v_start(m, cnt, cmd)
  785.     MARK    m;    /* starting point for a v or V command */
  786.     long    cnt;    /* ignored */
  787.     int    cmd;    /* either 'v' or 'V' */
  788. {
  789.     if (V_from)
  790.     {
  791.         V_from = MARK_UNSET;
  792.     }
  793.     else
  794.     {
  795.         V_from = m;
  796.         V_linemd = isupper(cmd);
  797.     }
  798.     return m;
  799. }
  800. #endif
  801.  
  802. #ifndef NO_POPUP
  803. # define MENU_HEIGHT 11
  804. # define MENU_WIDTH  23
  805. MARK v_popup(m, n)
  806.     MARK        m, n;    /* the range of text to change */
  807. {
  808.     int        i;
  809.     int        y, x;    /* position where the window will pop up at */
  810.     int        key;    /* keystroke from the user */
  811.     int        sel;    /* index of the selected operation */
  812.     static int    dfltsel;/* default value of sel */
  813.     static char    *labels[11] =
  814.     {
  815.         "ESC cancel!         ",
  816.         " d  delete (cut)    ",
  817.         " y  yank (copy)     ",
  818.         " p  paste after     ",
  819.         " P  paste before    ",
  820.         " >  more indented   ",
  821.         " <  less indented   ",
  822.         " =  reformat        ",
  823.         " !  external filter ",
  824.         " ZZ save & exit     ",
  825.         " u  undo previous   "
  826.     };
  827.  
  828.     /* try to position the menu near the cursor */
  829.     x = physcol - (MENU_WIDTH / 2);
  830.     if (x < 0)
  831.         x = 0;
  832.     else if (x + MENU_WIDTH + 2 > COLS)
  833.         x = COLS - MENU_WIDTH - 2;
  834.     if (markline(cursor) < topline || markline(cursor) > botline)
  835.         y = 0;
  836.     else if (markline(cursor) + 1L + MENU_HEIGHT > botline)
  837.         y = (int)(markline(cursor) - topline) - MENU_HEIGHT;
  838.     else
  839.         y = (int)(markline(cursor) - topline) + 1L;
  840.  
  841.     /* draw the menu */
  842.     for (sel = 0; sel < MENU_HEIGHT; sel++)
  843.     {
  844.         move(y + sel, x);
  845.         do_POPUP();
  846.         if (sel == dfltsel)
  847.             qaddstr("-->");
  848.         else
  849.             qaddstr("   ");
  850.         qaddstr(labels[sel]);
  851.         do_SE();
  852.     }
  853.  
  854.     /* get a selection */
  855.     move(y + dfltsel, x + 4);
  856.     for (sel = dfltsel; (key = getkey(WHEN_POPUP)) != '\\' && key != '\r'; )
  857.     {
  858.         /* erase the selection arrow */
  859.         move(y + sel, x);
  860.         do_POPUP();
  861.         qaddstr("   ");
  862.         qaddstr(labels[sel]);
  863.         do_SE();
  864.  
  865.         /* process the user's keystroke */
  866.         if (key == 'j' && sel < MENU_HEIGHT - 1)
  867.         {
  868.             sel++;
  869.         }
  870.         else if (key == 'k' && sel > 0)
  871.         {
  872.             sel--;
  873.         }
  874.         else if (key == '\033')
  875.         {
  876.             sel = 0;
  877.             break;
  878.         }
  879.         else
  880.         {
  881.             for (i = 1; i < MENU_HEIGHT && labels[i][1] != key; i++)
  882.             {
  883.             }
  884.             if (i < MENU_HEIGHT)
  885.             {
  886.                 sel = i;
  887.                 break;
  888.             }
  889.         }
  890.  
  891.         /* redraw the arrow, possibly in a new place */
  892.         move(y + sel, x);
  893.         do_POPUP();
  894.         qaddstr("-->");
  895.         qaddstr(labels[sel]);
  896.         do_SE();
  897.         move(y + sel, x + 4);
  898.     }
  899.  
  900.     /* in most cases, the default selection is "paste after" */
  901.     dfltsel = 3;
  902.  
  903.     /* perform the appropriate action */
  904.     switch (sel)
  905.     {
  906.       case 0:
  907.         m = cursor;
  908.         dfltsel = 0;
  909.         break;
  910.  
  911.       case 1: /* delete (cut) */
  912.         m = v_delete(m, n);
  913.         break;
  914.  
  915.       case 2: /* yank (copy) */
  916.         m = v_yank(m, n);
  917.         break;
  918.  
  919.       case 3: /* paste after */
  920.         m = v_paste(n, 1L, 'P');
  921.         break;
  922.  
  923.       case 4: /* paste before */
  924.         m = v_paste(m, 1L, 'P');
  925.         dfltsel = 4;
  926.         break;
  927.  
  928.       case 5: /* more indented */
  929.         m = v_rshift(m, n);
  930.         dfltsel = 5;
  931.         break;
  932.  
  933.       case 6: /* less indented */
  934.         m = v_lshift(m, n);
  935.         dfltsel = 6;
  936.         break;
  937.  
  938.       case 7: /* reformat */
  939.         m = v_reformat(m, n);
  940.         dfltsel = 7;
  941.         break;
  942.  
  943.       case 8: /* external filter */
  944.         m = v_filter(m, n);
  945.         dfltsel = 0;
  946.         break;
  947.  
  948.       case 9: /* save & exit */
  949.         /* get confirmation first */
  950.         do
  951.         {
  952.             key = getkey(0);
  953.         } while (key != '\\' && key != 'Z' && key != '\r'    /* good */
  954.               && key != '\033');                 /* bad  */
  955.         if (key != '\033')
  956.         {
  957.             m = v_xit(m, 0L, 'Z');
  958.         }
  959.         break;
  960.  
  961.       case 10: /* undo previous */
  962.         m = v_undo(m);
  963.         dfltsel = 9;
  964.         break;
  965.     }
  966.  
  967.     /* arrange for the menu to be erased (except "save & exit" doesn't care)
  968.      */
  969. #if 0
  970.     /* hmm.  doesn't work for `\\Z'  -nox */
  971.     if (sel != 9)
  972. #endif
  973.         redraw(MARK_UNSET, FALSE);
  974.  
  975.     return m;
  976. }
  977. #endif /* undef NO_POPUP */
  978.