]> code.delx.au - gnu-emacs/blobdiff - src/syntax.c
(skip_invisible): Use new retval of TEXT_PROP_MEANS_INVISIBLE.
[gnu-emacs] / src / syntax.c
index 98829863d424f92a4faa745d0f110228bcb8de50..6a17e6599792495aedb90e2afbf96f172b97f931 100644 (file)
@@ -1,5 +1,5 @@
 /* GNU Emacs routines to deal with syntax tables; also word and list parsing.
 /* GNU Emacs routines to deal with syntax tables; also word and list parsing.
-   Copyright (C) 1985, 87, 93, 94, 95, 97, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1985, 87, 93, 94, 95, 97, 1998, 1999 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 
 This file is part of GNU Emacs.
 
@@ -25,7 +25,7 @@ Boston, MA 02111-1307, USA.  */
 #include "commands.h"
 #include "buffer.h"
 #include "charset.h"
 #include "commands.h"
 #include "buffer.h"
 #include "charset.h"
-#include <assert.h>
+#include "keymap.h"
 
 /* Make syntax table lookup grant data in gl_state.  */
 #define SYNTAX_ENTRY_VIA_PROPERTY
 
 /* Make syntax table lookup grant data in gl_state.  */
 #define SYNTAX_ENTRY_VIA_PROPERTY
@@ -46,11 +46,20 @@ Lisp_Object Qsyntax_table_p, Qsyntax_table, Qscan_error;
 int words_include_escapes;
 int parse_sexp_lookup_properties;
 
 int words_include_escapes;
 int parse_sexp_lookup_properties;
 
+/* Nonzero means `scan-sexps' treat all multibyte characters as symbol.  */
+int multibyte_syntax_as_symbol;
+
 /* Used as a temporary in SYNTAX_ENTRY and other macros in syntax.h,
    if not compiled with GCC.  No need to mark it, since it is used
    only very temporarily.  */
 Lisp_Object syntax_temp;
 
 /* Used as a temporary in SYNTAX_ENTRY and other macros in syntax.h,
    if not compiled with GCC.  No need to mark it, since it is used
    only very temporarily.  */
 Lisp_Object syntax_temp;
 
+/* Non-zero means an open parenthesis in column 0 is always considered
+   to be the start of a defun.  Zero means an open parenthesis in
+   column 0 has no special meaning.  */
+
+int open_paren_in_column_0_is_defun_start;
+
 /* This is the internal form of the parse state used in parse-partial-sexp.  */
 
 struct lisp_parse_state
 /* This is the internal form of the parse state used in parse-partial-sexp.  */
 
 struct lisp_parse_state
@@ -120,41 +129,41 @@ update_syntax_table (charpos, count, init, object)
      Lisp_Object object;
 {
   Lisp_Object tmp_table;
      Lisp_Object object;
 {
   Lisp_Object tmp_table;
-  int cnt = 0, doing_extra = 0, invalidate = 1;
+  int cnt = 0, invalidate = 1;
   INTERVAL i, oldi;
 
   if (init)
     {
   INTERVAL i, oldi;
 
   if (init)
     {
+      gl_state.old_prop = Qnil;
       gl_state.start = gl_state.b_property;
       gl_state.stop = gl_state.e_property;
       gl_state.start = gl_state.b_property;
       gl_state.stop = gl_state.e_property;
-      gl_state.forward_i = interval_of (charpos, object);
-      i = gl_state.backward_i = gl_state.forward_i;
-      gl_state.left_ok = gl_state.right_ok = 1;
+      i = interval_of (charpos, object);
+      gl_state.backward_i = gl_state.forward_i = i;
       invalidate = 0;
       if (NULL_INTERVAL_P (i))
        return;
       /* interval_of updates only ->position of the return value, so
         update the parents manually to speed up update_interval.  */
       invalidate = 0;
       if (NULL_INTERVAL_P (i))
        return;
       /* interval_of updates only ->position of the return value, so
         update the parents manually to speed up update_interval.  */
-      while (!NULL_PARENT (i)) 
+      while (!NULL_PARENT (i))
        {
          if (AM_RIGHT_CHILD (i))
        {
          if (AM_RIGHT_CHILD (i))
-           i->parent->position = i->position
+           INTERVAL_PARENT (i)->position = i->position
              - LEFT_TOTAL_LENGTH (i) + TOTAL_LENGTH (i) /* right end */
              - LEFT_TOTAL_LENGTH (i) + TOTAL_LENGTH (i) /* right end */
-             - TOTAL_LENGTH (i->parent)
-             + LEFT_TOTAL_LENGTH (i->parent);
+             - TOTAL_LENGTH (INTERVAL_PARENT (i))
+             + LEFT_TOTAL_LENGTH (INTERVAL_PARENT (i));
          else
          else
-           i->parent->position = i->position - LEFT_TOTAL_LENGTH (i)
+           INTERVAL_PARENT (i)->position = i->position - LEFT_TOTAL_LENGTH (i)
              + TOTAL_LENGTH (i);
              + TOTAL_LENGTH (i);
-         i = i->parent;
+         i = INTERVAL_PARENT (i);
        }
       i = gl_state.forward_i;
        }
       i = gl_state.forward_i;
-      gl_state.b_property = i->position - 1 - gl_state.offset;
+      gl_state.b_property = i->position - gl_state.offset;
       gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
       goto update;
     }
   oldi = i = count > 0 ? gl_state.forward_i : gl_state.backward_i;
 
       gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
       goto update;
     }
   oldi = i = count > 0 ? gl_state.forward_i : gl_state.backward_i;
 
-  /* We are guarantied to be called with CHARPOS either in i,
+  /* We are guaranteed to be called with CHARPOS either in i,
      or further off.  */
   if (NULL_INTERVAL_P (i))
     error ("Error in syntax_table logic for to-the-end intervals");
      or further off.  */
   if (NULL_INTERVAL_P (i))
     error ("Error in syntax_table logic for to-the-end intervals");
@@ -164,33 +173,26 @@ update_syntax_table (charpos, count, init, object)
        error ("Error in syntax_table logic for intervals <-");
       /* Update the interval.  */
       i = update_interval (i, charpos);
        error ("Error in syntax_table logic for intervals <-");
       /* Update the interval.  */
       i = update_interval (i, charpos);
-      if (oldi->position != INTERVAL_LAST_POS (i))
+      if (INTERVAL_LAST_POS (i) != gl_state.b_property)
        {
          invalidate = 0;
        {
          invalidate = 0;
-         gl_state.right_ok = 1;        /* Invalidate the other end.  */
          gl_state.forward_i = i;
          gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
        }
          gl_state.forward_i = i;
          gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
        }
-    } 
+    }
   else if (charpos >= INTERVAL_LAST_POS (i)) /* Move right.  */
     {
       if (count < 0)
        error ("Error in syntax_table logic for intervals ->");
       /* Update the interval.  */
       i = update_interval (i, charpos);
   else if (charpos >= INTERVAL_LAST_POS (i)) /* Move right.  */
     {
       if (count < 0)
        error ("Error in syntax_table logic for intervals ->");
       /* Update the interval.  */
       i = update_interval (i, charpos);
-      if (i->position != INTERVAL_LAST_POS (oldi))
+      if (i->position != gl_state.e_property)
        {
          invalidate = 0;
        {
          invalidate = 0;
-         gl_state.left_ok = 1;         /* Invalidate the other end.  */
          gl_state.backward_i = i;
          gl_state.backward_i = i;
-         gl_state.b_property = i->position - 1 - gl_state.offset;
+         gl_state.b_property = i->position - gl_state.offset;
        }
     }
        }
     }
-  else if (count > 0 ? gl_state.right_ok : gl_state.left_ok)
-    {
-      /* We do not need to recalculate tmp_table.  */
-      tmp_table = gl_state.old_prop;
-    }
 
   update:
   tmp_table = textget (i->plist, Qsyntax_table);
 
   update:
   tmp_table = textget (i->plist, Qsyntax_table);
@@ -198,38 +200,39 @@ update_syntax_table (charpos, count, init, object)
   if (invalidate)
     invalidate = !EQ (tmp_table, gl_state.old_prop); /* Need to invalidate? */
       
   if (invalidate)
     invalidate = !EQ (tmp_table, gl_state.old_prop); /* Need to invalidate? */
       
-  if (invalidate)                      /* Did not get to adjacent interval.  */
-    {                                  /* with the same table => */
-                                       /* invalidate the old range.  */
+  if (invalidate)              /* Did not get to adjacent interval.  */
+    {                          /* with the same table => */
+                               /* invalidate the old range.  */
       if (count > 0)
        {
          gl_state.backward_i = i;
       if (count > 0)
        {
          gl_state.backward_i = i;
-         gl_state.left_ok = 1;         /* Invalidate the other end.  */
-         gl_state.b_property = i->position - 1 - gl_state.offset;
-       } 
-      else 
+         gl_state.b_property = i->position - gl_state.offset;
+       }
+      else
        {
        {
-         gl_state.forward_i = i;       
-         gl_state.right_ok = 1;        /* Invalidate the other end.  */
+         gl_state.forward_i = i;
          gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
        }
     }
 
          gl_state.e_property = INTERVAL_LAST_POS (i) - gl_state.offset;
        }
     }
 
-  gl_state.current_syntax_table = tmp_table;
-  gl_state.old_prop = tmp_table;
-  if (EQ (Fsyntax_table_p (tmp_table), Qt))
+  if (!EQ (tmp_table, gl_state.old_prop))
     {
     {
-      gl_state.use_global = 0;
-    } 
-  else if (CONSP (tmp_table))
-    {
-      gl_state.use_global = 1;
-      gl_state.global_code = tmp_table;
-    }
-  else 
-    {
-      gl_state.use_global = 0;
-      gl_state.current_syntax_table = current_buffer->syntax_table;
+      gl_state.current_syntax_table = tmp_table;
+      gl_state.old_prop = tmp_table;
+      if (EQ (Fsyntax_table_p (tmp_table), Qt))
+       {
+         gl_state.use_global = 0;
+       } 
+      else if (CONSP (tmp_table))
+       {
+         gl_state.use_global = 1;
+         gl_state.global_code = tmp_table;
+       }
+      else 
+       {
+         gl_state.use_global = 0;
+         gl_state.current_syntax_table = current_buffer->syntax_table;
+       }
     }
 
   while (!NULL_INTERVAL_P (i))
     }
 
   while (!NULL_INTERVAL_P (i))
