]> code.delx.au - gnu-emacs/blobdiff - src/syntax.c
Backport revisions 2011-04-24T05:30:24Z!eggert@cs.ucla.edu..2011-04-25T19:40:22Z...
[gnu-emacs] / src / syntax.c
index 9161512a15bb1cfb672f75e46d315219c3f316d4..bfdf0e5ee6d6c65f412326cb3d426bf72d32530d 100644 (file)
@@ -1,14 +1,14 @@
 /* GNU Emacs routines to deal with syntax tables; also word and list parsing.
    Copyright (C) 1985, 1987, 1993, 1994, 1995, 1997, 1998, 1999, 2001,
-                 2002, 2003, 2004, 2005, 2006, 2007
+                 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
                  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,17 +16,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
 #include <config.h>
 #include <ctype.h>
+#include <setjmp.h>
 #include "lisp.h"
 #include "commands.h"
 #include "buffer.h"
-#include "charset.h"
+#include "character.h"
 #include "keymap.h"
 #include "regex.h"
 
@@ -67,18 +66,20 @@ int open_paren_in_column_0_is_defun_start;
 
 struct lisp_parse_state
   {
-    int depth;         /* Depth at end of parsing.  */
-    int instring;      /* -1 if not within string, else desired terminator.  */
-    int incomment;     /* -1 if in unnestable comment else comment nesting */
-    int comstyle;      /* comment style a=0, or b=1, or ST_COMMENT_STYLE.  */
-    int quoted;                /* Nonzero if just after an escape char at end of parsing */
-    int thislevelstart;        /* Char number of most recent start-of-expression at current level */
-    int prevlevelstart; /* Char number of start of containing expression */
-    int location;      /* Char number at which parsing stopped.  */
-    int mindepth;      /* Minimum depth seen while scanning.  */
-    int comstr_start;  /* Position just after last comment/string starter.  */
-    Lisp_Object levelstarts;   /* Char numbers of starts-of-expression
-                                  of levels (starting from outermost).  */
+    int depth;    /* Depth at end of parsing.  */
+    int instring;  /* -1 if not within string, else desired terminator.  */
+    int incomment; /* -1 if in unnestable comment else comment nesting */
+    int comstyle;  /* comment style a=0, or b=1, or ST_COMMENT_STYLE.  */
+    int quoted;           /* Nonzero if just after an escape char at end of parsing */
+    int mindepth;  /* Minimum depth seen while scanning.  */
+    /* Char number of most recent start-of-expression at current level */
+    EMACS_INT thislevelstart;
+    /* Char number of start of containing expression */
+    EMACS_INT prevlevelstart;
+    EMACS_INT location;             /* Char number at which parsing stopped.  */
+    EMACS_INT comstr_start;  /* Position of last comment/string starter.  */
+    Lisp_Object levelstarts; /* Char numbers of starts-of-expression
+                               of levels (starting from outermost).  */
   };
 \f
 /* These variables are a cache for finding the start of a defun.
@@ -89,22 +90,19 @@ struct lisp_parse_state
    find_start_begv is the BEGV value when it was found.
    find_start_modiff is the value of MODIFF when it was found.  */
 
-static int find_start_pos;
-static int find_start_value;
-static int find_start_value_byte;
+static EMACS_INT find_start_pos;
+static EMACS_INT find_start_value;
+static EMACS_INT find_start_value_byte;
 static struct buffer *find_start_buffer;
-static int find_start_begv;
+static EMACS_INT find_start_begv;
 static int find_start_modiff;
 
 
-static int find_defun_start P_ ((int, int));
-static int back_comment P_ ((EMACS_INT, EMACS_INT, EMACS_INT, int, int,
-                            EMACS_INT *, EMACS_INT *));
-static int char_quoted P_ ((int, int));
-static Lisp_Object skip_chars P_ ((int, int, Lisp_Object, Lisp_Object, int));
+static Lisp_Object skip_chars P_ ((int, Lisp_Object, Lisp_Object, int));
+static Lisp_Object skip_syntaxes P_ ((int, Lisp_Object, Lisp_Object));
 static Lisp_Object scan_lists P_ ((EMACS_INT, EMACS_INT, EMACS_INT, int));
 static void scan_sexps_forward P_ ((struct lisp_parse_state *,
-                                   int, int, int, int,
+                                   EMACS_INT, EMACS_INT, EMACS_INT, int,
                                    int, Lisp_Object, int));
 static int in_classes P_ ((int, Lisp_Object));
 \f
@@ -291,27 +289,24 @@ update_syntax_table (charpos, count, init, object)
    or after.  On return global syntax data is good for lookup at CHARPOS. */
 
 static int
