home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / v_word.c < prev    next >
C/C++ Source or Header  |  1996-04-27  |  13KB  |  548 lines

  1. /*-
  2.  * Copyright (c) 1992, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)v_word.c    10.5 (Berkeley) 3/6/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/time.h>
  19.  
  20. #include <bitstring.h>
  21. #include <ctype.h>
  22. #include <limits.h>
  23. #include <stdio.h>
  24.  
  25. #include "../common/common.h"
  26. #include "vi.h"
  27.  
  28. /*
  29.  * There are two types of "words".  Bigwords are easy -- groups of anything
  30.  * delimited by whitespace.  Normal words are trickier.  They are either a
  31.  * group of characters, numbers and underscores, or a group of anything but,
  32.  * delimited by whitespace.  When for a word, if you're in whitespace, it's
  33.  * easy, just remove the whitespace and go to the beginning or end of the
  34.  * word.  Otherwise, figure out if the next character is in a different group.
  35.  * If it is, go to the beginning or end of that group, otherwise, go to the
  36.  * beginning or end of the current group.  The historic version of vi didn't
  37.  * get this right, so, for example, there were cases where "4e" was not the
  38.  * same as "eeee" -- in particular, single character words, and commands that
  39.  * began in whitespace were almost always handled incorrectly.  To get it right
  40.  * you have to resolve the cursor after each search so that the look-ahead to
  41.  * figure out what type of "word" the cursor is in will be correct.
  42.  *
  43.  * Empty lines, and lines that consist of only white-space characters count
  44.  * as a single word, and the beginning and end of the file counts as an
  45.  * infinite number of words.
  46.  *
  47.  * Movements associated with commands are different than movement commands.
  48.  * For example, in "abc  def", with the cursor on the 'a', "cw" is from
  49.  * 'a' to 'c', while "w" is from 'a' to 'd'.  In general, trailing white
  50.  * space is discarded from the change movement.  Another example is that,
  51.  * in the same string, a "cw" on any white space character replaces that
  52.  * single character, and nothing else.  Ain't nothin' in here that's easy.
  53.  *
  54.  * One historic note -- in the original vi, the 'w', 'W' and 'B' commands
  55.  * would treat groups of empty lines as individual words, i.e. the command
  56.  * would move the cursor to each new empty line.  The 'e' and 'E' commands
  57.  * would treat groups of empty lines as a single word, i.e. the first use
  58.  * would move past the group of lines.  The 'b' command would just beep at
  59.  * you, or, if you did it from the start of the line as part of a motion
  60.  * command, go absolutely nuts.  If the lines contained only white-space
  61.  * characters, the 'w' and 'W' commands would just beep at you, and the 'B',
  62.  * 'b', 'E' and 'e' commands would treat the group as a single word, and
  63.  * the 'B' and 'b' commands will treat the lines as individual words.  This
  64.  * implementation treats all of these cases as a single white-space word.
  65.  */
  66.  
  67. enum which {BIGWORD, LITTLEWORD};
  68.  
  69. static int bword __P((SCR *, VICMD *, enum which));
  70. static int eword __P((SCR *, VICMD *, enum which));
  71. static int fword __P((SCR *, VICMD *, enum which));
  72.  
  73. /*
  74.  * v_wordW -- [count]W
  75.  *    Move forward a bigword at a time.
  76.  *
  77.  * PUBLIC: int v_wordW __P((SCR *, VICMD *));
  78.  */
  79. int
  80. v_wordW(sp, vp)
  81.     SCR *sp;
  82.     VICMD *vp;
  83. {
  84.     return (fword(sp, vp, BIGWORD));
  85. }
  86.  
  87. /*
  88.  * v_wordw -- [count]w
  89.  *    Move forward a word at a time.
  90.  *
  91.  * PUBLIC: int v_wordw __P((SCR *, VICMD *));
  92.  */
  93. int
  94. v_wordw(sp, vp)
  95.     SCR *sp;
  96.     VICMD *vp;
  97. {
  98.     return (fword(sp, vp, LITTLEWORD));
  99. }
  100.  
  101. /*
  102.  * fword --
  103.  *    Move forward by words.
  104.  */
  105. static int
  106. fword(sp, vp, type)
  107.     SCR *sp;
  108.     VICMD *vp;
  109.     enum which type;
  110. {
  111.     enum { INWORD, NOTWORD } state;
  112.     VCS cs;
  113.     u_long cnt;
  114.  
  115.     cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
  116.     cs.cs_lno = vp->m_start.lno;
  117.     cs.cs_cno = vp->m_start.cno;
  118.     if (cs_init(sp, &cs))
  119.         return (1);
  120.  
  121.     /*
  122.      * If in white-space:
  123.      *    If the count is 1, and it's a change command, we're done.
  124.      *    Else, move to the first non-white-space character, which
  125.      *    counts as a single word move.  If it's a motion command,
  126.      *    don't move off the end of the line.
  127.      */
  128.     if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
  129.         if (ISMOTION(vp) && cs.cs_flags != CS_EMP && cnt == 1) {
  130.             if (ISCMD(vp->rkp, 'c'))
  131.                 return (0);
  132.             if (ISCMD(vp->rkp, 'd') || ISCMD(vp->rkp, 'y')) {
  133.                 if (cs_fspace(sp, &cs))
  134.                     return (1);
  135.                 goto ret;
  136.             }
  137.         }
  138.         if (cs_fblank(sp, &cs))
  139.             return (1);
  140.         --cnt;
  141.     }
  142.  
  143.     /*
  144.      * Cyclically move to the next word -- this involves skipping
  145.      * over word characters and then any trailing non-word characters.
  146.      * Note, for the 'w' command, the definition of a word keeps
  147.      * switching.
  148.      */
  149.     if (type == BIGWORD)
  150.         while (cnt--) {
  151.             for (;;) {
  152.                 if (cs_next(sp, &cs))
  153.                     return (1);
  154.                 if (cs.cs_flags == CS_EOF)
  155.                     goto ret;
  156.                 if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  157.                     break;
  158.             }
  159.             /*
  160.              * If a motion command and we're at the end of the
  161.              * last word, we're done.  Delete and yank eat any
  162.              * trailing blanks, but we don't move off the end
  163.              * of the line regardless.
  164.              */
  165.             if (cnt == 0 && ISMOTION(vp)) {
  166.                 if ((ISCMD(vp->rkp, 'd') ||
  167.                     ISCMD(vp->rkp, 'y')) &&
  168.                     cs_fspace(sp, &cs))
  169.                     return (1);
  170.                 break;
  171.             }
  172.  
  173.             /* Eat whitespace characters. */
  174.             if (cs_fblank(sp, &cs))
  175.                 return (1);
  176.             if (cs.cs_flags == CS_EOF)
  177.                 goto ret;
  178.         }
  179.     else
  180.         while (cnt--) {
  181.             state = cs.cs_flags == 0 &&
  182.                 inword(cs.cs_ch) ? INWORD : NOTWORD;
  183.             for (;;) {
  184.                 if (cs_next(sp, &cs))
  185.                     return (1);
  186.                 if (cs.cs_flags == CS_EOF)
  187.                     goto ret;
  188.                 if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  189.                     break;
  190.                 if (state == INWORD) {
  191.                     if (!inword(cs.cs_ch))
  192.                         break;
  193.                 } else
  194.                     if (inword(cs.cs_ch))
  195.                         break;
  196.             }
  197.             /* See comment above. */
  198.             if (cnt == 0 && ISMOTION(vp)) {
  199.                 if ((ISCMD(vp->rkp, 'd') ||
  200.                     ISCMD(vp->rkp, 'y')) &&
  201.                     cs_fspace(sp, &cs))
  202.                     return (1);
  203.                 break;
  204.             }
  205.  
  206.             /* Eat whitespace characters. */
  207.             if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  208.                 if (cs_fblank(sp, &cs))
  209.                     return (1);
  210.             if (cs.cs_flags == CS_EOF)
  211.                 goto ret;
  212.         }
  213.  
  214.     /*
  215.      * If we didn't move, we must be at EOF.
  216.      *
  217.      * !!!
  218.      * That's okay for motion commands, however.
  219.      */
  220. ret:    if (!ISMOTION(vp) &&
  221.         cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
  222.         v_eof(sp, &vp->m_start);
  223.         return (1);
  224.     }
  225.  
  226.     /* Adjust the end of the range for motion commands. */
  227.     vp->m_stop.lno = cs.cs_lno;
  228.     vp->m_stop.cno = cs.cs_cno;
  229.     if (ISMOTION(vp) && cs.cs_flags == 0)
  230.         --vp->m_stop.cno;
  231.  
  232.     /*
  233.      * Non-motion commands move to the end of the range.  Delete
  234.      * and yank stay at the start, ignore others.
  235.      */
  236.     vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
  237.     return (0);
  238. }
  239.  
  240. /*
  241.  * v_wordE -- [count]E
  242.  *    Move forward to the end of the bigword.
  243.  *
  244.  * PUBLIC: int v_wordE __P((SCR *, VICMD *));
  245.  */
  246. int
  247. v_wordE(sp, vp)
  248.     SCR *sp;
  249.     VICMD *vp;
  250. {
  251.     return (eword(sp, vp, BIGWORD));
  252. }
  253.  
  254. /*
  255.  * v_worde -- [count]e
  256.  *    Move forward to the end of the word.
  257.  *
  258.  * PUBLIC: int v_worde __P((SCR *, VICMD *));
  259.  */
  260. int
  261. v_worde(sp, vp)
  262.     SCR *sp;
  263.     VICMD *vp;
  264. {
  265.     return (eword(sp, vp, LITTLEWORD));
  266. }
  267.  
  268. /*
  269.  * eword --
  270.  *    Move forward to the end of the word.
  271.  */
  272. static int
  273. eword(sp, vp, type)
  274.     SCR *sp;
  275.     VICMD *vp;
  276.     enum which type;
  277. {
  278.     enum { INWORD, NOTWORD } state;
  279.     VCS cs;
  280.     u_long cnt;
  281.  
  282.     cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
  283.     cs.cs_lno = vp->m_start.lno;
  284.     cs.cs_cno = vp->m_start.cno;
  285.     if (cs_init(sp, &cs))
  286.         return (1);
  287.  
  288.     /*
  289.      * !!!
  290.      * If in whitespace, or the next character is whitespace, move past
  291.      * it.  (This doesn't count as a word move.)  Stay at the character
  292.      * past the current one, it sets word "state" for the 'e' command.
  293.      */
  294.     if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
  295.         if (cs_next(sp, &cs))
  296.             return (1);
  297.         if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
  298.             goto start;
  299.     }
  300.     if (cs_fblank(sp, &cs))
  301.         return (1);
  302.  
  303.     /*
  304.      * Cyclically move to the next word -- this involves skipping
  305.      * over word characters and then any trailing non-word characters.
  306.      * Note, for the 'e' command, the definition of a word keeps
  307.      * switching.
  308.      */
  309. start:    if (type == BIGWORD)
  310.         while (cnt--) {
  311.             for (;;) {
  312.                 if (cs_next(sp, &cs))
  313.                     return (1);
  314.                 if (cs.cs_flags == CS_EOF)
  315.                     goto ret;
  316.                 if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  317.                     break;
  318.             }
  319.             /*
  320.              * When we reach the start of the word after the last
  321.              * word, we're done.  If we changed state, back up one
  322.              * to the end of the previous word.
  323.              */
  324.             if (cnt == 0) {
  325.                 if (cs.cs_flags == 0 && cs_prev(sp, &cs))
  326.                     return (1);
  327.                 break;
  328.             }
  329.  
  330.             /* Eat whitespace characters. */
  331.             if (cs_fblank(sp, &cs))
  332.                 return (1);
  333.             if (cs.cs_flags == CS_EOF)
  334.                 goto ret;
  335.         }
  336.     else
  337.         while (cnt--) {
  338.             state = cs.cs_flags == 0 &&
  339.                 inword(cs.cs_ch) ? INWORD : NOTWORD;
  340.             for (;;) {
  341.                 if (cs_next(sp, &cs))
  342.                     return (1);
  343.                 if (cs.cs_flags == CS_EOF)
  344.                     goto ret;
  345.                 if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  346.                     break;
  347.                 if (state == INWORD) {
  348.                     if (!inword(cs.cs_ch))
  349.                         break;
  350.                 } else
  351.                     if (inword(cs.cs_ch))
  352.                         break;
  353.             }
  354.             /* See comment above. */
  355.             if (cnt == 0) {
  356.                 if (cs.cs_flags == 0 && cs_prev(sp, &cs))
  357.                     return (1);
  358.                 break;
  359.             }
  360.  
  361.             /* Eat whitespace characters. */
  362.             if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  363.                 if (cs_fblank(sp, &cs))
  364.                     return (1);
  365.             if (cs.cs_flags == CS_EOF)
  366.                 goto ret;
  367.         }
  368.  
  369.     /*
  370.      * If we didn't move, we must be at EOF.
  371.      *
  372.      * !!!
  373.      * That's okay for motion commands, however.
  374.      */
  375. ret:    if (!ISMOTION(vp) &&
  376.         cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
  377.         v_eof(sp, &vp->m_start);
  378.         return (1);
  379.     }
  380.  
  381.     /* Set the end of the range for motion commands. */
  382.     vp->m_stop.lno = cs.cs_lno;
  383.     vp->m_stop.cno = cs.cs_cno;
  384.  
  385.     /*
  386.      * Non-motion commands move to the end of the range.
  387.      * Delete and yank stay at the start, ignore others.
  388.      */
  389.     vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
  390.     return (0);
  391. }
  392.  
  393. /*
  394.  * v_WordB -- [count]B
  395.  *    Move backward a bigword at a time.
  396.  *
  397.  * PUBLIC: int v_wordB __P((SCR *, VICMD *));
  398.  */
  399. int
  400. v_wordB(sp, vp)
  401.     SCR *sp;
  402.     VICMD *vp;
  403. {
  404.     return (bword(sp, vp, BIGWORD));
  405. }
  406.  
  407. /*
  408.  * v_wordb -- [count]b
  409.  *    Move backward a word at a time.
  410.  *
  411.  * PUBLIC: int v_wordb __P((SCR *, VICMD *));
  412.  */
  413. int
  414. v_wordb(sp, vp)
  415.     SCR *sp;
  416.     VICMD *vp;
  417. {
  418.     return (bword(sp, vp, LITTLEWORD));
  419. }
  420.  
  421. /*
  422.  * bword --
  423.  *    Move backward by words.
  424.  */
  425. static int
  426. bword(sp, vp, type)
  427.     SCR *sp;
  428.     VICMD *vp;
  429.     enum which type;
  430. {
  431.     enum { INWORD, NOTWORD } state;
  432.     VCS cs;
  433.     u_long cnt;
  434.  
  435.     cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
  436.     cs.cs_lno = vp->m_start.lno;
  437.     cs.cs_cno = vp->m_start.cno;
  438.     if (cs_init(sp, &cs))
  439.         return (1);
  440.  
  441.     /*
  442.      * !!!
  443.      * If in whitespace, or the previous character is whitespace, move
  444.      * past it.  (This doesn't count as a word move.)  Stay at the
  445.      * character before the current one, it sets word "state" for the
  446.      * 'b' command.
  447.      */
  448.     if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
  449.         if (cs_prev(sp, &cs))
  450.             return (1);
  451.         if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
  452.             goto start;
  453.     }
  454.     if (cs_bblank(sp, &cs))
  455.         return (1);
  456.  
  457.     /*
  458.      * Cyclically move to the beginning of the previous word -- this
  459.      * involves skipping over word characters and then any trailing
  460.      * non-word characters.  Note, for the 'b' command, the definition
  461.      * of a word keeps switching.
  462.      */
  463. start:    if (type == BIGWORD)
  464.         while (cnt--) {
  465.             for (;;) {
  466.                 if (cs_prev(sp, &cs))
  467.                     return (1);
  468.                 if (cs.cs_flags == CS_SOF)
  469.                     goto ret;
  470.                 if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  471.                     break;
  472.             }
  473.             /*
  474.              * When we reach the end of the word before the last
  475.              * word, we're done.  If we changed state, move forward
  476.              * one to the end of the next word.
  477.              */
  478.             if (cnt == 0) {
  479.                 if (cs.cs_flags == 0 && cs_next(sp, &cs))
  480.                     return (1);
  481.                 break;
  482.             }
  483.  
  484.             /* Eat whitespace characters. */
  485.             if (cs_bblank(sp, &cs))
  486.                 return (1);
  487.             if (cs.cs_flags == CS_SOF)
  488.                 goto ret;
  489.         }
  490.     else
  491.         while (cnt--) {
  492.             state = cs.cs_flags == 0 &&
  493.                 inword(cs.cs_ch) ? INWORD : NOTWORD;
  494.             for (;;) {
  495.                 if (cs_prev(sp, &cs))
  496.                     return (1);
  497.                 if (cs.cs_flags == CS_SOF)
  498.                     goto ret;
  499.                 if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  500.                     break;
  501.                 if (state == INWORD) {
  502.                     if (!inword(cs.cs_ch))
  503.                         break;
  504.                 } else
  505.                     if (inword(cs.cs_ch))
  506.                         break;
  507.             }
  508.             /* See comment above. */
  509.             if (cnt == 0) {
  510.                 if (cs.cs_flags == 0 && cs_next(sp, &cs))
  511.                     return (1);
  512.                 break;
  513.             }
  514.  
  515.             /* Eat whitespace characters. */
  516.             if (cs.cs_flags != 0 || isblank(cs.cs_ch))
  517.                 if (cs_bblank(sp, &cs))
  518.                     return (1);
  519.             if (cs.cs_flags == CS_SOF)
  520.                 goto ret;
  521.         }
  522.  
  523.     /* If we didn't move, we must be at SOF. */
  524. ret:    if (cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
  525.         v_sof(sp, &vp->m_start);
  526.         return (1);
  527.     }
  528.  
  529.     /* Set the end of the range for motion commands. */
  530.     vp->m_stop.lno = cs.cs_lno;
  531.     vp->m_stop.cno = cs.cs_cno;
  532.  
  533.     /*
  534.      * All commands move to the end of the range.  Motion commands
  535.      * adjust the starting point to the character before the current
  536.      * one.
  537.      *
  538.      * !!!
  539.      * The historic vi didn't get this right -- the `yb' command yanked
  540.      * the right stuff and even updated the cursor value, but the cursor
  541.      * was not actually updated on the screen.
  542.      */
  543.     vp->m_final = vp->m_stop;
  544.     if (ISMOTION(vp))
  545.         --vp->m_start.cno;
  546.     return (0);
  547. }
  548.