Include <config.h> instead of "config.h".
[bpt/emacs.git] / src / search.c
index 83ddaeb..930d1bb 100644 (file)
@@ -1,5 +1,5 @@
 /* String search routines for GNU Emacs.
-   Copyright (C) 1985, 1986, 1987, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1985, 1986, 1987, 1993 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -18,11 +18,12 @@ along with GNU Emacs; see the file COPYING.  If not, write to
 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 
-#include "config.h"
+#include <config.h>
 #include "lisp.h"
 #include "syntax.h"
 #include "buffer.h"
 #include "commands.h"
+#include "blockinput.h"
 
 #include <sys/types.h>
 #include "regex.h"
@@ -97,9 +98,10 @@ compile_pattern (pattern, bufp, regp, translate)
 
   last_regexp = Qnil;
   bufp->translate = translate;
-  val = re_compile_pattern ((char *) XSTRING (pattern)->data,
-                           XSTRING (pattern)->size,
-                           bufp);
+  BLOCK_INPUT;
+  val = (CONST char *) re_compile_pattern ((char *) XSTRING (pattern)->data,
+                                          XSTRING (pattern)->size, bufp);
+  UNBLOCK_INPUT;
   if (val)
     {
       dummy = build_string (val);
@@ -111,8 +113,10 @@ compile_pattern (pattern, bufp, regp, translate)
 
   /* Advise the searching functions about the space we have allocated
      for register data.  */
+  BLOCK_INPUT;
   if (regp)
     re_set_registers (bufp, regp, regp->num_regs, regp->start, regp->end);
+  UNBLOCK_INPUT;
 
   return;
 }
@@ -251,7 +255,8 @@ fast_string_match (regexp, string)
    If COUNT is negative, search backwards.
 
    If we find COUNT instances, set *SHORTAGE to zero, and return the
-   position of the COUNTth character.
+   position after the COUNTth match.  Note that for reverse motion
+   this is not the same as the usual convention for Emacs motion commands.
 
    If we don't find COUNT instances before reaching the end of the
    buffer (or the beginning, if scanning backwards), set *SHORTAGE to
@@ -344,31 +349,58 @@ find_next_newline (from, cnt)
   return (scan_buffer ('\n', from, cnt, (int *) 0));
 }
 \f