@@ -237,42 +240,39 @@ update_syntax_table (charpos, count, init, object)
       if (cnt && !EQ (tmp_table, textget (i->plist, Qsyntax_table)))
        {
          if (count > 0)
       if (cnt && !EQ (tmp_table, textget (i->plist, Qsyntax_table)))
        {
          if (count > 0)
-           gl_state.right_ok = 0;
-         else 
-           gl_state.left_ok = 0;
-         break;          
+           {
+             gl_state.e_property = i->position - gl_state.offset;
+             gl_state.forward_i = i;
+           }
+         else
+           {
+             gl_state.b_property = i->position + LENGTH (i) - gl_state.offset;
+             gl_state.backward_i = i;
+           }
+         return;
        }
       else if (cnt == INTERVALS_AT_ONCE) 
        {
          if (count > 0)
        }
       else if (cnt == INTERVALS_AT_ONCE) 
        {
          if (count > 0)
-           gl_state.right_ok = 1;
-         else 
-           gl_state.left_ok = 1;
-         break;
+           {
+             gl_state.e_property = i->position + LENGTH (i) - gl_state.offset;
+             gl_state.forward_i = i;
+           }
+         else
+           {
+             gl_state.b_property = i->position - gl_state.offset;
+             gl_state.backward_i = i;
+           }
+         return;
        }
       cnt++;
       i = count > 0 ? next_interval (i) : previous_interval (i);
     }
        }
       cnt++;
       i = count > 0 ? next_interval (i) : previous_interval (i);
     }
-  if (NULL_INTERVAL_P (i)) 
-    {                                  /* This property goes to the end.  */
-      if (count > 0)
-       gl_state.e_property = gl_state.stop;
-      else
-       gl_state.b_property = gl_state.start;
-    } 
-  else 
-    {
-      if (count > 0) 
-       {
-         gl_state.e_property = i->position - gl_state.offset;
-         gl_state.forward_i = i;
-       }
-      else 
-       {
-         gl_state.b_property = i->position + LENGTH (i) - 1 - gl_state.offset;
-         gl_state.backward_i = i;
-       }
-    }    
+  eassert (NULL_INTERVAL_P (i)); /* This property goes to the end.  */
+  if (count > 0)
+    gl_state.e_property = gl_state.stop;
+  else
+    gl_state.b_property = gl_state.start;
 }
 \f
 /* Returns TRUE if char at CHARPOS is quoted.
 }
 \f
 /* Returns TRUE if char at CHARPOS is quoted.
@@ -333,9 +333,15 @@ dec_bytepos (bytepos)
   return bytepos;
 }
 \f
   return bytepos;
 }
 \f
-/* Find a defun-start that is the last one before POS (or nearly the last).
-   We record what we find, so that another call in the same area
-   can return the same value right away.  
+/* Return a defun-start position before 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,
+   the beginning of every line is treated as a defun-start.
+
+   We record the information about where the scan started
+   and what its result was, so that another call in the same area
+   can return the same value very quickly.
 
    There is no promise at which position the global syntax data is
    valid on return from the subroutine, so the caller should explicitly
 
    There is no promise at which position the global syntax data is
    valid on return from the subroutine, so the caller should explicitly
@@ -345,8 +351,6 @@ static int
 find_defun_start (pos, pos_byte)
      int pos, pos_byte;
 {
 find_defun_start (pos, pos_byte)
      int pos, pos_byte;
 {
-  int tem;
-  int shortage;
   int opoint = PT, opoint_byte = PT_BYTE;
 
   /* Use previous finding, if it's valid and applies to this inquiry.  */
   int opoint = PT, opoint_byte = PT_BYTE;
 
   /* Use previous finding, if it's valid and applies to this inquiry.  */
@@ -368,20 +372,24 @@ find_defun_start (pos, pos_byte)
      syntax-tables.  */
   gl_state.current_syntax_table = current_buffer->syntax_table;
   gl_state.use_global = 0;
      syntax-tables.  */
   gl_state.current_syntax_table = current_buffer->syntax_table;
   gl_state.use_global = 0;
-  while (PT > BEGV)
+  if (open_paren_in_column_0_is_defun_start)
     {
     {
-      /* Open-paren at start of line means we found our defun-start.  */
-      if (SYNTAX (FETCH_CHAR (PT_BYTE)) == Sopen)
+      while (PT > BEGV)
        {
        {
-         SETUP_SYNTAX_TABLE (PT + 1, -1);      /* Try again... */
+         /* Open-paren at start of line means we may have found our
+            defun-start.  */
          if (SYNTAX (FETCH_CHAR (PT_BYTE)) == Sopen)
          if (SYNTAX (FETCH_CHAR (PT_BYTE)) == Sopen)
-           break;
-         /* Now fallback to the default value.  */
-         gl_state.current_syntax_table = current_buffer->syntax_table;
-         gl_state.use_global = 0;
+           {
+             SETUP_SYNTAX_TABLE (PT + 1, -1);  /* Try again... */
+             if (SYNTAX (FETCH_CHAR (PT_BYTE)) == Sopen)
+               break;
+             /* Now fallback to the default value.  */
+             gl_state.current_syntax_table = current_buffer->syntax_table;
+             gl_state.use_global = 0;
+           }
+         /* Move to beg of previous line.  */
+         scan_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -2, 1);
        }
        }
-      /* Move to beg of previous line.  */
-      scan_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -2, 1);
     }
 
   /* Record what we found, for the next try.  */
     }
 
   /* Record what we found, for the next try.  */
@@ -415,19 +423,19 @@ prev_char_comend_first (pos, pos_byte)
 
 /* Return the SYNTAX_COMSTART_FIRST of the character before POS, POS_BYTE.  */
 
 
 /* Return the SYNTAX_COMSTART_FIRST of the character before POS, POS_BYTE.  */
 
-static int
-prev_char_comstart_first (pos, pos_byte)
-     int pos, pos_byte;
-{
-  int c, val;
-
-  DEC_BOTH (pos, pos_byte);
-  UPDATE_SYNTAX_TABLE_BACKWARD (pos);
-  c = FETCH_CHAR (pos_byte);
-  val = SYNTAX_COMSTART_FIRST (c);
-  UPDATE_SYNTAX_TABLE_FORWARD (pos + 1);
-  return val;
-}
+/* static int
+ * prev_char_comstart_first (pos, pos_byte)
*      int pos, pos_byte;
+ * {
*   int c, val;
+ * 
*   DEC_BOTH (pos, pos_byte);
*   UPDATE_SYNTAX_TABLE_BACKWARD (pos);
*   c = FETCH_CHAR (pos_byte);
*   val = SYNTAX_COMSTART_FIRST (c);
*   UPDATE_SYNTAX_TABLE_FORWARD (pos + 1);
*   return val;
+ * } */
 
 /* Checks whether charpos FROM is at the end of a comment.
    FROM_BYTE is the bytepos corresponding to FROM.
 
 /* Checks whether charpos FROM is at the end of a comment.
    FROM_BYTE is the bytepos corresponding to FROM.
@@ -457,17 +465,18 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
      OFROM[I] is position of the earliest comment-starter seen
      which is I+2X quotes from the comment-end.
      PARITY is current parity of quotes from the comment end.  */
      OFROM[I] is position of the earliest comment-starter seen
      which is I+2X quotes from the comment-end.
      PARITY is current parity of quotes from the comment end.  */
-  int parity = 0;
-  int my_stringend = 0;
+  int string_style = -1;       /* Presumed outside of any string. */
   int string_lossage = 0;
   int string_lossage = 0;
+  /* Not a real lossage: indicates that we have passed a matching comment
+     starter plus an non-matching comment-ender, meaning that any matching
+     comment-starter we might see later could be a false positive (hidden
+     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;
   int comment_end = from;
   int comment_end_byte = from_byte;
   int comstart_pos = 0;
   int comstart_byte;
-  /* Value that PARITY had, when we reached the position
-     in COMSTART_POS.  */
-  int comstart_parity = 0;
-  int scanstart = from - 1;
   /* Place where the containing defun starts,
      or 0 if we didn't come across it yet.  */
   int defun_start = 0;
   /* Place where the containing defun starts,
      or 0 if we didn't come across it yet.  */
   int defun_start = 0;
@@ -475,109 +484,167 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
   register enum syntaxcode code;
   int nesting = 1;             /* current comment nesting */
   int c;
   register enum syntaxcode code;
   int nesting = 1;             /* current comment nesting */
   int c;
