home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / v_search.c < prev    next >
C/C++ Source or Header  |  1996-09-19  |  13KB  |  516 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_search.c    10.18 (Berkeley) 9/19/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 <errno.h>
  23. #include <limits.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27.  
  28. #include "../common/common.h"
  29. #include "vi.h"
  30.  
  31. static int v_exaddr __P((SCR *, VICMD *, dir_t));
  32. static int v_search __P((SCR *, VICMD *, char *, size_t, u_int, dir_t));
  33.  
  34. /*
  35.  * v_srch -- [count]?RE[? offset]
  36.  *    Ex address search backward.
  37.  *
  38.  * PUBLIC: int v_searchb __P((SCR *, VICMD *));
  39.  */
  40. int
  41. v_searchb(sp, vp)
  42.     SCR *sp;
  43.     VICMD *vp;
  44. {
  45.     return (v_exaddr(sp, vp, BACKWARD));
  46. }
  47.  
  48. /*
  49.  * v_searchf -- [count]/RE[/ offset]
  50.  *    Ex address search forward.
  51.  *
  52.  * PUBLIC: int v_searchf __P((SCR *, VICMD *));
  53.  */
  54. int
  55. v_searchf(sp, vp)
  56.     SCR *sp;
  57.     VICMD *vp;
  58. {
  59.     return (v_exaddr(sp, vp, FORWARD));
  60. }
  61.  
  62. /*
  63.  * v_exaddr --
  64.  *    Do a vi search (which is really an ex address).
  65.  */
  66. static int
  67. v_exaddr(sp, vp, dir)
  68.     SCR *sp;
  69.     VICMD *vp;
  70.     dir_t dir;
  71. {
  72.     static EXCMDLIST fake = { "search" };
  73.     EXCMD *cmdp;
  74.     GS *gp;
  75.     TEXT *tp;
  76.     recno_t s_lno;
  77.     size_t len, s_cno, tlen;
  78.     int err, nb, type;
  79.     char *cmd, *t, buf[20];
  80.  
  81.     /*
  82.      * !!!
  83.      * If using the search command as a motion, any addressing components
  84.      * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/.
  85.      */
  86.     if (F_ISSET(vp, VC_ISDOT))
  87.         return (v_search(sp, vp,
  88.             NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir));
  89.  
  90.     /* Get the search pattern. */
  91.     if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH,
  92.         TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT |
  93.         (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0)))
  94.         return (1);
  95.  
  96.     tp = sp->tiq.cqh_first;
  97.  
  98.     /* If the user backspaced over the prompt, do nothing. */
  99.     if (tp->term == TERM_BS)
  100.         return (1);
  101.  
  102.     /*
  103.      * If the user was doing an incremental search, then we've already
  104.      * updated the cursor and moved to the right location.  Return the
  105.      * correct values, we're done.
  106.      */
  107.     if (tp->term == TERM_SEARCH) {
  108.         vp->m_stop.lno = sp->lno;
  109.         vp->m_stop.cno = sp->cno;
  110.         if (ISMOTION(vp))
  111.             return (v_correct(sp, vp, 0));
  112.         vp->m_final = vp->m_stop;
  113.         return (0);
  114.     }
  115.  
  116.     /*
  117.      * If the user entered <escape> or <carriage-return>, the length is
  118.      * 1 and the right thing will happen, i.e. the prompt will be used
  119.      * as a command character.
  120.      *
  121.      * Build a fake ex command structure.
  122.      */
  123.     gp = sp->gp;
  124.     gp->excmd.cp = tp->lb;
  125.     gp->excmd.clen = tp->len;
  126.     F_INIT(&gp->excmd, E_VISEARCH);
  127.  
  128.     /*
  129.      * XXX
  130.      * Warn if the search wraps.  This is a pretty special case, but it's
  131.      * nice feature that wasn't in the original implementations of ex/vi.
  132.      * (It was added at some point to System V's version.)  This message
  133.      * is only displayed if there are no keys in the queue. The problem is
  134.      * the command is going to succeed, and the message is informational,
  135.      * not an error.  If a macro displays it repeatedly, e.g., the pattern
  136.      * only occurs once in the file and wrapscan is set, you lose big.  For
  137.      * example, if the macro does something like:
  138.      *
  139.      *    :map K /pattern/^MjK
  140.      *
  141.      * Each search will display the message, but the following "/pattern/"
  142.      * will immediately overwrite it, with strange results.  The System V
  143.      * vi displays the "wrapped" message multiple times, but because it's
  144.      * overwritten each time, it's not as noticeable.  As we don't discard
  145.      * messages, it's a real problem for us.
  146.      */
  147.     if (!KEYS_WAITING(sp))
  148.         F_SET(&gp->excmd, E_SEARCH_WMSG);
  149.         
  150.     /* Save the current line/column. */
  151.     s_lno = sp->lno;
  152.     s_cno = sp->cno;
  153.  
  154.     /*
  155.      * !!!
  156.      * Historically, vi / and ? commands were full-blown ex addresses,
  157.      * including ';' delimiters, trailing <blank>'s, multiple search
  158.      * strings (separated by semi-colons) and, finally, full-blown z
  159.      * commands after the / and ? search strings.  (If the search was
  160.      * being used as a motion, the trailing z command was ignored.
  161.      * Also, we do some argument checking on the z command, to be sure
  162.      * that it's not some other random command.) For multiple search
  163.      * strings, leading <blank>'s at the second and subsequent strings
  164.      * were eaten as well.  This has some (unintended?) side-effects:
  165.      * the command /ptrn/;3 is legal and results in moving to line 3.
  166.      * I suppose you could use it to optionally move to line 3...
  167.      *
  168.      * !!!
  169.      * Historically, if any part of the search command failed, the cursor
  170.      * remained unmodified (even if ; was used).  We have to play games
  171.      * because the underlying ex parser thinks we're modifying the cursor
  172.      * as we go, but I think we're compatible with historic practice.
  173.      *
  174.      * !!!
  175.      * Historically, the command "/STRING/;   " failed, apparently it
  176.      * confused the parser.  We're not that compatible.
  177.      */
  178.     cmdp = &gp->excmd;
  179.     if (ex_range(sp, cmdp, &err))
  180.         return (1);
  181.     
  182.     /*
  183.      * Remember where any remaining command information is, and clean
  184.      * up the fake ex command.
  185.      */
  186.     cmd = cmdp->cp;
  187.     len = cmdp->clen;
  188.     gp->excmd.clen = 0;
  189.  
  190.     if (err)
  191.         goto err2;
  192.  
  193.     /* Copy out the new cursor position and make sure it's okay. */
  194.     switch (cmdp->addrcnt) {
  195.     case 1:
  196.         vp->m_stop = cmdp->addr1;
  197.         break;
  198.     case 2:
  199.         vp->m_stop = cmdp->addr2;
  200.         break;
  201.     }
  202.     if (!db_exist(sp, vp->m_stop.lno)) {
  203.         ex_badaddr(sp, &fake,
  204.             vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK);
  205.         goto err2;
  206.     }
  207.  
  208.     /*
  209.      * !!!
  210.      * Historic practice is that a trailing 'z' was ignored if it was a
  211.      * motion command.  Should probably be an error, but not worth the
  212.      * effort.
  213.      */
  214.     if (ISMOTION(vp))
  215.         return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA)));
  216.         
  217.     /*
  218.      * !!!
  219.      * Historically, if it wasn't a motion command, a delta in the search
  220.      * pattern turns it into a first nonblank movement.
  221.      */
  222.     nb = F_ISSET(cmdp, E_DELTA);
  223.  
  224.     /* Check for the 'z' command. */
  225.     if (len != 0) {
  226.         if (*cmd != 'z')
  227.             goto err1;
  228.  
  229.         /* No blanks, just like the z command. */
  230.         for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen)
  231.             if (!isdigit(*t))
  232.                 break;
  233.         if (tlen &&
  234.             (*t == '-' || *t == '.' || *t == '+' || *t == '^')) {
  235.             ++t;
  236.             --tlen;
  237.             type = 1;
  238.         } else
  239.             type = 0;
  240.         if (tlen)
  241.             goto err1;
  242.  
  243.         /* The z command will do the nonblank for us. */
  244.         nb = 0;
  245.  
  246.         /* Default to z+. */
  247.         if (!type &&
  248.             v_event_push(sp, NULL, "+", 1, CH_NOMAP | CH_QUOTED))
  249.             return (1);
  250.  
  251.         /* Push the user's command. */
  252.         if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED))
  253.             return (1);
  254.  
  255.         /* Push line number so get correct z display. */
  256.         tlen = snprintf(buf,
  257.             sizeof(buf), "%lu", (u_long)vp->m_stop.lno);
  258.         if (v_event_push(sp, NULL, buf, tlen, CH_NOMAP | CH_QUOTED))
  259.             return (1);
  260.          
  261.         /* Don't refresh until after 'z' happens. */
  262.         F_SET(VIP(sp), VIP_S_REFRESH);
  263.     }
  264.  
  265.     /* Non-motion commands move to the end of the range. */
  266.     vp->m_final = vp->m_stop;
  267.     if (nb) {
  268.         F_CLR(vp, VM_RCM_MASK);
  269.         F_SET(vp, VM_RCM_SETFNB);
  270.     }
  271.     return (0);
  272.  
  273. err1:    msgq(sp, M_ERR,
  274.         "188|Characters after search string, line offset and/or z command");
  275. err2:    vp->m_final.lno = s_lno;
  276.     vp->m_final.cno = s_cno;
  277.     return (1);
  278. }
  279.  
  280. /*
  281.  * v_searchN -- N
  282.  *    Reverse last search.
  283.  *
  284.  * PUBLIC: int v_searchN __P((SCR *, VICMD *));
  285.  */
  286. int
  287. v_searchN(sp, vp)
  288.     SCR *sp;
  289.     VICMD *vp;
  290. {
  291.     dir_t dir;
  292.  
  293.     switch (sp->searchdir) {
  294.     case BACKWARD:
  295.         dir = FORWARD;
  296.         break;
  297.     case FORWARD:
  298.         dir = BACKWARD;
  299.         break;
  300.     default:
  301.         dir = sp->searchdir;
  302.         break;
  303.     }
  304.     return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, dir));
  305. }
  306.  
  307. /*
  308.  * v_searchn -- n
  309.  *    Repeat last search.
  310.  *
  311.  * PUBLIC: int v_searchn __P((SCR *, VICMD *));
  312.  */
  313. int
  314. v_searchn(sp, vp)
  315.     SCR *sp;
  316.     VICMD *vp;
  317. {
  318.     return (v_search(sp, vp, NULL, 0, SEARCH_PARSE, sp->searchdir));
  319. }
  320.  
  321. /*
  322.  * v_searchw -- [count]^A
  323.  *    Search for the word under the cursor.
  324.  *
  325.  * PUBLIC: int v_searchw __P((SCR *, VICMD *));
  326.  */
  327. int
  328. v_searchw(sp, vp)
  329.     SCR *sp;
  330.     VICMD *vp;
  331. {
  332.     size_t blen, len;
  333.     int rval;
  334.     char *bp;
  335.  
  336.     len = VIP(sp)->klen + sizeof(RE_WSTART) + sizeof(RE_WSTOP);
  337.     GET_SPACE_RET(sp, bp, blen, len);
  338.     len = snprintf(bp, blen, "%s%s%s", RE_WSTART, VIP(sp)->keyw, RE_WSTOP);
  339.  
  340.     rval = v_search(sp, vp, bp, len, SEARCH_SET, FORWARD);
  341.  
  342.     FREE_SPACE(sp, bp, blen);
  343.     return (rval);
  344. }
  345.  
  346. /*
  347.  * v_search --
  348.  *    The search commands.
  349.  */
  350. static int
  351. v_search(sp, vp, ptrn, plen, flags, dir)
  352.     SCR *sp;
  353.     VICMD *vp;
  354.     u_int flags;
  355.     char *ptrn;
  356.     size_t plen;
  357.     dir_t dir;
  358. {
  359.     /* Display messages. */
  360.     LF_SET(SEARCH_MSG);
  361.  
  362.     /* If it's a motion search, offset past end-of-line is okay. */
  363.     if (ISMOTION(vp))
  364.         LF_SET(SEARCH_EOL);
  365.  
  366.     /*
  367.      * XXX
  368.      * Warn if the search wraps.  See the comment above, in v_exaddr().
  369.      */
  370.     if (!KEYS_WAITING(sp))
  371.         LF_SET(SEARCH_WMSG);
  372.         
  373.     switch (dir) {
  374.     case BACKWARD:
  375.         if (b_search(sp,
  376.             &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
  377.             return (1);
  378.         break;
  379.     case FORWARD:
  380.         if (f_search(sp,
  381.             &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
  382.             return (1);
  383.         break;
  384.     case NOTSET:
  385.         msgq(sp, M_ERR, "189|No previous search pattern");
  386.         return (1);
  387.     default:
  388.         abort();
  389.     }
  390.  
  391.     /* Correct motion commands, otherwise, simply move to the location. */
  392.     if (ISMOTION(vp)) {
  393.         if (v_correct(sp, vp, 0))
  394.             return(1);
  395.     } else
  396.         vp->m_final = vp->m_stop;
  397.     return (0);
  398. }
  399.  
  400. /*
  401.  * v_correct --
  402.  *    Handle command with a search as the motion.
  403.  *
  404.  * !!!
  405.  * Historically, commands didn't affect the line searched to/from if the
  406.  * motion command was a search and the final position was the start/end
  407.  * of the line.  There were some special cases and vi was not consistent;
  408.  * it was fairly easy to confuse it.  For example, given the two lines:
  409.  *
  410.  *    abcdefghi
  411.  *    ABCDEFGHI
  412.  *
  413.  * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h'
  414.  * 'k' and put would no longer work correctly.  In any case, we try to do
  415.  * the right thing, but it's not going to exactly match historic practice.
  416.  *
  417.  * PUBLIC: int v_correct __P((SCR *, VICMD *, int));
  418.  */
  419. int
  420. v_correct(sp, vp, isdelta)
  421.     SCR *sp;
  422.     VICMD *vp;
  423.     int isdelta;
  424. {
  425.     dir_t dir;
  426.     MARK m;
  427.     size_t len;
  428.  
  429.     /*
  430.      * !!!
  431.      * We may have wrapped if wrapscan was set, and we may have returned
  432.      * to the position where the cursor started.  Historic vi didn't cope
  433.      * with this well.  Yank wouldn't beep, but the first put after the
  434.      * yank would move the cursor right one column (without adding any
  435.      * text) and the second would put a copy of the current line.  The
  436.      * change and delete commands would beep, but would leave the cursor
  437.      * on the colon command line.  I believe that there are macros that
  438.      * depend on delete, at least, failing.  For now, commands that use
  439.      * search as a motion component fail when the search returns to the
  440.      * original cursor position.
  441.      */
  442.     if (vp->m_start.lno == vp->m_stop.lno &&
  443.         vp->m_start.cno == vp->m_stop.cno) {
  444.         msgq(sp, M_BERR, "190|Search wrapped to original position");
  445.         return (1);
  446.     }
  447.  
  448.     /*
  449.      * !!!
  450.      * Searches become line mode operations if there was a delta specified
  451.      * to the search pattern.
  452.      */
  453.     if (isdelta)
  454.         F_SET(vp, VM_LMODE);
  455.  
  456.     /*
  457.      * If the motion is in the reverse direction, switch the start and
  458.      * stop MARK's so that it's in a forward direction.  (There's no
  459.      * reason for this other than to make the tests below easier.  The
  460.      * code in vi.c:vi() would have done the switch.)  Both forward
  461.      * and backward motions can happen for any kind of search command
  462.      * because of the wrapscan option.
  463.      */
  464.     if (vp->m_start.lno > vp->m_stop.lno ||
  465.         vp->m_start.lno == vp->m_stop.lno &&
  466.         vp->m_start.cno > vp->m_stop.cno) {
  467.         m = vp->m_start;
  468.         vp->m_start = vp->m_stop;
  469.         vp->m_stop = m;
  470.         dir = BACKWARD;
  471.     } else
  472.         dir = FORWARD;
  473.  
  474.     /*
  475.      * BACKWARD:
  476.      *    Delete and yank commands move to the end of the range.
  477.      *    Ignore others.
  478.      *
  479.      * FORWARD:
  480.      *    Delete and yank commands don't move.  Ignore others.
  481.      */
  482.     vp->m_final = vp->m_start;
  483.  
  484.     /*
  485.      * !!!
  486.      * Delta'd searches don't correct based on column positions.
  487.      */
  488.     if (isdelta)
  489.         return (0);
  490.  
  491.     /*
  492.      * !!!
  493.      * Backward searches starting at column 0, and forward searches ending
  494.      * at column 0 are corrected to the last column of the previous line.
  495.      * Otherwise, adjust the starting/ending point to the character before
  496.      * the current one (this is safe because we know the search had to move
  497.      * to succeed).
  498.      *
  499.      * Searches become line mode operations if they start at the first
  500.      * nonblank and end at column 0 of another line.
  501.      */
  502.     if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
  503.         if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
  504.             return (1);
  505.         vp->m_stop.cno = len ? len - 1 : 0;
  506.         len = 0;
  507.         if (nonblank(sp, vp->m_start.lno, &len))
  508.             return (1);
  509.         if (vp->m_start.cno <= len)
  510.             F_SET(vp, VM_LMODE);
  511.     } else
  512.         --vp->m_stop.cno;
  513.  
  514.     return (0);
  515. }
  516.