-char_quoted (charpos, bytepos)
-     register int charpos, bytepos;
+char_quoted (EMACS_INT charpos, EMACS_INT bytepos)
 {
   register enum syntaxcode code;
-  register int beg = BEGV;
+  register EMACS_INT beg = BEGV;
   register int quoted = 0;
-  int orig = charpos;
-
-  DEC_BOTH (charpos, bytepos);
+  EMACS_INT orig = charpos;
 
-  while (charpos >= beg)
+  while (charpos > beg)
     {
       int c;
+      DEC_BOTH (charpos, bytepos);
 
       UPDATE_SYNTAX_TABLE_BACKWARD (charpos);
-      c = FETCH_CHAR (bytepos);
+      c = FETCH_CHAR_AS_MULTIBYTE (bytepos);
       code = SYNTAX (c);
       if (! (code == Scharquote || code == Sescape))
        break;
 
-      DEC_BOTH (charpos, bytepos);
       quoted = !quoted;
     }
 
@@ -322,9 +317,9 @@ char_quoted (charpos, bytepos)
 /* Return the bytepos one character after BYTEPOS.
    We assume that BYTEPOS is not at the end of the buffer.  */
 
-INLINE int
+INLINE EMACS_INT
 inc_bytepos (bytepos)
-     int bytepos;
+     EMACS_INT bytepos;
 {
   if (NILP (current_buffer->enable_multibyte_characters))
     return bytepos + 1;
@@ -336,9 +331,9 @@ inc_bytepos (bytepos)
 /* Return the bytepos one character before BYTEPOS.
    We assume that BYTEPOS is not at the start of the buffer.  */
 
-INLINE int
+INLINE EMACS_INT
 dec_bytepos (bytepos)
-     int bytepos;
+     EMACS_INT bytepos;
 {
   if (NILP (current_buffer->enable_multibyte_characters))
     return bytepos - 1;
@@ -347,7 +342,7 @@ dec_bytepos (bytepos)
   return bytepos;
 }
 \f
-/* Return a defun-start position before before POS and not too far before.
+/* Return a defun-start position before POS and not too far before.
    It should be the last one before POS, or nearly the last.
 
    When open_paren_in_column_0_is_defun_start is nonzero,
@@ -361,11 +356,11 @@ dec_bytepos (bytepos)
    valid on return from the subroutine, so the caller should explicitly
    update the global data.  */
 
-static int
+static EMACS_INT
 find_defun_start (pos, pos_byte)
-     int pos, pos_byte;
+     EMACS_INT pos, pos_byte;
 {
-  int opoint = PT, opoint_byte = PT_BYTE;
+  EMACS_INT opoint = PT, opoint_byte = PT_BYTE;
 
   if (!open_paren_in_column_0_is_defun_start)
     {
@@ -398,11 +393,11 @@ find_defun_start (pos, pos_byte)
 
       /* Open-paren at start of line means we may have found our
         defun-start.  */
-      c = FETCH_CHAR (PT_BYTE);
+      c = FETCH_CHAR_AS_MULTIBYTE (PT_BYTE);
       if (SYNTAX (c) == Sopen)
        {
          SETUP_SYNTAX_TABLE (PT + 1, -1);      /* Try again... */
-         c = FETCH_CHAR (PT_BYTE);
+         c = FETCH_CHAR_AS_MULTIBYTE (PT_BYTE);
          if (SYNTAX (c) == Sopen)
            break;
          /* Now fallback to the default value.  */
@@ -494,14 +489,14 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
      inside another comment).
      Test case:  { a (* b } c (* d *) */
   int comment_lossage = 0;
-  int comment_end = from;
-  int comment_end_byte = from_byte;
-  int comstart_pos = 0;
-  int comstart_byte;
+  EMACS_INT comment_end = from;
+  EMACS_INT comment_end_byte = from_byte;
+  EMACS_INT comstart_pos = 0;
+  EMACS_INT comstart_byte;
   /* Place where the containing defun starts,
      or 0 if we didn't come across it yet.  */
-  int defun_start = 0;
-  int defun_start_byte = 0;
+  EMACS_INT defun_start = 0;
+  EMACS_INT defun_start_byte = 0;
   register enum syntaxcode code;
   int nesting = 1;             /* current comment nesting */
   int c;
@@ -517,13 +512,14 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
     {
       int temp_byte, prev_syntax;
       int com2start, com2end;
+      int comstart;
 
       /* Move back and examine a character.  */
       DEC_BOTH (from, from_byte);
       UPDATE_SYNTAX_TABLE_BACKWARD (from);
 
       prev_syntax = syntax;
-      c = FETCH_CHAR (from_byte);
+      c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
       syntax = SYNTAX_WITH_FLAGS (c);
       code = SYNTAX (c);
 
@@ -535,7 +531,8 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
                       || SYNTAX_FLAGS_COMMENT_NESTED (syntax)) == comnested);
       com2end = (SYNTAX_FLAGS_COMEND_FIRST (syntax)
                 && SYNTAX_FLAGS_COMEND_SECOND (prev_syntax));
-
+      comstart = (com2start || code == Scomment);
+      
       /* Nasty cases with overlapping 2-char comment markers:
         - snmp-mode: -- c -- foo -- c --
                      --- c --
@@ -546,15 +543,16 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
                      ///   */
 
       /* If a 2-char comment sequence partly overlaps with another,
-        we don't try to be clever.  */
-      if (from > stop && (com2end || com2start))
+        we don't try to be clever.  E.g. |*| in C, or }% in modes that
+        have %..\n and %{..}%.  */
+      if (from > stop && (com2end || comstart))
        {
          int next = from, next_byte = from_byte, next_c, next_syntax;
          DEC_BOTH (next, next_byte);
          UPDATE_SYNTAX_TABLE_BACKWARD (next);
-         next_c = FETCH_CHAR (next_byte);
+         next_c = FETCH_CHAR_AS_MULTIBYTE (next_byte);
          next_syntax = SYNTAX_WITH_FLAGS (next_c);
-         if (((com2start || comnested)
+         if (((comstart || comnested)
               && SYNTAX_FLAGS_COMEND_SECOND (syntax)
               && SYNTAX_FLAGS_COMEND_FIRST (next_syntax))
              || ((com2end || comnested)
@@ -855,33 +853,10 @@ char syntax_code_spec[16] =
 static Lisp_Object Vsyntax_code_object;
 
 \f
-/* Look up the value for CHARACTER in syntax table TABLE's parent
-   and its parents.  SYNTAX_ENTRY calls this, when TABLE itself has nil
-   for CHARACTER.  It's actually used only when not compiled with GCC.  */
-
-Lisp_Object
-syntax_parent_lookup (table, character)
-     Lisp_Object table;
-     int character;
-{
-  Lisp_Object value;
-
-  while (1)
-    {
-      table = XCHAR_TABLE (table)->parent;
-      if (NILP (table))
-       return Qnil;
-
-      value = XCHAR_TABLE (table)->contents[character];
-      if (!NILP (value))
-       return value;
-    }
-}
-
 DEFUN ("char-syntax", Fchar_syntax, Schar_syntax, 1, 1, 0,
        doc: /* Return the syntax code of CHARACTER, described by a character.
-For example, if CHARACTER is a word constituent,
-the character `w' is returned.
+For example, if CHARACTER is a word constituent, the
+character `w' (119) is returned.
 The characters that correspond to various syntax codes
 are listed in the documentation of `modify-syntax-entry'.  */)
      (character)
@@ -939,8 +914,7 @@ text property.  */)
   if (*p)
     {
       int len;
-      int character = (STRING_CHAR_AND_LENGTH
-                      (p, SBYTES (string) - 1, len));
+      int character = STRING_CHAR_AND_LENGTH (p, len);
       XSETINT (match, character);
       if (XFASTINT (match) == ' ')
        match = Qnil;
@@ -982,7 +956,7 @@ text property.  */)
        break;
       }
 
-  if (val < XVECTOR (Vsyntax_code_object)->size && NILP (match))
+  if (val < XVECTOR_SIZE (Vsyntax_code_object) && NILP (match))
     return XVECTOR (Vsyntax_code_object)->contents[val];
   else
     /* Since we can't use a shared object, let's make a new one.  */
@@ -996,6 +970,8 @@ DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3,
        doc: /* Set syntax for character CHAR according to string NEWENTRY.
 The syntax is changed only for table SYNTAX-TABLE, which defaults to
  the current buffer's syntax table.
+CHAR may be a cons (MIN . MAX), in which case, syntaxes of all characters
+in the range MIN to MAX are changed.
 The first character of NEWENTRY should be one of the following:
   Space or -  whitespace syntax.    w   word constituent.
   _           symbol constituent.   .   punctuation.
@@ -1028,18 +1004,28 @@ this flag:
  p means CHAR is a prefix character for `backward-prefix-chars';
    such characters are treated as whitespace when they occur
    between expressions.
-usage: (modify-syntax-entry CHAR NEWENTRY &optional SYNTAX-TABLE) */)
+usage: (modify-syntax-entry CHAR NEWENTRY &optional SYNTAX-TABLE)  */)
      (c, newentry, syntax_table)
      Lisp_Object c, newentry, syntax_table;
 {
-  CHECK_NUMBER (c);
+  if (CONSP (c))
+    {
+      CHECK_CHARACTER_CAR (c);
+      CHECK_CHARACTER_CDR (c);
+    }
+  else
+    CHECK_CHARACTER (c);
 
   if (NILP (syntax_table))
     syntax_table = current_buffer->syntax_table;
   else
     check_syntax_table (syntax_table);
 
-  SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), Fstring_to_syntax (newentry));
+  newentry = Fstring_to_syntax (newentry);
+  if (CONSP (c))
+    SET_RAW_SYNTAX_ENTRY_RANGE (syntax_table, c, newentry);
+  else
+    SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), newentry);
 
   /* We clear the regexp cache, since character classes can now have
      different values from those in the compiled regexps.*/
@@ -1198,6 +1184,10 @@ DEFUN ("internal-describe-syntax-value", Finternal_describe_syntax_value,
 \f
 int parse_sexp_ignore_comments;
 
+/* Char-table of functions that find the next or previous word
+   boundary.  */
+Lisp_Object Vfind_word_boundary_function_table;
+
 /* Return the position across COUNT words from FROM.
    If that many words cannot be found before the end of the buffer, return 0.
    COUNT negative means scan backward and stop at word beginning.  */
@@ -1211,6 +1201,7 @@ scan_words (from, count)
   register int from_byte = CHAR_TO_BYTE (from);
   register enum syntaxcode code;
   int ch0, ch1;
+  Lisp_Object func, script, pos;
 
   immediate_quit = 1;
   QUIT;
@@ -1227,7 +1218,7 @@ scan_words (from, count)
              return 0;
            }
          UPDATE_SYNTAX_TABLE_FORWARD (from);
-         ch0 = FETCH_CHAR (from_byte);
+         ch0 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
          code = SYNTAX (ch0);
          INC_BOTH (from, from_byte);
          if (words_include_escapes
@@ -1238,18 +1229,33 @@ scan_words (from, count)
        }
       /* Now CH0 is a character which begins a word and FROM is the
          position of the next character.  */
-      while (1)
+      func = CHAR_TABLE_REF (Vfind_word_boundary_function_table, ch0);
+      if (! NILP (Ffboundp (func)))
        {
-         if (from == end) break;
-         UPDATE_SYNTAX_TABLE_FORWARD (from);
-         ch1 = FETCH_CHAR (from_byte);
-         code = SYNTAX (ch1);
-         if (!(words_include_escapes
-               && (code == Sescape || code == Scharquote)))
-           if (code != Sword || WORD_BOUNDARY_P (ch0, ch1))
-             break;
-         INC_BOTH (from, from_byte);
-         ch0 = ch1;
+         pos = call2 (func, make_number (from - 1), make_number (end));
+         if (INTEGERP (pos) && XINT (pos) > from)
+           {
+             from = XINT (pos);
+             from_byte = CHAR_TO_BYTE (from);
+           }
+       }
+      else
+       {
+         script = CHAR_TABLE_REF (Vchar_script_table, ch0);
+         while (1)
+           {
+             if (from == end) break;
+             UPDATE_SYNTAX_TABLE_FORWARD (from);
+             ch1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
+             code = SYNTAX (ch1);
+             if ((code != Sword
+                  && (! words_include_escapes
+                      || (code != Sescape && code != Scharquote)))
+                 || word_boundary_p (ch0, ch1))
+               break;
+             INC_BOTH (from, from_byte);
+             ch0 = ch1;
+           }
        }
       count--;
     }
@@ -1264,7 +1270,7 @@ scan_words (from, count)
            }
          DEC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_BACKWARD (from);