+  int syntax = 0;
+
+  /* FIXME: A }} comment-ender style leads to incorrect behavior
+     in the case of {{ c }}} because we ignore the last two chars which are
+     assumed to be comment-enders although they aren't.  */
 
   /* At beginning of range to scan, we're outside of strings;
      that determines quote parity to the comment-end.  */
   while (from != stop)
     {
 
   /* At beginning of range to scan, we're outside of strings;
      that determines quote parity to the comment-end.  */
   while (from != stop)
     {
-      int temp_byte, prev_comend_second;
+      int temp_byte, prev_syntax;
+      int com2start, com2end;
 
       /* Move back and examine a character.  */
       DEC_BOTH (from, from_byte);
       UPDATE_SYNTAX_TABLE_BACKWARD (from);
 
 
       /* 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 (from_byte);
+      syntax = SYNTAX_WITH_FLAGS (c);
       code = SYNTAX (c);
 
       code = SYNTAX (c);
 
-      /* If this char is the second of a 2-char comment end sequence,
-        back up and give the pair the appropriate syntax.  */
-      if (from > stop && SYNTAX_COMEND_SECOND (c)
-         && prev_char_comend_first (from, from_byte))
+      /* Check for 2-char comment markers.  */
+      com2start = (SYNTAX_FLAGS_COMSTART_FIRST (syntax)
+                  && SYNTAX_FLAGS_COMSTART_SECOND (prev_syntax)
+                  && comstyle == SYNTAX_FLAGS_COMMENT_STYLE (prev_syntax)
+                  && (SYNTAX_FLAGS_COMMENT_NESTED (prev_syntax)
+                      || SYNTAX_FLAGS_COMMENT_NESTED (syntax)) == comnested);
+      com2end = (SYNTAX_FLAGS_COMEND_FIRST (syntax)
+                && SYNTAX_FLAGS_COMEND_SECOND (prev_syntax));
+
+      /* Nasty cases with overlapping 2-char comment markers:
+        - snmp-mode: -- c -- foo -- c --
+                     --- c --
+                     ------ c --
+        - c-mode:    *||*
+                     |* *|* *|
+                     |*| |* |*|
+                     ///   */
+
+      /* If a 2-char comment sequence partly overlaps with another,
+        we don't try to be clever.  */
+      if (from > stop && (com2end || com2start))
        {
        {
-         code = Sendcomment;
-         DEC_BOTH (from, from_byte);
-         UPDATE_SYNTAX_TABLE_BACKWARD (from);
-         c = FETCH_CHAR (from_byte);
-       }
-                       
-      /* If this char starts a 2-char comment start sequence,
-        treat it like a 1-char comment starter.  */
-      if (from < scanstart && SYNTAX_COMSTART_FIRST (c))
-       {
-         temp_byte = inc_bytepos (from_byte);
-         UPDATE_SYNTAX_TABLE_FORWARD (from + 1);
-         if (SYNTAX_COMSTART_SECOND (FETCH_CHAR (temp_byte))
-             && comstyle == SYNTAX_COMMENT_STYLE (FETCH_CHAR (temp_byte)))
-           code = Scomment;
-         UPDATE_SYNTAX_TABLE_BACKWARD (from);
+         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_syntax = SYNTAX_WITH_FLAGS (next_c);
+         if (((com2start || comnested)
+              && SYNTAX_FLAGS_COMEND_SECOND (syntax)
+              && SYNTAX_FLAGS_COMEND_FIRST (next_syntax))
+             || ((com2end || comnested)
+                 && SYNTAX_FLAGS_COMSTART_SECOND (syntax)
+                 && comstyle == SYNTAX_FLAGS_COMMENT_STYLE (syntax)
+                 && SYNTAX_FLAGS_COMSTART_FIRST (next_syntax)))
+           goto lossage;
+         /* UPDATE_SYNTAX_TABLE_FORWARD (next + 1); */
        }
 
        }
 
+      if (com2start && comstart_pos == 0)
+       /* We're looking at a comment starter.  But it might be a comment
+          ender as well (see snmp-mode).  The first time we see one, we
+          need to consider it as a comment starter,
+          and the subsequent times as a comment ender.  */
+       com2end = 0;
+
+      /* Turn a 2-char comment sequences into the appropriate syntax.  */
+      if (com2end)
+       code = Sendcomment;
+      else if (com2start)
+       code = Scomment;
+      /* Ignore comment starters of a different style.  */
+      else if (code == Scomment
+              && (comstyle != SYNTAX_FLAGS_COMMENT_STYLE (syntax)
+                  || SYNTAX_FLAGS_COMMENT_NESTED (syntax) != comnested))
+       continue;
+
       /* Ignore escaped characters, except comment-enders.  */
       if (code != Sendcomment && char_quoted (from, from_byte))
        continue;
 
       /* Ignore escaped characters, except comment-enders.  */
       if (code != Sendcomment && char_quoted (from, from_byte))
        continue;
 
-      /* Track parity of quotes.  */
-      if (code == Sstring)
+      switch (code)
        {
        {
-         parity ^= 1;
-         if (my_stringend == 0)
-           my_stringend = c;
-         /* If we have two kinds of string delimiters.
-            There's no way to grok this scanning backwards.  */
-         else if (my_stringend != c)
-           string_lossage = 1;
-       }
-
-      if (code == Sstring_fence || code == Scomment_fence)
-       {
-         parity ^= 1;
-         if (my_stringend == 0)
-           my_stringend
-             = code == Sstring_fence ? ST_STRING_STYLE : ST_COMMENT_STYLE;
-         /* If we have two kinds of string delimiters.
-            There's no way to grok this scanning backwards.  */
-         else if (my_stringend != (code == Sstring_fence 
-                                   ? ST_STRING_STYLE : ST_COMMENT_STYLE))
+       case Sstring_fence:
+       case Scomment_fence:
+         c = (code == Sstring_fence ? ST_STRING_STYLE : ST_COMMENT_STYLE);
+       case Sstring:
+         /* Track parity of quotes.  */
+         if (string_style == -1)
+           /* Entering a string.  */
+           string_style = c;
+         else if (string_style == c)
+           /* Leaving the string.  */
+           string_style = -1;
+         else
+           /* If we have two kinds of string delimiters.
+              There's no way to grok this scanning backwards.  */
            string_lossage = 1;
            string_lossage = 1;
-       }
+         break;
+         
+       case Scomment:
+         /* We've already checked that it is the relevant comstyle.  */
+         if (string_style != -1 || comment_lossage || string_lossage)
+           /* There are odd string quotes involved, so let's be careful.
+              Test case in Pascal: " { " a { " } */
+           goto lossage;
 
 
-      if (code == Scomment)
-       /* FIXME: we should also check that the comstyle is correct
-          if the Scomment is a single-char. */
-       {
-         if (comnested && --nesting <= 0 && parity == 0 && !string_lossage)
+         if (!comnested)
+           {
+             /* Record best comment-starter so far.  */
+             comstart_pos = from;
+             comstart_byte = from_byte;
+           }
+         else if (--nesting <= 0)
            /* nested comments have to be balanced, so we don't need to
               keep looking for earlier ones.  We use here the same (slightly
               incorrect) reasoning as below:  since it is followed by uniform
               paired string quotes, this comment-start has to be outside of
               strings, else the comment-end itself would be inside a string. */
            goto done;
            /* nested comments have to be balanced, so we don't need to
               keep looking for earlier ones.  We use here the same (slightly
               incorrect) reasoning as below:  since it is followed by uniform
               paired string quotes, this comment-start has to be outside of
               strings, else the comment-end itself would be inside a string. */
            goto done;
+         break;
 
 
-         /* Record comment-starters according to that
-            quote-parity to the comment-end.  */
-         comstart_parity = parity;
-         comstart_pos = from;
-         comstart_byte = from_byte;
-       }
+       case Sendcomment:
+         if (SYNTAX_FLAGS_COMMENT_STYLE (syntax) == comstyle
+             && (SYNTAX_FLAGS_COMMENT_NESTED (prev_syntax)
+                 || SYNTAX_FLAGS_COMMENT_NESTED (syntax)) == comnested)
+           /* This is the same style of comment ender as ours. */
+           {
+             if (comnested)
+               nesting++;
+             else
+               /* Anything before that can't count because it would match
+                  this comment-ender rather than ours.  */
+               from = stop;    /* Break out of the loop.  */
+           }
+         else if (comstart_pos != 0 || c != '\n')
+           /* We're mixing comment styles here, so we'd better be careful.
+              The (comstart_pos != 0 || c != '\n') check is not quite correct
+              (we should just always set comment_lossage), but removing it
+              would imply that any multiline comment in C would go through
+              lossage, which seems overkill.
+              The failure should only happen in the rare cases such as
+                { (* } *)   */
+           comment_lossage = 1;
+         break;
 
 
-      /* If we find another earlier comment-ender,
-        any comment-starts earlier than that don't count
-        (because they go with the earlier comment-ender).  */
-      if (code == Sendcomment
-         && SYNTAX_COMMENT_STYLE (FETCH_CHAR (from_byte)) == comstyle)
-       if (comnested)
-         nesting++;
-       else
+       case Sopen:
+         /* Assume a defun-start point is outside of strings.  */
+         if (open_paren_in_column_0_is_defun_start
+             && (from == stop
+                 || (temp_byte = dec_bytepos (from_byte),
+                     FETCH_CHAR (temp_byte) == '\n')))
+           {
+             defun_start = from;
+             defun_start_byte = from_byte;
+             from = stop;      /* Break out of the loop.  */
+           }
          break;
 
          break;
 
-      /* Assume a defun-start point is outside of strings.  */
-      if (code == Sopen
-         && (from == stop
-             || (temp_byte = dec_bytepos (from_byte),
-                 FETCH_CHAR (temp_byte) == '\n')))
-       {
-         defun_start = from;
-         defun_start_byte = from_byte;
+       default:
          break;
        }
     }
          break;
        }
     }
@@ -588,12 +655,9 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
       from_byte = comment_end_byte;
       UPDATE_SYNTAX_TABLE_FORWARD (comment_end - 1);
     }
       from_byte = comment_end_byte;
       UPDATE_SYNTAX_TABLE_FORWARD (comment_end - 1);
     }
-  /* If the earliest comment starter
-     is followed by uniform paired string quotes or none,
-     we know it can't be inside a string
-     since if it were then the comment ender would be inside one.
-     So it does start a comment.  Skip back to it.  */
-  else if (!comnested && comstart_parity == 0 && !string_lossage) 
+  /* If comstart_pos is set and we get here (ie. didn't jump to `lossage'
+     or `done'), then we've found the beginning of the non-nested comment.  */
+  else if (1)  /* !comnested */
     {
       from = comstart_pos;
       from_byte = comstart_byte;
     {
       from = comstart_pos;
       from_byte = comstart_byte;
@@ -601,32 +665,43 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
     }
   else
     {
     }
   else
     {
+      struct lisp_parse_state state;
+    lossage:
       /* We had two kinds of string delimiters mixed up
         together.  Decode this going forwards.
       /* We had two kinds of string delimiters mixed up
         together.  Decode this going forwards.
-        Scan fwd from the previous comment ender
+        Scan fwd from a known safe place (beginning-of-defun)
         to the one in question; this records where we
         last passed a comment starter.  */
         to the one in question; this records where we
         last passed a comment starter.  */
-      struct lisp_parse_state state;
       /* If we did not already find the defun start, find it now.  */
       if (defun_start == 0)
        {
          defun_start = find_defun_start (comment_end, comment_end_byte);
          defun_start_byte = find_start_value_byte;
        }
       /* If we did not already find the defun start, find it now.  */
       if (defun_start == 0)
        {
          defun_start = find_defun_start (comment_end, comment_end_byte);
          defun_start_byte = find_start_value_byte;
        }
-      scan_sexps_forward (&state,
-                         defun_start, defun_start_byte,
-                         comment_end - 1, -10000, 0, Qnil, 0);
-      if (state.incomment)
-       {
-         /* scan_sexps_forward changed the direction of search in
-            global variables, so we need to update it completely.  */
-         
-         from = state.comstr_start;
-       }
-      else
+      do
        {
        {
-         from = comment_end;     
-       }
+         scan_sexps_forward (&state,
+                             defun_start, defun_start_byte,
+                             comment_end, -10000, 0, Qnil, 0);
+         defun_start = comment_end;
+         if (state.incomment == (comnested ? 1 : -1)
+             && state.comstyle == comstyle)
+           from = state.comstr_start;
+         else
+           {
+             from = comment_end;
+             if (state.incomment)
+               /* If comment_end is inside some other comment, maybe ours
+                  is nested, so we need to try again from within the
+                  surrounding comment.  Example: { a (* " *)  */
+               {
+                 /* FIXME: We should advance by one or two chars. */
+                 defun_start = state.comstr_start + 2;
+                 defun_start_byte = CHAR_TO_BYTE (defun_start);
+               }
+           }
+       } while (defun_start < comment_end);
+
       from_byte = CHAR_TO_BYTE (from);
       UPDATE_SYNTAX_TABLE_FORWARD (from - 1);
     }
       from_byte = CHAR_TO_BYTE (from);
       UPDATE_SYNTAX_TABLE_FORWARD (from - 1);
     }
