home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 365_03 / vi.c < prev    next >
C/C++ Source or Header  |  1992-04-06  |  22KB  |  816 lines

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