xref: /vim-8.2.3635/src/syntax.c (revision df1bdc92)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * syntax.c: code for syntax highlighting
12  */
13 
14 #include "vim.h"
15 
16 /*
17  * Structure that stores information about a highlight group.
18  * The ID of a highlight group is also called group ID.  It is the index in
19  * the highlight_ga array PLUS ONE.
20  */
21 struct hl_group
22 {
23     char_u	*sg_name;	/* highlight group name */
24     char_u	*sg_name_u;	/* uppercase of sg_name */
25 /* for normal terminals */
26     int		sg_term;	/* "term=" highlighting attributes */
27     char_u	*sg_start;	/* terminal string for start highl */
28     char_u	*sg_stop;	/* terminal string for stop highl */
29     int		sg_term_attr;	/* Screen attr for term mode */
30 /* for color terminals */
31     int		sg_cterm;	/* "cterm=" highlighting attr */
32     int		sg_cterm_bold;	/* bold attr was set for light color */
33     int		sg_cterm_fg;	/* terminal fg color number + 1 */
34     int		sg_cterm_bg;	/* terminal bg color number + 1 */
35     int		sg_cterm_attr;	/* Screen attr for color term mode */
36 #ifdef FEAT_GUI
37 /* for when using the GUI */
38     int		sg_gui;		/* "gui=" highlighting attributes */
39     guicolor_T	sg_gui_fg;	/* GUI foreground color handle */
40     char_u	*sg_gui_fg_name;/* GUI foreground color name */
41     guicolor_T	sg_gui_bg;	/* GUI background color handle */
42     char_u	*sg_gui_bg_name;/* GUI background color name */
43     guicolor_T	sg_gui_sp;	/* GUI special color handle */
44     char_u	*sg_gui_sp_name;/* GUI special color name */
45     GuiFont	sg_font;	/* GUI font handle */
46 #ifdef FEAT_XFONTSET
47     GuiFontset	sg_fontset;	/* GUI fontset handle */
48 #endif
49     char_u	*sg_font_name;  /* GUI font or fontset name */
50     int		sg_gui_attr;    /* Screen attr for GUI mode */
51 #endif
52     int		sg_link;	/* link to this highlight group ID */
53     int		sg_set;		/* combination of SG_* flags */
54 #ifdef FEAT_EVAL
55     scid_T	sg_scriptID;	/* script in which the group was last set */
56 #endif
57 };
58 
59 #define SG_TERM		1	/* term has been set */
60 #define SG_CTERM	2	/* cterm has been set */
61 #define SG_GUI		4	/* gui has been set */
62 #define SG_LINK		8	/* link has been set */
63 
64 static garray_T highlight_ga;	/* highlight groups for 'highlight' option */
65 
66 #define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
67 
68 #ifdef FEAT_CMDL_COMPL
69 static int include_default = FALSE;	/* include "default" for expansion */
70 static int include_link = FALSE;	/* include "link" for expansion */
71 #endif
72 
73 /*
74  * The "term", "cterm" and "gui" arguments can be any combination of the
75  * following names, separated by commas (but no spaces!).
76  */
77 static char *(hl_name_table[]) =
78     {"bold", "standout", "underline", "undercurl",
79 				      "italic", "reverse", "inverse", "NONE"};
80 static int hl_attr_table[] =
81     {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
82 
83 static int get_attr_entry  __ARGS((garray_T *table, attrentry_T *aep));
84 static void syn_unadd_group __ARGS((void));
85 static void set_hl_attr __ARGS((int idx));
86 static void highlight_list_one __ARGS((int id));
87 static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
88 static int syn_add_group __ARGS((char_u *name));
89 static int syn_list_header __ARGS((int did_header, int outlen, int id));
90 static int hl_has_settings __ARGS((int idx, int check_link));
91 static void highlight_clear __ARGS((int idx));
92 
93 #ifdef FEAT_GUI
94 static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
95 static int  set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
96 static guicolor_T color_name2handle __ARGS((char_u *name));
97 static GuiFont font_name2handle __ARGS((char_u *name));
98 # ifdef FEAT_XFONTSET
99 static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
100 # endif
101 static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
102 #endif
103 
104 /*
105  * An attribute number is the index in attr_table plus ATTR_OFF.
106  */
107 #define ATTR_OFF (HL_ALL + 1)
108 
109 #if defined(FEAT_SYN_HL) || defined(PROTO)
110 
111 #define SYN_NAMELEN	50		/* maximum length of a syntax name */
112 
113 /* different types of offsets that are possible */
114 #define SPO_MS_OFF	0	/* match  start offset */
115 #define SPO_ME_OFF	1	/* match  end	offset */
116 #define SPO_HS_OFF	2	/* highl. start offset */
117 #define SPO_HE_OFF	3	/* highl. end	offset */
118 #define SPO_RS_OFF	4	/* region start offset */
119 #define SPO_RE_OFF	5	/* region end	offset */
120 #define SPO_LC_OFF	6	/* leading context offset */
121 #define SPO_COUNT	7
122 
123 static char *(spo_name_tab[SPO_COUNT]) =
124 	    {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
125 
126 /*
127  * The patterns that are being searched for are stored in a syn_pattern.
128  * A match item consists of one pattern.
129  * A start/end item consists of n start patterns and m end patterns.
130  * A start/skip/end item consists of n start patterns, one skip pattern and m
131  * end patterns.
132  * For the latter two, the patterns are always consecutive: start-skip-end.
133  *
134  * A character offset can be given for the matched text (_m_start and _m_end)
135  * and for the actually highlighted text (_h_start and _h_end).
136  */
137 typedef struct syn_pattern
138 {
139     char	 sp_type;		/* see SPTYPE_ defines below */
140     char	 sp_syncing;		/* this item used for syncing */
141     short	 sp_flags;		/* see HL_ defines below */
142     struct sp_syn sp_syn;		/* struct passed to in_id_list() */
143     short	 sp_syn_match_id;	/* highlight group ID of pattern */
144     char_u	*sp_pattern;		/* regexp to match, pattern */
145     regprog_T	*sp_prog;		/* regexp to match, program */
146     int		 sp_ic;			/* ignore-case flag for sp_prog */
147     short	 sp_off_flags;		/* see below */
148     int		 sp_offsets[SPO_COUNT];	/* offsets */
149     short	*sp_cont_list;		/* cont. group IDs, if non-zero */
150     short	*sp_next_list;		/* next group IDs, if non-zero */
151     int		 sp_sync_idx;		/* sync item index (syncing only) */
152     int		 sp_line_id;		/* ID of last line where tried */
153     int		 sp_startcol;		/* next match in sp_line_id line */
154 } synpat_T;
155 
156 /* The sp_off_flags are computed like this:
157  * offset from the start of the matched text: (1 << SPO_XX_OFF)
158  * offset from the end	 of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
159  * When both are present, only one is used.
160  */
161 
162 #define SPTYPE_MATCH	1	/* match keyword with this group ID */
163 #define SPTYPE_START	2	/* match a regexp, start of item */
164 #define SPTYPE_END	3	/* match a regexp, end of item */
165 #define SPTYPE_SKIP	4	/* match a regexp, skip within item */
166 
167 #define HL_CONTAINED	0x01	/* not used on toplevel */
168 #define HL_TRANSP	0x02	/* has no highlighting	*/
169 #define HL_ONELINE	0x04	/* match within one line only */
170 #define HL_HAS_EOL	0x08	/* end pattern that matches with $ */
171 #define HL_SYNC_HERE	0x10	/* sync point after this item (syncing only) */
172 #define HL_SYNC_THERE	0x20	/* sync point at current line (syncing only) */
173 #define HL_MATCH	0x40	/* use match ID instead of item ID */
174 #define HL_SKIPNL	0x80	/* nextgroup can skip newlines */
175 #define HL_SKIPWHITE	0x100	/* nextgroup can skip white space */
176 #define HL_SKIPEMPTY	0x200	/* nextgroup can skip empty lines */
177 #define HL_KEEPEND	0x400	/* end match always kept */
178 #define HL_EXCLUDENL	0x800	/* exclude NL from match */
179 #define HL_DISPLAY	0x1000	/* only used for displaying, not syncing */
180 #define HL_FOLD		0x2000	/* define fold */
181 #define HL_EXTEND	0x4000	/* ignore a keepend */
182 /* These don't fit in a short, thus can't be used for syntax items, only for
183  * si_flags and bs_flags. */
184 #define HL_MATCHCONT	0x8000	/* match continued from previous line */
185 #define HL_TRANS_CONT	0x10000L /* transparent item without contains arg */
186 
187 #define SYN_ITEMS(buf)	((synpat_T *)((buf)->b_syn_patterns.ga_data))
188 
189 #define NONE_IDX	-2	/* value of sp_sync_idx for "NONE" */
190 
191 /*
192  * Flags for b_syn_sync_flags:
193  */
194 #define SF_CCOMMENT	0x01	/* sync on a C-style comment */
195 #define SF_MATCH	0x02	/* sync by matching a pattern */
196 
197 #define SYN_STATE_P(ssp)    ((bufstate_T *)((ssp)->ga_data))
198 
199 #define MAXKEYWLEN	80	    /* maximum length of a keyword */
200 
201 /*
202  * The attributes of the syntax item that has been recognized.
203  */
204 static int current_attr = 0;	    /* attr of current syntax word */
205 #ifdef FEAT_EVAL
206 static int current_id = 0;	    /* ID of current char for syn_get_id() */
207 static int current_trans_id = 0;    /* idem, transparancy removed */
208 #endif
209 
210 typedef struct syn_cluster_S
211 {
212     char_u	    *scl_name;	    /* syntax cluster name */
213     char_u	    *scl_name_u;    /* uppercase of scl_name */
214     short	    *scl_list;	    /* IDs in this syntax cluster */
215 } syn_cluster_T;
216 
217 /*
218  * Methods of combining two clusters
219  */
220 #define CLUSTER_REPLACE	    1	/* replace first list with second */
221 #define CLUSTER_ADD	    2	/* add second list to first */
222 #define CLUSTER_SUBTRACT    3	/* subtract second list from first */
223 
224 #define SYN_CLSTR(buf)	((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
225 
226 /*
227  * Syntax group IDs have different types:
228  *     0 -  9999  normal syntax groups
229  * 10000 - 14999  ALLBUT indicator (current_syn_inc_tag added)
230  * 15000 - 19999  TOP indicator (current_syn_inc_tag added)
231  * 20000 - 24999  CONTAINED indicator (current_syn_inc_tag added)
232  * >= 25000	  cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
233  */
234 #define SYNID_ALLBUT	10000	    /* syntax group ID for contains=ALLBUT */
235 #define SYNID_TOP	15000	    /* syntax group ID for contains=TOP */
236 #define SYNID_CONTAINED	20000	    /* syntax group ID for contains=CONTAINED */
237 #define SYNID_CLUSTER	25000	    /* first syntax group ID for clusters */
238 
239 /*
240  * Annoying Hack(TM):  ":syn include" needs this pointer to pass to
241  * expand_filename().  Most of the other syntax commands don't need it, so
242  * instead of passing it to them, we stow it here.
243  */
244 static char_u **syn_cmdlinep;
245 
246 /*
247  * Another Annoying Hack(TM):  To prevent rules from other ":syn include"'d
248  * files from from leaking into ALLBUT lists, we assign a unique ID to the
249  * rules in each ":syn include"'d file.
250  */
251 static int current_syn_inc_tag = 0;
252 static int running_syn_inc_tag = 0;
253 
254 /*
255  * In a hashtable item "hi_key" points to "keyword" in a keyentry.
256  * This avoids adding a pointer to the hashtable item.
257  * KE2HIKEY() converts a var pointer to a hashitem key pointer.
258  * HIKEY2KE() converts a hashitem key pointer to a var pointer.
259  * HI2KE() converts a hashitem pointer to a var pointer.
260  */
261 static keyentry_T dumkey;
262 #define KE2HIKEY(kp)  ((kp)->keyword)
263 #define HIKEY2KE(p)   ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
264 #define HI2KE(hi)      HIKEY2KE((hi)->hi_key)
265 
266 /*
267  * To reduce the time spent in keepend(), remember at which level in the state
268  * stack the first item with "keepend" is present.  When "-1", there is no
269  * "keepend" on the stack.
270  */
271 static int keepend_level = -1;
272 
273 /*
274  * For the current state we need to remember more than just the idx.
275  * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
276  * (The end positions have the column number of the next char)
277  */
278 typedef struct state_item
279 {
280     int		si_idx;			/* index of syntax pattern */
281     int		si_id;			/* highlight group ID for keywords */
282     int		si_trans_id;		/* idem, transparancy removed */
283     int		si_m_lnum;		/* lnum of the match */
284     int		si_m_startcol;		/* starting column of the match */
285     lpos_T	si_m_endpos;		/* just after end posn of the match */
286     lpos_T	si_h_startpos;		/* start position of the highlighting */
287     lpos_T	si_h_endpos;		/* end position of the highlighting */
288     lpos_T	si_eoe_pos;		/* end position of end pattern */
289     int		si_end_idx;		/* group ID for end pattern or zero */
290     int		si_ends;		/* if match ends before si_m_endpos */
291     int		si_attr;		/* attributes in this state */
292     long	si_flags;		/* HL_HAS_EOL flag in this state, and
293 					 * HL_SKIP* for si_next_list */
294     short	*si_cont_list;		/* list of contained groups */
295     short	*si_next_list;		/* nextgroup IDs after this item ends */
296     reg_extmatch_T *si_extmatch;	/* \z(...\) matches from start
297 					 * pattern */
298 } stateitem_T;
299 
300 #define KEYWORD_IDX	-1	    /* value of si_idx for keywords */
301 #define ID_LIST_ALL	(short *)-1 /* valid of si_cont_list for containing all
302 				       but contained groups */
303 
304 /*
305  * Struct to reduce the number of arguments to get_syn_options(), it's used
306  * very often.
307  */
308 typedef struct
309 {
310     int		flags;		/* flags for contained and transpartent */
311     int		keyword;	/* TRUE for ":syn keyword" */
312     int		*sync_idx;	/* syntax item for "grouphere" argument, NULL
313 				   if not allowed */
314     char	has_cont_list;	/* TRUE if "cont_list" can be used */
315     short	*cont_list;	/* group IDs for "contains" argument */
316     short	*cont_in_list;	/* group IDs for "containedin" argument */
317     short	*next_list;	/* group IDs for "nextgroup" argument */
318 } syn_opt_arg_T;
319 
320 /*
321  * The next possible match in the current line for any pattern is remembered,
322  * to avoid having to try for a match in each column.
323  * If next_match_idx == -1, not tried (in this line) yet.
324  * If next_match_col == MAXCOL, no match found in this line.
325  * (All end positions have the column of the char after the end)
326  */
327 static int next_match_col;		/* column for start of next match */
328 static lpos_T next_match_m_endpos;	/* position for end of next match */
329 static lpos_T next_match_h_startpos;  /* pos. for highl. start of next match */
330 static lpos_T next_match_h_endpos;	/* pos. for highl. end of next match */
331 static int next_match_idx;		/* index of matched item */
332 static long next_match_flags;		/* flags for next match */
333 static lpos_T next_match_eos_pos;	/* end of start pattn (start region) */
334 static lpos_T next_match_eoe_pos;	/* pos. for end of end pattern */
335 static int next_match_end_idx;		/* ID of group for end pattn or zero */
336 static reg_extmatch_T *next_match_extmatch = NULL;
337 
338 /*
339  * A state stack is an array of integers or stateitem_T, stored in a
340  * garray_T.  A state stack is invalid if it's itemsize entry is zero.
341  */
342 #define INVALID_STATE(ssp)  ((ssp)->ga_itemsize == 0)
343 #define VALID_STATE(ssp)    ((ssp)->ga_itemsize != 0)
344 
345 /*
346  * The current state (within the line) of the recognition engine.
347  * When current_state.ga_itemsize is 0 the current state is invalid.
348  */
349 static win_T	*syn_win;		/* current window for highlighting */
350 static buf_T	*syn_buf;		/* current buffer for highlighting */
351 static linenr_T current_lnum = 0;	/* lnum of current state */
352 static colnr_T	current_col = 0;	/* column of current state */
353 static int	current_state_stored = 0; /* TRUE if stored current state
354 					   * after setting current_finished */
355 static int	current_finished = 0;	/* current line has been finished */
356 static garray_T current_state		/* current stack of state_items */
357 		= {0, 0, 0, 0, NULL};
358 static short	*current_next_list = NULL; /* when non-zero, nextgroup list */
359 static int	current_next_flags = 0; /* flags for current_next_list */
360 static int	current_line_id = 0;	/* unique number for current line */
361 
362 #define CUR_STATE(idx)	((stateitem_T *)(current_state.ga_data))[idx]
363 
364 static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
365 static int syn_match_linecont __ARGS((linenr_T lnum));
366 static void syn_start_line __ARGS((void));
367 static void syn_update_ends __ARGS((int startofline));
368 static void syn_stack_alloc __ARGS((void));
369 static int syn_stack_cleanup __ARGS((void));
370 static void syn_stack_free_entry __ARGS((buf_T *buf, synstate_T *p));
371 static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
372 static synstate_T *store_current_state __ARGS((synstate_T *sp));
373 static void load_current_state __ARGS((synstate_T *from));
374 static void invalidate_current_state __ARGS((void));
375 static int syn_stack_equal __ARGS((synstate_T *sp));
376 static void validate_current_state __ARGS((void));
377 static int syn_finish_line __ARGS((int syncing));
378 static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell));
379 static int did_match_already __ARGS((int idx, garray_T *gap));
380 static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
381 static void check_state_ends __ARGS((void));
382 static void update_si_attr __ARGS((int idx));
383 static void check_keepend __ARGS((void));
384 static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
385 static short *copy_id_list __ARGS((short *list));
386 static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
387 static int push_current_state __ARGS((int idx));
388 static void pop_current_state __ARGS((void));
389 
390 static void find_endpos __ARGS((int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext));
391 static void clear_syn_state __ARGS((synstate_T *p));
392 static void clear_current_state __ARGS((void));
393 
394 static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
395 static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
396 static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
397 static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
398 static char_u *syn_getcurline __ARGS((void));
399 static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
400 static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si));
401 static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
402 static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
403 static void syntax_sync_clear __ARGS((void));
404 static void syn_remove_pattern __ARGS((buf_T *buf, int idx));
405 static void syn_clear_pattern __ARGS((buf_T *buf, int i));
406 static void syn_clear_cluster __ARGS((buf_T *buf, int i));
407 static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
408 static void syn_clear_one __ARGS((int id, int syncing));
409 static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
410 static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
411 static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
412 static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
413 static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
414 static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
415 static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
416 static void syn_lines_msg __ARGS((void));
417 static void syn_match_msg __ARGS((void));
418 static void syn_list_one __ARGS((int id, int syncing, int link_only));
419 static void syn_list_cluster __ARGS((int id));
420 static void put_id_list __ARGS((char_u *name, short *list, int attr));
421 static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
422 static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
423 static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
424 static void clear_keywtab __ARGS((hashtab_T *ht));
425 static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list));
426 static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
427 static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt));
428 static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
429 static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
430 static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
431 static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
432 #ifdef __BORLANDC__
433 static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
434 #else
435 static int syn_compare_stub __ARGS((const void *v1, const void *v2));
436 #endif
437 static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
438 static int syn_scl_name2id __ARGS((char_u *name));
439 static int syn_scl_namen2id __ARGS((char_u *linep, int len));
440 static int syn_check_cluster __ARGS((char_u *pp, int len));
441 static int syn_add_cluster __ARGS((char_u *name));
442 static void init_syn_patterns __ARGS((void));
443 static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
444 static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
445 static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
446 static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
447 static void syn_incl_toplevel __ARGS((int id, int *flagsp));
448 
449 /*
450  * Start the syntax recognition for a line.  This function is normally called
451  * from the screen updating, once for each displayed line.
452  * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
453  * it.	Careful: curbuf and curwin are likely to point to another buffer and
454  * window.
455  */
456     void
457 syntax_start(wp, lnum)
458     win_T	*wp;
459     linenr_T	lnum;
460 {
461     synstate_T	*p;
462     synstate_T	*last_valid = NULL;
463     synstate_T	*last_min_valid = NULL;
464     synstate_T	*sp, *prev;
465     linenr_T	parsed_lnum;
466     linenr_T	first_stored;
467     int		dist;
468     static int	changedtick = 0;	/* remember the last change ID */
469 
470     /*
471      * After switching buffers, invalidate current_state.
472      * Also do this when a change was made, the current state may be invalid
473      * then.
474      */
475     if (syn_buf != wp->w_buffer || changedtick != syn_buf->b_changedtick)
476     {
477 	invalidate_current_state();
478 	syn_buf = wp->w_buffer;
479     }
480     changedtick = syn_buf->b_changedtick;
481     syn_win = wp;
482 
483     /*
484      * Allocate syntax stack when needed.
485      */
486     syn_stack_alloc();
487     if (syn_buf->b_sst_array == NULL)
488 	return;		/* out of memory */
489     syn_buf->b_sst_lasttick = display_tick;
490 
491     /*
492      * If the state of the end of the previous line is useful, store it.
493      */
494     if (VALID_STATE(&current_state)
495 	    && current_lnum < lnum
496 	    && current_lnum < syn_buf->b_ml.ml_line_count)
497     {
498 	(void)syn_finish_line(FALSE);
499 	if (!current_state_stored)
500 	{
501 	    ++current_lnum;
502 	    (void)store_current_state(NULL);
503 	}
504 
505 	/*
506 	 * If the current_lnum is now the same as "lnum", keep the current
507 	 * state (this happens very often!).  Otherwise invalidate
508 	 * current_state and figure it out below.
509 	 */
510 	if (current_lnum != lnum)
511 	    invalidate_current_state();
512     }
513     else
514 	invalidate_current_state();
515 
516     /*
517      * Try to synchronize from a saved state in b_sst_array[].
518      * Only do this if lnum is not before and not to far beyond a saved state.
519      */
520     if (INVALID_STATE(&current_state) && syn_buf->b_sst_array != NULL)
521     {
522 	/* Find last valid saved state before start_lnum. */
523 	for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
524 	{
525 	    if (p->sst_lnum > lnum)
526 		break;
527 	    if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
528 	    {
529 		last_valid = p;
530 		if (p->sst_lnum >= lnum - syn_buf->b_syn_sync_minlines)
531 		    last_min_valid = p;
532 	    }
533 	}
534 	if (last_min_valid != NULL)
535 	    load_current_state(last_min_valid);
536     }
537 
538     /*
539      * If "lnum" is before or far beyond a line with a saved state, need to
540      * re-synchronize.
541      */
542     if (INVALID_STATE(&current_state))
543     {
544 	syn_sync(wp, lnum, last_valid);
545 	first_stored = current_lnum + syn_buf->b_syn_sync_minlines;
546     }
547     else
548 	first_stored = current_lnum;
549 
550     /*
551      * Advance from the sync point or saved state until the current line.
552      * Save some entries for syncing with later on.
553      */
554     dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
555     prev = syn_stack_find_entry(current_lnum);
556     while (current_lnum < lnum)
557     {
558 	syn_start_line();
559 	(void)syn_finish_line(FALSE);
560 	++current_lnum;
561 
562 	/* If we parsed at least "minlines" lines or started at a valid
563 	 * state, the current state is considered valid. */
564 	if (current_lnum >= first_stored)
565 	{
566 	    /* Check if the saved state entry is for the current line and is
567 	     * equal to the current state.  If so, then validate all saved
568 	     * states that depended on a change before the parsed line. */
569 	    if (prev == NULL)
570 		sp = syn_buf->b_sst_first;
571 	    else
572 		sp = prev->sst_next;
573 	    if (sp != NULL
574 		    && sp->sst_lnum == current_lnum
575 		    && syn_stack_equal(sp))
576 	    {
577 		parsed_lnum = current_lnum;
578 		prev = sp;
579 		while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
580 		{
581 		    if (sp->sst_lnum <= lnum)
582 			/* valid state before desired line, use this one */
583 			prev = sp;
584 		    else if (sp->sst_change_lnum == 0)
585 			/* past saved states depending on change, break here. */
586 			break;
587 		    sp->sst_change_lnum = 0;
588 		    sp = sp->sst_next;
589 		}
590 		load_current_state(prev);
591 	    }
592 	    /* Store the state at this line when it's the first one, the line
593 	     * where we start parsing, or some distance from the previously
594 	     * saved state.  But only when parsed at least 'minlines'. */
595 	    else if (prev == NULL
596 			|| current_lnum == lnum
597 			|| current_lnum >= prev->sst_lnum + dist)
598 		prev = store_current_state(prev);
599 	}
600 
601 	/* This can take a long time: break when CTRL-C pressed.  The current
602 	 * state will be wrong then. */
603 	line_breakcheck();
604 	if (got_int)
605 	{
606 	    current_lnum = lnum;
607 	    break;
608 	}
609     }
610 
611     syn_start_line();
612 }
613 
614 /*
615  * We cannot simply discard growarrays full of state_items or buf_states; we
616  * have to manually release their extmatch pointers first.
617  */
618     static void
619 clear_syn_state(p)
620     synstate_T *p;
621 {
622     int		i;
623     garray_T	*gap;
624 
625     if (p->sst_stacksize > SST_FIX_STATES)
626     {
627 	gap = &(p->sst_union.sst_ga);
628 	for (i = 0; i < gap->ga_len; i++)
629 	    unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
630 	ga_clear(gap);
631     }
632     else
633     {
634 	for (i = 0; i < p->sst_stacksize; i++)
635 	    unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
636     }
637 }
638 
639 /*
640  * Cleanup the current_state stack.
641  */
642     static void
643 clear_current_state()
644 {
645     int		i;
646     stateitem_T	*sip;
647 
648     sip = (stateitem_T *)(current_state.ga_data);
649     for (i = 0; i < current_state.ga_len; i++)
650 	unref_extmatch(sip[i].si_extmatch);
651     ga_clear(&current_state);
652 }
653 
654 /*
655  * Try to find a synchronisation point for line "lnum".
656  *
657  * This sets current_lnum and the current state.  One of three methods is
658  * used:
659  * 1. Search backwards for the end of a C-comment.
660  * 2. Search backwards for given sync patterns.
661  * 3. Simply start on a given number of lines above "lnum".
662  */
663     static void
664 syn_sync(wp, start_lnum, last_valid)
665     win_T	*wp;
666     linenr_T	start_lnum;
667     synstate_T	*last_valid;
668 {
669     buf_T	*curbuf_save;
670     win_T	*curwin_save;
671     pos_T	cursor_save;
672     int		idx;
673     linenr_T	lnum;
674     linenr_T	end_lnum;
675     linenr_T	break_lnum;
676     int		had_sync_point;
677     stateitem_T	*cur_si;
678     synpat_T	*spp;
679     char_u	*line;
680     int		found_flags = 0;
681     int		found_match_idx = 0;
682     linenr_T	found_current_lnum = 0;
683     int		found_current_col= 0;
684     lpos_T	found_m_endpos;
685     colnr_T	prev_current_col;
686 
687     /*
688      * Clear any current state that might be hanging around.
689      */
690     invalidate_current_state();
691 
692     /*
693      * Start at least "minlines" back.  Default starting point for parsing is
694      * there.
695      * Start further back, to avoid that scrolling backwards will result in
696      * resyncing for every line.  Now it resyncs only one out of N lines,
697      * where N is minlines * 1.5, or minlines * 2 if minlines is small.
698      * Watch out for overflow when minlines is MAXLNUM.
699      */
700     if (syn_buf->b_syn_sync_minlines > start_lnum)
701 	start_lnum = 1;
702     else
703     {
704 	if (syn_buf->b_syn_sync_minlines == 1)
705 	    lnum = 1;
706 	else if (syn_buf->b_syn_sync_minlines < 10)
707 	    lnum = syn_buf->b_syn_sync_minlines * 2;
708 	else
709 	    lnum = syn_buf->b_syn_sync_minlines * 3 / 2;
710 	if (syn_buf->b_syn_sync_maxlines != 0
711 				       && lnum > syn_buf->b_syn_sync_maxlines)
712 	    lnum = syn_buf->b_syn_sync_maxlines;
713 	if (lnum >= start_lnum)
714 	    start_lnum = 1;
715 	else
716 	    start_lnum -= lnum;
717     }
718     current_lnum = start_lnum;
719 
720     /*
721      * 1. Search backwards for the end of a C-style comment.
722      */
723     if (syn_buf->b_syn_sync_flags & SF_CCOMMENT)
724     {
725 	/* Need to make syn_buf the current buffer for a moment, to be able to
726 	 * use find_start_comment(). */
727 	curwin_save = curwin;
728 	curwin = wp;
729 	curbuf_save = curbuf;
730 	curbuf = syn_buf;
731 
732 	/*
733 	 * Skip lines that end in a backslash.
734 	 */
735 	for ( ; start_lnum > 1; --start_lnum)
736 	{
737 	    line = ml_get(start_lnum - 1);
738 	    if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
739 		break;
740 	}
741 	current_lnum = start_lnum;
742 
743 	/* set cursor to start of search */
744 	cursor_save = wp->w_cursor;
745 	wp->w_cursor.lnum = start_lnum;
746 	wp->w_cursor.col = 0;
747 
748 	/*
749 	 * If the line is inside a comment, need to find the syntax item that
750 	 * defines the comment.
751 	 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
752 	 */
753 	if (find_start_comment((int)syn_buf->b_syn_sync_maxlines) != NULL)
754 	{
755 	    for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
756 		if (SYN_ITEMS(syn_buf)[idx].sp_syn.id == syn_buf->b_syn_sync_id
757 			&& SYN_ITEMS(syn_buf)[idx].sp_type == SPTYPE_START)
758 		{
759 		    validate_current_state();
760 		    if (push_current_state(idx) == OK)
761 			update_si_attr(current_state.ga_len - 1);
762 		    break;
763 		}
764 	}
765 
766 	/* restore cursor and buffer */
767 	wp->w_cursor = cursor_save;
768 	curwin = curwin_save;
769 	curbuf = curbuf_save;
770     }
771 
772     /*
773      * 2. Search backwards for given sync patterns.
774      */
775     else if (syn_buf->b_syn_sync_flags & SF_MATCH)
776     {
777 	if (syn_buf->b_syn_sync_maxlines != 0
778 				 && start_lnum > syn_buf->b_syn_sync_maxlines)
779 	    break_lnum = start_lnum - syn_buf->b_syn_sync_maxlines;
780 	else
781 	    break_lnum = 0;
782 
783 	end_lnum = start_lnum;
784 	lnum = start_lnum;
785 	while (--lnum > break_lnum)
786 	{
787 	    /* This can take a long time: break when CTRL-C pressed. */
788 	    line_breakcheck();
789 	    if (got_int)
790 	    {
791 		invalidate_current_state();
792 		current_lnum = start_lnum;
793 		break;
794 	    }
795 
796 	    /* Check if we have run into a valid saved state stack now. */
797 	    if (last_valid != NULL && lnum == last_valid->sst_lnum)
798 	    {
799 		load_current_state(last_valid);
800 		break;
801 	    }
802 
803 	    /*
804 	     * Check if the previous line has the line-continuation pattern.
805 	     */
806 	    if (lnum > 1 && syn_match_linecont(lnum - 1))
807 		continue;
808 
809 	    /*
810 	     * Start with nothing on the state stack
811 	     */
812 	    validate_current_state();
813 
814 	    for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
815 	    {
816 		syn_start_line();
817 		for (;;)
818 		{
819 		    had_sync_point = syn_finish_line(TRUE);
820 		    /*
821 		     * When a sync point has been found, remember where, and
822 		     * continue to look for another one, further on in the line.
823 		     */
824 		    if (had_sync_point && current_state.ga_len)
825 		    {
826 			cur_si = &CUR_STATE(current_state.ga_len - 1);
827 			if (cur_si->si_m_endpos.lnum > start_lnum)
828 			{
829 			    /* ignore match that goes to after where started */
830 			    current_lnum = end_lnum;
831 			    break;
832 			}
833 			spp = &(SYN_ITEMS(syn_buf)[cur_si->si_idx]);
834 			found_flags = spp->sp_flags;
835 			found_match_idx = spp->sp_sync_idx;
836 			found_current_lnum = current_lnum;
837 			found_current_col = current_col;
838 			found_m_endpos = cur_si->si_m_endpos;
839 			/*
840 			 * Continue after the match (be aware of a zero-length
841 			 * match).
842 			 */
843 			if (found_m_endpos.lnum > current_lnum)
844 			{
845 			    current_lnum = found_m_endpos.lnum;
846 			    current_col = found_m_endpos.col;
847 			    if (current_lnum >= end_lnum)
848 				break;
849 			}
850 			else if (found_m_endpos.col > current_col)
851 			    current_col = found_m_endpos.col;
852 			else
853 			    ++current_col;
854 
855 			/* syn_current_attr() will have skipped the check for
856 			 * an item that ends here, need to do that now.  Be
857 			 * careful not to go past the NUL. */
858 			prev_current_col = current_col;
859 			if (syn_getcurline()[current_col] != NUL)
860 			    ++current_col;
861 			check_state_ends();
862 			current_col = prev_current_col;
863 		    }
864 		    else
865 			break;
866 		}
867 	    }
868 
869 	    /*
870 	     * If a sync point was encountered, break here.
871 	     */
872 	    if (found_flags)
873 	    {
874 		/*
875 		 * Put the item that was specified by the sync point on the
876 		 * state stack.  If there was no item specified, make the
877 		 * state stack empty.
878 		 */
879 		clear_current_state();
880 		if (found_match_idx >= 0
881 			&& push_current_state(found_match_idx) == OK)
882 		    update_si_attr(current_state.ga_len - 1);
883 
884 		/*
885 		 * When using "grouphere", continue from the sync point
886 		 * match, until the end of the line.  Parsing starts at
887 		 * the next line.
888 		 * For "groupthere" the parsing starts at start_lnum.
889 		 */
890 		if (found_flags & HL_SYNC_HERE)
891 		{
892 		    if (current_state.ga_len)
893 		    {
894 			cur_si = &CUR_STATE(current_state.ga_len - 1);
895 			cur_si->si_h_startpos.lnum = found_current_lnum;
896 			cur_si->si_h_startpos.col = found_current_col;
897 			update_si_end(cur_si, (int)current_col, TRUE);
898 			check_keepend();
899 		    }
900 		    current_col = found_m_endpos.col;
901 		    current_lnum = found_m_endpos.lnum;
902 		    (void)syn_finish_line(FALSE);
903 		    ++current_lnum;
904 		}
905 		else
906 		    current_lnum = start_lnum;
907 
908 		break;
909 	    }
910 
911 	    end_lnum = lnum;
912 	    invalidate_current_state();
913 	}
914 
915 	/* Ran into start of the file or exceeded maximum number of lines */
916 	if (lnum <= break_lnum)
917 	{
918 	    invalidate_current_state();
919 	    current_lnum = break_lnum + 1;
920 	}
921     }
922 
923     validate_current_state();
924 }
925 
926 /*
927  * Return TRUE if the line-continuation pattern matches in line "lnum".
928  */
929     static int
930 syn_match_linecont(lnum)
931     linenr_T	lnum;
932 {
933     regmmatch_T regmatch;
934 
935     if (syn_buf->b_syn_linecont_prog != NULL)
936     {
937 	regmatch.rmm_ic = syn_buf->b_syn_linecont_ic;
938 	regmatch.regprog = syn_buf->b_syn_linecont_prog;
939 	return syn_regexec(&regmatch, lnum, (colnr_T)0);
940     }
941     return FALSE;
942 }
943 
944 /*
945  * Prepare the current state for the start of a line.
946  */
947     static void
948 syn_start_line()
949 {
950     current_finished = FALSE;
951     current_col = 0;
952 
953     /*
954      * Need to update the end of a start/skip/end that continues from the
955      * previous line and regions that have "keepend".
956      */
957     if (current_state.ga_len > 0)
958 	syn_update_ends(TRUE);
959 
960     next_match_idx = -1;
961     ++current_line_id;
962 }
963 
964 /*
965  * Check for items in the stack that need their end updated.
966  * When "startofline" is TRUE the last item is always updated.
967  * When "startofline" is FALSE the item with "keepend" is forcefully updated.
968  */
969     static void
970 syn_update_ends(startofline)
971     int		startofline;
972 {
973     stateitem_T	*cur_si;
974     int		i;
975 
976     if (startofline)
977     {
978 	/* Check for a match carried over from a previous line with a
979 	 * contained region.  The match ends as soon as the region ends. */
980 	for (i = 0; i < current_state.ga_len; ++i)
981 	{
982 	    cur_si = &CUR_STATE(i);
983 	    if (cur_si->si_idx >= 0
984 		    && (SYN_ITEMS(syn_buf)[cur_si->si_idx]).sp_type
985 							       == SPTYPE_MATCH
986 		    && cur_si->si_m_endpos.lnum < current_lnum)
987 	    {
988 		cur_si->si_flags |= HL_MATCHCONT;
989 		cur_si->si_m_endpos.lnum = 0;
990 		cur_si->si_m_endpos.col = 0;
991 		cur_si->si_h_endpos = cur_si->si_m_endpos;
992 		cur_si->si_ends = TRUE;
993 	    }
994 	}
995     }
996 
997     /*
998      * Need to update the end of a start/skip/end that continues from the
999      * previous line.  And regions that have "keepend", because they may
1000      * influence contained items.
1001      * Then check for items ending in column 0.
1002      */
1003     i = current_state.ga_len - 1;
1004     if (keepend_level >= 0)
1005 	for ( ; i > keepend_level; --i)
1006 	    if (CUR_STATE(i).si_flags & HL_EXTEND)
1007 		break;
1008     for ( ; i < current_state.ga_len; ++i)
1009     {
1010 	cur_si = &CUR_STATE(i);
1011 	if ((cur_si->si_flags & HL_KEEPEND)
1012 			    || (i == current_state.ga_len - 1 && startofline))
1013 	{
1014 	    cur_si->si_h_startpos.col = 0;	/* start highl. in col 0 */
1015 	    cur_si->si_h_startpos.lnum = current_lnum;
1016 
1017 	    if (!(cur_si->si_flags & HL_MATCHCONT))
1018 		update_si_end(cur_si, (int)current_col, !startofline);
1019 	}
1020     }
1021     check_keepend();
1022     check_state_ends();
1023 }
1024 
1025 /****************************************
1026  * Handling of the state stack cache.
1027  */
1028 
1029 /*
1030  * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1031  *
1032  * To speed up syntax highlighting, the state stack for the start of some
1033  * lines is cached.  These entries can be used to start parsing at that point.
1034  *
1035  * The stack is kept in b_sst_array[] for each buffer.  There is a list of
1036  * valid entries.  b_sst_first points to the first one, then follow sst_next.
1037  * The entries are sorted on line number.  The first entry is often for line 2
1038  * (line 1 always starts with an empty stack).
1039  * There is also a list for free entries.  This construction is used to avoid
1040  * having to allocate and free memory blocks too often.
1041  *
1042  * When making changes to the buffer, this is logged in b_mod_*.  When calling
1043  * update_screen() to update the display, it will call
1044  * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1045  * entries.  The entries which are inside the changed area are removed,
1046  * because they must be recomputed.  Entries below the changed have their line
1047  * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1048  * set to indicate that a check must be made if the changed lines would change
1049  * the cached entry.
1050  *
1051  * When later displaying lines, an entry is stored for each line.  Displayed
1052  * lines are likely to be displayed again, in which case the state at the
1053  * start of the line is needed.
1054  * For not displayed lines, an entry is stored for every so many lines.  These
1055  * entries will be used e.g., when scrolling backwards.  The distance between
1056  * entries depends on the number of lines in the buffer.  For small buffers
1057  * the distance is fixed at SST_DIST, for large buffers there is a fixed
1058  * number of entries SST_MAX_ENTRIES, and the distance is computed.
1059  */
1060 
1061 /*
1062  * Free b_sst_array[] for buffer "buf".
1063  * Used when syntax items changed to force resyncing everywhere.
1064  */
1065     void
1066 syn_stack_free_all(buf)
1067     buf_T	*buf;
1068 {
1069     synstate_T	*p;
1070     win_T	*wp;
1071 
1072     if (buf->b_sst_array != NULL)
1073     {
1074 	for (p = buf->b_sst_first; p != NULL; p = p->sst_next)
1075 	    clear_syn_state(p);
1076 	vim_free(buf->b_sst_array);
1077 	buf->b_sst_array = NULL;
1078 	buf->b_sst_len = 0;
1079     }
1080 #ifdef FEAT_FOLDING
1081     /* When using "syntax" fold method, must update all folds. */
1082     FOR_ALL_WINDOWS(wp)
1083     {
1084 	if (wp->w_buffer == buf && foldmethodIsSyntax(wp))
1085 	    foldUpdateAll(wp);
1086     }
1087 #endif
1088 }
1089 
1090 /*
1091  * Allocate the syntax state stack for syn_buf when needed.
1092  * If the number of entries in b_sst_array[] is much too big or a bit too
1093  * small, reallocate it.
1094  * Also used to allocate b_sst_array[] for the first time.
1095  */
1096     static void
1097 syn_stack_alloc()
1098 {
1099     long	len;
1100     synstate_T	*to, *from;
1101     synstate_T	*sstp;
1102 
1103     len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1104     if (len < SST_MIN_ENTRIES)
1105 	len = SST_MIN_ENTRIES;
1106     else if (len > SST_MAX_ENTRIES)
1107 	len = SST_MAX_ENTRIES;
1108     if (syn_buf->b_sst_len > len * 2 || syn_buf->b_sst_len < len)
1109     {
1110 	/* Allocate 50% too much, to avoid reallocating too often. */
1111 	len = syn_buf->b_ml.ml_line_count;
1112 	len = (len + len / 2) / SST_DIST + Rows * 2;
1113 	if (len < SST_MIN_ENTRIES)
1114 	    len = SST_MIN_ENTRIES;
1115 	else if (len > SST_MAX_ENTRIES)
1116 	    len = SST_MAX_ENTRIES;
1117 
1118 	if (syn_buf->b_sst_array != NULL)
1119 	{
1120 	    /* When shrinking the array, cleanup the existing stack.
1121 	     * Make sure that all valid entries fit in the new array. */
1122 	    while (syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2 > len
1123 		    && syn_stack_cleanup())
1124 		;
1125 	    if (len < syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2)
1126 		len = syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2;
1127 	}
1128 
1129 	sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1130 	if (sstp == NULL)	/* out of memory! */
1131 	    return;
1132 
1133 	to = sstp - 1;
1134 	if (syn_buf->b_sst_array != NULL)
1135 	{
1136 	    /* Move the states from the old array to the new one. */
1137 	    for (from = syn_buf->b_sst_first; from != NULL;
1138 							from = from->sst_next)
1139 	    {
1140 		++to;
1141 		*to = *from;
1142 		to->sst_next = to + 1;
1143 	    }
1144 	}
1145 	if (to != sstp - 1)
1146 	{
1147 	    to->sst_next = NULL;
1148 	    syn_buf->b_sst_first = sstp;
1149 	    syn_buf->b_sst_freecount = len - (int)(to - sstp) - 1;
1150 	}
1151 	else
1152 	{
1153 	    syn_buf->b_sst_first = NULL;
1154 	    syn_buf->b_sst_freecount = len;
1155 	}
1156 
1157 	/* Create the list of free entries. */
1158 	syn_buf->b_sst_firstfree = to + 1;
1159 	while (++to < sstp + len)
1160 	    to->sst_next = to + 1;
1161 	(sstp + len - 1)->sst_next = NULL;
1162 
1163 	vim_free(syn_buf->b_sst_array);
1164 	syn_buf->b_sst_array = sstp;
1165 	syn_buf->b_sst_len = len;
1166     }
1167 }
1168 
1169 /*
1170  * Check for changes in a buffer to affect stored syntax states.  Uses the
1171  * b_mod_* fields.
1172  * Called from update_screen(), before screen is being updated, once for each
1173  * displayed buffer.
1174  */
1175     void
1176 syn_stack_apply_changes(buf)
1177     buf_T	*buf;
1178 {
1179     synstate_T	*p, *prev, *np;
1180     linenr_T	n;
1181 
1182     if (buf->b_sst_array == NULL)	/* nothing to do */
1183 	return;
1184 
1185     prev = NULL;
1186     for (p = buf->b_sst_first; p != NULL; )
1187     {
1188 	if (p->sst_lnum + buf->b_syn_sync_linebreaks > buf->b_mod_top)
1189 	{
1190 	    n = p->sst_lnum + buf->b_mod_xlines;
1191 	    if (n <= buf->b_mod_bot)
1192 	    {
1193 		/* this state is inside the changed area, remove it */
1194 		np = p->sst_next;
1195 		if (prev == NULL)
1196 		    buf->b_sst_first = np;
1197 		else
1198 		    prev->sst_next = np;
1199 		syn_stack_free_entry(buf, p);
1200 		p = np;
1201 		continue;
1202 	    }
1203 	    /* This state is below the changed area.  Remember the line
1204 	     * that needs to be parsed before this entry can be made valid
1205 	     * again. */
1206 	    if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1207 	    {
1208 		if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1209 		    p->sst_change_lnum += buf->b_mod_xlines;
1210 		else
1211 		    p->sst_change_lnum = buf->b_mod_top;
1212 	    }
1213 	    if (p->sst_change_lnum == 0
1214 		    || p->sst_change_lnum < buf->b_mod_bot)
1215 		p->sst_change_lnum = buf->b_mod_bot;
1216 
1217 	    p->sst_lnum = n;
1218 	}
1219 	prev = p;
1220 	p = p->sst_next;
1221     }
1222 }
1223 
1224 /*
1225  * Reduce the number of entries in the state stack for syn_buf.
1226  * Returns TRUE if at least one entry was freed.
1227  */
1228     static int
1229 syn_stack_cleanup()
1230 {
1231     synstate_T	*p, *prev;
1232     disptick_T	tick;
1233     int		above;
1234     int		dist;
1235     int		retval = FALSE;
1236 
1237     if (syn_buf->b_sst_array == NULL || syn_buf->b_sst_first == NULL)
1238 	return retval;
1239 
1240     /* Compute normal distance between non-displayed entries. */
1241     dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
1242 
1243     /*
1244      * Go throught the list to find the "tick" for the oldest entry that can
1245      * be removed.  Set "above" when the "tick" for the oldest entry is above
1246      * "b_sst_lasttick" (the display tick wraps around).
1247      */
1248     tick = syn_buf->b_sst_lasttick;
1249     above = FALSE;
1250     prev = syn_buf->b_sst_first;
1251     for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1252     {
1253 	if (prev->sst_lnum + dist > p->sst_lnum)
1254 	{
1255 	    if (p->sst_tick > syn_buf->b_sst_lasttick)
1256 	    {
1257 		if (!above || p->sst_tick < tick)
1258 		    tick = p->sst_tick;
1259 		above = TRUE;
1260 	    }
1261 	    else if (!above && p->sst_tick < tick)
1262 		tick = p->sst_tick;
1263 	}
1264     }
1265 
1266     /*
1267      * Go through the list to make the entries for the oldest tick at an
1268      * interval of several lines.
1269      */
1270     prev = syn_buf->b_sst_first;
1271     for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1272     {
1273 	if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1274 	{
1275 	    /* Move this entry from used list to free list */
1276 	    prev->sst_next = p->sst_next;
1277 	    syn_stack_free_entry(syn_buf, p);
1278 	    p = prev;
1279 	    retval = TRUE;
1280 	}
1281     }
1282     return retval;
1283 }
1284 
1285 /*
1286  * Free the allocated memory for a syn_state item.
1287  * Move the entry into the free list.
1288  */
1289     static void
1290 syn_stack_free_entry(buf, p)
1291     buf_T	*buf;
1292     synstate_T	*p;
1293 {
1294     clear_syn_state(p);
1295     p->sst_next = buf->b_sst_firstfree;
1296     buf->b_sst_firstfree = p;
1297     ++buf->b_sst_freecount;
1298 }
1299 
1300 /*
1301  * Find an entry in the list of state stacks at or before "lnum".
1302  * Returns NULL when there is no entry or the first entry is after "lnum".
1303  */
1304     static synstate_T *
1305 syn_stack_find_entry(lnum)
1306     linenr_T	lnum;
1307 {
1308     synstate_T	*p, *prev;
1309 
1310     prev = NULL;
1311     for (p = syn_buf->b_sst_first; p != NULL; prev = p, p = p->sst_next)
1312     {
1313 	if (p->sst_lnum == lnum)
1314 	    return p;
1315 	if (p->sst_lnum > lnum)
1316 	    break;
1317     }
1318     return prev;
1319 }
1320 
1321 /*
1322  * Try saving the current state in b_sst_array[].
1323  * The current state must be valid for the start of the current_lnum line!
1324  */
1325     static synstate_T *
1326 store_current_state(sp)
1327     synstate_T	*sp;	/* at or before where state is to be saved or
1328 				   NULL */
1329 {
1330     int		i;
1331     synstate_T	*p;
1332     bufstate_T	*bp;
1333     stateitem_T	*cur_si;
1334 
1335     if (sp == NULL)
1336 	sp = syn_stack_find_entry(current_lnum);
1337 
1338     /*
1339      * If the current state contains a start or end pattern that continues
1340      * from the previous line, we can't use it.  Don't store it then.
1341      */
1342     for (i = current_state.ga_len - 1; i >= 0; --i)
1343     {
1344 	cur_si = &CUR_STATE(i);
1345 	if (cur_si->si_h_startpos.lnum >= current_lnum
1346 		|| cur_si->si_m_endpos.lnum >= current_lnum
1347 		|| cur_si->si_h_endpos.lnum >= current_lnum
1348 		|| (cur_si->si_end_idx
1349 		    && cur_si->si_eoe_pos.lnum >= current_lnum))
1350 	    break;
1351     }
1352     if (i >= 0)
1353     {
1354 	if (sp != NULL)
1355 	{
1356 	    /* find "sp" in the list and remove it */
1357 	    if (syn_buf->b_sst_first == sp)
1358 		/* it's the first entry */
1359 		syn_buf->b_sst_first = sp->sst_next;
1360 	    else
1361 	    {
1362 		/* find the entry just before this one to adjust sst_next */
1363 		for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
1364 		    if (p->sst_next == sp)
1365 			break;
1366 		p->sst_next = sp->sst_next;
1367 	    }
1368 	    syn_stack_free_entry(syn_buf, sp);
1369 	    sp = NULL;
1370 	}
1371     }
1372     else if (sp == NULL || sp->sst_lnum != current_lnum)
1373     {
1374 	/*
1375 	 * Add a new entry
1376 	 */
1377 	/* If no free items, cleanup the array first. */
1378 	if (syn_buf->b_sst_freecount == 0)
1379 	{
1380 	    (void)syn_stack_cleanup();
1381 	    /* "sp" may have been moved to the freelist now */
1382 	    sp = syn_stack_find_entry(current_lnum);
1383 	}
1384 	/* Still no free items?  Must be a strange problem... */
1385 	if (syn_buf->b_sst_freecount == 0)
1386 	    sp = NULL;
1387 	else
1388 	{
1389 	    /* Take the first item from the free list and put it in the used
1390 	     * list, after *sp */
1391 	    p = syn_buf->b_sst_firstfree;
1392 	    syn_buf->b_sst_firstfree = p->sst_next;
1393 	    --syn_buf->b_sst_freecount;
1394 	    if (sp == NULL)
1395 	    {
1396 		/* Insert in front of the list */
1397 		p->sst_next = syn_buf->b_sst_first;
1398 		syn_buf->b_sst_first = p;
1399 	    }
1400 	    else
1401 	    {
1402 		/* insert in list after *sp */
1403 		p->sst_next = sp->sst_next;
1404 		sp->sst_next = p;
1405 	    }
1406 	    sp = p;
1407 	    sp->sst_stacksize = 0;
1408 	    sp->sst_lnum = current_lnum;
1409 	}
1410     }
1411     if (sp != NULL)
1412     {
1413 	/* When overwriting an existing state stack, clear it first */
1414 	clear_syn_state(sp);
1415 	sp->sst_stacksize = current_state.ga_len;
1416 	if (current_state.ga_len > SST_FIX_STATES)
1417 	{
1418 	    /* Need to clear it, might be something remaining from when the
1419 	     * length was less than SST_FIX_STATES. */
1420 	    ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1421 	    if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1422 		sp->sst_stacksize = 0;
1423 	    else
1424 		sp->sst_union.sst_ga.ga_len = current_state.ga_len;
1425 	    bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1426 	}
1427 	else
1428 	    bp = sp->sst_union.sst_stack;
1429 	for (i = 0; i < sp->sst_stacksize; ++i)
1430 	{
1431 	    bp[i].bs_idx = CUR_STATE(i).si_idx;
1432 	    bp[i].bs_flags = CUR_STATE(i).si_flags;
1433 	    bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1434 	}
1435 	sp->sst_next_flags = current_next_flags;
1436 	sp->sst_next_list = current_next_list;
1437 	sp->sst_tick = display_tick;
1438 	sp->sst_change_lnum = 0;
1439     }
1440     current_state_stored = TRUE;
1441     return sp;
1442 }
1443 
1444 /*
1445  * Copy a state stack from "from" in b_sst_array[] to current_state;
1446  */
1447     static void
1448 load_current_state(from)
1449     synstate_T	*from;
1450 {
1451     int		i;
1452     bufstate_T	*bp;
1453 
1454     clear_current_state();
1455     validate_current_state();
1456     keepend_level = -1;
1457     if (from->sst_stacksize
1458 	    && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1459     {
1460 	if (from->sst_stacksize > SST_FIX_STATES)
1461 	    bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1462 	else
1463 	    bp = from->sst_union.sst_stack;
1464 	for (i = 0; i < from->sst_stacksize; ++i)
1465 	{
1466 	    CUR_STATE(i).si_idx = bp[i].bs_idx;
1467 	    CUR_STATE(i).si_flags = bp[i].bs_flags;
1468 	    CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1469 	    if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1470 		keepend_level = i;
1471 	    CUR_STATE(i).si_ends = FALSE;
1472 	    CUR_STATE(i).si_m_lnum = 0;
1473 	    if (CUR_STATE(i).si_idx >= 0)
1474 		CUR_STATE(i).si_next_list =
1475 		       (SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_next_list;
1476 	    else
1477 		CUR_STATE(i).si_next_list = NULL;
1478 	    update_si_attr(i);
1479 	}
1480 	current_state.ga_len = from->sst_stacksize;
1481     }
1482     current_next_list = from->sst_next_list;
1483     current_next_flags = from->sst_next_flags;
1484     current_lnum = from->sst_lnum;
1485 }
1486 
1487 /*
1488  * Compare saved state stack "*sp" with the current state.
1489  * Return TRUE when they are equal.
1490  */
1491     static int
1492 syn_stack_equal(sp)
1493     synstate_T *sp;
1494 {
1495     int		i, j;
1496     bufstate_T	*bp;
1497     reg_extmatch_T	*six, *bsx;
1498 
1499     /* First a quick check if the stacks have the same size end nextlist. */
1500     if (sp->sst_stacksize == current_state.ga_len
1501 	    && sp->sst_next_list == current_next_list)
1502     {
1503 	/* Need to compare all states on both stacks. */
1504 	if (sp->sst_stacksize > SST_FIX_STATES)
1505 	    bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1506 	else
1507 	    bp = sp->sst_union.sst_stack;
1508 
1509 	for (i = current_state.ga_len; --i >= 0; )
1510 	{
1511 	    /* If the item has another index the state is different. */
1512 	    if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1513 		break;
1514 	    if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1515 	    {
1516 		/* When the extmatch pointers are different, the strings in
1517 		 * them can still be the same.  Check if the extmatch
1518 		 * references are equal. */
1519 		bsx = bp[i].bs_extmatch;
1520 		six = CUR_STATE(i).si_extmatch;
1521 		/* If one of the extmatch pointers is NULL the states are
1522 		 * different. */
1523 		if (bsx == NULL || six == NULL)
1524 		    break;
1525 		for (j = 0; j < NSUBEXP; ++j)
1526 		{
1527 		    /* Check each referenced match string. They must all be
1528 		     * equal. */
1529 		    if (bsx->matches[j] != six->matches[j])
1530 		    {
1531 			/* If the pointer is different it can still be the
1532 			 * same text.  Compare the strings, ignore case when
1533 			 * the start item has the sp_ic flag set. */
1534 			if (bsx->matches[j] == NULL
1535 				|| six->matches[j] == NULL)
1536 			    break;
1537 			if ((SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_ic
1538 				? MB_STRICMP(bsx->matches[j],
1539 							 six->matches[j]) != 0
1540 				: STRCMP(bsx->matches[j], six->matches[j]) != 0)
1541 			    break;
1542 		    }
1543 		}
1544 		if (j != NSUBEXP)
1545 		    break;
1546 	    }
1547 	}
1548 	if (i < 0)
1549 	    return TRUE;
1550     }
1551     return FALSE;
1552 }
1553 
1554 /*
1555  * We stop parsing syntax above line "lnum".  If the stored state at or below
1556  * this line depended on a change before it, it now depends on the line below
1557  * the last parsed line.
1558  * The window looks like this:
1559  *	    line which changed
1560  *	    displayed line
1561  *	    displayed line
1562  * lnum ->  line below window
1563  */
1564     void
1565 syntax_end_parsing(lnum)
1566     linenr_T	lnum;
1567 {
1568     synstate_T	*sp;
1569 
1570     sp = syn_stack_find_entry(lnum);
1571     if (sp != NULL && sp->sst_lnum < lnum)
1572 	sp = sp->sst_next;
1573 
1574     if (sp != NULL && sp->sst_change_lnum != 0)
1575 	sp->sst_change_lnum = lnum;
1576 }
1577 
1578 /*
1579  * End of handling of the state stack.
1580  ****************************************/
1581 
1582     static void
1583 invalidate_current_state()
1584 {
1585     clear_current_state();
1586     current_state.ga_itemsize = 0;	/* mark current_state invalid */
1587     current_next_list = NULL;
1588     keepend_level = -1;
1589 }
1590 
1591     static void
1592 validate_current_state()
1593 {
1594     current_state.ga_itemsize = sizeof(stateitem_T);
1595     current_state.ga_growsize = 3;
1596 }
1597 
1598 /*
1599  * Return TRUE if the syntax at start of lnum changed since last time.
1600  * This will only be called just after get_syntax_attr() for the previous
1601  * line, to check if the next line needs to be redrawn too.
1602  */
1603     int
1604 syntax_check_changed(lnum)
1605     linenr_T	lnum;
1606 {
1607     int		retval = TRUE;
1608     synstate_T	*sp;
1609 
1610     /*
1611      * Check the state stack when:
1612      * - lnum is just below the previously syntaxed line.
1613      * - lnum is not before the lines with saved states.
1614      * - lnum is not past the lines with saved states.
1615      * - lnum is at or before the last changed line.
1616      */
1617     if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1618     {
1619 	sp = syn_stack_find_entry(lnum);
1620 	if (sp != NULL && sp->sst_lnum == lnum)
1621 	{
1622 	    /*
1623 	     * finish the previous line (needed when not all of the line was
1624 	     * drawn)
1625 	     */
1626 	    (void)syn_finish_line(FALSE);
1627 
1628 	    /*
1629 	     * Compare the current state with the previously saved state of
1630 	     * the line.
1631 	     */
1632 	    if (syn_stack_equal(sp))
1633 		retval = FALSE;
1634 
1635 	    /*
1636 	     * Store the current state in b_sst_array[] for later use.
1637 	     */
1638 	    ++current_lnum;
1639 	    (void)store_current_state(NULL);
1640 	}
1641     }
1642 
1643     return retval;
1644 }
1645 
1646 /*
1647  * Finish the current line.
1648  * This doesn't return any attributes, it only gets the state at the end of
1649  * the line.  It can start anywhere in the line, as long as the current state
1650  * is valid.
1651  */
1652     static int
1653 syn_finish_line(syncing)
1654     int	    syncing;		/* called for syncing */
1655 {
1656     stateitem_T	*cur_si;
1657     colnr_T	prev_current_col;
1658 
1659     if (!current_finished)
1660     {
1661 	while (!current_finished)
1662 	{
1663 	    (void)syn_current_attr(syncing, FALSE, NULL);
1664 	    /*
1665 	     * When syncing, and found some item, need to check the item.
1666 	     */
1667 	    if (syncing && current_state.ga_len)
1668 	    {
1669 		/*
1670 		 * Check for match with sync item.
1671 		 */
1672 		cur_si = &CUR_STATE(current_state.ga_len - 1);
1673 		if (cur_si->si_idx >= 0
1674 			&& (SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags
1675 					      & (HL_SYNC_HERE|HL_SYNC_THERE)))
1676 		    return TRUE;
1677 
1678 		/* syn_current_attr() will have skipped the check for an item
1679 		 * that ends here, need to do that now.  Be careful not to go
1680 		 * past the NUL. */
1681 		prev_current_col = current_col;
1682 		if (syn_getcurline()[current_col] != NUL)
1683 		    ++current_col;
1684 		check_state_ends();
1685 		current_col = prev_current_col;
1686 	    }
1687 	    ++current_col;
1688 	}
1689     }
1690     return FALSE;
1691 }
1692 
1693 /*
1694  * Return highlight attributes for next character.
1695  * Must first call syntax_start() once for the line.
1696  * "col" is normally 0 for the first use in a line, and increments by one each
1697  * time.  It's allowed to skip characters and to stop before the end of the
1698  * line.  But only a "col" after a previously used column is allowed.
1699  * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1700  * done.
1701  */
1702     int
1703 get_syntax_attr(col, can_spell)
1704     colnr_T	col;
1705     int		*can_spell;
1706 {
1707     int	    attr = 0;
1708 
1709     /* check for out of memory situation */
1710     if (syn_buf->b_sst_array == NULL)
1711 	return 0;
1712 
1713     /* After 'synmaxcol' the attribute is always zero. */
1714     if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
1715     {
1716 	clear_current_state();
1717 #ifdef FEAT_EVAL
1718 	current_id = 0;
1719 	current_trans_id = 0;
1720 #endif
1721 	return 0;
1722     }
1723 
1724     /* Make sure current_state is valid */
1725     if (INVALID_STATE(&current_state))
1726 	validate_current_state();
1727 
1728     /*
1729      * Skip from the current column to "col", get the attributes for "col".
1730      */
1731     while (current_col <= col)
1732     {
1733 	attr = syn_current_attr(FALSE, TRUE, can_spell);
1734 	++current_col;
1735     }
1736 
1737     return attr;
1738 }
1739 
1740 /*
1741  * Get syntax attributes for current_lnum, current_col.
1742  */
1743     static int
1744 syn_current_attr(syncing, displaying, can_spell)
1745     int		syncing;		/* When 1: called for syncing */
1746     int		displaying;		/* result will be displayed */
1747     int		*can_spell;		/* return: do spell checking */
1748 {
1749     int		syn_id;
1750     lpos_T	endpos;		/* was: char_u *endp; */
1751     lpos_T	hl_startpos;	/* was: int hl_startcol; */
1752     lpos_T	hl_endpos;
1753     lpos_T	eos_pos;	/* end-of-start match (start region) */
1754     lpos_T	eoe_pos;	/* end-of-end pattern */
1755     int		end_idx;	/* group ID for end pattern */
1756     int		idx;
1757     synpat_T	*spp;
1758     stateitem_T	*cur_si, *sip = NULL;
1759     int		startcol;
1760     int		endcol;
1761     long	flags;
1762     short	*next_list;
1763     int		found_match;		    /* found usable match */
1764     static int	try_next_column = FALSE;    /* must try in next col */
1765     int		do_keywords;
1766     regmmatch_T	regmatch;
1767     lpos_T	pos;
1768     int		lc_col;
1769     reg_extmatch_T *cur_extmatch = NULL;
1770     char_u	*line;		/* current line.  NOTE: becomes invalid after
1771 				   looking for a pattern match! */
1772 
1773     /* variables for zero-width matches that have a "nextgroup" argument */
1774     int		keep_next_list;
1775     int		zero_width_next_list = FALSE;
1776     garray_T	zero_width_next_ga;
1777 
1778     /*
1779      * No character, no attributes!  Past end of line?
1780      * Do try matching with an empty line (could be the start of a region).
1781      */
1782     line = syn_getcurline();
1783     if (line[current_col] == NUL && current_col != 0)
1784     {
1785 	/*
1786 	 * If we found a match after the last column, use it.
1787 	 */
1788 	if (next_match_idx >= 0 && next_match_col >= (int)current_col
1789 						  && next_match_col != MAXCOL)
1790 	    (void)push_next_match(NULL);
1791 
1792 	current_finished = TRUE;
1793 	current_state_stored = FALSE;
1794 	return 0;
1795     }
1796 
1797     /* if the current or next character is NUL, we will finish the line now */
1798     if (line[current_col] == NUL || line[current_col + 1] == NUL)
1799     {
1800 	current_finished = TRUE;
1801 	current_state_stored = FALSE;
1802     }
1803 
1804     /*
1805      * When in the previous column there was a match but it could not be used
1806      * (empty match or already matched in this column) need to try again in
1807      * the next column.
1808      */
1809     if (try_next_column)
1810     {
1811 	next_match_idx = -1;
1812 	try_next_column = FALSE;
1813     }
1814 
1815     /* Only check for keywords when not syncing and there are some. */
1816     do_keywords = !syncing
1817 		    && (syn_buf->b_keywtab.ht_used > 0
1818 			    || syn_buf->b_keywtab_ic.ht_used > 0);
1819 
1820     /* Init the list of zero-width matches with a nextlist.  This is used to
1821      * avoid matching the same item in the same position twice. */
1822     ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1823 
1824     /*
1825      * Repeat matching keywords and patterns, to find contained items at the
1826      * same column.  This stops when there are no extra matches at the current
1827      * column.
1828      */
1829     do
1830     {
1831 	found_match = FALSE;
1832 	keep_next_list = FALSE;
1833 	syn_id = 0;
1834 
1835 	/*
1836 	 * 1. Check for a current state.
1837 	 *    Only when there is no current state, or if the current state may
1838 	 *    contain other things, we need to check for keywords and patterns.
1839 	 *    Always need to check for contained items if some item has the
1840 	 *    "containedin" argument (takes extra time!).
1841 	 */
1842 	if (current_state.ga_len)
1843 	    cur_si = &CUR_STATE(current_state.ga_len - 1);
1844 	else
1845 	    cur_si = NULL;
1846 
1847 	if (syn_buf->b_syn_containedin || cur_si == NULL
1848 					      || cur_si->si_cont_list != NULL)
1849 	{
1850 	    /*
1851 	     * 2. Check for keywords, if on a keyword char after a non-keyword
1852 	     *	  char.  Don't do this when syncing.
1853 	     */
1854 	    if (do_keywords)
1855 	    {
1856 	      line = syn_getcurline();
1857 	      if (vim_iswordc_buf(line + current_col, syn_buf)
1858 		      && (current_col == 0
1859 			  || !vim_iswordc_buf(line + current_col - 1
1860 #ifdef FEAT_MBYTE
1861 			      - (has_mbyte
1862 				  ? (*mb_head_off)(line, line + current_col - 1)
1863 				  : 0)
1864 #endif
1865 			       , syn_buf)))
1866 	      {
1867 		syn_id = check_keyword_id(line, (int)current_col,
1868 					 &endcol, &flags, &next_list, cur_si);
1869 		if (syn_id != 0)
1870 		{
1871 		    if (push_current_state(KEYWORD_IDX) == OK)
1872 		    {
1873 			cur_si = &CUR_STATE(current_state.ga_len - 1);
1874 			cur_si->si_m_startcol = current_col;
1875 			cur_si->si_h_startpos.lnum = current_lnum;
1876 			cur_si->si_h_startpos.col = 0;	/* starts right away */
1877 			cur_si->si_m_endpos.lnum = current_lnum;
1878 			cur_si->si_m_endpos.col = endcol;
1879 			cur_si->si_h_endpos.lnum = current_lnum;
1880 			cur_si->si_h_endpos.col = endcol;
1881 			cur_si->si_ends = TRUE;
1882 			cur_si->si_end_idx = 0;
1883 			cur_si->si_flags = flags;
1884 			cur_si->si_id = syn_id;
1885 			cur_si->si_trans_id = syn_id;
1886 			if (flags & HL_TRANSP)
1887 			{
1888 			    if (current_state.ga_len < 2)
1889 			    {
1890 				cur_si->si_attr = 0;
1891 				cur_si->si_trans_id = 0;
1892 			    }
1893 			    else
1894 			    {
1895 				cur_si->si_attr = CUR_STATE(
1896 					current_state.ga_len - 2).si_attr;
1897 				cur_si->si_trans_id = CUR_STATE(
1898 					current_state.ga_len - 2).si_trans_id;
1899 			    }
1900 			}
1901 			else
1902 			    cur_si->si_attr = syn_id2attr(syn_id);
1903 			cur_si->si_cont_list = NULL;
1904 			cur_si->si_next_list = next_list;
1905 			check_keepend();
1906 		    }
1907 		    else
1908 			vim_free(next_list);
1909 		}
1910 	      }
1911 	    }
1912 
1913 	    /*
1914 	     * 3. Check for patterns (only if no keyword found).
1915 	     */
1916 	    if (syn_id == 0 && syn_buf->b_syn_patterns.ga_len)
1917 	    {
1918 		/*
1919 		 * If we didn't check for a match yet, or we are past it, check
1920 		 * for any match with a pattern.
1921 		 */
1922 		if (next_match_idx < 0 || next_match_col < (int)current_col)
1923 		{
1924 		    /*
1925 		     * Check all relevant patterns for a match at this
1926 		     * position.  This is complicated, because matching with a
1927 		     * pattern takes quite a bit of time, thus we want to
1928 		     * avoid doing it when it's not needed.
1929 		     */
1930 		    next_match_idx = 0;		/* no match in this line yet */
1931 		    next_match_col = MAXCOL;
1932 		    for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
1933 		    {
1934 			spp = &(SYN_ITEMS(syn_buf)[idx]);
1935 			if (	   spp->sp_syncing == syncing
1936 				&& (displaying || !(spp->sp_flags & HL_DISPLAY))
1937 				&& (spp->sp_type == SPTYPE_MATCH
1938 				    || spp->sp_type == SPTYPE_START)
1939 				&& (current_next_list != NULL
1940 				    ? in_id_list(NULL, current_next_list,
1941 							      &spp->sp_syn, 0)
1942 				    : (cur_si == NULL
1943 					? !(spp->sp_flags & HL_CONTAINED)
1944 					: in_id_list(cur_si,
1945 					    cur_si->si_cont_list, &spp->sp_syn,
1946 					    spp->sp_flags & HL_CONTAINED))))
1947 			{
1948 			    /* If we already tried matching in this line, and
1949 			     * there isn't a match before next_match_col, skip
1950 			     * this item. */
1951 			    if (spp->sp_line_id == current_line_id
1952 				    && spp->sp_startcol >= next_match_col)
1953 				continue;
1954 			    spp->sp_line_id = current_line_id;
1955 
1956 			    lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1957 			    if (lc_col < 0)
1958 				lc_col = 0;
1959 
1960 			    regmatch.rmm_ic = spp->sp_ic;
1961 			    regmatch.regprog = spp->sp_prog;
1962 			    if (!syn_regexec(&regmatch, current_lnum,
1963 							     (colnr_T)lc_col))
1964 			    {
1965 				/* no match in this line, try another one */
1966 				spp->sp_startcol = MAXCOL;
1967 				continue;
1968 			    }
1969 
1970 			    /*
1971 			     * Compute the first column of the match.
1972 			     */
1973 			    syn_add_start_off(&pos, &regmatch,
1974 							 spp, SPO_MS_OFF, -1);
1975 			    if (pos.lnum > current_lnum)
1976 			    {
1977 				/* must have used end of match in a next line,
1978 				 * we can't handle that */
1979 				spp->sp_startcol = MAXCOL;
1980 				continue;
1981 			    }
1982 			    startcol = pos.col;
1983 
1984 			    /* remember the next column where this pattern
1985 			     * matches in the current line */
1986 			    spp->sp_startcol = startcol;
1987 
1988 			    /*
1989 			     * If a previously found match starts at a lower
1990 			     * column number, don't use this one.
1991 			     */
1992 			    if (startcol >= next_match_col)
1993 				continue;
1994 
1995 			    /*
1996 			     * If we matched this pattern at this position
1997 			     * before, skip it.  Must retry in the next
1998 			     * column, because it may match from there.
1999 			     */
2000 			    if (did_match_already(idx, &zero_width_next_ga))
2001 			    {
2002 				try_next_column = TRUE;
2003 				continue;
2004 			    }
2005 
2006 			    endpos.lnum = regmatch.endpos[0].lnum;
2007 			    endpos.col = regmatch.endpos[0].col;
2008 
2009 			    /* Compute the highlight start. */
2010 			    syn_add_start_off(&hl_startpos, &regmatch,
2011 							 spp, SPO_HS_OFF, -1);
2012 
2013 			    /* Compute the region start. */
2014 			    /* Default is to use the end of the match. */
2015 			    syn_add_end_off(&eos_pos, &regmatch,
2016 							 spp, SPO_RS_OFF, 0);
2017 
2018 			    /*
2019 			     * Grab the external submatches before they get
2020 			     * overwritten.  Reference count doesn't change.
2021 			     */
2022 			    unref_extmatch(cur_extmatch);
2023 			    cur_extmatch = re_extmatch_out;
2024 			    re_extmatch_out = NULL;
2025 
2026 			    flags = 0;
2027 			    eoe_pos.lnum = 0;	/* avoid warning */
2028 			    eoe_pos.col = 0;
2029 			    end_idx = 0;
2030 			    hl_endpos.lnum = 0;
2031 
2032 			    /*
2033 			     * For a "oneline" the end must be found in the
2034 			     * same line too.  Search for it after the end of
2035 			     * the match with the start pattern.  Set the
2036 			     * resulting end positions at the same time.
2037 			     */
2038 			    if (spp->sp_type == SPTYPE_START
2039 					      && (spp->sp_flags & HL_ONELINE))
2040 			    {
2041 				lpos_T	startpos;
2042 
2043 				startpos = endpos;
2044 				find_endpos(idx, &startpos, &endpos, &hl_endpos,
2045 				    &flags, &eoe_pos, &end_idx, cur_extmatch);
2046 				if (endpos.lnum == 0)
2047 				    continue;	    /* not found */
2048 			    }
2049 
2050 			    /*
2051 			     * For a "match" the size must be > 0 after the
2052 			     * end offset needs has been added.  Except when
2053 			     * syncing.
2054 			     */
2055 			    else if (spp->sp_type == SPTYPE_MATCH)
2056 			    {
2057 				syn_add_end_off(&hl_endpos, &regmatch, spp,
2058 							       SPO_HE_OFF, 0);
2059 				syn_add_end_off(&endpos, &regmatch, spp,
2060 							       SPO_ME_OFF, 0);
2061 				if (endpos.lnum == current_lnum
2062 				      && (int)endpos.col + syncing < startcol)
2063 				{
2064 				    /*
2065 				     * If an empty string is matched, may need
2066 				     * to try matching again at next column.
2067 				     */
2068 				    if (regmatch.startpos[0].col
2069 						    == regmatch.endpos[0].col)
2070 					try_next_column = TRUE;
2071 				    continue;
2072 				}
2073 			    }
2074 
2075 			    /*
2076 			     * keep the best match so far in next_match_*
2077 			     */
2078 			    /* Highlighting must start after startpos and end
2079 			     * before endpos. */
2080 			    if (hl_startpos.lnum == current_lnum
2081 					   && (int)hl_startpos.col < startcol)
2082 				hl_startpos.col = startcol;
2083 			    limit_pos_zero(&hl_endpos, &endpos);
2084 
2085 			    next_match_idx = idx;
2086 			    next_match_col = startcol;
2087 			    next_match_m_endpos = endpos;
2088 			    next_match_h_endpos = hl_endpos;
2089 			    next_match_h_startpos = hl_startpos;
2090 			    next_match_flags = flags;
2091 			    next_match_eos_pos = eos_pos;
2092 			    next_match_eoe_pos = eoe_pos;
2093 			    next_match_end_idx = end_idx;
2094 			    unref_extmatch(next_match_extmatch);
2095 			    next_match_extmatch = cur_extmatch;
2096 			    cur_extmatch = NULL;
2097 			}
2098 		    }
2099 		}
2100 
2101 		/*
2102 		 * If we found a match at the current column, use it.
2103 		 */
2104 		if (next_match_idx >= 0 && next_match_col == (int)current_col)
2105 		{
2106 		    synpat_T	*lspp;
2107 
2108 		    /* When a zero-width item matched which has a nextgroup,
2109 		     * don't push the item but set nextgroup. */
2110 		    lspp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2111 		    if (next_match_m_endpos.lnum == current_lnum
2112 			    && next_match_m_endpos.col == current_col
2113 			    && lspp->sp_next_list != NULL)
2114 		    {
2115 			current_next_list = lspp->sp_next_list;
2116 			current_next_flags = lspp->sp_flags;
2117 			keep_next_list = TRUE;
2118 			zero_width_next_list = TRUE;
2119 
2120 			/* Add the index to a list, so that we can check
2121 			 * later that we don't match it again (and cause an
2122 			 * endless loop). */
2123 			if (ga_grow(&zero_width_next_ga, 1) == OK)
2124 			{
2125 			    ((int *)(zero_width_next_ga.ga_data))
2126 				[zero_width_next_ga.ga_len++] = next_match_idx;
2127 			}
2128 			next_match_idx = -1;
2129 		    }
2130 		    else
2131 			cur_si = push_next_match(cur_si);
2132 		    found_match = TRUE;
2133 		}
2134 	    }
2135 	}
2136 
2137 	/*
2138 	 * Handle searching for nextgroup match.
2139 	 */
2140 	if (current_next_list != NULL && !keep_next_list)
2141 	{
2142 	    /*
2143 	     * If a nextgroup was not found, continue looking for one if:
2144 	     * - this is an empty line and the "skipempty" option was given
2145 	     * - we are on white space and the "skipwhite" option was given
2146 	     */
2147 	    if (!found_match)
2148 	    {
2149 		line = syn_getcurline();
2150 		if (((current_next_flags & HL_SKIPWHITE)
2151 			    && vim_iswhite(line[current_col]))
2152 			|| ((current_next_flags & HL_SKIPEMPTY)
2153 			    && *line == NUL))
2154 		    break;
2155 	    }
2156 
2157 	    /*
2158 	     * If a nextgroup was found: Use it, and continue looking for
2159 	     * contained matches.
2160 	     * If a nextgroup was not found: Continue looking for a normal
2161 	     * match.
2162 	     * When did set current_next_list for a zero-width item and no
2163 	     * match was found don't loop (would get stuck).
2164 	     */
2165 	    current_next_list = NULL;
2166 	    next_match_idx = -1;
2167 	    if (!zero_width_next_list)
2168 		found_match = TRUE;
2169 	}
2170 
2171     } while (found_match);
2172 
2173     /*
2174      * Use attributes from the current state, if within its highlighting.
2175      * If not, use attributes from the current-but-one state, etc.
2176      */
2177     current_attr = 0;
2178 #ifdef FEAT_EVAL
2179     current_id = 0;
2180     current_trans_id = 0;
2181 #endif
2182     if (cur_si != NULL)
2183     {
2184 #ifndef FEAT_EVAL
2185 	int	current_trans_id = 0;
2186 #endif
2187 	for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2188 	{
2189 	    sip = &CUR_STATE(idx);
2190 	    if ((current_lnum > sip->si_h_startpos.lnum
2191 			|| (current_lnum == sip->si_h_startpos.lnum
2192 			    && current_col >= sip->si_h_startpos.col))
2193 		    && (sip->si_h_endpos.lnum == 0
2194 			|| current_lnum < sip->si_h_endpos.lnum
2195 			|| (current_lnum == sip->si_h_endpos.lnum
2196 			    && current_col < sip->si_h_endpos.col)))
2197 	    {
2198 		current_attr = sip->si_attr;
2199 #ifdef FEAT_EVAL
2200 		current_id = sip->si_id;
2201 #endif
2202 		current_trans_id = sip->si_trans_id;
2203 		break;
2204 	    }
2205 	}
2206 
2207 	if (can_spell != NULL)
2208 	{
2209 	    struct sp_syn   sps;
2210 
2211 	    /*
2212 	     * set "can_spell" to TRUE if spell checking is supposed to be
2213 	     * done in the current item.
2214 	     */
2215 	    if (syn_buf->b_spell_cluster_id == 0)
2216 	    {
2217 		/* There is no @Spell cluster: Do spelling for items without
2218 		 * @NoSpell cluster. */
2219 		if (syn_buf->b_nospell_cluster_id == 0 || current_trans_id == 0)
2220 		    *can_spell = (syn_buf->b_syn_spell != SYNSPL_NOTOP);
2221 		else
2222 		{
2223 		    sps.inc_tag = 0;
2224 		    sps.id = syn_buf->b_nospell_cluster_id;
2225 		    sps.cont_in_list = NULL;
2226 		    *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2227 		}
2228 	    }
2229 	    else
2230 	    {
2231 		/* The @Spell cluster is defined: Do spelling in items with
2232 		 * the @Spell cluster.  But not when @NoSpell is also there.
2233 		 * At the toplevel only spell check when ":syn spell toplevel"
2234 		 * was used. */
2235 		if (current_trans_id == 0)
2236 		    *can_spell = (syn_buf->b_syn_spell == SYNSPL_TOP);
2237 		else
2238 		{
2239 		    sps.inc_tag = 0;
2240 		    sps.id = syn_buf->b_spell_cluster_id;
2241 		    sps.cont_in_list = NULL;
2242 		    *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2243 
2244 		    if (syn_buf->b_nospell_cluster_id != 0)
2245 		    {
2246 			sps.id = syn_buf->b_nospell_cluster_id;
2247 			if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2248 			    *can_spell = FALSE;
2249 		    }
2250 		}
2251 	    }
2252 	}
2253 
2254 
2255 	/*
2256 	 * Check for end of current state (and the states before it) at the
2257 	 * next column.  Don't do this for syncing, because we would miss a
2258 	 * single character match.
2259 	 * First check if the current state ends at the current column.  It
2260 	 * may be for an empty match and a containing item might end in the
2261 	 * current column.
2262 	 */
2263 	if (!syncing)
2264 	{
2265 	    check_state_ends();
2266 	    if (current_state.ga_len > 0
2267 				      && syn_getcurline()[current_col] != NUL)
2268 	    {
2269 		++current_col;
2270 		check_state_ends();
2271 		--current_col;
2272 	    }
2273 	}
2274     }
2275     else if (can_spell != NULL)
2276 	/* Default: Only do spelling when there is no @Spell cluster or when
2277 	 * ":syn spell toplevel" was used. */
2278 	*can_spell = syn_buf->b_syn_spell == SYNSPL_DEFAULT
2279 		    ? (syn_buf->b_spell_cluster_id == 0)
2280 		    : (syn_buf->b_syn_spell == SYNSPL_TOP);
2281 
2282     /* nextgroup ends at end of line, unless "skipnl" or "skipemtpy" present */
2283     if (current_next_list != NULL
2284 	    && syn_getcurline()[current_col + 1] == NUL
2285 	    && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2286 	current_next_list = NULL;
2287 
2288     if (zero_width_next_ga.ga_len > 0)
2289 	ga_clear(&zero_width_next_ga);
2290 
2291     /* No longer need external matches.  But keep next_match_extmatch. */
2292     unref_extmatch(re_extmatch_out);
2293     re_extmatch_out = NULL;
2294     unref_extmatch(cur_extmatch);
2295 
2296     return current_attr;
2297 }
2298 
2299 
2300 /*
2301  * Check if we already matched pattern "idx" at the current column.
2302  */
2303     static int
2304 did_match_already(idx, gap)
2305     int		idx;
2306     garray_T	*gap;
2307 {
2308     int		i;
2309 
2310     for (i = current_state.ga_len; --i >= 0; )
2311 	if (CUR_STATE(i).si_m_startcol == (int)current_col
2312 		&& CUR_STATE(i).si_m_lnum == (int)current_lnum
2313 		&& CUR_STATE(i).si_idx == idx)
2314 	    return TRUE;
2315 
2316     /* Zero-width matches with a nextgroup argument are not put on the syntax
2317      * stack, and can only be matched once anyway. */
2318     for (i = gap->ga_len; --i >= 0; )
2319 	if (((int *)(gap->ga_data))[i] == idx)
2320 	    return TRUE;
2321 
2322     return FALSE;
2323 }
2324 
2325 /*
2326  * Push the next match onto the stack.
2327  */
2328     static stateitem_T *
2329 push_next_match(cur_si)
2330     stateitem_T	*cur_si;
2331 {
2332     synpat_T	*spp;
2333 
2334     spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2335 
2336     /*
2337      * Push the item in current_state stack;
2338      */
2339     if (push_current_state(next_match_idx) == OK)
2340     {
2341 	/*
2342 	 * If it's a start-skip-end type that crosses lines, figure out how
2343 	 * much it continues in this line.  Otherwise just fill in the length.
2344 	 */
2345 	cur_si = &CUR_STATE(current_state.ga_len - 1);
2346 	cur_si->si_h_startpos = next_match_h_startpos;
2347 	cur_si->si_m_startcol = current_col;
2348 	cur_si->si_m_lnum = current_lnum;
2349 	cur_si->si_flags = spp->sp_flags;
2350 	cur_si->si_next_list = spp->sp_next_list;
2351 	cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2352 	if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2353 	{
2354 	    /* Try to find the end pattern in the current line */
2355 	    update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2356 	    check_keepend();
2357 	}
2358 	else
2359 	{
2360 	    cur_si->si_m_endpos = next_match_m_endpos;
2361 	    cur_si->si_h_endpos = next_match_h_endpos;
2362 	    cur_si->si_ends = TRUE;
2363 	    cur_si->si_flags |= next_match_flags;
2364 	    cur_si->si_eoe_pos = next_match_eoe_pos;
2365 	    cur_si->si_end_idx = next_match_end_idx;
2366 	}
2367 	if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2368 	    keepend_level = current_state.ga_len - 1;
2369 	check_keepend();
2370 	update_si_attr(current_state.ga_len - 1);
2371 
2372 	/*
2373 	 * If the start pattern has another highlight group, push another item
2374 	 * on the stack for the start pattern.
2375 	 */
2376 	if (	   spp->sp_type == SPTYPE_START
2377 		&& spp->sp_syn_match_id != 0
2378 		&& push_current_state(next_match_idx) == OK)
2379 	{
2380 	    cur_si = &CUR_STATE(current_state.ga_len - 1);
2381 	    cur_si->si_h_startpos = next_match_h_startpos;
2382 	    cur_si->si_m_startcol = current_col;
2383 	    cur_si->si_m_lnum = current_lnum;
2384 	    cur_si->si_m_endpos = next_match_eos_pos;
2385 	    cur_si->si_h_endpos = next_match_eos_pos;
2386 	    cur_si->si_ends = TRUE;
2387 	    cur_si->si_end_idx = 0;
2388 	    cur_si->si_flags = HL_MATCH;
2389 	    cur_si->si_next_list = NULL;
2390 	    check_keepend();
2391 	    update_si_attr(current_state.ga_len - 1);
2392 	}
2393     }
2394 
2395     next_match_idx = -1;	/* try other match next time */
2396 
2397     return cur_si;
2398 }
2399 
2400 /*
2401  * Check for end of current state (and the states before it).
2402  */
2403     static void
2404 check_state_ends()
2405 {
2406     stateitem_T	*cur_si;
2407     int		had_extend = FALSE;
2408 
2409     cur_si = &CUR_STATE(current_state.ga_len - 1);
2410     for (;;)
2411     {
2412 	if (cur_si->si_ends
2413 		&& (cur_si->si_m_endpos.lnum < current_lnum
2414 		    || (cur_si->si_m_endpos.lnum == current_lnum
2415 			&& cur_si->si_m_endpos.col <= current_col)))
2416 	{
2417 	    /*
2418 	     * If there is an end pattern group ID, highlight the end pattern
2419 	     * now.  No need to pop the current item from the stack.
2420 	     * Only do this if the end pattern continues beyond the current
2421 	     * position.
2422 	     */
2423 	    if (cur_si->si_end_idx
2424 		    && (cur_si->si_eoe_pos.lnum > current_lnum
2425 			|| (cur_si->si_eoe_pos.lnum == current_lnum
2426 			    && cur_si->si_eoe_pos.col > current_col)))
2427 	    {
2428 		cur_si->si_idx = cur_si->si_end_idx;
2429 		cur_si->si_end_idx = 0;
2430 		cur_si->si_m_endpos = cur_si->si_eoe_pos;
2431 		cur_si->si_h_endpos = cur_si->si_eoe_pos;
2432 		cur_si->si_flags |= HL_MATCH;
2433 		update_si_attr(current_state.ga_len - 1);
2434 
2435 		/* what matches next may be different now, clear it */
2436 		next_match_idx = 0;
2437 		next_match_col = MAXCOL;
2438 		break;
2439 	    }
2440 	    else
2441 	    {
2442 		/* handle next_list, unless at end of line and no "skipnl" or
2443 		 * "skipempty" */
2444 		current_next_list = cur_si->si_next_list;
2445 		current_next_flags = cur_si->si_flags;
2446 		if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2447 			&& syn_getcurline()[current_col] == NUL)
2448 		    current_next_list = NULL;
2449 
2450 		/* When the ended item has "extend", another item with
2451 		 * "keepend" now needs to check for its end. */
2452 		 if (cur_si->si_flags & HL_EXTEND)
2453 		     had_extend = TRUE;
2454 
2455 		pop_current_state();
2456 
2457 		if (current_state.ga_len == 0)
2458 		    break;
2459 
2460 		if (had_extend)
2461 		{
2462 		    syn_update_ends(FALSE);
2463 		    if (current_state.ga_len == 0)
2464 			break;
2465 		}
2466 
2467 		cur_si = &CUR_STATE(current_state.ga_len - 1);
2468 
2469 		/*
2470 		 * Only for a region the search for the end continues after
2471 		 * the end of the contained item.  If the contained match
2472 		 * included the end-of-line, break here, the region continues.
2473 		 * Don't do this when:
2474 		 * - "keepend" is used for the contained item
2475 		 * - not at the end of the line (could be end="x$"me=e-1).
2476 		 * - "excludenl" is used (HL_HAS_EOL won't be set)
2477 		 */
2478 		if (cur_si->si_idx >= 0
2479 			&& SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type
2480 							       == SPTYPE_START
2481 			&& !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2482 		{
2483 		    update_si_end(cur_si, (int)current_col, TRUE);
2484 		    check_keepend();
2485 		    if ((current_next_flags & HL_HAS_EOL)
2486 			    && keepend_level < 0
2487 			    && syn_getcurline()[current_col] == NUL)
2488 			break;
2489 		}
2490 	    }
2491 	}
2492 	else
2493 	    break;
2494     }
2495 }
2496 
2497 /*
2498  * Update an entry in the current_state stack for a match or region.  This
2499  * fills in si_attr, si_next_list and si_cont_list.
2500  */
2501     static void
2502 update_si_attr(idx)
2503     int	    idx;
2504 {
2505     stateitem_T	*sip = &CUR_STATE(idx);
2506     synpat_T	*spp;
2507 
2508     spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
2509     if (sip->si_flags & HL_MATCH)
2510 	sip->si_id = spp->sp_syn_match_id;
2511     else
2512 	sip->si_id = spp->sp_syn.id;
2513     sip->si_attr = syn_id2attr(sip->si_id);
2514     sip->si_trans_id = sip->si_id;
2515     if (sip->si_flags & HL_MATCH)
2516 	sip->si_cont_list = NULL;
2517     else
2518 	sip->si_cont_list = spp->sp_cont_list;
2519 
2520     /*
2521      * For transparent items, take attr from outer item.
2522      * Also take cont_list, if there is none.
2523      * Don't do this for the matchgroup of a start or end pattern.
2524      */
2525     if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2526     {
2527 	if (idx == 0)
2528 	{
2529 	    sip->si_attr = 0;
2530 	    sip->si_trans_id = 0;
2531 	    if (sip->si_cont_list == NULL)
2532 		sip->si_cont_list = ID_LIST_ALL;
2533 	}
2534 	else
2535 	{
2536 	    sip->si_attr = CUR_STATE(idx - 1).si_attr;
2537 	    sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
2538 	    sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2539 	    sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
2540 	    if (sip->si_cont_list == NULL)
2541 	    {
2542 		sip->si_flags |= HL_TRANS_CONT;
2543 		sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2544 	    }
2545 	}
2546     }
2547 }
2548 
2549 /*
2550  * Check the current stack for patterns with "keepend" flag.
2551  * Propagate the match-end to contained items, until a "skipend" item is found.
2552  */
2553     static void
2554 check_keepend()
2555 {
2556     int		i;
2557     lpos_T	maxpos;
2558     stateitem_T	*sip;
2559 
2560     /*
2561      * This check can consume a lot of time; only do it from the level where
2562      * there really is a keepend.
2563      */
2564     if (keepend_level < 0)
2565 	return;
2566 
2567     /*
2568      * Find the last index of an "extend" item.  "keepend" items before that
2569      * won't do anything.  If there is no "extend" item "i" will be
2570      * "keepend_level" and all "keepend" items will work normally.
2571      */
2572     for (i = current_state.ga_len - 1; i > keepend_level; --i)
2573 	if (CUR_STATE(i).si_flags & HL_EXTEND)
2574 	    break;
2575 
2576     maxpos.lnum = 0;
2577     for ( ; i < current_state.ga_len; ++i)
2578     {
2579 	sip = &CUR_STATE(i);
2580 	if (maxpos.lnum != 0)
2581 	{
2582 	    limit_pos_zero(&sip->si_m_endpos, &maxpos);
2583 	    limit_pos_zero(&sip->si_h_endpos, &maxpos);
2584 	    limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2585 	    sip->si_ends = TRUE;
2586 	}
2587 	if (sip->si_ends
2588 		&& (sip->si_flags & HL_KEEPEND)
2589 		&& (maxpos.lnum == 0
2590 		    || maxpos.lnum > sip->si_m_endpos.lnum
2591 		    || (maxpos.lnum == sip->si_m_endpos.lnum
2592 			&& maxpos.col > sip->si_m_endpos.col)))
2593 	    maxpos = sip->si_m_endpos;
2594     }
2595 }
2596 
2597 /*
2598  * Update an entry in the current_state stack for a start-skip-end pattern.
2599  * This finds the end of the current item, if it's in the current line.
2600  *
2601  * Return the flags for the matched END.
2602  */
2603     static void
2604 update_si_end(sip, startcol, force)
2605     stateitem_T	*sip;
2606     int		startcol;   /* where to start searching for the end */
2607     int		force;	    /* when TRUE overrule a previous end */
2608 {
2609     lpos_T	startpos;
2610     lpos_T	endpos;
2611     lpos_T	hl_endpos;
2612     lpos_T	end_endpos;
2613     int		end_idx;
2614 
2615     /* Don't update when it's already done.  Can be a match of an end pattern
2616      * that started in a previous line.  Watch out: can also be a "keepend"
2617      * from a containing item. */
2618     if (!force && sip->si_m_endpos.lnum >= current_lnum)
2619 	return;
2620 
2621     /*
2622      * We need to find the end of the region.  It may continue in the next
2623      * line.
2624      */
2625     end_idx = 0;
2626     startpos.lnum = current_lnum;
2627     startpos.col = startcol;
2628     find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2629 		   &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2630 
2631     if (endpos.lnum == 0)
2632     {
2633 	/* No end pattern matched. */
2634 	if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
2635 	{
2636 	    /* a "oneline" never continues in the next line */
2637 	    sip->si_ends = TRUE;
2638 	    sip->si_m_endpos.lnum = current_lnum;
2639 	    sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2640 	}
2641 	else
2642 	{
2643 	    /* continues in the next line */
2644 	    sip->si_ends = FALSE;
2645 	    sip->si_m_endpos.lnum = 0;
2646 	}
2647 	sip->si_h_endpos = sip->si_m_endpos;
2648     }
2649     else
2650     {
2651 	/* match within this line */
2652 	sip->si_m_endpos = endpos;
2653 	sip->si_h_endpos = hl_endpos;
2654 	sip->si_eoe_pos = end_endpos;
2655 	sip->si_ends = TRUE;
2656 	sip->si_end_idx = end_idx;
2657     }
2658 }
2659 
2660 /*
2661  * Add a new state to the current state stack.
2662  * It is cleared and the index set to "idx".
2663  * Return FAIL if it's not possible (out of memory).
2664  */
2665     static int
2666 push_current_state(idx)
2667     int	    idx;
2668 {
2669     if (ga_grow(&current_state, 1) == FAIL)
2670 	return FAIL;
2671     vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2672     CUR_STATE(current_state.ga_len).si_idx = idx;
2673     ++current_state.ga_len;
2674     return OK;
2675 }
2676 
2677 /*
2678  * Remove a state from the current_state stack.
2679  */
2680     static void
2681 pop_current_state()
2682 {
2683     if (current_state.ga_len)
2684     {
2685 	unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2686 	--current_state.ga_len;
2687     }
2688     /* after the end of a pattern, try matching a keyword or pattern */
2689     next_match_idx = -1;
2690 
2691     /* if first state with "keepend" is popped, reset keepend_level */
2692     if (keepend_level >= current_state.ga_len)
2693 	keepend_level = -1;
2694 }
2695 
2696 /*
2697  * Find the end of a start/skip/end syntax region after "startpos".
2698  * Only checks one line.
2699  * Also handles a match item that continued from a previous line.
2700  * If not found, the syntax item continues in the next line.  m_endpos->lnum
2701  * will be 0.
2702  * If found, the end of the region and the end of the highlighting is
2703  * computed.
2704  */
2705     static void
2706 find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2707 							   end_idx, start_ext)
2708     int		idx;		/* index of the pattern */
2709     lpos_T	*startpos;	/* where to start looking for an END match */
2710     lpos_T	*m_endpos;	/* return: end of match */
2711     lpos_T	*hl_endpos;	/* return: end of highlighting */
2712     long	*flagsp;	/* return: flags of matching END */
2713     lpos_T	*end_endpos;	/* return: end of end pattern match */
2714     int		*end_idx;	/* return: group ID for end pat. match, or 0 */
2715     reg_extmatch_T *start_ext;	/* submatches from the start pattern */
2716 {
2717     colnr_T	matchcol;
2718     synpat_T	*spp, *spp_skip;
2719     int		start_idx;
2720     int		best_idx;
2721     regmmatch_T	regmatch;
2722     regmmatch_T	best_regmatch;	    /* startpos/endpos of best match */
2723     lpos_T	pos;
2724     char_u	*line;
2725     int		had_match = FALSE;
2726 
2727     /*
2728      * Check for being called with a START pattern.
2729      * Can happen with a match that continues to the next line, because it
2730      * contained a region.
2731      */
2732     spp = &(SYN_ITEMS(syn_buf)[idx]);
2733     if (spp->sp_type != SPTYPE_START)
2734     {
2735 	*hl_endpos = *startpos;
2736 	return;
2737     }
2738 
2739     /*
2740      * Find the SKIP or first END pattern after the last START pattern.
2741      */
2742     for (;;)
2743     {
2744 	spp = &(SYN_ITEMS(syn_buf)[idx]);
2745 	if (spp->sp_type != SPTYPE_START)
2746 	    break;
2747 	++idx;
2748     }
2749 
2750     /*
2751      *	Lookup the SKIP pattern (if present)
2752      */
2753     if (spp->sp_type == SPTYPE_SKIP)
2754     {
2755 	spp_skip = spp;
2756 	++idx;
2757     }
2758     else
2759 	spp_skip = NULL;
2760 
2761     /* Setup external matches for syn_regexec(). */
2762     unref_extmatch(re_extmatch_in);
2763     re_extmatch_in = ref_extmatch(start_ext);
2764 
2765     matchcol = startpos->col;	/* start looking for a match at sstart */
2766     start_idx = idx;		/* remember the first END pattern. */
2767     best_regmatch.startpos[0].col = 0;		/* avoid compiler warning */
2768     for (;;)
2769     {
2770 	/*
2771 	 * Find end pattern that matches first after "matchcol".
2772 	 */
2773 	best_idx = -1;
2774 	for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
2775 	{
2776 	    int lc_col = matchcol;
2777 
2778 	    spp = &(SYN_ITEMS(syn_buf)[idx]);
2779 	    if (spp->sp_type != SPTYPE_END)	/* past last END pattern */
2780 		break;
2781 	    lc_col -= spp->sp_offsets[SPO_LC_OFF];
2782 	    if (lc_col < 0)
2783 		lc_col = 0;
2784 
2785 	    regmatch.rmm_ic = spp->sp_ic;
2786 	    regmatch.regprog = spp->sp_prog;
2787 	    if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2788 	    {
2789 		if (best_idx == -1 || regmatch.startpos[0].col
2790 					      < best_regmatch.startpos[0].col)
2791 		{
2792 		    best_idx = idx;
2793 		    best_regmatch.startpos[0] = regmatch.startpos[0];
2794 		    best_regmatch.endpos[0] = regmatch.endpos[0];
2795 		}
2796 	    }
2797 	}
2798 
2799 	/*
2800 	 * If all end patterns have been tried, and there is no match, the
2801 	 * item continues until end-of-line.
2802 	 */
2803 	if (best_idx == -1)
2804 	    break;
2805 
2806 	/*
2807 	 * If the skip pattern matches before the end pattern,
2808 	 * continue searching after the skip pattern.
2809 	 */
2810 	if (spp_skip != NULL)
2811 	{
2812 	    int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2813 
2814 	    if (lc_col < 0)
2815 		lc_col = 0;
2816 	    regmatch.rmm_ic = spp_skip->sp_ic;
2817 	    regmatch.regprog = spp_skip->sp_prog;
2818 	    if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2819 		    && regmatch.startpos[0].col
2820 					     <= best_regmatch.startpos[0].col)
2821 	    {
2822 		/* Add offset to skip pattern match */
2823 		syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2824 
2825 		/* If the skip pattern goes on to the next line, there is no
2826 		 * match with an end pattern in this line. */
2827 		if (pos.lnum > startpos->lnum)
2828 		    break;
2829 
2830 		line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2831 
2832 		/* take care of an empty match or negative offset */
2833 		if (pos.col <= matchcol)
2834 		    ++matchcol;
2835 		else if (pos.col <= regmatch.endpos[0].col)
2836 		    matchcol = pos.col;
2837 		else
2838 		    /* Be careful not to jump over the NUL at the end-of-line */
2839 		    for (matchcol = regmatch.endpos[0].col;
2840 			    line[matchcol] != NUL && matchcol < pos.col;
2841 								   ++matchcol)
2842 			;
2843 
2844 		/* if the skip pattern includes end-of-line, break here */
2845 		if (line[matchcol] == NUL)
2846 		    break;
2847 
2848 		continue;	    /* start with first end pattern again */
2849 	    }
2850 	}
2851 
2852 	/*
2853 	 * Match from start pattern to end pattern.
2854 	 * Correct for match and highlight offset of end pattern.
2855 	 */
2856 	spp = &(SYN_ITEMS(syn_buf)[best_idx]);
2857 	syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2858 	/* can't end before the start */
2859 	if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2860 	    m_endpos->col = startpos->col;
2861 
2862 	syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2863 	/* can't end before the start */
2864 	if (end_endpos->lnum == startpos->lnum
2865 					   && end_endpos->col < startpos->col)
2866 	    end_endpos->col = startpos->col;
2867 	/* can't end after the match */
2868 	limit_pos(end_endpos, m_endpos);
2869 
2870 	/*
2871 	 * If the end group is highlighted differently, adjust the pointers.
2872 	 */
2873 	if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2874 	{
2875 	    *end_idx = best_idx;
2876 	    if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2877 	    {
2878 		hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2879 		hl_endpos->col = best_regmatch.endpos[0].col;
2880 	    }
2881 	    else
2882 	    {
2883 		hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2884 		hl_endpos->col = best_regmatch.startpos[0].col;
2885 	    }
2886 	    hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2887 
2888 	    /* can't end before the start */
2889 	    if (hl_endpos->lnum == startpos->lnum
2890 					    && hl_endpos->col < startpos->col)
2891 		hl_endpos->col = startpos->col;
2892 	    limit_pos(hl_endpos, m_endpos);
2893 
2894 	    /* now the match ends where the highlighting ends, it is turned
2895 	     * into the matchgroup for the end */
2896 	    *m_endpos = *hl_endpos;
2897 	}
2898 	else
2899 	{
2900 	    *end_idx = 0;
2901 	    *hl_endpos = *end_endpos;
2902 	}
2903 
2904 	*flagsp = spp->sp_flags;
2905 
2906 	had_match = TRUE;
2907 	break;
2908     }
2909 
2910     /* no match for an END pattern in this line */
2911     if (!had_match)
2912 	m_endpos->lnum = 0;
2913 
2914     /* Remove external matches. */
2915     unref_extmatch(re_extmatch_in);
2916     re_extmatch_in = NULL;
2917 }
2918 
2919 /*
2920  * Limit "pos" not to be after "limit".
2921  */
2922     static void
2923 limit_pos(pos, limit)
2924     lpos_T	*pos;
2925     lpos_T	*limit;
2926 {
2927     if (pos->lnum > limit->lnum)
2928 	*pos = *limit;
2929     else if (pos->lnum == limit->lnum && pos->col > limit->col)
2930 	pos->col = limit->col;
2931 }
2932 
2933 /*
2934  * Limit "pos" not to be after "limit", unless pos->lnum is zero.
2935  */
2936     static void
2937 limit_pos_zero(pos, limit)
2938     lpos_T	*pos;
2939     lpos_T	*limit;
2940 {
2941     if (pos->lnum == 0)
2942 	*pos = *limit;
2943     else
2944 	limit_pos(pos, limit);
2945 }
2946 
2947 /*
2948  * Add offset to matched text for end of match or highlight.
2949  */
2950     static void
2951 syn_add_end_off(result, regmatch, spp, idx, extra)
2952     lpos_T	*result;	/* returned position */
2953     regmmatch_T	*regmatch;	/* start/end of match */
2954     synpat_T	*spp;		/* matched pattern */
2955     int		idx;		/* index of offset */
2956     int		extra;		/* extra chars for offset to start */
2957 {
2958     int		col;
2959     int		len;
2960 
2961     if (spp->sp_off_flags & (1 << idx))
2962     {
2963 	result->lnum = regmatch->startpos[0].lnum;
2964 	col = regmatch->startpos[0].col + extra;
2965     }
2966     else
2967     {
2968 	result->lnum = regmatch->endpos[0].lnum;
2969 	col = regmatch->endpos[0].col;
2970     }
2971     col += spp->sp_offsets[idx];
2972     if (col < 0)
2973 	result->col = 0;
2974     else
2975     {
2976 	/* Don't go past the end of the line.  Matters for "rs=e+2" when there
2977 	 * is a matchgroup. Watch out for match with last NL in the buffer. */
2978 	if (result->lnum > syn_buf->b_ml.ml_line_count)
2979 	    len = 0;
2980 	else
2981 	    len = STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
2982 	if (col > len)
2983 	    result->col = len;
2984 	else
2985 	    result->col = col;
2986     }
2987 }
2988 
2989 /*
2990  * Add offset to matched text for start of match or highlight.
2991  * Avoid resulting column to become negative.
2992  */
2993     static void
2994 syn_add_start_off(result, regmatch, spp, idx, extra)
2995     lpos_T	*result;	/* returned position */
2996     regmmatch_T	*regmatch;	/* start/end of match */
2997     synpat_T	*spp;
2998     int		idx;
2999     int		extra;	    /* extra chars for offset to end */
3000 {
3001     int		col;
3002 
3003     if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3004     {
3005 	result->lnum = regmatch->endpos[0].lnum;
3006 	col = regmatch->endpos[0].col + extra;
3007     }
3008     else
3009     {
3010 	result->lnum = regmatch->startpos[0].lnum;
3011 	col = regmatch->startpos[0].col;
3012     }
3013     col += spp->sp_offsets[idx];
3014     if (col < 0)
3015 	result->col = 0;
3016     else
3017 	result->col = col;
3018 }
3019 
3020 /*
3021  * Get current line in syntax buffer.
3022  */
3023     static char_u *
3024 syn_getcurline()
3025 {
3026     return ml_get_buf(syn_buf, current_lnum, FALSE);
3027 }
3028 
3029 /*
3030  * Call vim_regexec() to find a match with "rmp" in "syn_buf".
3031  * Returns TRUE when there is a match.
3032  */
3033     static int
3034 syn_regexec(rmp, lnum, col)
3035     regmmatch_T	*rmp;
3036     linenr_T	lnum;
3037     colnr_T	col;
3038 {
3039     rmp->rmm_maxcol = syn_buf->b_p_smc;
3040     if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
3041     {
3042 	rmp->startpos[0].lnum += lnum;
3043 	rmp->endpos[0].lnum += lnum;
3044 	return TRUE;
3045     }
3046     return FALSE;
3047 }
3048 
3049 /*
3050  * Check one position in a line for a matching keyword.
3051  * The caller must check if a keyword can start at startcol.
3052  * Return it's ID if found, 0 otherwise.
3053  */
3054     static int
3055 check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3056     char_u	*line;
3057     int		startcol;	/* position in line to check for keyword */
3058     int		*endcolp;	/* return: character after found keyword */
3059     long	*flagsp;	/* return: flags of matching keyword */
3060     short	**next_listp;	/* return: next_list of matching keyword */
3061     stateitem_T	*cur_si;	/* item at the top of the stack */
3062 {
3063     keyentry_T	*kp;
3064     char_u	*kwp;
3065     int		round;
3066     int		kwlen;
3067     char_u	keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
3068     hashtab_T	*ht;
3069     hashitem_T	*hi;
3070 
3071     /* Find first character after the keyword.  First character was already
3072      * checked. */
3073     kwp = line + startcol;
3074     kwlen = 0;
3075     do
3076     {
3077 #ifdef FEAT_MBYTE
3078 	if (has_mbyte)
3079 	    kwlen += (*mb_ptr2len)(kwp + kwlen);
3080 	else
3081 #endif
3082 	    ++kwlen;
3083     }
3084     while (vim_iswordc_buf(kwp + kwlen, syn_buf));
3085 
3086     if (kwlen > MAXKEYWLEN)
3087 	return 0;
3088 
3089     /*
3090      * Must make a copy of the keyword, so we can add a NUL and make it
3091      * lowercase.
3092      */
3093     vim_strncpy(keyword, kwp, kwlen);
3094 
3095     /*
3096      * Try twice:
3097      * 1. matching case
3098      * 2. ignoring case
3099      */
3100     for (round = 1; round <= 2; ++round)
3101     {
3102 	ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3103 	if (ht->ht_used == 0)
3104 	    continue;
3105 	if (round == 2)	/* ignore case */
3106 	    (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
3107 
3108 	/*
3109 	 * Find keywords that match.  There can be several with different
3110 	 * attributes.
3111 	 * When current_next_list is non-zero accept only that group, otherwise:
3112 	 *  Accept a not-contained keyword at toplevel.
3113 	 *  Accept a keyword at other levels only if it is in the contains list.
3114 	 */
3115 	hi = hash_find(ht, keyword);
3116 	if (!HASHITEM_EMPTY(hi))
3117 	    for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
3118 	    {
3119 		if (current_next_list != 0
3120 			? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3121 			: (cur_si == NULL
3122 			    ? !(kp->flags & HL_CONTAINED)
3123 			    : in_id_list(cur_si, cur_si->si_cont_list,
3124 				      &kp->k_syn, kp->flags & HL_CONTAINED)))
3125 		{
3126 		    *endcolp = startcol + kwlen;
3127 		    *flagsp = kp->flags;
3128 		    *next_listp = kp->next_list;
3129 		    return kp->k_syn.id;
3130 		}
3131 	    }
3132     }
3133     return 0;
3134 }
3135 
3136 /*
3137  * Handle ":syntax case" command.
3138  */
3139 /* ARGSUSED */
3140     static void
3141 syn_cmd_case(eap, syncing)
3142     exarg_T	*eap;
3143     int		syncing;	    /* not used */
3144 {
3145     char_u	*arg = eap->arg;
3146     char_u	*next;
3147 
3148     eap->nextcmd = find_nextcmd(arg);
3149     if (eap->skip)
3150 	return;
3151 
3152     next = skiptowhite(arg);
3153     if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3154 	curbuf->b_syn_ic = FALSE;
3155     else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3156 	curbuf->b_syn_ic = TRUE;
3157     else
3158 	EMSG2(_("E390: Illegal argument: %s"), arg);
3159 }
3160 
3161 /*
3162  * Handle ":syntax spell" command.
3163  */
3164 /* ARGSUSED */
3165     static void
3166 syn_cmd_spell(eap, syncing)
3167     exarg_T	*eap;
3168     int		syncing;	    /* not used */
3169 {
3170     char_u	*arg = eap->arg;
3171     char_u	*next;
3172 
3173     eap->nextcmd = find_nextcmd(arg);
3174     if (eap->skip)
3175 	return;
3176 
3177     next = skiptowhite(arg);
3178     if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3179 	curbuf->b_syn_spell = SYNSPL_TOP;
3180     else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3181 	curbuf->b_syn_spell = SYNSPL_NOTOP;
3182     else if (STRNICMP(arg, "default", 4) == 0 && next - arg == 4)
3183 	curbuf->b_syn_spell = SYNSPL_DEFAULT;
3184     else
3185 	EMSG2(_("E390: Illegal argument: %s"), arg);
3186 }
3187 
3188 /*
3189  * Clear all syntax info for one buffer.
3190  */
3191     void
3192 syntax_clear(buf)
3193     buf_T	*buf;
3194 {
3195     int i;
3196 
3197     buf->b_syn_error = FALSE;	    /* clear previous error */
3198     buf->b_syn_ic = FALSE;	    /* Use case, by default */
3199     buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3200     buf->b_syn_containedin = FALSE;
3201 
3202     /* free the keywords */
3203     clear_keywtab(&buf->b_keywtab);
3204     clear_keywtab(&buf->b_keywtab_ic);
3205 
3206     /* free the syntax patterns */
3207     for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3208 	syn_clear_pattern(buf, i);
3209     ga_clear(&buf->b_syn_patterns);
3210 
3211     /* free the syntax clusters */
3212     for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3213 	syn_clear_cluster(buf, i);
3214     ga_clear(&buf->b_syn_clusters);
3215     buf->b_spell_cluster_id = 0;
3216     buf->b_nospell_cluster_id = 0;
3217 
3218     buf->b_syn_sync_flags = 0;
3219     buf->b_syn_sync_minlines = 0;
3220     buf->b_syn_sync_maxlines = 0;
3221     buf->b_syn_sync_linebreaks = 0;
3222 
3223     vim_free(buf->b_syn_linecont_prog);
3224     buf->b_syn_linecont_prog = NULL;
3225     vim_free(buf->b_syn_linecont_pat);
3226     buf->b_syn_linecont_pat = NULL;
3227 #ifdef FEAT_FOLDING
3228     buf->b_syn_folditems = 0;
3229 #endif
3230 
3231     /* free the stored states */
3232     syn_stack_free_all(buf);
3233     invalidate_current_state();
3234 }
3235 
3236 /*
3237  * Clear syncing info for one buffer.
3238  */
3239     static void
3240 syntax_sync_clear()
3241 {
3242     int i;
3243 
3244     /* free the syntax patterns */
3245     for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3246 	if (SYN_ITEMS(curbuf)[i].sp_syncing)
3247 	    syn_remove_pattern(curbuf, i);
3248 
3249     curbuf->b_syn_sync_flags = 0;
3250     curbuf->b_syn_sync_minlines = 0;
3251     curbuf->b_syn_sync_maxlines = 0;
3252     curbuf->b_syn_sync_linebreaks = 0;
3253 
3254     vim_free(curbuf->b_syn_linecont_prog);
3255     curbuf->b_syn_linecont_prog = NULL;
3256     vim_free(curbuf->b_syn_linecont_pat);
3257     curbuf->b_syn_linecont_pat = NULL;
3258 
3259     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3260 }
3261 
3262 /*
3263  * Remove one pattern from the buffer's pattern list.
3264  */
3265     static void
3266 syn_remove_pattern(buf, idx)
3267     buf_T	*buf;
3268     int		idx;
3269 {
3270     synpat_T	*spp;
3271 
3272     spp = &(SYN_ITEMS(buf)[idx]);
3273 #ifdef FEAT_FOLDING
3274     if (spp->sp_flags & HL_FOLD)
3275 	--buf->b_syn_folditems;
3276 #endif
3277     syn_clear_pattern(buf, idx);
3278     mch_memmove(spp, spp + 1,
3279 		   sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3280     --buf->b_syn_patterns.ga_len;
3281 }
3282 
3283 /*
3284  * Clear and free one syntax pattern.  When clearing all, must be called from
3285  * last to first!
3286  */
3287     static void
3288 syn_clear_pattern(buf, i)
3289     buf_T	*buf;
3290     int		i;
3291 {
3292     vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3293     vim_free(SYN_ITEMS(buf)[i].sp_prog);
3294     /* Only free sp_cont_list and sp_next_list of first start pattern */
3295     if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3296     {
3297 	vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3298 	vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3299     }
3300 }
3301 
3302 /*
3303  * Clear and free one syntax cluster.
3304  */
3305     static void
3306 syn_clear_cluster(buf, i)
3307     buf_T	*buf;
3308     int		i;
3309 {
3310     vim_free(SYN_CLSTR(buf)[i].scl_name);
3311     vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3312     vim_free(SYN_CLSTR(buf)[i].scl_list);
3313 }
3314 
3315 /*
3316  * Handle ":syntax clear" command.
3317  */
3318     static void
3319 syn_cmd_clear(eap, syncing)
3320     exarg_T	*eap;
3321     int		syncing;
3322 {
3323     char_u	*arg = eap->arg;
3324     char_u	*arg_end;
3325     int		id;
3326 
3327     eap->nextcmd = find_nextcmd(arg);
3328     if (eap->skip)
3329 	return;
3330 
3331     /*
3332      * We have to disable this within ":syn include @group filename",
3333      * because otherwise @group would get deleted.
3334      * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3335      * clear".
3336      */
3337     if (curbuf->b_syn_topgrp != 0)
3338 	return;
3339 
3340     if (ends_excmd(*arg))
3341     {
3342 	/*
3343 	 * No argument: Clear all syntax items.
3344 	 */
3345 	if (syncing)
3346 	    syntax_sync_clear();
3347 	else
3348 	{
3349 	    syntax_clear(curbuf);
3350 	    do_unlet((char_u *)"b:current_syntax", TRUE);
3351 	}
3352     }
3353     else
3354     {
3355 	/*
3356 	 * Clear the group IDs that are in the argument.
3357 	 */
3358 	while (!ends_excmd(*arg))
3359 	{
3360 	    arg_end = skiptowhite(arg);
3361 	    if (*arg == '@')
3362 	    {
3363 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3364 		if (id == 0)
3365 		{
3366 		    EMSG2(_("E391: No such syntax cluster: %s"), arg);
3367 		    break;
3368 		}
3369 		else
3370 		{
3371 		    /*
3372 		     * We can't physically delete a cluster without changing
3373 		     * the IDs of other clusters, so we do the next best thing
3374 		     * and make it empty.
3375 		     */
3376 		    short scl_id = id - SYNID_CLUSTER;
3377 
3378 		    vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3379 		    SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3380 		}
3381 	    }
3382 	    else
3383 	    {
3384 		id = syn_namen2id(arg, (int)(arg_end - arg));
3385 		if (id == 0)
3386 		{
3387 		    EMSG2(_(e_nogroup), arg);
3388 		    break;
3389 		}
3390 		else
3391 		    syn_clear_one(id, syncing);
3392 	    }
3393 	    arg = skipwhite(arg_end);
3394 	}
3395     }
3396     redraw_curbuf_later(NOT_VALID);
3397     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3398 }
3399 
3400 /*
3401  * Clear one syntax group for the current buffer.
3402  */
3403     static void
3404 syn_clear_one(id, syncing)
3405     int		id;
3406     int		syncing;
3407 {
3408     synpat_T	*spp;
3409     int		idx;
3410 
3411     /* Clear keywords only when not ":syn sync clear group-name" */
3412     if (!syncing)
3413     {
3414 	(void)syn_clear_keyword(id, &curbuf->b_keywtab);
3415 	(void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
3416     }
3417 
3418     /* clear the patterns for "id" */
3419     for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3420     {
3421 	spp = &(SYN_ITEMS(curbuf)[idx]);
3422 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3423 	    continue;
3424 	syn_remove_pattern(curbuf, idx);
3425     }
3426 }
3427 
3428 /*
3429  * Handle ":syntax on" command.
3430  */
3431 /* ARGSUSED */
3432     static void
3433 syn_cmd_on(eap, syncing)
3434     exarg_T	*eap;
3435     int		syncing;	/* not used */
3436 {
3437     syn_cmd_onoff(eap, "syntax");
3438 }
3439 
3440 /*
3441  * Handle ":syntax enable" command.
3442  */
3443 /* ARGSUSED */
3444     static void
3445 syn_cmd_enable(eap, syncing)
3446     exarg_T	*eap;
3447     int		syncing;	/* not used */
3448 {
3449     set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3450     syn_cmd_onoff(eap, "syntax");
3451     do_unlet((char_u *)"g:syntax_cmd", TRUE);
3452 }
3453 
3454 /*
3455  * Handle ":syntax reset" command.
3456  */
3457 /* ARGSUSED */
3458     static void
3459 syn_cmd_reset(eap, syncing)
3460     exarg_T	*eap;
3461     int		syncing;	/* not used */
3462 {
3463     eap->nextcmd = check_nextcmd(eap->arg);
3464     if (!eap->skip)
3465     {
3466 	set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3467 	do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
3468 	do_unlet((char_u *)"g:syntax_cmd", TRUE);
3469     }
3470 }
3471 
3472 /*
3473  * Handle ":syntax manual" command.
3474  */
3475 /* ARGSUSED */
3476     static void
3477 syn_cmd_manual(eap, syncing)
3478     exarg_T	*eap;
3479     int		syncing;	/* not used */
3480 {
3481     syn_cmd_onoff(eap, "manual");
3482 }
3483 
3484 /*
3485  * Handle ":syntax off" command.
3486  */
3487 /* ARGSUSED */
3488     static void
3489 syn_cmd_off(eap, syncing)
3490     exarg_T	*eap;
3491     int		syncing;	/* not used */
3492 {
3493     syn_cmd_onoff(eap, "nosyntax");
3494 }
3495 
3496     static void
3497 syn_cmd_onoff(eap, name)
3498     exarg_T	*eap;
3499     char	*name;
3500 {
3501     char_u	buf[100];
3502 
3503     eap->nextcmd = check_nextcmd(eap->arg);
3504     if (!eap->skip)
3505     {
3506 	STRCPY(buf, "so ");
3507 	vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
3508 	do_cmdline_cmd(buf);
3509     }
3510 }
3511 
3512 /*
3513  * Handle ":syntax [list]" command: list current syntax words.
3514  */
3515     static void
3516 syn_cmd_list(eap, syncing)
3517     exarg_T	*eap;
3518     int		syncing;	    /* when TRUE: list syncing items */
3519 {
3520     char_u	*arg = eap->arg;
3521     int		id;
3522     char_u	*arg_end;
3523 
3524     eap->nextcmd = find_nextcmd(arg);
3525     if (eap->skip)
3526 	return;
3527 
3528     if (!syntax_present(curbuf))
3529     {
3530 	MSG(_("No Syntax items defined for this buffer"));
3531 	return;
3532     }
3533 
3534     if (syncing)
3535     {
3536 	if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3537 	{
3538 	    MSG_PUTS(_("syncing on C-style comments"));
3539 	    syn_lines_msg();
3540 	    syn_match_msg();
3541 	    return;
3542 	}
3543 	else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3544 	{
3545 	    if (curbuf->b_syn_sync_minlines == 0)
3546 		MSG_PUTS(_("no syncing"));
3547 	    else
3548 	    {
3549 		MSG_PUTS(_("syncing starts "));
3550 		msg_outnum(curbuf->b_syn_sync_minlines);
3551 		MSG_PUTS(_(" lines before top line"));
3552 		syn_match_msg();
3553 	    }
3554 	    return;
3555 	}
3556 	MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3557 	if (curbuf->b_syn_sync_minlines > 0
3558 		|| curbuf->b_syn_sync_maxlines > 0
3559 		|| curbuf->b_syn_sync_linebreaks > 0)
3560 	{
3561 	    MSG_PUTS(_("\nsyncing on items"));
3562 	    syn_lines_msg();
3563 	    syn_match_msg();
3564 	}
3565     }
3566     else
3567 	MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3568     if (ends_excmd(*arg))
3569     {
3570 	/*
3571 	 * No argument: List all group IDs and all syntax clusters.
3572 	 */
3573 	for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3574 	    syn_list_one(id, syncing, FALSE);
3575 	for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3576 	    syn_list_cluster(id);
3577     }
3578     else
3579     {
3580 	/*
3581 	 * List the group IDs and syntax clusters that are in the argument.
3582 	 */
3583 	while (!ends_excmd(*arg) && !got_int)
3584 	{
3585 	    arg_end = skiptowhite(arg);
3586 	    if (*arg == '@')
3587 	    {
3588 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3589 		if (id == 0)
3590 		    EMSG2(_("E392: No such syntax cluster: %s"), arg);
3591 		else
3592 		    syn_list_cluster(id - SYNID_CLUSTER);
3593 	    }
3594 	    else
3595 	    {
3596 		id = syn_namen2id(arg, (int)(arg_end - arg));
3597 		if (id == 0)
3598 		    EMSG2(_(e_nogroup), arg);
3599 		else
3600 		    syn_list_one(id, syncing, TRUE);
3601 	    }
3602 	    arg = skipwhite(arg_end);
3603 	}
3604     }
3605     eap->nextcmd = check_nextcmd(arg);
3606 }
3607 
3608     static void
3609 syn_lines_msg()
3610 {
3611     if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3612     {
3613 	MSG_PUTS("; ");
3614 	if (curbuf->b_syn_sync_minlines > 0)
3615 	{
3616 	    MSG_PUTS(_("minimal "));
3617 	    msg_outnum(curbuf->b_syn_sync_minlines);
3618 	    if (curbuf->b_syn_sync_maxlines)
3619 		MSG_PUTS(", ");
3620 	}
3621 	if (curbuf->b_syn_sync_maxlines > 0)
3622 	{
3623 	    MSG_PUTS(_("maximal "));
3624 	    msg_outnum(curbuf->b_syn_sync_maxlines);
3625 	}
3626 	MSG_PUTS(_(" lines before top line"));
3627     }
3628 }
3629 
3630     static void
3631 syn_match_msg()
3632 {
3633     if (curbuf->b_syn_sync_linebreaks > 0)
3634     {
3635 	MSG_PUTS(_("; match "));
3636 	msg_outnum(curbuf->b_syn_sync_linebreaks);
3637 	MSG_PUTS(_(" line breaks"));
3638     }
3639 }
3640 
3641 static int  last_matchgroup;
3642 
3643 struct name_list
3644 {
3645     int		flag;
3646     char	*name;
3647 };
3648 
3649 static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3650 
3651 /*
3652  * List one syntax item, for ":syntax" or "syntax list syntax_name".
3653  */
3654     static void
3655 syn_list_one(id, syncing, link_only)
3656     int		id;
3657     int		syncing;	    /* when TRUE: list syncing items */
3658     int		link_only;	    /* when TRUE; list link-only too */
3659 {
3660     int		attr;
3661     int		idx;
3662     int		did_header = FALSE;
3663     synpat_T	*spp;
3664     static struct name_list namelist1[] =
3665 		{
3666 		    {HL_DISPLAY, "display"},
3667 		    {HL_CONTAINED, "contained"},
3668 		    {HL_ONELINE, "oneline"},
3669 		    {HL_KEEPEND, "keepend"},
3670 		    {HL_EXTEND, "extend"},
3671 		    {HL_EXCLUDENL, "excludenl"},
3672 		    {HL_TRANSP, "transparent"},
3673 		    {HL_FOLD, "fold"},
3674 		    {0, NULL}
3675 		};
3676     static struct name_list namelist2[] =
3677 		{
3678 		    {HL_SKIPWHITE, "skipwhite"},
3679 		    {HL_SKIPNL, "skipnl"},
3680 		    {HL_SKIPEMPTY, "skipempty"},
3681 		    {0, NULL}
3682 		};
3683 
3684     attr = hl_attr(HLF_D);		/* highlight like directories */
3685 
3686     /* list the keywords for "id" */
3687     if (!syncing)
3688     {
3689 	did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3690 	did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
3691 							    did_header, attr);
3692     }
3693 
3694     /* list the patterns for "id" */
3695     for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3696     {
3697 	spp = &(SYN_ITEMS(curbuf)[idx]);
3698 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3699 	    continue;
3700 
3701 	(void)syn_list_header(did_header, 999, id);
3702 	did_header = TRUE;
3703 	last_matchgroup = 0;
3704 	if (spp->sp_type == SPTYPE_MATCH)
3705 	{
3706 	    put_pattern("match", ' ', spp, attr);
3707 	    msg_putchar(' ');
3708 	}
3709 	else if (spp->sp_type == SPTYPE_START)
3710 	{
3711 	    while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3712 		put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3713 	    if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3714 		put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3715 	    while (idx < curbuf->b_syn_patterns.ga_len
3716 			      && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3717 		put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3718 	    --idx;
3719 	    msg_putchar(' ');
3720 	}
3721 	syn_list_flags(namelist1, spp->sp_flags, attr);
3722 
3723 	if (spp->sp_cont_list != NULL)
3724 	    put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3725 
3726 	if (spp->sp_syn.cont_in_list != NULL)
3727 	    put_id_list((char_u *)"containedin",
3728 					      spp->sp_syn.cont_in_list, attr);
3729 
3730 	if (spp->sp_next_list != NULL)
3731 	{
3732 	    put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3733 	    syn_list_flags(namelist2, spp->sp_flags, attr);
3734 	}
3735 	if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3736 	{
3737 	    if (spp->sp_flags & HL_SYNC_HERE)
3738 		msg_puts_attr((char_u *)"grouphere", attr);
3739 	    else
3740 		msg_puts_attr((char_u *)"groupthere", attr);
3741 	    msg_putchar(' ');
3742 	    if (spp->sp_sync_idx >= 0)
3743 		msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3744 				   [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3745 	    else
3746 		MSG_PUTS("NONE");
3747 	    msg_putchar(' ');
3748 	}
3749     }
3750 
3751     /* list the link, if there is one */
3752     if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3753     {
3754 	(void)syn_list_header(did_header, 999, id);
3755 	msg_puts_attr((char_u *)"links to", attr);
3756 	msg_putchar(' ');
3757 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3758     }
3759 }
3760 
3761     static void
3762 syn_list_flags(nl, flags, attr)
3763     struct name_list	*nl;
3764     int			flags;
3765     int			attr;
3766 {
3767     int		i;
3768 
3769     for (i = 0; nl[i].flag != 0; ++i)
3770 	if (flags & nl[i].flag)
3771 	{
3772 	    msg_puts_attr((char_u *)nl[i].name, attr);
3773 	    msg_putchar(' ');
3774 	}
3775 }
3776 
3777 /*
3778  * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3779  */
3780     static void
3781 syn_list_cluster(id)
3782     int id;
3783 {
3784     int	    endcol = 15;
3785 
3786     /* slight hack:  roughly duplicate the guts of syn_list_header() */
3787     msg_putchar('\n');
3788     msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3789 
3790     if (msg_col >= endcol)	/* output at least one space */
3791 	endcol = msg_col + 1;
3792     if (Columns <= endcol)	/* avoid hang for tiny window */
3793 	endcol = Columns - 1;
3794 
3795     msg_advance(endcol);
3796     if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3797     {
3798 	put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3799 		    hl_attr(HLF_D));
3800     }
3801     else
3802     {
3803 	msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3804 	msg_puts((char_u *)"=NONE");
3805     }
3806 }
3807 
3808     static void
3809 put_id_list(name, list, attr)
3810     char_u	*name;
3811     short	*list;
3812     int		attr;
3813 {
3814     short		*p;
3815 
3816     msg_puts_attr(name, attr);
3817     msg_putchar('=');
3818     for (p = list; *p; ++p)
3819     {
3820 	if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3821 	{
3822 	    if (p[1])
3823 		MSG_PUTS("ALLBUT");
3824 	    else
3825 		MSG_PUTS("ALL");
3826 	}
3827 	else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3828 	{
3829 	    MSG_PUTS("TOP");
3830 	}
3831 	else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3832 	{
3833 	    MSG_PUTS("CONTAINED");
3834 	}
3835 	else if (*p >= SYNID_CLUSTER)
3836 	{
3837 	    short scl_id = *p - SYNID_CLUSTER;
3838 
3839 	    msg_putchar('@');
3840 	    msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3841 	}
3842 	else
3843 	    msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3844 	if (p[1])
3845 	    msg_putchar(',');
3846     }
3847     msg_putchar(' ');
3848 }
3849 
3850     static void
3851 put_pattern(s, c, spp, attr)
3852     char	*s;
3853     int		c;
3854     synpat_T	*spp;
3855     int		attr;
3856 {
3857     long	n;
3858     int		mask;
3859     int		first;
3860     static char	*sepchars = "/+=-#@\"|'^&";
3861     int		i;
3862 
3863     /* May have to write "matchgroup=group" */
3864     if (last_matchgroup != spp->sp_syn_match_id)
3865     {
3866 	last_matchgroup = spp->sp_syn_match_id;
3867 	msg_puts_attr((char_u *)"matchgroup", attr);
3868 	msg_putchar('=');
3869 	if (last_matchgroup == 0)
3870 	    msg_outtrans((char_u *)"NONE");
3871 	else
3872 	    msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3873 	msg_putchar(' ');
3874     }
3875 
3876     /* output the name of the pattern and an '=' or ' ' */
3877     msg_puts_attr((char_u *)s, attr);
3878     msg_putchar(c);
3879 
3880     /* output the pattern, in between a char that is not in the pattern */
3881     for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3882 	if (sepchars[++i] == NUL)
3883 	{
3884 	    i = 0;	/* no good char found, just use the first one */
3885 	    break;
3886 	}
3887     msg_putchar(sepchars[i]);
3888     msg_outtrans(spp->sp_pattern);
3889     msg_putchar(sepchars[i]);
3890 
3891     /* output any pattern options */
3892     first = TRUE;
3893     for (i = 0; i < SPO_COUNT; ++i)
3894     {
3895 	mask = (1 << i);
3896 	if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3897 	{
3898 	    if (!first)
3899 		msg_putchar(',');	/* separate with commas */
3900 	    msg_puts((char_u *)spo_name_tab[i]);
3901 	    n = spp->sp_offsets[i];
3902 	    if (i != SPO_LC_OFF)
3903 	    {
3904 		if (spp->sp_off_flags & mask)
3905 		    msg_putchar('s');
3906 		else
3907 		    msg_putchar('e');
3908 		if (n > 0)
3909 		    msg_putchar('+');
3910 	    }
3911 	    if (n || i == SPO_LC_OFF)
3912 		msg_outnum(n);
3913 	    first = FALSE;
3914 	}
3915     }
3916     msg_putchar(' ');
3917 }
3918 
3919 /*
3920  * List or clear the keywords for one syntax group.
3921  * Return TRUE if the header has been printed.
3922  */
3923     static int
3924 syn_list_keywords(id, ht, did_header, attr)
3925     int		id;
3926     hashtab_T	*ht;
3927     int		did_header;		/* header has already been printed */
3928     int		attr;
3929 {
3930     int		outlen;
3931     hashitem_T	*hi;
3932     keyentry_T	*kp;
3933     int		todo;
3934     int		prev_contained = 0;
3935     short	*prev_next_list = NULL;
3936     short	*prev_cont_in_list = NULL;
3937     int		prev_skipnl = 0;
3938     int		prev_skipwhite = 0;
3939     int		prev_skipempty = 0;
3940 
3941     /*
3942      * Unfortunately, this list of keywords is not sorted on alphabet but on
3943      * hash value...
3944      */
3945     todo = ht->ht_used;
3946     for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
3947     {
3948 	if (!HASHITEM_EMPTY(hi))
3949 	{
3950 	    --todo;
3951 	    for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
3952 	    {
3953 		if (kp->k_syn.id == id)
3954 		{
3955 		    if (prev_contained != (kp->flags & HL_CONTAINED)
3956 			    || prev_skipnl != (kp->flags & HL_SKIPNL)
3957 			    || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3958 			    || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3959 			    || prev_cont_in_list != kp->k_syn.cont_in_list
3960 			    || prev_next_list != kp->next_list)
3961 			outlen = 9999;
3962 		    else
3963 			outlen = (int)STRLEN(kp->keyword);
3964 		    /* output "contained" and "nextgroup" on each line */
3965 		    if (syn_list_header(did_header, outlen, id))
3966 		    {
3967 			prev_contained = 0;
3968 			prev_next_list = NULL;
3969 			prev_cont_in_list = NULL;
3970 			prev_skipnl = 0;
3971 			prev_skipwhite = 0;
3972 			prev_skipempty = 0;
3973 		    }
3974 		    did_header = TRUE;
3975 		    if (prev_contained != (kp->flags & HL_CONTAINED))
3976 		    {
3977 			msg_puts_attr((char_u *)"contained", attr);
3978 			msg_putchar(' ');
3979 			prev_contained = (kp->flags & HL_CONTAINED);
3980 		    }
3981 		    if (kp->k_syn.cont_in_list != prev_cont_in_list)
3982 		    {
3983 			put_id_list((char_u *)"containedin",
3984 						kp->k_syn.cont_in_list, attr);
3985 			msg_putchar(' ');
3986 			prev_cont_in_list = kp->k_syn.cont_in_list;
3987 		    }
3988 		    if (kp->next_list != prev_next_list)
3989 		    {
3990 			put_id_list((char_u *)"nextgroup", kp->next_list, attr);
3991 			msg_putchar(' ');
3992 			prev_next_list = kp->next_list;
3993 			if (kp->flags & HL_SKIPNL)
3994 			{
3995 			    msg_puts_attr((char_u *)"skipnl", attr);
3996 			    msg_putchar(' ');
3997 			    prev_skipnl = (kp->flags & HL_SKIPNL);
3998 			}
3999 			if (kp->flags & HL_SKIPWHITE)
4000 			{
4001 			    msg_puts_attr((char_u *)"skipwhite", attr);
4002 			    msg_putchar(' ');
4003 			    prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4004 			}
4005 			if (kp->flags & HL_SKIPEMPTY)
4006 			{
4007 			    msg_puts_attr((char_u *)"skipempty", attr);
4008 			    msg_putchar(' ');
4009 			    prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4010 			}
4011 		    }
4012 		    msg_outtrans(kp->keyword);
4013 		}
4014 	    }
4015 	}
4016     }
4017 
4018     return did_header;
4019 }
4020 
4021     static void
4022 syn_clear_keyword(id, ht)
4023     int		id;
4024     hashtab_T	*ht;
4025 {
4026     hashitem_T	*hi;
4027     keyentry_T	*kp;
4028     keyentry_T	*kp_prev;
4029     keyentry_T	*kp_next;
4030     int		todo;
4031 
4032     hash_lock(ht);
4033     todo = ht->ht_used;
4034     for (hi = ht->ht_array; todo > 0; ++hi)
4035     {
4036 	if (!HASHITEM_EMPTY(hi))
4037 	{
4038 	    --todo;
4039 	    kp_prev = NULL;
4040 	    for (kp = HI2KE(hi); kp != NULL; )
4041 	    {
4042 		if (kp->k_syn.id == id)
4043 		{
4044 		    kp_next = kp->ke_next;
4045 		    if (kp_prev == NULL)
4046 		    {
4047 			if (kp_next == NULL)
4048 			    hash_remove(ht, hi);
4049 			else
4050 			    hi->hi_key = KE2HIKEY(kp_next);
4051 		    }
4052 		    else
4053 			kp_prev->ke_next = kp_next;
4054 		    vim_free(kp->next_list);
4055 		    vim_free(kp->k_syn.cont_in_list);
4056 		    vim_free(kp);
4057 		    kp = kp_next;
4058 		}
4059 		else
4060 		{
4061 		    kp_prev = kp;
4062 		    kp = kp->ke_next;
4063 		}
4064 	    }
4065 	}
4066     }
4067     hash_unlock(ht);
4068 }
4069 
4070 /*
4071  * Clear a whole keyword table.
4072  */
4073     static void
4074 clear_keywtab(ht)
4075     hashtab_T	*ht;
4076 {
4077     hashitem_T	*hi;
4078     int		todo;
4079     keyentry_T	*kp;
4080     keyentry_T	*kp_next;
4081 
4082     todo = ht->ht_used;
4083     for (hi = ht->ht_array; todo > 0; ++hi)
4084     {
4085 	if (!HASHITEM_EMPTY(hi))
4086 	{
4087 	    --todo;
4088 	    kp = HI2KE(hi);
4089 	    for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
4090 	    {
4091 		kp_next = kp->ke_next;
4092 		vim_free(kp->next_list);
4093 		vim_free(kp->k_syn.cont_in_list);
4094 		vim_free(kp);
4095 	    }
4096 	}
4097     }
4098     hash_clear(ht);
4099     hash_init(ht);
4100 }
4101 
4102 /*
4103  * Add a keyword to the list of keywords.
4104  */
4105     static void
4106 add_keyword(name, id, flags, cont_in_list, next_list)
4107     char_u	*name;	    /* name of keyword */
4108     int		id;	    /* group ID for this keyword */
4109     int		flags;	    /* flags for this keyword */
4110     short	*cont_in_list; /* containedin for this keyword */
4111     short	*next_list; /* nextgroup for this keyword */
4112 {
4113     keyentry_T	*kp;
4114     hashtab_T	*ht;
4115     hashitem_T	*hi;
4116     char_u	*name_ic;
4117     long_u	hash;
4118     char_u	name_folded[MAXKEYWLEN + 1];
4119 
4120     if (curbuf->b_syn_ic)
4121 	name_ic = str_foldcase(name, (int)STRLEN(name),
4122 						 name_folded, MAXKEYWLEN + 1);
4123     else
4124 	name_ic = name;
4125     kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4126     if (kp == NULL)
4127 	return;
4128     STRCPY(kp->keyword, name_ic);
4129     kp->k_syn.id = id;
4130     kp->k_syn.inc_tag = current_syn_inc_tag;
4131     kp->flags = flags;
4132     kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
4133     if (cont_in_list != NULL)
4134 	curbuf->b_syn_containedin = TRUE;
4135     kp->next_list = copy_id_list(next_list);
4136 
4137     if (curbuf->b_syn_ic)
4138 	ht = &curbuf->b_keywtab_ic;
4139     else
4140 	ht = &curbuf->b_keywtab;
4141 
4142     hash = hash_hash(kp->keyword);
4143     hi = hash_lookup(ht, kp->keyword, hash);
4144     if (HASHITEM_EMPTY(hi))
4145     {
4146 	/* new keyword, add to hashtable */
4147 	kp->ke_next = NULL;
4148 	hash_add_item(ht, hi, kp->keyword, hash);
4149     }
4150     else
4151     {
4152 	/* keyword already exists, prepend to list */
4153 	kp->ke_next = HI2KE(hi);
4154 	hi->hi_key = KE2HIKEY(kp);
4155     }
4156 }
4157 
4158 /*
4159  * Get the start and end of the group name argument.
4160  * Return a pointer to the first argument.
4161  * Return NULL if the end of the command was found instead of further args.
4162  */
4163     static char_u *
4164 get_group_name(arg, name_end)
4165     char_u	*arg;		/* start of the argument */
4166     char_u	**name_end;	/* pointer to end of the name */
4167 {
4168     char_u	*rest;
4169 
4170     *name_end = skiptowhite(arg);
4171     rest = skipwhite(*name_end);
4172 
4173     /*
4174      * Check if there are enough arguments.  The first argument may be a
4175      * pattern, where '|' is allowed, so only check for NUL.
4176      */
4177     if (ends_excmd(*arg) || *rest == NUL)
4178 	return NULL;
4179     return rest;
4180 }
4181 
4182 /*
4183  * Check for syntax command option arguments.
4184  * This can be called at any place in the list of arguments, and just picks
4185  * out the arguments that are known.  Can be called several times in a row to
4186  * collect all options in between other arguments.
4187  * Return a pointer to the next argument (which isn't an option).
4188  * Return NULL for any error;
4189  */
4190     static char_u *
4191 get_syn_options(arg, opt)
4192     char_u	    *arg;		/* next argument to be checked */
4193     syn_opt_arg_T   *opt;		/* various things */
4194 {
4195     char_u	*gname_start, *gname;
4196     int		syn_id;
4197     int		len;
4198     char	*p;
4199     int		i;
4200     int		fidx;
4201     static struct flag
4202     {
4203 	char	*name;
4204 	int	argtype;
4205 	int	flags;
4206     } flagtab[] = { {"cCoOnNtTaAiInNeEdD",	0,	HL_CONTAINED},
4207 		    {"oOnNeElLiInNeE",		0,	HL_ONELINE},
4208 		    {"kKeEeEpPeEnNdD",		0,	HL_KEEPEND},
4209 		    {"eExXtTeEnNdD",		0,	HL_EXTEND},
4210 		    {"eExXcClLuUdDeEnNlL",	0,	HL_EXCLUDENL},
4211 		    {"tTrRaAnNsSpPaArReEnNtT",	0,	HL_TRANSP},
4212 		    {"sSkKiIpPnNlL",		0,	HL_SKIPNL},
4213 		    {"sSkKiIpPwWhHiItTeE",	0,	HL_SKIPWHITE},
4214 		    {"sSkKiIpPeEmMpPtTyY",	0,	HL_SKIPEMPTY},
4215 		    {"gGrRoOuUpPhHeErReE",	0,	HL_SYNC_HERE},
4216 		    {"gGrRoOuUpPtThHeErReE",	0,	HL_SYNC_THERE},
4217 		    {"dDiIsSpPlLaAyY",		0,	HL_DISPLAY},
4218 		    {"fFoOlLdD",		0,	HL_FOLD},
4219 		    {"cCoOnNtTaAiInNsS",	1,	0},
4220 		    {"cCoOnNtTaAiInNeEdDiInN",	2,	0},
4221 		    {"nNeExXtTgGrRoOuUpP",	3,	0},
4222 		};
4223     static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
4224 
4225     if (arg == NULL)		/* already detected error */
4226 	return NULL;
4227 
4228     for (;;)
4229     {
4230 	/*
4231 	 * This is used very often when a large number of keywords is defined.
4232 	 * Need to skip quickly when no option name is found.
4233 	 * Also avoid tolower(), it's slow.
4234 	 */
4235 	if (strchr(first_letters, *arg) == NULL)
4236 	    break;
4237 
4238 	for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4239 	{
4240 	    p = flagtab[fidx].name;
4241 	    for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4242 		if (arg[len] != p[i] && arg[len] != p[i + 1])
4243 		    break;
4244 	    if (p[i] == NUL && (vim_iswhite(arg[len])
4245 				    || (flagtab[fidx].argtype > 0
4246 					 ? arg[len] == '='
4247 					 : ends_excmd(arg[len]))))
4248 	    {
4249 		if (opt->keyword
4250 			&& (flagtab[fidx].flags == HL_DISPLAY
4251 			    || flagtab[fidx].flags == HL_FOLD
4252 			    || flagtab[fidx].flags == HL_EXTEND))
4253 		    /* treat "display", "fold" and "extend" as a keyword */
4254 		    fidx = -1;
4255 		break;
4256 	    }
4257 	}
4258 	if (fidx < 0)	    /* no match found */
4259 	    break;
4260 
4261 	if (flagtab[fidx].argtype == 1)
4262 	{
4263 	    if (!opt->has_cont_list)
4264 	    {
4265 		EMSG(_("E395: contains argument not accepted here"));
4266 		return NULL;
4267 	    }
4268 	    if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
4269 		return NULL;
4270 	}
4271 	else if (flagtab[fidx].argtype == 2)
4272 	{
4273 #if 0	    /* cannot happen */
4274 	    if (opt->cont_in_list == NULL)
4275 	    {
4276 		EMSG(_("E396: containedin argument not accepted here"));
4277 		return NULL;
4278 	    }
4279 #endif
4280 	    if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
4281 		return NULL;
4282 	}
4283 	else if (flagtab[fidx].argtype == 3)
4284 	{
4285 	    if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
4286 		return NULL;
4287 	}
4288 	else
4289 	{
4290 	    opt->flags |= flagtab[fidx].flags;
4291 	    arg = skipwhite(arg + len);
4292 
4293 	    if (flagtab[fidx].flags == HL_SYNC_HERE
4294 		    || flagtab[fidx].flags == HL_SYNC_THERE)
4295 	    {
4296 		if (opt->sync_idx == NULL)
4297 		{
4298 		    EMSG(_("E393: group[t]here not accepted here"));
4299 		    return NULL;
4300 		}
4301 		gname_start = arg;
4302 		arg = skiptowhite(arg);
4303 		if (gname_start == arg)
4304 		    return NULL;
4305 		gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4306 		if (gname == NULL)
4307 		    return NULL;
4308 		if (STRCMP(gname, "NONE") == 0)
4309 		    *opt->sync_idx = NONE_IDX;
4310 		else
4311 		{
4312 		    syn_id = syn_name2id(gname);
4313 		    for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4314 			if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4315 			      && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4316 			{
4317 			    *opt->sync_idx = i;
4318 			    break;
4319 			}
4320 		    if (i < 0)
4321 		    {
4322 			EMSG2(_("E394: Didn't find region item for %s"), gname);
4323 			vim_free(gname);
4324 			return NULL;
4325 		    }
4326 		}
4327 
4328 		vim_free(gname);
4329 		arg = skipwhite(arg);
4330 	    }
4331 #ifdef FEAT_FOLDING
4332 	    else if (flagtab[fidx].flags == HL_FOLD
4333 						&& foldmethodIsSyntax(curwin))
4334 		/* Need to update folds later. */
4335 		foldUpdateAll(curwin);
4336 #endif
4337 	}
4338     }
4339 
4340     return arg;
4341 }
4342 
4343 /*
4344  * Adjustments to syntax item when declared in a ":syn include"'d file.
4345  * Set the contained flag, and if the item is not already contained, add it
4346  * to the specified top-level group, if any.
4347  */
4348     static void
4349 syn_incl_toplevel(id, flagsp)
4350     int		id;
4351     int		*flagsp;
4352 {
4353     if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4354 	return;
4355     *flagsp |= HL_CONTAINED;
4356     if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4357     {
4358 	/* We have to alloc this, because syn_combine_list() will free it. */
4359 	short	    *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4360 	int	    tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4361 
4362 	if (grp_list != NULL)
4363 	{
4364 	    grp_list[0] = id;
4365 	    grp_list[1] = 0;
4366 	    syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4367 			 CLUSTER_ADD);
4368 	}
4369     }
4370 }
4371 
4372 /*
4373  * Handle ":syntax include [@{group-name}] filename" command.
4374  */
4375 /* ARGSUSED */
4376     static void
4377 syn_cmd_include(eap, syncing)
4378     exarg_T	*eap;
4379     int		syncing;	    /* not used */
4380 {
4381     char_u	*arg = eap->arg;
4382     int		sgl_id = 1;
4383     char_u	*group_name_end;
4384     char_u	*rest;
4385     char_u	*errormsg = NULL;
4386     int		prev_toplvl_grp;
4387     int		prev_syn_inc_tag;
4388     int		source = FALSE;
4389 
4390     eap->nextcmd = find_nextcmd(arg);
4391     if (eap->skip)
4392 	return;
4393 
4394     if (arg[0] == '@')
4395     {
4396 	++arg;
4397 	rest = get_group_name(arg, &group_name_end);
4398 	if (rest == NULL)
4399 	{
4400 	    EMSG((char_u *)_("E397: Filename required"));
4401 	    return;
4402 	}
4403 	sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4404 	/* separate_nextcmd() and expand_filename() depend on this */
4405 	eap->arg = rest;
4406     }
4407 
4408     /*
4409      * Everything that's left, up to the next command, should be the
4410      * filename to include.
4411      */
4412     eap->argt |= (XFILE | NOSPC);
4413     separate_nextcmd(eap);
4414     if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4415     {
4416 	/* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4417 	 * file.  Need to expand the file name first.  In other cases
4418 	 * ":runtime!" is used. */
4419 	source = TRUE;
4420 	if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4421 	{
4422 	    if (errormsg != NULL)
4423 		EMSG(errormsg);
4424 	    return;
4425 	}
4426     }
4427 
4428     /*
4429      * Save and restore the existing top-level grouplist id and ":syn
4430      * include" tag around the actual inclusion.
4431      */
4432     prev_syn_inc_tag = current_syn_inc_tag;
4433     current_syn_inc_tag = ++running_syn_inc_tag;
4434     prev_toplvl_grp = curbuf->b_syn_topgrp;
4435     curbuf->b_syn_topgrp = sgl_id;
4436     if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
4437 				     : source_runtime(eap->arg, TRUE) == FAIL)
4438 	EMSG2(_(e_notopen), eap->arg);
4439     curbuf->b_syn_topgrp = prev_toplvl_grp;
4440     current_syn_inc_tag = prev_syn_inc_tag;
4441 }
4442 
4443 /*
4444  * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4445  */
4446 /* ARGSUSED */
4447     static void
4448 syn_cmd_keyword(eap, syncing)
4449     exarg_T	*eap;
4450     int		syncing;	    /* not used */
4451 {
4452     char_u	*arg = eap->arg;
4453     char_u	*group_name_end;
4454     int		syn_id;
4455     char_u	*rest;
4456     char_u	*keyword_copy;
4457     char_u	*p;
4458     char_u	*kw;
4459     syn_opt_arg_T syn_opt_arg;
4460     int		cnt;
4461 
4462     rest = get_group_name(arg, &group_name_end);
4463 
4464     if (rest != NULL)
4465     {
4466 	syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4467 
4468 	/* allocate a buffer, for removing the backslashes in the keyword */
4469 	keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4470 	if (keyword_copy != NULL)
4471 	{
4472 	    syn_opt_arg.flags = 0;
4473 	    syn_opt_arg.keyword = TRUE;
4474 	    syn_opt_arg.sync_idx = NULL;
4475 	    syn_opt_arg.has_cont_list = FALSE;
4476 	    syn_opt_arg.cont_in_list = NULL;
4477 	    syn_opt_arg.next_list = NULL;
4478 
4479 	    /*
4480 	     * The options given apply to ALL keywords, so all options must be
4481 	     * found before keywords can be created.
4482 	     * 1: collect the options and copy the keywords to keyword_copy.
4483 	     */
4484 	    cnt = 0;
4485 	    p = keyword_copy;
4486 	    for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
4487 	    {
4488 		rest = get_syn_options(rest, &syn_opt_arg);
4489 		if (rest == NULL || ends_excmd(*rest))
4490 		    break;
4491 		/* Copy the keyword, removing backslashes, and add a NUL. */
4492 		while (*rest != NUL && !vim_iswhite(*rest))
4493 		{
4494 		    if (*rest == '\\' && rest[1] != NUL)
4495 			++rest;
4496 		    *p++ = *rest++;
4497 		}
4498 		*p++ = NUL;
4499 		++cnt;
4500 	    }
4501 
4502 	    if (!eap->skip)
4503 	    {
4504 		/* Adjust flags for use of ":syn include". */
4505 		syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4506 
4507 		/*
4508 		 * 2: Add an entry for each keyword.
4509 		 */
4510 		for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4511 		{
4512 		    for (p = vim_strchr(kw, '['); ; )
4513 		    {
4514 			if (p != NULL)
4515 			    *p = NUL;
4516 			add_keyword(kw, syn_id, syn_opt_arg.flags,
4517 						     syn_opt_arg.cont_in_list,
4518 						       syn_opt_arg.next_list);
4519 			if (p == NULL)
4520 			    break;
4521 			if (p[1] == NUL)
4522 			{
4523 			    EMSG2(_("E747: Missing ']': %s"), kw);
4524 			    kw = p + 2;		/* skip over the NUL */
4525 			    break;
4526 			}
4527 			if (p[1] == ']')
4528 			{
4529 			    kw = p + 1;		/* skip over the "]" */
4530 			    break;
4531 			}
4532 #ifdef FEAT_MBYTE
4533 			if (has_mbyte)
4534 			{
4535 			    int l = (*mb_ptr2len)(p + 1);
4536 
4537 			    mch_memmove(p, p + 1, l);
4538 			    p += l;
4539 			}
4540 			else
4541 #endif
4542 			{
4543 			    p[0] = p[1];
4544 			    ++p;
4545 			}
4546 		    }
4547 		}
4548 	    }
4549 
4550 	    vim_free(keyword_copy);
4551 	    vim_free(syn_opt_arg.cont_in_list);
4552 	    vim_free(syn_opt_arg.next_list);
4553 	}
4554     }
4555 
4556     if (rest != NULL)
4557 	eap->nextcmd = check_nextcmd(rest);
4558     else
4559 	EMSG2(_(e_invarg2), arg);
4560 
4561     redraw_curbuf_later(NOT_VALID);
4562     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
4563 }
4564 
4565 /*
4566  * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4567  *
4568  * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4569  */
4570     static void
4571 syn_cmd_match(eap, syncing)
4572     exarg_T	*eap;
4573     int		syncing;	    /* TRUE for ":syntax sync match .. " */
4574 {
4575     char_u	*arg = eap->arg;
4576     char_u	*group_name_end;
4577     char_u	*rest;
4578     synpat_T	item;		/* the item found in the line */
4579     int		syn_id;
4580     int		idx;
4581     syn_opt_arg_T syn_opt_arg;
4582     int		sync_idx = 0;
4583 
4584     /* Isolate the group name, check for validity */
4585     rest = get_group_name(arg, &group_name_end);
4586 
4587     /* Get options before the pattern */
4588     syn_opt_arg.flags = 0;
4589     syn_opt_arg.keyword = FALSE;
4590     syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4591     syn_opt_arg.has_cont_list = TRUE;
4592     syn_opt_arg.cont_list = NULL;
4593     syn_opt_arg.cont_in_list = NULL;
4594     syn_opt_arg.next_list = NULL;
4595     rest = get_syn_options(rest, &syn_opt_arg);
4596 
4597     /* get the pattern. */
4598     init_syn_patterns();
4599     vim_memset(&item, 0, sizeof(item));
4600     rest = get_syn_pattern(rest, &item);
4601     if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4602 	syn_opt_arg.flags |= HL_HAS_EOL;
4603 
4604     /* Get options after the pattern */
4605     rest = get_syn_options(rest, &syn_opt_arg);
4606 
4607     if (rest != NULL)		/* all arguments are valid */
4608     {
4609 	/*
4610 	 * Check for trailing command and illegal trailing arguments.
4611 	 */
4612 	eap->nextcmd = check_nextcmd(rest);
4613 	if (!ends_excmd(*rest) || eap->skip)
4614 	    rest = NULL;
4615 	else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4616 		&& (syn_id = syn_check_group(arg,
4617 					   (int)(group_name_end - arg))) != 0)
4618 	{
4619 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4620 	    /*
4621 	     * Store the pattern in the syn_items list
4622 	     */
4623 	    idx = curbuf->b_syn_patterns.ga_len;
4624 	    SYN_ITEMS(curbuf)[idx] = item;
4625 	    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4626 	    SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4627 	    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4628 	    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4629 	    SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
4630 	    SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
4631 	    SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4632 	    SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4633 						     syn_opt_arg.cont_in_list;
4634 	    if (syn_opt_arg.cont_in_list != NULL)
4635 		curbuf->b_syn_containedin = TRUE;
4636 	    SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
4637 	    ++curbuf->b_syn_patterns.ga_len;
4638 
4639 	    /* remember that we found a match for syncing on */
4640 	    if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4641 		curbuf->b_syn_sync_flags |= SF_MATCH;
4642 #ifdef FEAT_FOLDING
4643 	    if (syn_opt_arg.flags & HL_FOLD)
4644 		++curbuf->b_syn_folditems;
4645 #endif
4646 
4647 	    redraw_curbuf_later(NOT_VALID);
4648 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4649 	    return;	/* don't free the progs and patterns now */
4650 	}
4651     }
4652 
4653     /*
4654      * Something failed, free the allocated memory.
4655      */
4656     vim_free(item.sp_prog);
4657     vim_free(item.sp_pattern);
4658     vim_free(syn_opt_arg.cont_list);
4659     vim_free(syn_opt_arg.cont_in_list);
4660     vim_free(syn_opt_arg.next_list);
4661 
4662     if (rest == NULL)
4663 	EMSG2(_(e_invarg2), arg);
4664 }
4665 
4666 /*
4667  * Handle ":syntax region {group-name} [matchgroup={group-name}]
4668  *		start {start} .. [skip {skip}] end {end} .. [{options}]".
4669  */
4670     static void
4671 syn_cmd_region(eap, syncing)
4672     exarg_T	*eap;
4673     int		syncing;	    /* TRUE for ":syntax sync region .." */
4674 {
4675     char_u		*arg = eap->arg;
4676     char_u		*group_name_end;
4677     char_u		*rest;			/* next arg, NULL on error */
4678     char_u		*key_end;
4679     char_u		*key = NULL;
4680     char_u		*p;
4681     int			item;
4682 #define ITEM_START	    0
4683 #define ITEM_SKIP	    1
4684 #define ITEM_END	    2
4685 #define ITEM_MATCHGROUP	    3
4686     struct pat_ptr
4687     {
4688 	synpat_T	*pp_synp;		/* pointer to syn_pattern */
4689 	int		pp_matchgroup_id;	/* matchgroup ID */
4690 	struct pat_ptr	*pp_next;		/* pointer to next pat_ptr */
4691     }			*(pat_ptrs[3]);
4692 					/* patterns found in the line */
4693     struct pat_ptr	*ppp;
4694     struct pat_ptr	*ppp_next;
4695     int			pat_count = 0;		/* nr of syn_patterns found */
4696     int			syn_id;
4697     int			matchgroup_id = 0;
4698     int			not_enough = FALSE;	/* not enough arguments */
4699     int			illegal = FALSE;	/* illegal arguments */
4700     int			success = FALSE;
4701     int			idx;
4702     syn_opt_arg_T	syn_opt_arg;
4703 
4704     /* Isolate the group name, check for validity */
4705     rest = get_group_name(arg, &group_name_end);
4706 
4707     pat_ptrs[0] = NULL;
4708     pat_ptrs[1] = NULL;
4709     pat_ptrs[2] = NULL;
4710 
4711     init_syn_patterns();
4712 
4713     syn_opt_arg.flags = 0;
4714     syn_opt_arg.keyword = FALSE;
4715     syn_opt_arg.sync_idx = NULL;
4716     syn_opt_arg.has_cont_list = TRUE;
4717     syn_opt_arg.cont_list = NULL;
4718     syn_opt_arg.cont_in_list = NULL;
4719     syn_opt_arg.next_list = NULL;
4720 
4721     /*
4722      * get the options, patterns and matchgroup.
4723      */
4724     while (rest != NULL && !ends_excmd(*rest))
4725     {
4726 	/* Check for option arguments */
4727 	rest = get_syn_options(rest, &syn_opt_arg);
4728 	if (rest == NULL || ends_excmd(*rest))
4729 	    break;
4730 
4731 	/* must be a pattern or matchgroup then */
4732 	key_end = rest;
4733 	while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4734 	    ++key_end;
4735 	vim_free(key);
4736 	key = vim_strnsave_up(rest, (int)(key_end - rest));
4737 	if (key == NULL)			/* out of memory */
4738 	{
4739 	    rest = NULL;
4740 	    break;
4741 	}
4742 	if (STRCMP(key, "MATCHGROUP") == 0)
4743 	    item = ITEM_MATCHGROUP;
4744 	else if (STRCMP(key, "START") == 0)
4745 	    item = ITEM_START;
4746 	else if (STRCMP(key, "END") == 0)
4747 	    item = ITEM_END;
4748 	else if (STRCMP(key, "SKIP") == 0)
4749 	{
4750 	    if (pat_ptrs[ITEM_SKIP] != NULL)	/* one skip pattern allowed */
4751 	    {
4752 		illegal = TRUE;
4753 		break;
4754 	    }
4755 	    item = ITEM_SKIP;
4756 	}
4757 	else
4758 	    break;
4759 	rest = skipwhite(key_end);
4760 	if (*rest != '=')
4761 	{
4762 	    rest = NULL;
4763 	    EMSG2(_("E398: Missing '=': %s"), arg);
4764 	    break;
4765 	}
4766 	rest = skipwhite(rest + 1);
4767 	if (*rest == NUL)
4768 	{
4769 	    not_enough = TRUE;
4770 	    break;
4771 	}
4772 
4773 	if (item == ITEM_MATCHGROUP)
4774 	{
4775 	    p = skiptowhite(rest);
4776 	    if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4777 		matchgroup_id = 0;
4778 	    else
4779 	    {
4780 		matchgroup_id = syn_check_group(rest, (int)(p - rest));
4781 		if (matchgroup_id == 0)
4782 		{
4783 		    illegal = TRUE;
4784 		    break;
4785 		}
4786 	    }
4787 	    rest = skipwhite(p);
4788 	}
4789 	else
4790 	{
4791 	    /*
4792 	     * Allocate room for a syn_pattern, and link it in the list of
4793 	     * syn_patterns for this item, at the start (because the list is
4794 	     * used from end to start).
4795 	     */
4796 	    ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4797 	    if (ppp == NULL)
4798 	    {
4799 		rest = NULL;
4800 		break;
4801 	    }
4802 	    ppp->pp_next = pat_ptrs[item];
4803 	    pat_ptrs[item] = ppp;
4804 	    ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4805 	    if (ppp->pp_synp == NULL)
4806 	    {
4807 		rest = NULL;
4808 		break;
4809 	    }
4810 
4811 	    /*
4812 	     * Get the syntax pattern and the following offset(s).
4813 	     */
4814 	    /* Enable the appropriate \z specials. */
4815 	    if (item == ITEM_START)
4816 		reg_do_extmatch = REX_SET;
4817 	    else if (item == ITEM_SKIP || item == ITEM_END)
4818 		reg_do_extmatch = REX_USE;
4819 	    rest = get_syn_pattern(rest, ppp->pp_synp);
4820 	    reg_do_extmatch = 0;
4821 	    if (item == ITEM_END && vim_regcomp_had_eol()
4822 				       && !(syn_opt_arg.flags & HL_EXCLUDENL))
4823 		ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4824 	    ppp->pp_matchgroup_id = matchgroup_id;
4825 	    ++pat_count;
4826 	}
4827     }
4828     vim_free(key);
4829     if (illegal || not_enough)
4830 	rest = NULL;
4831 
4832     /*
4833      * Must have a "start" and "end" pattern.
4834      */
4835     if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4836 						  pat_ptrs[ITEM_END] == NULL))
4837     {
4838 	not_enough = TRUE;
4839 	rest = NULL;
4840     }
4841 
4842     if (rest != NULL)
4843     {
4844 	/*
4845 	 * Check for trailing garbage or command.
4846 	 * If OK, add the item.
4847 	 */
4848 	eap->nextcmd = check_nextcmd(rest);
4849 	if (!ends_excmd(*rest) || eap->skip)
4850 	    rest = NULL;
4851 	else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4852 		&& (syn_id = syn_check_group(arg,
4853 					   (int)(group_name_end - arg))) != 0)
4854 	{
4855 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4856 	    /*
4857 	     * Store the start/skip/end in the syn_items list
4858 	     */
4859 	    idx = curbuf->b_syn_patterns.ga_len;
4860 	    for (item = ITEM_START; item <= ITEM_END; ++item)
4861 	    {
4862 		for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4863 		{
4864 		    SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4865 		    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4866 		    SYN_ITEMS(curbuf)[idx].sp_type =
4867 			    (item == ITEM_START) ? SPTYPE_START :
4868 			    (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
4869 		    SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
4870 		    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4871 		    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4872 		    SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4873 							ppp->pp_matchgroup_id;
4874 		    if (item == ITEM_START)
4875 		    {
4876 			SYN_ITEMS(curbuf)[idx].sp_cont_list =
4877 							syn_opt_arg.cont_list;
4878 			SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4879 						     syn_opt_arg.cont_in_list;
4880 			if (syn_opt_arg.cont_in_list != NULL)
4881 			    curbuf->b_syn_containedin = TRUE;
4882 			SYN_ITEMS(curbuf)[idx].sp_next_list =
4883 							syn_opt_arg.next_list;
4884 		    }
4885 		    ++curbuf->b_syn_patterns.ga_len;
4886 		    ++idx;
4887 #ifdef FEAT_FOLDING
4888 		    if (syn_opt_arg.flags & HL_FOLD)
4889 			++curbuf->b_syn_folditems;
4890 #endif
4891 		}
4892 	    }
4893 
4894 	    redraw_curbuf_later(NOT_VALID);
4895 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4896 	    success = TRUE;	    /* don't free the progs and patterns now */
4897 	}
4898     }
4899 
4900     /*
4901      * Free the allocated memory.
4902      */
4903     for (item = ITEM_START; item <= ITEM_END; ++item)
4904 	for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4905 	{
4906 	    if (!success)
4907 	    {
4908 		vim_free(ppp->pp_synp->sp_prog);
4909 		vim_free(ppp->pp_synp->sp_pattern);
4910 	    }
4911 	    vim_free(ppp->pp_synp);
4912 	    ppp_next = ppp->pp_next;
4913 	    vim_free(ppp);
4914 	}
4915 
4916     if (!success)
4917     {
4918 	vim_free(syn_opt_arg.cont_list);
4919 	vim_free(syn_opt_arg.cont_in_list);
4920 	vim_free(syn_opt_arg.next_list);
4921 	if (not_enough)
4922 	    EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4923 	else if (illegal || rest == NULL)
4924 	    EMSG2(_(e_invarg2), arg);
4925     }
4926 }
4927 
4928 /*
4929  * A simple syntax group ID comparison function suitable for use in qsort()
4930  */
4931     static int
4932 #ifdef __BORLANDC__
4933 _RTLENTRYF
4934 #endif
4935 syn_compare_stub(v1, v2)
4936     const void	*v1;
4937     const void	*v2;
4938 {
4939     const short	*s1 = v1;
4940     const short	*s2 = v2;
4941 
4942     return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4943 }
4944 
4945 /*
4946  * Combines lists of syntax clusters.
4947  * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4948  */
4949     static void
4950 syn_combine_list(clstr1, clstr2, list_op)
4951     short	**clstr1;
4952     short	**clstr2;
4953     int		list_op;
4954 {
4955     int		count1 = 0;
4956     int		count2 = 0;
4957     short	*g1;
4958     short	*g2;
4959     short	*clstr = NULL;
4960     int		count;
4961     int		round;
4962 
4963     /*
4964      * Handle degenerate cases.
4965      */
4966     if (*clstr2 == NULL)
4967 	return;
4968     if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4969     {
4970 	if (list_op == CLUSTER_REPLACE)
4971 	    vim_free(*clstr1);
4972 	if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
4973 	    *clstr1 = *clstr2;
4974 	else
4975 	    vim_free(*clstr2);
4976 	return;
4977     }
4978 
4979     for (g1 = *clstr1; *g1; g1++)
4980 	++count1;
4981     for (g2 = *clstr2; *g2; g2++)
4982 	++count2;
4983 
4984     /*
4985      * For speed purposes, sort both lists.
4986      */
4987     qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
4988     qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
4989 
4990     /*
4991      * We proceed in two passes; in round 1, we count the elements to place
4992      * in the new list, and in round 2, we allocate and populate the new
4993      * list.  For speed, we use a mergesort-like method, adding the smaller
4994      * of the current elements in each list to the new list.
4995      */
4996     for (round = 1; round <= 2; round++)
4997     {
4998 	g1 = *clstr1;
4999 	g2 = *clstr2;
5000 	count = 0;
5001 
5002 	/*
5003 	 * First, loop through the lists until one of them is empty.
5004 	 */
5005 	while (*g1 && *g2)
5006 	{
5007 	    /*
5008 	     * We always want to add from the first list.
5009 	     */
5010 	    if (*g1 < *g2)
5011 	    {
5012 		if (round == 2)
5013 		    clstr[count] = *g1;
5014 		count++;
5015 		g1++;
5016 		continue;
5017 	    }
5018 	    /*
5019 	     * We only want to add from the second list if we're adding the
5020 	     * lists.
5021 	     */
5022 	    if (list_op == CLUSTER_ADD)
5023 	    {
5024 		if (round == 2)
5025 		    clstr[count] = *g2;
5026 		count++;
5027 	    }
5028 	    if (*g1 == *g2)
5029 		g1++;
5030 	    g2++;
5031 	}
5032 
5033 	/*
5034 	 * Now add the leftovers from whichever list didn't get finished
5035 	 * first.  As before, we only want to add from the second list if
5036 	 * we're adding the lists.
5037 	 */
5038 	for (; *g1; g1++, count++)
5039 	    if (round == 2)
5040 		clstr[count] = *g1;
5041 	if (list_op == CLUSTER_ADD)
5042 	    for (; *g2; g2++, count++)
5043 		if (round == 2)
5044 		    clstr[count] = *g2;
5045 
5046 	if (round == 1)
5047 	{
5048 	    /*
5049 	     * If the group ended up empty, we don't need to allocate any
5050 	     * space for it.
5051 	     */
5052 	    if (count == 0)
5053 	    {
5054 		clstr = NULL;
5055 		break;
5056 	    }
5057 	    clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5058 	    if (clstr == NULL)
5059 		break;
5060 	    clstr[count] = 0;
5061 	}
5062     }
5063 
5064     /*
5065      * Finally, put the new list in place.
5066      */
5067     vim_free(*clstr1);
5068     vim_free(*clstr2);
5069     *clstr1 = clstr;
5070 }
5071 
5072 /*
5073  * Lookup a syntax cluster name and return it's ID.
5074  * If it is not found, 0 is returned.
5075  */
5076     static int
5077 syn_scl_name2id(name)
5078     char_u	*name;
5079 {
5080     int		i;
5081     char_u	*name_u;
5082 
5083     /* Avoid using stricmp() too much, it's slow on some systems */
5084     name_u = vim_strsave_up(name);
5085     if (name_u == NULL)
5086 	return 0;
5087     for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5088 	if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5089 		&& STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5090 	    break;
5091     vim_free(name_u);
5092     return (i < 0 ? 0 : i + SYNID_CLUSTER);
5093 }
5094 
5095 /*
5096  * Like syn_scl_name2id(), but take a pointer + length argument.
5097  */
5098     static int
5099 syn_scl_namen2id(linep, len)
5100     char_u  *linep;
5101     int	    len;
5102 {
5103     char_u  *name;
5104     int	    id = 0;
5105 
5106     name = vim_strnsave(linep, len);
5107     if (name != NULL)
5108     {
5109 	id = syn_scl_name2id(name);
5110 	vim_free(name);
5111     }
5112     return id;
5113 }
5114 
5115 /*
5116  * Find syntax cluster name in the table and return it's ID.
5117  * The argument is a pointer to the name and the length of the name.
5118  * If it doesn't exist yet, a new entry is created.
5119  * Return 0 for failure.
5120  */
5121     static int
5122 syn_check_cluster(pp, len)
5123     char_u	*pp;
5124     int		len;
5125 {
5126     int		id;
5127     char_u	*name;
5128 
5129     name = vim_strnsave(pp, len);
5130     if (name == NULL)
5131 	return 0;
5132 
5133     id = syn_scl_name2id(name);
5134     if (id == 0)			/* doesn't exist yet */
5135 	id = syn_add_cluster(name);
5136     else
5137 	vim_free(name);
5138     return id;
5139 }
5140 
5141 /*
5142  * Add new syntax cluster and return it's ID.
5143  * "name" must be an allocated string, it will be consumed.
5144  * Return 0 for failure.
5145  */
5146     static int
5147 syn_add_cluster(name)
5148     char_u	*name;
5149 {
5150     int		len;
5151 
5152     /*
5153      * First call for this growarray: init growing array.
5154      */
5155     if (curbuf->b_syn_clusters.ga_data == NULL)
5156     {
5157 	curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5158 	curbuf->b_syn_clusters.ga_growsize = 10;
5159     }
5160 
5161     /*
5162      * Make room for at least one other cluster entry.
5163      */
5164     if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5165     {
5166 	vim_free(name);
5167 	return 0;
5168     }
5169     len = curbuf->b_syn_clusters.ga_len;
5170 
5171     vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
5172     SYN_CLSTR(curbuf)[len].scl_name = name;
5173     SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5174     SYN_CLSTR(curbuf)[len].scl_list = NULL;
5175     ++curbuf->b_syn_clusters.ga_len;
5176 
5177     if (STRICMP(name, "Spell") == 0)
5178 	curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
5179     if (STRICMP(name, "NoSpell") == 0)
5180 	curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
5181 
5182     return len + SYNID_CLUSTER;
5183 }
5184 
5185 /*
5186  * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5187  *		[add={groupname},..] [remove={groupname},..]".
5188  */
5189 /* ARGSUSED */
5190     static void
5191 syn_cmd_cluster(eap, syncing)
5192     exarg_T	*eap;
5193     int		syncing;	    /* not used */
5194 {
5195     char_u	*arg = eap->arg;
5196     char_u	*group_name_end;
5197     char_u	*rest;
5198     int		scl_id;
5199     short	*clstr_list;
5200     int		got_clstr = FALSE;
5201     int		opt_len;
5202     int		list_op;
5203 
5204     eap->nextcmd = find_nextcmd(arg);
5205     if (eap->skip)
5206 	return;
5207 
5208     rest = get_group_name(arg, &group_name_end);
5209 
5210     if (rest != NULL)
5211     {
5212 	scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
5213 							      - SYNID_CLUSTER;
5214 
5215 	for (;;)
5216 	{
5217 	    if (STRNICMP(rest, "add", 3) == 0
5218 		    && (vim_iswhite(rest[3]) || rest[3] == '='))
5219 	    {
5220 		opt_len = 3;
5221 		list_op = CLUSTER_ADD;
5222 	    }
5223 	    else if (STRNICMP(rest, "remove", 6) == 0
5224 		    && (vim_iswhite(rest[6]) || rest[6] == '='))
5225 	    {
5226 		opt_len = 6;
5227 		list_op = CLUSTER_SUBTRACT;
5228 	    }
5229 	    else if (STRNICMP(rest, "contains", 8) == 0
5230 			&& (vim_iswhite(rest[8]) || rest[8] == '='))
5231 	    {
5232 		opt_len = 8;
5233 		list_op = CLUSTER_REPLACE;
5234 	    }
5235 	    else
5236 		break;
5237 
5238 	    clstr_list = NULL;
5239 	    if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5240 	    {
5241 		EMSG2(_(e_invarg2), rest);
5242 		break;
5243 	    }
5244 	    syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5245 			     &clstr_list, list_op);
5246 	    got_clstr = TRUE;
5247 	}
5248 
5249 	if (got_clstr)
5250 	{
5251 	    redraw_curbuf_later(NOT_VALID);
5252 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5253 	}
5254     }
5255 
5256     if (!got_clstr)
5257 	EMSG(_("E400: No cluster specified"));
5258     if (rest == NULL || !ends_excmd(*rest))
5259 	EMSG2(_(e_invarg2), arg);
5260 }
5261 
5262 /*
5263  * On first call for current buffer: Init growing array.
5264  */
5265     static void
5266 init_syn_patterns()
5267 {
5268     curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5269     curbuf->b_syn_patterns.ga_growsize = 10;
5270 }
5271 
5272 /*
5273  * Get one pattern for a ":syntax match" or ":syntax region" command.
5274  * Stores the pattern and program in a synpat_T.
5275  * Returns a pointer to the next argument, or NULL in case of an error.
5276  */
5277     static char_u *
5278 get_syn_pattern(arg, ci)
5279     char_u	*arg;
5280     synpat_T	*ci;
5281 {
5282     char_u	*end;
5283     int		*p;
5284     int		idx;
5285     char_u	*cpo_save;
5286 
5287     /* need at least three chars */
5288     if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5289 	return NULL;
5290 
5291     end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5292     if (*end != *arg)			    /* end delimiter not found */
5293     {
5294 	EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5295 	return NULL;
5296     }
5297     /* store the pattern and compiled regexp program */
5298     if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5299 	return NULL;
5300 
5301     /* Make 'cpoptions' empty, to avoid the 'l' flag */
5302     cpo_save = p_cpo;
5303     p_cpo = (char_u *)"";
5304     ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5305     p_cpo = cpo_save;
5306 
5307     if (ci->sp_prog == NULL)
5308 	return NULL;
5309     ci->sp_ic = curbuf->b_syn_ic;
5310 
5311     /*
5312      * Check for a match, highlight or region offset.
5313      */
5314     ++end;
5315     do
5316     {
5317 	for (idx = SPO_COUNT; --idx >= 0; )
5318 	    if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5319 		break;
5320 	if (idx >= 0)
5321 	{
5322 	    p = &(ci->sp_offsets[idx]);
5323 	    if (idx != SPO_LC_OFF)
5324 		switch (end[3])
5325 		{
5326 		    case 's':   break;
5327 		    case 'b':   break;
5328 		    case 'e':   idx += SPO_COUNT; break;
5329 		    default:    idx = -1; break;
5330 		}
5331 	    if (idx >= 0)
5332 	    {
5333 		ci->sp_off_flags |= (1 << idx);
5334 		if (idx == SPO_LC_OFF)	    /* lc=99 */
5335 		{
5336 		    end += 3;
5337 		    *p = getdigits(&end);
5338 
5339 		    /* "lc=" offset automatically sets "ms=" offset */
5340 		    if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5341 		    {
5342 			ci->sp_off_flags |= (1 << SPO_MS_OFF);
5343 			ci->sp_offsets[SPO_MS_OFF] = *p;
5344 		    }
5345 		}
5346 		else			    /* yy=x+99 */
5347 		{
5348 		    end += 4;
5349 		    if (*end == '+')
5350 		    {
5351 			++end;
5352 			*p = getdigits(&end);		/* positive offset */
5353 		    }
5354 		    else if (*end == '-')
5355 		    {
5356 			++end;
5357 			*p = -getdigits(&end);		/* negative offset */
5358 		    }
5359 		}
5360 		if (*end != ',')
5361 		    break;
5362 		++end;
5363 	    }
5364 	}
5365     } while (idx >= 0);
5366 
5367     if (!ends_excmd(*end) && !vim_iswhite(*end))
5368     {
5369 	EMSG2(_("E402: Garbage after pattern: %s"), arg);
5370 	return NULL;
5371     }
5372     return skipwhite(end);
5373 }
5374 
5375 /*
5376  * Handle ":syntax sync .." command.
5377  */
5378 /* ARGSUSED */
5379     static void
5380 syn_cmd_sync(eap, syncing)
5381     exarg_T	*eap;
5382     int		syncing;	    /* not used */
5383 {
5384     char_u	*arg_start = eap->arg;
5385     char_u	*arg_end;
5386     char_u	*key = NULL;
5387     char_u	*next_arg;
5388     int		illegal = FALSE;
5389     int		finished = FALSE;
5390     long	n;
5391     char_u	*cpo_save;
5392 
5393     if (ends_excmd(*arg_start))
5394     {
5395 	syn_cmd_list(eap, TRUE);
5396 	return;
5397     }
5398 
5399     while (!ends_excmd(*arg_start))
5400     {
5401 	arg_end = skiptowhite(arg_start);
5402 	next_arg = skipwhite(arg_end);
5403 	vim_free(key);
5404 	key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5405 	if (STRCMP(key, "CCOMMENT") == 0)
5406 	{
5407 	    if (!eap->skip)
5408 		curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5409 	    if (!ends_excmd(*next_arg))
5410 	    {
5411 		arg_end = skiptowhite(next_arg);
5412 		if (!eap->skip)
5413 		    curbuf->b_syn_sync_id = syn_check_group(next_arg,
5414 						   (int)(arg_end - next_arg));
5415 		next_arg = skipwhite(arg_end);
5416 	    }
5417 	    else if (!eap->skip)
5418 		curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5419 	}
5420 	else if (  STRNCMP(key, "LINES", 5) == 0
5421 		|| STRNCMP(key, "MINLINES", 8) == 0
5422 		|| STRNCMP(key, "MAXLINES", 8) == 0
5423 		|| STRNCMP(key, "LINEBREAKS", 10) == 0)
5424 	{
5425 	    if (key[4] == 'S')
5426 		arg_end = key + 6;
5427 	    else if (key[0] == 'L')
5428 		arg_end = key + 11;
5429 	    else
5430 		arg_end = key + 9;
5431 	    if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5432 	    {
5433 		illegal = TRUE;
5434 		break;
5435 	    }
5436 	    n = getdigits(&arg_end);
5437 	    if (!eap->skip)
5438 	    {
5439 		if (key[4] == 'B')
5440 		    curbuf->b_syn_sync_linebreaks = n;
5441 		else if (key[1] == 'A')
5442 		    curbuf->b_syn_sync_maxlines = n;
5443 		else
5444 		    curbuf->b_syn_sync_minlines = n;
5445 	    }
5446 	}
5447 	else if (STRCMP(key, "FROMSTART") == 0)
5448 	{
5449 	    if (!eap->skip)
5450 	    {
5451 		curbuf->b_syn_sync_minlines = MAXLNUM;
5452 		curbuf->b_syn_sync_maxlines = 0;
5453 	    }
5454 	}
5455 	else if (STRCMP(key, "LINECONT") == 0)
5456 	{
5457 	    if (curbuf->b_syn_linecont_pat != NULL)
5458 	    {
5459 		EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5460 		finished = TRUE;
5461 		break;
5462 	    }
5463 	    arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5464 	    if (*arg_end != *next_arg)	    /* end delimiter not found */
5465 	    {
5466 		illegal = TRUE;
5467 		break;
5468 	    }
5469 
5470 	    if (!eap->skip)
5471 	    {
5472 		/* store the pattern and compiled regexp program */
5473 		if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5474 				      (int)(arg_end - next_arg - 1))) == NULL)
5475 		{
5476 		    finished = TRUE;
5477 		    break;
5478 		}
5479 		curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5480 
5481 		/* Make 'cpoptions' empty, to avoid the 'l' flag */
5482 		cpo_save = p_cpo;
5483 		p_cpo = (char_u *)"";
5484 		curbuf->b_syn_linecont_prog =
5485 			    vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5486 		p_cpo = cpo_save;
5487 
5488 		if (curbuf->b_syn_linecont_prog == NULL)
5489 		{
5490 		    vim_free(curbuf->b_syn_linecont_pat);
5491 		    curbuf->b_syn_linecont_pat = NULL;
5492 		    finished = TRUE;
5493 		    break;
5494 		}
5495 	    }
5496 	    next_arg = skipwhite(arg_end + 1);
5497 	}
5498 	else
5499 	{
5500 	    eap->arg = next_arg;
5501 	    if (STRCMP(key, "MATCH") == 0)
5502 		syn_cmd_match(eap, TRUE);
5503 	    else if (STRCMP(key, "REGION") == 0)
5504 		syn_cmd_region(eap, TRUE);
5505 	    else if (STRCMP(key, "CLEAR") == 0)
5506 		syn_cmd_clear(eap, TRUE);
5507 	    else
5508 		illegal = TRUE;
5509 	    finished = TRUE;
5510 	    break;
5511 	}
5512 	arg_start = next_arg;
5513     }
5514     vim_free(key);
5515     if (illegal)
5516 	EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5517     else if (!finished)
5518     {
5519 	eap->nextcmd = check_nextcmd(arg_start);
5520 	redraw_curbuf_later(NOT_VALID);
5521 	syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5522     }
5523 }
5524 
5525 /*
5526  * Convert a line of highlight group names into a list of group ID numbers.
5527  * "arg" should point to the "contains" or "nextgroup" keyword.
5528  * "arg" is advanced to after the last group name.
5529  * Careful: the argument is modified (NULs added).
5530  * returns FAIL for some error, OK for success.
5531  */
5532     static int
5533 get_id_list(arg, keylen, list)
5534     char_u	**arg;
5535     int		keylen;		/* length of keyword */
5536     short	**list;		/* where to store the resulting list, if not
5537 				   NULL, the list is silently skipped! */
5538 {
5539     char_u	*p = NULL;
5540     char_u	*end;
5541     int		round;
5542     int		count;
5543     int		total_count = 0;
5544     short	*retval = NULL;
5545     char_u	*name;
5546     regmatch_T	regmatch;
5547     int		id;
5548     int		i;
5549     int		failed = FALSE;
5550 
5551     /*
5552      * We parse the list twice:
5553      * round == 1: count the number of items, allocate the array.
5554      * round == 2: fill the array with the items.
5555      * In round 1 new groups may be added, causing the number of items to
5556      * grow when a regexp is used.  In that case round 1 is done once again.
5557      */
5558     for (round = 1; round <= 2; ++round)
5559     {
5560 	/*
5561 	 * skip "contains"
5562 	 */
5563 	p = skipwhite(*arg + keylen);
5564 	if (*p != '=')
5565 	{
5566 	    EMSG2(_("E405: Missing equal sign: %s"), *arg);
5567 	    break;
5568 	}
5569 	p = skipwhite(p + 1);
5570 	if (ends_excmd(*p))
5571 	{
5572 	    EMSG2(_("E406: Empty argument: %s"), *arg);
5573 	    break;
5574 	}
5575 
5576 	/*
5577 	 * parse the arguments after "contains"
5578 	 */
5579 	count = 0;
5580 	while (!ends_excmd(*p))
5581 	{
5582 	    for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5583 		;
5584 	    name = alloc((int)(end - p + 3));	    /* leave room for "^$" */
5585 	    if (name == NULL)
5586 	    {
5587 		failed = TRUE;
5588 		break;
5589 	    }
5590 	    vim_strncpy(name + 1, p, end - p);
5591 	    if (       STRCMP(name + 1, "ALLBUT") == 0
5592 		    || STRCMP(name + 1, "ALL") == 0
5593 		    || STRCMP(name + 1, "TOP") == 0
5594 		    || STRCMP(name + 1, "CONTAINED") == 0)
5595 	    {
5596 		if (TOUPPER_ASC(**arg) != 'C')
5597 		{
5598 		    EMSG2(_("E407: %s not allowed here"), name + 1);
5599 		    failed = TRUE;
5600 		    vim_free(name);
5601 		    break;
5602 		}
5603 		if (count != 0)
5604 		{
5605 		    EMSG2(_("E408: %s must be first in contains list"), name + 1);
5606 		    failed = TRUE;
5607 		    vim_free(name);
5608 		    break;
5609 		}
5610 		if (name[1] == 'A')
5611 		    id = SYNID_ALLBUT;
5612 		else if (name[1] == 'T')
5613 		    id = SYNID_TOP;
5614 		else
5615 		    id = SYNID_CONTAINED;
5616 		id += current_syn_inc_tag;
5617 	    }
5618 	    else if (name[1] == '@')
5619 	    {
5620 		id = syn_check_cluster(name + 2, (int)(end - p - 1));
5621 	    }
5622 	    else
5623 	    {
5624 		/*
5625 		 * Handle full group name.
5626 		 */
5627 		if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5628 		    id = syn_check_group(name + 1, (int)(end - p));
5629 		else
5630 		{
5631 		    /*
5632 		     * Handle match of regexp with group names.
5633 		     */
5634 		    *name = '^';
5635 		    STRCAT(name, "$");
5636 		    regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5637 		    if (regmatch.regprog == NULL)
5638 		    {
5639 			failed = TRUE;
5640 			vim_free(name);
5641 			break;
5642 		    }
5643 
5644 		    regmatch.rm_ic = TRUE;
5645 		    id = 0;
5646 		    for (i = highlight_ga.ga_len; --i >= 0; )
5647 		    {
5648 			if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5649 								  (colnr_T)0))
5650 			{
5651 			    if (round == 2)
5652 			    {
5653 				/* Got more items than expected; can happen
5654 				 * when adding items that match:
5655 				 * "contains=a.*b,axb".
5656 				 * Go back to first round */
5657 				if (count >= total_count)
5658 				{
5659 				    vim_free(retval);
5660 				    round = 1;
5661 				}
5662 				else
5663 				    retval[count] = i + 1;
5664 			    }
5665 			    ++count;
5666 			    id = -1;	    /* remember that we found one */
5667 			}
5668 		    }
5669 		    vim_free(regmatch.regprog);
5670 		}
5671 	    }
5672 	    vim_free(name);
5673 	    if (id == 0)
5674 	    {
5675 		EMSG2(_("E409: Unknown group name: %s"), p);
5676 		failed = TRUE;
5677 		break;
5678 	    }
5679 	    if (id > 0)
5680 	    {
5681 		if (round == 2)
5682 		{
5683 		    /* Got more items than expected, go back to first round */
5684 		    if (count >= total_count)
5685 		    {
5686 			vim_free(retval);
5687 			round = 1;
5688 		    }
5689 		    else
5690 			retval[count] = id;
5691 		}
5692 		++count;
5693 	    }
5694 	    p = skipwhite(end);
5695 	    if (*p != ',')
5696 		break;
5697 	    p = skipwhite(p + 1);	/* skip comma in between arguments */
5698 	}
5699 	if (failed)
5700 	    break;
5701 	if (round == 1)
5702 	{
5703 	    retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5704 	    if (retval == NULL)
5705 		break;
5706 	    retval[count] = 0;	    /* zero means end of the list */
5707 	    total_count = count;
5708 	}
5709     }
5710 
5711     *arg = p;
5712     if (failed || retval == NULL)
5713     {
5714 	vim_free(retval);
5715 	return FAIL;
5716     }
5717 
5718     if (*list == NULL)
5719 	*list = retval;
5720     else
5721 	vim_free(retval);	/* list already found, don't overwrite it */
5722 
5723     return OK;
5724 }
5725 
5726 /*
5727  * Make a copy of an ID list.
5728  */
5729     static short *
5730 copy_id_list(list)
5731     short   *list;
5732 {
5733     int	    len;
5734     int	    count;
5735     short   *retval;
5736 
5737     if (list == NULL)
5738 	return NULL;
5739 
5740     for (count = 0; list[count]; ++count)
5741 	;
5742     len = (count + 1) * sizeof(short);
5743     retval = (short *)alloc((unsigned)len);
5744     if (retval != NULL)
5745 	mch_memmove(retval, list, (size_t)len);
5746 
5747     return retval;
5748 }
5749 
5750 /*
5751  * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5752  * "cur_si" can be NULL if not checking the "containedin" list.
5753  * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5754  * the current item.
5755  * This function is called very often, keep it fast!!
5756  */
5757     static int
5758 in_id_list(cur_si, list, ssp, contained)
5759     stateitem_T	*cur_si;	/* current item or NULL */
5760     short	*list;		/* id list */
5761     struct sp_syn *ssp;		/* group id and ":syn include" tag of group */
5762     int		contained;	/* group id is contained */
5763 {
5764     int		retval;
5765     short	*scl_list;
5766     short	item;
5767     short	id = ssp->id;
5768     static int	depth = 0;
5769     int		r;
5770 
5771     /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
5772     if (cur_si != NULL && ssp->cont_in_list != NULL
5773 					    && !(cur_si->si_flags & HL_MATCH))
5774     {
5775 	/* Ignore transparent items without a contains argument.  Double check
5776 	 * that we don't go back past the first one. */
5777 	while ((cur_si->si_flags & HL_TRANS_CONT)
5778 		&& cur_si > (stateitem_T *)(current_state.ga_data))
5779 	    --cur_si;
5780 	/* cur_si->si_idx is -1 for keywords, these never contain anything. */
5781 	if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5782 		&(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5783 		  SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5784 	    return TRUE;
5785     }
5786 
5787     if (list == NULL)
5788 	return FALSE;
5789 
5790     /*
5791      * If list is ID_LIST_ALL, we are in a transparent item that isn't
5792      * inside anything.  Only allow not-contained groups.
5793      */
5794     if (list == ID_LIST_ALL)
5795 	return !contained;
5796 
5797     /*
5798      * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5799      * contains list.  We also require that "id" is at the same ":syn include"
5800      * level as the list.
5801      */
5802     item = *list;
5803     if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5804     {
5805 	if (item < SYNID_TOP)
5806 	{
5807 	    /* ALL or ALLBUT: accept all groups in the same file */
5808 	    if (item - SYNID_ALLBUT != ssp->inc_tag)
5809 		return FALSE;
5810 	}
5811 	else if (item < SYNID_CONTAINED)
5812 	{
5813 	    /* TOP: accept all not-contained groups in the same file */
5814 	    if (item - SYNID_TOP != ssp->inc_tag || contained)
5815 		return FALSE;
5816 	}
5817 	else
5818 	{
5819 	    /* CONTAINED: accept all contained groups in the same file */
5820 	    if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5821 		return FALSE;
5822 	}
5823 	item = *++list;
5824 	retval = FALSE;
5825     }
5826     else
5827 	retval = TRUE;
5828 
5829     /*
5830      * Return "retval" if id is in the contains list.
5831      */
5832     while (item != 0)
5833     {
5834 	if (item == id)
5835 	    return retval;
5836 	if (item >= SYNID_CLUSTER)
5837 	{
5838 	    scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5839 	    /* restrict recursiveness to 30 to avoid an endless loop for a
5840 	     * cluster that includes itself (indirectly) */
5841 	    if (scl_list != NULL && depth < 30)
5842 	    {
5843 		++depth;
5844 		r = in_id_list(NULL, scl_list, ssp, contained);
5845 		--depth;
5846 		if (r)
5847 		    return retval;
5848 	    }
5849 	}
5850 	item = *++list;
5851     }
5852     return !retval;
5853 }
5854 
5855 struct subcommand
5856 {
5857     char    *name;				/* subcommand name */
5858     void    (*func)__ARGS((exarg_T *, int));	/* function to call */
5859 };
5860 
5861 static struct subcommand subcommands[] =
5862 {
5863     {"case",		syn_cmd_case},
5864     {"clear",		syn_cmd_clear},
5865     {"cluster",		syn_cmd_cluster},
5866     {"enable",		syn_cmd_enable},
5867     {"include",		syn_cmd_include},
5868     {"keyword",		syn_cmd_keyword},
5869     {"list",		syn_cmd_list},
5870     {"manual",		syn_cmd_manual},
5871     {"match",		syn_cmd_match},
5872     {"on",		syn_cmd_on},
5873     {"off",		syn_cmd_off},
5874     {"region",		syn_cmd_region},
5875     {"reset",		syn_cmd_reset},
5876     {"spell",		syn_cmd_spell},
5877     {"sync",		syn_cmd_sync},
5878     {"",		syn_cmd_list},
5879     {NULL, NULL}
5880 };
5881 
5882 /*
5883  * ":syntax".
5884  * This searches the subcommands[] table for the subcommand name, and calls a
5885  * syntax_subcommand() function to do the rest.
5886  */
5887     void
5888 ex_syntax(eap)
5889     exarg_T	*eap;
5890 {
5891     char_u	*arg = eap->arg;
5892     char_u	*subcmd_end;
5893     char_u	*subcmd_name;
5894     int		i;
5895 
5896     syn_cmdlinep = eap->cmdlinep;
5897 
5898     /* isolate subcommand name */
5899     for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5900 	;
5901     subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5902     if (subcmd_name != NULL)
5903     {
5904 	if (eap->skip)		/* skip error messages for all subcommands */
5905 	    ++emsg_skip;
5906 	for (i = 0; ; ++i)
5907 	{
5908 	    if (subcommands[i].name == NULL)
5909 	    {
5910 		EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5911 		break;
5912 	    }
5913 	    if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5914 	    {
5915 		eap->arg = skipwhite(subcmd_end);
5916 		(subcommands[i].func)(eap, FALSE);
5917 		break;
5918 	    }
5919 	}
5920 	vim_free(subcmd_name);
5921 	if (eap->skip)
5922 	    --emsg_skip;
5923     }
5924 }
5925 
5926     int
5927 syntax_present(buf)
5928     buf_T	*buf;
5929 {
5930     return (buf->b_syn_patterns.ga_len != 0
5931 	    || buf->b_syn_clusters.ga_len != 0
5932 	    || curbuf->b_keywtab.ht_used > 0
5933 	    || curbuf->b_keywtab_ic.ht_used > 0);
5934 }
5935 
5936 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5937 
5938 static enum
5939 {
5940     EXP_SUBCMD,	    /* expand ":syn" sub-commands */
5941     EXP_CASE	    /* expand ":syn case" arguments */
5942 } expand_what;
5943 
5944 
5945 /*
5946  * Handle command line completion for :syntax command.
5947  */
5948     void
5949 set_context_in_syntax_cmd(xp, arg)
5950     expand_T	*xp;
5951     char_u	*arg;
5952 {
5953     char_u	*p;
5954 
5955     /* Default: expand subcommands */
5956     xp->xp_context = EXPAND_SYNTAX;
5957     expand_what = EXP_SUBCMD;
5958     xp->xp_pattern = arg;
5959     include_link = FALSE;
5960     include_default = FALSE;
5961 
5962     /* (part of) subcommand already typed */
5963     if (*arg != NUL)
5964     {
5965 	p = skiptowhite(arg);
5966 	if (*p != NUL)		    /* past first word */
5967 	{
5968 	    xp->xp_pattern = skipwhite(p);
5969 	    if (*skiptowhite(xp->xp_pattern) != NUL)
5970 		xp->xp_context = EXPAND_NOTHING;
5971 	    else if (STRNICMP(arg, "case", p - arg) == 0)
5972 		expand_what = EXP_CASE;
5973 	    else if (  STRNICMP(arg, "keyword", p - arg) == 0
5974 		    || STRNICMP(arg, "region", p - arg) == 0
5975 		    || STRNICMP(arg, "match", p - arg) == 0
5976 		    || STRNICMP(arg, "list", p - arg) == 0)
5977 		xp->xp_context = EXPAND_HIGHLIGHT;
5978 	    else
5979 		xp->xp_context = EXPAND_NOTHING;
5980 	}
5981     }
5982 }
5983 
5984 static char *(case_args[]) = {"match", "ignore", NULL};
5985 
5986 /*
5987  * Function given to ExpandGeneric() to obtain the list syntax names for
5988  * expansion.
5989  */
5990 /*ARGSUSED*/
5991     char_u *
5992 get_syntax_name(xp, idx)
5993     expand_T	*xp;
5994     int		idx;
5995 {
5996     if (expand_what == EXP_SUBCMD)
5997 	return (char_u *)subcommands[idx].name;
5998     return (char_u *)case_args[idx];
5999 }
6000 
6001 #endif /* FEAT_CMDL_COMPL */
6002 
6003 /*
6004  * Function called for expression evaluation: get syntax ID at file position.
6005  */
6006     int
6007 syn_get_id(wp, lnum, col, trans, spellp)
6008     win_T	*wp;
6009     long	lnum;
6010     colnr_T	col;
6011     int		trans;	    /* remove transparancy */
6012     int		*spellp;    /* return: can do spell checking */
6013 {
6014     /* When the position is not after the current position and in the same
6015      * line of the same buffer, need to restart parsing. */
6016     if (wp->w_buffer != syn_buf
6017 	    || lnum != current_lnum
6018 	    || col < current_col)
6019 	syntax_start(wp, lnum);
6020 
6021     (void)get_syntax_attr(col, spellp);
6022 
6023     return (trans ? current_trans_id : current_id);
6024 }
6025 
6026 #if defined(FEAT_FOLDING) || defined(PROTO)
6027 /*
6028  * Function called to get folding level for line "lnum" in window "wp".
6029  */
6030     int
6031 syn_get_foldlevel(wp, lnum)
6032     win_T	*wp;
6033     long	lnum;
6034 {
6035     int		level = 0;
6036     int		i;
6037 
6038     /* Return quickly when there are no fold items at all. */
6039     if (wp->w_buffer->b_syn_folditems != 0)
6040     {
6041 	syntax_start(wp, lnum);
6042 
6043 	for (i = 0; i < current_state.ga_len; ++i)
6044 	    if (CUR_STATE(i).si_flags & HL_FOLD)
6045 		++level;
6046     }
6047     if (level > wp->w_p_fdn)
6048 	level = wp->w_p_fdn;
6049     return level;
6050 }
6051 #endif
6052 
6053 #endif /* FEAT_SYN_HL */
6054 
6055 
6056 /**************************************
6057  *  Highlighting stuff		      *
6058  **************************************/
6059 
6060 /*
6061  * The default highlight groups.  These are compiled-in for fast startup and
6062  * they still work when the runtime files can't be found.
6063  * When making changes here, also change runtime/colors/default.vim!
6064  */
6065 static char *(highlight_init_both[]) =
6066     {
6067 #ifdef FEAT_GUI
6068 	"Cursor guibg=fg guifg=bg",
6069 	"lCursor guibg=fg guifg=bg",	/* should be different, but what? */
6070 #endif
6071 	"ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
6072 	"IncSearch term=reverse cterm=reverse gui=reverse",
6073 	"ModeMsg term=bold cterm=bold gui=bold",
6074 	"NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
6075 	"StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
6076 	"StatusLineNC term=reverse cterm=reverse gui=reverse",
6077 	"VertSplit term=reverse cterm=reverse gui=reverse",
6078 	"VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold",
6079 	"DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
6080 	"PmenuThumb cterm=reverse gui=reverse",
6081 	"PmenuSbar ctermbg=Grey guibg=Grey",
6082 	"TabLineSel term=bold cterm=bold gui=bold",
6083 	"TabLineFill term=reverse cterm=reverse gui=reverse",
6084 	NULL
6085     };
6086 
6087 static char *(highlight_init_light[]) =
6088     {
6089 	"Directory term=bold ctermfg=DarkBlue guifg=Blue",
6090 	"LineNr term=underline ctermfg=Brown guifg=Brown",
6091 	"MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6092 	"Normal gui=NONE",
6093 	"Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6094 	"Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
6095 	"SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl",
6096 	"SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl",
6097 	"SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl",
6098 	"SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl",
6099 	"Pmenu ctermbg=LightMagenta guibg=LightMagenta",
6100 	"PmenuSel ctermbg=LightGrey guibg=Grey",
6101 	"SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
6102 	"Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
6103 	"WarningMsg term=standout ctermfg=DarkRed guifg=Red",
6104 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6105 	"Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
6106 	"FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6107 	"SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6108 	"Visual term=reverse ctermbg=Magenta guibg=LightGrey",
6109 	"DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
6110 	"DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
6111 	"DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
6112 	"TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey",
6113 	NULL
6114     };
6115 
6116 static char *(highlight_init_dark[]) =
6117     {
6118 	"Directory term=bold ctermfg=LightCyan guifg=Cyan",
6119 	"LineNr term=underline ctermfg=Yellow guifg=Yellow",
6120 	"MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
6121 	"Normal gui=NONE",
6122 	"Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
6123 	"Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6124 	"SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
6125 	"SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl",
6126 	"SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl",
6127 	"SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl",
6128 	"SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl",
6129 	"Pmenu ctermbg=Magenta guibg=Magenta",
6130 	"PmenuSel ctermbg=Grey guibg=Grey",
6131 	"Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
6132 	"WarningMsg term=standout ctermfg=LightRed guifg=Red",
6133 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6134 	"Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
6135 	"FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6136 	"SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6137 	"Visual term=reverse ctermbg=Magenta guibg=DarkGrey",
6138 	"DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6139 	"DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6140 	"DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6141 	"TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey",
6142 	NULL
6143     };
6144 
6145     void
6146 init_highlight(both, reset)
6147     int		both;	    /* include groups where 'bg' doesn't matter */
6148     int		reset;	    /* clear group first */
6149 {
6150     int		i;
6151     char	**pp;
6152     static int	had_both = FALSE;
6153 #ifdef FEAT_EVAL
6154     char_u	*p;
6155 
6156     /*
6157      * Try finding the color scheme file.  Used when a color file was loaded
6158      * and 'background' or 't_Co' is changed.
6159      */
6160     p = get_var_value((char_u *)"g:colors_name");
6161     if (p != NULL && load_colors(p) == OK)
6162 	return;
6163 #endif
6164 
6165     /*
6166      * Didn't use a color file, use the compiled-in colors.
6167      */
6168     if (both)
6169     {
6170 	had_both = TRUE;
6171 	pp = highlight_init_both;
6172 	for (i = 0; pp[i] != NULL; ++i)
6173 	    do_highlight((char_u *)pp[i], reset, TRUE);
6174     }
6175     else if (!had_both)
6176 	/* Don't do anything before the call with both == TRUE from main().
6177 	 * Not everything has been setup then, and that call will overrule
6178 	 * everything anyway. */
6179 	return;
6180 
6181     if (*p_bg == 'l')
6182 	pp = highlight_init_light;
6183     else
6184 	pp = highlight_init_dark;
6185     for (i = 0; pp[i] != NULL; ++i)
6186 	do_highlight((char_u *)pp[i], reset, TRUE);
6187 
6188     /* Magenta background looks ugly, but grey may not work for 8 colors.
6189      * Thus let it depend on the number of colors available. */
6190     if (t_colors > 8)
6191 	do_highlight((char_u *)(*p_bg == 'l' ? "Visual ctermbg=LightGrey"
6192 				   : "Visual ctermbg=DarkGrey"), reset, TRUE);
6193 
6194 #ifdef FEAT_SYN_HL
6195     /*
6196      * If syntax highlighting is enabled load the highlighting for it.
6197      */
6198     if (get_var_value((char_u *)"g:syntax_on") != NULL)
6199     {
6200 	static int	recursive = 0;
6201 
6202 	if (recursive >= 5)
6203 	    EMSG(_("E679: recursive loop loading syncolor.vim"));
6204 	else
6205 	{
6206 	    ++recursive;
6207 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6208 	    --recursive;
6209 	}
6210     }
6211 #endif
6212 }
6213 
6214 /*
6215  * Load color file "name".
6216  * Return OK for success, FAIL for failure.
6217  */
6218     int
6219 load_colors(name)
6220     char_u	*name;
6221 {
6222     char_u	*buf;
6223     int		retval = FAIL;
6224     static int	recursive = FALSE;
6225 
6226     /* When being called recursively, this is probably because setting
6227      * 'background' caused the highlighting to be reloaded.  This means it is
6228      * working, thus we should return OK. */
6229     if (recursive)
6230 	return OK;
6231 
6232     recursive = TRUE;
6233     buf = alloc((unsigned)(STRLEN(name) + 12));
6234     if (buf != NULL)
6235     {
6236 	sprintf((char *)buf, "colors/%s.vim", name);
6237 	retval = source_runtime(buf, FALSE);
6238 	vim_free(buf);
6239 #ifdef FEAT_AUTOCMD
6240 	apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6241 #endif
6242     }
6243     recursive = FALSE;
6244 
6245     return retval;
6246 }
6247 
6248 /*
6249  * Handle the ":highlight .." command.
6250  * When using ":hi clear" this is called recursively for each group with
6251  * "forceit" and "init" both TRUE.
6252  */
6253     void
6254 do_highlight(line, forceit, init)
6255     char_u	*line;
6256     int		forceit;
6257     int		init;	    /* TRUE when called for initializing */
6258 {
6259     char_u	*name_end;
6260     char_u	*p;
6261     char_u	*linep;
6262     char_u	*key_start;
6263     char_u	*arg_start;
6264     char_u	*key = NULL, *arg = NULL;
6265     long	i;
6266     int		off;
6267     int		len;
6268     int		attr;
6269     int		id;
6270     int		idx;
6271     int		dodefault = FALSE;
6272     int		doclear = FALSE;
6273     int		dolink = FALSE;
6274     int		error = FALSE;
6275     int		color;
6276     int		is_normal_group = FALSE;	/* "Normal" group */
6277 #ifdef FEAT_GUI_X11
6278     int		is_menu_group = FALSE;		/* "Menu" group */
6279     int		is_scrollbar_group = FALSE;	/* "Scrollbar" group */
6280     int		is_tooltip_group = FALSE;	/* "Tooltip" group */
6281     int		do_colors = FALSE;		/* need to update colors? */
6282 #else
6283 # define is_menu_group 0
6284 # define is_tooltip_group 0
6285 #endif
6286 
6287     /*
6288      * If no argument, list current highlighting.
6289      */
6290     if (ends_excmd(*line))
6291     {
6292 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6293 	    /* TODO: only call when the group has attributes set */
6294 	    highlight_list_one((int)i);
6295 	return;
6296     }
6297 
6298     /*
6299      * Isolate the name.
6300      */
6301     name_end = skiptowhite(line);
6302     linep = skipwhite(name_end);
6303 
6304     /*
6305      * Check for "default" argument.
6306      */
6307     if (STRNCMP(line, "default", name_end - line) == 0)
6308     {
6309 	dodefault = TRUE;
6310 	line = linep;
6311 	name_end = skiptowhite(line);
6312 	linep = skipwhite(name_end);
6313     }
6314 
6315     /*
6316      * Check for "clear" or "link" argument.
6317      */
6318     if (STRNCMP(line, "clear", name_end - line) == 0)
6319 	doclear = TRUE;
6320     if (STRNCMP(line, "link", name_end - line) == 0)
6321 	dolink = TRUE;
6322 
6323     /*
6324      * ":highlight {group-name}": list highlighting for one group.
6325      */
6326     if (!doclear && !dolink && ends_excmd(*linep))
6327     {
6328 	id = syn_namen2id(line, (int)(name_end - line));
6329 	if (id == 0)
6330 	    EMSG2(_("E411: highlight group not found: %s"), line);
6331 	else
6332 	    highlight_list_one(id);
6333 	return;
6334     }
6335 
6336     /*
6337      * Handle ":highlight link {from} {to}" command.
6338      */
6339     if (dolink)
6340     {
6341 	char_u	    *from_start = linep;
6342 	char_u	    *from_end;
6343 	char_u	    *to_start;
6344 	char_u	    *to_end;
6345 	int	    from_id;
6346 	int	    to_id;
6347 
6348 	from_end = skiptowhite(from_start);
6349 	to_start = skipwhite(from_end);
6350 	to_end	 = skiptowhite(to_start);
6351 
6352 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
6353 	{
6354 	    EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6355 								  from_start);
6356 	    return;
6357 	}
6358 
6359 	if (!ends_excmd(*skipwhite(to_end)))
6360 	{
6361 	    EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6362 	    return;
6363 	}
6364 
6365 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
6366 	if (STRNCMP(to_start, "NONE", 4) == 0)
6367 	    to_id = 0;
6368 	else
6369 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
6370 
6371 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6372 	{
6373 	    /*
6374 	     * Don't allow a link when there already is some highlighting
6375 	     * for the group, unless '!' is used
6376 	     */
6377 	    if (to_id > 0 && !forceit && !init
6378 				   && hl_has_settings(from_id - 1, dodefault))
6379 	    {
6380 		if (sourcing_name == NULL && !dodefault)
6381 		    EMSG(_("E414: group has settings, highlight link ignored"));
6382 	    }
6383 	    else
6384 	    {
6385 		if (!init)
6386 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6387 		HL_TABLE()[from_id - 1].sg_link = to_id;
6388 #ifdef FEAT_EVAL
6389 		HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6390 #endif
6391 		redraw_all_later(NOT_VALID);
6392 	    }
6393 	}
6394 
6395 	/* Only call highlight_changed() once, after sourcing a syntax file */
6396 	need_highlight_changed = TRUE;
6397 
6398 	return;
6399     }
6400 
6401     if (doclear)
6402     {
6403 	/*
6404 	 * ":highlight clear [group]" command.
6405 	 */
6406 	line = linep;
6407 	if (ends_excmd(*line))
6408 	{
6409 #ifdef FEAT_GUI
6410 	    /* First, we do not destroy the old values, but allocate the new
6411 	     * ones and update the display. THEN we destroy the old values.
6412 	     * If we destroy the old values first, then the old values
6413 	     * (such as GuiFont's or GuiFontset's) will still be displayed but
6414 	     * invalid because they were free'd.
6415 	     */
6416 	    if (gui.in_use)
6417 	    {
6418 # ifdef FEAT_BEVAL_TIP
6419 		gui_init_tooltip_font();
6420 # endif
6421 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6422 		gui_init_menu_font();
6423 # endif
6424 	    }
6425 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6426 	    gui_mch_def_colors();
6427 # endif
6428 # ifdef FEAT_GUI_X11
6429 #  ifdef FEAT_MENU
6430 
6431 	    /* This only needs to be done when there is no Menu highlight
6432 	     * group defined by default, which IS currently the case.
6433 	     */
6434 	    gui_mch_new_menu_colors();
6435 #  endif
6436 	    if (gui.in_use)
6437 	    {
6438 		gui_new_scrollbar_colors();
6439 #  ifdef FEAT_BEVAL
6440 		gui_mch_new_tooltip_colors();
6441 #  endif
6442 #  ifdef FEAT_MENU
6443 		gui_mch_new_menu_font();
6444 #  endif
6445 	    }
6446 # endif
6447 
6448 	    /* Ok, we're done allocating the new default graphics items.
6449 	     * The screen should already be refreshed at this point.
6450 	     * It is now Ok to clear out the old data.
6451 	     */
6452 #endif
6453 #ifdef FEAT_EVAL
6454 	    do_unlet((char_u *)"colors_name", TRUE);
6455 #endif
6456 	    restore_cterm_colors();
6457 
6458 	    /*
6459 	     * Clear all default highlight groups and load the defaults.
6460 	     */
6461 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6462 		highlight_clear(idx);
6463 	    init_highlight(TRUE, TRUE);
6464 #ifdef FEAT_GUI
6465 	    if (gui.in_use)
6466 		highlight_gui_started();
6467 #endif
6468 	    highlight_changed();
6469 	    redraw_later_clear();
6470 	    return;
6471 	}
6472 	name_end = skiptowhite(line);
6473 	linep = skipwhite(name_end);
6474     }
6475 
6476     /*
6477      * Find the group name in the table.  If it does not exist yet, add it.
6478      */
6479     id = syn_check_group(line, (int)(name_end - line));
6480     if (id == 0)			/* failed (out of memory) */
6481 	return;
6482     idx = id - 1;			/* index is ID minus one */
6483 
6484     /* Return if "default" was used and the group already has settings. */
6485     if (dodefault && hl_has_settings(idx, TRUE))
6486 	return;
6487 
6488     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6489 	is_normal_group = TRUE;
6490 #ifdef FEAT_GUI_X11
6491     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6492 	is_menu_group = TRUE;
6493     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6494 	is_scrollbar_group = TRUE;
6495     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6496 	is_tooltip_group = TRUE;
6497 #endif
6498 
6499     /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6500     if (doclear || (forceit && init))
6501     {
6502 	highlight_clear(idx);
6503 	if (!doclear)
6504 	    HL_TABLE()[idx].sg_set = 0;
6505     }
6506 
6507     if (!doclear)
6508       while (!ends_excmd(*linep))
6509       {
6510 	key_start = linep;
6511 	if (*linep == '=')
6512 	{
6513 	    EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6514 	    error = TRUE;
6515 	    break;
6516 	}
6517 
6518 	/*
6519 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6520 	 * "guibg").
6521 	 */
6522 	while (*linep && !vim_iswhite(*linep) && *linep != '=')
6523 	    ++linep;
6524 	vim_free(key);
6525 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
6526 	if (key == NULL)
6527 	{
6528 	    error = TRUE;
6529 	    break;
6530 	}
6531 	linep = skipwhite(linep);
6532 
6533 	if (STRCMP(key, "NONE") == 0)
6534 	{
6535 	    if (!init || HL_TABLE()[idx].sg_set == 0)
6536 	    {
6537 		if (!init)
6538 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6539 		highlight_clear(idx);
6540 	    }
6541 	    continue;
6542 	}
6543 
6544 	/*
6545 	 * Check for the equal sign.
6546 	 */
6547 	if (*linep != '=')
6548 	{
6549 	    EMSG2(_("E416: missing equal sign: %s"), key_start);
6550 	    error = TRUE;
6551 	    break;
6552 	}
6553 	++linep;
6554 
6555 	/*
6556 	 * Isolate the argument.
6557 	 */
6558 	linep = skipwhite(linep);
6559 	if (*linep == '\'')		/* guifg='color name' */
6560 	{
6561 	    arg_start = ++linep;
6562 	    linep = vim_strchr(linep, '\'');
6563 	    if (linep == NULL)
6564 	    {
6565 		EMSG2(_(e_invarg2), key_start);
6566 		error = TRUE;
6567 		break;
6568 	    }
6569 	}
6570 	else
6571 	{
6572 	    arg_start = linep;
6573 	    linep = skiptowhite(linep);
6574 	}
6575 	if (linep == arg_start)
6576 	{
6577 	    EMSG2(_("E417: missing argument: %s"), key_start);
6578 	    error = TRUE;
6579 	    break;
6580 	}
6581 	vim_free(arg);
6582 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6583 	if (arg == NULL)
6584 	{
6585 	    error = TRUE;
6586 	    break;
6587 	}
6588 	if (*linep == '\'')
6589 	    ++linep;
6590 
6591 	/*
6592 	 * Store the argument.
6593 	 */
6594 	if (  STRCMP(key, "TERM") == 0
6595 		|| STRCMP(key, "CTERM") == 0
6596 		|| STRCMP(key, "GUI") == 0)
6597 	{
6598 	    attr = 0;
6599 	    off = 0;
6600 	    while (arg[off] != NUL)
6601 	    {
6602 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6603 		{
6604 		    len = (int)STRLEN(hl_name_table[i]);
6605 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6606 		    {
6607 			attr |= hl_attr_table[i];
6608 			off += len;
6609 			break;
6610 		    }
6611 		}
6612 		if (i < 0)
6613 		{
6614 		    EMSG2(_("E418: Illegal value: %s"), arg);
6615 		    error = TRUE;
6616 		    break;
6617 		}
6618 		if (arg[off] == ',')		/* another one follows */
6619 		    ++off;
6620 	    }
6621 	    if (error)
6622 		break;
6623 	    if (*key == 'T')
6624 	    {
6625 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6626 		{
6627 		    if (!init)
6628 			HL_TABLE()[idx].sg_set |= SG_TERM;
6629 		    HL_TABLE()[idx].sg_term = attr;
6630 		}
6631 	    }
6632 	    else if (*key == 'C')
6633 	    {
6634 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6635 		{
6636 		    if (!init)
6637 			HL_TABLE()[idx].sg_set |= SG_CTERM;
6638 		    HL_TABLE()[idx].sg_cterm = attr;
6639 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
6640 		}
6641 	    }
6642 #ifdef FEAT_GUI
6643 	    else
6644 	    {
6645 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6646 		{
6647 		    if (!init)
6648 			HL_TABLE()[idx].sg_set |= SG_GUI;
6649 		    HL_TABLE()[idx].sg_gui = attr;
6650 		}
6651 	    }
6652 #endif
6653 	}
6654 	else if (STRCMP(key, "FONT") == 0)
6655 	{
6656 	    /* in non-GUI fonts are simply ignored */
6657 #ifdef FEAT_GUI
6658 	    if (!gui.shell_created)
6659 	    {
6660 		/* GUI not started yet, always accept the name. */
6661 		vim_free(HL_TABLE()[idx].sg_font_name);
6662 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6663 	    }
6664 	    else
6665 	    {
6666 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6667 # ifdef FEAT_XFONTSET
6668 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6669 # endif
6670 		/* First, save the current font/fontset.
6671 		 * Then try to allocate the font/fontset.
6672 		 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6673 		 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6674 		 */
6675 
6676 		HL_TABLE()[idx].sg_font = NOFONT;
6677 # ifdef FEAT_XFONTSET
6678 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
6679 # endif
6680 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
6681 							    is_tooltip_group);
6682 
6683 # ifdef FEAT_XFONTSET
6684 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6685 		{
6686 		    /* New fontset was accepted. Free the old one, if there was
6687 		     * one.
6688 		     */
6689 		    gui_mch_free_fontset(temp_sg_fontset);
6690 		    vim_free(HL_TABLE()[idx].sg_font_name);
6691 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6692 		}
6693 		else
6694 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6695 # endif
6696 		if (HL_TABLE()[idx].sg_font != NOFONT)
6697 		{
6698 		    /* New font was accepted. Free the old one, if there was
6699 		     * one.
6700 		     */
6701 		    gui_mch_free_font(temp_sg_font);
6702 		    vim_free(HL_TABLE()[idx].sg_font_name);
6703 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6704 		}
6705 		else
6706 		    HL_TABLE()[idx].sg_font = temp_sg_font;
6707 	    }
6708 #endif
6709 	}
6710 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6711 	{
6712 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6713 	  {
6714 	    if (!init)
6715 		HL_TABLE()[idx].sg_set |= SG_CTERM;
6716 
6717 	    /* When setting the foreground color, and previously the "bold"
6718 	     * flag was set for a light color, reset it now */
6719 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6720 	    {
6721 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6722 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
6723 	    }
6724 
6725 	    if (VIM_ISDIGIT(*arg))
6726 		color = atoi((char *)arg);
6727 	    else if (STRICMP(arg, "fg") == 0)
6728 	    {
6729 		if (cterm_normal_fg_color)
6730 		    color = cterm_normal_fg_color - 1;
6731 		else
6732 		{
6733 		    EMSG(_("E419: FG color unknown"));
6734 		    error = TRUE;
6735 		    break;
6736 		}
6737 	    }
6738 	    else if (STRICMP(arg, "bg") == 0)
6739 	    {
6740 		if (cterm_normal_bg_color > 0)
6741 		    color = cterm_normal_bg_color - 1;
6742 		else
6743 		{
6744 		    EMSG(_("E420: BG color unknown"));
6745 		    error = TRUE;
6746 		    break;
6747 		}
6748 	    }
6749 	    else
6750 	    {
6751 		static char *(color_names[28]) = {
6752 			    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6753 			    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6754 			    "Gray", "Grey",
6755 			    "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6756 			    "Blue", "LightBlue", "Green", "LightGreen",
6757 			    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6758 			    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6759 		static int color_numbers_16[28] = {0, 1, 2, 3,
6760 						 4, 5, 6, 6,
6761 						 7, 7,
6762 						 7, 7, 8, 8,
6763 						 9, 9, 10, 10,
6764 						 11, 11, 12, 12, 13,
6765 						 13, 14, 14, 15, -1};
6766 		/* for xterm with 88 colors... */
6767 		static int color_numbers_88[28] = {0, 4, 2, 6,
6768 						 1, 5, 32, 72,
6769 						 84, 84,
6770 						 7, 7, 82, 82,
6771 						 12, 43, 10, 61,
6772 						 14, 63, 9, 74, 13,
6773 						 75, 11, 78, 15, -1};
6774 		/* for xterm with 256 colors... */
6775 		static int color_numbers_256[28] = {0, 4, 2, 6,
6776 						 1, 5, 130, 130,
6777 						 248, 248,
6778 						 7, 7, 242, 242,
6779 						 12, 81, 10, 121,
6780 						 14, 159, 9, 224, 13,
6781 						 225, 11, 229, 15, -1};
6782 		/* for terminals with less than 16 colors... */
6783 		static int color_numbers_8[28] = {0, 4, 2, 6,
6784 						 1, 5, 3, 3,
6785 						 7, 7,
6786 						 7, 7, 0+8, 0+8,
6787 						 4+8, 4+8, 2+8, 2+8,
6788 						 6+8, 6+8, 1+8, 1+8, 5+8,
6789 						 5+8, 3+8, 3+8, 7+8, -1};
6790 #if defined(__QNXNTO__)
6791 		static int *color_numbers_8_qansi = color_numbers_8;
6792 		/* On qnx, the 8 & 16 color arrays are the same */
6793 		if (STRNCMP(T_NAME, "qansi", 5) == 0)
6794 		    color_numbers_8_qansi = color_numbers_16;
6795 #endif
6796 
6797 		/* reduce calls to STRICMP a bit, it can be slow */
6798 		off = TOUPPER_ASC(*arg);
6799 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6800 		    if (off == color_names[i][0]
6801 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6802 			break;
6803 		if (i < 0)
6804 		{
6805 		    EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6806 		    error = TRUE;
6807 		    break;
6808 		}
6809 
6810 		/* Use the _16 table to check if its a valid color name. */
6811 		color = color_numbers_16[i];
6812 		if (color >= 0)
6813 		{
6814 		    if (t_colors == 8)
6815 		    {
6816 			/* t_Co is 8: use the 8 colors table */
6817 #if defined(__QNXNTO__)
6818 			color = color_numbers_8_qansi[i];
6819 #else
6820 			color = color_numbers_8[i];
6821 #endif
6822 			if (key[5] == 'F')
6823 			{
6824 			    /* set/reset bold attribute to get light foreground
6825 			     * colors (on some terminals, e.g. "linux") */
6826 			    if (color & 8)
6827 			    {
6828 				HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6829 				HL_TABLE()[idx].sg_cterm_bold = TRUE;
6830 			    }
6831 			    else
6832 				HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6833 			}
6834 			color &= 7;	/* truncate to 8 colors */
6835 		    }
6836 		    else if (t_colors == 16 || t_colors == 88
6837 							   || t_colors == 256)
6838 		    {
6839 			/*
6840 			 * Guess: if the termcap entry ends in 'm', it is
6841 			 * probably an xterm-like terminal.  Use the changed
6842 			 * order for colors.
6843 			 */
6844 			if (*T_CAF != NUL)
6845 			    p = T_CAF;
6846 			else
6847 			    p = T_CSF;
6848 			if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6849 			    switch (t_colors)
6850 			    {
6851 				case 16:
6852 				    color = color_numbers_8[i];
6853 				    break;
6854 				case 88:
6855 				    color = color_numbers_88[i];
6856 				    break;
6857 				case 256:
6858 				    color = color_numbers_256[i];
6859 				    break;
6860 			    }
6861 		    }
6862 		}
6863 	    }
6864 	    /* Add one to the argument, to avoid zero */
6865 	    if (key[5] == 'F')
6866 	    {
6867 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
6868 		if (is_normal_group)
6869 		{
6870 		    cterm_normal_fg_color = color + 1;
6871 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6872 #ifdef FEAT_GUI
6873 		    /* Don't do this if the GUI is used. */
6874 		    if (!gui.in_use && !gui.starting)
6875 #endif
6876 		    {
6877 			must_redraw = CLEAR;
6878 			if (termcap_active)
6879 			    term_fg_color(color);
6880 		    }
6881 		}
6882 	    }
6883 	    else
6884 	    {
6885 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
6886 		if (is_normal_group)
6887 		{
6888 		    cterm_normal_bg_color = color + 1;
6889 #ifdef FEAT_GUI
6890 		    /* Don't mess with 'background' if the GUI is used. */
6891 		    if (!gui.in_use && !gui.starting)
6892 #endif
6893 		    {
6894 			must_redraw = CLEAR;
6895 			if (termcap_active)
6896 			    term_bg_color(color);
6897 			if (t_colors < 16)
6898 			    i = (color == 0 || color == 4);
6899 			else
6900 			    i = (color < 7 || color == 8);
6901 			/* Set the 'background' option if the value is wrong. */
6902 			if (i != (*p_bg == 'd'))
6903 			    set_option_value((char_u *)"bg", 0L,
6904 				 i ? (char_u *)"dark" : (char_u *)"light", 0);
6905 		    }
6906 		}
6907 	    }
6908 	  }
6909 	}
6910 	else if (STRCMP(key, "GUIFG") == 0)
6911 	{
6912 #ifdef FEAT_GUI	    /* in non-GUI guifg colors are simply ignored */
6913 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6914 	    {
6915 		if (!init)
6916 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6917 
6918 		i = color_name2handle(arg);
6919 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6920 		{
6921 		    HL_TABLE()[idx].sg_gui_fg = i;
6922 		    vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6923 		    if (STRCMP(arg, "NONE"))
6924 			HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6925 		    else
6926 			HL_TABLE()[idx].sg_gui_fg_name = NULL;
6927 # ifdef FEAT_GUI_X11
6928 		    if (is_menu_group)
6929 			gui.menu_fg_pixel = i;
6930 		    if (is_scrollbar_group)
6931 			gui.scroll_fg_pixel = i;
6932 #  ifdef FEAT_BEVAL
6933 		    if (is_tooltip_group)
6934 			gui.tooltip_fg_pixel = i;
6935 #  endif
6936 		    do_colors = TRUE;
6937 # endif
6938 		}
6939 	    }
6940 #endif
6941 	}
6942 	else if (STRCMP(key, "GUIBG") == 0)
6943 	{
6944 #ifdef FEAT_GUI	    /* in non-GUI guibg colors are simply ignored */
6945 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6946 	    {
6947 		if (!init)
6948 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6949 
6950 		i = color_name2handle(arg);
6951 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6952 		{
6953 		    HL_TABLE()[idx].sg_gui_bg = i;
6954 		    vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6955 		    if (STRCMP(arg, "NONE") != 0)
6956 			HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6957 		    else
6958 			HL_TABLE()[idx].sg_gui_bg_name = NULL;
6959 # ifdef FEAT_GUI_X11
6960 		    if (is_menu_group)
6961 			gui.menu_bg_pixel = i;
6962 		    if (is_scrollbar_group)
6963 			gui.scroll_bg_pixel = i;
6964 #  ifdef FEAT_BEVAL
6965 		    if (is_tooltip_group)
6966 			gui.tooltip_bg_pixel = i;
6967 #  endif
6968 		    do_colors = TRUE;
6969 # endif
6970 		}
6971 	    }
6972 #endif
6973 	}
6974 	else if (STRCMP(key, "GUISP") == 0)
6975 	{
6976 #ifdef FEAT_GUI	    /* in non-GUI guisp colors are simply ignored */
6977 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6978 	    {
6979 		if (!init)
6980 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6981 
6982 		i = color_name2handle(arg);
6983 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6984 		{
6985 		    HL_TABLE()[idx].sg_gui_sp = i;
6986 		    vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6987 		    if (STRCMP(arg, "NONE") != 0)
6988 			HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6989 		    else
6990 			HL_TABLE()[idx].sg_gui_sp_name = NULL;
6991 		}
6992 	    }
6993 #endif
6994 	}
6995 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6996 	{
6997 	    char_u	buf[100];
6998 	    char_u	*tname;
6999 
7000 	    if (!init)
7001 		HL_TABLE()[idx].sg_set |= SG_TERM;
7002 
7003 	    /*
7004 	     * The "start" and "stop"  arguments can be a literal escape
7005 	     * sequence, or a comma seperated list of terminal codes.
7006 	     */
7007 	    if (STRNCMP(arg, "t_", 2) == 0)
7008 	    {
7009 		off = 0;
7010 		buf[0] = 0;
7011 		while (arg[off] != NUL)
7012 		{
7013 		    /* Isolate one termcap name */
7014 		    for (len = 0; arg[off + len] &&
7015 						 arg[off + len] != ','; ++len)
7016 			;
7017 		    tname = vim_strnsave(arg + off, len);
7018 		    if (tname == NULL)		/* out of memory */
7019 		    {
7020 			error = TRUE;
7021 			break;
7022 		    }
7023 		    /* lookup the escape sequence for the item */
7024 		    p = get_term_code(tname);
7025 		    vim_free(tname);
7026 		    if (p == NULL)	    /* ignore non-existing things */
7027 			p = (char_u *)"";
7028 
7029 		    /* Append it to the already found stuff */
7030 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7031 		    {
7032 			EMSG2(_("E422: terminal code too long: %s"), arg);
7033 			error = TRUE;
7034 			break;
7035 		    }
7036 		    STRCAT(buf, p);
7037 
7038 		    /* Advance to the next item */
7039 		    off += len;
7040 		    if (arg[off] == ',')	    /* another one follows */
7041 			++off;
7042 		}
7043 	    }
7044 	    else
7045 	    {
7046 		/*
7047 		 * Copy characters from arg[] to buf[], translating <> codes.
7048 		 */
7049 		for (p = arg, off = 0; off < 100 && *p; )
7050 		{
7051 		    len = trans_special(&p, buf + off, FALSE);
7052 		    if (len)		    /* recognized special char */
7053 			off += len;
7054 		    else		    /* copy as normal char */
7055 			buf[off++] = *p++;
7056 		}
7057 		buf[off] = NUL;
7058 	    }
7059 	    if (error)
7060 		break;
7061 
7062 	    if (STRCMP(buf, "NONE") == 0)	/* resetting the value */
7063 		p = NULL;
7064 	    else
7065 		p = vim_strsave(buf);
7066 	    if (key[2] == 'A')
7067 	    {
7068 		vim_free(HL_TABLE()[idx].sg_start);
7069 		HL_TABLE()[idx].sg_start = p;
7070 	    }
7071 	    else
7072 	    {
7073 		vim_free(HL_TABLE()[idx].sg_stop);
7074 		HL_TABLE()[idx].sg_stop = p;
7075 	    }
7076 	}
7077 	else
7078 	{
7079 	    EMSG2(_("E423: Illegal argument: %s"), key_start);
7080 	    error = TRUE;
7081 	    break;
7082 	}
7083 
7084 	/*
7085 	 * When highlighting has been given for a group, don't link it.
7086 	 */
7087 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7088 	    HL_TABLE()[idx].sg_link = 0;
7089 
7090 	/*
7091 	 * Continue with next argument.
7092 	 */
7093 	linep = skipwhite(linep);
7094       }
7095 
7096     /*
7097      * If there is an error, and it's a new entry, remove it from the table.
7098      */
7099     if (error && idx == highlight_ga.ga_len)
7100 	syn_unadd_group();
7101     else
7102     {
7103 	if (is_normal_group)
7104 	{
7105 	    HL_TABLE()[idx].sg_term_attr = 0;
7106 	    HL_TABLE()[idx].sg_cterm_attr = 0;
7107 #ifdef FEAT_GUI
7108 	    HL_TABLE()[idx].sg_gui_attr = 0;
7109 	    /*
7110 	     * Need to update all groups, because they might be using "bg"
7111 	     * and/or "fg", which have been changed now.
7112 	     */
7113 	    if (gui.in_use)
7114 		highlight_gui_started();
7115 #endif
7116 	}
7117 #ifdef FEAT_GUI_X11
7118 # ifdef FEAT_MENU
7119 	else if (is_menu_group)
7120 	{
7121 	    if (gui.in_use && do_colors)
7122 		gui_mch_new_menu_colors();
7123 	}
7124 # endif
7125 	else if (is_scrollbar_group)
7126 	{
7127 	    if (gui.in_use && do_colors)
7128 		gui_new_scrollbar_colors();
7129 	}
7130 # ifdef FEAT_BEVAL
7131 	else if (is_tooltip_group)
7132 	{
7133 	    if (gui.in_use && do_colors)
7134 		gui_mch_new_tooltip_colors();
7135 	}
7136 # endif
7137 #endif
7138 	else
7139 	    set_hl_attr(idx);
7140 #ifdef FEAT_EVAL
7141 	HL_TABLE()[idx].sg_scriptID = current_SID;
7142 #endif
7143 	redraw_all_later(NOT_VALID);
7144     }
7145     vim_free(key);
7146     vim_free(arg);
7147 
7148     /* Only call highlight_changed() once, after sourcing a syntax file */
7149     need_highlight_changed = TRUE;
7150 }
7151 
7152 #if defined(EXITFREE) || defined(PROTO)
7153     void
7154 free_highlight()
7155 {
7156     int	    i;
7157 
7158     for (i = 0; i < highlight_ga.ga_len; ++i)
7159     {
7160 	highlight_clear(i);
7161 	vim_free(HL_TABLE()[i].sg_name);
7162 	vim_free(HL_TABLE()[i].sg_name_u);
7163     }
7164     ga_clear(&highlight_ga);
7165 }
7166 #endif
7167 
7168 /*
7169  * Reset the cterm colors to what they were before Vim was started, if
7170  * possible.  Otherwise reset them to zero.
7171  */
7172     void
7173 restore_cterm_colors()
7174 {
7175 #if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7176     /* Since t_me has been set, this probably means that the user
7177      * wants to use this as default colors.  Need to reset default
7178      * background/foreground colors. */
7179     mch_set_normal_colors();
7180 #else
7181     cterm_normal_fg_color = 0;
7182     cterm_normal_fg_bold = 0;
7183     cterm_normal_bg_color = 0;
7184 #endif
7185 }
7186 
7187 /*
7188  * Return TRUE if highlight group "idx" has any settings.
7189  * When "check_link" is TRUE also check for an existing link.
7190  */
7191     static int
7192 hl_has_settings(idx, check_link)
7193     int		idx;
7194     int		check_link;
7195 {
7196     return (   HL_TABLE()[idx].sg_term_attr != 0
7197 	    || HL_TABLE()[idx].sg_cterm_attr != 0
7198 #ifdef FEAT_GUI
7199 	    || HL_TABLE()[idx].sg_gui_attr != 0
7200 #endif
7201 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7202 }
7203 
7204 /*
7205  * Clear highlighting for one group.
7206  */
7207     static void
7208 highlight_clear(idx)
7209     int idx;
7210 {
7211     HL_TABLE()[idx].sg_term = 0;
7212     vim_free(HL_TABLE()[idx].sg_start);
7213     HL_TABLE()[idx].sg_start = NULL;
7214     vim_free(HL_TABLE()[idx].sg_stop);
7215     HL_TABLE()[idx].sg_stop = NULL;
7216     HL_TABLE()[idx].sg_term_attr = 0;
7217     HL_TABLE()[idx].sg_cterm = 0;
7218     HL_TABLE()[idx].sg_cterm_bold = FALSE;
7219     HL_TABLE()[idx].sg_cterm_fg = 0;
7220     HL_TABLE()[idx].sg_cterm_bg = 0;
7221     HL_TABLE()[idx].sg_cterm_attr = 0;
7222 #ifdef FEAT_GUI	    /* in non-GUI fonts are simply ignored */
7223     HL_TABLE()[idx].sg_gui = 0;
7224     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7225     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7226     HL_TABLE()[idx].sg_gui_fg_name = NULL;
7227     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7228     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7229     HL_TABLE()[idx].sg_gui_bg_name = NULL;
7230     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7231     vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7232     HL_TABLE()[idx].sg_gui_sp_name = NULL;
7233     gui_mch_free_font(HL_TABLE()[idx].sg_font);
7234     HL_TABLE()[idx].sg_font = NOFONT;
7235 # ifdef FEAT_XFONTSET
7236     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7237     HL_TABLE()[idx].sg_fontset = NOFONTSET;
7238 # endif
7239     vim_free(HL_TABLE()[idx].sg_font_name);
7240     HL_TABLE()[idx].sg_font_name = NULL;
7241     HL_TABLE()[idx].sg_gui_attr = 0;
7242 #endif
7243 #ifdef FEAT_EVAL
7244     /* Clear the script ID only when there is no link, since that is not
7245      * cleared. */
7246     if (HL_TABLE()[idx].sg_link == 0)
7247 	HL_TABLE()[idx].sg_scriptID = 0;
7248 #endif
7249 }
7250 
7251 #if defined(FEAT_GUI) || defined(PROTO)
7252 /*
7253  * Set the normal foreground and background colors according to the "Normal"
7254  * highlighighting group.  For X11 also set "Menu", "Scrollbar", and
7255  * "Tooltip" colors.
7256  */
7257     void
7258 set_normal_colors()
7259 {
7260     if (set_group_colors((char_u *)"Normal",
7261 			     &gui.norm_pixel, &gui.back_pixel,
7262 			     FALSE, TRUE, FALSE))
7263     {
7264 	gui_mch_new_colors();
7265 	must_redraw = CLEAR;
7266     }
7267 #ifdef FEAT_GUI_X11
7268     if (set_group_colors((char_u *)"Menu",
7269 			 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7270 			 TRUE, FALSE, FALSE))
7271     {
7272 # ifdef FEAT_MENU
7273 	gui_mch_new_menu_colors();
7274 # endif
7275 	must_redraw = CLEAR;
7276     }
7277 # ifdef FEAT_BEVAL
7278     if (set_group_colors((char_u *)"Tooltip",
7279 			 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7280 			 FALSE, FALSE, TRUE))
7281     {
7282 # ifdef FEAT_TOOLBAR
7283 	gui_mch_new_tooltip_colors();
7284 # endif
7285 	must_redraw = CLEAR;
7286     }
7287 #endif
7288     if (set_group_colors((char_u *)"Scrollbar",
7289 		    &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7290 		    FALSE, FALSE, FALSE))
7291     {
7292 	gui_new_scrollbar_colors();
7293 	must_redraw = CLEAR;
7294     }
7295 #endif
7296 }
7297 
7298 /*
7299  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7300  */
7301     static int
7302 set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7303     char_u	*name;
7304     guicolor_T	*fgp;
7305     guicolor_T	*bgp;
7306     int		do_menu;
7307     int		use_norm;
7308     int		do_tooltip;
7309 {
7310     int		idx;
7311 
7312     idx = syn_name2id(name) - 1;
7313     if (idx >= 0)
7314     {
7315 	gui_do_one_color(idx, do_menu, do_tooltip);
7316 
7317 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7318 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
7319 	else if (use_norm)
7320 	    *fgp = gui.def_norm_pixel;
7321 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7322 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
7323 	else if (use_norm)
7324 	    *bgp = gui.def_back_pixel;
7325 	return TRUE;
7326     }
7327     return FALSE;
7328 }
7329 
7330 /*
7331  * Get the font of the "Normal" group.
7332  * Returns "" when it's not found or not set.
7333  */
7334     char_u *
7335 hl_get_font_name()
7336 {
7337     int		id;
7338     char_u	*s;
7339 
7340     id = syn_name2id((char_u *)"Normal");
7341     if (id > 0)
7342     {
7343 	s = HL_TABLE()[id - 1].sg_font_name;
7344 	if (s != NULL)
7345 	    return s;
7346     }
7347     return (char_u *)"";
7348 }
7349 
7350 /*
7351  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
7352  * actually chosen to be used.
7353  */
7354     void
7355 hl_set_font_name(font_name)
7356     char_u	*font_name;
7357 {
7358     int	    id;
7359 
7360     id = syn_name2id((char_u *)"Normal");
7361     if (id > 0)
7362     {
7363 	vim_free(HL_TABLE()[id - 1].sg_font_name);
7364 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7365     }
7366 }
7367 
7368 /*
7369  * Set background color for "Normal" group.  Called by gui_set_bg_color()
7370  * when the color is known.
7371  */
7372     void
7373 hl_set_bg_color_name(name)
7374     char_u  *name;	    /* must have been allocated */
7375 {
7376     int	    id;
7377 
7378     if (name != NULL)
7379     {
7380 	id = syn_name2id((char_u *)"Normal");
7381 	if (id > 0)
7382 	{
7383 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7384 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
7385 	}
7386     }
7387 }
7388 
7389 /*
7390  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
7391  * when the color is known.
7392  */
7393     void
7394 hl_set_fg_color_name(name)
7395     char_u  *name;	    /* must have been allocated */
7396 {
7397     int	    id;
7398 
7399     if (name != NULL)
7400     {
7401 	id = syn_name2id((char_u *)"Normal");
7402 	if (id > 0)
7403 	{
7404 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7405 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
7406 	}
7407     }
7408 }
7409 
7410 /*
7411  * Return the handle for a color name.
7412  * Returns INVALCOLOR when failed.
7413  */
7414     static guicolor_T
7415 color_name2handle(name)
7416     char_u  *name;
7417 {
7418     if (STRCMP(name, "NONE") == 0)
7419 	return INVALCOLOR;
7420 
7421     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7422 	return gui.norm_pixel;
7423     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7424 	return gui.back_pixel;
7425 
7426     return gui_get_color(name);
7427 }
7428 
7429 /*
7430  * Return the handle for a font name.
7431  * Returns NOFONT when failed.
7432  */
7433     static GuiFont
7434 font_name2handle(name)
7435     char_u  *name;
7436 {
7437     if (STRCMP(name, "NONE") == 0)
7438 	return NOFONT;
7439 
7440     return gui_mch_get_font(name, TRUE);
7441 }
7442 
7443 # ifdef FEAT_XFONTSET
7444 /*
7445  * Return the handle for a fontset name.
7446  * Returns NOFONTSET when failed.
7447  */
7448     static GuiFontset
7449 fontset_name2handle(name, fixed_width)
7450     char_u	*name;
7451     int		fixed_width;
7452 {
7453     if (STRCMP(name, "NONE") == 0)
7454 	return NOFONTSET;
7455 
7456     return gui_mch_get_fontset(name, TRUE, fixed_width);
7457 }
7458 # endif
7459 
7460 /*
7461  * Get the font or fontset for one highlight group.
7462  */
7463 /*ARGSUSED*/
7464     static void
7465 hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7466     int		idx;
7467     char_u	*arg;
7468     int		do_normal;	/* set normal font */
7469     int		do_menu;	/* set menu font */
7470     int		do_tooltip;	/* set tooltip font */
7471 {
7472 # ifdef FEAT_XFONTSET
7473     /* If 'guifontset' is not empty, first try using the name as a
7474      * fontset.  If that doesn't work, use it as a font name. */
7475     if (*p_guifontset != NUL
7476 #  ifdef FONTSET_ALWAYS
7477 	|| do_menu
7478 #  endif
7479 #  ifdef FEAT_BEVAL_TIP
7480 	/* In Athena & Motif, the Tooltip highlight group is always a fontset */
7481 	|| do_tooltip
7482 #  endif
7483 	    )
7484 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7485 #  ifdef FONTSET_ALWAYS
7486 		|| do_menu
7487 #  endif
7488 #  ifdef FEAT_BEVAL_TIP
7489 		|| do_tooltip
7490 #  endif
7491 		);
7492     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7493     {
7494 	/* If it worked and it's the Normal group, use it as the
7495 	 * normal fontset.  Same for the Menu group. */
7496 	if (do_normal)
7497 	    gui_init_font(arg, TRUE);
7498 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7499 	if (do_menu)
7500 	{
7501 #    ifdef FONTSET_ALWAYS
7502 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7503 #    else
7504 	    /* YIKES!  This is a bug waiting to crash the program */
7505 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
7506 #    endif
7507 	    gui_mch_new_menu_font();
7508 	}
7509 #    ifdef FEAT_BEVAL
7510 	if (do_tooltip)
7511 	{
7512 	    /* The Athena widget set cannot currently handle switching between
7513 	     * displaying a single font and a fontset.
7514 	     * If the XtNinternational resource is set to True at widget
7515 	     * creation, then a fontset is always used, othwise an
7516 	     * XFontStruct is used.
7517 	     */
7518 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7519 	    gui_mch_new_tooltip_font();
7520 	}
7521 #    endif
7522 #   endif
7523     }
7524     else
7525 # endif
7526     {
7527 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
7528 	/* If it worked and it's the Normal group, use it as the
7529 	 * normal font.  Same for the Menu group. */
7530 	if (HL_TABLE()[idx].sg_font != NOFONT)
7531 	{
7532 	    if (do_normal)
7533 		gui_init_font(arg, FALSE);
7534 #ifndef FONTSET_ALWAYS
7535 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7536 	    if (do_menu)
7537 	    {
7538 		gui.menu_font = HL_TABLE()[idx].sg_font;
7539 		gui_mch_new_menu_font();
7540 	    }
7541 # endif
7542 #endif
7543 	}
7544     }
7545 }
7546 
7547 #endif /* FEAT_GUI */
7548 
7549 /*
7550  * Table with the specifications for an attribute number.
7551  * Note that this table is used by ALL buffers.  This is required because the
7552  * GUI can redraw at any time for any buffer.
7553  */
7554 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
7555 
7556 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7557 
7558 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
7559 
7560 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7561 
7562 #ifdef FEAT_GUI
7563 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
7564 
7565 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7566 #endif
7567 
7568 /*
7569  * Return the attr number for a set of colors and font.
7570  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7571  * if the combination is new.
7572  * Return 0 for error (no more room).
7573  */
7574     static int
7575 get_attr_entry(table, aep)
7576     garray_T	*table;
7577     attrentry_T	*aep;
7578 {
7579     int		i;
7580     attrentry_T	*taep;
7581     static int	recursive = FALSE;
7582 
7583     /*
7584      * Init the table, in case it wasn't done yet.
7585      */
7586     table->ga_itemsize = sizeof(attrentry_T);
7587     table->ga_growsize = 7;
7588 
7589     /*
7590      * Try to find an entry with the same specifications.
7591      */
7592     for (i = 0; i < table->ga_len; ++i)
7593     {
7594 	taep = &(((attrentry_T *)table->ga_data)[i]);
7595 	if (	   aep->ae_attr == taep->ae_attr
7596 		&& (
7597 #ifdef FEAT_GUI
7598 		       (table == &gui_attr_table
7599 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7600 			    && aep->ae_u.gui.bg_color
7601 						    == taep->ae_u.gui.bg_color
7602 			    && aep->ae_u.gui.sp_color
7603 						    == taep->ae_u.gui.sp_color
7604 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
7605 #  ifdef FEAT_XFONTSET
7606 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
7607 #  endif
7608 			    ))
7609 		    ||
7610 #endif
7611 		       (table == &term_attr_table
7612 			&& (aep->ae_u.term.start == NULL)
7613 					    == (taep->ae_u.term.start == NULL)
7614 			&& (aep->ae_u.term.start == NULL
7615 			    || STRCMP(aep->ae_u.term.start,
7616 						  taep->ae_u.term.start) == 0)
7617 			&& (aep->ae_u.term.stop == NULL)
7618 					     == (taep->ae_u.term.stop == NULL)
7619 			&& (aep->ae_u.term.stop == NULL
7620 			    || STRCMP(aep->ae_u.term.stop,
7621 						  taep->ae_u.term.stop) == 0))
7622 		    || (table == &cterm_attr_table
7623 			    && aep->ae_u.cterm.fg_color
7624 						  == taep->ae_u.cterm.fg_color
7625 			    && aep->ae_u.cterm.bg_color
7626 						 == taep->ae_u.cterm.bg_color)
7627 		     ))
7628 
7629 	return i + ATTR_OFF;
7630     }
7631 
7632     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
7633     {
7634 	/*
7635 	 * Running out of attribute entries!  remove all attributes, and
7636 	 * compute new ones for all groups.
7637 	 * When called recursively, we are really out of numbers.
7638 	 */
7639 	if (recursive)
7640 	{
7641 	    EMSG(_("E424: Too many different highlighting attributes in use"));
7642 	    return 0;
7643 	}
7644 	recursive = TRUE;
7645 
7646 	clear_hl_tables();
7647 
7648 	must_redraw = CLEAR;
7649 
7650 	for (i = 0; i < highlight_ga.ga_len; ++i)
7651 	    set_hl_attr(i);
7652 
7653 	recursive = FALSE;
7654     }
7655 
7656     /*
7657      * This is a new combination of colors and font, add an entry.
7658      */
7659     if (ga_grow(table, 1) == FAIL)
7660 	return 0;
7661 
7662     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7663     vim_memset(taep, 0, sizeof(attrentry_T));
7664     taep->ae_attr = aep->ae_attr;
7665 #ifdef FEAT_GUI
7666     if (table == &gui_attr_table)
7667     {
7668 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7669 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7670 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7671 	taep->ae_u.gui.font = aep->ae_u.gui.font;
7672 # ifdef FEAT_XFONTSET
7673 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7674 # endif
7675     }
7676 #endif
7677     if (table == &term_attr_table)
7678     {
7679 	if (aep->ae_u.term.start == NULL)
7680 	    taep->ae_u.term.start = NULL;
7681 	else
7682 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7683 	if (aep->ae_u.term.stop == NULL)
7684 	    taep->ae_u.term.stop = NULL;
7685 	else
7686 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7687     }
7688     else if (table == &cterm_attr_table)
7689     {
7690 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7691 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7692     }
7693     ++table->ga_len;
7694     return (table->ga_len - 1 + ATTR_OFF);
7695 }
7696 
7697 /*
7698  * Clear all highlight tables.
7699  */
7700     void
7701 clear_hl_tables()
7702 {
7703     int		i;
7704     attrentry_T	*taep;
7705 
7706 #ifdef FEAT_GUI
7707     ga_clear(&gui_attr_table);
7708 #endif
7709     for (i = 0; i < term_attr_table.ga_len; ++i)
7710     {
7711 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7712 	vim_free(taep->ae_u.term.start);
7713 	vim_free(taep->ae_u.term.stop);
7714     }
7715     ga_clear(&term_attr_table);
7716     ga_clear(&cterm_attr_table);
7717 }
7718 
7719 #if defined(FEAT_SYN_HL) || defined(PROTO)
7720 /*
7721  * Combine special attributes (e.g., for spelling) with other attributes
7722  * (e.g., for syntax highlighting).
7723  * "prim_attr" overrules "char_attr".
7724  * This creates a new group when required.
7725  * Since we expect there to be few spelling mistakes we don't cache the
7726  * result.
7727  * Return the resulting attributes.
7728  */
7729     int
7730 hl_combine_attr(char_attr, prim_attr)
7731     int	    char_attr;
7732     int	    prim_attr;
7733 {
7734     attrentry_T *char_aep = NULL;
7735     attrentry_T *spell_aep;
7736     attrentry_T new_en;
7737 
7738     if (char_attr == 0)
7739 	return prim_attr;
7740     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7741 	return char_attr | prim_attr;
7742 #ifdef FEAT_GUI
7743     if (gui.in_use)
7744     {
7745 	if (char_attr > HL_ALL)
7746 	    char_aep = syn_gui_attr2entry(char_attr);
7747 	if (char_aep != NULL)
7748 	    new_en = *char_aep;
7749 	else
7750 	{
7751 	    vim_memset(&new_en, 0, sizeof(new_en));
7752 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
7753 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
7754 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
7755 	    if (char_attr <= HL_ALL)
7756 		new_en.ae_attr = char_attr;
7757 	}
7758 
7759 	if (prim_attr <= HL_ALL)
7760 	    new_en.ae_attr |= prim_attr;
7761 	else
7762 	{
7763 	    spell_aep = syn_gui_attr2entry(prim_attr);
7764 	    if (spell_aep != NULL)
7765 	    {
7766 		new_en.ae_attr |= spell_aep->ae_attr;
7767 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7768 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7769 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7770 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7771 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7772 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7773 		if (spell_aep->ae_u.gui.font != NOFONT)
7774 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7775 # ifdef FEAT_XFONTSET
7776 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7777 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7778 # endif
7779 	    }
7780 	}
7781 	return get_attr_entry(&gui_attr_table, &new_en);
7782     }
7783 #endif
7784 
7785     if (t_colors > 1)
7786     {
7787 	if (char_attr > HL_ALL)
7788 	    char_aep = syn_cterm_attr2entry(char_attr);
7789 	if (char_aep != NULL)
7790 	    new_en = *char_aep;
7791 	else
7792 	{
7793 	    vim_memset(&new_en, 0, sizeof(new_en));
7794 	    if (char_attr <= HL_ALL)
7795 		new_en.ae_attr = char_attr;
7796 	}
7797 
7798 	if (prim_attr <= HL_ALL)
7799 	    new_en.ae_attr |= prim_attr;
7800 	else
7801 	{
7802 	    spell_aep = syn_cterm_attr2entry(prim_attr);
7803 	    if (spell_aep != NULL)
7804 	    {
7805 		new_en.ae_attr |= spell_aep->ae_attr;
7806 		if (spell_aep->ae_u.cterm.fg_color > 0)
7807 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7808 		if (spell_aep->ae_u.cterm.bg_color > 0)
7809 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7810 	    }
7811 	}
7812 	return get_attr_entry(&cterm_attr_table, &new_en);
7813     }
7814 
7815     if (char_attr > HL_ALL)
7816 	char_aep = syn_term_attr2entry(char_attr);
7817     if (char_aep != NULL)
7818 	new_en = *char_aep;
7819     else
7820     {
7821 	vim_memset(&new_en, 0, sizeof(new_en));
7822 	if (char_attr <= HL_ALL)
7823 	    new_en.ae_attr = char_attr;
7824     }
7825 
7826     if (prim_attr <= HL_ALL)
7827 	new_en.ae_attr |= prim_attr;
7828     else
7829     {
7830 	spell_aep = syn_term_attr2entry(prim_attr);
7831 	if (spell_aep != NULL)
7832 	{
7833 	    new_en.ae_attr |= spell_aep->ae_attr;
7834 	    if (spell_aep->ae_u.term.start != NULL)
7835 	    {
7836 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
7837 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
7838 	    }
7839 	}
7840     }
7841     return get_attr_entry(&term_attr_table, &new_en);
7842 }
7843 #endif
7844 
7845 #ifdef FEAT_GUI
7846 
7847     attrentry_T *
7848 syn_gui_attr2entry(attr)
7849     int		    attr;
7850 {
7851     attr -= ATTR_OFF;
7852     if (attr >= gui_attr_table.ga_len)	    /* did ":syntax clear" */
7853 	return NULL;
7854     return &(GUI_ATTR_ENTRY(attr));
7855 }
7856 
7857 #endif /* FEAT_GUI */
7858 
7859     attrentry_T *
7860 syn_term_attr2entry(attr)
7861     int		    attr;
7862 {
7863     attr -= ATTR_OFF;
7864     if (attr >= term_attr_table.ga_len)	    /* did ":syntax clear" */
7865 	return NULL;
7866     return &(TERM_ATTR_ENTRY(attr));
7867 }
7868 
7869     attrentry_T *
7870 syn_cterm_attr2entry(attr)
7871     int		    attr;
7872 {
7873     attr -= ATTR_OFF;
7874     if (attr >= cterm_attr_table.ga_len)	/* did ":syntax clear" */
7875 	return NULL;
7876     return &(CTERM_ATTR_ENTRY(attr));
7877 }
7878 
7879 #define LIST_ATTR   1
7880 #define LIST_STRING 2
7881 #define LIST_INT    3
7882 
7883     static void
7884 highlight_list_one(id)
7885     int		id;
7886 {
7887     struct hl_group	*sgp;
7888     int			didh = FALSE;
7889 
7890     sgp = &HL_TABLE()[id - 1];	    /* index is ID minus one */
7891 
7892     didh = highlight_list_arg(id, didh, LIST_ATTR,
7893 				    sgp->sg_term, NULL, "term");
7894     didh = highlight_list_arg(id, didh, LIST_STRING,
7895 				    0, sgp->sg_start, "start");
7896     didh = highlight_list_arg(id, didh, LIST_STRING,
7897 				    0, sgp->sg_stop, "stop");
7898 
7899     didh = highlight_list_arg(id, didh, LIST_ATTR,
7900 				    sgp->sg_cterm, NULL, "cterm");
7901     didh = highlight_list_arg(id, didh, LIST_INT,
7902 				    sgp->sg_cterm_fg, NULL, "ctermfg");
7903     didh = highlight_list_arg(id, didh, LIST_INT,
7904 				    sgp->sg_cterm_bg, NULL, "ctermbg");
7905 
7906 #ifdef FEAT_GUI
7907     didh = highlight_list_arg(id, didh, LIST_ATTR,
7908 				    sgp->sg_gui, NULL, "gui");
7909     didh = highlight_list_arg(id, didh, LIST_STRING,
7910 				    0, sgp->sg_gui_fg_name, "guifg");
7911     didh = highlight_list_arg(id, didh, LIST_STRING,
7912 				    0, sgp->sg_gui_bg_name, "guibg");
7913     didh = highlight_list_arg(id, didh, LIST_STRING,
7914 				    0, sgp->sg_gui_sp_name, "guisp");
7915     didh = highlight_list_arg(id, didh, LIST_STRING,
7916 				    0, sgp->sg_font_name, "font");
7917 #endif
7918 
7919     if (sgp->sg_link && !got_int)
7920     {
7921 	(void)syn_list_header(didh, 9999, id);
7922 	didh = TRUE;
7923 	msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7924 	msg_putchar(' ');
7925 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7926     }
7927 
7928 #ifdef FEAT_EVAL
7929     if (didh && p_verbose > 0)
7930 	last_set_msg(sgp->sg_scriptID);
7931 #endif
7932 }
7933 
7934     static int
7935 highlight_list_arg(id, didh, type, iarg, sarg, name)
7936     int		id;
7937     int		didh;
7938     int		type;
7939     int		iarg;
7940     char_u	*sarg;
7941     char	*name;
7942 {
7943     char_u	buf[100];
7944     char_u	*ts;
7945     int		i;
7946 
7947     if (got_int)
7948 	return FALSE;
7949     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7950     {
7951 	ts = buf;
7952 	if (type == LIST_INT)
7953 	    sprintf((char *)buf, "%d", iarg - 1);
7954 	else if (type == LIST_STRING)
7955 	    ts = sarg;
7956 	else /* type == LIST_ATTR */
7957 	{
7958 	    buf[0] = NUL;
7959 	    for (i = 0; hl_attr_table[i] != 0; ++i)
7960 	    {
7961 		if (iarg & hl_attr_table[i])
7962 		{
7963 		    if (buf[0] != NUL)
7964 			STRCAT(buf, ",");
7965 		    STRCAT(buf, hl_name_table[i]);
7966 		    iarg &= ~hl_attr_table[i];	    /* don't want "inverse" */
7967 		}
7968 	    }
7969 	}
7970 
7971 	(void)syn_list_header(didh,
7972 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7973 	didh = TRUE;
7974 	if (!got_int)
7975 	{
7976 	    MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7977 	    MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7978 	    msg_outtrans(ts);
7979 	}
7980     }
7981     return didh;
7982 }
7983 
7984 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7985 /*
7986  * Return "1" if highlight group "id" has attribute "flag".
7987  * Return NULL otherwise.
7988  */
7989     char_u *
7990 highlight_has_attr(id, flag, modec)
7991     int		id;
7992     int		flag;
7993     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
7994 {
7995     int		attr;
7996 
7997     if (id <= 0 || id > highlight_ga.ga_len)
7998 	return NULL;
7999 
8000 #ifdef FEAT_GUI
8001     if (modec == 'g')
8002 	attr = HL_TABLE()[id - 1].sg_gui;
8003     else
8004 #endif
8005 	 if (modec == 'c')
8006 	attr = HL_TABLE()[id - 1].sg_cterm;
8007     else
8008 	attr = HL_TABLE()[id - 1].sg_term;
8009 
8010     if (attr & flag)
8011 	return (char_u *)"1";
8012     return NULL;
8013 }
8014 #endif
8015 
8016 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8017 /*
8018  * Return color name of highlight group "id".
8019  */
8020     char_u *
8021 highlight_color(id, what, modec)
8022     int		id;
8023     char_u	*what;	/* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
8024     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
8025 {
8026     static char_u	name[20];
8027     int			n;
8028     int			fg = FALSE;
8029 # ifdef FEAT_GUI
8030     int			sp = FALSE;
8031 # endif
8032 
8033     if (id <= 0 || id > highlight_ga.ga_len)
8034 	return NULL;
8035 
8036     if (TOLOWER_ASC(what[0]) == 'f')
8037 	fg = TRUE;
8038 # ifdef FEAT_GUI
8039     else if (TOLOWER_ASC(what[0]) == 's')
8040 	sp = TRUE;
8041     if (modec == 'g')
8042     {
8043 	/* return #RRGGBB form (only possible when GUI is running) */
8044 	if (gui.in_use && what[1] && what[2] == '#')
8045 	{
8046 	    guicolor_T		color;
8047 	    long_u		rgb;
8048 	    static char_u	buf[10];
8049 
8050 	    if (fg)
8051 		color = HL_TABLE()[id - 1].sg_gui_fg;
8052 	    else if (sp)
8053 		color = HL_TABLE()[id - 1].sg_gui_sp;
8054 	    else
8055 		color = HL_TABLE()[id - 1].sg_gui_bg;
8056 	    if (color == INVALCOLOR)
8057 		return NULL;
8058 	    rgb = gui_mch_get_rgb(color);
8059 	    sprintf((char *)buf, "#%02x%02x%02x",
8060 				      (unsigned)(rgb >> 16),
8061 				      (unsigned)(rgb >> 8) & 255,
8062 				      (unsigned)rgb & 255);
8063 	    return buf;
8064 	}
8065 	if (fg)
8066 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
8067 	if (sp)
8068 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
8069 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
8070     }
8071 # endif
8072     if (modec == 'c')
8073     {
8074 	if (fg)
8075 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8076 	else
8077 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8078 	sprintf((char *)name, "%d", n);
8079 	return name;
8080     }
8081     /* term doesn't have color */
8082     return NULL;
8083 }
8084 #endif
8085 
8086 #if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8087 	|| defined(PROTO)
8088 /*
8089  * Return color name of highlight group "id" as RGB value.
8090  */
8091     long_u
8092 highlight_gui_color_rgb(id, fg)
8093     int		id;
8094     int		fg;	/* TRUE = fg, FALSE = bg */
8095 {
8096     guicolor_T	color;
8097 
8098     if (id <= 0 || id > highlight_ga.ga_len)
8099 	return 0L;
8100 
8101     if (fg)
8102 	color = HL_TABLE()[id - 1].sg_gui_fg;
8103     else
8104 	color = HL_TABLE()[id - 1].sg_gui_bg;
8105 
8106     if (color == INVALCOLOR)
8107 	return 0L;
8108 
8109     return gui_mch_get_rgb(color);
8110 }
8111 #endif
8112 
8113 /*
8114  * Output the syntax list header.
8115  * Return TRUE when started a new line.
8116  */
8117     static int
8118 syn_list_header(did_header, outlen, id)
8119     int	    did_header;		/* did header already */
8120     int	    outlen;		/* length of string that comes */
8121     int	    id;			/* highlight group id */
8122 {
8123     int	    endcol = 19;
8124     int	    newline = TRUE;
8125 
8126     if (!did_header)
8127     {
8128 	msg_putchar('\n');
8129 	if (got_int)
8130 	    return TRUE;
8131 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
8132 	endcol = 15;
8133     }
8134     else if (msg_col + outlen + 1 >= Columns)
8135     {
8136 	msg_putchar('\n');
8137 	if (got_int)
8138 	    return TRUE;
8139     }
8140     else
8141     {
8142 	if (msg_col >= endcol)	/* wrap around is like starting a new line */
8143 	    newline = FALSE;
8144     }
8145 
8146     if (msg_col >= endcol)	/* output at least one space */
8147 	endcol = msg_col + 1;
8148     if (Columns <= endcol)	/* avoid hang for tiny window */
8149 	endcol = Columns - 1;
8150 
8151     msg_advance(endcol);
8152 
8153     /* Show "xxx" with the attributes. */
8154     if (!did_header)
8155     {
8156 	msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8157 	msg_putchar(' ');
8158     }
8159 
8160     return newline;
8161 }
8162 
8163 /*
8164  * Set the attribute numbers for a highlight group.
8165  * Called after one of the attributes has changed.
8166  */
8167     static void
8168 set_hl_attr(idx)
8169     int		idx;	    /* index in array */
8170 {
8171     attrentry_T		at_en;
8172     struct hl_group	*sgp = HL_TABLE() + idx;
8173 
8174     /* The "Normal" group doesn't need an attribute number */
8175     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8176 	return;
8177 
8178 #ifdef FEAT_GUI
8179     /*
8180      * For the GUI mode: If there are other than "normal" highlighting
8181      * attributes, need to allocate an attr number.
8182      */
8183     if (sgp->sg_gui_fg == INVALCOLOR
8184 	    && sgp->sg_gui_bg == INVALCOLOR
8185 	    && sgp->sg_gui_sp == INVALCOLOR
8186 	    && sgp->sg_font == NOFONT
8187 # ifdef FEAT_XFONTSET
8188 	    && sgp->sg_fontset == NOFONTSET
8189 # endif
8190 	    )
8191     {
8192 	sgp->sg_gui_attr = sgp->sg_gui;
8193     }
8194     else
8195     {
8196 	at_en.ae_attr = sgp->sg_gui;
8197 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8198 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
8199 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
8200 	at_en.ae_u.gui.font = sgp->sg_font;
8201 # ifdef FEAT_XFONTSET
8202 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
8203 # endif
8204 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8205     }
8206 #endif
8207     /*
8208      * For the term mode: If there are other than "normal" highlighting
8209      * attributes, need to allocate an attr number.
8210      */
8211     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8212 	sgp->sg_term_attr = sgp->sg_term;
8213     else
8214     {
8215 	at_en.ae_attr = sgp->sg_term;
8216 	at_en.ae_u.term.start = sgp->sg_start;
8217 	at_en.ae_u.term.stop = sgp->sg_stop;
8218 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8219     }
8220 
8221     /*
8222      * For the color term mode: If there are other than "normal"
8223      * highlighting attributes, need to allocate an attr number.
8224      */
8225     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8226 	sgp->sg_cterm_attr = sgp->sg_cterm;
8227     else
8228     {
8229 	at_en.ae_attr = sgp->sg_cterm;
8230 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8231 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8232 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8233     }
8234 }
8235 
8236 /*
8237  * Lookup a highlight group name and return it's ID.
8238  * If it is not found, 0 is returned.
8239  */
8240     int
8241 syn_name2id(name)
8242     char_u	*name;
8243 {
8244     int		i;
8245     char_u	name_u[200];
8246 
8247     /* Avoid using stricmp() too much, it's slow on some systems */
8248     /* Avoid alloc()/free(), these are slow too.  ID names over 200 chars
8249      * don't deserve to be found! */
8250     vim_strncpy(name_u, name, 199);
8251     vim_strup(name_u);
8252     for (i = highlight_ga.ga_len; --i >= 0; )
8253 	if (HL_TABLE()[i].sg_name_u != NULL
8254 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8255 	    break;
8256     return i + 1;
8257 }
8258 
8259 #if defined(FEAT_EVAL) || defined(PROTO)
8260 /*
8261  * Return TRUE if highlight group "name" exists.
8262  */
8263     int
8264 highlight_exists(name)
8265     char_u	*name;
8266 {
8267     return (syn_name2id(name) > 0);
8268 }
8269 #endif
8270 
8271 /*
8272  * Like syn_name2id(), but take a pointer + length argument.
8273  */
8274     int
8275 syn_namen2id(linep, len)
8276     char_u  *linep;
8277     int	    len;
8278 {
8279     char_u  *name;
8280     int	    id = 0;
8281 
8282     name = vim_strnsave(linep, len);
8283     if (name != NULL)
8284     {
8285 	id = syn_name2id(name);
8286 	vim_free(name);
8287     }
8288     return id;
8289 }
8290 
8291 /*
8292  * Find highlight group name in the table and return it's ID.
8293  * The argument is a pointer to the name and the length of the name.
8294  * If it doesn't exist yet, a new entry is created.
8295  * Return 0 for failure.
8296  */
8297     int
8298 syn_check_group(pp, len)
8299     char_u		*pp;
8300     int			len;
8301 {
8302     int	    id;
8303     char_u  *name;
8304 
8305     name = vim_strnsave(pp, len);
8306     if (name == NULL)
8307 	return 0;
8308 
8309     id = syn_name2id(name);
8310     if (id == 0)			/* doesn't exist yet */
8311 	id = syn_add_group(name);
8312     else
8313 	vim_free(name);
8314     return id;
8315 }
8316 
8317 /*
8318  * Add new highlight group and return it's ID.
8319  * "name" must be an allocated string, it will be consumed.
8320  * Return 0 for failure.
8321  */
8322     static int
8323 syn_add_group(name)
8324     char_u	*name;
8325 {
8326     char_u	*p;
8327 
8328     /* Check that the name is ASCII letters, digits and underscore. */
8329     for (p = name; *p != NUL; ++p)
8330     {
8331 	if (!vim_isprintc(*p))
8332 	{
8333 	    EMSG(_("E669: Unprintable character in group name"));
8334 	    return 0;
8335 	}
8336 	else if (!ASCII_ISALNUM(*p) && *p != '_')
8337 	{
8338 	    /* This is an error, but since there previously was no check only
8339 	     * give a warning. */
8340 	    msg_source(hl_attr(HLF_W));
8341 	    MSG(_("W18: Invalid character in group name"));
8342 	    break;
8343 	}
8344     }
8345 
8346     /*
8347      * First call for this growarray: init growing array.
8348      */
8349     if (highlight_ga.ga_data == NULL)
8350     {
8351 	highlight_ga.ga_itemsize = sizeof(struct hl_group);
8352 	highlight_ga.ga_growsize = 10;
8353     }
8354 
8355     /*
8356      * Make room for at least one other syntax_highlight entry.
8357      */
8358     if (ga_grow(&highlight_ga, 1) == FAIL)
8359     {
8360 	vim_free(name);
8361 	return 0;
8362     }
8363 
8364     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8365     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8366     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8367 #ifdef FEAT_GUI
8368     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8369     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
8370     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
8371 #endif
8372     ++highlight_ga.ga_len;
8373 
8374     return highlight_ga.ga_len;		    /* ID is index plus one */
8375 }
8376 
8377 /*
8378  * When, just after calling syn_add_group(), an error is discovered, this
8379  * function deletes the new name.
8380  */
8381     static void
8382 syn_unadd_group()
8383 {
8384     --highlight_ga.ga_len;
8385     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8386     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8387 }
8388 
8389 /*
8390  * Translate a group ID to highlight attributes.
8391  */
8392     int
8393 syn_id2attr(hl_id)
8394     int			hl_id;
8395 {
8396     int			attr;
8397     struct hl_group	*sgp;
8398 
8399     hl_id = syn_get_final_id(hl_id);
8400     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8401 
8402 #ifdef FEAT_GUI
8403     /*
8404      * Only use GUI attr when the GUI is being used.
8405      */
8406     if (gui.in_use)
8407 	attr = sgp->sg_gui_attr;
8408     else
8409 #endif
8410 	if (t_colors > 1)
8411 	    attr = sgp->sg_cterm_attr;
8412 	else
8413 	    attr = sgp->sg_term_attr;
8414 
8415     return attr;
8416 }
8417 
8418 #ifdef FEAT_GUI
8419 /*
8420  * Get the GUI colors and attributes for a group ID.
8421  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8422  */
8423     int
8424 syn_id2colors(hl_id, fgp, bgp)
8425     int		hl_id;
8426     guicolor_T	*fgp;
8427     guicolor_T	*bgp;
8428 {
8429     struct hl_group	*sgp;
8430 
8431     hl_id = syn_get_final_id(hl_id);
8432     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8433 
8434     *fgp = sgp->sg_gui_fg;
8435     *bgp = sgp->sg_gui_bg;
8436     return sgp->sg_gui;
8437 }
8438 #endif
8439 
8440 /*
8441  * Translate a group ID to the final group ID (following links).
8442  */
8443     int
8444 syn_get_final_id(hl_id)
8445     int			hl_id;
8446 {
8447     int			count;
8448     struct hl_group	*sgp;
8449 
8450     if (hl_id > highlight_ga.ga_len || hl_id < 1)
8451 	return 0;			/* Can be called from eval!! */
8452 
8453     /*
8454      * Follow links until there is no more.
8455      * Look out for loops!  Break after 100 links.
8456      */
8457     for (count = 100; --count >= 0; )
8458     {
8459 	sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8460 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8461 	    break;
8462 	hl_id = sgp->sg_link;
8463     }
8464 
8465     return hl_id;
8466 }
8467 
8468 #ifdef FEAT_GUI
8469 /*
8470  * Call this function just after the GUI has started.
8471  * It finds the font and color handles for the highlighting groups.
8472  */
8473     void
8474 highlight_gui_started()
8475 {
8476     int	    idx;
8477 
8478     /* First get the colors from the "Normal" and "Menu" group, if set */
8479     set_normal_colors();
8480 
8481     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8482 	gui_do_one_color(idx, FALSE, FALSE);
8483 
8484     highlight_changed();
8485 }
8486 
8487     static void
8488 gui_do_one_color(idx, do_menu, do_tooltip)
8489     int		idx;
8490     int		do_menu;	/* TRUE: might set the menu font */
8491     int		do_tooltip;	/* TRUE: might set the tooltip font */
8492 {
8493     int		didit = FALSE;
8494 
8495     if (HL_TABLE()[idx].sg_font_name != NULL)
8496     {
8497 	hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8498 		   do_tooltip);
8499 	didit = TRUE;
8500     }
8501     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8502     {
8503 	HL_TABLE()[idx].sg_gui_fg =
8504 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8505 	didit = TRUE;
8506     }
8507     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8508     {
8509 	HL_TABLE()[idx].sg_gui_bg =
8510 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8511 	didit = TRUE;
8512     }
8513     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8514     {
8515 	HL_TABLE()[idx].sg_gui_sp =
8516 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8517 	didit = TRUE;
8518     }
8519     if (didit)	/* need to get a new attr number */
8520 	set_hl_attr(idx);
8521 }
8522 
8523 #endif
8524 
8525 /*
8526  * Translate the 'highlight' option into attributes in highlight_attr[] and
8527  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
8528  * corresponding highlights to use on top of HLF_SNC is computed.
8529  * Called only when the 'highlight' option has been changed and upon first
8530  * screen redraw after any :highlight command.
8531  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
8532  */
8533     int
8534 highlight_changed()
8535 {
8536     int		hlf;
8537     int		i;
8538     char_u	*p;
8539     int		attr;
8540     char_u	*end;
8541     int		id;
8542 #ifdef USER_HIGHLIGHT
8543     char_u      userhl[10];
8544 # ifdef FEAT_STL_OPT
8545     int		id_SNC = -1;
8546     int		id_S = -1;
8547     int		hlcnt;
8548 # endif
8549 #endif
8550     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
8551 
8552     need_highlight_changed = FALSE;
8553 
8554     /*
8555      * Clear all attributes.
8556      */
8557     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8558 	highlight_attr[hlf] = 0;
8559 
8560     /*
8561      * First set all attributes to their default value.
8562      * Then use the attributes from the 'highlight' option.
8563      */
8564     for (i = 0; i < 2; ++i)
8565     {
8566 	if (i)
8567 	    p = p_hl;
8568 	else
8569 	    p = get_highlight_default();
8570 	if (p == NULL)	    /* just in case */
8571 	    continue;
8572 
8573 	while (*p)
8574 	{
8575 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8576 		if (hl_flags[hlf] == *p)
8577 		    break;
8578 	    ++p;
8579 	    if (hlf == (int)HLF_COUNT || *p == NUL)
8580 		return FAIL;
8581 
8582 	    /*
8583 	     * Allow several hl_flags to be combined, like "bu" for
8584 	     * bold-underlined.
8585 	     */
8586 	    attr = 0;
8587 	    for ( ; *p && *p != ','; ++p)	    /* parse upto comma */
8588 	    {
8589 		if (vim_iswhite(*p))		    /* ignore white space */
8590 		    continue;
8591 
8592 		if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
8593 		    return FAIL;
8594 
8595 		switch (*p)
8596 		{
8597 		    case 'b':	attr |= HL_BOLD;
8598 				break;
8599 		    case 'i':	attr |= HL_ITALIC;
8600 				break;
8601 		    case '-':
8602 		    case 'n':			    /* no highlighting */
8603 				break;
8604 		    case 'r':	attr |= HL_INVERSE;
8605 				break;
8606 		    case 's':	attr |= HL_STANDOUT;
8607 				break;
8608 		    case 'u':	attr |= HL_UNDERLINE;
8609 				break;
8610 		    case 'c':	attr |= HL_UNDERCURL;
8611 				break;
8612 		    case ':':	++p;		    /* highlight group name */
8613 				if (attr || *p == NUL)	 /* no combinations */
8614 				    return FAIL;
8615 				end = vim_strchr(p, ',');
8616 				if (end == NULL)
8617 				    end = p + STRLEN(p);
8618 				id = syn_check_group(p, (int)(end - p));
8619 				if (id == 0)
8620 				    return FAIL;
8621 				attr = syn_id2attr(id);
8622 				p = end - 1;
8623 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8624 				if (hlf == (int)HLF_SNC)
8625 				    id_SNC = syn_get_final_id(id);
8626 				else if (hlf == (int)HLF_S)
8627 				    id_S = syn_get_final_id(id);
8628 #endif
8629 				break;
8630 		    default:	return FAIL;
8631 		}
8632 	    }
8633 	    highlight_attr[hlf] = attr;
8634 
8635 	    p = skip_to_option_part(p);	    /* skip comma and spaces */
8636 	}
8637     }
8638 
8639 #ifdef USER_HIGHLIGHT
8640     /* Setup the user highlights
8641      *
8642      * Temporarily  utilize 10 more hl entries.  Have to be in there
8643      * simultaneously in case of table overflows in get_attr_entry()
8644      */
8645 # ifdef FEAT_STL_OPT
8646     if (ga_grow(&highlight_ga, 10) == FAIL)
8647 	return FAIL;
8648     hlcnt = highlight_ga.ga_len;
8649     if (id_S == 0)
8650     {		    /* Make sure id_S is always valid to simplify code below */
8651 	memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8652 	HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8653 	id_S = hlcnt + 10;
8654     }
8655 # endif
8656     for (i = 0; i < 9; i++)
8657     {
8658 	sprintf((char *)userhl, "User%d", i + 1);
8659 	id = syn_name2id(userhl);
8660 	if (id == 0)
8661 	{
8662 	    highlight_user[i] = 0;
8663 # ifdef FEAT_STL_OPT
8664 	    highlight_stlnc[i] = 0;
8665 # endif
8666 	}
8667 	else
8668 	{
8669 # ifdef FEAT_STL_OPT
8670 	    struct hl_group *hlt = HL_TABLE();
8671 # endif
8672 
8673 	    highlight_user[i] = syn_id2attr(id);
8674 # ifdef FEAT_STL_OPT
8675 	    if (id_SNC == 0)
8676 	    {
8677 		memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8678 		hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8679 		hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8680 #  ifdef FEAT_GUI
8681 		hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8682 #  endif
8683 	    }
8684 	    else
8685 		mch_memmove(&hlt[hlcnt + i],
8686 			    &hlt[id_SNC - 1],
8687 			    sizeof(struct hl_group));
8688 	    hlt[hlcnt + i].sg_link = 0;
8689 
8690 	    /* Apply difference between UserX and HLF_S to HLF_SNC */
8691 	    hlt[hlcnt + i].sg_term ^=
8692 		hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8693 	    if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8694 		hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8695 	    if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8696 		hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8697 	    hlt[hlcnt + i].sg_cterm ^=
8698 		hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8699 	    if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8700 		hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8701 	    if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8702 		hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8703 #  ifdef FEAT_GUI
8704 	    hlt[hlcnt + i].sg_gui ^=
8705 		hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8706 	    if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8707 		hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8708 	    if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8709 		hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
8710 	    if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8711 		hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
8712 	    if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8713 		hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8714 #   ifdef FEAT_XFONTSET
8715 	    if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8716 		hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8717 #   endif
8718 #  endif
8719 	    highlight_ga.ga_len = hlcnt + i + 1;
8720 	    set_hl_attr(hlcnt + i);	/* At long last we can apply */
8721 	    highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8722 # endif
8723 	}
8724     }
8725 # ifdef FEAT_STL_OPT
8726     highlight_ga.ga_len = hlcnt;
8727 # endif
8728 
8729 #endif /* USER_HIGHLIGHT */
8730 
8731     return OK;
8732 }
8733 
8734 #ifdef FEAT_CMDL_COMPL
8735 
8736 static void highlight_list __ARGS((void));
8737 static void highlight_list_two __ARGS((int cnt, int attr));
8738 
8739 /*
8740  * Handle command line completion for :highlight command.
8741  */
8742     void
8743 set_context_in_highlight_cmd(xp, arg)
8744     expand_T	*xp;
8745     char_u	*arg;
8746 {
8747     char_u	*p;
8748 
8749     /* Default: expand group names */
8750     xp->xp_context = EXPAND_HIGHLIGHT;
8751     xp->xp_pattern = arg;
8752     include_link = TRUE;
8753     include_default = TRUE;
8754 
8755     /* (part of) subcommand already typed */
8756     if (*arg != NUL)
8757     {
8758 	p = skiptowhite(arg);
8759 	if (*p != NUL)			/* past "default" or group name */
8760 	{
8761 	    include_default = FALSE;
8762 	    if (STRNCMP("default", arg, p - arg) == 0)
8763 	    {
8764 		arg = skipwhite(p);
8765 		xp->xp_pattern = arg;
8766 		p = skiptowhite(arg);
8767 	    }
8768 	    if (*p != NUL)			/* past group name */
8769 	    {
8770 		include_link = FALSE;
8771 		if (arg[1] == 'i' && arg[0] == 'N')
8772 		    highlight_list();
8773 		if (STRNCMP("link", arg, p - arg) == 0
8774 			|| STRNCMP("clear", arg, p - arg) == 0)
8775 		{
8776 		    xp->xp_pattern = skipwhite(p);
8777 		    p = skiptowhite(xp->xp_pattern);
8778 		    if (*p != NUL)		/* past first group name */
8779 		    {
8780 			xp->xp_pattern = skipwhite(p);
8781 			p = skiptowhite(xp->xp_pattern);
8782 		    }
8783 		}
8784 		if (*p != NUL)			/* past group name(s) */
8785 		    xp->xp_context = EXPAND_NOTHING;
8786 	    }
8787 	}
8788     }
8789 }
8790 
8791 /*
8792  * List highlighting matches in a nice way.
8793  */
8794     static void
8795 highlight_list()
8796 {
8797     int		i;
8798 
8799     for (i = 10; --i >= 0; )
8800 	highlight_list_two(i, hl_attr(HLF_D));
8801     for (i = 40; --i >= 0; )
8802 	highlight_list_two(99, 0);
8803 }
8804 
8805     static void
8806 highlight_list_two(cnt, attr)
8807     int	    cnt;
8808     int	    attr;
8809 {
8810     msg_puts_attr((char_u *)("N \bI \b!  \b" + cnt / 11), attr);
8811     msg_clr_eos();
8812     out_flush();
8813     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8814 }
8815 
8816 #endif /* FEAT_CMDL_COMPL */
8817 
8818 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8819     || defined(FEAT_SIGNS) || defined(PROTO)
8820 /*
8821  * Function given to ExpandGeneric() to obtain the list of group names.
8822  * Also used for synIDattr() function.
8823  */
8824 /*ARGSUSED*/
8825     char_u *
8826 get_highlight_name(xp, idx)
8827     expand_T	*xp;
8828     int		idx;
8829 {
8830     if (idx == highlight_ga.ga_len
8831 #ifdef FEAT_CMDL_COMPL
8832 	    && include_link
8833 #endif
8834 	    )
8835 	return (char_u *)"link";
8836     if (idx == highlight_ga.ga_len + 1
8837 #ifdef FEAT_CMDL_COMPL
8838 	    && include_link
8839 #endif
8840 	    )
8841 	return (char_u *)"clear";
8842     if (idx == highlight_ga.ga_len + 2
8843 #ifdef FEAT_CMDL_COMPL
8844 	    && include_default
8845 #endif
8846 	    )
8847 	return (char_u *)"default";
8848     if (idx < 0 || idx >= highlight_ga.ga_len)
8849 	return NULL;
8850     return HL_TABLE()[idx].sg_name;
8851 }
8852 #endif
8853 
8854 #ifdef FEAT_GUI
8855 /*
8856  * Free all the highlight group fonts.
8857  * Used when quitting for systems which need it.
8858  */
8859     void
8860 free_highlight_fonts()
8861 {
8862     int	    idx;
8863 
8864     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8865     {
8866 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
8867 	HL_TABLE()[idx].sg_font = NOFONT;
8868 # ifdef FEAT_XFONTSET
8869 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8870 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
8871 # endif
8872     }
8873 
8874     gui_mch_free_font(gui.norm_font);
8875 # ifdef FEAT_XFONTSET
8876     gui_mch_free_fontset(gui.fontset);
8877 # endif
8878 # ifndef HAVE_GTK2
8879     gui_mch_free_font(gui.bold_font);
8880     gui_mch_free_font(gui.ital_font);
8881     gui_mch_free_font(gui.boldital_font);
8882 # endif
8883 }
8884 #endif
8885 
8886 /**************************************
8887  *  End of Highlighting stuff	      *
8888  **************************************/
8889