home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / vi.c < prev    next >
C/C++ Source or Header  |  1996-10-23  |  31KB  |  1,252 lines

  1. /*-
  2.  * Copyright (c) 1992, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)vi.c    10.57 (Berkeley) 10/13/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/time.h>
  19.  
  20. #include <bitstring.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <limits.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <unistd.h>
  28.  
  29. #include "../common/common.h"
  30. #include "vi.h"
  31.  
  32. typedef enum {
  33.     GC_ERR, GC_ERR_NOFLUSH, GC_EVENT, GC_FATAL, GC_INTERRUPT, GC_OK
  34. } gcret_t;
  35.  
  36. static VIKEYS const
  37.            *v_alias __P((SCR *, VICMD *, VIKEYS const *));
  38. static gcret_t    v_cmd __P((SCR *, VICMD *, VICMD *, VICMD *, int *, int *));
  39. static int    v_count __P((SCR *, ARG_CHAR_T, u_long *));
  40. static void    v_dtoh __P((SCR *));
  41. static int    v_init __P((SCR *));
  42. static gcret_t    v_key __P((SCR *, int, EVENT *, u_int32_t));
  43. static int    v_keyword __P((SCR *));
  44. static int    v_motion __P((SCR *, VICMD *, VICMD *, int *));
  45.  
  46. #if defined(DEBUG) && defined(COMLOG)
  47. static void    v_comlog __P((SCR *, VICMD *));
  48. #endif
  49.  
  50. /*
  51.  * Side-effect:
  52.  *    The dot structure can be set by the underlying vi functions,
  53.  *    see v_Put() and v_put().
  54.  */
  55. #define    DOT        (&VIP(sp)->sdot)
  56. #define    DOTMOTION    (&VIP(sp)->sdotmotion)
  57.  
  58. /*
  59.  * vi --
  60.  *     Main vi command loop.
  61.  *
  62.  * PUBLIC: int vi __P((SCR **));
  63.  */
  64. int
  65. vi(spp)
  66.     SCR **spp;
  67. {
  68.     GS *gp;
  69.     MARK abs;
  70.     SCR *next, *sp;
  71.     VICMD cmd, *vp;
  72.     VI_PRIVATE *vip;
  73.     int comcount, mapped, rval;
  74.  
  75.     /* Get the first screen. */
  76.     sp = *spp;
  77.     gp = sp->gp;
  78.  
  79.     /* Initialize the command structure. */
  80.     vp = &cmd;
  81.     memset(vp, 0, sizeof(VICMD));
  82.  
  83.     /* Reset strange attraction. */
  84.     F_SET(vp, VM_RCM_SET);
  85.  
  86.     /* Initialize the vi screen. */
  87.     if (v_init(sp))
  88.         return (1);
  89.  
  90.     /* Set the focus. */
  91.     (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
  92.  
  93.     for (vip = VIP(sp), rval = 0;;) {
  94.         /* Resolve messages. */
  95.         if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
  96.             goto ret;
  97.  
  98.         /*
  99.          * If not skipping a refresh, return to command mode and
  100.          * refresh the screen.
  101.          */
  102.         if (F_ISSET(vip, VIP_S_REFRESH))
  103.             F_CLR(vip, VIP_S_REFRESH);
  104.         else {
  105.             sp->showmode = SM_COMMAND;
  106.             if (vs_refresh(sp, 0))
  107.                 goto ret;
  108.         }
  109.  
  110.         /* Set the new favorite position. */
  111.         if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
  112.             F_CLR(vip, VIP_RCM_LAST);
  113.             (void)vs_column(sp, &sp->rcm);
  114.         }
  115.  
  116.         /*
  117.          * If not currently in a map, log the cursor position,
  118.          * and set a flag so that this command can become the
  119.          * DOT command.
  120.          */
  121.         if (MAPPED_KEYS_WAITING(sp))
  122.             mapped = 1;
  123.         else {
  124.             if (log_cursor(sp))
  125.                 goto err;
  126.             mapped = 0;
  127.         }
  128.  
  129.         /*
  130.          * There may be an ex command waiting, and we returned here
  131.          * only because we exited a screen or file.  In this case,
  132.          * we simply go back into the ex parser.
  133.          */
  134.         if (EXCMD_RUNNING(gp)) {
  135.             vp->kp = &vikeys[':'];
  136.             goto ex_continue;
  137.         }
  138.  
  139.         /* Refresh the command structure. */
  140.         memset(vp, 0, sizeof(VICMD));
  141.  
  142.         /*
  143.          * We get a command, which may or may not have an associated
  144.          * motion.  If it does, we get it too, calling its underlying
  145.          * function to get the resulting mark.  We then call the
  146.          * command setting the cursor to the resulting mark.
  147.          *
  148.          * !!!
  149.          * Vi historically flushed mapped characters on error, but
  150.          * entering extra <escape> characters at the beginning of
  151.          * a map wasn't considered an error -- in fact, users would
  152.          * put leading <escape> characters in maps to clean up vi
  153.          * state before the map was interpreted.  Beauty!
  154.          */
  155.         switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
  156.         case GC_ERR:
  157.             goto err;
  158.         case GC_ERR_NOFLUSH:
  159.             goto gc_err_noflush;
  160.         case GC_EVENT:
  161.             if (v_event_exec(sp, vp))
  162.                 goto err;
  163.             goto gc_event;
  164.         case GC_FATAL:
  165.             goto ret;
  166.         case GC_INTERRUPT:
  167.             goto intr;
  168.         case GC_OK:
  169.             break;
  170.         }
  171.  
  172.         /* Check for security setting. */
  173.         if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
  174.             ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
  175.             goto err;
  176.         }
  177.  
  178.         /*
  179.          * Historical practice: if a dot command gets a new count,
  180.          * any motion component goes away, i.e. "d3w2." deletes a
  181.          * total of 5 words.
  182.          */
  183.         if (F_ISSET(vp, VC_ISDOT) && comcount)
  184.             DOTMOTION->count = 1;
  185.  
  186.         /* Copy the key flags into the local structure. */
  187.         F_SET(vp, vp->kp->flags);
  188.  
  189.         /* Prepare to set the previous context. */
  190.         if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
  191.             abs.lno = sp->lno;
  192.             abs.cno = sp->cno;
  193.         }
  194.  
  195.         /*
  196.          * Set the three cursor locations to the current cursor.  The
  197.          * underlying routines don't bother if the cursor doesn't move.
  198.          * This also handles line commands (e.g. Y) defaulting to the
  199.          * current line.
  200.          */
  201.         vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
  202.         vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
  203.  
  204.         /*
  205.          * Do any required motion; v_motion sets the from MARK and the
  206.          * line mode flag, as well as the VM_RCM flags.
  207.          */
  208.         if (F_ISSET(vp, V_MOTION) &&
  209.             v_motion(sp, DOTMOTION, vp, &mapped)) {
  210.             if (INTERRUPTED(sp))
  211.                 goto intr;
  212.             goto err;
  213.         }
  214.  
  215.         /*
  216.          * If a count is set and the command is line oriented, set the
  217.          * to MARK here relative to the cursor/from MARK.  This is for
  218.          * commands that take both counts and motions, i.e. "4yy" and
  219.          * "y%".  As there's no way the command can know which the user
  220.          * did, we have to do it here.  (There are commands that are
  221.          * line oriented and that take counts ("#G", "#H"), for which
  222.          * this calculation is either completely meaningless or wrong.
  223.          * Each command must validate the value for itself.
  224.          */
  225.         if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
  226.             vp->m_stop.lno += vp->count - 1;
  227.  
  228.         /* Increment the command count. */
  229.         ++sp->ccnt;
  230.  
  231. #if defined(DEBUG) && defined(COMLOG)
  232.         v_comlog(sp, vp);
  233. #endif
  234.         /* Call the function. */
  235. ex_continue:    if (vp->kp->func(sp, vp))
  236.             goto err;
  237. gc_event:
  238. #ifdef DEBUG
  239.         /* Make sure no function left the temporary space locked. */
  240.         if (F_ISSET(gp, G_TMP_INUSE)) {
  241.             F_CLR(gp, G_TMP_INUSE);
  242.             msgq(sp, M_ERR,
  243.                 "232|vi: temporary buffer not released");
  244.         }
  245. #endif
  246.         /*
  247.          * If we're exiting this screen, move to the next one, or, if
  248.          * there aren't any more, return to the main editor loop.  The
  249.          * ordering is careful, don't discard the contents of sp until
  250.          * the end.
  251.          */
  252.         if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
  253.             if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
  254.                 goto ret;
  255.             if (vs_discard(sp, &next))
  256.                 goto ret;
  257.             if (next == NULL && vs_swap(sp, &next, NULL))
  258.                 goto ret;
  259.             *spp = next;
  260.             if (screen_end(sp))
  261.                 goto ret;
  262.             if (next == NULL)
  263.                 break;
  264.  
  265.             /* Switch screens, change focus. */
  266.             sp = next;
  267.             vip = VIP(sp);
  268.             (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
  269.  
  270.             /* Don't trust the cursor. */
  271.             F_SET(vip, VIP_CUR_INVALID);
  272.  
  273.             continue;
  274.         }
  275.  
  276.         /*
  277.          * Set the dot command structure.
  278.          *
  279.          * !!!
  280.          * Historically, commands which used mapped keys did not
  281.          * set the dot command, with the exception of the text
  282.          * input commands.
  283.          */
  284.         if (F_ISSET(vp, V_DOT) && !mapped) {
  285.             *DOT = cmd;
  286.             F_SET(DOT, VC_ISDOT);
  287.  
  288.             /*
  289.              * If a count was supplied for both the command and
  290.              * its motion, the count was used only for the motion.
  291.              * Turn the count back on for the dot structure.
  292.              */
  293.             if (F_ISSET(vp, VC_C1RESET))
  294.                 F_SET(DOT, VC_C1SET);
  295.  
  296.             /* VM flags aren't retained. */
  297.             F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
  298.         }
  299.  
  300.         /*
  301.          * Some vi row movements are "attracted" to the last position
  302.          * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
  303.          * commands' candle.  If the movement is to the EOL the vi
  304.          * command handles it.  If it's to the beginning, we handle it
  305.          * here.
  306.          *
  307.          * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
  308.          * flag, but do the work themselves.  The reason is that they
  309.          * have to modify the column in case they're being used as a
  310.          * motion component.  Other similar commands (e.g. +, -) don't
  311.          * have to modify the column because they are always line mode
  312.          * operations when used as motions, so the column number isn't
  313.          * of any interest.
  314.          *
  315.          * Does this totally violate the screen and editor layering?
  316.          * You betcha.  As they say, if you think you understand it,
  317.          * you don't.
  318.          */
  319.         switch (F_ISSET(vp, VM_RCM_MASK)) {
  320.         case 0:
  321.         case VM_RCM_SET:
  322.             break;
  323.         case VM_RCM:
  324.             vp->m_final.cno = vs_rcm(sp,
  325.                 vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
  326.             break;
  327.         case VM_RCM_SETLAST:
  328.             F_SET(vip, VIP_RCM_LAST);
  329.             break;
  330.         case VM_RCM_SETFNB:
  331.             vp->m_final.cno = 0;
  332.             /* FALLTHROUGH */
  333.         case VM_RCM_SETNNB:
  334.             if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
  335.                 goto err;
  336.             break;
  337.         default:
  338.             abort();
  339.         }
  340.  
  341.         /* Update the cursor. */
  342.         sp->lno = vp->m_final.lno;
  343.         sp->cno = vp->m_final.cno;
  344.  
  345.         /*
  346.          * Set the absolute mark -- set even if a tags or similar
  347.          * command, since the tag may be moving to the same file.
  348.          */
  349.         if ((F_ISSET(vp, V_ABS) ||
  350.             F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno ||
  351.             F_ISSET(vp, V_ABS_C) &&
  352.             (sp->lno != abs.lno || sp->cno != abs.cno)) &&
  353.             mark_set(sp, ABSMARK1, &abs, 1))
  354.             goto err;
  355.  
  356.         if (0) {
  357. err:            if (v_event_flush(sp, CH_MAPPED))
  358.                 msgq(sp, M_BERR,
  359.                 "110|Vi command failed: mapped keys discarded");
  360.         }
  361.  
  362.         /*
  363.          * Check and clear interrupts.  There's an obvious race, but
  364.          * it's not worth fixing.
  365.          */
  366. gc_err_noflush:    if (INTERRUPTED(sp)) {
  367. intr:            CLR_INTERRUPT(sp);
  368.             if (v_event_flush(sp, CH_MAPPED))
  369.                 msgq(sp, M_ERR,
  370.                     "231|Interrupted: mapped keys discarded");
  371.             else
  372.                 msgq(sp, M_ERR, "236|Interrupted");
  373.         }
  374.  
  375.         /* If the last command switched screens, update. */
  376.         if (F_ISSET(sp, SC_SSWITCH)) {
  377.             F_CLR(sp, SC_SSWITCH);
  378.  
  379.             /*
  380.              * If the current screen is still displayed, it will
  381.              * need a new status line.
  382.              */
  383.             F_SET(sp, SC_STATUS);
  384.  
  385.             /* Switch screens, change focus. */
  386.             sp = sp->nextdisp;
  387.             vip = VIP(sp);
  388.             (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
  389.  
  390.             /* Don't trust the cursor. */
  391.             F_SET(vip, VIP_CUR_INVALID);
  392.  
  393.             /* Refresh so we can display messages. */
  394.             if (vs_refresh(sp, 1))
  395.                 return (1);
  396.         }
  397.  
  398.         /* If the last command switched files, change focus. */
  399.         if (F_ISSET(sp, SC_FSWITCH)) {
  400.             F_CLR(sp, SC_FSWITCH);
  401.             (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
  402.         }
  403.  
  404.         /* If leaving vi, return to the main editor loop. */
  405.         if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
  406.             *spp = sp;
  407.             v_dtoh(sp);
  408.             break;
  409.         }
  410.     }
  411.     if (0)
  412. ret:        rval = 1;
  413.     return (rval);
  414. }
  415.  
  416. #define    KEY(key, ec_flags) {                        \
  417.     if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK)        \
  418.         return (gcret);                        \
  419.     if (ev.e_value == K_ESCAPE)                    \
  420.         goto esc;                        \
  421.     if (F_ISSET(&ev.e_ch, CH_MAPPED))                \
  422.         *mappedp = 1;                        \
  423.     key = ev.e_c;                            \
  424. }
  425.  
  426. /*
  427.  * The O_TILDEOP option makes the ~ command take a motion instead
  428.  * of a straight count.  This is the replacement structure we use
  429.  * instead of the one currently in the VIKEYS table.
  430.  *
  431.  * XXX
  432.  * This should probably be deleted -- it's not all that useful, and
  433.  * we get help messages wrong.
  434.  */
  435. VIKEYS const tmotion = {
  436.     v_mulcase,    V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
  437.     "[count]~[count]motion",
  438.     " ~ change case to motion"
  439. };
  440.  
  441. /*
  442.  * v_cmd --
  443.  *
  444.  * The command structure for vi is less complex than ex (and don't think
  445.  * I'm not grateful!)  The command syntax is:
  446.  *
  447.  *    [count] [buffer] [count] key [[motion] | [buffer] [character]]
  448.  *
  449.  * and there are several special cases.  The motion value is itself a vi
  450.  * command, with the syntax:
  451.  *
  452.  *    [count] key [character]
  453.  */
  454. static gcret_t
  455. v_cmd(sp, dp, vp, ismotion, comcountp, mappedp)
  456.     SCR *sp;
  457.     VICMD *dp, *vp;
  458.     VICMD *ismotion;    /* Previous key if getting motion component. */
  459.     int *comcountp, *mappedp;
  460. {
  461.     enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart;
  462.     EVENT ev;
  463.     VIKEYS const *kp;
  464.     gcret_t gcret;
  465.     u_int flags;
  466.     CHAR_T key;
  467.     char *s;
  468.  
  469.     /*
  470.      * Get a key.
  471.      *
  472.      * <escape> cancels partial commands, i.e. a command where at least
  473.      * one non-numeric character has been entered.  Otherwise, it beeps
  474.      * the terminal.
  475.      *
  476.      * !!!
  477.      * POSIX 1003.2-1992 explicitly disallows cancelling commands where
  478.      * all that's been entered is a number, requiring that the terminal
  479.      * be alerted.
  480.      */
  481.     cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL;
  482.     if ((gcret =
  483.         v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) {
  484.         if (gcret == GC_EVENT)
  485.             vp->ev = ev;
  486.         return (gcret);
  487.     }
  488.     if (ev.e_value == K_ESCAPE)
  489.         goto esc;
  490.     if (F_ISSET(&ev.e_ch, CH_MAPPED))
  491.         *mappedp = 1;
  492.     key = ev.e_c;
  493.  
  494.     if (ismotion == NULL)
  495.         cpart = NOTPARTIAL;
  496.  
  497.     /* Pick up optional buffer. */
  498.     if (key == '"') {
  499.         cpart = ISPARTIAL;
  500.         if (ismotion != NULL) {
  501.             v_emsg(sp, NULL, VIM_COMBUF);
  502.             return (GC_ERR);
  503.         }
  504.         KEY(vp->buffer, 0);
  505.         F_SET(vp, VC_BUFFER);
  506.  
  507.         KEY(key, EC_MAPCOMMAND);
  508.     }
  509.  
  510.     /*
  511.      * Pick up optional count, where a leading 0 is not a count,
  512.      * it's a command.
  513.      */
  514.     if (isdigit(key) && key != '0') {
  515.         if (v_count(sp, key, &vp->count))
  516.             return (GC_ERR);
  517.         F_SET(vp, VC_C1SET);
  518.         *comcountp = 1;
  519.  
  520.         KEY(key, EC_MAPCOMMAND);
  521.     } else
  522.         *comcountp = 0;
  523.  
  524.     /* Pick up optional buffer. */
  525.     if (key == '"') {
  526.         cpart = ISPARTIAL;
  527.         if (F_ISSET(vp, VC_BUFFER)) {
  528.             msgq(sp, M_ERR, "234|Only one buffer may be specified");
  529.             return (GC_ERR);
  530.         }
  531.         if (ismotion != NULL) {
  532.             v_emsg(sp, NULL, VIM_COMBUF);
  533.             return (GC_ERR);
  534.         }
  535.         KEY(vp->buffer, 0);
  536.         F_SET(vp, VC_BUFFER);
  537.  
  538.         KEY(key, EC_MAPCOMMAND);
  539.     }
  540.  
  541.     /* Check for an OOB command key. */
  542.     cpart = ISPARTIAL;
  543.     if (key > MAXVIKEY) {
  544.         v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM);
  545.         return (GC_ERR);
  546.     }
  547.     kp = &vikeys[vp->key = key];
  548.  
  549.     /*
  550.      * !!!
  551.      * Historically, D accepted and then ignored a count.  Match it.
  552.      */
  553.     if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) {
  554.         *comcountp = 0;
  555.         vp->count = 0;
  556.         F_CLR(vp, VC_C1SET);
  557.     }
  558.  
  559.     /* Check for command aliases. */
  560.     if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL)
  561.         return (GC_ERR);
  562.  
  563.     /* The tildeop option makes the ~ command take a motion. */
  564.     if (key == '~' && O_ISSET(sp, O_TILDEOP))
  565.         kp = &tmotion;
  566.  
  567.     vp->kp = kp;
  568.  
  569.     /*
  570.      * Find the command.  The only legal command with no underlying
  571.      * function is dot.  It's historic practice that <escape> doesn't
  572.      * just erase the preceding number, it beeps the terminal as well.
  573.      * It's a common problem, so just beep the terminal unless verbose
  574.      * was set.
  575.      */
  576.     if (kp->func == NULL) {
  577.         if (key != '.') {
  578.             v_emsg(sp, KEY_NAME(sp, key),
  579.                 ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM);
  580.             return (GC_ERR);
  581.         }
  582.  
  583.         /* If called for a motion command, stop now. */
  584.         if (dp == NULL)
  585.             goto usage;
  586.  
  587.         /*
  588.          * !!!
  589.          * If a '.' is immediately entered after an undo command, we
  590.          * replay the log instead of redoing the last command.  This
  591.          * is necessary because 'u' can't set the dot command -- see
  592.          * vi/v_undo.c:v_undo for details.
  593.          */
  594.         if (VIP(sp)->u_ccnt == sp->ccnt) {
  595.             vp->kp = &vikeys['u'];
  596.             F_SET(vp, VC_ISDOT);
  597.             return (GC_OK);
  598.         }
  599.  
  600.         /* Otherwise, a repeatable command must have been executed. */
  601.         if (!F_ISSET(dp, VC_ISDOT)) {
  602.             msgq(sp, M_ERR, "208|No command to repeat");
  603.             return (GC_ERR);
  604.         }
  605.  
  606.         /* Set new count/buffer, if any, and return. */
  607.         if (F_ISSET(vp, VC_C1SET)) {
  608.             F_SET(dp, VC_C1SET);
  609.             dp->count = vp->count;
  610.         }
  611.         if (F_ISSET(vp, VC_BUFFER))
  612.             dp->buffer = vp->buffer;
  613.  
  614.         *vp = *dp;
  615.         return (GC_OK);
  616.     }
  617.  
  618.     /* Set the flags based on the command flags. */
  619.     flags = kp->flags;
  620.  
  621.     /* Check for illegal count. */
  622.     if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
  623.         goto usage;
  624.  
  625.     /* Illegal motion command. */
  626.     if (ismotion == NULL) {
  627.         /* Illegal buffer. */
  628.         if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
  629.             goto usage;
  630.  
  631.         /* Required buffer. */
  632.         if (LF_ISSET(V_RBUF)) {
  633.             KEY(vp->buffer, 0);
  634.             F_SET(vp, VC_BUFFER);
  635.         }
  636.     }
  637.  
  638.     /*
  639.      * Special case: '[', ']' and 'Z' commands.  Doesn't the fact that
  640.      * the *single* characters don't mean anything but the *doubled*
  641.      * characters do, just frost your shorts?
  642.      */
  643.     if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
  644.         /*
  645.          * Historically, half entered [[, ]] or Z commands weren't
  646.          * cancelled by <escape>, the terminal was beeped instead.
  647.          * POSIX.2-1992 probably didn't notice, and requires that
  648.          * they be cancelled instead of beeping.  Seems fine to me.
  649.          *
  650.          * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular
  651.          * vi meta-character, and we don't want the user to wait while
  652.          * we time out a possible mapping.  This *appears* to match
  653.          * historic vi practice, but with mapping characters, you Just
  654.          * Never Know.
  655.          */
  656.         KEY(key, 0);
  657.  
  658.         if (vp->key != key) {
  659. usage:            if (ismotion == NULL)
  660.                 s = kp->usage;
  661.             else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP))
  662.                 s = tmotion.usage;
  663.             else
  664.                 s = vikeys[ismotion->key].usage;
  665.             v_emsg(sp, s, VIM_USAGE);
  666.             return (GC_ERR);
  667.         }
  668.     }
  669.     /* Special case: 'z' command. */
  670.     if (vp->key == 'z') {
  671.         KEY(vp->character, 0);
  672.         if (isdigit(vp->character)) {
  673.             if (v_count(sp, vp->character, &vp->count2))
  674.                 return (GC_ERR);
  675.             F_SET(vp, VC_C2SET);
  676.             KEY(vp->character, 0);
  677.         }
  678.     }
  679.  
  680.     /*
  681.      * Commands that have motion components can be doubled to
  682.      * imply the current line.
  683.      */
  684.     if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
  685.         msgq(sp, M_ERR, "210|%s may not be used as a motion command",
  686.             KEY_NAME(sp, key));
  687.         return (GC_ERR);
  688.     }
  689.  
  690.     /* Required character. */
  691.     if (LF_ISSET(V_CHAR))
  692.         KEY(vp->character, 0);
  693.  
  694.     /* Get any associated cursor word. */
  695.     if (F_ISSET(kp, V_KEYW) && v_keyword(sp))
  696.         return (GC_ERR);
  697.  
  698.     return (GC_OK);
  699.  
  700. esc:    switch (cpart) {
  701.     case COMMANDMODE:
  702.         msgq(sp, M_BERR, "211|Already in command mode");
  703.         return (GC_ERR_NOFLUSH);
  704.     case ISPARTIAL:
  705.         break;
  706.     case NOTPARTIAL:
  707.         (void)sp->gp->scr_bell(sp);
  708.         break;
  709.     }
  710.     return (GC_ERR);
  711. }
  712.  
  713. /*
  714.  * v_motion --
  715.  *
  716.  * Get resulting motion mark.
  717.  */
  718. static int
  719. v_motion(sp, dm, vp, mappedp)
  720.     SCR *sp;
  721.     VICMD *dm, *vp;
  722.     int *mappedp;
  723. {
  724.     VICMD motion;
  725.     size_t len;
  726.     u_long cnt;
  727.     u_int flags;
  728.     int tilde_reset, notused;
  729.  
  730.     /*
  731.      * If '.' command, use the dot motion, else get the motion command.
  732.      * Clear any line motion flags, the subsequent motion isn't always
  733.      * the same, i.e. "/aaa" may or may not be a line motion.
  734.      */
  735.     if (F_ISSET(vp, VC_ISDOT)) {
  736.         motion = *dm;
  737.         F_SET(&motion, VC_ISDOT);
  738.         F_CLR(&motion, VM_COMMASK);
  739.     } else {
  740.         memset(&motion, 0, sizeof(VICMD));
  741.         if (v_cmd(sp, NULL, &motion, vp, ¬used, mappedp) != GC_OK)
  742.             return (1);
  743.     }
  744.  
  745.     /*
  746.      * A count may be provided both to the command and to the motion, in
  747.      * which case the count is multiplicative.  For example, "3y4y" is the
  748.      * same as "12yy".  This count is provided to the motion command and
  749.      * not to the regular function.
  750.      */
  751.     cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
  752.     if (F_ISSET(vp, VC_C1SET)) {
  753.         motion.count *= vp->count;
  754.         F_SET(&motion, VC_C1SET);
  755.  
  756.         /*
  757.          * Set flags to restore the original values of the command
  758.          * structure so dot commands can change the count values,
  759.          * e.g. "2dw" "3." deletes a total of five words.
  760.          */
  761.         F_CLR(vp, VC_C1SET);
  762.         F_SET(vp, VC_C1RESET);
  763.     }
  764.  
  765.     /*
  766.      * Some commands can be repeated to indicate the current line.  In
  767.      * this case, or if the command is a "line command", set the flags
  768.      * appropriately.  If not a doubled command, run the function to get
  769.      * the resulting mark.
  770.       */
  771.     if (vp->key == motion.key) {
  772.         F_SET(vp, VM_LDOUBLE | VM_LMODE);
  773.  
  774.         /* Set the origin of the command. */
  775.         vp->m_start.lno = sp->lno;
  776.         vp->m_start.cno = 0;
  777.  
  778.         /*
  779.          * Set the end of the command.
  780.          *
  781.          * If the current line is missing, i.e. the file is empty,
  782.          * historic vi permitted a "cc" or "!!" command to insert
  783.          * text.
  784.          */
  785.         vp->m_stop.lno = sp->lno + motion.count - 1;
  786.         if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) {
  787.             if (vp->m_stop.lno != 1 ||
  788.                vp->key != 'c' && vp->key != '!') {
  789.                 v_emsg(sp, NULL, VIM_EMPTY);
  790.                 return (1);
  791.             }
  792.             vp->m_stop.cno = 0;
  793.         } else
  794.             vp->m_stop.cno = len ? len - 1 : 0;
  795.     } else {
  796.         /*
  797.          * Motion commands change the underlying movement (*snarl*).
  798.          * For example, "l" is illegal at the end of a line, but "dl"
  799.          * is not.  Set flags so the function knows the situation.
  800.          */
  801.         motion.rkp = vp->kp;
  802.  
  803.         /*
  804.          * XXX
  805.          * Use yank instead of creating a new motion command, it's a
  806.          * lot easier for now.
  807.          */
  808.         if (vp->kp == &tmotion) {
  809.             tilde_reset = 1;
  810.             vp->kp = &vikeys['y'];
  811.         } else
  812.             tilde_reset = 0;
  813.  
  814.         /*
  815.          * Copy the key flags into the local structure, except for the
  816.          * RCM flags -- the motion command will set the RCM flags in
  817.          * the vp structure if necessary.  This means that the motion
  818.          * command is expected to determine where the cursor ends up!
  819.          * However, we save off the current RCM mask and restore it if
  820.          * it no RCM flags are set by the motion command, with a small
  821.          * modification.
  822.          *
  823.          * We replace the VM_RCM_SET flag with the VM_RCM flag.  This
  824.          * is so that cursor movement doesn't set the relative position
  825.          * unless the motion command explicitly specified it.  This
  826.          * appears to match historic practice, but I've never been able
  827.          * to develop a hard-and-fast rule.
  828.          */
  829.         flags = F_ISSET(vp, VM_RCM_MASK);
  830.         if (LF_ISSET(VM_RCM_SET)) {
  831.             LF_SET(VM_RCM);
  832.             LF_CLR(VM_RCM_SET);
  833.         }
  834.         F_CLR(vp, VM_RCM_MASK);
  835.         F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK);
  836.  
  837.         /*
  838.          * Set the three cursor locations to the current cursor.  This
  839.          * permits commands like 'j' and 'k', that are line oriented
  840.          * motions and have special cursor suck semantics when they are
  841.          * used as standalone commands, to ignore column positioning.
  842.          */
  843.         motion.m_final.lno =
  844.             motion.m_stop.lno = motion.m_start.lno = sp->lno;
  845.         motion.m_final.cno =
  846.             motion.m_stop.cno = motion.m_start.cno = sp->cno;
  847.  
  848.         /* Run the function. */
  849.         if ((motion.kp->func)(sp, &motion))
  850.             return (1);
  851.  
  852.         /*
  853.          * If the current line is missing, i.e. the file is empty,
  854.          * historic vi allowed "c<motion>" or "!<motion>" to insert
  855.          * text.  Otherwise fail -- most motion commands will have
  856.          * already failed, but some, e.g. G, succeed in empty files.
  857.          */
  858.         if (!db_exist(sp, vp->m_stop.lno)) {
  859.             if (vp->m_stop.lno != 1 ||
  860.                vp->key != 'c' && vp->key != '!') {
  861.                 v_emsg(sp, NULL, VIM_EMPTY);
  862.                 return (1);
  863.             }
  864.             vp->m_stop.cno = 0;
  865.         }
  866.  
  867.         /*
  868.          * XXX
  869.          * See above.
  870.          */
  871.         if (tilde_reset)
  872.             vp->kp = &tmotion;
  873.  
  874.         /*
  875.          * Copy cut buffer, line mode and cursor position information
  876.          * from the motion command structure, i.e. anything that the
  877.          * motion command can set for us.  The commands can flag the
  878.          * movement as a line motion (see v_sentence) as well as set
  879.          * the VM_RCM_* flags explicitly.
  880.          */
  881.         F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK));
  882.  
  883.         /*
  884.          * If the motion command set no relative motion flags, use
  885.          * the (slightly) modified previous values.
  886.          */
  887.         if (!F_ISSET(vp, VM_RCM_MASK))
  888.             F_SET(vp, flags);
  889.  
  890.         /*
  891.          * Commands can change behaviors based on the motion command
  892.          * used, for example, the ! command repeated the last bang
  893.          * command if N or n was used as the motion.
  894.          */
  895.         vp->rkp = motion.kp;
  896.  
  897.         /*
  898.          * Motion commands can reset all of the cursor information.
  899.          * If the motion is in the reverse direction, switch the
  900.          * from and to MARK's so that it's in a forward direction.
  901.          * Motions are from the from MARK to the to MARK (inclusive).
  902.          */
  903.         if (motion.m_start.lno > motion.m_stop.lno ||
  904.             motion.m_start.lno == motion.m_stop.lno &&
  905.             motion.m_start.cno > motion.m_stop.cno) {
  906.             vp->m_start = motion.m_stop;
  907.             vp->m_stop = motion.m_start;
  908.         } else {
  909.             vp->m_start = motion.m_start;
  910.             vp->m_stop = motion.m_stop;
  911.         }
  912.         vp->m_final = motion.m_final;
  913.     }
  914.  
  915.     /*
  916.      * If the command sets dot, save the motion structure.  The motion
  917.      * count was changed above and needs to be reset, that's why this
  918.      * is done here, and not in the calling routine.
  919.      */
  920.     if (F_ISSET(vp->kp, V_DOT)) {
  921.         *dm = motion;
  922.         dm->count = cnt;
  923.     }
  924.     return (0);
  925. }
  926.  
  927. /*
  928.  * v_init --
  929.  *    Initialize the vi screen.
  930.  */
  931. static int
  932. v_init(sp)
  933.     SCR *sp;
  934. {
  935.     GS *gp;
  936.     VI_PRIVATE *vip;
  937.  
  938.     gp = sp->gp;
  939.     vip = VIP(sp);
  940.  
  941.     /* Switch into vi. */
  942.     if (gp->scr_screen(sp, SC_VI))
  943.         return (1);
  944.     (void)gp->scr_attr(sp, SA_ALTERNATE, 1);
  945.  
  946.     F_CLR(sp, SC_EX | SC_SCR_EX);
  947.     F_SET(sp, SC_VI);
  948.  
  949.     /*
  950.      * Initialize screen values.
  951.      *
  952.      * Small windows: see vs_refresh(), section 6a.
  953.      *
  954.      * Setup:
  955.      *    t_minrows is the minimum rows to display
  956.      *    t_maxrows is the maximum rows to display (rows - 1)
  957.      *    t_rows is the rows currently being displayed
  958.      */
  959.     sp->rows = vip->srows = O_VAL(sp, O_LINES);
  960.     sp->cols = O_VAL(sp, O_COLUMNS);
  961.     sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
  962.     if (sp->rows != 1) {
  963.         if (sp->t_rows > sp->rows - 1) {
  964.             sp->t_minrows = sp->t_rows = sp->rows - 1;
  965.             msgq(sp, M_INFO,
  966.                 "214|Windows option value is too large, max is %u",
  967.                 sp->t_rows);
  968.         }
  969.         sp->t_maxrows = sp->rows - 1;
  970.     } else
  971.         sp->t_maxrows = 1;
  972.     sp->woff = 0;
  973.  
  974.     /* Create a screen map. */
  975.     CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
  976.     TMAP = HMAP + (sp->t_rows - 1);
  977.     HMAP->lno = sp->lno;
  978.     HMAP->coff = 0;
  979.     HMAP->soff = 1;
  980.  
  981.     /*
  982.      * Fill the screen map from scratch -- try and center the line.  That
  983.      * way if we're starting with a file we've seen before, we'll put the
  984.      * line in the middle, otherwise, it won't work and we'll end up with
  985.      * the line at the top.
  986.      */
  987.     F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER);
  988.  
  989.     /* Invalidate the cursor. */
  990.     F_SET(vip, VIP_CUR_INVALID);
  991.  
  992.     /* Paint the screen image from scratch. */
  993.     F_SET(vip, VIP_N_EX_PAINT);
  994.  
  995.     return (0);
  996. }
  997.  
  998. /*
  999.  * v_dtoh --
  1000.  *    Move all but the current screen to the hidden queue.
  1001.  */
  1002. static void
  1003. v_dtoh(sp)
  1004.     SCR *sp;
  1005. {
  1006.     GS *gp;
  1007.     SCR *tsp;
  1008.     int hidden;
  1009.  
  1010.     /* Move all screens to the hidden queue, tossing screen maps. */
  1011.     for (hidden = 0, gp = sp->gp;
  1012.         (tsp = gp->dq.cqh_first) != (void *)&gp->dq; ++hidden) {
  1013.         if (_HMAP(tsp) != NULL) {
  1014.             free(_HMAP(tsp));
  1015.             _HMAP(tsp) = NULL;
  1016.         }
  1017.         CIRCLEQ_REMOVE(&gp->dq, tsp, q);
  1018.         CIRCLEQ_INSERT_TAIL(&gp->hq, tsp, q);
  1019.     }
  1020.  
  1021.     /* Move current screen back to the display queue. */
  1022.     CIRCLEQ_REMOVE(&gp->hq, sp, q);
  1023.     CIRCLEQ_INSERT_TAIL(&gp->dq, sp, q);
  1024.  
  1025.     /*
  1026.      * XXX
  1027.      * Don't bother internationalizing this message, it's going to
  1028.      * go away as soon as we have one-line screens.  --TK
  1029.      */
  1030.     if (hidden > 1)
  1031.         msgq(sp, M_INFO,
  1032.             "%d screens backgrounded; use :display to list them",
  1033.             hidden - 1);
  1034. }
  1035.  
  1036. /*
  1037.  * v_keyword --
  1038.  *    Get the word (or non-word) the cursor is on.
  1039.  */
  1040. static int
  1041. v_keyword(sp)
  1042.     SCR *sp;
  1043. {
  1044.     VI_PRIVATE *vip;
  1045.     size_t beg, end, len;
  1046.     int moved, state;
  1047.     char *p;
  1048.  
  1049.     if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
  1050.         return (1);
  1051.  
  1052.     /*
  1053.      * !!!
  1054.      * Historically, tag commands skipped over any leading whitespace
  1055.      * characters.  Make this true in general when using cursor words.
  1056.      * If movement, getting a cursor word implies moving the cursor to
  1057.      * its beginning.  Refresh now.
  1058.      *
  1059.      * !!!
  1060.      * Find the beginning/end of the keyword.  Keywords are currently
  1061.      * used for cursor-word searching and for tags.  Historical vi
  1062.      * only used the word in a tag search from the cursor to the end
  1063.      * of the word, i.e. if the cursor was on the 'b' in " abc ", the
  1064.      * tag was "bc".  For consistency, we make cursor word searches
  1065.      * follow the same rule.
  1066.      */
  1067.     for (moved = 0,
  1068.         beg = sp->cno; beg < len && isspace(p[beg]); moved = 1, ++beg);
  1069.     if (beg >= len) {
  1070.         msgq(sp, M_BERR, "212|Cursor not in a word");
  1071.         return (1);
  1072.     }
  1073.     if (moved) {
  1074.         sp->cno = beg;
  1075.         (void)vs_refresh(sp, 0);
  1076.     }
  1077.  
  1078.     /* Find the end of the word. */
  1079.     for (state = inword(p[beg]),
  1080.         end = beg; ++end < len && state == inword(p[end]););
  1081.  
  1082.     vip = VIP(sp);
  1083.     len = (end - beg);
  1084.     BINC_RET(sp, vip->keyw, vip->klen, len);
  1085.     memmove(vip->keyw, p + beg, len);
  1086.     vip->keyw[len] = '\0';                /* XXX */
  1087.     return (0);
  1088. }
  1089.  
  1090. /*
  1091.  * v_alias --
  1092.  *    Check for a command alias.
  1093.  */
  1094. static VIKEYS const *
  1095. v_alias(sp, vp, kp)
  1096.     SCR *sp;
  1097.     VICMD *vp;
  1098.     VIKEYS const *kp;
  1099. {
  1100.     CHAR_T push;
  1101.  
  1102.     switch (vp->key) {
  1103.     case 'C':            /* C -> c$ */
  1104.         push = '$';
  1105.         vp->key = 'c';
  1106.         break;
  1107.     case 'D':            /* D -> d$ */
  1108.         push = '$';
  1109.         vp->key = 'd';
  1110.         break;
  1111.     case 'S':            /* S -> c_ */
  1112.         push = '_';
  1113.         vp->key = 'c';
  1114.         break;
  1115.     case 'Y':            /* Y -> y_ */
  1116.         push = '_';
  1117.         vp->key = 'y';
  1118.         break;
  1119.     default:
  1120.         return (kp);
  1121.     }
  1122.     return (v_event_push(sp,
  1123.         NULL, &push, 1, CH_NOMAP | CH_QUOTED) ? NULL : &vikeys[vp->key]);
  1124. }
  1125.  
  1126. /*
  1127.  * v_count --
  1128.  *    Return the next count.
  1129.  */
  1130. static int
  1131. v_count(sp, fkey, countp)
  1132.     SCR *sp;
  1133.     ARG_CHAR_T fkey;
  1134.     u_long *countp;
  1135. {
  1136.     EVENT ev;
  1137.     u_long count, tc;
  1138.  
  1139.     ev.e_c = fkey;
  1140.     count = tc = 0;
  1141.     do {
  1142.         /*
  1143.          * XXX
  1144.          * Assume that overflow results in a smaller number.
  1145.          */
  1146.         tc = count * 10 + ev.e_c - '0';
  1147.         if (count > tc) {
  1148.             /* Toss to the next non-digit. */
  1149.             do {
  1150.                 if (v_key(sp, 0, &ev,
  1151.                     EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
  1152.                     return (1);
  1153.             } while (isdigit(ev.e_c));
  1154.             msgq(sp, M_ERR,
  1155.                 "235|Number larger than %lu", ULONG_MAX);
  1156.             return (1);
  1157.         }
  1158.         count = tc;
  1159.         if (v_key(sp, 0, &ev, EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
  1160.             return (1);
  1161.     } while (isdigit(ev.e_c));
  1162.     *countp = count;
  1163.     return (0);
  1164. }
  1165.  
  1166. /*
  1167.  * v_key --
  1168.  *    Return the next event.
  1169.  */
  1170. static gcret_t
  1171. v_key(sp, command_events, evp, ec_flags)
  1172.     SCR *sp;
  1173.     int command_events;
  1174.     EVENT *evp;
  1175.     u_int32_t ec_flags;
  1176. {
  1177.     u_int32_t quote;
  1178.  
  1179.     for (quote = 0;;) {
  1180.         if (v_event_get(sp, evp, 0, ec_flags | quote))
  1181.             return (GC_FATAL);
  1182.         quote = 0;
  1183.  
  1184.         switch (evp->e_event) {
  1185.         case E_CHARACTER:
  1186.             /*
  1187.              * !!!
  1188.              * Historically, ^V was ignored in the command stream,
  1189.              * although it had a useful side-effect of interrupting
  1190.              * mappings.  Adding a quoting bit to the call probably
  1191.              * extends historic practice, but it feels right.
  1192.              */
  1193.             if (evp->e_value == K_VLNEXT) {
  1194.                 quote = EC_QUOTED;
  1195.                 break;
  1196.             }
  1197.             return (GC_OK);
  1198.         case E_ERR:
  1199.         case E_EOF:
  1200.             return (GC_FATAL);
  1201.         case E_INTERRUPT:
  1202.             /*
  1203.              * !!!
  1204.              * Historically, vi beeped on command level interrupts.
  1205.              *
  1206.              * Historically, vi exited to ex mode if no file was
  1207.              * named on the command line, and two interrupts were
  1208.              * generated in a row.  (Just figured you might want
  1209.              * to know that.)
  1210.              */
  1211.             (void)sp->gp->scr_bell(sp);
  1212.             return (GC_INTERRUPT);
  1213.         case E_REPAINT:
  1214.             if (vs_repaint(sp, evp))
  1215.                 return (GC_FATAL);
  1216.             break;
  1217.         case E_WRESIZE:
  1218.             return (GC_ERR);
  1219.         case E_QUIT:
  1220.         case E_WRITE:
  1221.             if (command_events)
  1222.                 return (GC_EVENT);
  1223.             /* FALLTHROUGH */
  1224.         default:
  1225.             v_event_err(sp, evp);
  1226.             return (GC_ERR);
  1227.         }
  1228.     }
  1229.     /* NOTREACHED */
  1230. }
  1231.  
  1232. #if defined(DEBUG) && defined(COMLOG)
  1233. /*
  1234.  * v_comlog --
  1235.  *    Log the contents of the command structure.
  1236.  */
  1237. static void
  1238. v_comlog(sp, vp)
  1239.     SCR *sp;
  1240.     VICMD *vp;
  1241. {
  1242.     TRACE(sp, "vcmd: %c", vp->key);
  1243.     if (F_ISSET(vp, VC_BUFFER))
  1244.         TRACE(sp, " buffer: %c", vp->buffer);
  1245.     if (F_ISSET(vp, VC_C1SET))
  1246.         TRACE(sp, " c1: %lu", vp->count);
  1247.     if (F_ISSET(vp, VC_C2SET))
  1248.         TRACE(sp, " c2: %lu", vp->count2);
  1249.     TRACE(sp, " flags: 0x%x\n", vp->flags);
  1250. }
  1251. #endif
  1252.