XEmacs 21.2.46 "Urania".
[chise/xemacs-chise.git.1] / src / syntax.c
1 /* XEmacs routines to deal with syntax tables; also word and list parsing.
2    Copyright (C) 1985-1994 Free Software Foundation, Inc.
3    Copyright (C) 1995 Sun Microsystems, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: FSF 19.28. */
23
24 /* This file has been Mule-ized. */
25
26 #include <config.h>
27 #include "lisp.h"
28
29 #include "buffer.h"
30 #include "syntax.h"
31 #include "extents.h"
32
33 /* Here is a comment from Ken'ichi HANDA <handa@etl.go.jp>
34    explaining the purpose of the Sextword syntax category:
35
36 Japanese words are not separated by spaces, which makes finding word
37 boundaries very difficult.  Theoretically it's impossible without
38 using natural language processing techniques.  But, by defining
39 pseudo-words as below (much simplified for letting you understand it
40 easily) for Japanese, we can have a convenient forward-word function
41 for Japanese.
42
43         A Japanese word is a sequence of characters that consists of
44         zero or more Kanji characters followed by zero or more
45         Hiragana characters.
46
47 Then, the problem is that now we can't say that a sequence of
48 word-constituents makes up a WORD.  For instance, both Hiragana "A"
49 and Kanji "KAN" are word-constituents but the sequence of these two
50 letters can't be a single word.
51
52 So, we introduced Sextword for Japanese letters.  A character of
53 Sextword is a word-constituent but a word boundary may exist between
54 two such characters.  */
55
56 /* Mule 2.4 doesn't seem to have Sextword - I'm removing it -- mrb */
57 /* Recovered by tomo */
58
59 #define ST_COMMENT_STYLE 0x101
60 #define ST_STRING_STYLE  0x102
61
62 Lisp_Object Qsyntax_table;
63 int lookup_syntax_properties;
64
65 Lisp_Object Qsyntax_table_p;
66
67 int words_include_escapes;
68
69 int parse_sexp_ignore_comments;
70
71 /* The following two variables are provided to tell additional information
72    to the regex routines.  We do it this way rather than change the
73    arguments to re_search_2() in an attempt to maintain some call
74    compatibility with other versions of the regex code. */
75
76 /* Tell the regex routines not to QUIT.  Normally there is a QUIT
77    each iteration in re_search_2(). */
78 int no_quit_in_re_search;
79
80 /* Tell the regex routines which buffer to access for SYNTAX() lookups
81    and the like. */
82 struct buffer *regex_emacs_buffer;
83
84 /* In Emacs, this is the string or buffer in which we
85    are matching.  It is used for looking up syntax properties.  */
86 Lisp_Object regex_match_object;
87
88 Lisp_Object Vstandard_syntax_table;
89
90 Lisp_Object Vsyntax_designator_chars_string;
91
92 /* This is the internal form of the parse state used in parse-partial-sexp.  */
93
94 struct lisp_parse_state
95 {
96   int depth;            /* Depth at end of parsing */
97   Emchar instring;      /* -1 if not within string, else desired terminator */
98   int incomment;        /* Nonzero if within a comment at end of parsing */
99   int comstyle;         /* comment style a=0, or b=1, or ST_COMMENT_STYLE */
100   int quoted;           /* Nonzero if just after an escape char at end of
101                            parsing */
102   Bufpos thislevelstart;/* Char number of most recent start-of-expression
103                            at current level */
104   Bufpos prevlevelstart;/* Char number of start of containing expression */
105   Bufpos location;      /* Char number at which parsing stopped */
106   int mindepth;         /* Minimum depth seen while scanning  */
107   Bufpos comstr_start;  /* Position just after last comment/string starter  */
108   Lisp_Object levelstarts;      /* Char numbers of starts-of-expression
109                                    of levels (starting from outermost).  */
110 };
111 \f
112 /* These variables are a cache for finding the start of a defun.
113    find_start_pos    is the place for which the defun start was found.
114    find_start_value  is the defun start position found for it.
115    find_start_buffer is the buffer it was found in.
116    find_start_begv   is the BEGV value when it was found.
117    find_start_modiff is the value of MODIFF when it was found.  */
118
119 static Bufpos find_start_pos;
120 static Bufpos find_start_value;
121 static struct buffer *find_start_buffer;
122 static Bufpos find_start_begv;
123 static int find_start_modiff;
124
125 /* Find a defun-start that is the last one before POS (or nearly the last).
126    We record what we find, so that another call in the same area
127    can return the same value right away.  */
128
129 static Bufpos
130 find_defun_start (struct buffer *buf, Bufpos pos)
131 {
132   Bufpos tem;
133
134   /* Use previous finding, if it's valid and applies to this inquiry.  */
135   if (buf == find_start_buffer
136       /* Reuse the defun-start even if POS is a little farther on.
137          POS might be in the next defun, but that's ok.
138          Our value may not be the best possible, but will still be usable.  */
139       && pos <= find_start_pos + 1000
140       && pos >= find_start_value
141       && BUF_BEGV (buf) == find_start_begv
142       && BUF_MODIFF (buf) == find_start_modiff)
143     return find_start_value;
144
145   /* Back up to start of line.  */
146   tem = find_next_newline (buf, pos, -1);
147
148   SETUP_SYNTAX_CACHE (tem, 1);
149   while (tem > BUF_BEGV (buf))
150     {
151       UPDATE_SYNTAX_CACHE_BACKWARD(tem);
152
153       /* Open-paren at start of line means we found our defun-start.  */
154       if (SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, tem)) == Sopen)
155         break;
156       /* Move to beg of previous line.  */
157       tem = find_next_newline (buf, tem, -2);
158     }
159
160   /* Record what we found, for the next try.  */
161   find_start_value  = tem;
162   find_start_buffer = buf;
163   find_start_modiff = BUF_MODIFF (buf);
164   find_start_begv   = BUF_BEGV (buf);
165   find_start_pos    = pos;
166
167   return find_start_value;
168 }
169 \f
170 DEFUN ("syntax-table-p", Fsyntax_table_p, 1, 1, 0, /*
171 Return t if OBJECT is a syntax table.
172 Any vector of 256 elements will do.
173 */
174        (object))
175 {
176   return (CHAR_TABLEP (object)
177           && XCHAR_TABLE_TYPE (object) == CHAR_TABLE_TYPE_SYNTAX)
178     ? Qt : Qnil;
179 }
180
181 static Lisp_Object
182 check_syntax_table (Lisp_Object obj, Lisp_Object default_)
183 {
184   if (NILP (obj))
185     obj = default_;
186   while (NILP (Fsyntax_table_p (obj)))
187     obj = wrong_type_argument (Qsyntax_table_p, obj);
188   return obj;
189 }
190
191 DEFUN ("syntax-table", Fsyntax_table, 0, 1, 0, /*
192 Return the current syntax table.
193 This is the one specified by the current buffer, or by BUFFER if it
194 is non-nil.
195 */
196        (buffer))
197 {
198   return decode_buffer (buffer, 0)->syntax_table;
199 }
200
201 DEFUN ("standard-syntax-table", Fstandard_syntax_table, 0, 0, 0, /*
202 Return the standard syntax table.
203 This is the one used for new buffers.
204 */
205        ())
206 {
207   return Vstandard_syntax_table;
208 }
209
210 DEFUN ("copy-syntax-table", Fcopy_syntax_table, 0, 1, 0, /*
211 Return a new syntax table which is a copy of SYNTAX-TABLE.
212 SYNTAX-TABLE defaults to the standard syntax table.
213 */
214        (syntax_table))
215 {
216   if (NILP (Vstandard_syntax_table))
217     return Fmake_char_table (Qsyntax);
218
219   syntax_table = check_syntax_table (syntax_table, Vstandard_syntax_table);
220   return Fcopy_char_table (syntax_table);
221 }
222
223 DEFUN ("set-syntax-table", Fset_syntax_table, 1, 2, 0, /*
224 Select SYNTAX-TABLE as the new syntax table for BUFFER.
225 BUFFER defaults to the current buffer if omitted.
226 */
227        (syntax_table, buffer))
228 {
229   struct buffer *buf = decode_buffer (buffer, 0);
230   syntax_table = check_syntax_table (syntax_table, Qnil);
231   buf->syntax_table = syntax_table;
232   buf->mirror_syntax_table = XCHAR_TABLE (syntax_table)->mirror_table;
233   /* Indicate that this buffer now has a specified syntax table.  */
234   buf->local_var_flags |= XINT (buffer_local_flags.syntax_table);
235   return syntax_table;
236 }
237 \f
238 /* The current syntax state */
239 struct syntax_cache syntax_cache;
240
241
242 /* 
243    Update syntax_cache to an appropriate setting for position POS
244
245    The sign of COUNT gives the relative position of POS wrt the
246    previously valid interval.  (not currently used)
247
248    `syntax_cache.*_change' are the next and previous positions at
249    which syntax_code and c_s_t will need to be recalculated.
250
251    #### Currently this code uses 'get-char-property', which will
252    return the "last smallest" extent at a given position. In cases
253    where overlapping extents are defined, this code will simply use
254    whatever is returned by get-char-property.
255
256    It might be worth it at some point to merge provided syntax tables
257    outward to the current buffer. */
258
259 void
260 update_syntax_cache (int pos, int count, int init)
261 {
262   Lisp_Object tmp_table;
263
264   if (init)
265     {
266       syntax_cache.prev_change = -1;
267       syntax_cache.next_change = -1;
268     }
269
270   if (pos > syntax_cache.prev_change &&
271       pos < syntax_cache.next_change)
272     {
273       /* do nothing */
274     }
275   else
276     {
277       if (NILP (syntax_cache.object) || EQ (syntax_cache.object, Qt))
278         {
279           int get_change_before = pos + 1;
280
281           tmp_table = Fget_char_property (make_int(pos), Qsyntax_table,
282                                           make_buffer (syntax_cache.buffer), Qnil);
283           syntax_cache.next_change =
284             XINT (Fnext_extent_change (make_int (pos > 0 ? pos : 1),
285                                        make_buffer (syntax_cache.buffer)));
286
287           if (get_change_before < 1)
288             get_change_before = 1;
289           else if (get_change_before > BUF_ZV (syntax_cache.buffer))
290             get_change_before = BUF_ZV (syntax_cache.buffer);
291
292           syntax_cache.prev_change =
293             XINT (Fprevious_extent_change (make_int (get_change_before),
294                                            make_buffer (syntax_cache.buffer)));
295         }
296       else
297         {
298           int get_change_before = pos + 1;
299
300           tmp_table = Fget_char_property (make_int(pos), Qsyntax_table,
301                                           syntax_cache.object, Qnil);
302           syntax_cache.next_change =
303             XINT (Fnext_extent_change (make_int (pos >= 0 ? pos : 0),
304                                        syntax_cache.object));
305
306           if (get_change_before < 0)
307             get_change_before = 0;
308           else if (get_change_before > XSTRING_LENGTH(syntax_cache.object))
309             get_change_before = XSTRING_LENGTH(syntax_cache.object);
310
311           syntax_cache.prev_change =
312             XINT (Fprevious_extent_change (make_int (pos >= 0 ? pos : 0),
313                                            syntax_cache.object));
314         }
315
316       if (EQ (Fsyntax_table_p (tmp_table), Qt))
317         {
318           syntax_cache.use_code = 0;
319           syntax_cache.current_syntax_table =
320             XCHAR_TABLE (tmp_table)->mirror_table;
321         } 
322       else if (CONSP (tmp_table) && INTP (XCAR (tmp_table)))
323         {
324           syntax_cache.use_code = 1;
325           syntax_cache.syntax_code = XINT (XCAR(tmp_table));
326         }
327       else 
328         {
329           syntax_cache.use_code = 0;
330           syntax_cache.current_syntax_table =
331             syntax_cache.buffer->mirror_syntax_table;
332         }
333     }
334 }
335 \f
336 /* Convert a letter which signifies a syntax code
337    into the code it signifies.
338    This is used by modify-syntax-entry, and other things. */
339
340 const unsigned char syntax_spec_code[0400] =
341 { 0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
342   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
343   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
344   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
345   (char) Swhitespace, 0377, (char) Sstring, 0377,
346       (char) Smath, 0377, 0377, (char) Squote,
347   (char) Sopen, (char) Sclose, 0377, 0377,
348         0377, (char) Swhitespace, (char) Spunct, (char) Scharquote,
349   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
350   0377, 0377, 0377, 0377,
351         (char) Scomment, 0377, (char) Sendcomment, 0377,
352   (char) Sinherit, 0377, 0377, 0377, 0377, 0377, 0377, 0377,   /* @, A ... */
353   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
354   0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
355   0377, 0377, 0377, 0377, (char) Sescape, 0377, 0377, (char) Ssymbol,
356   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,   /* `, a, ... */
357   0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
358   0377, 0377, 0377, 0377, 0377, 0377, 0377, (char) Sword,
359   0377, 0377, 0377, 0377, (char) Sstring_fence, 0377, 0377, 0377
360 };
361
362 const unsigned char syntax_code_spec[] =  " .w_()'\"$\\/<>@!|";
363
364 DEFUN ("syntax-designator-chars", Fsyntax_designator_chars, 0, 0, 0, /*
365 Return a string of the recognized syntax designator chars.
366 The chars are ordered by their internal syntax codes, which are
367 numbered starting at 0.
368 */
369        ())
370 {
371   return Vsyntax_designator_chars_string;
372 }
373
374 DEFUN ("char-syntax", Fchar_syntax, 1, 2, 0, /*
375 Return the syntax code of CHARACTER, described by a character.
376 For example, if CHARACTER is a word constituent,
377 the character `?w' is returned.
378 The characters that correspond to various syntax codes
379 are listed in the documentation of `modify-syntax-entry'.
380 Optional second argument SYNTAX-TABLE defaults to the current buffer's
381 syntax table.
382 */
383        (character, syntax_table))
384 {
385   Lisp_Char_Table *mirrortab;
386
387   if (NILP (character))
388     {
389       character = make_char ('\000');
390     }
391   CHECK_CHAR_COERCE_INT (character);
392   syntax_table = check_syntax_table (syntax_table, current_buffer->syntax_table);
393   mirrortab = XCHAR_TABLE (XCHAR_TABLE (syntax_table)->mirror_table);
394   return make_char (syntax_code_spec[(int) SYNTAX (mirrortab, XCHAR (character))]);
395 }
396
397 #ifdef MULE
398
399 enum syntaxcode
400 charset_syntax (struct buffer *buf, Lisp_Object charset, int *multi_p_out)
401 {
402   *multi_p_out = 1;
403   /* #### get this right */
404   return Spunct;
405 }
406
407 #endif
408
409 Lisp_Object
410 syntax_match (Lisp_Object syntax_table, Emchar ch)
411 {
412   Lisp_Object code = XCHAR_TABLE_VALUE_UNSAFE (syntax_table, ch);
413   Lisp_Object code2 = code;
414
415   if (CONSP (code))
416     code2 = XCAR (code);
417   if (SYNTAX_FROM_CODE (XINT (code2)) == Sinherit)
418     code = XCHAR_TABLE_VALUE_UNSAFE (Vstandard_syntax_table, ch);
419
420   return CONSP (code) ? XCDR (code) : Qnil;
421 }
422
423 DEFUN ("matching-paren", Fmatching_paren, 1, 2, 0, /*
424 Return the matching parenthesis of CHARACTER, or nil if none.
425 Optional second argument SYNTAX-TABLE defaults to the current buffer's
426 syntax table.
427 */
428        (character, syntax_table))
429 {
430   Lisp_Char_Table *mirrortab;
431   int code;
432
433   CHECK_CHAR_COERCE_INT (character);
434   syntax_table = check_syntax_table (syntax_table, current_buffer->syntax_table);
435   mirrortab = XCHAR_TABLE (XCHAR_TABLE (syntax_table)->mirror_table);
436   code = SYNTAX (mirrortab, XCHAR (character));
437   if (code == Sopen || code == Sclose || code == Sstring)
438     return syntax_match (syntax_table, XCHAR (character));
439   return Qnil;
440 }
441
442 \f
443
444 #ifdef MULE
445 /* Return 1 if there is a word boundary between two word-constituent
446    characters C1 and C2 if they appear in this order, else return 0.
447    There is no word boundary between two word-constituent ASCII
448    characters.  */
449 #define WORD_BOUNDARY_P(c1, c2)                 \
450   (!(CHAR_ASCII_P (c1) && CHAR_ASCII_P (c2))    \
451    && word_boundary_p (c1, c2))
452
453 extern int word_boundary_p (Emchar c1, Emchar c2);
454 #endif
455
456 /* Return the position across COUNT words from FROM.
457    If that many words cannot be found before the end of the buffer, return 0.
458    COUNT negative means scan backward and stop at word beginning.  */
459
460 Bufpos
461 scan_words (struct buffer *buf, Bufpos from, int count)
462 {
463   Bufpos limit = count > 0 ? BUF_ZV (buf) : BUF_BEGV (buf);
464   Emchar ch0, ch1;
465   enum syntaxcode code;
466
467   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, from, count);
468
469   /* #### is it really worth it to hand expand both cases? JV */
470   while (count > 0)
471     {
472       QUIT;
473
474       while (1)
475         {
476           if (from == limit)
477             return 0;
478
479           UPDATE_SYNTAX_CACHE_FORWARD (from);
480           ch0 = BUF_FETCH_CHAR (buf, from);
481           code = SYNTAX_FROM_CACHE (mirrortab, ch0);
482
483           from++;
484           if (words_include_escapes
485               && (code == Sescape || code == Scharquote))
486             break;
487           if (code == Sword)
488             break;
489         }
490
491       QUIT;
492
493       while (from != limit)
494         {
495           UPDATE_SYNTAX_CACHE_FORWARD (from);
496           ch1 = BUF_FETCH_CHAR (buf, from);
497           code = SYNTAX_FROM_CACHE (mirrortab, ch1);
498           if (!(words_include_escapes
499                 && (code == Sescape || code == Scharquote)))
500             if (code != Sword
501 #ifdef MULE
502                 || WORD_BOUNDARY_P (ch0, ch1)
503 #endif
504                 )
505               break;
506 #ifdef MULE
507           ch0 = ch1;
508 #endif
509           from++;
510         }
511       count--;
512     }
513
514   while (count < 0)
515     {
516       QUIT;
517
518       while (1)
519         {
520           if (from == limit)
521             return 0;
522
523           UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
524           ch1 = BUF_FETCH_CHAR (buf, from - 1);
525           code = SYNTAX_FROM_CACHE (mirrortab, ch1);
526           from--;
527
528           if (words_include_escapes
529               && (code == Sescape || code == Scharquote))
530             break;
531           if (code == Sword)
532             break;
533         }
534
535       QUIT;
536
537       while (from != limit)
538         {
539           UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
540           ch0 = BUF_FETCH_CHAR (buf, from - 1);
541           code = SYNTAX_FROM_CACHE (mirrortab, ch0);
542
543           if (!(words_include_escapes
544                 && (code == Sescape || code == Scharquote)))
545             if (code != Sword
546 #ifdef MULE
547                 || WORD_BOUNDARY_P (ch0, ch1)
548 #endif
549                 )
550               break;
551 #ifdef MULE
552           ch1 = ch0;
553 #endif
554           from--;
555         }
556       count++;
557     }
558
559   return from;
560 }
561
562 DEFUN ("forward-word", Fforward_word, 0, 2, "_p", /*
563 Move point forward COUNT words (backward if COUNT is negative).
564 Normally t is returned, but if an edge of the buffer is reached,
565 point is left there and nil is returned.
566
567 The characters that are moved over may be added to the current selection
568 \(i.e. active region) if the Shift key is held down, a motion key is used
569 to invoke this command, and `shifted-motion-keys-select-region' is t; see
570 the documentation for this variable for more details.
571
572 COUNT defaults to 1, and BUFFER defaults to the current buffer.
573 */
574        (count, buffer))
575 {
576   Bufpos val;
577   struct buffer *buf = decode_buffer (buffer, 0);
578   EMACS_INT n;
579
580   if (NILP (count))
581     n = 1;
582   else
583     {
584       CHECK_INT (count);
585       n = XINT (count);
586     }
587
588   val = scan_words (buf, BUF_PT (buf), n);
589   if (val)
590     {
591       BUF_SET_PT (buf, val);
592       return Qt;
593     }
594   else
595     {
596       BUF_SET_PT (buf, n > 0 ? BUF_ZV (buf) : BUF_BEGV (buf));
597       return Qnil;
598     }
599 }
600 \f
601 static void scan_sexps_forward (struct buffer *buf,
602                                 struct lisp_parse_state *,
603                                 Bufpos from, Bufpos end,
604                                 int targetdepth, int stopbefore,
605                                 Lisp_Object oldstate,
606                                 int commentstop);
607
608 static int
609 find_start_of_comment (struct buffer *buf, Bufpos from, Bufpos stop,
610                        int comstyle)
611 {
612   Emchar c;
613   enum syntaxcode code;
614
615   /* Look back, counting the parity of string-quotes,
616      and recording the comment-starters seen.
617      When we reach a safe place, assume that's not in a string;
618      then step the main scan to the earliest comment-starter seen
619      an even number of string quotes away from the safe place.
620
621      OFROM[I] is position of the earliest comment-starter seen
622      which is I+2X quotes from the comment-end.
623      PARITY is current parity of quotes from the comment end.  */
624   int parity = 0;
625   Emchar my_stringend = 0;
626   int string_lossage = 0;
627   Bufpos comment_end = from;
628   Bufpos comstart_pos = 0;
629   int comstart_parity = 0;
630   int styles_match_p = 0;
631   /* mask to match comment styles against; for ST_COMMENT_STYLE, this
632      will get set to SYNTAX_COMMENT_STYLE_B, but never get checked */
633   int mask = comstyle ? SYNTAX_COMMENT_STYLE_B : SYNTAX_COMMENT_STYLE_A;
634
635   /* At beginning of range to scan, we're outside of strings;
636      that determines quote parity to the comment-end.  */
637   while (from != stop)
638     {
639       int syncode;
640
641       /* Move back and examine a character.  */
642       from--;
643       UPDATE_SYNTAX_CACHE_BACKWARD (from);
644
645       c = BUF_FETCH_CHAR (buf, from);
646       code = SYNTAX_FROM_CACHE (mirrortab, c);
647       syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
648
649       /* is this a 1-char comment end sequence? if so, try
650          to see if style matches previously extracted mask */
651       if (code == Sendcomment)
652         {
653           styles_match_p =
654             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) & mask;
655         }
656
657       /* or are we looking at a 1-char comment start sequence
658          of the style matching mask? */
659       else if (code == Scomment)
660         {
661           styles_match_p =
662             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) & mask;
663         }
664
665       /* otherwise, is this a 2-char comment end or start sequence? */
666       else if (from > stop)
667         do
668           {
669             /* 2-char comment end sequence? */
670             if (SYNTAX_CODE_END_SECOND_P (syncode))
671               {
672                 int prev_syncode;
673                 UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
674                 prev_syncode =
675                   SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
676
677                 if (SYNTAX_CODES_END_P (prev_syncode, syncode))
678                   {
679                     code = Sendcomment;
680                     styles_match_p =
681                       SYNTAX_CODES_COMMENT_MASK_END (prev_syncode, syncode);
682                     from--;
683                     UPDATE_SYNTAX_CACHE_BACKWARD (from);
684                     c = BUF_FETCH_CHAR (buf, from);
685
686                     /* Found a comment-end sequence, so skip past the
687                        check for a comment-start */
688                     break;
689                   }
690               }
691
692             /* 2-char comment start sequence? */
693             if (SYNTAX_CODE_START_SECOND_P (syncode))
694               {
695                 int prev_syncode;
696                 UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
697                 prev_syncode =
698                   SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
699
700                 if (SYNTAX_CODES_START_P (prev_syncode, syncode))
701                   {
702                     code = Scomment;
703                     styles_match_p =
704                       SYNTAX_CODES_COMMENT_MASK_START (prev_syncode, syncode);
705                     from--;
706                     UPDATE_SYNTAX_CACHE_BACKWARD (from);
707                     c = BUF_FETCH_CHAR (buf, from);
708                   }
709               }
710           } while (0);
711
712       /* Ignore escaped characters.  */
713       if (char_quoted (buf, from))
714         continue;
715
716       /* Track parity of quotes.  */
717       if (code == Sstring)
718         {
719           parity ^= 1;
720           if (my_stringend == 0)
721             my_stringend = c;
722           /* If we have two kinds of string delimiters.
723              There's no way to grok this scanning backwards.  */
724           else if (my_stringend != c)
725             string_lossage = 1;
726         }
727
728       if (code == Sstring_fence || code == Scomment_fence)
729         {
730           parity ^= 1;
731           if (my_stringend == 0)
732             my_stringend =
733               code == Sstring_fence ? ST_STRING_STYLE : ST_COMMENT_STYLE;
734           /* If we have two kinds of string delimiters.
735              There's no way to grok this scanning backwards.  */
736           else if (my_stringend != (code == Sstring_fence 
737                                     ? ST_STRING_STYLE : ST_COMMENT_STYLE))
738             string_lossage = 1;
739         }
740
741       /* Record comment-starters according to that
742          quote-parity to the comment-end.  */
743       if (code == Scomment && styles_match_p)
744         {
745           comstart_parity = parity;
746           comstart_pos = from;
747         }
748
749       /* If we find another earlier comment-ender,
750          any comment-starts earlier than that don't count
751          (because they go with the earlier comment-ender).  */
752       if (code == Sendcomment && styles_match_p)
753         break;
754
755       /* Assume a defun-start point is outside of strings.  */
756       if (code == Sopen
757           && (from == stop || BUF_FETCH_CHAR (buf, from - 1) == '\n'))
758         break;
759     }
760
761   if (comstart_pos == 0)
762     from = comment_end;
763   /* If the earliest comment starter
764      is followed by uniform paired string quotes or none,
765      we know it can't be inside a string
766      since if it were then the comment ender would be inside one.
767      So it does start a comment.  Skip back to it.  */
768   else if (comstart_parity == 0 && !string_lossage)
769     from = comstart_pos;
770   else
771     {
772       /* We had two kinds of string delimiters mixed up
773          together.  Decode this going forwards.
774          Scan fwd from the previous comment ender
775          to the one in question; this records where we
776          last passed a comment starter.  */
777
778       struct lisp_parse_state state;
779       scan_sexps_forward (buf, &state, find_defun_start (buf, comment_end),
780                           comment_end - 1, -10000, 0, Qnil, 0);
781       if (state.incomment)
782         from = state.comstr_start;
783       else
784         /* We can't grok this as a comment; scan it normally.  */
785         from = comment_end;
786       UPDATE_SYNTAX_CACHE_FORWARD (from - 1);
787     }
788   return from;
789 }
790
791 static Bufpos
792 find_end_of_comment (struct buffer *buf, Bufpos from, Bufpos stop, int comstyle)
793 {
794   int c;
795   int prev_code;
796   /* mask to match comment styles against; for ST_COMMENT_STYLE, this
797      will get set to SYNTAX_COMMENT_STYLE_B, but never get checked */
798   int mask = comstyle ? SYNTAX_COMMENT_STYLE_B : SYNTAX_COMMENT_STYLE_A;
799
800   /* This is only called by functions which have already set up the
801      syntax_cache and are keeping it up-to-date */
802   while (1)
803     {
804       if (from == stop)
805         {
806           return -1;
807         }
808
809       UPDATE_SYNTAX_CACHE_FORWARD (from);
810       c = BUF_FETCH_CHAR (buf, from);
811
812       /* Test for generic comments */
813       if (comstyle == ST_COMMENT_STYLE)
814         {
815           if (SYNTAX_FROM_CACHE (mirrortab, c) == Scomment_fence)
816             {
817               from++;
818               UPDATE_SYNTAX_CACHE_FORWARD (from);
819               break;
820             }
821           from++;
822           continue; /* No need to test other comment styles in a
823                        generic comment */
824         }
825       else
826
827         if (SYNTAX_FROM_CACHE (mirrortab, c) == Sendcomment
828             && SYNTAX_CODE_MATCHES_1CHAR_P
829             (SYNTAX_CODE_FROM_CACHE (mirrortab, c), mask))
830         /* we have encountered a comment end of the same style
831            as the comment sequence which began this comment
832            section */
833           {
834             from++;
835             UPDATE_SYNTAX_CACHE_FORWARD (from);
836             break;
837           }
838
839       prev_code = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
840       from++;
841       UPDATE_SYNTAX_CACHE_FORWARD (from);
842       if (from < stop
843           && SYNTAX_CODES_MATCH_END_P
844           (prev_code,
845            SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from)),
846            mask)
847
848           )
849         /* we have encountered a comment end of the same style
850            as the comment sequence which began this comment
851            section */
852         {
853           from++;
854           UPDATE_SYNTAX_CACHE_FORWARD (from);
855           break;
856         }
857     }
858   return from;
859 }
860
861 \f
862 /* #### between FSF 19.23 and 19.28 there are some changes to the logic
863    in this function (and minor changes to find_start_of_comment(),
864    above, which is part of Fforward_comment() in FSF).  Attempts to port
865    that logic made this function break, so I'm leaving it out.  If anyone
866    ever complains about this function not working properly, take a look
867    at those changes.  --ben */
868
869 DEFUN ("forward-comment", Fforward_comment, 0, 2, 0, /*
870 Move forward across up to COUNT comments, or backwards if COUNT is negative.
871 Stop scanning if we find something other than a comment or whitespace.
872 Set point to where scanning stops.
873 If COUNT comments are found as expected, with nothing except whitespace
874 between them, return t; otherwise return nil.
875 Point is set in either case.
876 COUNT defaults to 1, and BUFFER defaults to the current buffer.
877 */
878        (count, buffer))
879 {
880   Bufpos from;
881   Bufpos stop;
882   Emchar c;
883   enum syntaxcode code;
884   int syncode;
885   EMACS_INT n;
886   struct buffer *buf = decode_buffer (buffer, 0);
887
888   if (NILP (count))
889     n = 1;
890   else
891     {
892       CHECK_INT (count);
893       n = XINT (count);
894     }
895
896   from = BUF_PT (buf);
897
898   SETUP_SYNTAX_CACHE (from, n);
899   while (n > 0)
900     {
901       QUIT;
902
903       stop = BUF_ZV (buf);
904       while (from < stop)
905         {
906           int comstyle = 0;     /* mask for finding matching comment style */
907
908           if (char_quoted (buf, from))
909             {
910               from++;
911               continue;
912             }
913
914           UPDATE_SYNTAX_CACHE_FORWARD (from);
915           c = BUF_FETCH_CHAR (buf, from);
916           code = SYNTAX_FROM_CACHE (mirrortab, c);
917           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
918
919           if (code == Scomment)
920             {
921               /* we have encountered a single character comment start
922                  sequence, and we are ignoring all text inside comments.
923                  we must record the comment style this character begins
924                  so that later, only a comment end of the same style actually
925                  ends the comment section */
926               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
927                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
928             }
929
930           else if (code == Scomment_fence)
931             {
932               from++;
933               code = Scomment;
934               comstyle = ST_COMMENT_STYLE;
935             }
936
937           else if (from < stop
938                    && SYNTAX_CODE_START_FIRST_P (syncode))
939             {
940               int next_syncode;
941               UPDATE_SYNTAX_CACHE_FORWARD (from + 1);
942               next_syncode =
943                 SYNTAX_CODE_FROM_CACHE (mirrortab, 
944                                         BUF_FETCH_CHAR (buf, from + 1));
945
946               if (SYNTAX_CODES_START_P (syncode, next_syncode))
947                 {
948                   /* we have encountered a 2char comment start sequence and we
949                      are ignoring all text inside comments. we must record
950                      the comment style this sequence begins so that later,
951                      only a comment end of the same style actually ends
952                      the comment section */
953                   code = Scomment;
954                   comstyle =
955                     SYNTAX_CODES_COMMENT_MASK_START (syncode, next_syncode)
956                     == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
957                   from++;
958                 }
959             }
960
961           if (code == Scomment)
962             {
963               Bufpos newfrom = find_end_of_comment (buf, from, stop, comstyle);
964               if (newfrom < 0)
965                 {
966                   /* we stopped because from==stop */
967                   BUF_SET_PT (buf, stop);
968                   return Qnil;
969                 }
970               from = newfrom;
971
972               /* We have skipped one comment.  */
973               break;
974             }
975           else if (code != Swhitespace
976                    && code != Sendcomment
977                    && code != Scomment )
978             {
979               BUF_SET_PT (buf, from);
980               return Qnil;
981             }
982           from++;
983         }
984
985       /* End of comment reached */
986       n--;
987     }
988
989   while (n < 0)
990     {
991       QUIT;
992
993       stop = BUF_BEGV (buf);
994       while (from > stop)
995         {
996           int comstyle = 0;     /* mask for finding matching comment style */
997
998           from--;
999           if (char_quoted (buf, from))
1000             {
1001               from--;
1002               continue;
1003             }
1004
1005           c = BUF_FETCH_CHAR (buf, from);
1006           code = SYNTAX_FROM_CACHE (mirrortab, c);
1007           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1008
1009           if (code == Sendcomment)
1010             {
1011               /* we have found a single char end comment. we must record
1012                  the comment style encountered so that later, we can match
1013                  only the proper comment begin sequence of the same style */
1014               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1015                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1016             }
1017
1018           else if (code == Scomment_fence)
1019             {
1020               code = Sendcomment;
1021               comstyle = ST_COMMENT_STYLE;
1022             }
1023
1024           else if (from > stop
1025                    && SYNTAX_CODE_END_SECOND_P (syncode))
1026             {
1027               int prev_syncode;
1028               UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1029               prev_syncode =
1030                 SYNTAX_CODE_FROM_CACHE (mirrortab,
1031                                         BUF_FETCH_CHAR (buf, from - 1));
1032               if (SYNTAX_CODES_END_P (prev_syncode, syncode))
1033                 {
1034                   /* We must record the comment style encountered so that
1035                      later, we can match only the proper comment begin
1036                      sequence of the same style.  */
1037                   code = Sendcomment;
1038                   comstyle = SYNTAX_CODES_COMMENT_MASK_END
1039                     (prev_syncode, syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1040                   from--;
1041                 }
1042             }
1043
1044           if (code == Sendcomment)
1045             {
1046               from = find_start_of_comment (buf, from, stop, comstyle);
1047               break;
1048             }
1049
1050           else if (code != Swhitespace
1051                    && code != Scomment
1052                    && code != Sendcomment)
1053             {
1054               BUF_SET_PT (buf, from + 1);
1055               return Qnil;
1056             }
1057         }
1058
1059       n++;
1060     }
1061
1062   BUF_SET_PT (buf, from);
1063   return Qt;
1064 }
1065
1066 \f
1067 Lisp_Object
1068 scan_lists (struct buffer *buf, Bufpos from, int count, int depth,
1069             int sexpflag, int noerror)
1070 {
1071   Bufpos stop;
1072   Emchar c;
1073   int quoted;
1074   int mathexit = 0;
1075   enum syntaxcode code;
1076   int syncode;
1077   int min_depth = depth;    /* Err out if depth gets less than this. */
1078
1079   if (depth > 0) min_depth = 0;
1080
1081   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, from, count);
1082   while (count > 0)
1083     {
1084       QUIT;
1085
1086       stop = BUF_ZV (buf);
1087       while (from < stop)
1088         {
1089           int comstyle = 0;     /* mask for finding matching comment style */
1090
1091           UPDATE_SYNTAX_CACHE_FORWARD (from);
1092           c = BUF_FETCH_CHAR (buf, from);
1093           code = SYNTAX_FROM_CACHE (mirrortab, c);
1094           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1095           from++;
1096
1097           /* a 1-char comment start sequence */
1098           if (code == Scomment && parse_sexp_ignore_comments)
1099             {
1100               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) ==
1101                 SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1102             }
1103
1104           /* else, a 2-char comment start sequence? */
1105           else if (from < stop
1106                    && SYNTAX_CODE_START_FIRST_P (syncode)
1107                    && parse_sexp_ignore_comments)
1108             {
1109               int next_syncode;
1110               UPDATE_SYNTAX_CACHE_FORWARD (from);
1111               next_syncode =
1112                 SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from));
1113
1114               if (SYNTAX_CODES_START_P (syncode, next_syncode))
1115                 {
1116               /* we have encountered a comment start sequence and we
1117                  are ignoring all text inside comments. we must record
1118                  the comment style this sequence begins so that later,
1119                  only a comment end of the same style actually ends
1120                  the comment section */
1121               code = Scomment;
1122                   comstyle = SYNTAX_CODES_COMMENT_MASK_START
1123                     (syncode, next_syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1124               from++;
1125             }
1126             }
1127           UPDATE_SYNTAX_CACHE_FORWARD (from);
1128
1129           if (SYNTAX_CODE_PREFIX (syncode))
1130             continue;
1131
1132           switch (code)
1133             {
1134             case Sescape:
1135             case Scharquote:
1136               if (from == stop) goto lose;
1137               from++;
1138               /* treat following character as a word constituent */
1139             case Sword:
1140             case Ssymbol:
1141               if (depth || !sexpflag) break;
1142               /* This word counts as a sexp; return at end of it. */
1143               while (from < stop)
1144                 {
1145                   UPDATE_SYNTAX_CACHE_FORWARD (from);
1146                   switch (SYNTAX_FROM_CACHE (mirrortab,
1147                                              BUF_FETCH_CHAR (buf, from)))
1148                     {
1149                     case Scharquote:
1150                     case Sescape:
1151                       from++;
1152                       if (from == stop) goto lose;
1153                       break;
1154                     case Sword:
1155                     case Ssymbol:
1156                     case Squote:
1157                       break;
1158                     default:
1159                       goto done;
1160                     }
1161                   from++;
1162                 }
1163               goto done;
1164
1165             case Scomment_fence:
1166               comstyle = ST_COMMENT_STYLE;
1167             case Scomment:
1168               if (!parse_sexp_ignore_comments)
1169                 break;
1170               UPDATE_SYNTAX_CACHE_FORWARD (from);
1171               {
1172                 Bufpos newfrom =
1173                   find_end_of_comment (buf, from, stop, comstyle);
1174                 if (newfrom < 0)
1175                   {
1176                     /* we stopped because from == stop in search forward */
1177                     from = stop;
1178                     if (depth == 0)
1179                       goto done;
1180                     goto lose;
1181                   }
1182                 from = newfrom;
1183               }
1184               break;
1185
1186             case Smath:
1187               if (!sexpflag)
1188                 break;
1189               if (from != stop && c == BUF_FETCH_CHAR (buf, from))
1190                 from++;
1191               if (mathexit)
1192                 {
1193                   mathexit = 0;
1194                   goto close1;
1195                 }
1196               mathexit = 1;
1197
1198             case Sopen:
1199               if (!++depth) goto done;
1200               break;
1201
1202             case Sclose:
1203             close1:
1204             if (!--depth) goto done;
1205             if (depth < min_depth)
1206               {
1207                 if (noerror)
1208                   return Qnil;
1209                 error ("Containing expression ends prematurely");
1210               }
1211             break;
1212
1213             case Sstring_fence:
1214             case Sstring:
1215               {
1216                 Emchar stringterm;
1217
1218                 if (code != Sstring_fence)
1219                   {
1220                 /* XEmacs change: call syntax_match on character */
1221                 Emchar ch = BUF_FETCH_CHAR (buf, from - 1);
1222                     Lisp_Object stermobj =
1223                       syntax_match (syntax_cache.current_syntax_table, ch);
1224
1225                 if (CHARP (stermobj))
1226                   stringterm = XCHAR (stermobj);
1227                 else
1228                   stringterm = ch;
1229                   }
1230                 else
1231                   stringterm = '\0'; /* avoid compiler warnings */
1232
1233                 while (1)
1234                   {
1235                     if (from >= stop)
1236                       goto lose;
1237                     UPDATE_SYNTAX_CACHE_FORWARD (from);
1238                     c = BUF_FETCH_CHAR (buf, from);
1239                     if (code == Sstring
1240                         ? c == stringterm
1241                         : SYNTAX_FROM_CACHE (mirrortab, c) == Sstring_fence)
1242                       break;
1243
1244                     switch (SYNTAX_FROM_CACHE (mirrortab, c))
1245                       {
1246                       case Scharquote:
1247                       case Sescape:
1248                         from++;
1249                         break;
1250                       default:
1251                         break;
1252                       }
1253                     from++;
1254                   }
1255                 from++;
1256                 if (!depth && sexpflag) goto done;
1257                 break;
1258               }
1259
1260             default:
1261               break;
1262             }
1263         }
1264
1265       /* Reached end of buffer.  Error if within object,
1266          return nil if between */
1267       if (depth) goto lose;
1268
1269       return Qnil;
1270
1271       /* End of object reached */
1272     done:
1273       count--;
1274     }
1275
1276   while (count < 0)
1277     {
1278       QUIT;
1279
1280       stop = BUF_BEGV (buf);
1281       while (from > stop)
1282         {
1283           int comstyle = 0;     /* mask for finding matching comment style */
1284
1285           from--;
1286           UPDATE_SYNTAX_CACHE_BACKWARD (from);
1287           quoted = char_quoted (buf, from);
1288           if (quoted)
1289             {
1290             from--;
1291               UPDATE_SYNTAX_CACHE_BACKWARD (from);
1292             }
1293
1294           c = BUF_FETCH_CHAR (buf, from);
1295           code = SYNTAX_FROM_CACHE (mirrortab, c);
1296           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1297
1298           if (code == Sendcomment && parse_sexp_ignore_comments)
1299             {
1300               /* we have found a single char end comment. we must record
1301                  the comment style encountered so that later, we can match
1302                  only the proper comment begin sequence of the same style */
1303               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1304                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1305             }
1306
1307           else if (from > stop
1308                    && SYNTAX_CODE_END_SECOND_P (syncode)
1309                    && !char_quoted (buf, from - 1)
1310                    && parse_sexp_ignore_comments)
1311             {
1312               int prev_syncode;
1313               UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1314               prev_syncode = SYNTAX_CODE_FROM_CACHE
1315                 (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
1316
1317               if (SYNTAX_CODES_END_P (prev_syncode, syncode))
1318                 {
1319               /* we must record the comment style encountered so that
1320                  later, we can match only the proper comment begin
1321                  sequence of the same style */
1322               code = Sendcomment;
1323                   comstyle = SYNTAX_CODES_COMMENT_MASK_END
1324                     (prev_syncode, syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1325               from--;
1326             }
1327             }
1328
1329           if (SYNTAX_CODE_PREFIX (syncode))
1330             continue;
1331
1332           switch (quoted ? Sword : code)
1333             {
1334             case Sword:
1335             case Ssymbol:
1336               if (depth || !sexpflag) break;
1337               /* This word counts as a sexp; count object finished after
1338                  passing it. */
1339               while (from > stop)
1340                 {
1341                   UPDATE_SYNTAX_CACHE_BACKWARD (from);
1342                   quoted = char_quoted (buf, from - 1);
1343
1344                   if (quoted)
1345                     from--;
1346                   if (! (quoted
1347                          || (syncode =
1348                              SYNTAX_FROM_CACHE (mirrortab,
1349                                                 BUF_FETCH_CHAR (buf, from - 1)))
1350                          == Sword
1351                          || syncode == Ssymbol
1352                          || syncode == Squote))
1353                     goto done2;
1354                   from--;
1355                 }
1356               goto done2;
1357
1358             case Smath:
1359               if (!sexpflag)
1360                 break;
1361               if (from != stop && c == BUF_FETCH_CHAR (buf, from - 1))
1362                 from--;
1363               if (mathexit)
1364                 {
1365                   mathexit = 0;
1366                   goto open2;
1367                 }
1368               mathexit = 1;
1369
1370             case Sclose:
1371               if (!++depth) goto done2;
1372               break;
1373
1374             case Sopen:
1375             open2:
1376             if (!--depth) goto done2;
1377             if (depth < min_depth)
1378               {
1379                 if (noerror)
1380                   return Qnil;
1381                 error ("Containing expression ends prematurely");
1382               }
1383             break;
1384
1385             case Scomment_fence:
1386               comstyle = ST_COMMENT_STYLE;
1387             case Sendcomment:
1388               if (parse_sexp_ignore_comments)
1389                 from = find_start_of_comment (buf, from, stop, comstyle);
1390               break;
1391
1392             case Sstring_fence:
1393             case Sstring:
1394               {
1395                 Emchar stringterm;
1396
1397                 if (code != Sstring_fence)
1398                   {
1399                 /* XEmacs change: call syntax_match() on character */
1400                 Emchar ch = BUF_FETCH_CHAR (buf, from);
1401                     Lisp_Object stermobj =
1402                       syntax_match (syntax_cache.current_syntax_table, ch);
1403
1404                 if (CHARP (stermobj))
1405                   stringterm = XCHAR (stermobj);
1406                 else
1407                   stringterm = ch;
1408                   }
1409                 else
1410                   stringterm = '\0'; /* avoid compiler warnings */
1411
1412                 while (1)
1413                   {
1414                     if (from == stop) goto lose;
1415
1416                     UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1417                     c = BUF_FETCH_CHAR (buf, from - 1);
1418
1419                     if ((code == Sstring
1420                         ? c == stringterm
1421                          : SYNTAX_FROM_CACHE (mirrortab, c) == Sstring_fence)
1422                         && !char_quoted (buf, from - 1))
1423                       {
1424                       break;
1425                       }
1426
1427                     from--;
1428                   }
1429                 from--;
1430                 if (!depth && sexpflag) goto done2;
1431                 break;
1432               }
1433             }
1434         }
1435
1436       /* Reached start of buffer.  Error if within object,
1437          return nil if between */
1438       if (depth) goto lose;
1439
1440       return Qnil;
1441
1442     done2:
1443       count++;
1444     }
1445
1446
1447   return (make_int (from));
1448
1449 lose:
1450   if (!noerror)
1451     error ("Unbalanced parentheses");
1452   return Qnil;
1453 }
1454
1455 int
1456 char_quoted (struct buffer *buf, Bufpos pos)
1457 {
1458   enum syntaxcode code;
1459   Bufpos beg = BUF_BEGV (buf);
1460   int quoted = 0;
1461   Bufpos startpos = pos;
1462
1463   while (pos > beg)
1464     {
1465       UPDATE_SYNTAX_CACHE_BACKWARD (pos - 1);
1466       code = SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, pos - 1));
1467
1468       if (code != Scharquote && code != Sescape)
1469         break;
1470       pos--;
1471       quoted = !quoted;
1472     }
1473
1474   UPDATE_SYNTAX_CACHE (startpos);
1475   return quoted;
1476 }
1477
1478 DEFUN ("scan-lists", Fscan_lists, 3, 5, 0, /*
1479 Scan from character number FROM by COUNT lists.
1480 Returns the character number of the position thus found.
1481
1482 If DEPTH is nonzero, paren depth begins counting from that value,
1483 only places where the depth in parentheses becomes zero
1484 are candidates for stopping; COUNT such places are counted.
1485 Thus, a positive value for DEPTH means go out levels.
1486
1487 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
1488
1489 If the beginning or end of (the accessible part of) the buffer is reached
1490 and the depth is wrong, an error is signaled.
1491 If the depth is right but the count is not used up, nil is returned.
1492
1493 If optional arg BUFFER is non-nil, scanning occurs in that buffer instead
1494 of in the current buffer.
1495
1496 If optional arg NOERROR is non-nil, scan-lists will return nil instead of
1497 signalling an error.
1498 */
1499        (from, count, depth, buffer, noerror))
1500 {
1501   struct buffer *buf;
1502
1503   CHECK_INT (from);
1504   CHECK_INT (count);
1505   CHECK_INT (depth);
1506   buf = decode_buffer (buffer, 0);
1507
1508   return scan_lists (buf, XINT (from), XINT (count), XINT (depth), 0,
1509                      !NILP (noerror));
1510 }
1511
1512 DEFUN ("scan-sexps", Fscan_sexps, 2, 4, 0, /*
1513 Scan from character number FROM by COUNT balanced expressions.
1514 If COUNT is negative, scan backwards.
1515 Returns the character number of the position thus found.
1516
1517 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
1518
1519 If the beginning or end of (the accessible part of) the buffer is reached
1520 in the middle of a parenthetical grouping, an error is signaled.
1521 If the beginning or end is reached between groupings
1522 but before count is used up, nil is returned.
1523
1524 If optional arg BUFFER is non-nil, scanning occurs in that buffer instead
1525 of in the current buffer.
1526
1527 If optional arg NOERROR is non-nil, scan-sexps will return nil instead of
1528 signalling an error.
1529 */
1530        (from, count, buffer, noerror))
1531 {
1532   struct buffer *buf = decode_buffer (buffer, 0);
1533   CHECK_INT (from);
1534   CHECK_INT (count);
1535
1536   return scan_lists (buf, XINT (from), XINT (count), 0, 1, !NILP (noerror));
1537 }
1538
1539 DEFUN ("backward-prefix-chars", Fbackward_prefix_chars, 0, 1, 0, /*
1540 Move point backward over any number of chars with prefix syntax.
1541 This includes chars with "quote" or "prefix" syntax (' or p).
1542
1543 Optional arg BUFFER defaults to the current buffer.
1544 */
1545        (buffer))
1546 {
1547   struct buffer *buf = decode_buffer (buffer, 0);
1548   Bufpos beg = BUF_BEGV (buf);
1549   Bufpos pos = BUF_PT (buf);
1550 #ifndef emacs
1551   Lisp_Char_Table *mirrortab = XCHAR_TABLE (buf->mirror_syntax_table);
1552 #endif
1553   Emchar c = '\0'; /* initialize to avoid compiler warnings */
1554
1555
1556   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, pos, -1);
1557
1558   while (pos > beg && !char_quoted (buf, pos - 1)
1559          /* Previous statement updates syntax table.  */
1560          && (SYNTAX_FROM_CACHE (mirrortab, c = BUF_FETCH_CHAR (buf, pos - 1)) == Squote
1561              || SYNTAX_CODE_PREFIX (SYNTAX_CODE_FROM_CACHE (mirrortab, c))))
1562     pos--;
1563
1564   BUF_SET_PT (buf, pos);
1565
1566   return Qnil;
1567 }
1568 \f
1569 /* Parse forward from FROM to END,
1570    assuming that FROM has state OLDSTATE (nil means FROM is start of function),
1571    and return a description of the state of the parse at END.
1572    If STOPBEFORE is nonzero, stop at the start of an atom.
1573    If COMMENTSTOP is nonzero, stop at the start of a comment.  */
1574
1575 static void
1576 scan_sexps_forward (struct buffer *buf, struct lisp_parse_state *stateptr,
1577                     Bufpos from, Bufpos end,
1578                     int targetdepth, int stopbefore,
1579                     Lisp_Object oldstate,
1580                     int commentstop)
1581 {
1582   struct lisp_parse_state state;
1583
1584   enum syntaxcode code;
1585   struct level { int last, prev; };
1586   struct level levelstart[100];
1587   struct level *curlevel = levelstart;
1588   struct level *endlevel = levelstart + 100;
1589   int depth;    /* Paren depth of current scanning location.
1590                            level - levelstart equals this except
1591                            when the depth becomes negative.  */
1592   int mindepth;         /* Lowest DEPTH value seen.  */
1593   int start_quoted = 0;         /* Nonzero means starting after a char quote */
1594   int boundary_stop = commentstop == -1;
1595   Lisp_Object tem;
1596
1597   SETUP_SYNTAX_CACHE (from, 1);
1598   if (NILP (oldstate))
1599     {
1600       depth = 0;
1601       state.instring = -1;
1602       state.incomment = 0;
1603       state.comstyle = 0;       /* comment style a by default */
1604       state.comstr_start = -1;  /* no comment/string seen.  */
1605     }
1606   else
1607     {
1608       tem = Fcar (oldstate);    /* elt 0, depth */
1609       if (!NILP (tem))
1610         depth = XINT (tem);
1611       else
1612         depth = 0;
1613
1614       oldstate = Fcdr (oldstate);
1615       oldstate = Fcdr (oldstate);
1616       oldstate = Fcdr (oldstate);
1617       tem = Fcar (oldstate);    /* elt 3, instring */
1618       state.instring = ( !NILP (tem) 
1619                          ? ( INTP (tem) ? XINT (tem) : ST_STRING_STYLE) 
1620                          : -1);
1621
1622       oldstate = Fcdr (oldstate);
1623       tem = Fcar (oldstate);    /* elt 4, incomment */
1624       state.incomment = !NILP (tem);
1625
1626       oldstate = Fcdr (oldstate);
1627       tem = Fcar (oldstate);    /* elt 5, follows-quote */
1628       start_quoted = !NILP (tem);
1629
1630       /* if the eighth element of the list is nil, we are in comment style
1631          a; if it is t, we are in comment style b; if it is 'syntax-table,
1632          we are in a generic comment */
1633       oldstate = Fcdr (oldstate);
1634       oldstate = Fcdr (oldstate);
1635       tem = Fcar (oldstate);    /* elt 7, comment style a/b/fence */
1636       state.comstyle = NILP (tem) ? 0 : ( EQ (tem, Qsyntax_table)
1637                                           ? ST_COMMENT_STYLE : 1 );
1638
1639       oldstate = Fcdr (oldstate); /* elt 8, start of last comment/string */
1640       tem = Fcar (oldstate);
1641       state.comstr_start = NILP (tem) ? -1 : XINT (tem);
1642
1643       /* elt 9, char numbers of starts-of-expression of levels
1644          (starting from outermost). */
1645       oldstate = Fcdr (oldstate);
1646       tem = Fcar (oldstate);    /* elt 9, intermediate data for
1647                                    continuation of parsing (subject
1648                                    to change). */
1649       while (!NILP (tem))       /* >= second enclosing sexps.  */
1650         {
1651           curlevel->last = XINT (Fcar (tem));
1652           if (++curlevel == endlevel)
1653             error ("Nesting too deep for parser");
1654           curlevel->prev = -1;
1655           curlevel->last = -1;
1656           tem = Fcdr (tem);
1657         }
1658     }
1659   state.quoted = 0;
1660   mindepth = depth;
1661
1662   curlevel->prev = -1;
1663   curlevel->last = -1;
1664
1665   /* Enter the loop at a place appropriate for initial state. */
1666
1667   if (state.incomment) goto startincomment;
1668   if (state.instring >= 0)
1669     {
1670       if (start_quoted) goto startquotedinstring;
1671       goto startinstring;
1672     }
1673   if (start_quoted) goto startquoted;
1674
1675   while (from < end)
1676     {
1677       Emchar c;
1678       int syncode;
1679
1680       QUIT;
1681
1682       UPDATE_SYNTAX_CACHE_FORWARD (from);
1683       c = BUF_FETCH_CHAR (buf, from);
1684       code = SYNTAX_FROM_CACHE (mirrortab, c);
1685       syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1686       from++;
1687
1688           /* record the comment style we have entered so that only the
1689              comment-ender sequence (or single char) of the same style
1690              actually terminates the comment section. */
1691       if (code == Scomment)
1692         {
1693           state.comstyle =
1694             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1695             == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1696           state.comstr_start = from - 1;
1697         }
1698
1699       /* a generic comment delimiter? */
1700       else if (code == Scomment_fence)
1701         {
1702           state.comstyle = ST_COMMENT_STYLE;
1703           state.comstr_start = from - 1;
1704           code = Scomment;
1705         }
1706
1707       else if (from < end &&
1708                SYNTAX_CODE_START_FIRST_P (syncode))
1709         {
1710           int next_syncode;
1711           UPDATE_SYNTAX_CACHE_FORWARD (from);
1712           next_syncode =
1713             SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from));
1714
1715           if (SYNTAX_CODES_START_P (syncode, next_syncode))
1716         {
1717           code = Scomment;
1718               state.comstyle = SYNTAX_CODES_COMMENT_MASK_START
1719                 (syncode, next_syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1720               state.comstr_start = from - 1;
1721           from++;
1722               UPDATE_SYNTAX_CACHE_FORWARD (from);
1723             }
1724         }
1725
1726       if (SYNTAX_CODE_PREFIX (syncode))
1727         continue;
1728       switch (code)
1729         {
1730         case Sescape:
1731         case Scharquote:
1732           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1733           curlevel->last = from - 1;
1734         startquoted:
1735           if (from == end) goto endquoted;
1736           from++;
1737           goto symstarted;
1738           /* treat following character as a word constituent */
1739         case Sword:
1740         case Ssymbol:
1741           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1742           curlevel->last = from - 1;
1743         symstarted:
1744           while (from < end)
1745             {
1746               UPDATE_SYNTAX_CACHE_FORWARD (from);
1747               switch (SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from)))
1748                 {
1749                 case Scharquote:
1750                 case Sescape:
1751                   from++;
1752                   if (from == end) goto endquoted;
1753                   break;
1754                 case Sword:
1755                 case Ssymbol:
1756                 case Squote:
1757                   break;
1758                 default:
1759                   goto symdone;
1760                 }
1761               from++;
1762             }
1763         symdone:
1764           curlevel->prev = curlevel->last;
1765           break;
1766
1767         case Scomment:
1768           state.incomment = 1;
1769           if (commentstop || boundary_stop) goto done;
1770         startincomment:
1771           if (commentstop == 1)
1772             goto done;
1773           UPDATE_SYNTAX_CACHE_FORWARD (from);
1774           {
1775             Bufpos newfrom = find_end_of_comment (buf, from, end, state.comstyle);
1776             if (newfrom < 0)
1777               {
1778                 /* we terminated search because from == end */
1779                 from = end;
1780                 goto done;
1781               }
1782             from = newfrom;
1783           }
1784           state.incomment = 0;
1785           state.comstyle = 0;                /* reset the comment style */
1786           if (boundary_stop) goto done;
1787           break;
1788
1789         case Sopen:
1790           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1791           depth++;
1792           /* curlevel++->last ran into compiler bug on Apollo */
1793           curlevel->last = from - 1;
1794           if (++curlevel == endlevel)
1795             error ("Nesting too deep for parser");
1796           curlevel->prev = -1;
1797           curlevel->last = -1;
1798           if (targetdepth == depth) goto done;
1799           break;
1800
1801         case Sclose:
1802           depth--;
1803           if (depth < mindepth)
1804             mindepth = depth;
1805           if (curlevel != levelstart)
1806             curlevel--;
1807           curlevel->prev = curlevel->last;
1808           if (targetdepth == depth) goto done;
1809           break;
1810
1811         case Sstring:
1812         case Sstring_fence:
1813           state.comstr_start = from - 1;
1814             if (stopbefore) goto stop; /* this arg means stop at sexp start */
1815             curlevel->last = from - 1;
1816           if (code == Sstring_fence)
1817             {
1818               state.instring = ST_STRING_STYLE;
1819             }
1820           else
1821             {
1822               /* XEmacs change: call syntax_match() on character */
1823               Emchar ch = BUF_FETCH_CHAR (buf, from - 1);
1824               Lisp_Object stermobj =
1825                 syntax_match (syntax_cache.current_syntax_table, ch);
1826
1827               if (CHARP (stermobj))
1828                 state.instring = XCHAR (stermobj);
1829               else
1830                 state.instring = ch;
1831           }
1832           if (boundary_stop) goto done;
1833         startinstring:
1834           while (1)
1835             {
1836               enum syntaxcode temp_code;
1837
1838               if (from >= end) goto done;
1839
1840               UPDATE_SYNTAX_CACHE_FORWARD (from);
1841               c = BUF_FETCH_CHAR (buf, from);
1842               temp_code = SYNTAX_FROM_CACHE (mirrortab, c);
1843
1844               if (
1845                   state.instring != ST_STRING_STYLE &&
1846                   temp_code == Sstring &&
1847                   c == state.instring) break;
1848
1849               switch (temp_code)
1850                 {
1851                 case Sstring_fence:
1852                   if (state.instring == ST_STRING_STYLE)
1853                     goto string_end;
1854                   break;
1855                 case Scharquote:
1856                 case Sescape:
1857                   {
1858                     from++;
1859                   startquotedinstring:
1860                     if (from >= end) goto endquoted;
1861                     break;
1862                   }
1863                 default:
1864                   break;
1865                 }
1866               from++;
1867             }
1868         string_end:
1869           state.instring = -1;
1870           curlevel->prev = curlevel->last;
1871           from++;
1872           if (boundary_stop) goto done;
1873           break;
1874
1875         case Smath:
1876           break;
1877
1878         case Swhitespace:
1879         case Spunct:
1880         case Squote:
1881         case Sendcomment:
1882         case Scomment_fence:
1883         case Sinherit:
1884         case Smax:
1885           break;
1886         }
1887     }
1888   goto done;
1889
1890  stop:   /* Here if stopping before start of sexp. */
1891   from--;    /* We have just fetched the char that starts it; */
1892   goto done; /* but return the position before it. */
1893
1894  endquoted:
1895   state.quoted = 1;
1896  done:
1897   state.depth = depth;
1898   state.mindepth = mindepth;
1899   state.thislevelstart = curlevel->prev;
1900   state.prevlevelstart
1901     = (curlevel == levelstart) ? -1 : (curlevel - 1)->last;
1902   state.location = from;
1903   state.levelstarts = Qnil;
1904   while (--curlevel >= levelstart)
1905     state.levelstarts = Fcons (make_int (curlevel->last),
1906                                state.levelstarts);
1907
1908   *stateptr = state;
1909 }
1910
1911 DEFUN ("parse-partial-sexp", Fparse_partial_sexp, 2, 7, 0, /*
1912 Parse Lisp syntax starting at FROM until TO; return status of parse at TO.
1913 Parsing stops at TO or when certain criteria are met;
1914  point is set to where parsing stops.
1915 If fifth arg OLDSTATE is omitted or nil,
1916  parsing assumes that FROM is the beginning of a function.
1917 Value is a list of nine elements describing final state of parsing:
1918  0. depth in parens.
1919  1. character address of start of innermost containing list; nil if none.
1920  2. character address of start of last complete sexp terminated.
1921  3. non-nil if inside a string.
1922     (It is the character that will terminate the string,
1923      or t if the string should be terminated by an explicit
1924      `syntax-table' property.)
1925  4. t if inside a comment.
1926  5. t if following a quote character.
1927  6. the minimum paren-depth encountered during this scan.
1928  7. nil if in comment style a, or not in a comment; t if in comment style b;
1929     `syntax-table' if given by an explicit `syntax-table' property.
1930  8. character address of start of last comment or string; nil if none.
1931  9. Intermediate data for continuation of parsing (subject to change).
1932 If third arg TARGETDEPTH is non-nil, parsing stops if the depth
1933 in parentheses becomes equal to TARGETDEPTH.
1934 Fourth arg STOPBEFORE non-nil means stop when come to
1935  any character that starts a sexp.
1936 Fifth arg OLDSTATE is a nine-element list like what this function returns.
1937 It is used to initialize the state of the parse.  Its second and third
1938 elements are ignored.
1939 Sixth arg COMMENTSTOP non-nil means stop at the start of a comment. If it
1940 is `syntax-table', stop after the start of a comment or a string, or after
1941 the end of a comment or string.
1942 */
1943        (from, to, targetdepth, stopbefore, oldstate, commentstop, buffer))
1944 {
1945   struct lisp_parse_state state;
1946   int target;
1947   Bufpos start, end;
1948   struct buffer *buf = decode_buffer (buffer, 0);
1949   Lisp_Object val;
1950
1951   if (!NILP (targetdepth))
1952     {
1953       CHECK_INT (targetdepth);
1954       target = XINT (targetdepth);
1955     }
1956   else
1957     target = -100000;           /* We won't reach this depth */
1958
1959   get_buffer_range_char (buf, from, to, &start, &end, 0);
1960   scan_sexps_forward (buf, &state, start, end,
1961                       target, !NILP (stopbefore), oldstate,
1962                       (NILP (commentstop)
1963                        ? 0 : (EQ (commentstop, Qsyntax_table) ? -1 : 1)));
1964   BUF_SET_PT (buf, state.location);
1965
1966   /* reverse order */
1967   val = Qnil;
1968   val = Fcons (state.levelstarts, val);
1969   val = Fcons ((state.incomment || (state.instring >= 0))
1970                ? make_int (state.comstr_start) : Qnil, val);
1971   val = Fcons (state.comstyle  ? (state.comstyle == ST_COMMENT_STYLE
1972                                   ? Qsyntax_table : Qt) : Qnil, val);
1973   val = Fcons (make_int (state.mindepth),   val);
1974   val = Fcons (state.quoted    ? Qt : Qnil, val);
1975   val = Fcons (state.incomment ? Qt : Qnil, val);
1976   val = Fcons (state.instring < 0
1977                ? Qnil
1978                : (state.instring == ST_STRING_STYLE
1979                   ? Qt : make_int (state.instring)), val);
1980   val = Fcons (state.thislevelstart < 0 ? Qnil : make_int (state.thislevelstart), val);
1981   val = Fcons (state.prevlevelstart < 0 ? Qnil : make_int (state.prevlevelstart), val);
1982   val = Fcons (make_int (state.depth), val);
1983
1984   return val;
1985 }
1986
1987
1988 /* Updating of the mirror syntax table.
1989
1990    Each syntax table has a corresponding mirror table in it.
1991    Whenever we make a change to a syntax table, we call
1992    update_syntax_table() on it.
1993
1994    #### We really only need to map over the changed range.
1995
1996    If we change the standard syntax table, we need to map over
1997    all tables because any of them could be inheriting from the
1998    standard syntax table.
1999
2000    When `set-syntax-table' is called, we set the buffer's mirror
2001    syntax table as well.
2002    */
2003
2004 struct cmst_arg
2005 {
2006   Lisp_Object mirrortab;
2007   int check_inherit;
2008 };
2009
2010 static int
2011 cmst_mapfun (struct chartab_range *range, Lisp_Object val, void *arg)
2012 {
2013   struct cmst_arg *closure = (struct cmst_arg *) arg;
2014
2015   if (CONSP (val))
2016     val = XCAR (val);
2017   if (SYNTAX_FROM_CODE (XINT (val)) == Sinherit
2018       && closure->check_inherit)
2019     {
2020       struct cmst_arg recursive;
2021
2022       recursive.mirrortab = closure->mirrortab;
2023       recursive.check_inherit = 0;
2024       map_char_table (XCHAR_TABLE (Vstandard_syntax_table), range,
2025                                    cmst_mapfun, &recursive);
2026     }
2027   else
2028     put_char_table (XCHAR_TABLE (closure->mirrortab), range, val);
2029   return 0;
2030 }
2031
2032 static void
2033 update_just_this_syntax_table (Lisp_Char_Table *ct)
2034 {
2035   struct chartab_range range;
2036   struct cmst_arg arg;
2037
2038   arg.mirrortab = ct->mirror_table;
2039   arg.check_inherit = (CHAR_TABLEP (Vstandard_syntax_table)
2040                        && ct != XCHAR_TABLE (Vstandard_syntax_table));
2041   range.type = CHARTAB_RANGE_ALL;
2042   map_char_table (ct, &range, cmst_mapfun, &arg);
2043 }
2044
2045 /* Called from chartab.c when a change is made to a syntax table.
2046    If this is the standard syntax table, we need to recompute
2047    *all* syntax tables (yuck).  Otherwise we just recompute this
2048    one. */
2049
2050 void
2051 update_syntax_table (Lisp_Char_Table *ct)
2052 {
2053   /* Don't be stymied at startup. */
2054   if (CHAR_TABLEP (Vstandard_syntax_table)
2055       && ct == XCHAR_TABLE (Vstandard_syntax_table))
2056     {
2057       Lisp_Object syntab;
2058
2059       for (syntab = Vall_syntax_tables; !NILP (syntab);
2060            syntab = XCHAR_TABLE (syntab)->next_table)
2061         update_just_this_syntax_table (XCHAR_TABLE (syntab));
2062     }
2063   else
2064     update_just_this_syntax_table (ct);
2065 }
2066
2067 \f
2068 /************************************************************************/
2069 /*                            initialization                            */
2070 /************************************************************************/
2071
2072 void
2073 syms_of_syntax (void)
2074 {
2075   defsymbol (&Qsyntax_table_p, "syntax-table-p");
2076   defsymbol (&Qsyntax_table, "syntax-table");
2077
2078   DEFSUBR (Fsyntax_table_p);
2079   DEFSUBR (Fsyntax_table);
2080   DEFSUBR (Fstandard_syntax_table);
2081   DEFSUBR (Fcopy_syntax_table);
2082   DEFSUBR (Fset_syntax_table);
2083   DEFSUBR (Fsyntax_designator_chars);
2084   DEFSUBR (Fchar_syntax);
2085   DEFSUBR (Fmatching_paren);
2086   /* DEFSUBR (Fmodify_syntax_entry); now in Lisp. */
2087   /* DEFSUBR (Fdescribe_syntax); now in Lisp. */
2088
2089   DEFSUBR (Fforward_word);
2090
2091   DEFSUBR (Fforward_comment);
2092   DEFSUBR (Fscan_lists);
2093   DEFSUBR (Fscan_sexps);
2094   DEFSUBR (Fbackward_prefix_chars);
2095   DEFSUBR (Fparse_partial_sexp);
2096 }
2097
2098 void
2099 vars_of_syntax (void)
2100 {
2101   DEFVAR_BOOL ("parse-sexp-ignore-comments", &parse_sexp_ignore_comments /*
2102 Non-nil means `forward-sexp', etc., should treat comments as whitespace.
2103 */ );
2104   parse_sexp_ignore_comments = 0;
2105
2106   DEFVAR_BOOL ("lookup-syntax-properties", &lookup_syntax_properties /*
2107 Non-nil means `forward-sexp', etc., grant `syntax-table' property.
2108 The value of this property should be either a syntax table, or a cons
2109 of the form (SYNTAXCODE . MATCHCHAR), SYNTAXCODE being the numeric
2110 syntax code, MATCHCHAR being nil or the character to match (which is
2111 relevant only for open/close type.
2112 */ );
2113   lookup_syntax_properties = 1;
2114
2115   DEFVAR_BOOL ("words-include-escapes", &words_include_escapes /*
2116 Non-nil means `forward-word', etc., should treat escape chars part of words.
2117 */ );
2118   words_include_escapes = 0;
2119
2120   no_quit_in_re_search = 0;
2121 }
2122
2123 static void
2124 define_standard_syntax (const char *p, enum syntaxcode syn)
2125 {
2126   for (; *p; p++)
2127     Fput_char_table (make_char (*p), make_int (syn), Vstandard_syntax_table);
2128 }
2129
2130 void
2131 complex_vars_of_syntax (void)
2132 {
2133   Emchar i;
2134   const char *p;
2135   /* Set this now, so first buffer creation can refer to it. */
2136   /* Make it nil before calling copy-syntax-table
2137      so that copy-syntax-table will know not to try to copy from garbage */
2138   Vstandard_syntax_table = Qnil;
2139   Vstandard_syntax_table = Fcopy_syntax_table (Qnil);
2140   staticpro (&Vstandard_syntax_table);
2141
2142   Vsyntax_designator_chars_string = make_string_nocopy (syntax_code_spec,
2143                                                         Smax);
2144   staticpro (&Vsyntax_designator_chars_string);
2145
2146   fill_char_table (XCHAR_TABLE (Vstandard_syntax_table), make_int (Spunct));
2147
2148   for (i = 0; i <= 32; i++)     /* Control 0 plus SPACE */
2149     Fput_char_table (make_char (i), make_int (Swhitespace),
2150                      Vstandard_syntax_table);
2151   for (i = 127; i <= 159; i++)  /* DEL plus Control 1 */
2152     Fput_char_table (make_char (i), make_int (Swhitespace),
2153                      Vstandard_syntax_table);
2154
2155   define_standard_syntax ("abcdefghijklmnopqrstuvwxyz"
2156                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2157                           "0123456789"
2158                           "$%", Sword);
2159   define_standard_syntax ("\"", Sstring);
2160   define_standard_syntax ("\\", Sescape);
2161   define_standard_syntax ("_-+*/&|<>=", Ssymbol);
2162   define_standard_syntax (".,;:?!#@~^'`", Spunct);
2163
2164   for (p = "()[]{}"; *p; p+=2)
2165     {
2166       Fput_char_table (make_char (p[0]),
2167                        Fcons (make_int (Sopen), make_char (p[1])),
2168                        Vstandard_syntax_table);
2169       Fput_char_table (make_char (p[1]),
2170                        Fcons (make_int (Sclose), make_char (p[0])),
2171                        Vstandard_syntax_table);
2172     }
2173 }