+Lisp_Object skip_chars ();
+
 DEFUN ("skip-chars-forward", Fskip_chars_forward, Sskip_chars_forward, 1, 2, 0,
   "Move point forward, stopping before a char not in CHARS, or at position LIM.\n\
 CHARS is like the inside of a `[...]' in a regular expression\n\
 except that `]' is never special and `\\' quotes `^', `-' or `\\'.\n\
 Thus, with arg \"a-zA-Z\", this skips letters stopping before first nonletter.\n\
-With arg \"^a-zA-Z\", skips nonletters stopping before first letter.")
+With arg \"^a-zA-Z\", skips nonletters stopping before first letter.\n\
+Returns the distance traveled, either zero or positive.")
   (string, lim)
      Lisp_Object string, lim;
 {
-  skip_chars (1, string, lim);
-  return Qnil;
+  return skip_chars (1, 0, string, lim);
 }
 
 DEFUN ("skip-chars-backward", Fskip_chars_backward, Sskip_chars_backward, 1, 2, 0,
   "Move point backward, stopping after a char not in CHARS, or at position LIM.\n\
-See `skip-chars-forward' for details.")
+See `skip-chars-forward' for details.\n\
+Returns the distance traveled, either zero or negative.")
   (string, lim)
      Lisp_Object string, lim;
 {
-  skip_chars (0, string, lim);
-  return Qnil;
+  return skip_chars (0, 0, string, lim);
+}
+
+DEFUN ("skip-syntax-forward", Fskip_syntax_forward, Sskip_syntax_forward, 1, 2, 0,
+  "Move point forward across chars in specified syntax classes.\n\
+SYNTAX is a string of syntax code characters.\n\
+Stop before a char whose syntax is not in SYNTAX, or at position LIM.\n\
+If SYNTAX starts with ^, skip characters whose syntax is NOT in SYNTAX.\n\
+This function returns the distance traveled, either zero or positive.")
+  (syntax, lim)
+     Lisp_Object syntax, lim;
+{
+  return skip_chars (1, 1, syntax, lim);
 }
 
-skip_chars (forwardp, string, lim)
-     int forwardp;
+DEFUN ("skip-syntax-backward", Fskip_syntax_backward, Sskip_syntax_backward, 1, 2, 0,
+  "Move point backward across chars in specified syntax classes.\n\
+SYNTAX is a string of syntax code characters.\n\
+Stop on reaching a char whose syntax is not in SYNTAX, or at position LIM.\n\
+If SYNTAX starts with ^, skip characters whose syntax is NOT in SYNTAX.\n\
+This function returns the distance traveled, either zero or negative.")
+  (syntax, lim)
+     Lisp_Object syntax, lim;
+{
+  return skip_chars (0, 1, syntax, lim);
+}
+
+Lisp_Object
+skip_chars (forwardp, syntaxp, string, lim)
+     int forwardp, syntaxp;
      Lisp_Object string, lim;
 {
   register unsigned char *p, *pend;
@@ -401,29 +433,36 @@ skip_chars (forwardp, string, lim)
       negate = 1; p++;
     }
 
-  /* Find the characters specified and set their elements of fastmap.  */
+  /* Find the characters specified and set their elements of fastmap.
+     If syntaxp, each character counts as itself.
+     Otherwise, handle backslashes and ranges specially  */
 
   while (p != pend)
     {
       c = *p++;
-      if (c == '\\')
-        {
-         if (p == pend) break;
-         c = *p++;
-       }
-      if (p != pend && *p == '-')
+      if (syntaxp)
+       fastmap[c] = 1;
+      else
        {
-         p++;
-         if (p == pend) break;
-         while (c <= *p)
+         if (c == '\\')
+           {
+             if (p == pend) break;
+             c = *p++;
+           }
+         if (p != pend && *p == '-')
            {
-             fastmap[c] = 1;
-             c++;
+             p++;
+             if (p == pend) break;
+             while (c <= *p)
+               {
+                 fastmap[c] = 1;
+                 c++;
+               }
+             p++;
            }
-         p++;
+         else
+           fastmap[c] = 1;
        }
-      else
-       fastmap[c] = 1;
     }
 
   /* If ^ was the first character, complement the fastmap. */
@@ -432,18 +471,43 @@ skip_chars (forwardp, string, lim)
     for (i = 0; i < sizeof fastmap; i++)
       fastmap[i] ^= 1;
 
-  immediate_quit = 1;
-  if (forwardp)
-    {
-      while (point < XINT (lim) && fastmap[FETCH_CHAR (point)])
-       SET_PT (point + 1);
-    }
-  else
-    {
-      while (point > XINT (lim) && fastmap[FETCH_CHAR (point - 1)])
-       SET_PT (point - 1);
-    }
-  immediate_quit = 0;
+  {
+    int start_point = point;
+
+    immediate_quit = 1;
+    if (syntaxp)
+      {
+
+       if (forwardp)
+         {
+           while (point < XINT (lim)
+                  && fastmap[(unsigned char) syntax_code_spec[(int) SYNTAX (FETCH_CHAR (point))]])
+             SET_PT (point + 1);
+         }
+       else
+         {
+           while (point > XINT (lim)
+                  && fastmap[(unsigned char) syntax_code_spec[(int) SYNTAX (FETCH_CHAR (point - 1))]])
+             SET_PT (point - 1);
+         }
+      }
+    else
+      {
+       if (forwardp)
+         {
+           while (point < XINT (lim) && fastmap[FETCH_CHAR (point)])
+             SET_PT (point + 1);
+         }
+       else
+         {
+           while (point > XINT (lim) && fastmap[FETCH_CHAR (point - 1)])
+             SET_PT (point - 1);
+         }
+      }
+    immediate_quit = 0;
+
+    return make_number (point - start_point);
+  }
 }
 \f
 /* Subroutines of Lisp buffer search functions. */
@@ -493,8 +557,14 @@ search_command (string, bound, noerror, count, direction, RE)
          if (lim < BEGV || lim > ZV)
            abort ();
          SET_PT (lim);
+         return Qnil;
+#if 0 /* This would be clean, but maybe programs depend on
+        a value of nil here.  */
+         np = lim;
+#endif
        }
-      return Qnil;
+      else
+       return Qnil;
     }
 
   if (np < BEGV || np > ZV)
@@ -539,7 +609,11 @@ search_buffer (string, pos, lim, n, RE, trt, inverse_trt)
   int s1, s2;
 
   /* Null string is found at starting position.  */
-  if (!len)
+  if (len == 0)
+    return pos;
+
+  /* Searching 0 times means don't move.  */
+  if (n == 0)
     return pos;
 
   if (RE)
@@ -593,10 +667,11 @@ search_buffer (string, pos, lim, n, RE, trt, inverse_trt)
        }
       while (n < 0)
        {
-         int val = re_search_2 (&searchbuf, (char *) p1, s1, (char *) p2, s2,
-                                pos - BEGV, lim - pos, &search_regs,
-                                /* Don't allow match past current point */
-                                pos - BEGV);
+         int val;
+         val = re_search_2 (&searchbuf, (char *) p1, s1, (char *) p2, s2,
+                            pos - BEGV, lim - pos, &search_regs,
+                            /* Don't allow match past current point */
+                            pos - BEGV);
          if (val == -2)
            matcher_overflow ();
          if (val >= 0)
@@ -621,9 +696,10 @@ search_buffer (string, pos, lim, n, RE, trt, inverse_trt)
        }
       while (n > 0)
        {
-         int val = re_search_2 (&searchbuf, (char *) p1, s1, (char *) p2, s2,
-                                pos - BEGV, lim - pos, &search_regs,
-                                lim - BEGV);
+         int val;
+         val = re_search_2 (&searchbuf, (char *) p1, s1, (char *) p2, s2,
+                            pos - BEGV, lim - pos, &search_regs,
+                            lim - BEGV);
          if (val == -2)
            matcher_overflow ();
          if (val >= 0)
@@ -816,9 +892,11 @@ search_buffer (string, pos, lim, n, RE, trt, inverse_trt)
                            (regoff_t *) xmalloc (2 * sizeof (regoff_t));
                          ends =
                            (regoff_t *) xmalloc (2 * sizeof (regoff_t));
+                         BLOCK_INPUT;
                          re_set_registers (&searchbuf,
                                            &search_regs,
                                            2, starts, ends);
+                         UNBLOCK_INPUT;
                        }
 
                      search_regs.start[0]
@@ -858,7 +936,7 @@ search_buffer (string, pos, lim, n, RE, trt, inverse_trt)
                  while ((limit - pos) * direction >= 0)
                    pos += BM_tab[FETCH_CHAR(pos)];
                  /* now run the same tests to distinguish going off the */
-                 /* end, a match or a phoney match. */
+                 /* end, a match or a phony match. */
                  if ((pos - limit) * direction <= len)
                    break;      /* ran off the end */
                  /* Found what might be a match.
@@ -891,9 +969,11 @@ search_buffer (string, pos, lim, n, RE, trt, inverse_trt)
                            (regoff_t *) xmalloc (2 * sizeof (regoff_t));
                          ends =
                            (regoff_t *) xmalloc (2 * sizeof (regoff_t));
+                         BLOCK_INPUT;
                          re_set_registers (&searchbuf,
                                            &search_regs,
                                            2, starts, ends);
+                         UNBLOCK_INPUT;
                        }
 
                      search_regs.start[0]
@@ -1081,7 +1161,8 @@ Leaves point at end of replacement text.")
   enum { nochange, all_caps, cap_initial } case_action;
   register int pos, last;
   int some_multiletter_word;
-  int some_letter = 0;
+  int some_lowercase;
+  int some_uppercase_initial;
   register int c, prevc;
   int inslen;
 
@@ -1096,8 +1177,8 @@ Leaves point at end of replacement text.")
   if (search_regs.start[0] < BEGV
       || search_regs.start[0] > search_regs.end[0]
       || search_regs.end[0] > ZV)
-    args_out_of_range(make_number (search_regs.start[0]),
-                     make_number (search_regs.end[0]));
+    args_out_of_range (make_number (search_regs.start[0]),
+                      make_number (search_regs.end[0]));
 
   if (NILP (fixedcase))
     {
@@ -1110,6 +1191,8 @@ Leaves point at end of replacement text.")
       /* some_multiletter_word is set nonzero if any original word
         is more than one letter long. */
       some_multiletter_word = 0;
+      some_lowercase = 0;
+      some_uppercase_initial = 0;
 
       for (pos = search_regs.start[0]; pos < last; pos++)
        {
@@ -1118,36 +1201,39 @@ Leaves point at end of replacement text.")
            {
              /* Cannot be all caps if any original char is lower case */
 
-             case_action = cap_initial;
+             some_lowercase = 1;
              if (SYNTAX (prevc) != Sword)
-               {
-                 /* Cannot even be cap initials
-                    if some original initial is lower case */
-                 case_action = nochange;
-                 break;
-               }
+               ;
              else
                some_multiletter_word = 1;
            }
          else if (!NOCASEP (c))
            {
-             some_letter = 1;
-             if (!some_multiletter_word && SYNTAX (prevc) == Sword)
+             if (SYNTAX (prevc) != Sword)
+               some_uppercase_initial = 1;
+             else
                some_multiletter_word = 1;
            }
 
          prevc = c;
        }
 
-      /* Do not make new text all caps
-        if the original text contained only single letter words. */
-      if (case_action == all_caps && !some_multiletter_word)
+      /* Convert to all caps if the old text is all caps
+        and has at least one multiletter word.  */
+      if (! some_lowercase && some_multiletter_word)
+       case_action = all_caps;
+      /* Capitalize each word, if the old text has a capitalized word.  */
+      else if (some_uppercase_initial)
        case_action = cap_initial;
-
-      if (!some_letter) case_action = nochange;
+      else
+       case_action = nochange;
     }
 
-  SET_PT (search_regs.end[0]);
+  /* We insert the replacement text before the old text, and then
+     delete the original text.  This means that markers at the
+     beginning or end of the original will float to the corresponding
+     position in the replacement.  */
+  SET_PT (search_regs.start[0]);
   if (!NILP (literal))
     Finsert (1, &string);
   else
@@ -1157,20 +1243,24 @@ Leaves point at end of replacement text.")
 
       for (pos = 0; pos < XSTRING (string)->size; pos++)
        {
+         int offset = point - search_regs.start[0];
+
          c = XSTRING (string)->data[pos];
          if (c == '\\')
            {
              c = XSTRING (string)->data[++pos];
              if (c == '&')
-               Finsert_buffer_substring (Fcurrent_buffer (),
-                                         make_number (search_regs.start[0]),
-                                         make_number (search_regs.end[0]));
+               Finsert_buffer_substring
+                 (Fcurrent_buffer (),
+                  make_number (search_regs.start[0] + offset),
+                  make_number (search_regs.end[0] + offset));
              else if (c >= '1' && c <= search_regs.num_regs + '0')
                {
                  if (search_regs.start[c - '0'] >= 1)
-                   Finsert_buffer_substring (Fcurrent_buffer (),
-                                             make_number (search_regs.start[c - '0']),
-                                             make_number (search_regs.end[c - '0']));
+                   Finsert_buffer_substring
+                     (Fcurrent_buffer (),
+                      make_number (search_regs.start[c - '0'] + offset),
+                      make_number (search_regs.end[c - '0'] + offset));
                }
              else
                insert_char (c);
@@ -1181,8 +1271,8 @@ Leaves point at end of replacement text.")
       UNGCPRO;
     }
 
-  inslen = point - (search_regs.end[0]);
-  del_range (search_regs.start[0], search_regs.end[0]);
+  inslen = point - (search_regs.start[0]);
+  del_range (search_regs.start[0] + inslen, search_regs.end[0] + inslen);
 
   if (case_action == all_caps)
     Fupcase_region (make_number (point - inslen), make_number (point));
@@ -1293,7 +1383,7 @@ LIST should have been created by calling `match-data' previously.")
   register Lisp_Object marker;
 
   if (!CONSP (list) && !NILP (list))
