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