-         ch1 = FETCH_CHAR (from_byte);
+         ch1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
          code = SYNTAX (ch1);
          if (words_include_escapes
              && (code == Sescape || code == Scharquote))
@@ -1274,22 +1280,37 @@ scan_words (from, count)
        }
       /* Now CH1 is a character which ends a word and FROM is the
          position of it.  */
-      while (1)
+      func = CHAR_TABLE_REF (Vfind_word_boundary_function_table, ch1);
+      if (! NILP (Ffboundp (func)))
+       {
+         pos = call2 (func, make_number (from), make_number (beg));
+         if (INTEGERP (pos) && XINT (pos) < from)
+           {
+             from = XINT (pos);
+             from_byte = CHAR_TO_BYTE (from);
+           }
+       }
+      else
        {
-         if (from == beg)
-           break;
-         DEC_BOTH (from, from_byte);
-         UPDATE_SYNTAX_TABLE_BACKWARD (from);
-         ch0 = FETCH_CHAR (from_byte);
-         code = SYNTAX (ch0);
-         if (!(words_include_escapes
-               && (code == Sescape || code == Scharquote)))
-           if (code != Sword || WORD_BOUNDARY_P (ch0, ch1))
-             {
-               INC_BOTH (from, from_byte);
+         script = CHAR_TABLE_REF (Vchar_script_table, ch1);
+         while (1)
+           {
+             if (from == beg)
                break;
-             }
-         ch1 = ch0;
+             DEC_BOTH (from, from_byte);
+             UPDATE_SYNTAX_TABLE_BACKWARD (from);
+             ch0 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
+             code = SYNTAX (ch0);
+             if ((code != Sword
+                  && (! words_include_escapes
+                      || (code != Sescape && code != Scharquote)))
+                 || word_boundary_p (ch0, ch1))
+               {
+                 INC_BOTH (from, from_byte);
+                 break;
+               }
+             ch1 = ch0;
+           }
        }
       count++;
     }
@@ -1299,7 +1320,7 @@ scan_words (from, count)
   return from;
 }
 
