This commit was manufactured by cvs2svn to create branch 'XEmacs-21_4'.
[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 COUNT defaults to 1, and BUFFER defaults to the current buffer.
568 */
569        (count, buffer))
570 {
571   Bufpos val;
572   struct buffer *buf = decode_buffer (buffer, 0);
573   EMACS_INT n;
574
575   if (NILP (count))
576     n = 1;
577   else
578     {
579       CHECK_INT (count);
580       n = XINT (count);
581     }
582
583   val = scan_words (buf, BUF_PT (buf), n);
584   if (val)
585     {
586       BUF_SET_PT (buf, val);
587       return Qt;
588     }
589   else
590     {
591       BUF_SET_PT (buf, n > 0 ? BUF_ZV (buf) : BUF_BEGV (buf));
592       return Qnil;
593     }
594 }
595 \f
596 static void scan_sexps_forward (struct buffer *buf,
597                                 struct lisp_parse_state *,
598                                 Bufpos from, Bufpos end,
599                                 int targetdepth, int stopbefore,
600                                 Lisp_Object oldstate,
601                                 int commentstop);
602
603 static int
604 find_start_of_comment (struct buffer *buf, Bufpos from, Bufpos stop,
605                        int comstyle)
606 {
607   Emchar c;
608   enum syntaxcode code;
609
610   /* Look back, counting the parity of string-quotes,
611      and recording the comment-starters seen.
612      When we reach a safe place, assume that's not in a string;
613      then step the main scan to the earliest comment-starter seen
614      an even number of string quotes away from the safe place.
615
616      OFROM[I] is position of the earliest comment-starter seen
617      which is I+2X quotes from the comment-end.
618      PARITY is current parity of quotes from the comment end.  */
619   int parity = 0;
620   Emchar my_stringend = 0;
621   int string_lossage = 0;
622   Bufpos comment_end = from;
623   Bufpos comstart_pos = 0;
624   int comstart_parity = 0;
625   int styles_match_p = 0;
626   /* mask to match comment styles against; for ST_COMMENT_STYLE, this
627      will get set to SYNTAX_COMMENT_STYLE_B, but never get checked */
628   int mask = comstyle ? SYNTAX_COMMENT_STYLE_B : SYNTAX_COMMENT_STYLE_A;
629
630   /* At beginning of range to scan, we're outside of strings;
631      that determines quote parity to the comment-end.  */
632   while (from != stop)
633     {
634       int syncode;
635
636       /* Move back and examine a character.  */
637       from--;
638       UPDATE_SYNTAX_CACHE_BACKWARD (from);
639
640       c = BUF_FETCH_CHAR (buf, from);
641       code = SYNTAX_FROM_CACHE (mirrortab, c);
642       syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
643
644       /* is this a 1-char comment end sequence? if so, try
645          to see if style matches previously extracted mask */
646       if (code == Sendcomment)
647         {
648           styles_match_p =
649             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) & mask;
650         }
651
652       /* or are we looking at a 1-char comment start sequence
653          of the style matching mask? */
654       else if (code == Scomment)
655         {
656           styles_match_p =
657             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) & mask;
658         }
659
660       /* otherwise, is this a 2-char comment end or start sequence? */
661       else if (from > stop)
662         do
663           {
664             /* 2-char comment end sequence? */
665             if (SYNTAX_CODE_END_SECOND_P (syncode))
666               {
667                 int prev_syncode;
668                 UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
669                 prev_syncode =
670                   SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
671
672                 if (SYNTAX_CODES_END_P (prev_syncode, syncode))
673                   {
674                     code = Sendcomment;
675                     styles_match_p =
676                       SYNTAX_CODES_COMMENT_MASK_END (prev_syncode, syncode);
677                     from--;
678                     UPDATE_SYNTAX_CACHE_BACKWARD (from);
679                     c = BUF_FETCH_CHAR (buf, from);
680
681                     /* Found a comment-end sequence, so skip past the
682                        check for a comment-start */
683                     break;
684                   }
685               }
686
687             /* 2-char comment start sequence? */
688             if (SYNTAX_CODE_START_SECOND_P (syncode))
689               {
690                 int prev_syncode;
691                 UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
692                 prev_syncode =
693                   SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
694
695                 if (SYNTAX_CODES_START_P (prev_syncode, syncode))
696                   {
697                     code = Scomment;
698                     styles_match_p =
699                       SYNTAX_CODES_COMMENT_MASK_START (prev_syncode, syncode);
700                     from--;
701                     UPDATE_SYNTAX_CACHE_BACKWARD (from);
702                     c = BUF_FETCH_CHAR (buf, from);
703                   }
704               }
705           } while (0);
706
707       /* Ignore escaped characters.  */
708       if (char_quoted (buf, from))
709         continue;
710
711       /* Track parity of quotes.  */
712       if (code == Sstring)
713         {
714           parity ^= 1;
715           if (my_stringend == 0)
716             my_stringend = c;
717           /* If we have two kinds of string delimiters.
718              There's no way to grok this scanning backwards.  */
719           else if (my_stringend != c)
720             string_lossage = 1;
721         }
722
723       if (code == Sstring_fence || code == Scomment_fence)
724         {
725           parity ^= 1;
726           if (my_stringend == 0)
727             my_stringend =
728               code == Sstring_fence ? ST_STRING_STYLE : ST_COMMENT_STYLE;
729           /* If we have two kinds of string delimiters.
730              There's no way to grok this scanning backwards.  */
731           else if (my_stringend != (code == Sstring_fence 
732                                     ? ST_STRING_STYLE : ST_COMMENT_STYLE))
733             string_lossage = 1;
734         }
735
736       /* Record comment-starters according to that
737          quote-parity to the comment-end.  */
738       if (code == Scomment && styles_match_p)
739         {
740           comstart_parity = parity;
741           comstart_pos = from;
742         }
743
744       /* If we find another earlier comment-ender,
745          any comment-starts earlier than that don't count
746          (because they go with the earlier comment-ender).  */
747       if (code == Sendcomment && styles_match_p)
748         break;
749
750       /* Assume a defun-start point is outside of strings.  */
751       if (code == Sopen
752           && (from == stop || BUF_FETCH_CHAR (buf, from - 1) == '\n'))
753         break;
754     }
755
756   if (comstart_pos == 0)
757     from = comment_end;
758   /* If the earliest comment starter
759      is followed by uniform paired string quotes or none,
760      we know it can't be inside a string
761      since if it were then the comment ender would be inside one.
762      So it does start a comment.  Skip back to it.  */
763   else if (comstart_parity == 0 && !string_lossage)
764     from = comstart_pos;
765   else
766     {
767       /* We had two kinds of string delimiters mixed up
768          together.  Decode this going forwards.
769          Scan fwd from the previous comment ender
770          to the one in question; this records where we
771          last passed a comment starter.  */
772
773       struct lisp_parse_state state;
774       scan_sexps_forward (buf, &state, find_defun_start (buf, comment_end),
775                           comment_end - 1, -10000, 0, Qnil, 0);
776       if (state.incomment)
777         from = state.comstr_start;
778       else
779         /* We can't grok this as a comment; scan it normally.  */
780         from = comment_end;
781       UPDATE_SYNTAX_CACHE_FORWARD (from - 1);
782     }
783   return from;
784 }
785
786 static Bufpos
787 find_end_of_comment (struct buffer *buf, Bufpos from, Bufpos stop, int comstyle)
788 {
789   int c;
790   int prev_code;
791   /* mask to match comment styles against; for ST_COMMENT_STYLE, this
792      will get set to SYNTAX_COMMENT_STYLE_B, but never get checked */
793   int mask = comstyle ? SYNTAX_COMMENT_STYLE_B : SYNTAX_COMMENT_STYLE_A;
794
795   /* This is only called by functions which have already set up the
796      syntax_cache and are keeping it up-to-date */
797   while (1)
798     {
799       if (from == stop)
800         {
801           return -1;
802         }
803
804       UPDATE_SYNTAX_CACHE_FORWARD (from);
805       c = BUF_FETCH_CHAR (buf, from);
806
807       /* Test for generic comments */
808       if (comstyle == ST_COMMENT_STYLE)
809         {
810           if (SYNTAX_FROM_CACHE (mirrortab, c) == Scomment_fence)
811             {
812               from++;
813               UPDATE_SYNTAX_CACHE_FORWARD (from);
814               break;
815             }
816           from++;
817           continue; /* No need to test other comment styles in a
818                        generic comment */
819         }
820       else
821
822         if (SYNTAX_FROM_CACHE (mirrortab, c) == Sendcomment
823             && SYNTAX_CODE_MATCHES_1CHAR_P
824             (SYNTAX_CODE_FROM_CACHE (mirrortab, c), mask))
825         /* we have encountered a comment end of the same style
826            as the comment sequence which began this comment
827            section */
828           {
829             from++;
830             UPDATE_SYNTAX_CACHE_FORWARD (from);
831             break;
832           }
833
834       prev_code = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
835       from++;
836       UPDATE_SYNTAX_CACHE_FORWARD (from);
837       if (from < stop
838           && SYNTAX_CODES_MATCH_END_P
839           (prev_code,
840            SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from)),
841            mask)
842
843           )
844         /* we have encountered a comment end of the same style
845            as the comment sequence which began this comment
846            section */
847         {
848           from++;
849           UPDATE_SYNTAX_CACHE_FORWARD (from);
850           break;
851         }
852     }
853   return from;
854 }
855
856 \f
857 /* #### between FSF 19.23 and 19.28 there are some changes to the logic
858    in this function (and minor changes to find_start_of_comment(),
859    above, which is part of Fforward_comment() in FSF).  Attempts to port
860    that logic made this function break, so I'm leaving it out.  If anyone
861    ever complains about this function not working properly, take a look
862    at those changes.  --ben */
863
864 DEFUN ("forward-comment", Fforward_comment, 0, 2, 0, /*
865 Move forward across up to COUNT comments, or backwards if COUNT is negative.
866 Stop scanning if we find something other than a comment or whitespace.
867 Set point to where scanning stops.
868 If COUNT comments are found as expected, with nothing except whitespace
869 between them, return t; otherwise return nil.
870 Point is set in either case.
871 COUNT defaults to 1, and BUFFER defaults to the current buffer.
872 */
873        (count, buffer))
874 {
875   Bufpos from;
876   Bufpos stop;
877   Emchar c;
878   enum syntaxcode code;
879   int syncode;
880   EMACS_INT n;
881   struct buffer *buf = decode_buffer (buffer, 0);
882
883   if (NILP (count))
884     n = 1;
885   else
886     {
887       CHECK_INT (count);
888       n = XINT (count);
889     }
890
891   from = BUF_PT (buf);
892
893   SETUP_SYNTAX_CACHE (from, n);
894   while (n > 0)
895     {
896       QUIT;
897
898       stop = BUF_ZV (buf);
899       while (from < stop)
900         {
901           int comstyle = 0;     /* mask for finding matching comment style */
902
903           if (char_quoted (buf, from))
904             {
905               from++;
906               continue;
907             }
908
909           UPDATE_SYNTAX_CACHE_FORWARD (from);
910           c = BUF_FETCH_CHAR (buf, from);
911           code = SYNTAX_FROM_CACHE (mirrortab, c);
912           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
913
914           if (code == Scomment)
915             {
916               /* we have encountered a single character comment start
917                  sequence, and we are ignoring all text inside comments.
918                  we must record the comment style this character begins
919                  so that later, only a comment end of the same style actually
920                  ends the comment section */
921               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
922                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
923             }
924
925           else if (code == Scomment_fence)
926             {
927               from++;
928               code = Scomment;
929               comstyle = ST_COMMENT_STYLE;
930             }
931
932           else if (from < stop
933                    && SYNTAX_CODE_START_FIRST_P (syncode))
934             {
935               int next_syncode;
936               UPDATE_SYNTAX_CACHE_FORWARD (from + 1);
937               next_syncode =
938                 SYNTAX_CODE_FROM_CACHE (mirrortab, 
939                                         BUF_FETCH_CHAR (buf, from + 1));
940
941               if (SYNTAX_CODES_START_P (syncode, next_syncode))
942                 {
943                   /* we have encountered a 2char comment start sequence and we
944                      are ignoring all text inside comments. we must record
945                      the comment style this sequence begins so that later,
946                      only a comment end of the same style actually ends
947                      the comment section */
948                   code = Scomment;
949                   comstyle =
950                     SYNTAX_CODES_COMMENT_MASK_START (syncode, next_syncode)
951                     == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
952                   from++;
953                 }
954             }
955
956           if (code == Scomment)
957             {
958               Bufpos newfrom = find_end_of_comment (buf, from, stop, comstyle);
959               if (newfrom < 0)
960                 {
961                   /* we stopped because from==stop */
962                   BUF_SET_PT (buf, stop);
963                   return Qnil;
964                 }
965               from = newfrom;
966
967               /* We have skipped one comment.  */
968               break;
969             }
970           else if (code != Swhitespace
971                    && code != Sendcomment
972                    && code != Scomment )
973             {
974               BUF_SET_PT (buf, from);
975               return Qnil;
976             }
977           from++;
978         }
979
980       /* End of comment reached */
981       n--;
982     }
983
984   while (n < 0)
985     {
986       QUIT;
987
988       stop = BUF_BEGV (buf);
989       while (from > stop)
990         {
991           int comstyle = 0;     /* mask for finding matching comment style */
992
993           from--;
994           if (char_quoted (buf, from))
995             {
996               from--;
997               continue;
998             }
999
1000           c = BUF_FETCH_CHAR (buf, from);
1001           code = SYNTAX_FROM_CACHE (mirrortab, c);
1002           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1003
1004           if (code == Sendcomment)
1005             {
1006               /* we have found a single char end comment. we must record
1007                  the comment style encountered so that later, we can match
1008                  only the proper comment begin sequence of the same style */
1009               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1010                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1011             }
1012
1013           else if (code == Scomment_fence)
1014             {
1015               code = Sendcomment;
1016               comstyle = ST_COMMENT_STYLE;
1017             }
1018
1019           else if (from > stop
1020                    && SYNTAX_CODE_END_SECOND_P (syncode))
1021             {
1022               int prev_syncode;
1023               UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1024               prev_syncode =
1025                 SYNTAX_CODE_FROM_CACHE (mirrortab,
1026                                         BUF_FETCH_CHAR (buf, from - 1));
1027               if (SYNTAX_CODES_END_P (prev_syncode, syncode))
1028                 {
1029                   /* We must record the comment style encountered so that
1030                      later, we can match only the proper comment begin
1031                      sequence of the same style.  */
1032                   code = Sendcomment;
1033                   comstyle = SYNTAX_CODES_COMMENT_MASK_END
1034                     (prev_syncode, syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1035                   from--;
1036                 }
1037             }
1038
1039           if (code == Sendcomment)
1040             {
1041               from = find_start_of_comment (buf, from, stop, comstyle);
1042               break;
1043             }
1044
1045           else if (code != Swhitespace
1046                    && code != Scomment
1047                    && code != Sendcomment)
1048             {
1049               BUF_SET_PT (buf, from + 1);
1050               return Qnil;
1051             }
1052         }
1053
1054       n++;
1055     }
1056
1057   BUF_SET_PT (buf, from);
1058   return Qt;
1059 }
1060
1061 \f
1062 Lisp_Object
1063 scan_lists (struct buffer *buf, Bufpos from, int count, int depth,
1064             int sexpflag, int noerror)
1065 {
1066   Bufpos stop;
1067   Emchar c;
1068   int quoted;
1069   int mathexit = 0;
1070   enum syntaxcode code;
1071   int syncode;
1072   int min_depth = depth;    /* Err out if depth gets less than this. */
1073
1074   if (depth > 0) min_depth = 0;
1075
1076   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, from, count);
1077   while (count > 0)
1078     {
1079       QUIT;
1080
1081       stop = BUF_ZV (buf);
1082       while (from < stop)
1083         {
1084           int comstyle = 0;     /* mask for finding matching comment style */
1085
1086           UPDATE_SYNTAX_CACHE_FORWARD (from);
1087           c = BUF_FETCH_CHAR (buf, from);
1088           code = SYNTAX_FROM_CACHE (mirrortab, c);
1089           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1090           from++;
1091
1092           /* a 1-char comment start sequence */
1093           if (code == Scomment && parse_sexp_ignore_comments)
1094             {
1095               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode) ==
1096                 SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1097             }
1098
1099           /* else, a 2-char comment start sequence? */
1100           else if (from < stop
1101                    && SYNTAX_CODE_START_FIRST_P (syncode)
1102                    && parse_sexp_ignore_comments)
1103             {
1104               int next_syncode;
1105               UPDATE_SYNTAX_CACHE_FORWARD (from);
1106               next_syncode =
1107                 SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from));
1108
1109               if (SYNTAX_CODES_START_P (syncode, next_syncode))
1110                 {
1111               /* we have encountered a comment start sequence and we
1112                  are ignoring all text inside comments. we must record
1113                  the comment style this sequence begins so that later,
1114                  only a comment end of the same style actually ends
1115                  the comment section */
1116               code = Scomment;
1117                   comstyle = SYNTAX_CODES_COMMENT_MASK_START
1118                     (syncode, next_syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1119               from++;
1120             }
1121             }
1122           UPDATE_SYNTAX_CACHE_FORWARD (from);
1123
1124           if (SYNTAX_CODE_PREFIX (syncode))
1125             continue;
1126
1127           switch (code)
1128             {
1129             case Sescape:
1130             case Scharquote:
1131               if (from == stop) goto lose;
1132               from++;
1133               /* treat following character as a word constituent */
1134             case Sword:
1135             case Ssymbol:
1136               if (depth || !sexpflag) break;
1137               /* This word counts as a sexp; return at end of it. */
1138               while (from < stop)
1139                 {
1140                   UPDATE_SYNTAX_CACHE_FORWARD (from);
1141                   switch (SYNTAX_FROM_CACHE (mirrortab,
1142                                              BUF_FETCH_CHAR (buf, from)))
1143                     {
1144                     case Scharquote:
1145                     case Sescape:
1146                       from++;
1147                       if (from == stop) goto lose;
1148                       break;
1149                     case Sword:
1150                     case Ssymbol:
1151                     case Squote:
1152                       break;
1153                     default:
1154                       goto done;
1155                     }
1156                   from++;
1157                 }
1158               goto done;
1159
1160             case Scomment_fence:
1161               comstyle = ST_COMMENT_STYLE;
1162             case Scomment:
1163               if (!parse_sexp_ignore_comments)
1164                 break;
1165               UPDATE_SYNTAX_CACHE_FORWARD (from);
1166               {
1167                 Bufpos newfrom =
1168                   find_end_of_comment (buf, from, stop, comstyle);
1169                 if (newfrom < 0)
1170                   {
1171                     /* we stopped because from == stop in search forward */
1172                     from = stop;
1173                     if (depth == 0)
1174                       goto done;
1175                     goto lose;
1176                   }
1177                 from = newfrom;
1178               }
1179               break;
1180
1181             case Smath:
1182               if (!sexpflag)
1183                 break;
1184               if (from != stop && c == BUF_FETCH_CHAR (buf, from))
1185                 from++;
1186               if (mathexit)
1187                 {
1188                   mathexit = 0;
1189                   goto close1;
1190                 }
1191               mathexit = 1;
1192
1193             case Sopen:
1194               if (!++depth) goto done;
1195               break;
1196
1197             case Sclose:
1198             close1:
1199             if (!--depth) goto done;
1200             if (depth < min_depth)
1201               {
1202                 if (noerror)
1203                   return Qnil;
1204                 error ("Containing expression ends prematurely");
1205               }
1206             break;
1207
1208             case Sstring_fence:
1209             case Sstring:
1210               {
1211                 Emchar stringterm;
1212
1213                 if (code != Sstring_fence)
1214                   {
1215                 /* XEmacs change: call syntax_match on character */
1216                 Emchar ch = BUF_FETCH_CHAR (buf, from - 1);
1217                     Lisp_Object stermobj =
1218                       syntax_match (syntax_cache.current_syntax_table, ch);
1219
1220                 if (CHARP (stermobj))
1221                   stringterm = XCHAR (stermobj);
1222                 else
1223                   stringterm = ch;
1224                   }
1225                 else
1226                   stringterm = '\0'; /* avoid compiler warnings */
1227
1228                 while (1)
1229                   {
1230                     if (from >= stop)
1231                       goto lose;
1232                     UPDATE_SYNTAX_CACHE_FORWARD (from);
1233                     c = BUF_FETCH_CHAR (buf, from);
1234                     if (code == Sstring
1235                         ? c == stringterm
1236                         : SYNTAX_FROM_CACHE (mirrortab, c) == Sstring_fence)
1237                       break;
1238
1239                     switch (SYNTAX_FROM_CACHE (mirrortab, c))
1240                       {
1241                       case Scharquote:
1242                       case Sescape:
1243                         from++;
1244                         break;
1245                       default:
1246                         break;
1247                       }
1248                     from++;
1249                   }
1250                 from++;
1251                 if (!depth && sexpflag) goto done;
1252                 break;
1253               }
1254
1255             default:
1256               break;
1257             }
1258         }
1259
1260       /* Reached end of buffer.  Error if within object,
1261          return nil if between */
1262       if (depth) goto lose;
1263
1264       return Qnil;
1265
1266       /* End of object reached */
1267     done:
1268       count--;
1269     }
1270
1271   while (count < 0)
1272     {
1273       QUIT;
1274
1275       stop = BUF_BEGV (buf);
1276       while (from > stop)
1277         {
1278           int comstyle = 0;     /* mask for finding matching comment style */
1279
1280           from--;
1281           UPDATE_SYNTAX_CACHE_BACKWARD (from);
1282           quoted = char_quoted (buf, from);
1283           if (quoted)
1284             {
1285             from--;
1286               UPDATE_SYNTAX_CACHE_BACKWARD (from);
1287             }
1288
1289           c = BUF_FETCH_CHAR (buf, from);
1290           code = SYNTAX_FROM_CACHE (mirrortab, c);
1291           syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1292
1293           if (code == Sendcomment && parse_sexp_ignore_comments)
1294             {
1295               /* we have found a single char end comment. we must record
1296                  the comment style encountered so that later, we can match
1297                  only the proper comment begin sequence of the same style */
1298               comstyle = SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1299                 == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1300             }
1301
1302           else if (from > stop
1303                    && SYNTAX_CODE_END_SECOND_P (syncode)
1304                    && !char_quoted (buf, from - 1)
1305                    && parse_sexp_ignore_comments)
1306             {
1307               int prev_syncode;
1308               UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1309               prev_syncode = SYNTAX_CODE_FROM_CACHE
1310                 (mirrortab, BUF_FETCH_CHAR (buf, from - 1));
1311
1312               if (SYNTAX_CODES_END_P (prev_syncode, syncode))
1313                 {
1314               /* we must record the comment style encountered so that
1315                  later, we can match only the proper comment begin
1316                  sequence of the same style */
1317               code = Sendcomment;
1318                   comstyle = SYNTAX_CODES_COMMENT_MASK_END
1319                     (prev_syncode, syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1320               from--;
1321             }
1322             }
1323
1324           if (SYNTAX_CODE_PREFIX (syncode))
1325             continue;
1326
1327           switch (quoted ? Sword : code)
1328             {
1329             case Sword:
1330             case Ssymbol:
1331               if (depth || !sexpflag) break;
1332               /* This word counts as a sexp; count object finished after
1333                  passing it. */
1334               while (from > stop)
1335                 {
1336                   UPDATE_SYNTAX_CACHE_BACKWARD (from);
1337                   quoted = char_quoted (buf, from - 1);
1338
1339                   if (quoted)
1340                     from--;
1341                   if (! (quoted
1342                          || (syncode =
1343                              SYNTAX_FROM_CACHE (mirrortab,
1344                                                 BUF_FETCH_CHAR (buf, from - 1)))
1345                          == Sword
1346                          || syncode == Ssymbol
1347                          || syncode == Squote))
1348                     goto done2;
1349                   from--;
1350                 }
1351               goto done2;
1352
1353             case Smath:
1354               if (!sexpflag)
1355                 break;
1356               if (from != stop && c == BUF_FETCH_CHAR (buf, from - 1))
1357                 from--;
1358               if (mathexit)
1359                 {
1360                   mathexit = 0;
1361                   goto open2;
1362                 }
1363               mathexit = 1;
1364
1365             case Sclose:
1366               if (!++depth) goto done2;
1367               break;
1368
1369             case Sopen:
1370             open2:
1371             if (!--depth) goto done2;
1372             if (depth < min_depth)
1373               {
1374                 if (noerror)
1375                   return Qnil;
1376                 error ("Containing expression ends prematurely");
1377               }
1378             break;
1379
1380             case Scomment_fence:
1381               comstyle = ST_COMMENT_STYLE;
1382             case Sendcomment:
1383               if (parse_sexp_ignore_comments)
1384                 from = find_start_of_comment (buf, from, stop, comstyle);
1385               break;
1386
1387             case Sstring_fence:
1388             case Sstring:
1389               {
1390                 Emchar stringterm;
1391
1392                 if (code != Sstring_fence)
1393                   {
1394                 /* XEmacs change: call syntax_match() on character */
1395                 Emchar ch = BUF_FETCH_CHAR (buf, from);
1396                     Lisp_Object stermobj =
1397                       syntax_match (syntax_cache.current_syntax_table, ch);
1398
1399                 if (CHARP (stermobj))
1400                   stringterm = XCHAR (stermobj);
1401                 else
1402                   stringterm = ch;
1403                   }
1404                 else
1405                   stringterm = '\0'; /* avoid compiler warnings */
1406
1407                 while (1)
1408                   {
1409                     if (from == stop) goto lose;
1410
1411                     UPDATE_SYNTAX_CACHE_BACKWARD (from - 1);
1412                     c = BUF_FETCH_CHAR (buf, from - 1);
1413
1414                     if ((code == Sstring
1415                         ? c == stringterm
1416                          : SYNTAX_FROM_CACHE (mirrortab, c) == Sstring_fence)
1417                         && !char_quoted (buf, from - 1))
1418                       {
1419                       break;
1420                       }
1421
1422                     from--;
1423                   }
1424                 from--;
1425                 if (!depth && sexpflag) goto done2;
1426                 break;
1427               }
1428             }
1429         }
1430
1431       /* Reached start of buffer.  Error if within object,
1432          return nil if between */
1433       if (depth) goto lose;
1434
1435       return Qnil;
1436
1437     done2:
1438       count++;
1439     }
1440
1441
1442   return (make_int (from));
1443
1444 lose:
1445   if (!noerror)
1446     error ("Unbalanced parentheses");
1447   return Qnil;
1448 }
1449
1450 int
1451 char_quoted (struct buffer *buf, Bufpos pos)
1452 {
1453   enum syntaxcode code;
1454   Bufpos beg = BUF_BEGV (buf);
1455   int quoted = 0;
1456   Bufpos startpos = pos;
1457
1458   while (pos > beg)
1459     {
1460       UPDATE_SYNTAX_CACHE_BACKWARD (pos - 1);
1461       code = SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, pos - 1));
1462
1463       if (code != Scharquote && code != Sescape)
1464         break;
1465       pos--;
1466       quoted = !quoted;
1467     }
1468
1469   UPDATE_SYNTAX_CACHE (startpos);
1470   return quoted;
1471 }
1472
1473 DEFUN ("scan-lists", Fscan_lists, 3, 5, 0, /*
1474 Scan from character number FROM by COUNT lists.
1475 Returns the character number of the position thus found.
1476
1477 If DEPTH is nonzero, paren depth begins counting from that value,
1478 only places where the depth in parentheses becomes zero
1479 are candidates for stopping; COUNT such places are counted.
1480 Thus, a positive value for DEPTH means go out levels.
1481
1482 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
1483
1484 If the beginning or end of (the accessible part of) the buffer is reached
1485 and the depth is wrong, an error is signaled.
1486 If the depth is right but the count is not used up, nil is returned.
1487
1488 If optional arg BUFFER is non-nil, scanning occurs in that buffer instead
1489 of in the current buffer.
1490
1491 If optional arg NOERROR is non-nil, scan-lists will return nil instead of
1492 signalling an error.
1493 */
1494        (from, count, depth, buffer, noerror))
1495 {
1496   struct buffer *buf;
1497
1498   CHECK_INT (from);
1499   CHECK_INT (count);
1500   CHECK_INT (depth);
1501   buf = decode_buffer (buffer, 0);
1502
1503   return scan_lists (buf, XINT (from), XINT (count), XINT (depth), 0,
1504                      !NILP (noerror));
1505 }
1506
1507 DEFUN ("scan-sexps", Fscan_sexps, 2, 4, 0, /*
1508 Scan from character number FROM by COUNT balanced expressions.
1509 If COUNT is negative, scan backwards.
1510 Returns the character number of the position thus found.
1511
1512 Comments are ignored if `parse-sexp-ignore-comments' is non-nil.
1513
1514 If the beginning or end of (the accessible part of) the buffer is reached
1515 in the middle of a parenthetical grouping, an error is signaled.
1516 If the beginning or end is reached between groupings
1517 but before count is used up, nil is returned.
1518
1519 If optional arg BUFFER is non-nil, scanning occurs in that buffer instead
1520 of in the current buffer.
1521
1522 If optional arg NOERROR is non-nil, scan-sexps will return nil instead of
1523 signalling an error.
1524 */
1525        (from, count, buffer, noerror))
1526 {
1527   struct buffer *buf = decode_buffer (buffer, 0);
1528   CHECK_INT (from);
1529   CHECK_INT (count);
1530
1531   return scan_lists (buf, XINT (from), XINT (count), 0, 1, !NILP (noerror));
1532 }
1533
1534 DEFUN ("backward-prefix-chars", Fbackward_prefix_chars, 0, 1, 0, /*
1535 Move point backward over any number of chars with prefix syntax.
1536 This includes chars with "quote" or "prefix" syntax (' or p).
1537
1538 Optional arg BUFFER defaults to the current buffer.
1539 */
1540        (buffer))
1541 {
1542   struct buffer *buf = decode_buffer (buffer, 0);
1543   Bufpos beg = BUF_BEGV (buf);
1544   Bufpos pos = BUF_PT (buf);
1545 #ifndef emacs
1546   Lisp_Char_Table *mirrortab = XCHAR_TABLE (buf->mirror_syntax_table);
1547 #endif
1548   Emchar c = '\0'; /* initialize to avoid compiler warnings */
1549
1550
1551   SETUP_SYNTAX_CACHE_FOR_BUFFER (buf, pos, -1);
1552
1553   while (pos > beg && !char_quoted (buf, pos - 1)
1554          /* Previous statement updates syntax table.  */
1555          && (SYNTAX_FROM_CACHE (mirrortab, c = BUF_FETCH_CHAR (buf, pos - 1)) == Squote
1556              || SYNTAX_CODE_PREFIX (SYNTAX_CODE_FROM_CACHE (mirrortab, c))))
1557     pos--;
1558
1559   BUF_SET_PT (buf, pos);
1560
1561   return Qnil;
1562 }
1563 \f
1564 /* Parse forward from FROM to END,
1565    assuming that FROM has state OLDSTATE (nil means FROM is start of function),
1566    and return a description of the state of the parse at END.
1567    If STOPBEFORE is nonzero, stop at the start of an atom.
1568    If COMMENTSTOP is nonzero, stop at the start of a comment.  */
1569
1570 static void
1571 scan_sexps_forward (struct buffer *buf, struct lisp_parse_state *stateptr,
1572                     Bufpos from, Bufpos end,
1573                     int targetdepth, int stopbefore,
1574                     Lisp_Object oldstate,
1575                     int commentstop)
1576 {
1577   struct lisp_parse_state state;
1578
1579   enum syntaxcode code;
1580   struct level { int last, prev; };
1581   struct level levelstart[100];
1582   struct level *curlevel = levelstart;
1583   struct level *endlevel = levelstart + 100;
1584   int depth;    /* Paren depth of current scanning location.
1585                            level - levelstart equals this except
1586                            when the depth becomes negative.  */
1587   int mindepth;         /* Lowest DEPTH value seen.  */
1588   int start_quoted = 0;         /* Nonzero means starting after a char quote */
1589   int boundary_stop = commentstop == -1;
1590   Lisp_Object tem;
1591
1592   SETUP_SYNTAX_CACHE (from, 1);
1593   if (NILP (oldstate))
1594     {
1595       depth = 0;
1596       state.instring = -1;
1597       state.incomment = 0;
1598       state.comstyle = 0;       /* comment style a by default */
1599       state.comstr_start = -1;  /* no comment/string seen.  */
1600     }
1601   else
1602     {
1603       tem = Fcar (oldstate);    /* elt 0, depth */
1604       if (!NILP (tem))
1605         depth = XINT (tem);
1606       else
1607         depth = 0;
1608
1609       oldstate = Fcdr (oldstate);
1610       oldstate = Fcdr (oldstate);
1611       oldstate = Fcdr (oldstate);
1612       tem = Fcar (oldstate);    /* elt 3, instring */
1613       state.instring = ( !NILP (tem) 
1614                          ? ( INTP (tem) ? XINT (tem) : ST_STRING_STYLE) 
1615                          : -1);
1616
1617       oldstate = Fcdr (oldstate);
1618       tem = Fcar (oldstate);    /* elt 4, incomment */
1619       state.incomment = !NILP (tem);
1620
1621       oldstate = Fcdr (oldstate);
1622       tem = Fcar (oldstate);    /* elt 5, follows-quote */
1623       start_quoted = !NILP (tem);
1624
1625       /* if the eighth element of the list is nil, we are in comment style
1626          a; if it is t, we are in comment style b; if it is 'syntax-table,
1627          we are in a generic comment */
1628       oldstate = Fcdr (oldstate);
1629       oldstate = Fcdr (oldstate);
1630       tem = Fcar (oldstate);    /* elt 7, comment style a/b/fence */
1631       state.comstyle = NILP (tem) ? 0 : ( EQ (tem, Qsyntax_table)
1632                                           ? ST_COMMENT_STYLE : 1 );
1633
1634       oldstate = Fcdr (oldstate); /* elt 8, start of last comment/string */
1635       tem = Fcar (oldstate);
1636       state.comstr_start = NILP (tem) ? -1 : XINT (tem);
1637
1638       /* elt 9, char numbers of starts-of-expression of levels
1639          (starting from outermost). */
1640       oldstate = Fcdr (oldstate);
1641       tem = Fcar (oldstate);    /* elt 9, intermediate data for
1642                                    continuation of parsing (subject
1643                                    to change). */
1644       while (!NILP (tem))       /* >= second enclosing sexps.  */
1645         {
1646           curlevel->last = XINT (Fcar (tem));
1647           if (++curlevel == endlevel)
1648             error ("Nesting too deep for parser");
1649           curlevel->prev = -1;
1650           curlevel->last = -1;
1651           tem = Fcdr (tem);
1652         }
1653     }
1654   state.quoted = 0;
1655   mindepth = depth;
1656
1657   curlevel->prev = -1;
1658   curlevel->last = -1;
1659
1660   /* Enter the loop at a place appropriate for initial state. */
1661
1662   if (state.incomment) goto startincomment;
1663   if (state.instring >= 0)
1664     {
1665       if (start_quoted) goto startquotedinstring;
1666       goto startinstring;
1667     }
1668   if (start_quoted) goto startquoted;
1669
1670   while (from < end)
1671     {
1672       Emchar c;
1673       int syncode;
1674
1675       QUIT;
1676
1677       UPDATE_SYNTAX_CACHE_FORWARD (from);
1678       c = BUF_FETCH_CHAR (buf, from);
1679       code = SYNTAX_FROM_CACHE (mirrortab, c);
1680       syncode = SYNTAX_CODE_FROM_CACHE (mirrortab, c);
1681       from++;
1682
1683           /* record the comment style we have entered so that only the
1684              comment-ender sequence (or single char) of the same style
1685              actually terminates the comment section. */
1686       if (code == Scomment)
1687         {
1688           state.comstyle =
1689             SYNTAX_CODE_COMMENT_1CHAR_MASK (syncode)
1690             == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1691           state.comstr_start = from - 1;
1692         }
1693
1694       /* a generic comment delimiter? */
1695       else if (code == Scomment_fence)
1696         {
1697           state.comstyle = ST_COMMENT_STYLE;
1698           state.comstr_start = from - 1;
1699           code = Scomment;
1700         }
1701
1702       else if (from < end &&
1703                SYNTAX_CODE_START_FIRST_P (syncode))
1704         {
1705           int next_syncode;
1706           UPDATE_SYNTAX_CACHE_FORWARD (from);
1707           next_syncode =
1708             SYNTAX_CODE_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from));
1709
1710           if (SYNTAX_CODES_START_P (syncode, next_syncode))
1711         {
1712           code = Scomment;
1713               state.comstyle = SYNTAX_CODES_COMMENT_MASK_START
1714                 (syncode, next_syncode) == SYNTAX_COMMENT_STYLE_A ? 0 : 1;
1715               state.comstr_start = from - 1;
1716           from++;
1717               UPDATE_SYNTAX_CACHE_FORWARD (from);
1718             }
1719         }
1720
1721       if (SYNTAX_CODE_PREFIX (syncode))
1722         continue;
1723       switch (code)
1724         {
1725         case Sescape:
1726         case Scharquote:
1727           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1728           curlevel->last = from - 1;
1729         startquoted:
1730           if (from == end) goto endquoted;
1731           from++;
1732           goto symstarted;
1733           /* treat following character as a word constituent */
1734         case Sword:
1735         case Ssymbol:
1736           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1737           curlevel->last = from - 1;
1738         symstarted:
1739           while (from < end)
1740             {
1741               UPDATE_SYNTAX_CACHE_FORWARD (from);
1742               switch (SYNTAX_FROM_CACHE (mirrortab, BUF_FETCH_CHAR (buf, from)))
1743                 {
1744                 case Scharquote:
1745                 case Sescape:
1746                   from++;
1747                   if (from == end) goto endquoted;
1748                   break;
1749                 case Sword:
1750                 case Ssymbol:
1751                 case Squote:
1752                   break;
1753                 default:
1754                   goto symdone;
1755                 }
1756               from++;
1757             }
1758         symdone:
1759           curlevel->prev = curlevel->last;
1760           break;
1761
1762         case Scomment:
1763           state.incomment = 1;
1764           if (commentstop || boundary_stop) goto done;
1765         startincomment:
1766           if (commentstop == 1)
1767             goto done;
1768           UPDATE_SYNTAX_CACHE_FORWARD (from);
1769           {
1770             Bufpos newfrom = find_end_of_comment (buf, from, end, state.comstyle);
1771             if (newfrom < 0)
1772               {
1773                 /* we terminated search because from == end */
1774                 from = end;
1775                 goto done;
1776               }
1777             from = newfrom;
1778           }
1779           state.incomment = 0;
1780           state.comstyle = 0;                /* reset the comment style */
1781           if (boundary_stop) goto done;
1782           break;
1783
1784         case Sopen:
1785           if (stopbefore) goto stop;  /* this arg means stop at sexp start */
1786           depth++;
1787           /* curlevel++->last ran into compiler bug on Apollo */
1788           curlevel->last = from - 1;
1789           if (++curlevel == endlevel)
1790             error ("Nesting too deep for parser");
1791           curlevel->prev = -1;
1792           curlevel->last = -1;
1793           if (targetdepth == depth) goto done;
1794           break;
1795
1796         case Sclose:
1797           depth--;
1798           if (depth < mindepth)
1799             mindepth = depth;
1800           if (curlevel != levelstart)
1801             curlevel--;
1802           curlevel->prev = curlevel->last;
1803           if (targetdepth == depth) goto done;
1804           break;
1805
1806         case Sstring:
1807         case Sstring_fence:
1808           state.comstr_start = from - 1;
1809             if (stopbefore) goto stop; /* this arg means stop at sexp start */
1810             curlevel->last = from - 1;
1811           if (code == Sstring_fence)
1812             {
1813               state.instring = ST_STRING_STYLE;
1814             }
1815           else
1816             {
1817               /* XEmacs change: call syntax_match() on character */
1818               Emchar ch = BUF_FETCH_CHAR (buf, from - 1);
1819               Lisp_Object stermobj =
1820                 syntax_match (syntax_cache.current_syntax_table, ch);
1821
1822               if (CHARP (stermobj))
1823                 state.instring = XCHAR (stermobj);
1824               else
1825                 state.instring = ch;
1826           }
1827           if (boundary_stop) goto done;
1828         startinstring:
1829           while (1)
1830             {
1831               enum syntaxcode temp_code;
1832
1833               if (from >= end) goto done;
1834
1835               UPDATE_SYNTAX_CACHE_FORWARD (from);
1836               c = BUF_FETCH_CHAR (buf, from);
1837               temp_code = SYNTAX_FROM_CACHE (mirrortab, c);
1838
1839               if (
1840                   state.instring != ST_STRING_STYLE &&
1841                   temp_code == Sstring &&
1842                   c == state.instring) break;
1843
1844               switch (temp_code)
1845                 {
1846                 case Sstring_fence:
1847                   if (state.instring == ST_STRING_STYLE)
1848                     goto string_end;
1849                   break;
1850                 case Scharquote:
1851                 case Sescape:
1852                   {
1853                     from++;
1854                   startquotedinstring:
1855                     if (from >= end) goto endquoted;
1856                     break;
1857                   }
1858                 default:
1859                   break;
1860                 }
1861               from++;
1862             }
1863         string_end:
1864           state.instring = -1;
1865           curlevel->prev = curlevel->last;
1866           from++;
1867           if (boundary_stop) goto done;
1868           break;
1869
1870         case Smath:
1871           break;
1872
1873         case Swhitespace:
1874         case Spunct:
1875         case Squote:
1876         case Sendcomment:
1877         case Scomment_fence:
1878         case Sinherit:
1879         case Smax:
1880           break;
1881         }
1882     }
1883   goto done;
1884
1885  stop:   /* Here if stopping before start of sexp. */
1886   from--;    /* We have just fetched the char that starts it; */
1887   goto done; /* but return the position before it. */
1888
1889  endquoted:
1890   state.quoted = 1;
1891  done:
1892   state.depth = depth;
1893   state.mindepth = mindepth;
1894   state.thislevelstart = curlevel->prev;
1895   state.prevlevelstart
1896     = (curlevel == levelstart) ? -1 : (curlevel - 1)->last;
1897   state.location = from;
1898   state.levelstarts = Qnil;
1899   while (--curlevel >= levelstart)
1900     state.levelstarts = Fcons (make_int (curlevel->last),
1901                                state.levelstarts);
1902
1903   *stateptr = state;
1904 }
1905
1906 DEFUN ("parse-partial-sexp", Fparse_partial_sexp, 2, 7, 0, /*
1907 Parse Lisp syntax starting at FROM until TO; return status of parse at TO.
1908 Parsing stops at TO or when certain criteria are met;
1909  point is set to where parsing stops.
1910 If fifth arg OLDSTATE is omitted or nil,
1911  parsing assumes that FROM is the beginning of a function.
1912 Value is a list of nine elements describing final state of parsing:
1913  0. depth in parens.
1914  1. character address of start of innermost containing list; nil if none.
1915  2. character address of start of last complete sexp terminated.
1916  3. non-nil if inside a string.
1917     (It is the character that will terminate the string,
1918      or t if the string should be terminated by an explicit
1919      `syntax-table' property.)
1920  4. t if inside a comment.
1921  5. t if following a quote character.
1922  6. the minimum paren-depth encountered during this scan.
1923  7. nil if in comment style a, or not in a comment; t if in comment style b;
1924     `syntax-table' if given by an explicit `syntax-table' property.
1925  8. character address of start of last comment or string; nil if none.
1926  9. Intermediate data for continuation of parsing (subject to change).
1927 If third arg TARGETDEPTH is non-nil, parsing stops if the depth
1928 in parentheses becomes equal to TARGETDEPTH.
1929 Fourth arg STOPBEFORE non-nil means stop when come to
1930  any character that starts a sexp.
1931 Fifth arg OLDSTATE is a nine-element list like what this function returns.
1932 It is used to initialize the state of the parse.  Its second and third
1933 elements are ignored.
1934 Sixth arg COMMENTSTOP non-nil means stop at the start of a comment. If it
1935 is `syntax-table', stop after the start of a comment or a string, or after
1936 the end of a comment or string.
1937 */
1938        (from, to, targetdepth, stopbefore, oldstate, commentstop, buffer))
1939 {
1940   struct lisp_parse_state state;
1941   int target;
1942   Bufpos start, end;
1943   struct buffer *buf = decode_buffer (buffer, 0);
1944   Lisp_Object val;
1945
1946   if (!NILP (targetdepth))
1947     {
1948       CHECK_INT (targetdepth);
1949       target = XINT (targetdepth);
1950     }
1951   else
1952     target = -100000;           /* We won't reach this depth */
1953
1954   get_buffer_range_char (buf, from, to, &start, &end, 0);
1955   scan_sexps_forward (buf, &state, start, end,
1956                       target, !NILP (stopbefore), oldstate,
1957                       (NILP (commentstop)
1958                        ? 0 : (EQ (commentstop, Qsyntax_table) ? -1 : 1)));
1959   BUF_SET_PT (buf, state.location);
1960
1961   /* reverse order */
1962   val = Qnil;
1963   val = Fcons (state.levelstarts, val);
1964   val = Fcons ((state.incomment || (state.instring >= 0))
1965                ? make_int (state.comstr_start) : Qnil, val);
1966   val = Fcons (state.comstyle  ? (state.comstyle == ST_COMMENT_STYLE
1967                                   ? Qsyntax_table : Qt) : Qnil, val);
1968   val = Fcons (make_int (state.mindepth),   val);
1969   val = Fcons (state.quoted    ? Qt : Qnil, val);
1970   val = Fcons (state.incomment ? Qt : Qnil, val);
1971   val = Fcons (state.instring < 0
1972                ? Qnil
1973                : (state.instring == ST_STRING_STYLE
1974                   ? Qt : make_int (state.instring)), val);
1975   val = Fcons (state.thislevelstart < 0 ? Qnil : make_int (state.thislevelstart), val);
1976   val = Fcons (state.prevlevelstart < 0 ? Qnil : make_int (state.prevlevelstart), val);
1977   val = Fcons (make_int (state.depth), val);
1978
1979   return val;
1980 }
1981
1982
1983 /* Updating of the mirror syntax table.
1984
1985    Each syntax table has a corresponding mirror table in it.
1986    Whenever we make a change to a syntax table, we call
1987    update_syntax_table() on it.
1988
1989    #### We really only need to map over the changed range.
1990
1991    If we change the standard syntax table, we need to map over
1992    all tables because any of them could be inheriting from the
1993    standard syntax table.
1994
1995    When `set-syntax-table' is called, we set the buffer's mirror
1996    syntax table as well.
1997    */
1998
1999 struct cmst_arg
2000 {
2001   Lisp_Object mirrortab;
2002   int check_inherit;
2003 };
2004
2005 static int
2006 cmst_mapfun (struct chartab_range *range, Lisp_Object val, void *arg)
2007 {
2008   struct cmst_arg *closure = (struct cmst_arg *) arg;
2009
2010   if (CONSP (val))
2011     val = XCAR (val);
2012   if (SYNTAX_FROM_CODE (XINT (val)) == Sinherit
2013       && closure->check_inherit)
2014     {
2015       struct cmst_arg recursive;
2016
2017       recursive.mirrortab = closure->mirrortab;
2018       recursive.check_inherit = 0;
2019       map_char_table (XCHAR_TABLE (Vstandard_syntax_table), range,
2020                                    cmst_mapfun, &recursive);
2021     }
2022   else
2023     put_char_table (XCHAR_TABLE (closure->mirrortab), range, val);
2024   return 0;
2025 }
2026
2027 static void
2028 update_just_this_syntax_table (Lisp_Char_Table *ct)
2029 {
2030   struct chartab_range range;
2031   struct cmst_arg arg;
2032
2033   arg.mirrortab = ct->mirror_table;
2034   arg.check_inherit = (CHAR_TABLEP (Vstandard_syntax_table)
2035                        && ct != XCHAR_TABLE (Vstandard_syntax_table));
2036   range.type = CHARTAB_RANGE_ALL;
2037   map_char_table (ct, &range, cmst_mapfun, &arg);
2038 }
2039
2040 /* Called from chartab.c when a change is made to a syntax table.
2041    If this is the standard syntax table, we need to recompute
2042    *all* syntax tables (yuck).  Otherwise we just recompute this
2043    one. */
2044
2045 void
2046 update_syntax_table (Lisp_Char_Table *ct)
2047 {
2048   /* Don't be stymied at startup. */
2049   if (CHAR_TABLEP (Vstandard_syntax_table)
2050       && ct == XCHAR_TABLE (Vstandard_syntax_table))
2051     {
2052       Lisp_Object syntab;
2053
2054       for (syntab = Vall_syntax_tables; !NILP (syntab);
2055            syntab = XCHAR_TABLE (syntab)->next_table)
2056         update_just_this_syntax_table (XCHAR_TABLE (syntab));
2057     }
2058   else
2059     update_just_this_syntax_table (ct);
2060 }
2061
2062 \f
2063 /************************************************************************/
2064 /*                            initialization                            */
2065 /************************************************************************/
2066
2067 void
2068 syms_of_syntax (void)
2069 {
2070   defsymbol (&Qsyntax_table_p, "syntax-table-p");
2071   defsymbol (&Qsyntax_table, "syntax-table");
2072
2073   DEFSUBR (Fsyntax_table_p);
2074   DEFSUBR (Fsyntax_table);
2075   DEFSUBR (Fstandard_syntax_table);
2076   DEFSUBR (Fcopy_syntax_table);
2077   DEFSUBR (Fset_syntax_table);
2078   DEFSUBR (Fsyntax_designator_chars);
2079   DEFSUBR (Fchar_syntax);
2080   DEFSUBR (Fmatching_paren);
2081   /* DEFSUBR (Fmodify_syntax_entry); now in Lisp. */
2082   /* DEFSUBR (Fdescribe_syntax); now in Lisp. */
2083
2084   DEFSUBR (Fforward_word);
2085
2086   DEFSUBR (Fforward_comment);
2087   DEFSUBR (Fscan_lists);
2088   DEFSUBR (Fscan_sexps);
2089   DEFSUBR (Fbackward_prefix_chars);
2090   DEFSUBR (Fparse_partial_sexp);
2091 }
2092
2093 void
2094 vars_of_syntax (void)
2095 {
2096   DEFVAR_BOOL ("parse-sexp-ignore-comments", &parse_sexp_ignore_comments /*
2097 Non-nil means `forward-sexp', etc., should treat comments as whitespace.
2098 */ );
2099   parse_sexp_ignore_comments = 0;
2100
2101   DEFVAR_BOOL ("lookup-syntax-properties", &lookup_syntax_properties /*
2102 Non-nil means `forward-sexp', etc., grant `syntax-table' property.
2103 The value of this property should be either a syntax table, or a cons
2104 of the form (SYNTAXCODE . MATCHCHAR), SYNTAXCODE being the numeric
2105 syntax code, MATCHCHAR being nil or the character to match (which is
2106 relevant only for open/close type.
2107 */ );
2108   lookup_syntax_properties = 1;
2109
2110   DEFVAR_BOOL ("words-include-escapes", &words_include_escapes /*
2111 Non-nil means `forward-word', etc., should treat escape chars part of words.
2112 */ );
2113   words_include_escapes = 0;
2114
2115   no_quit_in_re_search = 0;
2116 }
2117
2118 static void
2119 define_standard_syntax (const char *p, enum syntaxcode syn)
2120 {
2121   for (; *p; p++)
2122     Fput_char_table (make_char (*p), make_int (syn), Vstandard_syntax_table);
2123 }
2124
2125 void
2126 complex_vars_of_syntax (void)
2127 {
2128   Emchar i;
2129   const char *p;
2130   /* Set this now, so first buffer creation can refer to it. */
2131   /* Make it nil before calling copy-syntax-table
2132      so that copy-syntax-table will know not to try to copy from garbage */
2133   Vstandard_syntax_table = Qnil;
2134   Vstandard_syntax_table = Fcopy_syntax_table (Qnil);
2135   staticpro (&Vstandard_syntax_table);
2136
2137   Vsyntax_designator_chars_string = make_string_nocopy (syntax_code_spec,
2138                                                         Smax);
2139   staticpro (&Vsyntax_designator_chars_string);
2140
2141   fill_char_table (XCHAR_TABLE (Vstandard_syntax_table), make_int (Spunct));
2142
2143   for (i = 0; i <= 32; i++)     /* Control 0 plus SPACE */
2144     Fput_char_table (make_char (i), make_int (Swhitespace),
2145                      Vstandard_syntax_table);
2146   for (i = 127; i <= 159; i++)  /* DEL plus Control 1 */
2147     Fput_char_table (make_char (i), make_int (Swhitespace),
2148                      Vstandard_syntax_table);
2149
2150   define_standard_syntax ("abcdefghijklmnopqrstuvwxyz"
2151                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2152                           "0123456789"
2153                           "$%", Sword);
2154   define_standard_syntax ("\"", Sstring);
2155   define_standard_syntax ("\\", Sescape);
2156   define_standard_syntax ("_-+*/&|<>=", Ssymbol);
2157   define_standard_syntax (".,;:?!#@~^'`", Spunct);
2158
2159   for (p = "()[]{}"; *p; p+=2)
2160     {
2161       Fput_char_table (make_char (p[0]),
2162                        Fcons (make_int (Sopen), make_char (p[1])),
2163                        Vstandard_syntax_table);
2164       Fput_char_table (make_char (p[1]),
2165                        Fcons (make_int (Sclose), make_char (p[0])),
2166                        Vstandard_syntax_table);
2167     }
2168 }