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