-    list = wrong_type_argument (Qconsp, list, 0);
+    list = wrong_type_argument (Qconsp, list);
 
   /* Unless we find a marker with a buffer in LIST, assume that this 
      match data came from a string.  */
@@ -1301,7 +1391,7 @@ LIST should have been created by calling `match-data' previously.")
 
   /* Allocate registers if they don't already exist.  */
   {
-    int length = Flength (list) / 2;
+    int length = XFASTINT (Flength (list)) / 2;
 
     if (length > search_regs.num_regs)
       {
@@ -1322,8 +1412,10 @@ LIST should have been created by calling `match-data' previously.")
                                       length * sizeof (regoff_t));
          }
 
+       BLOCK_INPUT;
        re_set_registers (&searchbuf, &search_regs, length,
                          search_regs.start, search_regs.end);
+       UNBLOCK_INPUT;
       }
   }
 
@@ -1430,6 +1522,8 @@ syms_of_search ()
   defsubr (&Slooking_at);
   defsubr (&Sskip_chars_forward);
   defsubr (&Sskip_chars_backward);
+  defsubr (&Sskip_syntax_forward);
+  defsubr (&Sskip_syntax_backward);
   defsubr (&Ssearch_forward);
   defsubr (&Ssearch_backward);
   defsubr (&Sword_search_forward);