@@ -635,7 +710,7 @@ back_comment (from, from_byte, stop, comnested, comstyle, charpos_ptr, bytepos_p
   *charpos_ptr = from;
   *bytepos_ptr = from_byte;
 
   *charpos_ptr = from;
   *bytepos_ptr = from_byte;
 
-  return from;
+  return (from == comment_end) ? -1 : from;
 }
 \f
 DEFUN ("syntax-table-p", Fsyntax_table_p, Ssyntax_table_p, 1, 1, 0,
 }
 \f
 DEFUN ("syntax-table-p", Fsyntax_table_p, Ssyntax_table_p, 1, 1, 0,
@@ -709,11 +784,12 @@ One argument, a syntax table.")
   (table)
      Lisp_Object table;
 {
   (table)
      Lisp_Object table;
 {
+  int idx;
   check_syntax_table (table);
   current_buffer->syntax_table = table;
   /* Indicate that this buffer now has a specified syntax table.  */
   check_syntax_table (table);
   current_buffer->syntax_table = table;
   /* Indicate that this buffer now has a specified syntax table.  */
-  current_buffer->local_var_flags
-    |= XFASTINT (buffer_local_flags.syntax_table);
+  idx = PER_BUFFER_VAR_IDX (syntax_table);
+  SET_PER_BUFFER_VALUE_P (current_buffer, idx, 1);
   return table;
 }
 \f
   return table;
 }
 \f
@@ -816,86 +892,35 @@ DEFUN ("matching-paren", Fmatching_paren, Smatching_paren, 1, 1, 0,
   return Qnil;
 }
 
   return Qnil;
 }
 
-/* This comment supplies the doc string for modify-syntax-entry,
-   for make-docfile to see.  We cannot put this in the real DEFUN
-   due to limits in the Unix cpp.
-
-DEFUN ("modify-syntax-entry", foo, bar, 2, 3, 0,
-  "Set syntax for character CHAR according to string S.\n\
-The syntax is changed only for table TABLE, which defaults to\n\
- the current buffer's syntax table.\n\
-The first character of S should be one of the following:\n\
-  Space or -  whitespace syntax.    w   word constituent.\n\
-  _           symbol constituent.   .   punctuation.\n\
-  (           open-parenthesis.     )   close-parenthesis.\n\
-  \"           string quote.         \\   escape.\n\
-  $           paired delimiter.     '   expression quote or prefix operator.\n\
-  <           comment starter.      >   comment ender.\n\
-  /           character-quote.      @   inherit from `standard-syntax-table'.\n\
-\n\
-Only single-character comment start and end sequences are represented thus.\n\
-Two-character sequences are represented as described below.\n\
-The second character of S is the matching parenthesis,\n\
- used only if the first character is `(' or `)'.\n\
-Any additional characters are flags.\n\
-Defined flags are the characters 1, 2, 3, 4, b, p, and n.\n\
- 1 means CHAR is the start of a two-char comment start sequence.\n\
- 2 means CHAR is the second character of such a sequence.\n\
- 3 means CHAR is the start of a two-char comment end sequence.\n\
- 4 means CHAR is the second character of such a sequence.\n\
-\n\
-There can be up to two orthogonal comment sequences.  This is to support\n\
-language modes such as C++.  By default, all comment sequences are of style\n\
-a, but you can set the comment sequence style to b (on the second character\n\
-of a comment-start, or the first character of a comment-end sequence) using\n\
-this flag:\n\
- b means CHAR is part of comment sequence b.\n\
- n means CHAR is part of a nestable comment sequence.\n\
-\n\
- p means CHAR is a prefix character for `backward-prefix-chars';\n\
-   such characters are treated as whitespace when they occur\n\
-   between expressions.")
-  (char, s, table)
-*/
-
-DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3, 
-  /* I really don't know why this is interactive
-     help-form should at least be made useful whilst reading the second arg
-   */
-  "cSet syntax for character: \nsSet syntax for %s to: ",
-  0 /* See immediately above */)
-  (c, newentry, syntax_table)
-     Lisp_Object c, newentry, syntax_table;
+DEFUN ("string-to-syntax", Fstring_to_syntax, Sstring_to_syntax, 1, 1, 0,
+  "Convert a syntax specification STRING into syntax cell form.\n\
+STRING should be a string as it is allowed as argument of\n\
+`modify-syntax-entry'.  Value is the equivalent cons cell\n\
+\(CODE . MATCHING-CHAR) that can be used as value of a `syntax-table'\n\
+text property.")
+  (string)
+     Lisp_Object string;
 {
   register unsigned char *p;
   register enum syntaxcode code;
   int val;
   Lisp_Object match;
 
 {
   register unsigned char *p;
   register enum syntaxcode code;
   int val;
   Lisp_Object match;
 
-  CHECK_NUMBER (c, 0);
-  CHECK_STRING (newentry, 1);
-
-  if (NILP (syntax_table))
-    syntax_table = current_buffer->syntax_table;
-  else
-    check_syntax_table (syntax_table);
+  CHECK_STRING (string, 0);
 
 
-  p = XSTRING (newentry)->data;
+  p = XSTRING (string)->data;
   code = (enum syntaxcode) syntax_spec_code[*p++];
   if (((int) code & 0377) == 0377)
     error ("invalid syntax description letter: %c", p[-1]);
 
   if (code == Sinherit)
   code = (enum syntaxcode) syntax_spec_code[*p++];
   if (((int) code & 0377) == 0377)
     error ("invalid syntax description letter: %c", p[-1]);
 
   if (code == Sinherit)
-    {
-      SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), Qnil);
-      return Qnil;
-    }
+    return Qnil;
 
   if (*p)
     {
       int len;
       int character = (STRING_CHAR_AND_LENGTH
 
   if (*p)
     {
       int len;
       int character = (STRING_CHAR_AND_LENGTH
-                      (p, STRING_BYTES (XSTRING (newentry)) - 1, len));
+                      (p, STRING_BYTES (XSTRING (string)) - 1, len));
       XSETINT (match, character);
       if (XFASTINT (match) == ' ')
        match = Qnil;
       XSETINT (match, character);
       if (XFASTINT (match) == ' ')
        match = Qnil;
@@ -938,13 +963,72 @@ DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3,
       }
        
   if (val < XVECTOR (Vsyntax_code_object)->size && NILP (match))
       }
        
   if (val < XVECTOR (Vsyntax_code_object)->size && NILP (match))
-    newentry = XVECTOR (Vsyntax_code_object)->contents[val];
+    return XVECTOR (Vsyntax_code_object)->contents[val];
   else
     /* Since we can't use a shared object, let's make a new one.  */
   else
     /* Since we can't use a shared object, let's make a new one.  */
-    newentry = Fcons (make_number (val), match);
-    
-  SET_RAW_SYNTAX_ENTRY (syntax_table, XINT (c), newentry);
+    return Fcons (make_number (val), match);
+}
+
+/* This comment supplies the doc string for modify-syntax-entry,
+   for make-docfile to see.  We cannot put this in the real DEFUN
+   due to limits in the Unix cpp.
+
+DEFUN ("modify-syntax-entry", foo, bar, 2, 3, 0,
+  "Set syntax for character CHAR according to string S.\n\
+The syntax is changed only for table TABLE, which defaults to\n\
+ the current buffer's syntax table.\n\
+The first character of S should be one of the following:\n\
+  Space or -  whitespace syntax.    w   word constituent.\n\
+  _           symbol constituent.   .   punctuation.\n\
+  (           open-parenthesis.     )   close-parenthesis.\n\
+  \"           string quote.         \\   escape.\n\
+  $           paired delimiter.     '   expression quote or prefix operator.\n\
+  <           comment starter.      >   comment ender.\n\
+  /           character-quote.      @   inherit from `standard-syntax-table'.\n\
+  |           generic string fence. !   generic comment fence.\n\
+\n\
+Only single-character comment start and end sequences are represented thus.\n\
+Two-character sequences are represented as described below.\n\
+The second character of S is the matching parenthesis,\n\
+ used only if the first character is `(' or `)'.\n\
+Any additional characters are flags.\n\
+Defined flags are the characters 1, 2, 3, 4, b, p, and n.\n\
+ 1 means CHAR is the start of a two-char comment start sequence.\n\
+ 2 means CHAR is the second character of such a sequence.\n\
+ 3 means CHAR is the start of a two-char comment end sequence.\n\
+ 4 means CHAR is the second character of such a sequence.\n\
+\n\
+There can be up to two orthogonal comment sequences.  This is to support\n\
+language modes such as C++.  By default, all comment sequences are of style\n\
+a, but you can set the comment sequence style to b (on the second character\n\
+of a comment-start, or the first character of a comment-end sequence) using\n\
+this flag:\n\
+ b means CHAR is part of comment sequence b.\n\
+ n means CHAR is part of a nestable comment sequence.\n\
+\n\
+ p means CHAR is a prefix character for `backward-prefix-chars';\n\
+   such characters are treated as whitespace when they occur\n\
+   between expressions.")
+  (char, s, table)
+*/
+
+DEFUN ("modify-syntax-entry", Fmodify_syntax_entry, Smodify_syntax_entry, 2, 3, 
+  /* I really don't know why this is interactive
+     help-form should at least be made useful whilst reading the second arg
+   */
+  "cSet syntax for character: \nsSet syntax for %s to: ",
+  0 /* See immediately above */)
+  (c, newentry, syntax_table)
+     Lisp_Object c, newentry, syntax_table;
+{
+  CHECK_NUMBER (c, 0);
 
 
+  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));
   return Qnil;
 }
 \f
   return Qnil;
 }
 \f
