home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1710 / vi.c < prev    next >
C/C++ Source or Header  |  1990-12-28  |  20KB  |  714 lines

  1. /* vi.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    16820 SW Tallac Way
  6.  *    Beaverton, OR 97006
  7.  *    kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
  8.  */
  9.  
  10.  
  11. #include "config.h"
  12. #include <ctype.h>
  13. #include "vi.h"
  14.  
  15.  
  16.  
  17. /* This array describes what each key does */
  18. #define NO_FUNC        (MARK (*)())0
  19. #define NO_ARGS        0
  20. #define CURSOR_COUNT    1
  21. #define CURSOR        2
  22. #define CURSOR_CNT_KEY    3
  23. #define CURSOR_MOVED    4
  24. #define CURSOR_EOL    5
  25. #define ZERO        6
  26. #define DIGIT        7
  27. #define CURSOR_TEXT    8
  28. #define CURSOR_CNT_CMD    9
  29. #define KEYWORD        10
  30. #define NO_FLAGS    0x00
  31. #define    MVMT        0x01    /* this is a movement command */
  32. #define PTMV        0x02    /* this can be *part* of a movement command */
  33. #define FRNT        0x04    /* after move, go to front of line */
  34. #define INCL        0x08    /* include last char when used with c/d/y */
  35. #define LNMD        0x10    /* use line mode of c/d/y */
  36. #define NCOL        0x20    /* this command can't change the column# */
  37. #define NREL        0x40    /* this is "non-relative" -- set the '' mark */
  38. #define SDOT        0x80    /* set the "dot" variables, for the "." cmd */
  39. static struct keystru
  40. {
  41.     MARK    (*func)();    /* the function to run */
  42.     char    args;        /* description of the args needed */
  43.     char    flags;        /* other stuff */
  44. }
  45.     vikeys[] =
  46. {
  47. /* NUL not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  48. /* ^A  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  49. /* ^B  page backward    */    {m_scroll,    CURSOR_CNT_CMD,    FRNT},
  50. /* ^C  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  51. /* ^D  scroll dn 1/2page*/    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  52. /* ^E  scroll up    */    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  53. /* ^F  page forward    */    {m_scroll,    CURSOR_CNT_CMD,    FRNT},
  54. /* ^G  show file status    */    {v_status,    NO_ARGS,     NO_FLAGS},
  55. /* ^H  move left, like h*/    {m_left,    CURSOR_COUNT,    MVMT},
  56. /* ^I  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  57. /* ^J  move down    */    {m_down,    CURSOR_COUNT,    MVMT|LNMD},
  58. /* ^K  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  59. /* ^L  redraw screen    */    {v_redraw,    NO_ARGS,    NO_FLAGS},
  60. /* ^M  mv front next ln */    {m_down,    CURSOR_COUNT,    MVMT|FRNT|LNMD},
  61. /* ^N  move down    */    {m_down,    CURSOR_COUNT,    MVMT|LNMD},
  62. /* ^O  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  63. /* ^P  not defined    */    {m_up,        CURSOR_COUNT,    MVMT|LNMD},
  64. /* ^Q  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  65. /* ^R  redraw screen    */    {v_redraw,    NO_ARGS,    NO_FLAGS},
  66. /* ^S  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  67. /* ^T  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  68. /* ^U  scroll up 1/2page*/    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  69. /* ^V  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  70. /* ^W  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  71. /* ^X  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  72. /* ^Y  scroll down    */    {m_scroll,    CURSOR_CNT_CMD,    NCOL},
  73. /* ^Z  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  74. /* ESC not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  75. /* ^\  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  76. /* ^]  keyword is tag    */    {v_tag,        KEYWORD,    NO_FLAGS},
  77. /* ^^  previous file    */    {v_switch,    CURSOR,        NO_FLAGS},
  78. /* ^_  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  79. /* SPC move right,like l*/    {m_right,    CURSOR_COUNT,    MVMT},
  80. /*  !  run thru filter    */    {v_filter,    CURSOR_MOVED,    NO_FLAGS},
  81. /*  "  select cut buffer*/    {v_selcut,    CURSOR_CNT_KEY,    PTMV},
  82. #ifndef NO_EXTENSIONS
  83. /*  #  increment number    */    {v_increment,    KEYWORD,    SDOT},
  84. #else
  85. /*  #  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  86. #endif
  87. /*  $  move to rear    */    {m_rear,    CURSOR,        MVMT|INCL},
  88. /*  %  move to match    */    {m_match,    CURSOR,        MVMT|INCL},
  89. /*  &  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  90. /*  '  move to a mark    */    {m_tomark,    CURSOR_CNT_KEY,    MVMT|FRNT|NREL|LNMD},
  91. #ifndef NO_SENTENCE
  92. /*  (  mv back sentence    */    {m_bsentence,    CURSOR_COUNT,    MVMT},
  93. /*  )  mv fwd sentence    */    {m_fsentence,    CURSOR_COUNT,    MVMT},
  94. #else
  95. /*  (  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  96. /*  )  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  97. #endif
  98. /*  *  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  99. /*  +  mv front next ln */    {m_down,    CURSOR_COUNT,    MVMT|FRNT|LNMD},
  100. #ifndef NO_CHARSEARCH
  101. /*  ,  reverse [fFtT] cmd*/    {m__ch,        CURSOR_CNT_CMD,    MVMT|INCL},
  102. #else
  103. /*  ,  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  104. #endif
  105. /*  -  mv front prev ln    */    {m_up,        CURSOR_COUNT,    MVMT|FRNT|LNMD},
  106. /*  .  special...    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  107. /*  /  forward search    */    {m_fsrch,    CURSOR_TEXT,    MVMT|NREL},
  108. /*  0  part of count?    */    {NO_FUNC,    ZERO,        MVMT|PTMV},
  109. /*  1  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  110. /*  2  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  111. /*  3  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  112. /*  4  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  113. /*  5  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  114. /*  6  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  115. /*  7  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  116. /*  8  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  117. /*  9  part of count    */    {NO_FUNC,    DIGIT,        PTMV},
  118. /*  :  run single EX cmd*/    {v_1ex,        CURSOR_TEXT,    NO_FLAGS},
  119. #ifndef NO_CHARSEARCH
  120. /*  ;  repeat [fFtT] cmd*/    {m__ch,        CURSOR_CNT_CMD,    MVMT|INCL},
  121. #else
  122. /*  ;  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  123. #endif
  124. /*  <  shift text left    */    {v_lshift,    CURSOR_MOVED,    SDOT|FRNT},
  125. /*  =  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  126. /*  >  shift text right    */    {v_rshift,    CURSOR_MOVED,    SDOT|FRNT},
  127. /*  ?  backward search    */    {m_bsrch,    CURSOR_TEXT,    MVMT|NREL},
  128. /*  @  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  129. /*  A  append at EOL    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  130. /*  B  move back Word    */    {m_bWord,    CURSOR_COUNT,    MVMT},
  131. /*  C  change to EOL    */    {v_change,    CURSOR_EOL,    SDOT},
  132. /*  D  delete to EOL    */    {v_delete,    CURSOR_EOL,    SDOT},
  133. /*  E  move end of Word    */    {m_eWord,    CURSOR_COUNT,    MVMT|INCL},
  134. #ifndef NO_CHARSEARCH
  135. /*  F  move bk to char    */    {m_Fch,        CURSOR_CNT_KEY,    MVMT|INCL},
  136. #else
  137. /*  F  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  138. #endif
  139. /*  G  move to line #    */    {m_toline,    CURSOR_COUNT,    MVMT|NREL|LNMD},
  140. /*  H  move to row    */    {m_row,        CURSOR_CNT_CMD,    MVMT|FRNT},
  141. /*  I  insert at front    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  142. /*  J  join lines    */    {v_join,    CURSOR_COUNT,    SDOT},
  143. #ifndef NO_EXTENSIONS
  144. /*  K  look up keyword    */    {v_keyword,    KEYWORD,    NO_FLAGS},
  145. #else
  146. /*  K  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  147. #endif
  148. /*  L  move to last row    */    {m_row,        CURSOR_CNT_CMD,    MVMT|FRNT},
  149. /*  M  move to mid row    */    {m_row,        CURSOR_CNT_CMD,    MVMT|FRNT},
  150. /*  N  reverse prev srch*/    {m_Nsrch,    CURSOR,        MVMT},
  151. /*  O  insert above line*/    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  152. /*  P  paste before    */    {v_paste,    CURSOR_CNT_CMD,    NO_FLAGS},
  153. /*  Q  quit to EX mode    */    {v_quit,    NO_ARGS,    NO_FLAGS},
  154. /*  R  overtype        */    {v_overtype,    CURSOR,        SDOT},
  155. /*  S  change line    */    {v_change,    CURSOR_MOVED,    SDOT},
  156. #ifndef NO_CHARSEARCH
  157. /*  T  move bk to char    */    {m_Tch,        CURSOR_CNT_KEY,    MVMT|INCL},
  158. #else
  159. /*  T  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  160. #endif
  161. /*  U  undo whole line    */    {v_undoline,    CURSOR,        FRNT},
  162. /*  V  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  163. /*  W  move forward Word*/    {m_fWord,    CURSOR_COUNT,    MVMT},
  164. /*  X  delete to left    */    {v_Xchar,    CURSOR_COUNT,    SDOT},
  165. /*  Y  yank text    */    {v_yank,    CURSOR_MOVED,    NO_FLAGS},
  166. /*  Z  save file & exit    */    {v_xit,        CURSOR_CNT_KEY,    NO_FLAGS},
  167. /*  [  move back section*/    {m_bsection,    CURSOR_CNT_KEY,    MVMT|LNMD|NREL},
  168. /*  \  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  169. /*  ]  move fwd section */    {m_fsection,    CURSOR_CNT_KEY,    MVMT|LNMD|NREL},
  170. /*  ^  move to front    */    {m_front,    CURSOR,        MVMT},
  171. /*  _  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  172. /*  `  move to mark    */    {m_tomark,    CURSOR_CNT_KEY,    MVMT|NREL},
  173. /*  a  append at cursor    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  174. /*  b  move back word    */    {m_bword,    CURSOR_COUNT,    MVMT},
  175. /*  c  change text    */    {v_change,    CURSOR_MOVED,    SDOT},
  176. /*  d  delete op    */    {v_delete,    CURSOR_MOVED,    SDOT},
  177. /*  e  move end word    */    {m_eword,    CURSOR_COUNT,    MVMT|INCL},
  178. #ifndef NO_CHARSEARCH
  179. /*  f  move fwd for char*/    {m_fch,        CURSOR_CNT_KEY,    MVMT|INCL},
  180. #else
  181. /*  f  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  182. #endif
  183. /*  g  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  184. /*  h  move left    */    {m_left,    CURSOR_COUNT,    MVMT},
  185. /*  i  insert at cursor    */    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  186. /*  j  move down    */    {m_down,    CURSOR_COUNT,    MVMT|NCOL|LNMD},
  187. /*  k  move up        */    {m_up,        CURSOR_COUNT,    MVMT|NCOL|LNMD},
  188. /*  l  move right    */    {m_right,    CURSOR_COUNT,    MVMT},
  189. /*  m  define a mark    */    {v_mark,    CURSOR_CNT_KEY,    NO_FLAGS},
  190. /*  n  repeat prev srch    */    {m_nsrch,    CURSOR,     MVMT},
  191. /*  o  insert below line*/    {v_insert,    CURSOR_CNT_CMD,    SDOT},
  192. /*  p  paste after    */    {v_paste,    CURSOR_CNT_CMD,    NO_FLAGS},
  193. /*  q  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  194. /*  r  replace chars    */    {v_replace,    CURSOR_CNT_KEY,    SDOT},
  195. /*  s  subst N chars    */    {v_subst,    CURSOR_COUNT,    SDOT},
  196. #ifndef NO_CHARSEARCH
  197. /*  t  move fwd to char    */    {m_tch,        CURSOR_CNT_KEY,    MVMT|INCL},
  198. #else
  199. /*  t  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  200. #endif
  201. /*  u  undo        */    {v_undo,    CURSOR,        NO_FLAGS},
  202. /*  v  not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  203. /*  w  move fwd word    */    {m_fword,    CURSOR_COUNT,    MVMT},
  204. /*  x  delete character    */    {v_xchar,    CURSOR_COUNT,    SDOT},
  205. /*  y  yank text    */    {v_yank,    CURSOR_MOVED,    NO_FLAGS},
  206. /*  z  adjust scrn row    */    {m_z,         CURSOR_CNT_KEY,    NCOL},
  207. /*  {  back paragraph    */    {m_bparagraph,    CURSOR_COUNT,    MVMT|LNMD},
  208. /*  |  move to column    */    {m_tocol,    CURSOR_COUNT,    NREL},
  209. /*  }  fwd paragraph    */    {m_fparagraph,    CURSOR_COUNT,    MVMT|LNMD},
  210. /*  ~  upper/lowercase    */    {v_ulcase,    CURSOR,        SDOT},
  211. /* DEL not defined    */    {NO_FUNC,    NO_ARGS,    NO_FLAGS},
  212. };
  213.  
  214.  
  215.  
  216. vi()
  217. {
  218.     register int        key;    /* keystroke from user */
  219.     long            count;    /* numeric argument to some functions */
  220.     register struct keystru    *keyptr;/* pointer to vikeys[] element */
  221.     MARK            tcurs;    /* temporary cursor */
  222.     int            prevkey;/* previous key, if d/c/y/</>/! */
  223.     MARK            range;    /* start of range for d/c/y/</>/! */
  224.     char            text[100];
  225.     int            dotkey;    /* last "key" of a change */
  226.     int            dotpkey;/* last "prevkey" of a change */
  227.     int            dotkey2;/* last extra "getkey()" of a change */
  228.     int            dotcnt;    /* last "count" of a change */
  229.     int            firstkey;
  230.     register int        i;
  231.  
  232.     /* tell the redraw() function to start from scratch */
  233.     redraw(MARK_UNSET, FALSE);
  234.     msg((char *)0);
  235.  
  236. #ifdef lint
  237.     /* lint says that "range" might be used before it is set.  This
  238.      * can't really happen due to the way "range" and "prevkey" are used,
  239.      * but lint doesn't know that.  This line is here ONLY to keep lint
  240.      * happy.
  241.      */
  242.     range = 0L;
  243. #endif
  244.  
  245.     /* safeguard against '.' with no previous command */
  246.     dotkey = 0;
  247.  
  248. #ifndef NO_EXTENSIONS
  249.     /* go immediately into insert mode, if ":set inputmode" */
  250.     firstkey = 0;
  251.     if (*o_inputmode)
  252.     {
  253.         firstkey = 'i';
  254.     }
  255. #endif
  256.  
  257.     /* Repeatedly handle VI commands */
  258.     for (count = 0, prevkey = '\0'; mode == MODE_VI; )
  259.     {
  260.         /* if we've moved off the undoable line, then we can't undo it at all */
  261.         if (markline(cursor) != U_line)
  262.         {
  263.             U_line = 0L;
  264.         }
  265.  
  266.         /* report any changes from the previous command */
  267.         if (rptlines >= *o_report)
  268.         {
  269.             redraw(cursor, FALSE);
  270.             msg("%ld lines %s", rptlines, rptlabel);
  271.         }
  272.         rptlines = 0L;
  273.  
  274.         /* get the next command key.  It must be ASCII */
  275.         if (firstkey)
  276.         {
  277.             key = firstkey;
  278.             firstkey = 0;
  279.         }
  280.         else
  281.         {
  282.             do
  283.             {
  284.                 key = getkey(WHEN_VICMD);
  285.             } while (key < 0 || key > 127);
  286.         }
  287.  
  288.         /* change cw and cW commands to ce and cE, respectively */
  289.         /* (Why?  because the real vi does it that way!) */
  290.         if (prevkey == 'c')
  291.         {
  292.             if (key == 'w')
  293.                 key = 'e';
  294.             else if (key == 'W')
  295.                 key = 'E';
  296.  
  297.             /* wouldn't work right at the end of a word unless we
  298.              * backspace one character before doing the move.  This
  299.              * will fix most cases.
  300.              */
  301.             if (markidx(cursor) > 0 && (key == 'e' || key == 'E'))
  302.             {
  303.                 cursor--;
  304.             }
  305.         }
  306.  
  307.         /* look up the structure describing this command */
  308.         keyptr = &vikeys[key];
  309.  
  310.         /* if we're in the middle of a d/c/y/</>/! command, reject
  311.          * anything but movement or a doubled version like "dd".
  312.          */
  313.         if (prevkey && key != prevkey && !(keyptr->flags & (MVMT|PTMV)))
  314.         {
  315.             beep();
  316.             prevkey = 0;
  317.             count = 0;
  318.             continue;
  319.         }
  320.  
  321.         /* set the "dot" variables, if we're supposed to */
  322.         if ((keyptr->flags & SDOT)
  323.          || (prevkey && vikeys[prevkey].flags & SDOT))
  324.         {
  325.             dotkey = key;
  326.             dotpkey = prevkey;
  327.             dotkey2 = '\0';
  328.             dotcnt = count;
  329.  
  330.             /* remember the line before any changes are made */
  331.             if (U_line != markline(cursor))
  332.             {
  333.                 U_line = markline(cursor);
  334.                 strcpy(U_text, fetchline(U_line));
  335.             }
  336.         }
  337.  
  338.         /* if this is "." then set other vars from the "dot" vars */
  339.         if (key == '.')
  340.         {
  341.             key = dotkey;
  342.             keyptr = &vikeys[key];
  343.             prevkey = dotpkey;
  344.             if (prevkey)
  345.             {
  346.                 range = cursor;
  347.             }
  348.             if (count == 0)
  349.             {
  350.                 count = dotcnt;
  351.             }
  352.             doingdot = TRUE;
  353.  
  354.             /* remember the line before any changes are made */
  355.             if (U_line != markline(cursor))
  356.             {
  357.                 U_line = markline(cursor);
  358.                 strcpy(U_text, fetchline(U_line));
  359.             }
  360.         }
  361.         else
  362.         {
  363.             doingdot = FALSE;
  364.         }
  365.  
  366.         /* process the key as a command */
  367.         tcurs = cursor;
  368.         switch (keyptr->args)
  369.         {
  370.           case ZERO:
  371.             if (count == 0)
  372.             {
  373.                 tcurs = cursor & ~(BLKSIZE - 1);
  374.                 break;
  375.             }
  376.             /* else fall through & treat like other digits... */
  377.  
  378.           case DIGIT:
  379.             count = count * 10 + key - '0';
  380.             break;
  381.  
  382.           case KEYWORD:
  383.             /* if not on a keyword, fail */
  384.             pfetch(markline(cursor));
  385.             key = markidx(cursor);
  386.             if (isascii(ptext[key])
  387.                 && !isalnum(ptext[key]) && ptext[key] != '_')
  388.             {
  389.                 tcurs = MARK_UNSET;
  390.                 break;
  391.             }
  392.  
  393.             /* find the start of the keyword */
  394.             while (key > 0 && (!isascii(ptext[key-1]) ||
  395.             isalnum(ptext[key - 1]) || ptext[key - 1] == '_'))
  396.             {
  397.                 key--;
  398.             }
  399.             tcurs = (cursor & ~(BLKSIZE - 1)) + key;
  400.  
  401.             /* copy it into a buffer, and NUL-terminate it */
  402.             i = 0;
  403.             do
  404.             {
  405.                 text[i++] = ptext[key++];
  406.             } while (!isascii(ptext[key]) || isalnum(ptext[key]) || ptext[key] == '_');
  407.             text[i] = '\0';
  408.  
  409.             /* call the function */
  410.             tcurs = (*keyptr->func)(text, tcurs, count);
  411.             count = 0L;
  412.             break;
  413.  
  414.           case NO_ARGS:
  415.             if (keyptr->func)
  416.             {
  417.                 (*keyptr->func)();
  418.             }
  419.             else
  420.             {
  421.                 beep();
  422.             }
  423.             count = 0L;
  424.             break;
  425.     
  426.           case CURSOR_COUNT:
  427.             tcurs = (*keyptr->func)(cursor, count);
  428.             count = 0L;
  429.             break;
  430.     
  431.           case CURSOR:
  432.             tcurs = (*keyptr->func)(cursor);
  433.             count = 0L;
  434.             break;
  435.  
  436.           case CURSOR_CNT_KEY:
  437.             if (doingdot)
  438.             {
  439.                 tcurs = (*keyptr->func)(cursor, count, dotkey2);
  440.             }
  441.             else if (keyptr->flags & SDOT
  442.              || (prevkey && vikeys[prevkey].flags & SDOT))
  443.             {
  444.                 dotkey2 = getkey(0);
  445.                 tcurs = (*keyptr->func)(cursor, count, dotkey2);
  446.             }
  447.             else
  448.             {
  449.                 tcurs = (*keyptr->func)(cursor, count, getkey(0));
  450.             }
  451.             count = 0L;
  452.             break;
  453.     
  454.           case CURSOR_MOVED:
  455.             /* uppercase keys always act like doubled */
  456.             if (isascii(key) && isupper(key))
  457.             {
  458.                 prevkey = key;
  459.                 range = cursor;
  460.             }
  461.  
  462.             if (prevkey)
  463.             {
  464.                 /* doubling up a command, use complete lines */
  465.                 range &= ~(BLKSIZE - 1);
  466.                 if (count)
  467.                 {
  468.                     tcurs = range + MARK_AT_LINE(count);
  469.                     count = 0;
  470.                 }
  471.                 else
  472.                 {
  473.                     tcurs = range + BLKSIZE;
  474.                 }
  475.             }
  476.             else
  477.             {
  478.                 prevkey = key;
  479.                 range = cursor;
  480.                 key = -1; /* so we don't think we doubled yet */
  481.             }
  482.             break;
  483.  
  484.           case CURSOR_EOL:
  485.             prevkey = key;
  486.             /* a zero-length line needs special treatment */
  487.             pfetch(markline(cursor));
  488.             if (plen == 0)
  489.             {
  490.                 /* act on a zero-length section of text */
  491.                 range = tcurs = cursor;
  492.                 key = ' ';
  493.             }
  494.             else
  495.             {
  496.                 /* act like CURSOR_MOVED with '$' movement */
  497.                 range = cursor;
  498.                 tcurs = m_rear(cursor, 1L);
  499.                 key = '$';
  500.             }
  501.             count = 0L;
  502.             keyptr = &vikeys[key];
  503.             break;
  504.  
  505.           case CURSOR_TEXT:
  506.               do
  507.               {    
  508.                 if (vgets(key, text, sizeof text) >= 0)
  509.                 {
  510.                     /* reassure user that <CR> was hit */
  511.                     qaddch('\r');
  512.                     refresh();
  513.  
  514.                     /* call the function with the text */
  515.                     tcurs = (*keyptr->func)(cursor, text);
  516.                 }
  517.                 else
  518.                     mode = MODE_VI;
  519.             } while (mode == MODE_COLON);
  520.             count = 0L;
  521.             break;
  522.  
  523.           case CURSOR_CNT_CMD:
  524.             tcurs = (*keyptr->func)(cursor, count, key);
  525.             count = 0L;
  526.             break;
  527.         }
  528.  
  529.         /* if that command took us out of vi mode, then exit the loop
  530.          * NOW, without tweaking the cursor or anything.  This is very
  531.          * important when mode == MODE_QUIT.
  532.          */
  533.         if (mode != MODE_VI)
  534.         {
  535.             break;
  536.         }
  537.  
  538.         /* now move the cursor, as appropriate */
  539.         if (prevkey && markline(tcurs) > nlines)
  540.         {
  541.             /* destination for operator may be nlines + 1 */
  542.             cursor = MARK_AT_LINE(nlines + 1);
  543.         }
  544.         else if (keyptr->args == CURSOR_MOVED)
  545.         {
  546.             /* the < and > keys have FRNT,
  547.              * but it shouldn't be applied yet
  548.              */
  549.             cursor = adjmove(cursor, tcurs, 0);
  550.         }
  551.         else
  552.         {
  553.             cursor = adjmove(cursor, tcurs, keyptr->flags);
  554.         }
  555.  
  556.         /* was that the end of a d/c/y/</>/! command? */
  557.         if (prevkey && (prevkey == key || (keyptr->flags & MVMT)))
  558.         {
  559.             /* if the movement command failed, cancel operation */
  560.             if (tcurs == MARK_UNSET)
  561.             {
  562.                 prevkey = 0;
  563.                 count = 0;
  564.                 continue;
  565.             }
  566.  
  567.             /* make sure range=front and tcurs=rear */
  568.             if (cursor < range)
  569.             {
  570.                 tcurs = range;
  571.                 range = cursor;
  572.             }
  573.             else
  574.             {
  575.                 tcurs = cursor;
  576.             }
  577.  
  578.             /* adjust for line mode */
  579.             if (keyptr->flags & LNMD)
  580.             {
  581.                 range &= ~(BLKSIZE - 1);
  582.                 tcurs &= ~(BLKSIZE - 1);
  583.                 tcurs += BLKSIZE;
  584.             }
  585.  
  586.             /* adjust for inclusion of last char */
  587.             if (keyptr->flags & INCL)
  588.             {
  589.                 tcurs++;
  590.             }
  591.  
  592.             /* temporarily move the cursor to "range" so that
  593.              * beforedo() remembers the cursor's real location.
  594.              * This is important if the user later does undo()
  595.              */
  596.             cursor = range;
  597.  
  598.             /* run the function */
  599.             tcurs = (*vikeys[prevkey].func)(range, tcurs);
  600.             cursor = adjmove(cursor, tcurs, vikeys[prevkey].flags);
  601.  
  602.             /* cleanup */
  603.             prevkey = 0;
  604.         }
  605.     }
  606. }
  607.  
  608. /* This function adjusts the MARK value that they return; here we make sure
  609.  * it isn't past the end of the line, and that the column hasn't been
  610.  * *accidentally* changed.
  611.  */
  612. MARK adjmove(old, new, flags)
  613.     MARK        old;    /* the cursor position before the command */
  614.     register MARK    new;    /* the cursor position after the command */
  615.     int        flags;    /* various flags regarding cursor mvmt */
  616. {
  617.     static int    colno;    /* the column number that we want */
  618.     register char    *text;    /* used to scan through the line's text */
  619.     register int    i;
  620.  
  621.     /* if the command failed, bag it! */
  622.     if (new == MARK_UNSET)
  623.     {
  624.         beep();
  625.         return old;
  626.     }
  627.  
  628.     /* if this is a non-relative movement, set the '' mark */
  629.     if (flags & NREL)
  630.     {
  631.         mark[26] = old;
  632.     }
  633.  
  634.     /* make sure it isn't past the end of the file */
  635.     if (markline(new) < 1)
  636.     {
  637.         new = MARK_FIRST;
  638.     }
  639.     else if (markline(new) > nlines)
  640.     {
  641.         new = MARK_LAST;
  642.     }
  643.  
  644.     /* fetch the new line */
  645.     pfetch(markline(new));
  646.  
  647.     /* move to the front, if we're supposed to */
  648.     if (flags & FRNT)
  649.     {
  650.         new = m_front(new, 1L);
  651.     }
  652.  
  653.     /* change the column#, or change the mark to suit the column# */
  654.     if (!(flags & NCOL))
  655.     {
  656.         /* change the column# */
  657.         i = markidx(new);
  658.         if (i == BLKSIZE - 1)
  659.         {
  660.             new &= ~(BLKSIZE - 1);
  661.             if (plen > 0)
  662.             {
  663.                 new += plen - 1;
  664.             }
  665.             colno = BLKSIZE * 8; /* one heck of a big colno */
  666.         }
  667.         else if (plen > 0)
  668.         {
  669.             if (i >= plen)
  670.             {
  671.                 new = (new & ~(BLKSIZE - 1)) + plen - 1;
  672.             }
  673.             colno = idx2col(new, ptext, FALSE);
  674.         }
  675.         else
  676.         {
  677.             new &= ~(BLKSIZE - 1);
  678.             colno = 0;
  679.         }
  680.     }
  681.     else
  682.     {
  683.         /* adjust the mark to get as close as possible to column# */
  684.         for (i = 0, text = ptext; i <= colno && *text; text++)
  685.         {
  686.             if (*text == '\t' && !*o_list)
  687.             {
  688.                 i += *o_tabstop - (i % *o_tabstop);
  689.             }
  690.             else if (UCHAR(*text) < ' ' || *text == 127)
  691.             {
  692.                 i += 2;
  693.             }
  694. #ifndef NO_CHARATTR
  695.             else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2])
  696.             {
  697.                 text += 2; /* plus one more in "for()" stmt */
  698.             }
  699. #endif
  700.             else
  701.             {
  702.                 i++;
  703.             }
  704.         }
  705.         if (text > ptext)
  706.         {
  707.             text--;
  708.         }
  709.         new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext);
  710.     }
  711.  
  712.     return new;
  713. }
  714.