-DEFUN ("forward-word", Fforward_word, Sforward_word, 0, 1, "p",
+DEFUN ("forward-word", Fforward_word, Sforward_word, 0, 1, "^p",
        doc: /* Move point forward ARG words (backward if ARG is negative).
 Normally returns t.
 If an edge of the buffer or a field boundary is reached, point is left there
@@ -1335,7 +1356,7 @@ DEFUN ("skip-chars-forward", Fskip_chars_forward, Sskip_chars_forward, 1, 2, 0,
        doc: /* Move point forward, stopping before a char not in STRING, or at pos LIM.
 STRING is like the inside of a `[...]' in a regular expression
 except that `]' is never special and `\\' quotes `^', `-' or `\\'
- (but not as the end of a range; quoting is never needed there).
+ (but not at the end of a range; quoting is never needed there).
 Thus, with arg "a-zA-Z", this skips letters stopping before first nonletter.
 With arg "^a-zA-Z", skips nonletters stopping before first letter.
 Char classes, e.g. `[:alpha:]', are supported.
@@ -1344,7 +1365,7 @@ Returns the distance traveled, either zero or positive.  */)
      (string, lim)
      Lisp_Object string, lim;
 {
-  return skip_chars (1, 0, string, lim, 1);
+  return skip_chars (1, string, lim, 1);
 }
 
 DEFUN ("skip-chars-backward", Fskip_chars_backward, Sskip_chars_backward, 1, 2, 0,
@@ -1354,7 +1375,7 @@ Returns the distance traveled, either zero or negative.  */)
      (string, lim)
      Lisp_Object string, lim;
 {
-  return skip_chars (0, 0, string, lim, 1);
+  return skip_chars (0, string, lim, 1);
 }
 
 DEFUN ("skip-syntax-forward", Fskip_syntax_forward, Sskip_syntax_forward, 1, 2, 0,
@@ -1366,7 +1387,7 @@ This function returns the distance traveled, either zero or positive.  */)
      (syntax, lim)
      Lisp_Object syntax, lim;
 {
-  return skip_chars (1, 1, syntax, lim, 0);
+  return skip_syntaxes (1, syntax, lim);
 }
 
 DEFUN ("skip-syntax-backward", Fskip_syntax_backward, Sskip_syntax_backward, 1, 2, 0,
@@ -1378,25 +1399,27 @@ This function returns the distance traveled, either zero or negative.  */)
      (syntax, lim)
      Lisp_Object syntax, lim;
 {
-  return skip_chars (0, 1, syntax, lim, 0);
+  return skip_syntaxes (0, syntax, lim);
 }
 
 static Lisp_Object
-skip_chars (forwardp, syntaxp, string, lim, handle_iso_classes)
-     int forwardp, syntaxp;
+skip_chars (forwardp, string, lim, handle_iso_classes)
+     int forwardp;
      Lisp_Object string, lim;
      int handle_iso_classes;
 {
   register unsigned int c;
   unsigned char fastmap[0400];
-  /* If SYNTAXP is 0, STRING may contain multi-byte form of characters
-     of which codes don't fit in FASTMAP.  In that case, set the
-     ranges of characters in CHAR_RANGES.  */
+  /* Store the ranges of non-ASCII characters.  */
   int *char_ranges;
   int n_char_ranges = 0;
   int negate = 0;
   register int i, i_byte;
-  int multibyte = !NILP (current_buffer->enable_multibyte_characters);
+  /* Set to 1 if the current buffer is multibyte and the region
+     contains non-ASCII chars.  */
+  int multibyte;
+  /* Set to 1 if STRING is multibyte and it contains non-ASCII
+     chars.  */
   int string_multibyte;
   int size_byte;
   const unsigned char *str;
@@ -1404,32 +1427,8 @@ skip_chars (forwardp, syntaxp, string, lim, handle_iso_classes)
   Lisp_Object iso_classes;
 
   CHECK_STRING (string);
-  char_ranges = (int *) alloca (SCHARS (string) * (sizeof (int)) * 2);
-  string_multibyte = STRING_MULTIBYTE (string);
-  str = SDATA (string);
-  size_byte = SBYTES (string);
   iso_classes = Qnil;
 
-  /* Adjust the multibyteness of the string to that of the buffer.  */
-  if (multibyte != string_multibyte)
-    {
-      int nbytes;
-
-      if (multibyte)
-       nbytes = count_size_as_multibyte (SDATA (string),
-                                         SCHARS (string));
-      else
-       nbytes = SCHARS (string);
-      if (nbytes != size_byte)
-       {
-         unsigned char *tmp = (unsigned char *) alloca (nbytes);
-         copy_text (SDATA (string), tmp, size_byte,
-                    string_multibyte, multibyte);
-         size_byte = nbytes;
-         str = tmp;
-       }
-    }
-
   if (NILP (lim))
     XSETINT (lim, forwardp ? ZV : BEGV);
   else
@@ -1441,10 +1440,16 @@ skip_chars (forwardp, syntaxp, string, lim, handle_iso_classes)
   if (XINT (lim) < BEGV)
     XSETFASTINT (lim, BEGV);
 
+  multibyte = (!NILP (current_buffer->enable_multibyte_characters)
+              && (XINT (lim) - PT != CHAR_TO_BYTE (XINT (lim)) - PT_BYTE));
+  string_multibyte = SBYTES (string) > SCHARS (string);
+
   bzero (fastmap, sizeof fastmap);
 
-  i_byte = 0;
+  str = SDATA (string);
+  size_byte = SBYTES (string);
 
+  i_byte = 0;
   if (i_byte < size_byte
       && SREF (string, 0) == '^')
     {
@@ -1452,21 +1457,23 @@ skip_chars (forwardp, syntaxp, string, lim, handle_iso_classes)
     }
 
   /* Find the characters specified and set their elements of fastmap.
-     If syntaxp, each character counts as itself.
-     Otherwise, handle backslashes and ranges specially.  */
+     Handle backslashes and ranges specially.
 
-  while (i_byte < size_byte)
+     If STRING contains non-ASCII characters, setup char_ranges for
+     them and use fastmap only for their leading codes.  */
+
+  if (! string_multibyte)
     {
-      c = STRING_CHAR_AND_LENGTH (str + i_byte, size_byte - i_byte, len);
-      i_byte += len;
+      int string_has_eight_bit = 0;
 
-      if (syntaxp)
-       fastmap[syntax_spec_code[c & 0377]] = 1;
-      else
+      /* At first setup fastmap.  */
+      while (i_byte < size_byte)
        {
+         c = str[i_byte++];
+
          if (handle_iso_classes && c == '['
              && i_byte < size_byte
-             && STRING_CHAR (str + i_byte, size_byte - i_byte) == ':')
+             && str[i_byte] == ':')
            {
              const unsigned char *class_beg = str + i_byte + 1;
              const unsigned char *class_end = class_beg;
@@ -1505,8 +1512,130 @@ skip_chars (forwardp, syntaxp, string, lim, handle_iso_classes)
              if (i_byte == size_byte)
                break;
 
-             c = STRING_CHAR_AND_LENGTH (str + i_byte,
-                                         size_byte - i_byte, len);
+             c = str[i_byte++];
+           }
+         /* Treat `-' as range character only if another character
+            follows.  */
+         if (i_byte + 1 < size_byte
+             && str[i_byte] == '-')
+           {
+             unsigned int c2;
+
+             /* Skip over the dash.  */
+             i_byte++;
+
+             /* Get the end of the range.  */
+             c2 = str[i_byte++];
+             if (c2 == '\\'
+                 && i_byte < size_byte)
+               c2 = str[i_byte++];
+
+             if (c <= c2)
+               {
+                 while (c <= c2)
+                   fastmap[c++] = 1;
+                 if (! ASCII_CHAR_P (c2))
+                   string_has_eight_bit = 1;
+               }
+           }
+         else
+           {
+             fastmap[c] = 1;
+             if (! ASCII_CHAR_P (c))
+               string_has_eight_bit = 1;
+           }
+       }
+
+      /* If the current range is multibyte and STRING contains
+        eight-bit chars, arrange fastmap and setup char_ranges for
+        the corresponding multibyte chars.  */
+      if (multibyte && string_has_eight_bit)
+       {
+         unsigned char fastmap2[0400];
+         int range_start_byte, range_start_char;
+
+         bcopy (fastmap2 + 0200, fastmap + 0200, 0200);
+         bzero (fastmap + 0200, 0200);
+         /* We are sure that this loop stops.  */
+         for (i = 0200; ! fastmap2[i]; i++);
+         c = BYTE8_TO_CHAR (i);
+         fastmap[CHAR_LEADING_CODE (c)] = 1;
+         range_start_byte = i;
+         range_start_char = c;
+         char_ranges = (int *) alloca (sizeof (int) * 128 * 2);
+         for (i = 129; i < 0400; i++)
+           {
+             c = BYTE8_TO_CHAR (i);
+             fastmap[CHAR_LEADING_CODE (c)] = 1;
+             if (i - range_start_byte != c - range_start_char)
+               {
+                 char_ranges[n_char_ranges++] = range_start_char;
+                 char_ranges[n_char_ranges++] = ((i - 1 - range_start_byte)
+                                                 + range_start_char);
+                 range_start_byte = i;
+                 range_start_char = c;
+               }
+           }
+         char_ranges[n_char_ranges++] = range_start_char;
+         char_ranges[n_char_ranges++] = ((i - 1 - range_start_byte)
+                                         + range_start_char);
+       }
+    }
+  else                         /* STRING is multibyte */
+    {
+      char_ranges = (int *) alloca (sizeof (int) * SCHARS (string) * 2);
+
+      while (i_byte < size_byte)
+       {
+         unsigned char leading_code;
+
+         leading_code = str[i_byte];
+         c = STRING_CHAR_AND_LENGTH (str + i_byte, len);
+         i_byte += len;
+
+         if (handle_iso_classes && c == '['
+             && i_byte < size_byte
+             && STRING_CHAR (str + i_byte) == ':')
+           {
+             const unsigned char *class_beg = str + i_byte + 1;
+             const unsigned char *class_end = class_beg;
+             const unsigned char *class_limit = str + size_byte - 2;
+             /* Leave room for the null.        */
+             unsigned char class_name[CHAR_CLASS_MAX_LENGTH + 1];
+             re_wctype_t cc;
+
+             if (class_limit - class_beg > CHAR_CLASS_MAX_LENGTH)
+               class_limit = class_beg + CHAR_CLASS_MAX_LENGTH;
+
+             while (class_end < class_limit
+                    && *class_end >= 'a' && *class_end <= 'z')
+               class_end++;
+
+             if (class_end == class_beg
+                 || *class_end != ':' || class_end[1] != ']')
+               goto not_a_class_name_multibyte;
+
+             bcopy (class_beg, class_name, class_end - class_beg);
+             class_name[class_end - class_beg] = 0;
+
+             cc = re_wctype (class_name);
+             if (cc == 0)
+               error ("Invalid ISO C character class");
+
+             iso_classes = Fcons (make_number (cc), iso_classes);
+
+             i_byte = class_end + 2 - str;
+             continue;
+           }
+
+       not_a_class_name_multibyte:
+         if (c == '\\')
+           {
+             if (i_byte == size_byte)
+               break;
+
+             leading_code = str[i_byte];
+             c = STRING_CHAR_AND_LENGTH (str + i_byte, len);
              i_byte += len;
            }
          /* Treat `-' as range character only if another character
@@ -1515,61 +1644,93 @@ skip_chars (forwardp, syntaxp, string, lim, handle_iso_classes)
              && str[i_byte] == '-')
            {
              unsigned int c2;
+             unsigned char leading_code2;
 
              /* Skip over the dash.  */
              i_byte++;
 
              /* Get the end of the range.  */
-             c2 = STRING_CHAR_AND_LENGTH (str + i_byte,
-                                          size_byte - i_byte, len);
+             leading_code2 = str[i_byte];
+             c2 = STRING_CHAR_AND_LENGTH (str + i_byte, len);
              i_byte += len;
 
-             if (SINGLE_BYTE_CHAR_P (c))
+             if (c2 == '\\'
+                 && i_byte < size_byte)
+               {
+                 leading_code2 = str[i_byte];
+                 c2 =STRING_CHAR_AND_LENGTH (str + i_byte, len);
+                 i_byte += len;
+               }
+
+             if (c > c2)
+               continue;
+             if (ASCII_CHAR_P (c))
                {
-                 if (! SINGLE_BYTE_CHAR_P (c2))
+                 while (c <= c2 && c < 0x80)
+                   fastmap[c++] = 1;
+                 leading_code = CHAR_LEADING_CODE (c);
+               }
+             if (! ASCII_CHAR_P (c))
+               {
+                 while (leading_code <= leading_code2)
+                   fastmap[leading_code++] = 1;
+                 if (c <= c2)
                    {
-                     /* Handle a range starting with a character of
-                        less than 256, and ending with a character of
-                        not less than 256.  Split that into two
-                        ranges, the low one ending at 0377, and the
-                        high one starting at the smallest character
-                        in the charset of C2 and ending at C2.  */
-                     int charset = CHAR_CHARSET (c2);
-                     int c1 = MAKE_CHAR (charset, 0, 0);
-
-                     char_ranges[n_char_ranges++] = c1;
+                     char_ranges[n_char_ranges++] = c;
                      char_ranges[n_char_ranges++] = c2;
-                     c2 = 0377;
-                   }
-                 while (c <= c2)
-                   {
-                     fastmap[c] = 1;
-                     c++;
                    }
                }
-             else if (c <= c2) /* Both C and C2 are multibyte char.  */
-               {
-                 char_ranges[n_char_ranges++] = c;
-                 char_ranges[n_char_ranges++] = c2;
-               }
            }
          else
            {
-             if (SINGLE_BYTE_CHAR_P (c))
+             if (ASCII_CHAR_P (c))
                fastmap[c] = 1;
              else
                {
+                 fastmap[leading_code] = 1;
                  char_ranges[n_char_ranges++] = c;
                  char_ranges[n_char_ranges++] = c;
                }
            }
        }
+
+      /* If the current range is unibyte and STRING contains non-ASCII
+        chars, arrange fastmap for the corresponding unibyte
+        chars.  */
+
+      if (! multibyte && n_char_ranges > 0)
+       {
+         bzero (fastmap + 0200, 0200);
+         for (i = 0; i < n_char_ranges; i += 2)
+           {
+             int c1 = char_ranges[i];
+             int c2 = char_ranges[i + 1];
+
+             for (; c1 <= c2; c1++)
+               {
+                 int b = CHAR_TO_BYTE_SAFE (c1);
+                 if (b >= 0)
+                   fastmap[b] = 1;
+               }
+           }
+       }
     }
 
   /* If ^ was the first character, complement the fastmap.  */
   if (negate)
-    for (i = 0; i < sizeof fastmap; i++)
-      fastmap[i] ^= 1;
+    {
+      if (! multibyte)
+       for (i = 0; i < sizeof fastmap; i++)
+         fastmap[i] ^= 1;
+      else
+       {
+         for (i = 0; i < 0200; i++)
+           fastmap[i] ^= 1;
+         /* All non-ASCII chars possibly match.  */
+         for (; i < sizeof fastmap; i++)
+           fastmap[i] = 1;
+       }
+    }
 
   {
     int start_point = PT;
@@ -1589,254 +1750,318 @@ skip_chars (forwardp, syntaxp, string, lim, handle_iso_classes)
       }
 
     immediate_quit = 1;