@@ -955,7 +1039,7 @@ describe_syntax (value)
     Lisp_Object value;
 {
   register enum syntaxcode code;
     Lisp_Object value;
 {
   register enum syntaxcode code;
-  char desc, match, start1, start2, end1, end2, prefix, comstyle;
+  char desc, start1, start2, end1, end2, prefix, comstyle, comnested;
   char str[2];
   Lisp_Object first, match_lisp;
 
   char str[2];
   Lisp_Object first, match_lisp;
 
@@ -995,6 +1079,7 @@ describe_syntax (value)
   end2 = (XINT (first) >> 19) & 1;
   prefix = (XINT (first) >> 20) & 1;
   comstyle = (XINT (first) >> 21) & 1;
   end2 = (XINT (first) >> 19) & 1;
   prefix = (XINT (first) >> 20) & 1;
   comstyle = (XINT (first) >> 21) & 1;
+  comnested = (XINT (first) >> 22) & 1;
 
   if ((int) code < 0 || (int) code >= (int) Smax)
     {
 
   if ((int) code < 0 || (int) code >= (int) Smax)
     {
@@ -1025,6 +1110,8 @@ describe_syntax (value)
     insert ("p", 1);
   if (comstyle)
     insert ("b", 1);
     insert ("p", 1);
   if (comstyle)
     insert ("b", 1);
+  if (comnested)
+    insert ("n", 1);
 
   insert_string ("\twhich means: ");
 
 
   insert_string ("\twhich means: ");
 
@@ -1043,7 +1130,7 @@ describe_syntax (value)
     case Sclose:
       insert_string ("close"); break;
     case Squote:
     case Sclose:
       insert_string ("close"); break;
     case Squote:
-      insert_string ("quote"); break;
+      insert_string ("prefix"); break;
     case Sstring:
       insert_string ("string"); break;
     case Smath:
     case Sstring:
       insert_string ("string"); break;
     case Smath:
@@ -1056,6 +1143,12 @@ describe_syntax (value)
       insert_string ("comment"); break;
     case Sendcomment:
       insert_string ("endcomment"); break;
       insert_string ("comment"); break;
     case Sendcomment:
       insert_string ("endcomment"); break;
+    case Sinherit:
+      insert_string ("inherit"); break;
+    case Scomment_fence:
+      insert_string ("comment fence"); break;
+    case Sstring_fence:
+      insert_string ("string fence"); break;
     default:
       insert_string ("invalid");
       return;
     default:
       insert_string ("invalid");
       return;
@@ -1078,6 +1171,8 @@ describe_syntax (value)
     insert_string (",\n\t  is the second character of a comment-end sequence");
   if (comstyle)
     insert_string (" (comment style b)");
     insert_string (",\n\t  is the second character of a comment-end sequence");
   if (comstyle)
     insert_string (" (comment style b)");
+  if (comnested)
+    insert_string (" (nestable)");
 
   if (prefix)
     insert_string (",\n\t  is a prefix character for `backward-prefix-chars'");
 
   if (prefix)
     insert_string (",\n\t  is a prefix character for `backward-prefix-chars'");
@@ -1222,31 +1317,25 @@ scan_words (from, count)
 DEFUN ("forward-word", Fforward_word, Sforward_word, 1, 1, "p",
   "Move point forward ARG words (backward if ARG is negative).\n\
 Normally returns t.\n\
 DEFUN ("forward-word", Fforward_word, Sforward_word, 1, 1, "p",
   "Move point forward ARG words (backward if ARG is negative).\n\
 Normally returns t.\n\
-If an edge of the buffer is reached, point is left there\n\
-and nil is returned.")
+If an edge of the buffer or a field boundary is reached, point is left there\n\
+and the function returns nil.  Field boundaries are not noticed if\n\
+`inhibit-field-text-motion' is non-nil.")
   (count)
      Lisp_Object count;
 {
   (count)
      Lisp_Object count;
 {
-  int val, prompt_end;
+  int orig_val, val;
   CHECK_NUMBER (count, 0);
 
   CHECK_NUMBER (count, 0);
 
-  if (!(val = scan_words (PT, XINT (count))))
-    {
-      SET_PT (XINT (count) > 0 ? ZV : BEGV);
-      return Qnil;
-    }
+  val = orig_val = scan_words (PT, XINT (count));
+  if (! orig_val)
+    val = XINT (count) > 0 ? ZV : BEGV;
 
 
-  /* If in a mini-buffer and moving backwards, stop at the end of the
-     prompt.  This prevents accidentially moving into the read-only
-     prompt.  */
-  if (INTEGERP (current_buffer->prompt_end_charpos)
-      && (prompt_end = XINT (current_buffer->prompt_end_charpos),
-         ((PT > prompt_end && val < prompt_end)
-          || (PT < prompt_end && val > prompt_end))))
-    val = prompt_end;
-    
+  /* Avoid jumping out of an input field.  */
+  val = XFASTINT (Fconstrain_to_field (make_number (val), make_number (PT),
+                                      Qt, Qnil, Qnil));
+  
   SET_PT (val);
   SET_PT (val);
-  return Qt;
+  return val == orig_val ? Qt : Qnil;
 }
 \f
 Lisp_Object skip_chars ();
 }
 \f
 Lisp_Object skip_chars ();
@@ -1304,17 +1393,11 @@ skip_chars (forwardp, syntaxp, string, lim)
      int forwardp, syntaxp;
      Lisp_Object string, lim;
 {
      int forwardp, syntaxp;
      Lisp_Object string, lim;
 {
-  register unsigned char *p, *pend;
   register unsigned int c;
   register unsigned int c;
-  register int ch;
   unsigned char fastmap[0400];
   /* If SYNTAXP is 0, STRING may contain multi-byte form of characters
   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, we set the
-     first byte of multibyte form (i.e. base leading-code) in FASTMAP
-     and set the actual ranges of characters in CHAR_RANGES.  In the
-     form "X-Y" of STRING, both X and Y must belong to the same
-     character set because a range striding across character sets is
-     meaningless.  */
+     of which codes don't fit in FASTMAP.  In that case, set the
+     ranges of characters in CHAR_RANGES.  */
   int *char_ranges;
   int n_char_ranges = 0;
   int negate = 0;
   int *char_ranges;
   int n_char_ranges = 0;
   int negate = 0;
@@ -1322,12 +1405,34 @@ skip_chars (forwardp, syntaxp, string, lim)
   int multibyte = !NILP (current_buffer->enable_multibyte_characters);
   int string_multibyte;
   int size_byte;
   int multibyte = !NILP (current_buffer->enable_multibyte_characters);
   int string_multibyte;
   int size_byte;
+  unsigned char *str;
+  int len;
 
   CHECK_STRING (string, 0);
   char_ranges = (int *) alloca (XSTRING (string)->size * (sizeof (int)) * 2);
   string_multibyte = STRING_MULTIBYTE (string);
 
   CHECK_STRING (string, 0);
   char_ranges = (int *) alloca (XSTRING (string)->size * (sizeof (int)) * 2);
   string_multibyte = STRING_MULTIBYTE (string);
+  str = XSTRING (string)->data;
   size_byte = STRING_BYTES (XSTRING (string));
 
   size_byte = STRING_BYTES (XSTRING (string));
 
+  /* Adjust the multibyteness of the string to that of the buffer.  */
+  if (multibyte != string_multibyte)
+    {
+      int nbytes;
+
+      if (multibyte)
+       nbytes = count_size_as_multibyte (XSTRING (string)->data,
+                                         XSTRING (string)->size);
+      else
+       nbytes = XSTRING (string)->size;
+      if (nbytes != size_byte)
+       {
+         str = (unsigned char *) alloca (nbytes);
+         copy_text (XSTRING (string)->data, str, size_byte,
+                    string_multibyte, multibyte);
+         size_byte = nbytes;
+       }
+    }
+
   if (NILP (lim))
     XSETINT (lim, forwardp ? ZV : BEGV);
   else
   if (NILP (lim))
     XSETINT (lim, forwardp ? ZV : BEGV);
   else
@@ -1341,12 +1446,12 @@ skip_chars (forwardp, syntaxp, string, lim)
 
   bzero (fastmap, sizeof fastmap);
 
 
   bzero (fastmap, sizeof fastmap);
 
-  i = 0, i_byte = 0;
+  i_byte = 0;
 
   if (i_byte < size_byte
       && XSTRING (string)->data[0] == '^')
     {
 
   if (i_byte < size_byte
       && XSTRING (string)->data[0] == '^')
     {
-      negate = 1; i++, i_byte++;
+      negate = 1; i_byte++;
     }
 
   /* Find the characters specified and set their elements of fastmap.
     }
 
   /* Find the characters specified and set their elements of fastmap.
@@ -1355,22 +1460,8 @@ skip_chars (forwardp, syntaxp, string, lim)
 
   while (i_byte < size_byte)
     {
 
   while (i_byte < size_byte)
     {
-      int c_leading_code;
-
-      if (string_multibyte)
-       {
-         c_leading_code = XSTRING (string)->data[i_byte];
-         FETCH_STRING_CHAR_ADVANCE (c, string, i, i_byte);
-       }
-      else
-       c = c_leading_code = XSTRING (string)->data[i_byte++];
-
-      /* Convert multibyteness between what the string has
-        and what the buffer has.  */
-      if (multibyte)
-       c = unibyte_char_to_multibyte (c);
-      else
-       c &= 0377;
+      c = STRING_CHAR_AND_LENGTH (str + i_byte, size_byte - i_byte, len);
+      i_byte += len;
 
       if (syntaxp)
        fastmap[syntax_spec_code[c & 0377]] = 1;
 
       if (syntaxp)
        fastmap[syntax_spec_code[c & 0377]] = 1;
@@ -1381,62 +1472,58 @@ skip_chars (forwardp, syntaxp, string, lim)
              if (i_byte == size_byte)
                break;
 
              if (i_byte == size_byte)
                break;
 
-             if (string_multibyte)
-               {
-                 c_leading_code = XSTRING (string)->data[i_byte];
-                 FETCH_STRING_CHAR_ADVANCE (c, string, i, i_byte);
-               }
-             else
-               c = c_leading_code = XSTRING (string)->data[i_byte++];
+             c = STRING_CHAR_AND_LENGTH (str+i_byte, size_byte-i_byte, len);
+             i_byte += len;
            }
          if (i_byte < size_byte
            }
          if (i_byte < size_byte
-             && XSTRING (string)->data[i_byte] == '-')
+             && str[i_byte] == '-')
            {
            {
-             unsigned int c2, c2_leading_code;
+             unsigned int c2;
 
              /* Skip over the dash.  */
 
              /* Skip over the dash.  */
-             i++, i_byte++;
+             i_byte++;
 
              if (i_byte == size_byte)
                break;
 
              /* Get the end of the range.  */
 
              if (i_byte == size_byte)
                break;
 
              /* Get the end of the range.  */
-             if (string_multibyte)
-               {
-                 c2_leading_code = XSTRING (string)->data[i_byte];
-                 FETCH_STRING_CHAR_ADVANCE (c2, string, i, i_byte);
-               }
-             else
-               c2 = XSTRING (string)->data[i_byte++];
+             c2 =STRING_CHAR_AND_LENGTH (str+i_byte, size_byte-i_byte, len);
+             i_byte += len;
 
              if (SINGLE_BYTE_CHAR_P (c))
                {
                  if (! SINGLE_BYTE_CHAR_P (c2))
 
              if (SINGLE_BYTE_CHAR_P (c))
                {
                  if (! SINGLE_BYTE_CHAR_P (c2))
-                   error ("Invalid charcter range: %s",
-                          XSTRING (string)->data);
+                   {
+                     /* 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++] = c2;
+                     c2 = 0377;
+                   }
                  while (c <= c2)
                    {
                      fastmap[c] = 1;
                      c++;
                    }
                }
                  while (c <= c2)
                    {
                      fastmap[c] = 1;
                      c++;
                    }
                }
-             else
+             else if (c <= c2) /* Both C and C2 are multibyte char.  */
                {
                {
-                 if (c_leading_code != c2_leading_code)
-                   error ("Invalid charcter range: %s",
-                          XSTRING (string)->data);
-                 fastmap[c_leading_code] = 1;
-                 if (c <= c2)
-                   {
-                     char_ranges[n_char_ranges++] = c;
-                     char_ranges[n_char_ranges++] = c2;
-                   }
+                 char_ranges[n_char_ranges++] = c;
+                 char_ranges[n_char_ranges++] = c2;
                }
            }
          else
            {
                }
            }
          else
            {
-             fastmap[c_leading_code] = 1;
-             if (!SINGLE_BYTE_CHAR_P (c))
+             if (SINGLE_BYTE_CHAR_P (c))
+               fastmap[c] = 1;
+             else
                {
                  char_ranges[n_char_ranges++] = c;
                  char_ranges[n_char_ranges++] = c;
                {
                  char_ranges[n_char_ranges++] = c;
                  char_ranges[n_char_ranges++] = c;
@@ -1445,19 +1532,10 @@ skip_chars (forwardp, syntaxp, string, lim)
        }
     }
 
        }
     }
 
-  /* If ^ was the first character, complement the fastmap.  In
-     addition, as all multibyte characters have possibility of
-     matching, set all entries for base leading codes, which is
-     harmless even if SYNTAXP is 1.  */
-
+  /* If ^ was the first character, complement the fastmap.  */
   if (negate)
     for (i = 0; i < sizeof fastmap; i++)
   if (negate)
     for (i = 0; i < sizeof fastmap; i++)
-      {
-       if (!multibyte || !BASE_LEADING_CODE_P (i))
-         fastmap[i] ^= 1;
-       else
-         fastmap[i] = 1;
-      }
+      fastmap[i] ^= 1;
 
   {
     int start_point = PT;
 
   {
     int start_point = PT;
@@ -1532,28 +1610,32 @@ skip_chars (forwardp, syntaxp, string, lim)
        if (forwardp)
          {
            if (multibyte)
        if (forwardp)
          {
            if (multibyte)
-             while (pos < XINT (lim) && fastmap[(c = FETCH_BYTE (pos_byte))])
+             while (pos < XINT (lim))
                {
                {
-                 if (!BASE_LEADING_CODE_P (c))
-                   INC_BOTH (pos, pos_byte);
-                 else if (n_char_ranges)
+                 c = FETCH_MULTIBYTE_CHAR (pos_byte);
+                 if (SINGLE_BYTE_CHAR_P (c))
                    {
                    {
-                     /* We much check CHAR_RANGES for a multibyte
-                        character.  */
-                     ch = FETCH_MULTIBYTE_CHAR (pos_byte);
-                     for (i = 0; i < n_char_ranges; i += 2)
-                       if ((ch >= char_ranges[i] && ch <= char_ranges[i + 1]))
-                         break;
-                     if (!(negate ^ (i < n_char_ranges)))
+                     if (!fastmap[c])
                        break;
                        break;
-
-                     INC_BOTH (pos, pos_byte);
                    }
                  else
                    {
                    }
                  else
                    {
-                     if (!negate) break;
-                     INC_BOTH (pos, pos_byte);
+                     /* 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;
                    }
                    }
+                 INC_BOTH (pos, pos_byte);
                }
            else
              while (pos < XINT (lim) && fastmap[FETCH_BYTE (pos)])
                }
            else
              while (pos < XINT (lim) && fastmap[FETCH_BYTE (pos)])
@@ -1564,41 +1646,26 @@ skip_chars (forwardp, syntaxp, string, lim)
            if (multibyte)
              while (pos > XINT (lim))
                {
            if (multibyte)
              while (pos > XINT (lim))
                {
-                 int savepos = pos_byte;
-                 DEC_BOTH (pos, pos_byte);
-                 if (fastmap[(c = FETCH_BYTE (pos_byte))])
+                 int prev_pos_byte = pos_byte;
+
+                 DEC_POS (prev_pos_byte);
+                 c = FETCH_MULTIBYTE_CHAR (prev_pos_byte);
+                 if (SINGLE_BYTE_CHAR_P (c))
                    {
                    {
-                     if (!BASE_LEADING_CODE_P (c))
-                       ;
-                     else if (n_char_ranges)
-                       {
-                         /* We much check CHAR_RANGES for a multibyte
-                            character.  */
-                         ch = FETCH_MULTIBYTE_CHAR (pos_byte);
-                         for (i = 0; i < n_char_ranges; i += 2)
-                           if (ch >= char_ranges[i] && ch <= char_ranges[i + 1])
-                             break;
-                         if (!(negate ^ (i < n_char_ranges)))
-                           {
-                             pos++;
-                             pos_byte = savepos;
-                             break;
-                           }
-                       }
-                     else
-                       if (!negate)
-                         {
-                           pos++;
-                           pos_byte = savepos;
-                           break;
-                         }
+                     if (!fastmap[c])
+                       break;
                    }
                  else
                    {
                    }
                  else
                    {
-                     pos++;
-                     pos_byte = savepos;
-                     break;
+                     /* 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;
                    }
                    }
+                 pos--;
+                 pos_byte = prev_pos_byte;
                }
            else
              while (pos > XINT (lim) && fastmap[FETCH_BYTE (pos - 1)])
                }
            else
              while (pos > XINT (lim) && fastmap[FETCH_BYTE (pos - 1)])
@@ -1680,7 +1747,8 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
       code = syntax & 0xff;
       if (code == Sendcomment
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
       code = syntax & 0xff;
       if (code == Sendcomment
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
-         && --nesting <= 0)
+         && (SYNTAX_FLAGS_COMMENT_NESTED (syntax) ?
+             (nesting > 0 && --nesting == 0) : nesting < 0))
        /* we have encountered a comment end of the same style
           as the comment sequence which began this comment
           section */
        /* we have encountered a comment end of the same style
           as the comment sequence which began this comment
           section */
@@ -1693,6 +1761,7 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
        break;
       if (nesting > 0
          && code == Scomment
        break;
       if (nesting > 0
          && code == Scomment
+         && SYNTAX_FLAGS_COMMENT_NESTED (syntax)
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style)
        /* we have encountered a nested comment of the same style
           as the comment sequence which began this comment section */
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style)
        /* we have encountered a nested comment of the same style
           as the comment sequence which began this comment section */
@@ -1704,23 +1773,29 @@ forw_comment (from, from_byte, stop, nesting, style, prev_syntax,
       if (from < stop && SYNTAX_FLAGS_COMEND_FIRST (syntax)
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
          && (c1 = FETCH_CHAR (from_byte),
       if (from < stop && SYNTAX_FLAGS_COMEND_FIRST (syntax)
          && SYNTAX_FLAGS_COMMENT_STYLE (syntax) == style
          && (c1 = FETCH_CHAR (from_byte),
-             SYNTAX_COMEND_SECOND (c1)))
-       if (--nesting <= 0)
-         /* we have encountered a comment end of the same style
-            as the comment sequence which began this comment
-            section */
-         break;
-       else
-         {
-           INC_BOTH (from, from_byte);
-           UPDATE_SYNTAX_TABLE_FORWARD (from);
-         }
+             SYNTAX_COMEND_SECOND (c1))
+         && ((SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
+              SYNTAX_COMMENT_NESTED (c1)) ? nesting > 0 : nesting < 0))
+       {
+         if (--nesting <= 0)
+           /* we have encountered a comment end of the same style
+              as the comment sequence which began this comment
+              section */
+           break;
+         else
+           {
+             INC_BOTH (from, from_byte);
+             UPDATE_SYNTAX_TABLE_FORWARD (from);
+           }
+       }
       if (nesting > 0
          && from < stop
          && SYNTAX_FLAGS_COMSTART_FIRST (syntax)
          && (c1 = FETCH_CHAR (from_byte),
              SYNTAX_COMMENT_STYLE (c1) == style
       if (nesting > 0
          && from < stop
          && SYNTAX_FLAGS_COMSTART_FIRST (syntax)
          && (c1 = FETCH_CHAR (from_byte),
              SYNTAX_COMMENT_STYLE (c1) == style
-             && SYNTAX_COMSTART_SECOND (c1)))
+             && SYNTAX_COMSTART_SECOND (c1))
+         && (SYNTAX_FLAGS_COMMENT_NESTED (syntax) ||
+             SYNTAX_COMMENT_NESTED (c1)))
        /* we have encountered a nested comment of the same style
           as the comment sequence which began this comment
           section */
        /* we have encountered a nested comment of the same style
           as the comment sequence which began this comment
           section */
@@ -1783,9 +1858,9 @@ between them, return t; otherwise return nil.")
          code = SYNTAX (c);
          comstart_first = SYNTAX_COMSTART_FIRST (c);
          comnested = SYNTAX_COMMENT_NESTED (c);
          code = SYNTAX (c);
          comstart_first = SYNTAX_COMSTART_FIRST (c);
          comnested = SYNTAX_COMMENT_NESTED (c);
+         comstyle = SYNTAX_COMMENT_STYLE (c);
          INC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_FORWARD (from);
          INC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_FORWARD (from);
-         comstyle = 0;
          if (from < stop && comstart_first
              && (c1 = FETCH_CHAR (from_byte),
                  SYNTAX_COMSTART_SECOND (c1)))
          if (from < stop && comstart_first
              && (c1 = FETCH_CHAR (from_byte),
                  SYNTAX_COMSTART_SECOND (c1)))
@@ -1802,7 +1877,7 @@ between them, return t; otherwise return nil.")
              UPDATE_SYNTAX_TABLE_FORWARD (from);
            }
        }
              UPDATE_SYNTAX_TABLE_FORWARD (from);
            }
        }
-      while (code == Swhitespace || code == Sendcomment);
+      while (code == Swhitespace || (code == Sendcomment && c == '\n'));
 
       if (code == Scomment_fence)
        comstyle = ST_COMMENT_STYLE;
 
       if (code == Scomment_fence)
        comstyle = ST_COMMENT_STYLE;
@@ -1833,7 +1908,7 @@ between them, return t; otherwise return nil.")
     {
       while (1)
        {
     {
       while (1)
        {
-         int quoted, comstart_second;
+         int quoted;
 
          if (from <= stop)
            {
 
          if (from <= stop)
            {
@@ -1845,18 +1920,12 @@ 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);
          DEC_BOTH (from, from_byte);
          /* char_quoted does UPDATE_SYNTAX_TABLE_BACKWARD (from).  */
          quoted = char_quoted (from, from_byte);
-         if (quoted)
-           {
-             DEC_BOTH (from, from_byte);
-             goto leave;
-           }
          c = FETCH_CHAR (from_byte);
          code = SYNTAX (c);
          comstyle = 0;
          comnested = SYNTAX_COMMENT_NESTED (c);
          if (code == Sendcomment)
            comstyle = SYNTAX_COMMENT_STYLE (c);
          c = FETCH_CHAR (from_byte);
          code = SYNTAX (c);
          comstyle = 0;
          comnested = SYNTAX_COMMENT_NESTED (c);
          if (code == Sendcomment)
            comstyle = SYNTAX_COMMENT_STYLE (c);
-         comstart_second = SYNTAX_COMSTART_SECOND (c);
          if (from > stop && SYNTAX_COMEND_SECOND (c)
              && prev_char_comend_first (from, from_byte)
              && !char_quoted (from - 1, dec_bytepos (from_byte)))
          if (from > stop && SYNTAX_COMEND_SECOND (c)
              && prev_char_comend_first (from, from_byte)
              && !char_quoted (from - 1, dec_bytepos (from_byte)))
@@ -1872,13 +1941,6 @@ between them, return t; otherwise return nil.")
              comstyle = SYNTAX_COMMENT_STYLE (c1);
              comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
            }
              comstyle = SYNTAX_COMMENT_STYLE (c1);
              comnested = comnested || SYNTAX_COMMENT_NESTED (c1);
            }
-         if (from > stop && comstart_second
-             && prev_char_comstart_first (from, from_byte)
-             && !char_quoted (from - 1, dec_bytepos (from_byte)))
-           {
-             code = Scomment;
-             DEC_BOTH (from, from_byte);
-           }
 
          if (code == Scomment_fence)
            {
 
          if (code == Scomment_fence)
            {
@@ -1910,12 +1972,31 @@ between them, return t; otherwise return nil.")
            {
              found = back_comment (from, from_byte, stop, comnested, comstyle,
                                    &out_charpos, &out_bytepos);
            {
              found = back_comment (from, from_byte, stop, comnested, comstyle,
                                    &out_charpos, &out_bytepos);
-             if (found != -1)
-               from = out_charpos, from_byte = out_bytepos;
-             /* We have skipped one comment.  */
-             break;
+             if (found == -1)
+               {
+                 if (c == '\n')
+                   /* This end-of-line is not an end-of-comment.
+                      Treat it like a whitespace.
+                      CC-mode (and maybe others) relies on this behavior.  */
+                   ;
+                 else
+                   {
+                     /* Failure: we should go back to the end of this
+                        not-quite-endcomment.  */
+                     if (SYNTAX(c) != code)
+                       /* It was a two-char Sendcomment.  */
+                       INC_BOTH (from, from_byte);
+                     goto leave;
+                   }
+               }
+             else
+               {
+                 /* We have skipped one comment.  */
+                 from = out_charpos, from_byte = out_bytepos;
+                 break;
+               }
            }
            }
-         else if (code != Swhitespace && code != Scomment)
+         else if (code != Swhitespace || quoted)
            {
            leave:
              immediate_quit = 0;
            {
            leave:
              immediate_quit = 0;
@@ -1933,6 +2014,13 @@ between them, return t; otherwise return nil.")
   return Qt;
 }
 \f
   return Qt;
 }
 \f
+/* Return syntax code of character C if C is a single byte character
+   or `multibyte_symbol_p' is zero.  Otherwise, return Ssymbol.  */
+
+#define SYNTAX_WITH_MULTIBYTE_CHECK(c)                 \
+  ((SINGLE_BYTE_CHAR_P (c) || !multibyte_symbol_p)     \
+   ? SYNTAX (c) : Ssymbol)
+
 static Lisp_Object
 scan_lists (from, count, depth, sexpflag)
      register int from;
 static Lisp_Object
 scan_lists (from, count, depth, sexpflag)
      register int from;
@@ -1954,6 +2042,7 @@ scan_lists (from, count, depth, sexpflag)
   int from_byte;
   int out_bytepos, out_charpos;
   int temp, dummy;
   int from_byte;
   int out_bytepos, out_charpos;
   int temp, dummy;
+  int multibyte_symbol_p = sexpflag && multibyte_syntax_as_symbol;
 
   if (depth > 0) min_depth = 0;
 
 
   if (depth > 0) min_depth = 0;
 
@@ -1973,9 +2062,10 @@ scan_lists (from, count, depth, sexpflag)
          int comstart_first, prefix;
          UPDATE_SYNTAX_TABLE_FORWARD (from);
          c = FETCH_CHAR (from_byte);
          int comstart_first, prefix;
          UPDATE_SYNTAX_TABLE_FORWARD (from);
          c = FETCH_CHAR (from_byte);
-         code = SYNTAX (c);
+         code = SYNTAX_WITH_MULTIBYTE_CHECK (c);
          comstart_first = SYNTAX_COMSTART_FIRST (c);
          comnested = SYNTAX_COMMENT_NESTED (c);
          comstart_first = SYNTAX_COMSTART_FIRST (c);
          comnested = SYNTAX_COMMENT_NESTED (c);
+         comstyle = SYNTAX_COMMENT_STYLE (c);
          prefix = SYNTAX_PREFIX (c);
          if (depth == min_depth)
            last_good = from;
          prefix = SYNTAX_PREFIX (c);
          if (depth == min_depth)
            last_good = from;
@@ -2017,7 +2107,8 @@ scan_lists (from, count, depth, sexpflag)
                  UPDATE_SYNTAX_TABLE_FORWARD (from);
 
                  /* Some compilers can't handle this inside the switch.  */
                  UPDATE_SYNTAX_TABLE_FORWARD (from);
 
                  /* Some compilers can't handle this inside the switch.  */
-                 temp = SYNTAX (FETCH_CHAR (from_byte));
+                 c = FETCH_CHAR (from_byte);
+                 temp = SYNTAX_WITH_MULTIBYTE_CHECK (c);
                  switch (temp)
                    {
                    case Scharquote:
                  switch (temp)
                    {
                    case Scharquote:
@@ -2092,13 +2183,15 @@ scan_lists (from, count, depth, sexpflag)
                {
                  if (from >= stop) goto lose;
                  UPDATE_SYNTAX_TABLE_FORWARD (from);
                {
                  if (from >= stop) goto lose;
                  UPDATE_SYNTAX_TABLE_FORWARD (from);
-                 if (code == Sstring 
-                     ? (FETCH_CHAR (from_byte) == stringterm)
-                     : SYNTAX (FETCH_CHAR (from_byte)) == Sstring_fence) 
+                 c = FETCH_CHAR (from_byte);
+                 if (code == Sstring
+                     ? (c == stringterm
+                        && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
+                     : SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring_fence)
                    break;
 
                  /* Some compilers can't handle this inside the switch.  */
                    break;
 
                  /* Some compilers can't handle this inside the switch.  */
-                 temp = SYNTAX (FETCH_CHAR (from_byte));
+                 temp = SYNTAX_WITH_MULTIBYTE_CHECK (c);
                  switch (temp)
                    {
                    case Scharquote:
                  switch (temp)
                    {
                    case Scharquote:
@@ -2131,7 +2224,7 @@ scan_lists (from, count, depth, sexpflag)
          DEC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_BACKWARD (from);
          c = FETCH_CHAR (from_byte);
          DEC_BOTH (from, from_byte);
          UPDATE_SYNTAX_TABLE_BACKWARD (from);
          c = FETCH_CHAR (from_byte);
-         code = SYNTAX (c);
+         code = SYNTAX_WITH_MULTIBYTE_CHECK (c);
          if (depth == min_depth)
            last_good = from;
          comstyle = 0;
          if (depth == min_depth)
            last_good = from;
          comstyle = 0;
@@ -2154,7 +2247,7 @@ scan_lists (from, count, depth, sexpflag)
            }
          
          /* Quoting turns anything except a comment-ender
            }
          
          /* Quoting turns anything except a comment-ender
-            into a word character.  Note that this if cannot be true
+            into a word character.  Note that this cannot be true
             if we decremented FROM in the if-statement above.  */
          if (code != Sendcomment && char_quoted (from, from_byte))
            code = Sword;
             if we decremented FROM in the if-statement above.  */
          if (code != Sendcomment && char_quoted (from, from_byte))
            code = Sword;
@@ -2179,7 +2272,7 @@ scan_lists (from, count, depth, sexpflag)
                    temp_pos--;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                  c1 = FETCH_CHAR (temp_pos);
                    temp_pos--;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                  c1 = FETCH_CHAR (temp_pos);
-                 temp_code = SYNTAX (c1);
+                 temp_code = SYNTAX_WITH_MULTIBYTE_CHECK (c1);
                  /* Don't allow comment-end to be quoted.  */
                  if (temp_code == Sendcomment)
                    goto done2;
                  /* Don't allow comment-end to be quoted.  */
                  if (temp_code == Sendcomment)
                    goto done2;
@@ -2191,7 +2284,7 @@ scan_lists (from, count, depth, sexpflag)
                      UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                    }
                  c1 = FETCH_CHAR (temp_pos);
                      UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                    }
                  c1 = FETCH_CHAR (temp_pos);
-                 temp_code = SYNTAX (c1);
+                 temp_code = SYNTAX_WITH_MULTIBYTE_CHECK (c1);
                  if (! (quoted || temp_code == Sword
                         || temp_code == Ssymbol
                         || temp_code == Squote))
                  if (! (quoted || temp_code == Sword
                         || temp_code == Ssymbol
                         || temp_code == Squote))
@@ -2233,6 +2326,12 @@ scan_lists (from, count, depth, sexpflag)
                break;
              found = back_comment (from, from_byte, stop, comnested, comstyle,
                                    &out_charpos, &out_bytepos);
                break;
              found = back_comment (from, from_byte, stop, comnested, comstyle,
                                    &out_charpos, &out_bytepos);
+             /* FIXME:  if found == -1, then it really wasn't a comment-end.
+                For single-char Sendcomment, we can't do much about it apart
+                from skipping the char.
+                For 2-char endcomments, we could try again, taking both
+                chars as separate entities, but it's a lot of trouble
+                for very little gain, so we don't bother either.  -sm */
              if (found != -1)
                from = out_charpos, from_byte = out_bytepos;
              break;
              if (found != -1)
                from = out_charpos, from_byte = out_bytepos;
              break;
@@ -2245,7 +2344,8 @@ scan_lists (from, count, depth, sexpflag)
                  if (from == stop) goto lose;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from);
                  if (!char_quoted (from, from_byte) 
                  if (from == stop) goto lose;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from);
                  if (!char_quoted (from, from_byte) 
-                     && SYNTAX (FETCH_CHAR (from_byte)) == code)
+                     && (c = FETCH_CHAR (from_byte),
+                         SYNTAX_WITH_MULTIBYTE_CHECK (c) == code))
                    break;
                }
              if (code == Sstring_fence && !depth && sexpflag) goto done2;
                    break;
                }
              if (code == Sstring_fence && !depth && sexpflag) goto done2;
@@ -2263,7 +2363,8 @@ scan_lists (from, count, depth, sexpflag)
                    temp_pos--;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                  if (!char_quoted (from - 1, temp_pos)
                    temp_pos--;
                  UPDATE_SYNTAX_TABLE_BACKWARD (from - 1);
                  if (!char_quoted (from - 1, temp_pos)
-                     && stringterm == FETCH_CHAR (temp_pos))
+                     && stringterm == (c = FETCH_CHAR (temp_pos))
+                     && SYNTAX_WITH_MULTIBYTE_CHECK (c) == Sstring)
                    break;
                  DEC_BOTH (from, from_byte);
                }
                    break;
                  DEC_BOTH (from, from_byte);
                }
@@ -2462,14 +2563,14 @@ do { prev_from = from;                          \
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
       /* Check whether we are inside string_fence-style string: */
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
       /* Check whether we are inside string_fence-style string: */
-      state.instring = ( !NILP (tem) 
-                        ? ( INTEGERP (tem) ? XINT (tem) : ST_STRING_STYLE) 
-                        : -1);
+      state.instring = (!NILP (tem) 
+                       ? (INTEGERP (tem) ? XINT (tem) : ST_STRING_STYLE) 
+                       : -1);
 
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
 
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
-      state.incomment = ( !NILP (tem)
-                        ? ( INTEGERP (tem) ? XINT (tem) : -1)
+      state.incomment = (!NILP (tem)
+                        ? (INTEGERP (tem) ? XINT (tem) : -1)
                         : 0);
 
       oldstate = Fcdr (oldstate);
                         : 0);
 
       oldstate = Fcdr (oldstate);
@@ -2481,8 +2582,8 @@ do { prev_from = from;                            \
       oldstate = Fcdr (oldstate);
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
       oldstate = Fcdr (oldstate);
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
-      state.comstyle = NILP (tem) ? 0 : ( EQ (tem, Qsyntax_table) 
-                                         ? ST_COMMENT_STYLE : 1 );
+      state.comstyle = NILP (tem) ? 0 : (EQ (tem, Qsyntax_table) 
+                                        ? ST_COMMENT_STYLE : 1);
 
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
 
       oldstate = Fcdr (oldstate);
       tem = Fcar (oldstate);
@@ -2494,7 +2595,7 @@ do { prev_from = from;                            \
          /* curlevel++->last ran into compiler bug on Apollo */
          curlevel->last = XINT (Fcar (tem));
          if (++curlevel == endlevel)
          /* curlevel++->last ran into compiler bug on Apollo */
          curlevel->last = XINT (Fcar (tem));
          if (++curlevel == endlevel)
-           error ("Nesting too deep for parser");
+           curlevel--; /* error ("Nesting too deep for parser"); */
          curlevel->prev = -1;
          curlevel->last = -1;
          tem = Fcdr (tem);
          curlevel->prev = -1;
          curlevel->last = -1;
          tem = Fcdr (tem);
@@ -2537,6 +2638,7 @@ do { prev_from = from;                            \
 
       if (code == Scomment)
        {
 
       if (code == Scomment)
        {
+         state.comstyle = SYNTAX_FLAGS_COMMENT_STYLE (prev_from_syntax);
          state.incomment = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax) ?
                             1 : -1);
          state.comstr_start = prev_from;
          state.incomment = (SYNTAX_FLAGS_COMMENT_NESTED (prev_from_syntax) ?
                             1 : -1);
          state.comstr_start = prev_from;
@@ -2612,28 +2714,24 @@ do { prev_from = from;                          \
          curlevel->prev = curlevel->last;
          break;
 
          curlevel->prev = curlevel->last;
          break;
 
-       startincomment:
-         if (commentstop == 1)
-           goto done;
-         goto commentloop;
-
        case Scomment:
        case Scomment:
-         assert (state.incomment != 0); /* state.incomment = -1; */
          if (commentstop || boundary_stop) goto done;
          if (commentstop || boundary_stop) goto done;
-       commentloop:
-         /* The (from == BEGV) test is to enter the loop in the middle so
+       startincomment:
+         /* The (from == BEGV) test was to enter the loop in the middle so
             that we find a 2-char comment ender even if we start in the
             that we find a 2-char comment ender even if we start in the
-            middle of it.  */
+            middle of it.  We don't want to do that if we're just at the
+            beginning of the comment (think of (*) ... (*)).  */
          found = forw_comment (from, from_byte, end,
                                state.incomment, state.comstyle,
          found = forw_comment (from, from_byte, end,
                                state.incomment, state.comstyle,
-                               (from == BEGV) ? 0 : prev_from_syntax,
+                               (from == BEGV || from < state.comstr_start + 3)
+                               ? 0 : prev_from_syntax,
                                &out_charpos, &out_bytepos, &state.incomment);
          from = out_charpos; from_byte = out_bytepos;
          /* Beware!  prev_from and friends are invalid now.
             Luckily, the `done' doesn't use them and the INC_FROM
             sets them to a sane value without looking at them. */
          if (!found) goto done;
                                &out_charpos, &out_bytepos, &state.incomment);
          from = out_charpos; from_byte = out_bytepos;
          /* Beware!  prev_from and friends are invalid now.
             Luckily, the `done' doesn't use them and the INC_FROM
             sets them to a sane value without looking at them. */
          if (!found) goto done;
-         INC_FROM; 
+         INC_FROM;
          state.incomment = 0;
          state.comstyle = 0;   /* reset the comment style */
          if (boundary_stop) goto done;
          state.incomment = 0;
          state.comstyle = 0;   /* reset the comment style */
          if (boundary_stop) goto done;
@@ -2645,7 +2743,7 @@ do { prev_from = from;                            \
          /* curlevel++->last ran into compiler bug on Apollo */
          curlevel->last = prev_from;
          if (++curlevel == endlevel)
          /* curlevel++->last ran into compiler bug on Apollo */
          curlevel->last = prev_from;
          if (++curlevel == endlevel)
-           error ("Nesting too deep for parser");
+           curlevel--; /* error ("Nesting too deep for parser"); */
          curlevel->prev = -1;
          curlevel->last = -1;
          if (targetdepth == depth) goto done;
          curlevel->prev = -1;
          curlevel->last = -1;
          if (targetdepth == depth) goto done;
@@ -2759,7 +2857,7 @@ Value is a list of ten elements describing final state of parsing:\n\
     else an integer (the current comment nesting).\n\
  5. t if following a quote character.\n\
  6. the minimum paren-depth encountered during this scan.\n\
     else an integer (the current comment nesting).\n\
  5. t if following a quote character.\n\
  6. the minimum paren-depth encountered during this scan.\n\
- 7. t if in a comment of style b; `syntax-table' if the comment\n\
+ 7. t if in a comment of style b; symbol `syntax-table' if the comment\n\
     should be terminated by a generic comment delimiter.\n\
  8. character address of start of comment or string; nil if not in one.\n\
  9. Intermediate data for continuation of parsing (subject to change).\n\
     should be terminated by a generic comment delimiter.\n\
  8. character address of start of comment or string; nil if not in one.\n\
  9. Intermediate data for continuation of parsing (subject to change).\n\
@@ -2771,8 +2869,8 @@ Fifth arg STATE is a nine-element list like what this function returns.\n\
  It is used to initialize the state of the parse.  Elements number 1, 2, 6\n\
  and 8 are ignored; you can leave off element 8 (the last) entirely.\n\
 Sixth arg COMMENTSTOP non-nil means stop at the start of a comment.\n\
  It is used to initialize the state of the parse.  Elements number 1, 2, 6\n\
  and 8 are ignored; you can leave off element 8 (the last) entirely.\n\
 Sixth arg COMMENTSTOP non-nil means stop at the start of a comment.\n\
- If it is `syntax-table', stop after the start of a comment or a string,\n\
- or after end of a comment or a string.")
+ If it is symbol `syntax-table', stop after the start of a comment or a\n\
string, or after end of a comment or a string.")
   (from, to, targetdepth, stopbefore, state, commentstop)
 */
 
   (from, to, targetdepth, stopbefore, state, commentstop)
 */
 
@@ -2839,7 +2937,7 @@ init_syntax_once ()
   Qchar_table_extra_slots = intern ("char-table-extra-slots");
 
   /* Create objects which can be shared among syntax tables.  */
   Qchar_table_extra_slots = intern ("char-table-extra-slots");
 
   /* Create objects which can be shared among syntax tables.  */
-  Vsyntax_code_object = Fmake_vector (make_number (13), Qnil);
+  Vsyntax_code_object = Fmake_vector (make_number (Smax), Qnil);
   for (i = 0; i < XVECTOR (Vsyntax_code_object)->size; i++)
     XVECTOR (Vsyntax_code_object)->contents[i]
       = Fcons (make_number (i), Qnil);
   for (i = 0; i < XVECTOR (Vsyntax_code_object)->size; i++)
     XVECTOR (Vsyntax_code_object)->contents[i]
       = Fcons (make_number (i), Qnil);
@@ -2893,6 +2991,11 @@ init_syntax_once ()
       c = ".,;:?!#@~^'`"[i];
       SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, c, temp);
     }
       c = ".,;:?!#@~^'`"[i];
       SET_RAW_SYNTAX_ENTRY (Vstandard_syntax_table, c, temp);
     }
+
+  /* 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;
 }
 
 void
 }
 
 void
@@ -2914,16 +3017,24 @@ syms_of_syntax ()
     "Non-nil means `forward-sexp', etc., should treat comments as whitespace.");
 
   DEFVAR_BOOL ("parse-sexp-lookup-properties", &parse_sexp_lookup_properties,
     "Non-nil means `forward-sexp', etc., should treat comments as whitespace.");
 
   DEFVAR_BOOL ("parse-sexp-lookup-properties", &parse_sexp_lookup_properties,
-    "Non-nil means `forward-sexp', etc., grant `syntax-table' property.\n\
-The value of this property should be either a syntax table, or a cons\n\
-of the form (SYNTAXCODE . MATCHCHAR), SYNTAXCODE being the numeric\n\
-syntax code, MATCHCHAR being nil or the character to match (which is\n\
-relevant only for open/close type.");
+    "Non-nil means `forward-sexp', etc., obey `syntax-table' property.\n\
+Otherwise, that text property is simply ignored.\n\
+See the info node `(elisp)Syntax Properties' for a description of the\n\
+`syntax-table' property.");
 
   words_include_escapes = 0;
   DEFVAR_BOOL ("words-include-escapes", &words_include_escapes,
     "Non-nil means `forward-word', etc., should treat escape chars part of words.");
 
 
   words_include_escapes = 0;
   DEFVAR_BOOL ("words-include-escapes", &words_include_escapes,
     "Non-nil means `forward-word', etc., should treat escape chars part of words.");
 
+  DEFVAR_BOOL ("multibyte-syntax-as-symbol", &multibyte_syntax_as_symbol,
+    "Non-nil means `scan-sexps' treats all multibyte characters as symbol.");
+  multibyte_syntax_as_symbol = 0;
+
+  DEFVAR_BOOL ("open-paren-in-column-0-is-defun-start",
+              &open_paren_in_column_0_is_defun_start,
+    "Non-nil means an open paren in column 0 denotes the start of a defun.");
+  open_paren_in_column_0_is_defun_start = 1;
+
   defsubr (&Ssyntax_table_p);
   defsubr (&Ssyntax_table);
   defsubr (&Sstandard_syntax_table);
   defsubr (&Ssyntax_table_p);
   defsubr (&Ssyntax_table);
   defsubr (&Sstandard_syntax_table);
@@ -2931,6 +3042,7 @@ relevant only for open/close type.");
   defsubr (&Sset_syntax_table);
   defsubr (&Schar_syntax);
   defsubr (&Smatching_paren);
   defsubr (&Sset_syntax_table);
   defsubr (&Schar_syntax);
   defsubr (&Smatching_paren);
+  defsubr (&Sstring_to_syntax);
   defsubr (&Smodify_syntax_entry);
   defsubr (&Sdescribe_syntax);
 
   defsubr (&Smodify_syntax_entry);
   defsubr (&Sdescribe_syntax);