home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / csearch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  11.2 KB  |  492 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony
  8.  *                            G. R. (Fred) Walter     watmath!watcgl!grwalter
  9.  */
  10.  
  11. /*
  12.  *
  13.  * csearch.c: command line searching commands
  14.  */
  15.  
  16. #include "vim.h"
  17. #include "globals.h"
  18. #include "proto.h"
  19. #include "param.h"
  20.  
  21. /* we use modified Henry Spencer's regular expression routines */
  22. #include "regexp.h"
  23.  
  24. int global_busy = 0;            /* set to 1 if global busy, 2 if global has
  25.                                     been called during a global command */
  26. int global_wait;                /* set to 1 if wait_return has to be called
  27.                                     after global command */
  28. extern regexp *myregcomp __ARGS((char *));
  29.  
  30. /* dosub(lp, up, cmd)
  31.  *
  32.  * Perform a substitution from line 'lp' to line 'up' using the
  33.  * command pointed to by 'cmd' which should be of the form:
  34.  *
  35.  * /pattern/substitution/gc
  36.  *
  37.  * The trailing 'g' is optional and, if present, indicates that multiple
  38.  * substitutions should be performed on each line, if applicable.
  39.  * The trailing 'c' is optional and, if present, indicates that a confirmation
  40.  * will be asked for each replacement.
  41.  * The usual escapes are supported as described in the regexp docs.
  42.  */
  43.  
  44.     void
  45. dosub(lp, up, cmd, nextcommand)
  46.     linenr_t    lp;
  47.     linenr_t    up;
  48.     char        *cmd;
  49.     u_char        **nextcommand;
  50. {
  51.     linenr_t        lnum;
  52.     long            i;
  53.     char           *ptr;
  54.     regexp           *prog;
  55.     long            nsubs = 0;
  56.     linenr_t        nlines = 0;
  57.     static int        do_all = FALSE;     /* do multiple substitutions per line */
  58.     static int        do_ask = FALSE;     /* ask for confirmation */
  59.     char           *pat, *sub = NULL;
  60.     static char    *old_sub = NULL;
  61.     int             delimiter;
  62.     int             sublen;
  63.     int                got_quit = FALSE;
  64.     int                got_match = FALSE;
  65.     int                temp;
  66.  
  67.     if (strchr("0123456789gc|\"#", *cmd) == NULL)       /* new pattern and substitution */
  68.     {
  69.         delimiter = *cmd++;            /* remember delimiter character */
  70.         pat = cmd;                    /* remember the start of the regexp */
  71.  
  72.         /*
  73.          * do the next loop twice:
  74.          *  i == 0: find the end of the regexp
  75.          *  i == 1: find the end of the substitution
  76.          */
  77.         for (i = 0; ; ++i)
  78.         {
  79.             while (cmd[0])
  80.             {
  81.                 if (cmd[0] == delimiter)            /* end delimiter found */
  82.                 {
  83.                     *cmd++ = NUL;                    /* replace it by a NUL */
  84.                     break;
  85.                 }
  86.                 if (cmd[0] == '\\' && cmd[1] != 0)    /* skip escaped characters */
  87.                     ++cmd;
  88.                 ++cmd;
  89.             }
  90.             if (i == 1)
  91.                 break;
  92.             sub = cmd;                /* remember the start of the substitution */
  93.         }
  94.         free(old_sub);
  95.         old_sub = strsave(sub);
  96.     }
  97.     else                                /* use previous pattern and substitution */
  98.     {
  99.         if (old_sub == NULL)    /* there is no previous command */
  100.         {
  101.             beep();
  102.             return;
  103.         }
  104.         pat = NULL;             /* myregcomp() will use previous pattern */
  105.         sub = old_sub;
  106.     }
  107.  
  108.     /*
  109.      * find trailing options
  110.      */
  111.     if (!p_ed)
  112.     {
  113.         do_all = FALSE;
  114.         do_ask = FALSE;
  115.     }
  116.     while (*cmd)
  117.     {
  118.         if (*cmd == 'g')
  119.             do_all = !do_all;
  120.         else if (*cmd == 'c')
  121.             do_ask = !do_ask;
  122.         else
  123.             break;
  124.         ++cmd;
  125.     }
  126.  
  127.     /*
  128.      * check for a trailing count
  129.      */
  130.     skipspace(&cmd);
  131.     if (isdigit(*cmd))
  132.     {
  133.         i = getdigits(&cmd);
  134.         if (i <= 0)
  135.         {
  136.             emsg(e_zerocount);
  137.             return;
  138.         }
  139.         lp = up;
  140.         up += i - 1;
  141.     }
  142.  
  143.     /*
  144.      * check for trailing '|', '"' or '#'
  145.      */
  146.     skipspace(&cmd);
  147.     if (*cmd)
  148.     {
  149.         if (strchr("|\"#", *cmd) != NULL)
  150.         {
  151.             *nextcommand = (u_char *)cmd;
  152.         }
  153.         else
  154.         {
  155.             emsg(e_trailing);
  156.             return;
  157.         }
  158.     }
  159.  
  160.     if ((prog = myregcomp(pat)) == NULL)
  161.     {
  162.         emsg(e_invcmd);
  163.         return;
  164.     }
  165.  
  166.     /*
  167.      * ~ in the substitute pattern is replaced by the old pattern.
  168.      * We do it here once to avoid it to be replaced over and over again.
  169.      */
  170.     sub = regtilde(sub, (int)p_magic);
  171.  
  172.     for (lnum = lp; lnum <= up && !(got_int || got_quit); ++lnum)
  173.     {
  174.         ptr = nr2ptr(lnum);
  175.         if (regexec(prog, ptr, TRUE))  /* a match on this line */
  176.         {
  177.             char        *new_end, *new_start = NULL;
  178.             char        *old_match, *old_copy;
  179.             char        *prev_old_match = NULL;
  180.             char        *p1, *p2;
  181.             int            did_sub = FALSE;
  182.             int            match, lastone;
  183.  
  184.             if (!got_match)
  185.             {
  186.                 setpcmark();
  187.                 got_match = TRUE;
  188.             }
  189.  
  190.             /*
  191.              * Save the line that was last changed for the final cursor
  192.              * position (just like the real vi).
  193.              */
  194.             Curpos.lnum = lnum;
  195.  
  196.             old_copy = old_match = ptr;
  197.             for (;;)            /* loop until nothing more to replace */
  198.             {
  199.                 Curpos.col = (int)(prog->startp[0] - ptr);
  200.                 /*
  201.                  * Match empty string does not count, except for first match.
  202.                  * This reproduces the strange vi behaviour.
  203.                  * This also catches endless loops.
  204.                  */
  205.                 if (old_match == prev_old_match && old_match == prog->endp[0])
  206.                 {
  207.                     ++old_match;
  208.                     goto skip2;
  209.                 }
  210.                 while (do_ask)        /* loop until 'y', 'n' or 'q' typed */
  211.                 {
  212.                     temp = RedrawingDisabled;
  213.                     RedrawingDisabled = FALSE;
  214.                     updateScreen(CURSUPD);
  215.                     smsg("replace by %s (y/n/q)? ", sub);
  216.                     setcursor();
  217.                     RedrawingDisabled = temp;
  218.                     if ((i = vgetc()) == 'q' || i == ESC || i == Ctrl('C'))
  219.                     {
  220.                         got_quit = TRUE;
  221.                         break;
  222.                     }
  223.                     else if (i == 'n')
  224.                         goto skip;
  225.                     else if (i == 'y')
  226.                         break;
  227.                 }
  228.                 if (got_quit)
  229.                     break;
  230.  
  231.                         /* get length of substitution part */
  232.                 sublen = regsub(prog, sub, ptr, 0, (int)p_magic);
  233.                 if (new_start == NULL)
  234.                 {
  235.                     /*
  236.                      * Get some space for a temporary buffer to do the substitution
  237.                      * into.
  238.                      */
  239.                     if ((new_start = alloc((unsigned)(strlen(ptr) + sublen + 5))) == NULL)
  240.                         goto outofmem;
  241.                     *new_start = NUL;
  242.                 }
  243.                 else
  244.                 {
  245.                     /*
  246.                      * extend the temporary buffer to do the substitution into.
  247.                      */
  248.                     if ((p1 = alloc((unsigned)(strlen(new_start) + strlen(old_copy) + sublen + 1))) == NULL)
  249.                         goto outofmem;
  250.                     strcpy(p1, new_start);
  251.                     free(new_start);
  252.                     new_start = p1;
  253.                 }
  254.  
  255.                 for (new_end = new_start; *new_end; new_end++)
  256.                     ;
  257.                 /*
  258.                  * copy up to the part that matched
  259.                  */
  260.                 while (old_copy < prog->startp[0])
  261.                     *new_end++ = *old_copy++;
  262.  
  263.                 regsub(prog, sub, new_end, 1, (int)p_magic);
  264.                 nsubs++;
  265.                 did_sub = TRUE;
  266.  
  267.                 /*
  268.                  * Now the trick is to replace CTRL-Ms with a real line break.
  269.                  * This would make it impossible to insert CTRL-Ms in the text.
  270.                  * That is the way vi works. In Vim the line break can be
  271.                  * avoided by preceding the CTRL-M with a CTRL-V. Now you can't
  272.                  * precede a line break with a CTRL-V, big deal.
  273.                  */
  274.                 while ((p1 = strchr(new_end, CR)) != NULL)
  275.                 {
  276.                     if (p1 == new_end || p1[-1] != Ctrl('V'))
  277.                     {
  278.                         if (u_inssub(lnum))                /* prepare for undo */
  279.                         {
  280.                             *p1 = NUL;                    /* truncate up to the CR */
  281.                             if ((p2 = save_line(new_start)) != NULL)
  282.                             {
  283.                                 appendline(lnum - 1, p2);
  284.                                 ++lnum;
  285.                                 ++up;                    /* number of lines increases */
  286.                             }
  287.                             strcpy(new_start, p1 + 1);    /* copy the rest */
  288.                             new_end = new_start;
  289.                         }
  290.                     }
  291.                     else                            /* remove CTRL-V */
  292.                     {
  293.                         strcpy(p1 - 1, p1);
  294.                         new_end = p1;
  295.                     }
  296.                 }
  297.  
  298.                 old_copy = prog->endp[0];    /* remember next character to be copied */
  299.                 /*
  300.                  * continue searching after the match
  301.                  * prevent endless loop with patterns that match empty strings,
  302.                  * e.g. :s/$/pat/g or :s/[a-z]* /(&)/g
  303.                  */
  304. skip:
  305.                 old_match = prog->endp[0];
  306.                 prev_old_match = old_match;
  307. skip2:
  308.                 match = -1;
  309.                 lastone = (*old_match == NUL || got_int || got_quit || !do_all);
  310.                 if (lastone || do_ask || (match = regexec(prog, old_match, (int)FALSE)) == 0)
  311.                 {
  312.                     if (new_start)
  313.                     {
  314.                         /*
  315.                          * copy the rest of the line, that didn't match
  316.                          */
  317.                         strcat(new_start, old_copy);
  318.                         i = old_match - ptr;
  319.  
  320.                         if ((ptr = save_line(new_start)) != NULL && u_savesub(lnum))
  321.                             replaceline(lnum, ptr);
  322.  
  323.                         free(new_start);          /* free the temp buffer */
  324.                         new_start = NULL;
  325.                         old_match = ptr + i;
  326.                         old_copy = ptr;
  327.                     }
  328.                     if (match == -1 && !lastone)
  329.                         match = regexec(prog, old_match, (int)FALSE);
  330.                     if (match <= 0)        /* quit loop if there is no more match */
  331.                         break;
  332.                 }
  333.                     /* breakcheck is slow, don't call it too often */
  334.                 if ((nsubs & 15) == 0)
  335.                     breakcheck();
  336.  
  337.             }
  338.             if (did_sub)
  339.                 ++nlines;
  340.         }
  341.             /* breakcheck is slow, don't call it too often */
  342.         if ((lnum & 15) == 0)
  343.             breakcheck();
  344.     }
  345.  
  346. outofmem:
  347.     if (nsubs)
  348.     {
  349.         CHANGED;
  350.         updateScreen(CURSUPD); /* need this to update LineSizes */
  351.         beginline(TRUE);
  352.         if (nsubs > p_report)
  353.             smsg("%s%ld substitution%s on %ld line%s",
  354.                                 got_int ? "(Interrupted) " : "",
  355.                                 nsubs, plural(nsubs),
  356.                                 (long)nlines, plural((long)nlines));
  357.         else if (got_int)
  358.                 msg(e_interr);
  359.         else if (do_ask)
  360.                 msg("");
  361.     }
  362.     else if (got_int)        /* interrupted */
  363.         msg(e_interr);
  364.     else if (got_match)        /* did find something but nothing substituted */
  365.         msg("");
  366.     else                    /* nothing found */
  367.         msg(e_nomatch);
  368.  
  369.     free((char *) prog);
  370. }
  371.  
  372. /*
  373.  * doglob(cmd)
  374.  *
  375.  * Execute a global command of the form:
  376.  *
  377.  * g/pattern/X : execute X on all lines where pattern matches
  378.  * v/pattern/X : execute X on all lines where pattern does not match
  379.  *
  380.  * where 'X' is an EX command
  381.  *
  382.  * The command character (as well as the trailing slash) is optional, and
  383.  * is assumed to be 'p' if missing.
  384.  *
  385.  * This is implemented in two passes: first we scan the file for the pattern and
  386.  * set a mark for each line that (not) matches. secondly we execute the command
  387.  * for each line that has a mark. This is required because after deleting
  388.  * lines we do not know where to search for the next match.
  389.  */
  390.  
  391.     void
  392. doglob(type, lp, up, cmd)
  393.     int         type;
  394.     linenr_t    lp, up;
  395.     char        *cmd;
  396. {
  397.     linenr_t        lnum;        /* line number according to old situation */
  398.     linenr_t        old_lcount; /* line_count before the command */
  399.     int             ndone;
  400.  
  401.     char            delim;        /* delimiter, normally '/' */
  402.     char           *pat;
  403.     regexp           *prog;
  404.     int                match;
  405.  
  406.     if (global_busy)
  407.     {
  408.         emsg("Cannot do :global recursive");
  409.         ++global_busy;
  410.         return;
  411.     }
  412.  
  413.     delim = *cmd;             /* get the delimiter */
  414.     if (delim)
  415.         ++cmd;                /* skip delimiter if there is one */
  416.     pat = cmd;
  417.  
  418.     while (cmd[0])
  419.     {
  420.         if (cmd[0] == delim)                /* end delimiter found */
  421.         {
  422.             *cmd++ = NUL;                    /* replace it by a NUL */
  423.             break;
  424.         }
  425.         if (cmd[0] == '\\' && cmd[1] != 0)    /* skip escaped characters */
  426.             ++cmd;
  427.         ++cmd;
  428.     }
  429.  
  430.     reg_ic = p_ic;           /* set "ignore case" flag appropriately */
  431.  
  432.     if ((prog = myregcomp(pat)) == NULL)
  433.     {
  434.         emsg(e_invcmd);
  435.         return;
  436.     }
  437.     msg("");
  438.  
  439. /*
  440.  * pass 1: set marks for each (not) matching line
  441.  */
  442.     ndone = 0;
  443.     for (lnum = lp; lnum <= up && !got_int; ++lnum)
  444.     {
  445.         match = regexec(prog, nr2ptr(lnum), (int)TRUE);     /* a match on this line? */
  446.         if ((type == 'g' && match) || (type == 'v' && !match))
  447.         {
  448.             setmarked(lnum);
  449.             ndone++;
  450.         }
  451.             /* breakcheck is slow, don't call it too often */
  452.         if ((lnum & 15) == 0)
  453.             breakcheck();
  454.     }
  455.  
  456. /*
  457.  * pass 2: execute the command for each line that has been marked
  458.  */
  459.     if (got_int)
  460.         msg("Interrupted");
  461.     else if (ndone == 0)
  462.         msg(e_nomatch);
  463.     else
  464.     {
  465.         global_busy = 1;
  466.         global_wait = 0;
  467.         RedrawingDisabled = TRUE;
  468.         old_lcount = line_count;
  469.         while (!got_int && (lnum = firstmarked()) != 0 && global_busy == 1)
  470.         {
  471.             Curpos.lnum = lnum;
  472.             Curpos.col = 0;
  473.             if (*cmd == NUL)
  474.                 docmdline((u_char *)"p");
  475.             else
  476.                 docmdline((u_char *)cmd);
  477.             breakcheck();
  478.         }
  479.  
  480.         RedrawingDisabled = FALSE;
  481.         global_busy = 0;
  482.         if (global_wait)                /* wait for return */
  483.             wait_return(FALSE);
  484.         screenclear();
  485.         updateScreen(CURSUPD);
  486.         msgmore(line_count - old_lcount);
  487.     }
  488.  
  489.     clearmarked();      /* clear rest of the marks */
  490.     free((char *) prog);
  491. }
  492.