-    if (syntaxp)
+    /* This code may look up syntax tables using macros that rely on the
+       gl_state object.  To make sure this object is not out of date,
+       let's initialize it manually.
+       We ignore syntax-table text-properties for now, since that's
+       what we've done in the past.  */
+    SETUP_SYNTAX_TABLE (BEGV, 0);
+    if (forwardp)
       {
-        SETUP_SYNTAX_TABLE (pos, forwardp ? 1 : -1);
-       if (forwardp)
-         {
-           if (multibyte)
-             while (1)
-               {
-                 int nbytes;
+       if (multibyte)
+         while (1)
+           {
+             int nbytes;
 
-                 if (p >= stop)
-                   {
-                     if (p >= endp)
-                       break;
-                     p = GAP_END_ADDR;
-                     stop = endp;
-                   }
-                 c = STRING_CHAR_AND_LENGTH (p, MAX_MULTIBYTE_LENGTH, nbytes);
-                 if (! fastmap[(int) SYNTAX (c)])
+             if (p >= stop)
+               {
+                 if (p >= endp)
                    break;
-                 p += nbytes, pos++, pos_byte += nbytes;
-                 UPDATE_SYNTAX_TABLE_FORWARD (pos);
+                 p = GAP_END_ADDR;
+                 stop = endp;
                }
-           else
-             while (1)
+             c = STRING_CHAR_AND_LENGTH (p, nbytes);
+             if (! NILP (iso_classes) && in_classes (c, iso_classes))
                {
-                 if (p >= stop)
-                   {
-                     if (p >= endp)
-                       break;
-                     p = GAP_END_ADDR;
-                     stop = endp;
-                   }
-                 if (! fastmap[(int) SYNTAX (*p)])
+                 if (negate)
                    break;
-                 p++, pos++;
-                 UPDATE_SYNTAX_TABLE_FORWARD (pos);
+                 else
+                   goto fwd_ok;
                }
-         }
-       else
-         {
-           if (multibyte)
-             while (1)
-               {
-                 unsigned char *prev_p;
-                 int nbytes;
 
-                 if (p <= stop)
-                   {
-                     if (p <= endp)
-                       break;
-                     p = GPT_ADDR;
-                     stop = endp;
-                   }
-                 prev_p = p;
-                 while (--p >= stop && ! CHAR_HEAD_P (*p));
-                 PARSE_MULTIBYTE_SEQ (p, MAX_MULTIBYTE_LENGTH, nbytes);
-                 if (prev_p - p > nbytes)
-                   p = prev_p - 1, c = *p, nbytes = 1;
-                 else
-                   c = STRING_CHAR (p, MAX_MULTIBYTE_LENGTH);
-                 pos--, pos_byte -= nbytes;
-                 UPDATE_SYNTAX_TABLE_BACKWARD (pos);
-                 if (! fastmap[(int) SYNTAX (c)])
-                   {
-                     pos++;
-                     pos_byte += nbytes;
+             if (! fastmap[*p])
+               break;
+             if (! ASCII_CHAR_P (c))
+               {
+                 /* As we are looking at a multibyte character, we
+                    must look up the character in the table
+                    CHAR_RANGES.  If there's no data in the table,
+                    that character is not what we want to skip.  */
+
+                 /* The following code do the right thing even if
+                    n_char_ranges is zero (i.e. no data in
+                    CHAR_RANGES).  */
+                 for (i = 0; i < n_char_ranges; i += 2)
+                   if (c >= char_ranges[i] && c <= char_ranges[i + 1])
                      break;
-                   }
+                 if (!(negate ^ (i < n_char_ranges)))
+                   break;
                }
-           else
-             while (1)
+           fwd_ok:
+             p += nbytes, pos++, pos_byte += nbytes;
+           }
+       else
+         while (1)
+           {
+             if (p >= stop)
                {
-                 if (p <= stop)
-                   {
-                     if (p <= endp)
-                       break;
-                     p = GPT_ADDR;
-                     stop = endp;
-                   }
-                 UPDATE_SYNTAX_TABLE_BACKWARD (pos - 1);
-                 if (! fastmap[(int) SYNTAX (p[-1])])
+                 if (p >= endp)
                    break;
-                 p--, pos--;
+                 p = GAP_END_ADDR;
+                 stop = endp;
                }
-         }
+
+             if (!NILP (iso_classes) && in_classes (*p, iso_classes))
+               {
+                 if (negate)
+                   break;
+                 else
+                   goto fwd_unibyte_ok;
+               }
+
+             if (!fastmap[*p])
+               break;
+           fwd_unibyte_ok:
+             p++, pos++, pos_byte++;
+           }
       }
     else
       {
-       if (forwardp)
-         {
-           if (multibyte)
-             while (1)
-               {
-                 int nbytes;
-
-                 if (p >= stop)
-                   {
-                     if (p >= endp)
-                       break;
-                     p = GAP_END_ADDR;
-                     stop = endp;
-                   }
-                 c = STRING_CHAR_AND_LENGTH (p, MAX_MULTIBYTE_LENGTH, nbytes);
+       if (multibyte)
+         while (1)
+           {
+             unsigned char *prev_p;
 
-                 if (! NILP (iso_classes) && in_classes (c, iso_classes))
-                   {
-                     if (negate)
-                       break;
-                     else
-                       goto fwd_ok;
-                   }
+             if (p <= stop)
+               {
+                 if (p <= endp)
+                   break;
+                 p = GPT_ADDR;
+                 stop = endp;
+               }
+             prev_p = p;
+             while (--p >= stop && ! CHAR_HEAD_P (*p));
+             c = STRING_CHAR (p);
 
-                 if (SINGLE_BYTE_CHAR_P (c))
-                   {
-                     if (!fastmap[c])
-                       break;
-                   }
+             if (! NILP (iso_classes) && in_classes (c, iso_classes))
+               {
+                 if (negate)
+                   break;
                  else
-                   {
-                     /* If we are looking at a multibyte character,
-                        we must look up the character in the table
-                        CHAR_RANGES.  If there's no data in the
-                        table, that character is not what we want to
-                        skip.  */
-
-                     /* The following code do the right thing even if
-                        n_char_ranges is zero (i.e. no data in
-                        CHAR_RANGES).  */
-                     for (i = 0; i < n_char_ranges; i += 2)
-                       if (c >= char_ranges[i] && c <= char_ranges[i + 1])
-                         break;
-                     if (!(negate ^ (i < n_char_ranges)))
-                       break;
-                   }
-               fwd_ok:
-                 p += nbytes, pos++, pos_byte += nbytes;
+                   goto back_ok;
                }
-           else
-             while (1)
-               {
-                 if (p >= stop)
-                   {
-                     if (p >= endp)
-                       break;
-                     p = GAP_END_ADDR;
-                     stop = endp;
-                   }
 
-                 if (!NILP (iso_classes) && in_classes (*p, iso_classes))
-                   {
-                     if (negate)
-                       break;
-                     else
-                       goto fwd_unibyte_ok;
-                   }
-
-                 if (!fastmap[*p])
+             if (! fastmap[*p])
+               break;
+             if (! ASCII_CHAR_P (c))
+               {
+                 /* See the comment in the previous similar code.  */
+                 for (i = 0; i < n_char_ranges; i += 2)
+                   if (c >= char_ranges[i] && c <= char_ranges[i + 1])
+                     break;
+                 if (!(negate ^ (i < n_char_ranges)))
                    break;
-               fwd_unibyte_ok:
-                 p++, pos++;
                }
-         }
+           back_ok:
+             pos--, pos_byte -= prev_p - p;
+           }
        else
-         {
-           if (multibyte)
-             while (1)
+         while (1)
+           {
+             if (p <= stop)
                {
-                 unsigned char *prev_p;
-                 int nbytes;
+                 if (p <= endp)
+                   break;
+                 p = GPT_ADDR;
+                 stop = endp;
+               }
 
-                 if (p <= stop)
-                   {
-                     if (p <= endp)
-                       break;
-                     p = GPT_ADDR;
-                     stop = endp;
-                   }
-                 prev_p = p;
-                 while (--p >= stop && ! CHAR_HEAD_P (*p));
-                 PARSE_MULTIBYTE_SEQ (p, MAX_MULTIBYTE_LENGTH, nbytes);
-                 if (prev_p - p > nbytes)
-                   p = prev_p - 1, c = *p, nbytes = 1;
+             if (! NILP (iso_classes) && in_classes (p[-1], iso_classes))
+               {
+                 if (negate)
+                   break;
                  else
-                   c = STRING_CHAR (p, MAX_MULTIBYTE_LENGTH);
+                   goto back_unibyte_ok;
+               }
 
-                 if (! NILP (iso_classes) && in_classes (c, iso_classes))
-                   {
-                     if (negate)
-                       break;
-                     else
-                       goto back_ok;
-                   }
+             if (!fastmap[p[-1]])
+               break;
+           back_unibyte_ok:
+             p--, pos--, pos_byte--;
+           }
+      }
 
-                 if (SINGLE_BYTE_CHAR_P (c))
-                   {
-                     if (!fastmap[c])
-                       break;
-                   }
-                 else
-                   {
-                     /* See the comment in the previous similar code.  */
-                     for (i = 0; i < n_char_ranges; i += 2)
-                       if (c >= char_ranges[i] && c <= char_ranges[i + 1])
-                         break;
-                     if (!(negate ^ (i < n_char_ranges)))
-                       break;
-                   }
-               back_ok:
-                 pos--, pos_byte -= nbytes;
-               }
-           else
-             while (1)
-               {
-                 if (p <= stop)
-                   {
-                     if (p <= endp)
-                       break;
-                     p = GPT_ADDR;
-                     stop = endp;
-                   }
+    SET_PT_BOTH (pos, pos_byte);
+    immediate_quit = 0;
 
-                 if (! NILP (iso_classes) && in_classes (p[-1], iso_classes))
-                   {
-                     if (negate)
-                       break;
-                     else
-                       goto back_unibyte_ok;
-                   }
+    return make_number (PT - start_point);
+  }
+}
 
-                 if (!fastmap[p[-1]])
-                   break;
-               back_unibyte_ok:
-                 p--, pos--;
-               }
-         }
+
+static Lisp_Object
+skip_syntaxes (forwardp, string, lim)
+     int forwardp;
+     Lisp_Object string, lim;
+{
+  register unsigned int c;
+  unsigned char fastmap[0400];
+  int negate = 0;
+  register int i, i_byte;
+  int multibyte;
+  int size_byte;
+  unsigned char *str;
+
+  CHECK_STRING (string);
+
+  if (NILP (lim))
+    XSETINT (lim, forwardp ? ZV : BEGV);
+  else
+    CHECK_NUMBER_COERCE_MARKER (lim);
+
+  /* In any case, don't allow scan outside bounds of buffer.  */
+  if (XINT (lim) > ZV)
+    XSETFASTINT (lim, ZV);
+  if (XINT (lim) < BEGV)
+    XSETFASTINT (lim, BEGV);
+
+  if (forwardp ? (PT >= XFASTINT (lim)) : (PT <= XFASTINT (lim)))
+    return make_number (0);
+
+  multibyte = (!NILP (current_buffer->enable_multibyte_characters)
+              && (XINT (lim) - PT != CHAR_TO_BYTE (XINT (lim)) - PT_BYTE));
+
+  bzero (fastmap, sizeof fastmap);
+
+  if (SBYTES (string) > SCHARS (string))
+    /* As this is very rare case (syntax spec is ASCII only), don't
+       consider efficiency.  */
+    string = string_make_unibyte (string);
+
+  str = SDATA (string);
+  size_byte = SBYTES (string);
+
+  i_byte = 0;
+  if (i_byte < size_byte
+      && SREF (string, 0) == '^')
+    {
+      negate = 1; i_byte++;
+    }
+
+  /* Find the syntaxes specified and set their elements of fastmap.  */
+
+  while (i_byte < size_byte)
+    {
+      c = str[i_byte++];
+      fastmap[syntax_spec_code[c]] = 1;
+    }
+
+  /* If ^ was the first character, complement the fastmap.  */
+  if (negate)
+    for (i = 0; i < sizeof fastmap; i++)
+      fastmap[i] ^= 1;
+
+  {
+    int start_point = PT;
+    int pos = PT;
+    int pos_byte = PT_BYTE;
+    unsigned char *p = PT_ADDR, *endp, *stop;
+
+    if (forwardp)
+      {
+       endp = (XINT (lim) == GPT) ? GPT_ADDR : CHAR_POS_ADDR (XINT (lim));
+       stop = (pos < GPT && GPT < XINT (lim)) ? GPT_ADDR : endp;
+      }
+    else
+      {
+       endp = CHAR_POS_ADDR (XINT (lim));
+       stop = (pos >= GPT && GPT > XINT (lim)) ? GAP_END_ADDR : endp;
       }
 
-#if 0 /* Not needed now that a position in mid-character
-        cannot be specified in Lisp.  */
-    if (multibyte
-       /* INC_POS or DEC_POS might have moved POS over LIM.  */
-       && (forwardp ? (pos > XINT (lim)) : (pos < XINT (lim))))
-      pos = XINT (lim);
-#endif
+    immediate_quit = 1;
+    SETUP_SYNTAX_TABLE (pos, forwardp ? 1 : -1);
+    if (forwardp)
+      {
+       if (multibyte)
+         {
+           while (1)
+             {
+               int nbytes;
+
+               if (p >= stop)
+                 {
+                   if (p >= endp)
+                     break;
+                   p = GAP_END_ADDR;
+                   stop = endp;
+                 }
+               c = STRING_CHAR_AND_LENGTH (p, nbytes);
+               if (! fastmap[(int) SYNTAX (c)])
+                 break;
+               p += nbytes, pos++, pos_byte += nbytes;
+               UPDATE_SYNTAX_TABLE_FORWARD (pos);
+             }
+         }
+       else
+         {
+           while (1)
+             {
+               if (p >= stop)
+                 {
+                   if (p >= endp)
+                     break;
+                   p = GAP_END_ADDR;
+                   stop = endp;
+                 }
+               if (! fastmap[(int) SYNTAX (*p)])
+                 break;
+               p++, pos++, pos_byte++;
+               UPDATE_SYNTAX_TABLE_FORWARD (pos);
+             }
+         }
+      }
+    else
+      {
+       if (multibyte)
+         {
+           while (1)
+             {
+               unsigned char *prev_p;
 
-    if (! multibyte)
-      pos_byte = pos;
+               if (p <= stop)
+                 {
+                   if (p <= endp)
+                     break;
+                   p = GPT_ADDR;
+                   stop = endp;
+                 }
+               UPDATE_SYNTAX_TABLE_BACKWARD (pos - 1);
+               prev_p = p;
+               while (--p >= stop && ! CHAR_HEAD_P (*p));
+               c = STRING_CHAR (p);
+               if (! fastmap[(int) SYNTAX (c)])
+                 break;
+               pos--, pos_byte -= prev_p - p;
+             }
+         }
+       else
+         {
+           while (1)
+             {
+               if (p <= stop)
+                 {
+                   if (p <= endp)
+                     break;
+                   p = GPT_ADDR;
+                   stop = endp;
+                 }
+               UPDATE_SYNTAX_TABLE_BACKWARD (pos - 1);
+               if (! fastmap[(int) SYNTAX (p[-1])])
+                 break;
+               p--, pos--, pos_byte--;
+             }
+         }
+      }
 
     SET_PT_BOTH (pos, pos_byte);
     immediate_quit = 0;
@@ -1856,7 +2081,7 @@ in_classes (c, iso_classes)
 {
   int fits_class = 0;
 
-  while (! NILP (iso_classes))
+  while (CONSP (iso_classes))
     {
       Lisp_Object elt;
       elt = XCAR (iso_classes);
@@ -1921,7 +2146,7 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
          *bytepos_ptr = from_byte;
          return 0;
        }
-      c = FETCH_CHAR (from_byte);
+      c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
       syntax = SYNTAX_WITH_FLAGS (c);
       code = syntax & 0xff;
       if (code == Sendcomment
@@ -1951,7 +2176,7 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
     forw_incomment:
       if (from < stop && SYNTAX_FLAGS_COMEND_FIRST (syntax)
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
-         && (c1 = FETCH_CHAR (from_byte),
+         && (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
              SYNTAX_COMEND_SECOND (c1))
          && ((SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
               SYNTAX_COMMENT_NESTED (c1)) ? nesting > 0 : nesting < 0))
@@ -1970,7 +2195,7 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
       if (nesting > 0
          && from < stop
          && SYNTAX_FLAGS_COMSTART_FIRST (syntax)
-         && (c1 = FETCH_CHAR (from_byte),
+         && (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
              SYNTAX_COMMENT_STYLE (c1) == style
              && SYNTAX_COMSTART_SECOND (c1))
          && (SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
@@ -2034,7 +2259,7 @@ between them, return t; otherwise return nil.  */)
              immediate_quit = 0;
              return Qnil;
            }
-         c = FETCH_CHAR (from_byte);
+         c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
          code = SYNTAX (c);
          comstart_first = SYNTAX_COMSTART_FIRST (c);
          comnested = SYNTAX_COMMENT_NESTED (c);
@@ -2042,7 +2267,7 @@ between them, return t; otherwise return nil.  */)
          INC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_FORWARD (from);
          if (from < stop && comstart_first
-             && (c1 = FETCH_CHAR (from_byte),
+             && (c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte),
                  SYNTAX_COMSTART_SECOND (c1)))
            {
              /* We have encountered a comment start sequence and we
@@ -2100,7 +2325,7 @@ between them, return t; otherwise return nil.  */)
          DEC_BOTH (from, from_byte);
          /* char_quoted does UPDATE_SYNTAX_TABLE_BACKWARD (from).  */
          quoted = char_quoted (from, from_byte);
-         c = FETCH_CHAR (from_byte);
+         c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
          code = SYNTAX (c);
          comstyle = 0;
          comnested = SYNTAX_COMMENT_NESTED (c);
@@ -2117,7 +2342,7 @@ between them, return t; otherwise return nil.  */)
              code = Sendcomment;
              /* Calling char_quoted, above, set up global syntax position
                 at the new value of FROM.  */
-             c1 = FETCH_CHAR (from_byte);
+             c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
              comstyle = SYNTAX_COMMENT_STYLE (c1);
              comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
            }
@@ -2131,7 +2356,7 @@ between them, return t; otherwise return nil.  */)
                {
                  DEC_BOTH (from, from_byte);
                  UPDATE_SYNTAX_TABLE_BACKWARD (from);
-                 c = FETCH_CHAR (from_byte);
+                 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
                  if (SYNTAX (c) == Scomment_fence
                      && !char_quoted (from, from_byte))
                    {
@@ -2197,11 +2422,11 @@ between them, return t; otherwise return nil.  */)
   return Qt;
 }
 \f
-/* Return syntax code of character C if C is a single byte character
+/* Return syntax code of character C if C is an ASCII character
    or `multibyte_symbol_p' is zero.  Otherwise, return Ssymbol.  */
 
-#define SYNTAX_WITH_MULTIBYTE_CHECK(c)                 \
-  ((SINGLE_BYTE_CHAR_P (c) || !multibyte_symbol_p)     \
+#define SYNTAX_WITH_MULTIBYTE_CHECK(c)         \
+  ((ASCII_CHAR_P (c) || !multibyte_symbol_p)   \
    ? SYNTAX (c) : Ssymbol)
 
 static Lisp_Object
@@ -2245,7 +2470,7 @@ scan_lists (from, count, depth, sexpflag)
        {
          int comstart_first, prefix;
          UPDATE_SYNTAX_TABLE_FORWARD (from);
-         c = FETCH_CHAR (from_byte);
+         c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
          code = SYNTAX_WITH_MULTIBYTE_CHECK (c);
          comstart_first = SYNTAX_COMSTART_FIRST (c);
          comnested = SYNTAX_COMMENT_NESTED (c);
@@ -2256,7 +2481,8 @@ scan_lists (from, count, depth, sexpflag)
          INC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_FORWARD (from);
          if (from < stop && comstart_first
-             && (c = FETCH_CHAR (from_byte), SYNTAX_COMSTART_SECOND (c))
+             && (c = FETCH_CHAR_AS_MULTIBYTE (from_byte),
+                 SYNTAX_COMSTART_SECOND (c))
              && parse_sexp_ignore_comments)
            {
              /* we have encountered a comment start sequence and we
@@ -2265,7 +2491,7 @@ scan_lists (from, count, depth, sexpflag)
                 only a comment end of the same style actually ends
                 the comment section */
              code = Scomment;
-             c1 = FETCH_CHAR (from_byte);
+             c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
              comstyle = SYNTAX_COMMENT_STYLE (c1);
              comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
              INC_BOTH (from, from_byte);
@@ -2279,7 +2505,8 @@ scan_lists (from, count, depth, sexpflag)
            {
            case Sescape:
            case Scharquote:
-             if (from == stop) goto lose;
+             if (from == stop)
+               goto lose;
              INC_BOTH (from, from_byte);
              /* treat following character as a word constituent */
            case Sword:
@@ -2291,14 +2518,15 @@ scan_lists (from, count, depth, sexpflag)
                  UPDATE_SYNTAX_TABLE_FORWARD (from);
 
                  /* Some compilers can't handle this inside the switch.  */
-                 c = FETCH_CHAR (from_byte);
+                 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
                  temp = SYNTAX_WITH_MULTIBYTE_CHECK (c);
                  switch (temp)
                    {
                    case Scharquote:
                    case Sescape:
                      INC_BOTH (from, from_byte);
-                     if (from == stop) goto lose;
+                     if (from == stop)
+                       goto lose;
                      break;
                    case Sword:
                    case Ssymbol:
@@ -2334,7 +2562,7 @@ scan_lists (from, count, depth, sexpflag)
            case Smath:
              if (!sexpflag)
                break;
-             if (from != stop && c == FETCH_CHAR (from_byte))
+             if (from != stop && c == FETCH_CHAR_AS_MULTIBYTE (from_byte))
                {
                  INC_BOTH (from, from_byte);
                }
@@ -2361,12 +2589,13 @@ scan_lists (from, count, depth, sexpflag)
            case Sstring:
            case Sstring_fence:
              temp_pos = dec_bytepos (from_byte);
-             stringterm = FETCH_CHAR (temp_pos);
+             stringterm = FETCH_CHAR_AS_MULTIBYTE (temp_pos);
              while (1)
                {
-                 if (from >= stop) goto lose;
+                 if (from >= stop)
+                   goto lose;
                  UPDATE_SYNTAX_TABLE_FORWARD (from);
-                 c = FETCH_CHAR (from_byte);
+                 c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
                  if (code == Sstring
                      ? (c == stringterm
                         && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
@@ -2393,7 +2622,8 @@ scan_lists (from, count, depth, sexpflag)
        }
 
       /* Reached end of buffer.  Error if within object, return nil if between */
-      if (depth) goto lose;
+      if (depth)
+       goto lose;
 
       immediate_quit = 0;
       return Qnil;
@@ -2409,7 +2639,7 @@ scan_lists (from, count, depth, sexpflag)
        {
          DEC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_BACKWARD (from);
-         c = FETCH_CHAR (from_byte);
+         c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
          code = SYNTAX_WITH_MULTIBYTE_CHECK (c);
          if (depth == min_depth)
            last_good = from;
@@ -2427,7 +2657,7 @@ scan_lists (from, count, depth, sexpflag)
              DEC_BOTH (from, from_byte);
              UPDATE_SYNTAX_TABLE_BACKWARD (from);
              code = Sendcomment;
-             c1 = FETCH_CHAR (from_byte);
+             c1 = FETCH_CHAR_AS_MULTIBYTE (from_byte);
              comstyle = SYNTAX_COMMENT_STYLE (c1);
              comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
            }
@@ -2460,7 +2690,7 @@ scan_lists (from, count, depth, sexpflag)
                  else
                    temp_pos--;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
-                 c1 = FETCH_CHAR (temp_pos);
+                 c1 = FETCH_CHAR_AS_MULTIBYTE (temp_pos);
                  temp_code = SYNTAX_WITH_MULTIBYTE_CHECK (c1);
                  /* Don't allow comment-end to be quoted.  */
                  if (temp_code == Sendcomment)
@@ -2472,7 +2702,7 @@ scan_lists (from, count, depth, sexpflag)
                      temp_pos = dec_bytepos (temp_pos);
                      UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                    }
-                 c1 = FETCH_CHAR (temp_pos);
+                 c1 = FETCH_CHAR_AS_MULTIBYTE (temp_pos);
                  temp_code = SYNTAX_WITH_MULTIBYTE_CHECK (c1);
                  if (! (quoted || temp_code == Sword
                         || temp_code == Ssymbol
@@ -2487,7 +2717,7 @@ scan_lists (from, count, depth, sexpflag)
                break;
              temp_pos = dec_bytepos (from_byte);
              UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
-             if (from != stop && c == FETCH_CHAR (temp_pos))
+             if (from != stop && c == FETCH_CHAR_AS_MULTIBYTE (temp_pos))
                DEC_BOTH (from, from_byte);
              if (mathexit)
                {
@@ -2528,11 +2758,12 @@ scan_lists (from, count, depth, sexpflag)
            case Sstring_fence:
              while (1)
                {
-                 if (from == stop) goto lose;
+                 if (from == stop)
+                   goto lose;
                  DEC_BOTH (from, from_byte);
                  UPDATE_SYNTAX_TABLE_BACKWARD (from);
                  if (!char_quoted (from, from_byte)
-                     && (c = FETCH_CHAR (from_byte),
+                     && (c = FETCH_CHAR_AS_MULTIBYTE (from_byte),
                          SYNTAX_WITH_MULTIBYTE_CHECK (c) == code))
                    break;
                }
@@ -2540,14 +2771,16 @@ scan_lists (from, count, depth, sexpflag)
              break;
 
            case Sstring:
-             stringterm = FETCH_CHAR (from_byte);
+             stringterm = FETCH_CHAR_AS_MULTIBYTE (from_byte);
              while (1)
                {
-                 if (from == stop) goto lose;
+                 if (from == stop)
+                   goto lose;
                  DEC_BOTH (from, from_byte);
                  UPDATE_SYNTAX_TABLE_BACKWARD (from);
                  if (!char_quoted (from, from_byte)
-                     && stringterm == (c = FETCH_CHAR (from_byte))
+                     && (stringterm
+                         == (c = FETCH_CHAR_AS_MULTIBYTE (from_byte)))
                      && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
                    break;
                }
@@ -2560,7 +2793,8 @@ scan_lists (from, count, depth, sexpflag)
        }
 
       /* Reached start of buffer.  Error if within object, return nil if between */
-      if (depth) goto lose;
+      if (depth)
+       goto lose;
 
       immediate_quit = 0;
       return Qnil;
@@ -2650,7 +2884,7 @@ This includes chars with "quote" or "prefix" syntax (' or p).  */)
 
   while (!char_quoted (pos, pos_byte)
         /* Previous statement updates syntax table.  */
-        && ((c = FETCH_CHAR (pos_byte), SYNTAX (c) == Squote)
+        && ((c = FETCH_CHAR_AS_MULTIBYTE (pos_byte), SYNTAX (c) == Squote)
             || SYNTAX_PREFIX (c)))
     {
       opoint = pos;
@@ -2677,8 +2911,9 @@ static void
 scan_sexps_forward (stateptr, from, from_byte, end, targetdepth,
                    stopbefore, oldstate, commentstop)
      struct lisp_parse_state *stateptr;
-     register int from;
-     int end, targetdepth, stopbefore, from_byte;
+     register EMACS_INT from;
+     EMACS_INT from_byte, end;
+     int targetdepth, stopbefore;
      Lisp_Object oldstate;
      int commentstop;
 {
@@ -2697,8 +2932,8 @@ scan_sexps_forward (stateptr, from, from_byte, end, targetdepth,
   int mindepth;                /* Lowest DEPTH value seen.  */
   int start_quoted = 0;                /* Nonzero means starting after a char quote */
   Lisp_Object tem;
-  int prev_from;               /* Keep one character before FROM.  */
-  int prev_from_byte;
+  EMACS_INT prev_from;         /* Keep one character before FROM.  */
+  EMACS_INT prev_from_byte;
   int prev_from_syntax;
   int boundary_stop = commentstop == -1;
   int nofence;
@@ -2715,7 +2950,7 @@ scan_sexps_forward (stateptr, from, from_byte, end, targetdepth,
 #define INC_FROM                               \
 do { prev_from = from;                         \
      prev_from_byte = from_byte;               \
-     temp = FETCH_CHAR (prev_from_byte);       \
+     temp = FETCH_CHAR_AS_MULTIBYTE (prev_from_byte);  \
      prev_from_syntax = SYNTAX_WITH_FLAGS (temp); \
      INC_BOTH (from, from_byte);               \
      if (from < end)                           \
@@ -2871,7 +3106,7 @@ do { prev_from = from;                            \
          while (from < end)
            {
              /* Some compilers can't handle this inside the switch.  */
-             temp = FETCH_CHAR (from_byte);
+             temp = FETCH_CHAR_AS_MULTIBYTE (from_byte);
              temp = SYNTAX (temp);
              switch (temp)
                {
@@ -2945,7 +3180,7 @@ do { prev_from = from;                            \
          if (stopbefore) goto stop;  /* this arg means stop at sexp start */
          curlevel->last = prev_from;
          state.instring = (code == Sstring
-                           ? (FETCH_CHAR (prev_from_byte))
+                           ? (FETCH_CHAR_AS_MULTIBYTE (prev_from_byte))
                            : ST_STRING_STYLE);
          if (boundary_stop) goto done;
        startinstring:
@@ -2957,7 +3192,7 @@ do { prev_from = from;                            \
                int c;
 
                if (from >= end) goto done;
-               c = FETCH_CHAR (from_byte);
+               c = FETCH_CHAR_AS_MULTIBYTE (from_byte);
                /* Some compilers can't handle this inside the switch.  */
                temp = SYNTAX (c);
 
@@ -3103,17 +3338,17 @@ init_syntax_once ()
   Lisp_Object temp;
 
   /* This has to be done here, before we call Fmake_char_table.  */
-  Qsyntax_table = intern ("syntax-table");
+  Qsyntax_table = intern_c_string ("syntax-table");
   staticpro (&Qsyntax_table);
 
-  /* Intern this now in case it isn't already done.
+  /* Intern_C_String this now in case it isn't already done.
      Setting this variable twice is harmless.
      But don't staticpro it here--that is done in alloc.c.  */
-  Qchar_table_extra_slots = intern ("char-table-extra-slots");
+  Qchar_table_extra_slots = intern_c_string ("char-table-extra-slots");
 
   /* Create objects which can be shared among syntax tables.  */
   Vsyntax_code_object = Fmake_vector (make_number (Smax), Qnil);
-  for (i = 0; i < XVECTOR (Vsyntax_code_object)->size; i++)
+  for (i = 0; i < XVECTOR_SIZE (Vsyntax_code_object); i++)
     XVECTOR (Vsyntax_code_object)->contents[i]
       = Fcons (make_number (i), Qnil);
 
@@ -3183,14 +3418,13 @@ init_syntax_once ()
 
   /* All multibyte characters have syntax `word' by default.  */
   temp = XVECTOR (Vsyntax_code_object)->contents[(int) Sword];
-  for (i = CHAR_TABLE_SINGLE_BYTE_SLOTS; i < CHAR_TABLE_ORDINARY_SLOTS; i++)
-    XCHAR_TABLE (Vstandard_syntax_table)->contents[i] = temp;
+  char_table_set_range (Vstandard_syntax_table, 0x80, MAX_CHAR, temp);
 }
 
 void
 syms_of_syntax ()
 {
-  Qsyntax_table_p = intern ("syntax-table-p");
+  Qsyntax_table_p = intern_c_string ("syntax-table-p");
   staticpro (&Qsyntax_table_p);
 
   staticpro (&Vsyntax_code_object);
@@ -3203,12 +3437,12 @@ syms_of_syntax ()
   /* Defined in regex.c */
   staticpro (&re_match_object);
 
-  Qscan_error = intern ("scan-error");
+  Qscan_error = intern_c_string ("scan-error");
   staticpro (&Qscan_error);
   Fput (Qscan_error, Qerror_conditions,
-       Fcons (Qscan_error, Fcons (Qerror, Qnil)));
+       pure_cons (Qscan_error, pure_cons (Qerror, Qnil)));
   Fput (Qscan_error, Qerror_message,
-       build_string ("Scan error"));
+       make_pure_c_string ("Scan error"));
 
   DEFVAR_BOOL ("parse-sexp-ignore-comments", &parse_sexp_ignore_comments,
               doc: /* Non-nil means `forward-sexp', etc., should treat comments as whitespace.  */);
@@ -3232,6 +3466,25 @@ See the info node `(elisp)Syntax Properties' for a description of the
               doc: /* *Non-nil means an open paren in column 0 denotes the start of a defun.  */);
   open_paren_in_column_0_is_defun_start = 1;
 
+
+  DEFVAR_LISP ("find-word-boundary-function-table",
+              &Vfind_word_boundary_function_table,
+              doc: /*
+Char table of functions to search for the word boundary.
+Each function is called with two arguments; POS and LIMIT.
+POS and LIMIT are character positions in the current buffer.
+
+If POS is less than LIMIT, POS is at the first character of a word,
+and the return value of a function is a position after the last
+character of that word.
+
+If POS is not less than LIMIT, POS is at the last character of a word,
+and the return value of a function is a position at the first
+character of that word.
+
+In both cases, LIMIT bounds the search. */);
+  Vfind_word_boundary_function_table = Fmake_char_table (Qnil, Qnil);
+
   defsubr (&Ssyntax_table_p);
   defsubr (&Ssyntax_table);
   defsubr (&Sstandard_syntax_table);