xref: /vim-8.2.3635/src/syntax.c (revision 05a7bb36)
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. */
2978 	len = STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
2979 	if (col > len)
2980 	    result->col = len;
2981 	else
2982 	    result->col = col;
2983     }
2984 }
2985 
2986 /*
2987  * Add offset to matched text for start of match or highlight.
2988  * Avoid resulting column to become negative.
2989  */
2990     static void
2991 syn_add_start_off(result, regmatch, spp, idx, extra)
2992     lpos_T	*result;	/* returned position */
2993     regmmatch_T	*regmatch;	/* start/end of match */
2994     synpat_T	*spp;
2995     int		idx;
2996     int		extra;	    /* extra chars for offset to end */
2997 {
2998     int		col;
2999 
3000     if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3001     {
3002 	result->lnum = regmatch->endpos[0].lnum;
3003 	col = regmatch->endpos[0].col + extra;
3004     }
3005     else
3006     {
3007 	result->lnum = regmatch->startpos[0].lnum;
3008 	col = regmatch->startpos[0].col;
3009     }
3010     col += spp->sp_offsets[idx];
3011     if (col < 0)
3012 	result->col = 0;
3013     else
3014 	result->col = col;
3015 }
3016 
3017 /*
3018  * Get current line in syntax buffer.
3019  */
3020     static char_u *
3021 syn_getcurline()
3022 {
3023     return ml_get_buf(syn_buf, current_lnum, FALSE);
3024 }
3025 
3026 /*
3027  * Call vim_regexec() to find a match with "rmp" in "syn_buf".
3028  * Returns TRUE when there is a match.
3029  */
3030     static int
3031 syn_regexec(rmp, lnum, col)
3032     regmmatch_T	*rmp;
3033     linenr_T	lnum;
3034     colnr_T	col;
3035 {
3036     rmp->rmm_maxcol = syn_buf->b_p_smc;
3037     if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
3038     {
3039 	rmp->startpos[0].lnum += lnum;
3040 	rmp->endpos[0].lnum += lnum;
3041 	return TRUE;
3042     }
3043     return FALSE;
3044 }
3045 
3046 /*
3047  * Check one position in a line for a matching keyword.
3048  * The caller must check if a keyword can start at startcol.
3049  * Return it's ID if found, 0 otherwise.
3050  */
3051     static int
3052 check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3053     char_u	*line;
3054     int		startcol;	/* position in line to check for keyword */
3055     int		*endcolp;	/* return: character after found keyword */
3056     long	*flagsp;	/* return: flags of matching keyword */
3057     short	**next_listp;	/* return: next_list of matching keyword */
3058     stateitem_T	*cur_si;	/* item at the top of the stack */
3059 {
3060     keyentry_T	*kp;
3061     char_u	*kwp;
3062     int		round;
3063     int		kwlen;
3064     char_u	keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
3065     hashtab_T	*ht;
3066     hashitem_T	*hi;
3067 
3068     /* Find first character after the keyword.  First character was already
3069      * checked. */
3070     kwp = line + startcol;
3071     kwlen = 0;
3072     do
3073     {
3074 #ifdef FEAT_MBYTE
3075 	if (has_mbyte)
3076 	    kwlen += (*mb_ptr2len)(kwp + kwlen);
3077 	else
3078 #endif
3079 	    ++kwlen;
3080     }
3081     while (vim_iswordc_buf(kwp + kwlen, syn_buf));
3082 
3083     if (kwlen > MAXKEYWLEN)
3084 	return 0;
3085 
3086     /*
3087      * Must make a copy of the keyword, so we can add a NUL and make it
3088      * lowercase.
3089      */
3090     vim_strncpy(keyword, kwp, kwlen);
3091 
3092     /*
3093      * Try twice:
3094      * 1. matching case
3095      * 2. ignoring case
3096      */
3097     for (round = 1; round <= 2; ++round)
3098     {
3099 	ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3100 	if (ht->ht_used == 0)
3101 	    continue;
3102 	if (round == 2)	/* ignore case */
3103 	    (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
3104 
3105 	/*
3106 	 * Find keywords that match.  There can be several with different
3107 	 * attributes.
3108 	 * When current_next_list is non-zero accept only that group, otherwise:
3109 	 *  Accept a not-contained keyword at toplevel.
3110 	 *  Accept a keyword at other levels only if it is in the contains list.
3111 	 */
3112 	hi = hash_find(ht, keyword);
3113 	if (!HASHITEM_EMPTY(hi))
3114 	    for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
3115 	    {
3116 		if (current_next_list != 0
3117 			? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3118 			: (cur_si == NULL
3119 			    ? !(kp->flags & HL_CONTAINED)
3120 			    : in_id_list(cur_si, cur_si->si_cont_list,
3121 				      &kp->k_syn, kp->flags & HL_CONTAINED)))
3122 		{
3123 		    *endcolp = startcol + kwlen;
3124 		    *flagsp = kp->flags;
3125 		    *next_listp = kp->next_list;
3126 		    return kp->k_syn.id;
3127 		}
3128 	    }
3129     }
3130     return 0;
3131 }
3132 
3133 /*
3134  * Handle ":syntax case" command.
3135  */
3136 /* ARGSUSED */
3137     static void
3138 syn_cmd_case(eap, syncing)
3139     exarg_T	*eap;
3140     int		syncing;	    /* not used */
3141 {
3142     char_u	*arg = eap->arg;
3143     char_u	*next;
3144 
3145     eap->nextcmd = find_nextcmd(arg);
3146     if (eap->skip)
3147 	return;
3148 
3149     next = skiptowhite(arg);
3150     if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3151 	curbuf->b_syn_ic = FALSE;
3152     else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3153 	curbuf->b_syn_ic = TRUE;
3154     else
3155 	EMSG2(_("E390: Illegal argument: %s"), arg);
3156 }
3157 
3158 /*
3159  * Handle ":syntax spell" command.
3160  */
3161 /* ARGSUSED */
3162     static void
3163 syn_cmd_spell(eap, syncing)
3164     exarg_T	*eap;
3165     int		syncing;	    /* not used */
3166 {
3167     char_u	*arg = eap->arg;
3168     char_u	*next;
3169 
3170     eap->nextcmd = find_nextcmd(arg);
3171     if (eap->skip)
3172 	return;
3173 
3174     next = skiptowhite(arg);
3175     if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3176 	curbuf->b_syn_spell = SYNSPL_TOP;
3177     else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3178 	curbuf->b_syn_spell = SYNSPL_NOTOP;
3179     else if (STRNICMP(arg, "default", 4) == 0 && next - arg == 4)
3180 	curbuf->b_syn_spell = SYNSPL_DEFAULT;
3181     else
3182 	EMSG2(_("E390: Illegal argument: %s"), arg);
3183 }
3184 
3185 /*
3186  * Clear all syntax info for one buffer.
3187  */
3188     void
3189 syntax_clear(buf)
3190     buf_T	*buf;
3191 {
3192     int i;
3193 
3194     buf->b_syn_error = FALSE;	    /* clear previous error */
3195     buf->b_syn_ic = FALSE;	    /* Use case, by default */
3196     buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3197     buf->b_syn_containedin = FALSE;
3198 
3199     /* free the keywords */
3200     clear_keywtab(&buf->b_keywtab);
3201     clear_keywtab(&buf->b_keywtab_ic);
3202 
3203     /* free the syntax patterns */
3204     for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3205 	syn_clear_pattern(buf, i);
3206     ga_clear(&buf->b_syn_patterns);
3207 
3208     /* free the syntax clusters */
3209     for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3210 	syn_clear_cluster(buf, i);
3211     ga_clear(&buf->b_syn_clusters);
3212     buf->b_spell_cluster_id = 0;
3213     buf->b_nospell_cluster_id = 0;
3214 
3215     buf->b_syn_sync_flags = 0;
3216     buf->b_syn_sync_minlines = 0;
3217     buf->b_syn_sync_maxlines = 0;
3218     buf->b_syn_sync_linebreaks = 0;
3219 
3220     vim_free(buf->b_syn_linecont_prog);
3221     buf->b_syn_linecont_prog = NULL;
3222     vim_free(buf->b_syn_linecont_pat);
3223     buf->b_syn_linecont_pat = NULL;
3224 #ifdef FEAT_FOLDING
3225     buf->b_syn_folditems = 0;
3226 #endif
3227 
3228     /* free the stored states */
3229     syn_stack_free_all(buf);
3230     invalidate_current_state();
3231 }
3232 
3233 /*
3234  * Clear syncing info for one buffer.
3235  */
3236     static void
3237 syntax_sync_clear()
3238 {
3239     int i;
3240 
3241     /* free the syntax patterns */
3242     for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3243 	if (SYN_ITEMS(curbuf)[i].sp_syncing)
3244 	    syn_remove_pattern(curbuf, i);
3245 
3246     curbuf->b_syn_sync_flags = 0;
3247     curbuf->b_syn_sync_minlines = 0;
3248     curbuf->b_syn_sync_maxlines = 0;
3249     curbuf->b_syn_sync_linebreaks = 0;
3250 
3251     vim_free(curbuf->b_syn_linecont_prog);
3252     curbuf->b_syn_linecont_prog = NULL;
3253     vim_free(curbuf->b_syn_linecont_pat);
3254     curbuf->b_syn_linecont_pat = NULL;
3255 
3256     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3257 }
3258 
3259 /*
3260  * Remove one pattern from the buffer's pattern list.
3261  */
3262     static void
3263 syn_remove_pattern(buf, idx)
3264     buf_T	*buf;
3265     int		idx;
3266 {
3267     synpat_T	*spp;
3268 
3269     spp = &(SYN_ITEMS(buf)[idx]);
3270 #ifdef FEAT_FOLDING
3271     if (spp->sp_flags & HL_FOLD)
3272 	--buf->b_syn_folditems;
3273 #endif
3274     syn_clear_pattern(buf, idx);
3275     mch_memmove(spp, spp + 1,
3276 		   sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3277     --buf->b_syn_patterns.ga_len;
3278 }
3279 
3280 /*
3281  * Clear and free one syntax pattern.  When clearing all, must be called from
3282  * last to first!
3283  */
3284     static void
3285 syn_clear_pattern(buf, i)
3286     buf_T	*buf;
3287     int		i;
3288 {
3289     vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3290     vim_free(SYN_ITEMS(buf)[i].sp_prog);
3291     /* Only free sp_cont_list and sp_next_list of first start pattern */
3292     if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3293     {
3294 	vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3295 	vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3296     }
3297 }
3298 
3299 /*
3300  * Clear and free one syntax cluster.
3301  */
3302     static void
3303 syn_clear_cluster(buf, i)
3304     buf_T	*buf;
3305     int		i;
3306 {
3307     vim_free(SYN_CLSTR(buf)[i].scl_name);
3308     vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3309     vim_free(SYN_CLSTR(buf)[i].scl_list);
3310 }
3311 
3312 /*
3313  * Handle ":syntax clear" command.
3314  */
3315     static void
3316 syn_cmd_clear(eap, syncing)
3317     exarg_T	*eap;
3318     int		syncing;
3319 {
3320     char_u	*arg = eap->arg;
3321     char_u	*arg_end;
3322     int		id;
3323 
3324     eap->nextcmd = find_nextcmd(arg);
3325     if (eap->skip)
3326 	return;
3327 
3328     /*
3329      * We have to disable this within ":syn include @group filename",
3330      * because otherwise @group would get deleted.
3331      * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3332      * clear".
3333      */
3334     if (curbuf->b_syn_topgrp != 0)
3335 	return;
3336 
3337     if (ends_excmd(*arg))
3338     {
3339 	/*
3340 	 * No argument: Clear all syntax items.
3341 	 */
3342 	if (syncing)
3343 	    syntax_sync_clear();
3344 	else
3345 	{
3346 	    syntax_clear(curbuf);
3347 	    do_unlet((char_u *)"b:current_syntax", TRUE);
3348 	}
3349     }
3350     else
3351     {
3352 	/*
3353 	 * Clear the group IDs that are in the argument.
3354 	 */
3355 	while (!ends_excmd(*arg))
3356 	{
3357 	    arg_end = skiptowhite(arg);
3358 	    if (*arg == '@')
3359 	    {
3360 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3361 		if (id == 0)
3362 		{
3363 		    EMSG2(_("E391: No such syntax cluster: %s"), arg);
3364 		    break;
3365 		}
3366 		else
3367 		{
3368 		    /*
3369 		     * We can't physically delete a cluster without changing
3370 		     * the IDs of other clusters, so we do the next best thing
3371 		     * and make it empty.
3372 		     */
3373 		    short scl_id = id - SYNID_CLUSTER;
3374 
3375 		    vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3376 		    SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3377 		}
3378 	    }
3379 	    else
3380 	    {
3381 		id = syn_namen2id(arg, (int)(arg_end - arg));
3382 		if (id == 0)
3383 		{
3384 		    EMSG2(_(e_nogroup), arg);
3385 		    break;
3386 		}
3387 		else
3388 		    syn_clear_one(id, syncing);
3389 	    }
3390 	    arg = skipwhite(arg_end);
3391 	}
3392     }
3393     redraw_curbuf_later(NOT_VALID);
3394     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3395 }
3396 
3397 /*
3398  * Clear one syntax group for the current buffer.
3399  */
3400     static void
3401 syn_clear_one(id, syncing)
3402     int		id;
3403     int		syncing;
3404 {
3405     synpat_T	*spp;
3406     int		idx;
3407 
3408     /* Clear keywords only when not ":syn sync clear group-name" */
3409     if (!syncing)
3410     {
3411 	(void)syn_clear_keyword(id, &curbuf->b_keywtab);
3412 	(void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
3413     }
3414 
3415     /* clear the patterns for "id" */
3416     for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3417     {
3418 	spp = &(SYN_ITEMS(curbuf)[idx]);
3419 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3420 	    continue;
3421 	syn_remove_pattern(curbuf, idx);
3422     }
3423 }
3424 
3425 /*
3426  * Handle ":syntax on" command.
3427  */
3428 /* ARGSUSED */
3429     static void
3430 syn_cmd_on(eap, syncing)
3431     exarg_T	*eap;
3432     int		syncing;	/* not used */
3433 {
3434     syn_cmd_onoff(eap, "syntax");
3435 }
3436 
3437 /*
3438  * Handle ":syntax enable" command.
3439  */
3440 /* ARGSUSED */
3441     static void
3442 syn_cmd_enable(eap, syncing)
3443     exarg_T	*eap;
3444     int		syncing;	/* not used */
3445 {
3446     set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3447     syn_cmd_onoff(eap, "syntax");
3448     do_unlet((char_u *)"g:syntax_cmd", TRUE);
3449 }
3450 
3451 /*
3452  * Handle ":syntax reset" command.
3453  */
3454 /* ARGSUSED */
3455     static void
3456 syn_cmd_reset(eap, syncing)
3457     exarg_T	*eap;
3458     int		syncing;	/* not used */
3459 {
3460     eap->nextcmd = check_nextcmd(eap->arg);
3461     if (!eap->skip)
3462     {
3463 	set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3464 	do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
3465 	do_unlet((char_u *)"g:syntax_cmd", TRUE);
3466     }
3467 }
3468 
3469 /*
3470  * Handle ":syntax manual" command.
3471  */
3472 /* ARGSUSED */
3473     static void
3474 syn_cmd_manual(eap, syncing)
3475     exarg_T	*eap;
3476     int		syncing;	/* not used */
3477 {
3478     syn_cmd_onoff(eap, "manual");
3479 }
3480 
3481 /*
3482  * Handle ":syntax off" command.
3483  */
3484 /* ARGSUSED */
3485     static void
3486 syn_cmd_off(eap, syncing)
3487     exarg_T	*eap;
3488     int		syncing;	/* not used */
3489 {
3490     syn_cmd_onoff(eap, "nosyntax");
3491 }
3492 
3493     static void
3494 syn_cmd_onoff(eap, name)
3495     exarg_T	*eap;
3496     char	*name;
3497 {
3498     char_u	buf[100];
3499 
3500     eap->nextcmd = check_nextcmd(eap->arg);
3501     if (!eap->skip)
3502     {
3503 	STRCPY(buf, "so ");
3504 	vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
3505 	do_cmdline_cmd(buf);
3506     }
3507 }
3508 
3509 /*
3510  * Handle ":syntax [list]" command: list current syntax words.
3511  */
3512     static void
3513 syn_cmd_list(eap, syncing)
3514     exarg_T	*eap;
3515     int		syncing;	    /* when TRUE: list syncing items */
3516 {
3517     char_u	*arg = eap->arg;
3518     int		id;
3519     char_u	*arg_end;
3520 
3521     eap->nextcmd = find_nextcmd(arg);
3522     if (eap->skip)
3523 	return;
3524 
3525     if (!syntax_present(curbuf))
3526     {
3527 	MSG(_("No Syntax items defined for this buffer"));
3528 	return;
3529     }
3530 
3531     if (syncing)
3532     {
3533 	if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3534 	{
3535 	    MSG_PUTS(_("syncing on C-style comments"));
3536 	    syn_lines_msg();
3537 	    syn_match_msg();
3538 	    return;
3539 	}
3540 	else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3541 	{
3542 	    if (curbuf->b_syn_sync_minlines == 0)
3543 		MSG_PUTS(_("no syncing"));
3544 	    else
3545 	    {
3546 		MSG_PUTS(_("syncing starts "));
3547 		msg_outnum(curbuf->b_syn_sync_minlines);
3548 		MSG_PUTS(_(" lines before top line"));
3549 		syn_match_msg();
3550 	    }
3551 	    return;
3552 	}
3553 	MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3554 	if (curbuf->b_syn_sync_minlines > 0
3555 		|| curbuf->b_syn_sync_maxlines > 0
3556 		|| curbuf->b_syn_sync_linebreaks > 0)
3557 	{
3558 	    MSG_PUTS(_("\nsyncing on items"));
3559 	    syn_lines_msg();
3560 	    syn_match_msg();
3561 	}
3562     }
3563     else
3564 	MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3565     if (ends_excmd(*arg))
3566     {
3567 	/*
3568 	 * No argument: List all group IDs and all syntax clusters.
3569 	 */
3570 	for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3571 	    syn_list_one(id, syncing, FALSE);
3572 	for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3573 	    syn_list_cluster(id);
3574     }
3575     else
3576     {
3577 	/*
3578 	 * List the group IDs and syntax clusters that are in the argument.
3579 	 */
3580 	while (!ends_excmd(*arg) && !got_int)
3581 	{
3582 	    arg_end = skiptowhite(arg);
3583 	    if (*arg == '@')
3584 	    {
3585 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3586 		if (id == 0)
3587 		    EMSG2(_("E392: No such syntax cluster: %s"), arg);
3588 		else
3589 		    syn_list_cluster(id - SYNID_CLUSTER);
3590 	    }
3591 	    else
3592 	    {
3593 		id = syn_namen2id(arg, (int)(arg_end - arg));
3594 		if (id == 0)
3595 		    EMSG2(_(e_nogroup), arg);
3596 		else
3597 		    syn_list_one(id, syncing, TRUE);
3598 	    }
3599 	    arg = skipwhite(arg_end);
3600 	}
3601     }
3602     eap->nextcmd = check_nextcmd(arg);
3603 }
3604 
3605     static void
3606 syn_lines_msg()
3607 {
3608     if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3609     {
3610 	MSG_PUTS("; ");
3611 	if (curbuf->b_syn_sync_minlines > 0)
3612 	{
3613 	    MSG_PUTS(_("minimal "));
3614 	    msg_outnum(curbuf->b_syn_sync_minlines);
3615 	    if (curbuf->b_syn_sync_maxlines)
3616 		MSG_PUTS(", ");
3617 	}
3618 	if (curbuf->b_syn_sync_maxlines > 0)
3619 	{
3620 	    MSG_PUTS(_("maximal "));
3621 	    msg_outnum(curbuf->b_syn_sync_maxlines);
3622 	}
3623 	MSG_PUTS(_(" lines before top line"));
3624     }
3625 }
3626 
3627     static void
3628 syn_match_msg()
3629 {
3630     if (curbuf->b_syn_sync_linebreaks > 0)
3631     {
3632 	MSG_PUTS(_("; match "));
3633 	msg_outnum(curbuf->b_syn_sync_linebreaks);
3634 	MSG_PUTS(_(" line breaks"));
3635     }
3636 }
3637 
3638 static int  last_matchgroup;
3639 
3640 struct name_list
3641 {
3642     int		flag;
3643     char	*name;
3644 };
3645 
3646 static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3647 
3648 /*
3649  * List one syntax item, for ":syntax" or "syntax list syntax_name".
3650  */
3651     static void
3652 syn_list_one(id, syncing, link_only)
3653     int		id;
3654     int		syncing;	    /* when TRUE: list syncing items */
3655     int		link_only;	    /* when TRUE; list link-only too */
3656 {
3657     int		attr;
3658     int		idx;
3659     int		did_header = FALSE;
3660     synpat_T	*spp;
3661     static struct name_list namelist1[] =
3662 		{
3663 		    {HL_DISPLAY, "display"},
3664 		    {HL_CONTAINED, "contained"},
3665 		    {HL_ONELINE, "oneline"},
3666 		    {HL_KEEPEND, "keepend"},
3667 		    {HL_EXTEND, "extend"},
3668 		    {HL_EXCLUDENL, "excludenl"},
3669 		    {HL_TRANSP, "transparent"},
3670 		    {HL_FOLD, "fold"},
3671 		    {0, NULL}
3672 		};
3673     static struct name_list namelist2[] =
3674 		{
3675 		    {HL_SKIPWHITE, "skipwhite"},
3676 		    {HL_SKIPNL, "skipnl"},
3677 		    {HL_SKIPEMPTY, "skipempty"},
3678 		    {0, NULL}
3679 		};
3680 
3681     attr = hl_attr(HLF_D);		/* highlight like directories */
3682 
3683     /* list the keywords for "id" */
3684     if (!syncing)
3685     {
3686 	did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3687 	did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
3688 							    did_header, attr);
3689     }
3690 
3691     /* list the patterns for "id" */
3692     for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3693     {
3694 	spp = &(SYN_ITEMS(curbuf)[idx]);
3695 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3696 	    continue;
3697 
3698 	(void)syn_list_header(did_header, 999, id);
3699 	did_header = TRUE;
3700 	last_matchgroup = 0;
3701 	if (spp->sp_type == SPTYPE_MATCH)
3702 	{
3703 	    put_pattern("match", ' ', spp, attr);
3704 	    msg_putchar(' ');
3705 	}
3706 	else if (spp->sp_type == SPTYPE_START)
3707 	{
3708 	    while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3709 		put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3710 	    if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3711 		put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3712 	    while (idx < curbuf->b_syn_patterns.ga_len
3713 			      && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3714 		put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3715 	    --idx;
3716 	    msg_putchar(' ');
3717 	}
3718 	syn_list_flags(namelist1, spp->sp_flags, attr);
3719 
3720 	if (spp->sp_cont_list != NULL)
3721 	    put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3722 
3723 	if (spp->sp_syn.cont_in_list != NULL)
3724 	    put_id_list((char_u *)"containedin",
3725 					      spp->sp_syn.cont_in_list, attr);
3726 
3727 	if (spp->sp_next_list != NULL)
3728 	{
3729 	    put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3730 	    syn_list_flags(namelist2, spp->sp_flags, attr);
3731 	}
3732 	if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3733 	{
3734 	    if (spp->sp_flags & HL_SYNC_HERE)
3735 		msg_puts_attr((char_u *)"grouphere", attr);
3736 	    else
3737 		msg_puts_attr((char_u *)"groupthere", attr);
3738 	    msg_putchar(' ');
3739 	    if (spp->sp_sync_idx >= 0)
3740 		msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3741 				   [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3742 	    else
3743 		MSG_PUTS("NONE");
3744 	    msg_putchar(' ');
3745 	}
3746     }
3747 
3748     /* list the link, if there is one */
3749     if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3750     {
3751 	(void)syn_list_header(did_header, 999, id);
3752 	msg_puts_attr((char_u *)"links to", attr);
3753 	msg_putchar(' ');
3754 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3755     }
3756 }
3757 
3758     static void
3759 syn_list_flags(nl, flags, attr)
3760     struct name_list	*nl;
3761     int			flags;
3762     int			attr;
3763 {
3764     int		i;
3765 
3766     for (i = 0; nl[i].flag != 0; ++i)
3767 	if (flags & nl[i].flag)
3768 	{
3769 	    msg_puts_attr((char_u *)nl[i].name, attr);
3770 	    msg_putchar(' ');
3771 	}
3772 }
3773 
3774 /*
3775  * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3776  */
3777     static void
3778 syn_list_cluster(id)
3779     int id;
3780 {
3781     int	    endcol = 15;
3782 
3783     /* slight hack:  roughly duplicate the guts of syn_list_header() */
3784     msg_putchar('\n');
3785     msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3786 
3787     if (msg_col >= endcol)	/* output at least one space */
3788 	endcol = msg_col + 1;
3789     if (Columns <= endcol)	/* avoid hang for tiny window */
3790 	endcol = Columns - 1;
3791 
3792     msg_advance(endcol);
3793     if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3794     {
3795 	put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3796 		    hl_attr(HLF_D));
3797     }
3798     else
3799     {
3800 	msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3801 	msg_puts((char_u *)"=NONE");
3802     }
3803 }
3804 
3805     static void
3806 put_id_list(name, list, attr)
3807     char_u	*name;
3808     short	*list;
3809     int		attr;
3810 {
3811     short		*p;
3812 
3813     msg_puts_attr(name, attr);
3814     msg_putchar('=');
3815     for (p = list; *p; ++p)
3816     {
3817 	if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3818 	{
3819 	    if (p[1])
3820 		MSG_PUTS("ALLBUT");
3821 	    else
3822 		MSG_PUTS("ALL");
3823 	}
3824 	else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3825 	{
3826 	    MSG_PUTS("TOP");
3827 	}
3828 	else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3829 	{
3830 	    MSG_PUTS("CONTAINED");
3831 	}
3832 	else if (*p >= SYNID_CLUSTER)
3833 	{
3834 	    short scl_id = *p - SYNID_CLUSTER;
3835 
3836 	    msg_putchar('@');
3837 	    msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3838 	}
3839 	else
3840 	    msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3841 	if (p[1])
3842 	    msg_putchar(',');
3843     }
3844     msg_putchar(' ');
3845 }
3846 
3847     static void
3848 put_pattern(s, c, spp, attr)
3849     char	*s;
3850     int		c;
3851     synpat_T	*spp;
3852     int		attr;
3853 {
3854     long	n;
3855     int		mask;
3856     int		first;
3857     static char	*sepchars = "/+=-#@\"|'^&";
3858     int		i;
3859 
3860     /* May have to write "matchgroup=group" */
3861     if (last_matchgroup != spp->sp_syn_match_id)
3862     {
3863 	last_matchgroup = spp->sp_syn_match_id;
3864 	msg_puts_attr((char_u *)"matchgroup", attr);
3865 	msg_putchar('=');
3866 	if (last_matchgroup == 0)
3867 	    msg_outtrans((char_u *)"NONE");
3868 	else
3869 	    msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3870 	msg_putchar(' ');
3871     }
3872 
3873     /* output the name of the pattern and an '=' or ' ' */
3874     msg_puts_attr((char_u *)s, attr);
3875     msg_putchar(c);
3876 
3877     /* output the pattern, in between a char that is not in the pattern */
3878     for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3879 	if (sepchars[++i] == NUL)
3880 	{
3881 	    i = 0;	/* no good char found, just use the first one */
3882 	    break;
3883 	}
3884     msg_putchar(sepchars[i]);
3885     msg_outtrans(spp->sp_pattern);
3886     msg_putchar(sepchars[i]);
3887 
3888     /* output any pattern options */
3889     first = TRUE;
3890     for (i = 0; i < SPO_COUNT; ++i)
3891     {
3892 	mask = (1 << i);
3893 	if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3894 	{
3895 	    if (!first)
3896 		msg_putchar(',');	/* separate with commas */
3897 	    msg_puts((char_u *)spo_name_tab[i]);
3898 	    n = spp->sp_offsets[i];
3899 	    if (i != SPO_LC_OFF)
3900 	    {
3901 		if (spp->sp_off_flags & mask)
3902 		    msg_putchar('s');
3903 		else
3904 		    msg_putchar('e');
3905 		if (n > 0)
3906 		    msg_putchar('+');
3907 	    }
3908 	    if (n || i == SPO_LC_OFF)
3909 		msg_outnum(n);
3910 	    first = FALSE;
3911 	}
3912     }
3913     msg_putchar(' ');
3914 }
3915 
3916 /*
3917  * List or clear the keywords for one syntax group.
3918  * Return TRUE if the header has been printed.
3919  */
3920     static int
3921 syn_list_keywords(id, ht, did_header, attr)
3922     int		id;
3923     hashtab_T	*ht;
3924     int		did_header;		/* header has already been printed */
3925     int		attr;
3926 {
3927     int		outlen;
3928     hashitem_T	*hi;
3929     keyentry_T	*kp;
3930     int		todo;
3931     int		prev_contained = 0;
3932     short	*prev_next_list = NULL;
3933     short	*prev_cont_in_list = NULL;
3934     int		prev_skipnl = 0;
3935     int		prev_skipwhite = 0;
3936     int		prev_skipempty = 0;
3937 
3938     /*
3939      * Unfortunately, this list of keywords is not sorted on alphabet but on
3940      * hash value...
3941      */
3942     todo = ht->ht_used;
3943     for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
3944     {
3945 	if (!HASHITEM_EMPTY(hi))
3946 	{
3947 	    --todo;
3948 	    for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
3949 	    {
3950 		if (kp->k_syn.id == id)
3951 		{
3952 		    if (prev_contained != (kp->flags & HL_CONTAINED)
3953 			    || prev_skipnl != (kp->flags & HL_SKIPNL)
3954 			    || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3955 			    || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3956 			    || prev_cont_in_list != kp->k_syn.cont_in_list
3957 			    || prev_next_list != kp->next_list)
3958 			outlen = 9999;
3959 		    else
3960 			outlen = (int)STRLEN(kp->keyword);
3961 		    /* output "contained" and "nextgroup" on each line */
3962 		    if (syn_list_header(did_header, outlen, id))
3963 		    {
3964 			prev_contained = 0;
3965 			prev_next_list = NULL;
3966 			prev_cont_in_list = NULL;
3967 			prev_skipnl = 0;
3968 			prev_skipwhite = 0;
3969 			prev_skipempty = 0;
3970 		    }
3971 		    did_header = TRUE;
3972 		    if (prev_contained != (kp->flags & HL_CONTAINED))
3973 		    {
3974 			msg_puts_attr((char_u *)"contained", attr);
3975 			msg_putchar(' ');
3976 			prev_contained = (kp->flags & HL_CONTAINED);
3977 		    }
3978 		    if (kp->k_syn.cont_in_list != prev_cont_in_list)
3979 		    {
3980 			put_id_list((char_u *)"containedin",
3981 						kp->k_syn.cont_in_list, attr);
3982 			msg_putchar(' ');
3983 			prev_cont_in_list = kp->k_syn.cont_in_list;
3984 		    }
3985 		    if (kp->next_list != prev_next_list)
3986 		    {
3987 			put_id_list((char_u *)"nextgroup", kp->next_list, attr);
3988 			msg_putchar(' ');
3989 			prev_next_list = kp->next_list;
3990 			if (kp->flags & HL_SKIPNL)
3991 			{
3992 			    msg_puts_attr((char_u *)"skipnl", attr);
3993 			    msg_putchar(' ');
3994 			    prev_skipnl = (kp->flags & HL_SKIPNL);
3995 			}
3996 			if (kp->flags & HL_SKIPWHITE)
3997 			{
3998 			    msg_puts_attr((char_u *)"skipwhite", attr);
3999 			    msg_putchar(' ');
4000 			    prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4001 			}
4002 			if (kp->flags & HL_SKIPEMPTY)
4003 			{
4004 			    msg_puts_attr((char_u *)"skipempty", attr);
4005 			    msg_putchar(' ');
4006 			    prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4007 			}
4008 		    }
4009 		    msg_outtrans(kp->keyword);
4010 		}
4011 	    }
4012 	}
4013     }
4014 
4015     return did_header;
4016 }
4017 
4018     static void
4019 syn_clear_keyword(id, ht)
4020     int		id;
4021     hashtab_T	*ht;
4022 {
4023     hashitem_T	*hi;
4024     keyentry_T	*kp;
4025     keyentry_T	*kp_prev;
4026     keyentry_T	*kp_next;
4027     int		todo;
4028 
4029     hash_lock(ht);
4030     todo = ht->ht_used;
4031     for (hi = ht->ht_array; todo > 0; ++hi)
4032     {
4033 	if (!HASHITEM_EMPTY(hi))
4034 	{
4035 	    --todo;
4036 	    kp_prev = NULL;
4037 	    for (kp = HI2KE(hi); kp != NULL; )
4038 	    {
4039 		if (kp->k_syn.id == id)
4040 		{
4041 		    kp_next = kp->ke_next;
4042 		    if (kp_prev == NULL)
4043 		    {
4044 			if (kp_next == NULL)
4045 			    hash_remove(ht, hi);
4046 			else
4047 			    hi->hi_key = KE2HIKEY(kp_next);
4048 		    }
4049 		    else
4050 			kp_prev->ke_next = kp_next;
4051 		    vim_free(kp->next_list);
4052 		    vim_free(kp->k_syn.cont_in_list);
4053 		    vim_free(kp);
4054 		    kp = kp_next;
4055 		}
4056 		else
4057 		{
4058 		    kp_prev = kp;
4059 		    kp = kp->ke_next;
4060 		}
4061 	    }
4062 	}
4063     }
4064     hash_unlock(ht);
4065 }
4066 
4067 /*
4068  * Clear a whole keyword table.
4069  */
4070     static void
4071 clear_keywtab(ht)
4072     hashtab_T	*ht;
4073 {
4074     hashitem_T	*hi;
4075     int		todo;
4076     keyentry_T	*kp;
4077     keyentry_T	*kp_next;
4078 
4079     todo = ht->ht_used;
4080     for (hi = ht->ht_array; todo > 0; ++hi)
4081     {
4082 	if (!HASHITEM_EMPTY(hi))
4083 	{
4084 	    --todo;
4085 	    kp = HI2KE(hi);
4086 	    for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
4087 	    {
4088 		kp_next = kp->ke_next;
4089 		vim_free(kp->next_list);
4090 		vim_free(kp->k_syn.cont_in_list);
4091 		vim_free(kp);
4092 	    }
4093 	}
4094     }
4095     hash_clear(ht);
4096     hash_init(ht);
4097 }
4098 
4099 /*
4100  * Add a keyword to the list of keywords.
4101  */
4102     static void
4103 add_keyword(name, id, flags, cont_in_list, next_list)
4104     char_u	*name;	    /* name of keyword */
4105     int		id;	    /* group ID for this keyword */
4106     int		flags;	    /* flags for this keyword */
4107     short	*cont_in_list; /* containedin for this keyword */
4108     short	*next_list; /* nextgroup for this keyword */
4109 {
4110     keyentry_T	*kp;
4111     hashtab_T	*ht;
4112     hashitem_T	*hi;
4113     char_u	*name_ic;
4114     long_u	hash;
4115     char_u	name_folded[MAXKEYWLEN + 1];
4116 
4117     if (curbuf->b_syn_ic)
4118 	name_ic = str_foldcase(name, (int)STRLEN(name),
4119 						 name_folded, MAXKEYWLEN + 1);
4120     else
4121 	name_ic = name;
4122     kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4123     if (kp == NULL)
4124 	return;
4125     STRCPY(kp->keyword, name_ic);
4126     kp->k_syn.id = id;
4127     kp->k_syn.inc_tag = current_syn_inc_tag;
4128     kp->flags = flags;
4129     kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
4130     if (cont_in_list != NULL)
4131 	curbuf->b_syn_containedin = TRUE;
4132     kp->next_list = copy_id_list(next_list);
4133 
4134     if (curbuf->b_syn_ic)
4135 	ht = &curbuf->b_keywtab_ic;
4136     else
4137 	ht = &curbuf->b_keywtab;
4138 
4139     hash = hash_hash(kp->keyword);
4140     hi = hash_lookup(ht, kp->keyword, hash);
4141     if (HASHITEM_EMPTY(hi))
4142     {
4143 	/* new keyword, add to hashtable */
4144 	kp->ke_next = NULL;
4145 	hash_add_item(ht, hi, kp->keyword, hash);
4146     }
4147     else
4148     {
4149 	/* keyword already exists, prepend to list */
4150 	kp->ke_next = HI2KE(hi);
4151 	hi->hi_key = KE2HIKEY(kp);
4152     }
4153 }
4154 
4155 /*
4156  * Get the start and end of the group name argument.
4157  * Return a pointer to the first argument.
4158  * Return NULL if the end of the command was found instead of further args.
4159  */
4160     static char_u *
4161 get_group_name(arg, name_end)
4162     char_u	*arg;		/* start of the argument */
4163     char_u	**name_end;	/* pointer to end of the name */
4164 {
4165     char_u	*rest;
4166 
4167     *name_end = skiptowhite(arg);
4168     rest = skipwhite(*name_end);
4169 
4170     /*
4171      * Check if there are enough arguments.  The first argument may be a
4172      * pattern, where '|' is allowed, so only check for NUL.
4173      */
4174     if (ends_excmd(*arg) || *rest == NUL)
4175 	return NULL;
4176     return rest;
4177 }
4178 
4179 /*
4180  * Check for syntax command option arguments.
4181  * This can be called at any place in the list of arguments, and just picks
4182  * out the arguments that are known.  Can be called several times in a row to
4183  * collect all options in between other arguments.
4184  * Return a pointer to the next argument (which isn't an option).
4185  * Return NULL for any error;
4186  */
4187     static char_u *
4188 get_syn_options(arg, opt)
4189     char_u	    *arg;		/* next argument to be checked */
4190     syn_opt_arg_T   *opt;		/* various things */
4191 {
4192     char_u	*gname_start, *gname;
4193     int		syn_id;
4194     int		len;
4195     char	*p;
4196     int		i;
4197     int		fidx;
4198     static struct flag
4199     {
4200 	char	*name;
4201 	int	argtype;
4202 	int	flags;
4203     } flagtab[] = { {"cCoOnNtTaAiInNeEdD",	0,	HL_CONTAINED},
4204 		    {"oOnNeElLiInNeE",		0,	HL_ONELINE},
4205 		    {"kKeEeEpPeEnNdD",		0,	HL_KEEPEND},
4206 		    {"eExXtTeEnNdD",		0,	HL_EXTEND},
4207 		    {"eExXcClLuUdDeEnNlL",	0,	HL_EXCLUDENL},
4208 		    {"tTrRaAnNsSpPaArReEnNtT",	0,	HL_TRANSP},
4209 		    {"sSkKiIpPnNlL",		0,	HL_SKIPNL},
4210 		    {"sSkKiIpPwWhHiItTeE",	0,	HL_SKIPWHITE},
4211 		    {"sSkKiIpPeEmMpPtTyY",	0,	HL_SKIPEMPTY},
4212 		    {"gGrRoOuUpPhHeErReE",	0,	HL_SYNC_HERE},
4213 		    {"gGrRoOuUpPtThHeErReE",	0,	HL_SYNC_THERE},
4214 		    {"dDiIsSpPlLaAyY",		0,	HL_DISPLAY},
4215 		    {"fFoOlLdD",		0,	HL_FOLD},
4216 		    {"cCoOnNtTaAiInNsS",	1,	0},
4217 		    {"cCoOnNtTaAiInNeEdDiInN",	2,	0},
4218 		    {"nNeExXtTgGrRoOuUpP",	3,	0},
4219 		};
4220     static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
4221 
4222     if (arg == NULL)		/* already detected error */
4223 	return NULL;
4224 
4225     for (;;)
4226     {
4227 	/*
4228 	 * This is used very often when a large number of keywords is defined.
4229 	 * Need to skip quickly when no option name is found.
4230 	 * Also avoid tolower(), it's slow.
4231 	 */
4232 	if (strchr(first_letters, *arg) == NULL)
4233 	    break;
4234 
4235 	for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4236 	{
4237 	    p = flagtab[fidx].name;
4238 	    for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4239 		if (arg[len] != p[i] && arg[len] != p[i + 1])
4240 		    break;
4241 	    if (p[i] == NUL && (vim_iswhite(arg[len])
4242 				    || (flagtab[fidx].argtype > 0
4243 					 ? arg[len] == '='
4244 					 : ends_excmd(arg[len]))))
4245 	    {
4246 		if (opt->keyword
4247 			&& (flagtab[fidx].flags == HL_DISPLAY
4248 			    || flagtab[fidx].flags == HL_FOLD
4249 			    || flagtab[fidx].flags == HL_EXTEND))
4250 		    /* treat "display", "fold" and "extend" as a keyword */
4251 		    fidx = -1;
4252 		break;
4253 	    }
4254 	}
4255 	if (fidx < 0)	    /* no match found */
4256 	    break;
4257 
4258 	if (flagtab[fidx].argtype == 1)
4259 	{
4260 	    if (!opt->has_cont_list)
4261 	    {
4262 		EMSG(_("E395: contains argument not accepted here"));
4263 		return NULL;
4264 	    }
4265 	    if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
4266 		return NULL;
4267 	}
4268 	else if (flagtab[fidx].argtype == 2)
4269 	{
4270 #if 0	    /* cannot happen */
4271 	    if (opt->cont_in_list == NULL)
4272 	    {
4273 		EMSG(_("E396: containedin argument not accepted here"));
4274 		return NULL;
4275 	    }
4276 #endif
4277 	    if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
4278 		return NULL;
4279 	}
4280 	else if (flagtab[fidx].argtype == 3)
4281 	{
4282 	    if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
4283 		return NULL;
4284 	}
4285 	else
4286 	{
4287 	    opt->flags |= flagtab[fidx].flags;
4288 	    arg = skipwhite(arg + len);
4289 
4290 	    if (flagtab[fidx].flags == HL_SYNC_HERE
4291 		    || flagtab[fidx].flags == HL_SYNC_THERE)
4292 	    {
4293 		if (opt->sync_idx == NULL)
4294 		{
4295 		    EMSG(_("E393: group[t]here not accepted here"));
4296 		    return NULL;
4297 		}
4298 		gname_start = arg;
4299 		arg = skiptowhite(arg);
4300 		if (gname_start == arg)
4301 		    return NULL;
4302 		gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4303 		if (gname == NULL)
4304 		    return NULL;
4305 		if (STRCMP(gname, "NONE") == 0)
4306 		    *opt->sync_idx = NONE_IDX;
4307 		else
4308 		{
4309 		    syn_id = syn_name2id(gname);
4310 		    for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4311 			if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4312 			      && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4313 			{
4314 			    *opt->sync_idx = i;
4315 			    break;
4316 			}
4317 		    if (i < 0)
4318 		    {
4319 			EMSG2(_("E394: Didn't find region item for %s"), gname);
4320 			vim_free(gname);
4321 			return NULL;
4322 		    }
4323 		}
4324 
4325 		vim_free(gname);
4326 		arg = skipwhite(arg);
4327 	    }
4328 #ifdef FEAT_FOLDING
4329 	    else if (flagtab[fidx].flags == HL_FOLD
4330 						&& foldmethodIsSyntax(curwin))
4331 		/* Need to update folds later. */
4332 		foldUpdateAll(curwin);
4333 #endif
4334 	}
4335     }
4336 
4337     return arg;
4338 }
4339 
4340 /*
4341  * Adjustments to syntax item when declared in a ":syn include"'d file.
4342  * Set the contained flag, and if the item is not already contained, add it
4343  * to the specified top-level group, if any.
4344  */
4345     static void
4346 syn_incl_toplevel(id, flagsp)
4347     int		id;
4348     int		*flagsp;
4349 {
4350     if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4351 	return;
4352     *flagsp |= HL_CONTAINED;
4353     if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4354     {
4355 	/* We have to alloc this, because syn_combine_list() will free it. */
4356 	short	    *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4357 	int	    tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4358 
4359 	if (grp_list != NULL)
4360 	{
4361 	    grp_list[0] = id;
4362 	    grp_list[1] = 0;
4363 	    syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4364 			 CLUSTER_ADD);
4365 	}
4366     }
4367 }
4368 
4369 /*
4370  * Handle ":syntax include [@{group-name}] filename" command.
4371  */
4372 /* ARGSUSED */
4373     static void
4374 syn_cmd_include(eap, syncing)
4375     exarg_T	*eap;
4376     int		syncing;	    /* not used */
4377 {
4378     char_u	*arg = eap->arg;
4379     int		sgl_id = 1;
4380     char_u	*group_name_end;
4381     char_u	*rest;
4382     char_u	*errormsg = NULL;
4383     int		prev_toplvl_grp;
4384     int		prev_syn_inc_tag;
4385     int		source = FALSE;
4386 
4387     eap->nextcmd = find_nextcmd(arg);
4388     if (eap->skip)
4389 	return;
4390 
4391     if (arg[0] == '@')
4392     {
4393 	++arg;
4394 	rest = get_group_name(arg, &group_name_end);
4395 	if (rest == NULL)
4396 	{
4397 	    EMSG((char_u *)_("E397: Filename required"));
4398 	    return;
4399 	}
4400 	sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4401 	/* separate_nextcmd() and expand_filename() depend on this */
4402 	eap->arg = rest;
4403     }
4404 
4405     /*
4406      * Everything that's left, up to the next command, should be the
4407      * filename to include.
4408      */
4409     eap->argt |= (XFILE | NOSPC);
4410     separate_nextcmd(eap);
4411     if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4412     {
4413 	/* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4414 	 * file.  Need to expand the file name first.  In other cases
4415 	 * ":runtime!" is used. */
4416 	source = TRUE;
4417 	if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4418 	{
4419 	    if (errormsg != NULL)
4420 		EMSG(errormsg);
4421 	    return;
4422 	}
4423     }
4424 
4425     /*
4426      * Save and restore the existing top-level grouplist id and ":syn
4427      * include" tag around the actual inclusion.
4428      */
4429     prev_syn_inc_tag = current_syn_inc_tag;
4430     current_syn_inc_tag = ++running_syn_inc_tag;
4431     prev_toplvl_grp = curbuf->b_syn_topgrp;
4432     curbuf->b_syn_topgrp = sgl_id;
4433     if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
4434 				     : source_runtime(eap->arg, TRUE) == FAIL)
4435 	EMSG2(_(e_notopen), eap->arg);
4436     curbuf->b_syn_topgrp = prev_toplvl_grp;
4437     current_syn_inc_tag = prev_syn_inc_tag;
4438 }
4439 
4440 /*
4441  * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4442  */
4443 /* ARGSUSED */
4444     static void
4445 syn_cmd_keyword(eap, syncing)
4446     exarg_T	*eap;
4447     int		syncing;	    /* not used */
4448 {
4449     char_u	*arg = eap->arg;
4450     char_u	*group_name_end;
4451     int		syn_id;
4452     char_u	*rest;
4453     char_u	*keyword_copy;
4454     char_u	*p;
4455     char_u	*kw;
4456     syn_opt_arg_T syn_opt_arg;
4457     int		cnt;
4458 
4459     rest = get_group_name(arg, &group_name_end);
4460 
4461     if (rest != NULL)
4462     {
4463 	syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4464 
4465 	/* allocate a buffer, for removing the backslashes in the keyword */
4466 	keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4467 	if (keyword_copy != NULL)
4468 	{
4469 	    syn_opt_arg.flags = 0;
4470 	    syn_opt_arg.keyword = TRUE;
4471 	    syn_opt_arg.sync_idx = NULL;
4472 	    syn_opt_arg.has_cont_list = FALSE;
4473 	    syn_opt_arg.cont_in_list = NULL;
4474 	    syn_opt_arg.next_list = NULL;
4475 
4476 	    /*
4477 	     * The options given apply to ALL keywords, so all options must be
4478 	     * found before keywords can be created.
4479 	     * 1: collect the options and copy the keywords to keyword_copy.
4480 	     */
4481 	    cnt = 0;
4482 	    p = keyword_copy;
4483 	    for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
4484 	    {
4485 		rest = get_syn_options(rest, &syn_opt_arg);
4486 		if (rest == NULL || ends_excmd(*rest))
4487 		    break;
4488 		/* Copy the keyword, removing backslashes, and add a NUL. */
4489 		while (*rest != NUL && !vim_iswhite(*rest))
4490 		{
4491 		    if (*rest == '\\' && rest[1] != NUL)
4492 			++rest;
4493 		    *p++ = *rest++;
4494 		}
4495 		*p++ = NUL;
4496 		++cnt;
4497 	    }
4498 
4499 	    if (!eap->skip)
4500 	    {
4501 		/* Adjust flags for use of ":syn include". */
4502 		syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4503 
4504 		/*
4505 		 * 2: Add an entry for each keyword.
4506 		 */
4507 		for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4508 		{
4509 		    for (p = vim_strchr(kw, '['); ; )
4510 		    {
4511 			if (p != NULL)
4512 			    *p = NUL;
4513 			add_keyword(kw, syn_id, syn_opt_arg.flags,
4514 						     syn_opt_arg.cont_in_list,
4515 						       syn_opt_arg.next_list);
4516 			if (p == NULL)
4517 			    break;
4518 			if (p[1] == NUL)
4519 			{
4520 			    EMSG2(_("E747: Missing ']': %s"), kw);
4521 			    kw = p + 2;		/* skip over the NUL */
4522 			    break;
4523 			}
4524 			if (p[1] == ']')
4525 			{
4526 			    kw = p + 1;		/* skip over the "]" */
4527 			    break;
4528 			}
4529 #ifdef FEAT_MBYTE
4530 			if (has_mbyte)
4531 			{
4532 			    int l = (*mb_ptr2len)(p + 1);
4533 
4534 			    mch_memmove(p, p + 1, l);
4535 			    p += l;
4536 			}
4537 			else
4538 #endif
4539 			{
4540 			    p[0] = p[1];
4541 			    ++p;
4542 			}
4543 		    }
4544 		}
4545 	    }
4546 
4547 	    vim_free(keyword_copy);
4548 	    vim_free(syn_opt_arg.cont_in_list);
4549 	    vim_free(syn_opt_arg.next_list);
4550 	}
4551     }
4552 
4553     if (rest != NULL)
4554 	eap->nextcmd = check_nextcmd(rest);
4555     else
4556 	EMSG2(_(e_invarg2), arg);
4557 
4558     redraw_curbuf_later(NOT_VALID);
4559     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
4560 }
4561 
4562 /*
4563  * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4564  *
4565  * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4566  */
4567     static void
4568 syn_cmd_match(eap, syncing)
4569     exarg_T	*eap;
4570     int		syncing;	    /* TRUE for ":syntax sync match .. " */
4571 {
4572     char_u	*arg = eap->arg;
4573     char_u	*group_name_end;
4574     char_u	*rest;
4575     synpat_T	item;		/* the item found in the line */
4576     int		syn_id;
4577     int		idx;
4578     syn_opt_arg_T syn_opt_arg;
4579     int		sync_idx = 0;
4580 
4581     /* Isolate the group name, check for validity */
4582     rest = get_group_name(arg, &group_name_end);
4583 
4584     /* Get options before the pattern */
4585     syn_opt_arg.flags = 0;
4586     syn_opt_arg.keyword = FALSE;
4587     syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4588     syn_opt_arg.has_cont_list = TRUE;
4589     syn_opt_arg.cont_list = NULL;
4590     syn_opt_arg.cont_in_list = NULL;
4591     syn_opt_arg.next_list = NULL;
4592     rest = get_syn_options(rest, &syn_opt_arg);
4593 
4594     /* get the pattern. */
4595     init_syn_patterns();
4596     vim_memset(&item, 0, sizeof(item));
4597     rest = get_syn_pattern(rest, &item);
4598     if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4599 	syn_opt_arg.flags |= HL_HAS_EOL;
4600 
4601     /* Get options after the pattern */
4602     rest = get_syn_options(rest, &syn_opt_arg);
4603 
4604     if (rest != NULL)		/* all arguments are valid */
4605     {
4606 	/*
4607 	 * Check for trailing command and illegal trailing arguments.
4608 	 */
4609 	eap->nextcmd = check_nextcmd(rest);
4610 	if (!ends_excmd(*rest) || eap->skip)
4611 	    rest = NULL;
4612 	else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4613 		&& (syn_id = syn_check_group(arg,
4614 					   (int)(group_name_end - arg))) != 0)
4615 	{
4616 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4617 	    /*
4618 	     * Store the pattern in the syn_items list
4619 	     */
4620 	    idx = curbuf->b_syn_patterns.ga_len;
4621 	    SYN_ITEMS(curbuf)[idx] = item;
4622 	    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4623 	    SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4624 	    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4625 	    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4626 	    SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
4627 	    SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
4628 	    SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4629 	    SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4630 						     syn_opt_arg.cont_in_list;
4631 	    if (syn_opt_arg.cont_in_list != NULL)
4632 		curbuf->b_syn_containedin = TRUE;
4633 	    SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
4634 	    ++curbuf->b_syn_patterns.ga_len;
4635 
4636 	    /* remember that we found a match for syncing on */
4637 	    if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4638 		curbuf->b_syn_sync_flags |= SF_MATCH;
4639 #ifdef FEAT_FOLDING
4640 	    if (syn_opt_arg.flags & HL_FOLD)
4641 		++curbuf->b_syn_folditems;
4642 #endif
4643 
4644 	    redraw_curbuf_later(NOT_VALID);
4645 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4646 	    return;	/* don't free the progs and patterns now */
4647 	}
4648     }
4649 
4650     /*
4651      * Something failed, free the allocated memory.
4652      */
4653     vim_free(item.sp_prog);
4654     vim_free(item.sp_pattern);
4655     vim_free(syn_opt_arg.cont_list);
4656     vim_free(syn_opt_arg.cont_in_list);
4657     vim_free(syn_opt_arg.next_list);
4658 
4659     if (rest == NULL)
4660 	EMSG2(_(e_invarg2), arg);
4661 }
4662 
4663 /*
4664  * Handle ":syntax region {group-name} [matchgroup={group-name}]
4665  *		start {start} .. [skip {skip}] end {end} .. [{options}]".
4666  */
4667     static void
4668 syn_cmd_region(eap, syncing)
4669     exarg_T	*eap;
4670     int		syncing;	    /* TRUE for ":syntax sync region .." */
4671 {
4672     char_u		*arg = eap->arg;
4673     char_u		*group_name_end;
4674     char_u		*rest;			/* next arg, NULL on error */
4675     char_u		*key_end;
4676     char_u		*key = NULL;
4677     char_u		*p;
4678     int			item;
4679 #define ITEM_START	    0
4680 #define ITEM_SKIP	    1
4681 #define ITEM_END	    2
4682 #define ITEM_MATCHGROUP	    3
4683     struct pat_ptr
4684     {
4685 	synpat_T	*pp_synp;		/* pointer to syn_pattern */
4686 	int		pp_matchgroup_id;	/* matchgroup ID */
4687 	struct pat_ptr	*pp_next;		/* pointer to next pat_ptr */
4688     }			*(pat_ptrs[3]);
4689 					/* patterns found in the line */
4690     struct pat_ptr	*ppp;
4691     struct pat_ptr	*ppp_next;
4692     int			pat_count = 0;		/* nr of syn_patterns found */
4693     int			syn_id;
4694     int			matchgroup_id = 0;
4695     int			not_enough = FALSE;	/* not enough arguments */
4696     int			illegal = FALSE;	/* illegal arguments */
4697     int			success = FALSE;
4698     int			idx;
4699     syn_opt_arg_T	syn_opt_arg;
4700 
4701     /* Isolate the group name, check for validity */
4702     rest = get_group_name(arg, &group_name_end);
4703 
4704     pat_ptrs[0] = NULL;
4705     pat_ptrs[1] = NULL;
4706     pat_ptrs[2] = NULL;
4707 
4708     init_syn_patterns();
4709 
4710     syn_opt_arg.flags = 0;
4711     syn_opt_arg.keyword = FALSE;
4712     syn_opt_arg.sync_idx = NULL;
4713     syn_opt_arg.has_cont_list = TRUE;
4714     syn_opt_arg.cont_list = NULL;
4715     syn_opt_arg.cont_in_list = NULL;
4716     syn_opt_arg.next_list = NULL;
4717 
4718     /*
4719      * get the options, patterns and matchgroup.
4720      */
4721     while (rest != NULL && !ends_excmd(*rest))
4722     {
4723 	/* Check for option arguments */
4724 	rest = get_syn_options(rest, &syn_opt_arg);
4725 	if (rest == NULL || ends_excmd(*rest))
4726 	    break;
4727 
4728 	/* must be a pattern or matchgroup then */
4729 	key_end = rest;
4730 	while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4731 	    ++key_end;
4732 	vim_free(key);
4733 	key = vim_strnsave_up(rest, (int)(key_end - rest));
4734 	if (key == NULL)			/* out of memory */
4735 	{
4736 	    rest = NULL;
4737 	    break;
4738 	}
4739 	if (STRCMP(key, "MATCHGROUP") == 0)
4740 	    item = ITEM_MATCHGROUP;
4741 	else if (STRCMP(key, "START") == 0)
4742 	    item = ITEM_START;
4743 	else if (STRCMP(key, "END") == 0)
4744 	    item = ITEM_END;
4745 	else if (STRCMP(key, "SKIP") == 0)
4746 	{
4747 	    if (pat_ptrs[ITEM_SKIP] != NULL)	/* one skip pattern allowed */
4748 	    {
4749 		illegal = TRUE;
4750 		break;
4751 	    }
4752 	    item = ITEM_SKIP;
4753 	}
4754 	else
4755 	    break;
4756 	rest = skipwhite(key_end);
4757 	if (*rest != '=')
4758 	{
4759 	    rest = NULL;
4760 	    EMSG2(_("E398: Missing '=': %s"), arg);
4761 	    break;
4762 	}
4763 	rest = skipwhite(rest + 1);
4764 	if (*rest == NUL)
4765 	{
4766 	    not_enough = TRUE;
4767 	    break;
4768 	}
4769 
4770 	if (item == ITEM_MATCHGROUP)
4771 	{
4772 	    p = skiptowhite(rest);
4773 	    if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4774 		matchgroup_id = 0;
4775 	    else
4776 	    {
4777 		matchgroup_id = syn_check_group(rest, (int)(p - rest));
4778 		if (matchgroup_id == 0)
4779 		{
4780 		    illegal = TRUE;
4781 		    break;
4782 		}
4783 	    }
4784 	    rest = skipwhite(p);
4785 	}
4786 	else
4787 	{
4788 	    /*
4789 	     * Allocate room for a syn_pattern, and link it in the list of
4790 	     * syn_patterns for this item, at the start (because the list is
4791 	     * used from end to start).
4792 	     */
4793 	    ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4794 	    if (ppp == NULL)
4795 	    {
4796 		rest = NULL;
4797 		break;
4798 	    }
4799 	    ppp->pp_next = pat_ptrs[item];
4800 	    pat_ptrs[item] = ppp;
4801 	    ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4802 	    if (ppp->pp_synp == NULL)
4803 	    {
4804 		rest = NULL;
4805 		break;
4806 	    }
4807 
4808 	    /*
4809 	     * Get the syntax pattern and the following offset(s).
4810 	     */
4811 	    /* Enable the appropriate \z specials. */
4812 	    if (item == ITEM_START)
4813 		reg_do_extmatch = REX_SET;
4814 	    else if (item == ITEM_SKIP || item == ITEM_END)
4815 		reg_do_extmatch = REX_USE;
4816 	    rest = get_syn_pattern(rest, ppp->pp_synp);
4817 	    reg_do_extmatch = 0;
4818 	    if (item == ITEM_END && vim_regcomp_had_eol()
4819 				       && !(syn_opt_arg.flags & HL_EXCLUDENL))
4820 		ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4821 	    ppp->pp_matchgroup_id = matchgroup_id;
4822 	    ++pat_count;
4823 	}
4824     }
4825     vim_free(key);
4826     if (illegal || not_enough)
4827 	rest = NULL;
4828 
4829     /*
4830      * Must have a "start" and "end" pattern.
4831      */
4832     if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4833 						  pat_ptrs[ITEM_END] == NULL))
4834     {
4835 	not_enough = TRUE;
4836 	rest = NULL;
4837     }
4838 
4839     if (rest != NULL)
4840     {
4841 	/*
4842 	 * Check for trailing garbage or command.
4843 	 * If OK, add the item.
4844 	 */
4845 	eap->nextcmd = check_nextcmd(rest);
4846 	if (!ends_excmd(*rest) || eap->skip)
4847 	    rest = NULL;
4848 	else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4849 		&& (syn_id = syn_check_group(arg,
4850 					   (int)(group_name_end - arg))) != 0)
4851 	{
4852 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4853 	    /*
4854 	     * Store the start/skip/end in the syn_items list
4855 	     */
4856 	    idx = curbuf->b_syn_patterns.ga_len;
4857 	    for (item = ITEM_START; item <= ITEM_END; ++item)
4858 	    {
4859 		for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4860 		{
4861 		    SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4862 		    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4863 		    SYN_ITEMS(curbuf)[idx].sp_type =
4864 			    (item == ITEM_START) ? SPTYPE_START :
4865 			    (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
4866 		    SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
4867 		    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4868 		    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4869 		    SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4870 							ppp->pp_matchgroup_id;
4871 		    if (item == ITEM_START)
4872 		    {
4873 			SYN_ITEMS(curbuf)[idx].sp_cont_list =
4874 							syn_opt_arg.cont_list;
4875 			SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4876 						     syn_opt_arg.cont_in_list;
4877 			if (syn_opt_arg.cont_in_list != NULL)
4878 			    curbuf->b_syn_containedin = TRUE;
4879 			SYN_ITEMS(curbuf)[idx].sp_next_list =
4880 							syn_opt_arg.next_list;
4881 		    }
4882 		    ++curbuf->b_syn_patterns.ga_len;
4883 		    ++idx;
4884 #ifdef FEAT_FOLDING
4885 		    if (syn_opt_arg.flags & HL_FOLD)
4886 			++curbuf->b_syn_folditems;
4887 #endif
4888 		}
4889 	    }
4890 
4891 	    redraw_curbuf_later(NOT_VALID);
4892 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4893 	    success = TRUE;	    /* don't free the progs and patterns now */
4894 	}
4895     }
4896 
4897     /*
4898      * Free the allocated memory.
4899      */
4900     for (item = ITEM_START; item <= ITEM_END; ++item)
4901 	for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4902 	{
4903 	    if (!success)
4904 	    {
4905 		vim_free(ppp->pp_synp->sp_prog);
4906 		vim_free(ppp->pp_synp->sp_pattern);
4907 	    }
4908 	    vim_free(ppp->pp_synp);
4909 	    ppp_next = ppp->pp_next;
4910 	    vim_free(ppp);
4911 	}
4912 
4913     if (!success)
4914     {
4915 	vim_free(syn_opt_arg.cont_list);
4916 	vim_free(syn_opt_arg.cont_in_list);
4917 	vim_free(syn_opt_arg.next_list);
4918 	if (not_enough)
4919 	    EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4920 	else if (illegal || rest == NULL)
4921 	    EMSG2(_(e_invarg2), arg);
4922     }
4923 }
4924 
4925 /*
4926  * A simple syntax group ID comparison function suitable for use in qsort()
4927  */
4928     static int
4929 #ifdef __BORLANDC__
4930 _RTLENTRYF
4931 #endif
4932 syn_compare_stub(v1, v2)
4933     const void	*v1;
4934     const void	*v2;
4935 {
4936     const short	*s1 = v1;
4937     const short	*s2 = v2;
4938 
4939     return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4940 }
4941 
4942 /*
4943  * Combines lists of syntax clusters.
4944  * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4945  */
4946     static void
4947 syn_combine_list(clstr1, clstr2, list_op)
4948     short	**clstr1;
4949     short	**clstr2;
4950     int		list_op;
4951 {
4952     int		count1 = 0;
4953     int		count2 = 0;
4954     short	*g1;
4955     short	*g2;
4956     short	*clstr = NULL;
4957     int		count;
4958     int		round;
4959 
4960     /*
4961      * Handle degenerate cases.
4962      */
4963     if (*clstr2 == NULL)
4964 	return;
4965     if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4966     {
4967 	if (list_op == CLUSTER_REPLACE)
4968 	    vim_free(*clstr1);
4969 	if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
4970 	    *clstr1 = *clstr2;
4971 	else
4972 	    vim_free(*clstr2);
4973 	return;
4974     }
4975 
4976     for (g1 = *clstr1; *g1; g1++)
4977 	++count1;
4978     for (g2 = *clstr2; *g2; g2++)
4979 	++count2;
4980 
4981     /*
4982      * For speed purposes, sort both lists.
4983      */
4984     qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
4985     qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
4986 
4987     /*
4988      * We proceed in two passes; in round 1, we count the elements to place
4989      * in the new list, and in round 2, we allocate and populate the new
4990      * list.  For speed, we use a mergesort-like method, adding the smaller
4991      * of the current elements in each list to the new list.
4992      */
4993     for (round = 1; round <= 2; round++)
4994     {
4995 	g1 = *clstr1;
4996 	g2 = *clstr2;
4997 	count = 0;
4998 
4999 	/*
5000 	 * First, loop through the lists until one of them is empty.
5001 	 */
5002 	while (*g1 && *g2)
5003 	{
5004 	    /*
5005 	     * We always want to add from the first list.
5006 	     */
5007 	    if (*g1 < *g2)
5008 	    {
5009 		if (round == 2)
5010 		    clstr[count] = *g1;
5011 		count++;
5012 		g1++;
5013 		continue;
5014 	    }
5015 	    /*
5016 	     * We only want to add from the second list if we're adding the
5017 	     * lists.
5018 	     */
5019 	    if (list_op == CLUSTER_ADD)
5020 	    {
5021 		if (round == 2)
5022 		    clstr[count] = *g2;
5023 		count++;
5024 	    }
5025 	    if (*g1 == *g2)
5026 		g1++;
5027 	    g2++;
5028 	}
5029 
5030 	/*
5031 	 * Now add the leftovers from whichever list didn't get finished
5032 	 * first.  As before, we only want to add from the second list if
5033 	 * we're adding the lists.
5034 	 */
5035 	for (; *g1; g1++, count++)
5036 	    if (round == 2)
5037 		clstr[count] = *g1;
5038 	if (list_op == CLUSTER_ADD)
5039 	    for (; *g2; g2++, count++)
5040 		if (round == 2)
5041 		    clstr[count] = *g2;
5042 
5043 	if (round == 1)
5044 	{
5045 	    /*
5046 	     * If the group ended up empty, we don't need to allocate any
5047 	     * space for it.
5048 	     */
5049 	    if (count == 0)
5050 	    {
5051 		clstr = NULL;
5052 		break;
5053 	    }
5054 	    clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5055 	    if (clstr == NULL)
5056 		break;
5057 	    clstr[count] = 0;
5058 	}
5059     }
5060 
5061     /*
5062      * Finally, put the new list in place.
5063      */
5064     vim_free(*clstr1);
5065     vim_free(*clstr2);
5066     *clstr1 = clstr;
5067 }
5068 
5069 /*
5070  * Lookup a syntax cluster name and return it's ID.
5071  * If it is not found, 0 is returned.
5072  */
5073     static int
5074 syn_scl_name2id(name)
5075     char_u	*name;
5076 {
5077     int		i;
5078     char_u	*name_u;
5079 
5080     /* Avoid using stricmp() too much, it's slow on some systems */
5081     name_u = vim_strsave_up(name);
5082     if (name_u == NULL)
5083 	return 0;
5084     for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5085 	if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5086 		&& STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5087 	    break;
5088     vim_free(name_u);
5089     return (i < 0 ? 0 : i + SYNID_CLUSTER);
5090 }
5091 
5092 /*
5093  * Like syn_scl_name2id(), but take a pointer + length argument.
5094  */
5095     static int
5096 syn_scl_namen2id(linep, len)
5097     char_u  *linep;
5098     int	    len;
5099 {
5100     char_u  *name;
5101     int	    id = 0;
5102 
5103     name = vim_strnsave(linep, len);
5104     if (name != NULL)
5105     {
5106 	id = syn_scl_name2id(name);
5107 	vim_free(name);
5108     }
5109     return id;
5110 }
5111 
5112 /*
5113  * Find syntax cluster name in the table and return it's ID.
5114  * The argument is a pointer to the name and the length of the name.
5115  * If it doesn't exist yet, a new entry is created.
5116  * Return 0 for failure.
5117  */
5118     static int
5119 syn_check_cluster(pp, len)
5120     char_u	*pp;
5121     int		len;
5122 {
5123     int		id;
5124     char_u	*name;
5125 
5126     name = vim_strnsave(pp, len);
5127     if (name == NULL)
5128 	return 0;
5129 
5130     id = syn_scl_name2id(name);
5131     if (id == 0)			/* doesn't exist yet */
5132 	id = syn_add_cluster(name);
5133     else
5134 	vim_free(name);
5135     return id;
5136 }
5137 
5138 /*
5139  * Add new syntax cluster and return it's ID.
5140  * "name" must be an allocated string, it will be consumed.
5141  * Return 0 for failure.
5142  */
5143     static int
5144 syn_add_cluster(name)
5145     char_u	*name;
5146 {
5147     int		len;
5148 
5149     /*
5150      * First call for this growarray: init growing array.
5151      */
5152     if (curbuf->b_syn_clusters.ga_data == NULL)
5153     {
5154 	curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5155 	curbuf->b_syn_clusters.ga_growsize = 10;
5156     }
5157 
5158     /*
5159      * Make room for at least one other cluster entry.
5160      */
5161     if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5162     {
5163 	vim_free(name);
5164 	return 0;
5165     }
5166     len = curbuf->b_syn_clusters.ga_len;
5167 
5168     vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
5169     SYN_CLSTR(curbuf)[len].scl_name = name;
5170     SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5171     SYN_CLSTR(curbuf)[len].scl_list = NULL;
5172     ++curbuf->b_syn_clusters.ga_len;
5173 
5174     if (STRICMP(name, "Spell") == 0)
5175 	curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
5176     if (STRICMP(name, "NoSpell") == 0)
5177 	curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
5178 
5179     return len + SYNID_CLUSTER;
5180 }
5181 
5182 /*
5183  * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5184  *		[add={groupname},..] [remove={groupname},..]".
5185  */
5186 /* ARGSUSED */
5187     static void
5188 syn_cmd_cluster(eap, syncing)
5189     exarg_T	*eap;
5190     int		syncing;	    /* not used */
5191 {
5192     char_u	*arg = eap->arg;
5193     char_u	*group_name_end;
5194     char_u	*rest;
5195     int		scl_id;
5196     short	*clstr_list;
5197     int		got_clstr = FALSE;
5198     int		opt_len;
5199     int		list_op;
5200 
5201     eap->nextcmd = find_nextcmd(arg);
5202     if (eap->skip)
5203 	return;
5204 
5205     rest = get_group_name(arg, &group_name_end);
5206 
5207     if (rest != NULL)
5208     {
5209 	scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
5210 							      - SYNID_CLUSTER;
5211 
5212 	for (;;)
5213 	{
5214 	    if (STRNICMP(rest, "add", 3) == 0
5215 		    && (vim_iswhite(rest[3]) || rest[3] == '='))
5216 	    {
5217 		opt_len = 3;
5218 		list_op = CLUSTER_ADD;
5219 	    }
5220 	    else if (STRNICMP(rest, "remove", 6) == 0
5221 		    && (vim_iswhite(rest[6]) || rest[6] == '='))
5222 	    {
5223 		opt_len = 6;
5224 		list_op = CLUSTER_SUBTRACT;
5225 	    }
5226 	    else if (STRNICMP(rest, "contains", 8) == 0
5227 			&& (vim_iswhite(rest[8]) || rest[8] == '='))
5228 	    {
5229 		opt_len = 8;
5230 		list_op = CLUSTER_REPLACE;
5231 	    }
5232 	    else
5233 		break;
5234 
5235 	    clstr_list = NULL;
5236 	    if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5237 	    {
5238 		EMSG2(_(e_invarg2), rest);
5239 		break;
5240 	    }
5241 	    syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5242 			     &clstr_list, list_op);
5243 	    got_clstr = TRUE;
5244 	}
5245 
5246 	if (got_clstr)
5247 	{
5248 	    redraw_curbuf_later(NOT_VALID);
5249 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5250 	}
5251     }
5252 
5253     if (!got_clstr)
5254 	EMSG(_("E400: No cluster specified"));
5255     if (rest == NULL || !ends_excmd(*rest))
5256 	EMSG2(_(e_invarg2), arg);
5257 }
5258 
5259 /*
5260  * On first call for current buffer: Init growing array.
5261  */
5262     static void
5263 init_syn_patterns()
5264 {
5265     curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5266     curbuf->b_syn_patterns.ga_growsize = 10;
5267 }
5268 
5269 /*
5270  * Get one pattern for a ":syntax match" or ":syntax region" command.
5271  * Stores the pattern and program in a synpat_T.
5272  * Returns a pointer to the next argument, or NULL in case of an error.
5273  */
5274     static char_u *
5275 get_syn_pattern(arg, ci)
5276     char_u	*arg;
5277     synpat_T	*ci;
5278 {
5279     char_u	*end;
5280     int		*p;
5281     int		idx;
5282     char_u	*cpo_save;
5283 
5284     /* need at least three chars */
5285     if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5286 	return NULL;
5287 
5288     end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5289     if (*end != *arg)			    /* end delimiter not found */
5290     {
5291 	EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5292 	return NULL;
5293     }
5294     /* store the pattern and compiled regexp program */
5295     if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5296 	return NULL;
5297 
5298     /* Make 'cpoptions' empty, to avoid the 'l' flag */
5299     cpo_save = p_cpo;
5300     p_cpo = (char_u *)"";
5301     ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5302     p_cpo = cpo_save;
5303 
5304     if (ci->sp_prog == NULL)
5305 	return NULL;
5306     ci->sp_ic = curbuf->b_syn_ic;
5307 
5308     /*
5309      * Check for a match, highlight or region offset.
5310      */
5311     ++end;
5312     do
5313     {
5314 	for (idx = SPO_COUNT; --idx >= 0; )
5315 	    if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5316 		break;
5317 	if (idx >= 0)
5318 	{
5319 	    p = &(ci->sp_offsets[idx]);
5320 	    if (idx != SPO_LC_OFF)
5321 		switch (end[3])
5322 		{
5323 		    case 's':   break;
5324 		    case 'b':   break;
5325 		    case 'e':   idx += SPO_COUNT; break;
5326 		    default:    idx = -1; break;
5327 		}
5328 	    if (idx >= 0)
5329 	    {
5330 		ci->sp_off_flags |= (1 << idx);
5331 		if (idx == SPO_LC_OFF)	    /* lc=99 */
5332 		{
5333 		    end += 3;
5334 		    *p = getdigits(&end);
5335 
5336 		    /* "lc=" offset automatically sets "ms=" offset */
5337 		    if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5338 		    {
5339 			ci->sp_off_flags |= (1 << SPO_MS_OFF);
5340 			ci->sp_offsets[SPO_MS_OFF] = *p;
5341 		    }
5342 		}
5343 		else			    /* yy=x+99 */
5344 		{
5345 		    end += 4;
5346 		    if (*end == '+')
5347 		    {
5348 			++end;
5349 			*p = getdigits(&end);		/* positive offset */
5350 		    }
5351 		    else if (*end == '-')
5352 		    {
5353 			++end;
5354 			*p = -getdigits(&end);		/* negative offset */
5355 		    }
5356 		}
5357 		if (*end != ',')
5358 		    break;
5359 		++end;
5360 	    }
5361 	}
5362     } while (idx >= 0);
5363 
5364     if (!ends_excmd(*end) && !vim_iswhite(*end))
5365     {
5366 	EMSG2(_("E402: Garbage after pattern: %s"), arg);
5367 	return NULL;
5368     }
5369     return skipwhite(end);
5370 }
5371 
5372 /*
5373  * Handle ":syntax sync .." command.
5374  */
5375 /* ARGSUSED */
5376     static void
5377 syn_cmd_sync(eap, syncing)
5378     exarg_T	*eap;
5379     int		syncing;	    /* not used */
5380 {
5381     char_u	*arg_start = eap->arg;
5382     char_u	*arg_end;
5383     char_u	*key = NULL;
5384     char_u	*next_arg;
5385     int		illegal = FALSE;
5386     int		finished = FALSE;
5387     long	n;
5388     char_u	*cpo_save;
5389 
5390     if (ends_excmd(*arg_start))
5391     {
5392 	syn_cmd_list(eap, TRUE);
5393 	return;
5394     }
5395 
5396     while (!ends_excmd(*arg_start))
5397     {
5398 	arg_end = skiptowhite(arg_start);
5399 	next_arg = skipwhite(arg_end);
5400 	vim_free(key);
5401 	key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5402 	if (STRCMP(key, "CCOMMENT") == 0)
5403 	{
5404 	    if (!eap->skip)
5405 		curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5406 	    if (!ends_excmd(*next_arg))
5407 	    {
5408 		arg_end = skiptowhite(next_arg);
5409 		if (!eap->skip)
5410 		    curbuf->b_syn_sync_id = syn_check_group(next_arg,
5411 						   (int)(arg_end - next_arg));
5412 		next_arg = skipwhite(arg_end);
5413 	    }
5414 	    else if (!eap->skip)
5415 		curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5416 	}
5417 	else if (  STRNCMP(key, "LINES", 5) == 0
5418 		|| STRNCMP(key, "MINLINES", 8) == 0
5419 		|| STRNCMP(key, "MAXLINES", 8) == 0
5420 		|| STRNCMP(key, "LINEBREAKS", 10) == 0)
5421 	{
5422 	    if (key[4] == 'S')
5423 		arg_end = key + 6;
5424 	    else if (key[0] == 'L')
5425 		arg_end = key + 11;
5426 	    else
5427 		arg_end = key + 9;
5428 	    if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5429 	    {
5430 		illegal = TRUE;
5431 		break;
5432 	    }
5433 	    n = getdigits(&arg_end);
5434 	    if (!eap->skip)
5435 	    {
5436 		if (key[4] == 'B')
5437 		    curbuf->b_syn_sync_linebreaks = n;
5438 		else if (key[1] == 'A')
5439 		    curbuf->b_syn_sync_maxlines = n;
5440 		else
5441 		    curbuf->b_syn_sync_minlines = n;
5442 	    }
5443 	}
5444 	else if (STRCMP(key, "FROMSTART") == 0)
5445 	{
5446 	    if (!eap->skip)
5447 	    {
5448 		curbuf->b_syn_sync_minlines = MAXLNUM;
5449 		curbuf->b_syn_sync_maxlines = 0;
5450 	    }
5451 	}
5452 	else if (STRCMP(key, "LINECONT") == 0)
5453 	{
5454 	    if (curbuf->b_syn_linecont_pat != NULL)
5455 	    {
5456 		EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5457 		finished = TRUE;
5458 		break;
5459 	    }
5460 	    arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5461 	    if (*arg_end != *next_arg)	    /* end delimiter not found */
5462 	    {
5463 		illegal = TRUE;
5464 		break;
5465 	    }
5466 
5467 	    if (!eap->skip)
5468 	    {
5469 		/* store the pattern and compiled regexp program */
5470 		if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5471 				      (int)(arg_end - next_arg - 1))) == NULL)
5472 		{
5473 		    finished = TRUE;
5474 		    break;
5475 		}
5476 		curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5477 
5478 		/* Make 'cpoptions' empty, to avoid the 'l' flag */
5479 		cpo_save = p_cpo;
5480 		p_cpo = (char_u *)"";
5481 		curbuf->b_syn_linecont_prog =
5482 			    vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5483 		p_cpo = cpo_save;
5484 
5485 		if (curbuf->b_syn_linecont_prog == NULL)
5486 		{
5487 		    vim_free(curbuf->b_syn_linecont_pat);
5488 		    curbuf->b_syn_linecont_pat = NULL;
5489 		    finished = TRUE;
5490 		    break;
5491 		}
5492 	    }
5493 	    next_arg = skipwhite(arg_end + 1);
5494 	}
5495 	else
5496 	{
5497 	    eap->arg = next_arg;
5498 	    if (STRCMP(key, "MATCH") == 0)
5499 		syn_cmd_match(eap, TRUE);
5500 	    else if (STRCMP(key, "REGION") == 0)
5501 		syn_cmd_region(eap, TRUE);
5502 	    else if (STRCMP(key, "CLEAR") == 0)
5503 		syn_cmd_clear(eap, TRUE);
5504 	    else
5505 		illegal = TRUE;
5506 	    finished = TRUE;
5507 	    break;
5508 	}
5509 	arg_start = next_arg;
5510     }
5511     vim_free(key);
5512     if (illegal)
5513 	EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5514     else if (!finished)
5515     {
5516 	eap->nextcmd = check_nextcmd(arg_start);
5517 	redraw_curbuf_later(NOT_VALID);
5518 	syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5519     }
5520 }
5521 
5522 /*
5523  * Convert a line of highlight group names into a list of group ID numbers.
5524  * "arg" should point to the "contains" or "nextgroup" keyword.
5525  * "arg" is advanced to after the last group name.
5526  * Careful: the argument is modified (NULs added).
5527  * returns FAIL for some error, OK for success.
5528  */
5529     static int
5530 get_id_list(arg, keylen, list)
5531     char_u	**arg;
5532     int		keylen;		/* length of keyword */
5533     short	**list;		/* where to store the resulting list, if not
5534 				   NULL, the list is silently skipped! */
5535 {
5536     char_u	*p = NULL;
5537     char_u	*end;
5538     int		round;
5539     int		count;
5540     int		total_count = 0;
5541     short	*retval = NULL;
5542     char_u	*name;
5543     regmatch_T	regmatch;
5544     int		id;
5545     int		i;
5546     int		failed = FALSE;
5547 
5548     /*
5549      * We parse the list twice:
5550      * round == 1: count the number of items, allocate the array.
5551      * round == 2: fill the array with the items.
5552      * In round 1 new groups may be added, causing the number of items to
5553      * grow when a regexp is used.  In that case round 1 is done once again.
5554      */
5555     for (round = 1; round <= 2; ++round)
5556     {
5557 	/*
5558 	 * skip "contains"
5559 	 */
5560 	p = skipwhite(*arg + keylen);
5561 	if (*p != '=')
5562 	{
5563 	    EMSG2(_("E405: Missing equal sign: %s"), *arg);
5564 	    break;
5565 	}
5566 	p = skipwhite(p + 1);
5567 	if (ends_excmd(*p))
5568 	{
5569 	    EMSG2(_("E406: Empty argument: %s"), *arg);
5570 	    break;
5571 	}
5572 
5573 	/*
5574 	 * parse the arguments after "contains"
5575 	 */
5576 	count = 0;
5577 	while (!ends_excmd(*p))
5578 	{
5579 	    for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5580 		;
5581 	    name = alloc((int)(end - p + 3));	    /* leave room for "^$" */
5582 	    if (name == NULL)
5583 	    {
5584 		failed = TRUE;
5585 		break;
5586 	    }
5587 	    vim_strncpy(name + 1, p, end - p);
5588 	    if (       STRCMP(name + 1, "ALLBUT") == 0
5589 		    || STRCMP(name + 1, "ALL") == 0
5590 		    || STRCMP(name + 1, "TOP") == 0
5591 		    || STRCMP(name + 1, "CONTAINED") == 0)
5592 	    {
5593 		if (TOUPPER_ASC(**arg) != 'C')
5594 		{
5595 		    EMSG2(_("E407: %s not allowed here"), name + 1);
5596 		    failed = TRUE;
5597 		    vim_free(name);
5598 		    break;
5599 		}
5600 		if (count != 0)
5601 		{
5602 		    EMSG2(_("E408: %s must be first in contains list"), name + 1);
5603 		    failed = TRUE;
5604 		    vim_free(name);
5605 		    break;
5606 		}
5607 		if (name[1] == 'A')
5608 		    id = SYNID_ALLBUT;
5609 		else if (name[1] == 'T')
5610 		    id = SYNID_TOP;
5611 		else
5612 		    id = SYNID_CONTAINED;
5613 		id += current_syn_inc_tag;
5614 	    }
5615 	    else if (name[1] == '@')
5616 	    {
5617 		id = syn_check_cluster(name + 2, (int)(end - p - 1));
5618 	    }
5619 	    else
5620 	    {
5621 		/*
5622 		 * Handle full group name.
5623 		 */
5624 		if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5625 		    id = syn_check_group(name + 1, (int)(end - p));
5626 		else
5627 		{
5628 		    /*
5629 		     * Handle match of regexp with group names.
5630 		     */
5631 		    *name = '^';
5632 		    STRCAT(name, "$");
5633 		    regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5634 		    if (regmatch.regprog == NULL)
5635 		    {
5636 			failed = TRUE;
5637 			vim_free(name);
5638 			break;
5639 		    }
5640 
5641 		    regmatch.rm_ic = TRUE;
5642 		    id = 0;
5643 		    for (i = highlight_ga.ga_len; --i >= 0; )
5644 		    {
5645 			if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5646 								  (colnr_T)0))
5647 			{
5648 			    if (round == 2)
5649 			    {
5650 				/* Got more items than expected; can happen
5651 				 * when adding items that match:
5652 				 * "contains=a.*b,axb".
5653 				 * Go back to first round */
5654 				if (count >= total_count)
5655 				{
5656 				    vim_free(retval);
5657 				    round = 1;
5658 				}
5659 				else
5660 				    retval[count] = i + 1;
5661 			    }
5662 			    ++count;
5663 			    id = -1;	    /* remember that we found one */
5664 			}
5665 		    }
5666 		    vim_free(regmatch.regprog);
5667 		}
5668 	    }
5669 	    vim_free(name);
5670 	    if (id == 0)
5671 	    {
5672 		EMSG2(_("E409: Unknown group name: %s"), p);
5673 		failed = TRUE;
5674 		break;
5675 	    }
5676 	    if (id > 0)
5677 	    {
5678 		if (round == 2)
5679 		{
5680 		    /* Got more items than expected, go back to first round */
5681 		    if (count >= total_count)
5682 		    {
5683 			vim_free(retval);
5684 			round = 1;
5685 		    }
5686 		    else
5687 			retval[count] = id;
5688 		}
5689 		++count;
5690 	    }
5691 	    p = skipwhite(end);
5692 	    if (*p != ',')
5693 		break;
5694 	    p = skipwhite(p + 1);	/* skip comma in between arguments */
5695 	}
5696 	if (failed)
5697 	    break;
5698 	if (round == 1)
5699 	{
5700 	    retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5701 	    if (retval == NULL)
5702 		break;
5703 	    retval[count] = 0;	    /* zero means end of the list */
5704 	    total_count = count;
5705 	}
5706     }
5707 
5708     *arg = p;
5709     if (failed || retval == NULL)
5710     {
5711 	vim_free(retval);
5712 	return FAIL;
5713     }
5714 
5715     if (*list == NULL)
5716 	*list = retval;
5717     else
5718 	vim_free(retval);	/* list already found, don't overwrite it */
5719 
5720     return OK;
5721 }
5722 
5723 /*
5724  * Make a copy of an ID list.
5725  */
5726     static short *
5727 copy_id_list(list)
5728     short   *list;
5729 {
5730     int	    len;
5731     int	    count;
5732     short   *retval;
5733 
5734     if (list == NULL)
5735 	return NULL;
5736 
5737     for (count = 0; list[count]; ++count)
5738 	;
5739     len = (count + 1) * sizeof(short);
5740     retval = (short *)alloc((unsigned)len);
5741     if (retval != NULL)
5742 	mch_memmove(retval, list, (size_t)len);
5743 
5744     return retval;
5745 }
5746 
5747 /*
5748  * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5749  * "cur_si" can be NULL if not checking the "containedin" list.
5750  * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5751  * the current item.
5752  * This function is called very often, keep it fast!!
5753  */
5754     static int
5755 in_id_list(cur_si, list, ssp, contained)
5756     stateitem_T	*cur_si;	/* current item or NULL */
5757     short	*list;		/* id list */
5758     struct sp_syn *ssp;		/* group id and ":syn include" tag of group */
5759     int		contained;	/* group id is contained */
5760 {
5761     int		retval;
5762     short	*scl_list;
5763     short	item;
5764     short	id = ssp->id;
5765     static int	depth = 0;
5766     int		r;
5767 
5768     /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
5769     if (cur_si != NULL && ssp->cont_in_list != NULL
5770 					    && !(cur_si->si_flags & HL_MATCH))
5771     {
5772 	/* Ignore transparent items without a contains argument.  Double check
5773 	 * that we don't go back past the first one. */
5774 	while ((cur_si->si_flags & HL_TRANS_CONT)
5775 		&& cur_si > (stateitem_T *)(current_state.ga_data))
5776 	    --cur_si;
5777 	/* cur_si->si_idx is -1 for keywords, these never contain anything. */
5778 	if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5779 		&(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5780 		  SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5781 	    return TRUE;
5782     }
5783 
5784     if (list == NULL)
5785 	return FALSE;
5786 
5787     /*
5788      * If list is ID_LIST_ALL, we are in a transparent item that isn't
5789      * inside anything.  Only allow not-contained groups.
5790      */
5791     if (list == ID_LIST_ALL)
5792 	return !contained;
5793 
5794     /*
5795      * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5796      * contains list.  We also require that "id" is at the same ":syn include"
5797      * level as the list.
5798      */
5799     item = *list;
5800     if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5801     {
5802 	if (item < SYNID_TOP)
5803 	{
5804 	    /* ALL or ALLBUT: accept all groups in the same file */
5805 	    if (item - SYNID_ALLBUT != ssp->inc_tag)
5806 		return FALSE;
5807 	}
5808 	else if (item < SYNID_CONTAINED)
5809 	{
5810 	    /* TOP: accept all not-contained groups in the same file */
5811 	    if (item - SYNID_TOP != ssp->inc_tag || contained)
5812 		return FALSE;
5813 	}
5814 	else
5815 	{
5816 	    /* CONTAINED: accept all contained groups in the same file */
5817 	    if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5818 		return FALSE;
5819 	}
5820 	item = *++list;
5821 	retval = FALSE;
5822     }
5823     else
5824 	retval = TRUE;
5825 
5826     /*
5827      * Return "retval" if id is in the contains list.
5828      */
5829     while (item != 0)
5830     {
5831 	if (item == id)
5832 	    return retval;
5833 	if (item >= SYNID_CLUSTER)
5834 	{
5835 	    scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5836 	    /* restrict recursiveness to 30 to avoid an endless loop for a
5837 	     * cluster that includes itself (indirectly) */
5838 	    if (scl_list != NULL && depth < 30)
5839 	    {
5840 		++depth;
5841 		r = in_id_list(NULL, scl_list, ssp, contained);
5842 		--depth;
5843 		if (r)
5844 		    return retval;
5845 	    }
5846 	}
5847 	item = *++list;
5848     }
5849     return !retval;
5850 }
5851 
5852 struct subcommand
5853 {
5854     char    *name;				/* subcommand name */
5855     void    (*func)__ARGS((exarg_T *, int));	/* function to call */
5856 };
5857 
5858 static struct subcommand subcommands[] =
5859 {
5860     {"case",		syn_cmd_case},
5861     {"clear",		syn_cmd_clear},
5862     {"cluster",		syn_cmd_cluster},
5863     {"enable",		syn_cmd_enable},
5864     {"include",		syn_cmd_include},
5865     {"keyword",		syn_cmd_keyword},
5866     {"list",		syn_cmd_list},
5867     {"manual",		syn_cmd_manual},
5868     {"match",		syn_cmd_match},
5869     {"on",		syn_cmd_on},
5870     {"off",		syn_cmd_off},
5871     {"region",		syn_cmd_region},
5872     {"reset",		syn_cmd_reset},
5873     {"spell",		syn_cmd_spell},
5874     {"sync",		syn_cmd_sync},
5875     {"",		syn_cmd_list},
5876     {NULL, NULL}
5877 };
5878 
5879 /*
5880  * ":syntax".
5881  * This searches the subcommands[] table for the subcommand name, and calls a
5882  * syntax_subcommand() function to do the rest.
5883  */
5884     void
5885 ex_syntax(eap)
5886     exarg_T	*eap;
5887 {
5888     char_u	*arg = eap->arg;
5889     char_u	*subcmd_end;
5890     char_u	*subcmd_name;
5891     int		i;
5892 
5893     syn_cmdlinep = eap->cmdlinep;
5894 
5895     /* isolate subcommand name */
5896     for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5897 	;
5898     subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5899     if (subcmd_name != NULL)
5900     {
5901 	if (eap->skip)		/* skip error messages for all subcommands */
5902 	    ++emsg_skip;
5903 	for (i = 0; ; ++i)
5904 	{
5905 	    if (subcommands[i].name == NULL)
5906 	    {
5907 		EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5908 		break;
5909 	    }
5910 	    if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5911 	    {
5912 		eap->arg = skipwhite(subcmd_end);
5913 		(subcommands[i].func)(eap, FALSE);
5914 		break;
5915 	    }
5916 	}
5917 	vim_free(subcmd_name);
5918 	if (eap->skip)
5919 	    --emsg_skip;
5920     }
5921 }
5922 
5923     int
5924 syntax_present(buf)
5925     buf_T	*buf;
5926 {
5927     return (buf->b_syn_patterns.ga_len != 0
5928 	    || buf->b_syn_clusters.ga_len != 0
5929 	    || curbuf->b_keywtab.ht_used > 0
5930 	    || curbuf->b_keywtab_ic.ht_used > 0);
5931 }
5932 
5933 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5934 
5935 static enum
5936 {
5937     EXP_SUBCMD,	    /* expand ":syn" sub-commands */
5938     EXP_CASE	    /* expand ":syn case" arguments */
5939 } expand_what;
5940 
5941 
5942 /*
5943  * Handle command line completion for :syntax command.
5944  */
5945     void
5946 set_context_in_syntax_cmd(xp, arg)
5947     expand_T	*xp;
5948     char_u	*arg;
5949 {
5950     char_u	*p;
5951 
5952     /* Default: expand subcommands */
5953     xp->xp_context = EXPAND_SYNTAX;
5954     expand_what = EXP_SUBCMD;
5955     xp->xp_pattern = arg;
5956     include_link = FALSE;
5957     include_default = FALSE;
5958 
5959     /* (part of) subcommand already typed */
5960     if (*arg != NUL)
5961     {
5962 	p = skiptowhite(arg);
5963 	if (*p != NUL)		    /* past first word */
5964 	{
5965 	    xp->xp_pattern = skipwhite(p);
5966 	    if (*skiptowhite(xp->xp_pattern) != NUL)
5967 		xp->xp_context = EXPAND_NOTHING;
5968 	    else if (STRNICMP(arg, "case", p - arg) == 0)
5969 		expand_what = EXP_CASE;
5970 	    else if (  STRNICMP(arg, "keyword", p - arg) == 0
5971 		    || STRNICMP(arg, "region", p - arg) == 0
5972 		    || STRNICMP(arg, "match", p - arg) == 0
5973 		    || STRNICMP(arg, "list", p - arg) == 0)
5974 		xp->xp_context = EXPAND_HIGHLIGHT;
5975 	    else
5976 		xp->xp_context = EXPAND_NOTHING;
5977 	}
5978     }
5979 }
5980 
5981 static char *(case_args[]) = {"match", "ignore", NULL};
5982 
5983 /*
5984  * Function given to ExpandGeneric() to obtain the list syntax names for
5985  * expansion.
5986  */
5987 /*ARGSUSED*/
5988     char_u *
5989 get_syntax_name(xp, idx)
5990     expand_T	*xp;
5991     int		idx;
5992 {
5993     if (expand_what == EXP_SUBCMD)
5994 	return (char_u *)subcommands[idx].name;
5995     return (char_u *)case_args[idx];
5996 }
5997 
5998 #endif /* FEAT_CMDL_COMPL */
5999 
6000 /*
6001  * Function called for expression evaluation: get syntax ID at file position.
6002  */
6003     int
6004 syn_get_id(wp, lnum, col, trans, spellp)
6005     win_T	*wp;
6006     long	lnum;
6007     colnr_T	col;
6008     int		trans;	    /* remove transparancy */
6009     int		*spellp;    /* return: can do spell checking */
6010 {
6011     /* When the position is not after the current position and in the same
6012      * line of the same buffer, need to restart parsing. */
6013     if (wp->w_buffer != syn_buf
6014 	    || lnum != current_lnum
6015 	    || col < current_col)
6016 	syntax_start(wp, lnum);
6017 
6018     (void)get_syntax_attr(col, spellp);
6019 
6020     return (trans ? current_trans_id : current_id);
6021 }
6022 
6023 #if defined(FEAT_FOLDING) || defined(PROTO)
6024 /*
6025  * Function called to get folding level for line "lnum" in window "wp".
6026  */
6027     int
6028 syn_get_foldlevel(wp, lnum)
6029     win_T	*wp;
6030     long	lnum;
6031 {
6032     int		level = 0;
6033     int		i;
6034 
6035     /* Return quickly when there are no fold items at all. */
6036     if (wp->w_buffer->b_syn_folditems != 0)
6037     {
6038 	syntax_start(wp, lnum);
6039 
6040 	for (i = 0; i < current_state.ga_len; ++i)
6041 	    if (CUR_STATE(i).si_flags & HL_FOLD)
6042 		++level;
6043     }
6044     if (level > wp->w_p_fdn)
6045 	level = wp->w_p_fdn;
6046     return level;
6047 }
6048 #endif
6049 
6050 #endif /* FEAT_SYN_HL */
6051 
6052 
6053 /**************************************
6054  *  Highlighting stuff		      *
6055  **************************************/
6056 
6057 /*
6058  * The default highlight groups.  These are compiled-in for fast startup and
6059  * they still work when the runtime files can't be found.
6060  * When making changes here, also change runtime/colors/default.vim!
6061  */
6062 static char *(highlight_init_both[]) =
6063     {
6064 #ifdef FEAT_GUI
6065 	"Cursor guibg=fg guifg=bg",
6066 	"lCursor guibg=fg guifg=bg",	/* should be different, but what? */
6067 #endif
6068 	"ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
6069 	"IncSearch term=reverse cterm=reverse gui=reverse",
6070 	"ModeMsg term=bold cterm=bold gui=bold",
6071 	"NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
6072 	"StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
6073 	"StatusLineNC term=reverse cterm=reverse gui=reverse",
6074 	"VertSplit term=reverse cterm=reverse gui=reverse",
6075 	"VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold",
6076 	"DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
6077 	"PmenuThumb cterm=reverse gui=reverse",
6078 	"PmenuSbar ctermbg=Grey guibg=Grey",
6079 	NULL
6080     };
6081 
6082 static char *(highlight_init_light[]) =
6083     {
6084 	"Directory term=bold ctermfg=DarkBlue guifg=Blue",
6085 	"LineNr term=underline ctermfg=Brown guifg=Brown",
6086 	"MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6087 	"Normal gui=NONE",
6088 	"Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6089 	"Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
6090 	"SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl",
6091 	"SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl",
6092 	"SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl",
6093 	"SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl",
6094 	"Pmenu ctermbg=LightMagenta guibg=LightMagenta",
6095 	"PmenuSel ctermbg=LightGrey guibg=Grey",
6096 	"SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
6097 	"Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
6098 	"WarningMsg term=standout ctermfg=DarkRed guifg=Red",
6099 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6100 	"Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
6101 	"FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6102 	"SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6103 	"Visual term=reverse ctermbg=Magenta guibg=LightGrey",
6104 	"DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
6105 	"DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
6106 	"DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
6107 	NULL
6108     };
6109 
6110 static char *(highlight_init_dark[]) =
6111     {
6112 	"Directory term=bold ctermfg=LightCyan guifg=Cyan",
6113 	"LineNr term=underline ctermfg=Yellow guifg=Yellow",
6114 	"MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
6115 	"Normal gui=NONE",
6116 	"Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
6117 	"Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6118 	"SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
6119 	"SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl",
6120 	"SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl",
6121 	"SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl",
6122 	"SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl",
6123 	"Pmenu ctermbg=Magenta guibg=Magenta",
6124 	"PmenuSel ctermbg=Grey guibg=Grey",
6125 	"Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
6126 	"WarningMsg term=standout ctermfg=LightRed guifg=Red",
6127 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6128 	"Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
6129 	"FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6130 	"SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6131 	"Visual term=reverse ctermbg=Magenta guibg=DarkGrey",
6132 	"DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6133 	"DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6134 	"DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6135 	NULL
6136     };
6137 
6138     void
6139 init_highlight(both, reset)
6140     int		both;	    /* include groups where 'bg' doesn't matter */
6141     int		reset;	    /* clear group first */
6142 {
6143     int		i;
6144     char	**pp;
6145     static int	had_both = FALSE;
6146 #ifdef FEAT_EVAL
6147     char_u	*p;
6148 
6149     /*
6150      * Try finding the color scheme file.  Used when a color file was loaded
6151      * and 'background' or 't_Co' is changed.
6152      */
6153     p = get_var_value((char_u *)"g:colors_name");
6154     if (p != NULL && load_colors(p) == OK)
6155 	return;
6156 #endif
6157 
6158     /*
6159      * Didn't use a color file, use the compiled-in colors.
6160      */
6161     if (both)
6162     {
6163 	had_both = TRUE;
6164 	pp = highlight_init_both;
6165 	for (i = 0; pp[i] != NULL; ++i)
6166 	    do_highlight((char_u *)pp[i], reset, TRUE);
6167     }
6168     else if (!had_both)
6169 	/* Don't do anything before the call with both == TRUE from main().
6170 	 * Not everything has been setup then, and that call will overrule
6171 	 * everything anyway. */
6172 	return;
6173 
6174     if (*p_bg == 'l')
6175 	pp = highlight_init_light;
6176     else
6177 	pp = highlight_init_dark;
6178     for (i = 0; pp[i] != NULL; ++i)
6179 	do_highlight((char_u *)pp[i], reset, TRUE);
6180 
6181     /* Magenta background looks ugly, but grey may not work for 8 colors.
6182      * Thus let it depend on the number of colors available. */
6183     if (t_colors > 8)
6184 	do_highlight((char_u *)(*p_bg == 'l' ? "Visual ctermbg=LightGrey"
6185 				   : "Visual ctermbg=DarkGrey"), reset, TRUE);
6186 
6187 #ifdef FEAT_SYN_HL
6188     /*
6189      * If syntax highlighting is enabled load the highlighting for it.
6190      */
6191     if (get_var_value((char_u *)"g:syntax_on") != NULL)
6192     {
6193 	static int	recursive = 0;
6194 
6195 	if (recursive >= 5)
6196 	    EMSG(_("E679: recursive loop loading syncolor.vim"));
6197 	else
6198 	{
6199 	    ++recursive;
6200 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6201 	    --recursive;
6202 	}
6203     }
6204 #endif
6205 }
6206 
6207 /*
6208  * Load color file "name".
6209  * Return OK for success, FAIL for failure.
6210  */
6211     int
6212 load_colors(name)
6213     char_u	*name;
6214 {
6215     char_u	*buf;
6216     int		retval = FAIL;
6217     static int	recursive = FALSE;
6218 
6219     /* When being called recursively, this is probably because setting
6220      * 'background' caused the highlighting to be reloaded.  This means it is
6221      * working, thus we should return OK. */
6222     if (recursive)
6223 	return OK;
6224 
6225     recursive = TRUE;
6226     buf = alloc((unsigned)(STRLEN(name) + 12));
6227     if (buf != NULL)
6228     {
6229 	sprintf((char *)buf, "colors/%s.vim", name);
6230 	retval = source_runtime(buf, FALSE);
6231 	vim_free(buf);
6232 #ifdef FEAT_AUTOCMD
6233 	apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6234 #endif
6235     }
6236     recursive = FALSE;
6237 
6238     return retval;
6239 }
6240 
6241 /*
6242  * Handle the ":highlight .." command.
6243  * When using ":hi clear" this is called recursively for each group with
6244  * "forceit" and "init" both TRUE.
6245  */
6246     void
6247 do_highlight(line, forceit, init)
6248     char_u	*line;
6249     int		forceit;
6250     int		init;	    /* TRUE when called for initializing */
6251 {
6252     char_u	*name_end;
6253     char_u	*p;
6254     char_u	*linep;
6255     char_u	*key_start;
6256     char_u	*arg_start;
6257     char_u	*key = NULL, *arg = NULL;
6258     long	i;
6259     int		off;
6260     int		len;
6261     int		attr;
6262     int		id;
6263     int		idx;
6264     int		dodefault = FALSE;
6265     int		doclear = FALSE;
6266     int		dolink = FALSE;
6267     int		error = FALSE;
6268     int		color;
6269     int		is_normal_group = FALSE;	/* "Normal" group */
6270 #ifdef FEAT_GUI_X11
6271     int		is_menu_group = FALSE;		/* "Menu" group */
6272     int		is_scrollbar_group = FALSE;	/* "Scrollbar" group */
6273     int		is_tooltip_group = FALSE;	/* "Tooltip" group */
6274     int		do_colors = FALSE;		/* need to update colors? */
6275 #else
6276 # define is_menu_group 0
6277 # define is_tooltip_group 0
6278 #endif
6279 
6280     /*
6281      * If no argument, list current highlighting.
6282      */
6283     if (ends_excmd(*line))
6284     {
6285 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6286 	    /* TODO: only call when the group has attributes set */
6287 	    highlight_list_one((int)i);
6288 	return;
6289     }
6290 
6291     /*
6292      * Isolate the name.
6293      */
6294     name_end = skiptowhite(line);
6295     linep = skipwhite(name_end);
6296 
6297     /*
6298      * Check for "default" argument.
6299      */
6300     if (STRNCMP(line, "default", name_end - line) == 0)
6301     {
6302 	dodefault = TRUE;
6303 	line = linep;
6304 	name_end = skiptowhite(line);
6305 	linep = skipwhite(name_end);
6306     }
6307 
6308     /*
6309      * Check for "clear" or "link" argument.
6310      */
6311     if (STRNCMP(line, "clear", name_end - line) == 0)
6312 	doclear = TRUE;
6313     if (STRNCMP(line, "link", name_end - line) == 0)
6314 	dolink = TRUE;
6315 
6316     /*
6317      * ":highlight {group-name}": list highlighting for one group.
6318      */
6319     if (!doclear && !dolink && ends_excmd(*linep))
6320     {
6321 	id = syn_namen2id(line, (int)(name_end - line));
6322 	if (id == 0)
6323 	    EMSG2(_("E411: highlight group not found: %s"), line);
6324 	else
6325 	    highlight_list_one(id);
6326 	return;
6327     }
6328 
6329     /*
6330      * Handle ":highlight link {from} {to}" command.
6331      */
6332     if (dolink)
6333     {
6334 	char_u	    *from_start = linep;
6335 	char_u	    *from_end;
6336 	char_u	    *to_start;
6337 	char_u	    *to_end;
6338 	int	    from_id;
6339 	int	    to_id;
6340 
6341 	from_end = skiptowhite(from_start);
6342 	to_start = skipwhite(from_end);
6343 	to_end	 = skiptowhite(to_start);
6344 
6345 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
6346 	{
6347 	    EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6348 								  from_start);
6349 	    return;
6350 	}
6351 
6352 	if (!ends_excmd(*skipwhite(to_end)))
6353 	{
6354 	    EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6355 	    return;
6356 	}
6357 
6358 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
6359 	if (STRNCMP(to_start, "NONE", 4) == 0)
6360 	    to_id = 0;
6361 	else
6362 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
6363 
6364 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6365 	{
6366 	    /*
6367 	     * Don't allow a link when there already is some highlighting
6368 	     * for the group, unless '!' is used
6369 	     */
6370 	    if (to_id > 0 && !forceit && !init
6371 				   && hl_has_settings(from_id - 1, dodefault))
6372 	    {
6373 		if (sourcing_name == NULL && !dodefault)
6374 		    EMSG(_("E414: group has settings, highlight link ignored"));
6375 	    }
6376 	    else
6377 	    {
6378 		if (!init)
6379 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6380 		HL_TABLE()[from_id - 1].sg_link = to_id;
6381 #ifdef FEAT_EVAL
6382 		HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6383 #endif
6384 		redraw_all_later(NOT_VALID);
6385 	    }
6386 	}
6387 
6388 	/* Only call highlight_changed() once, after sourcing a syntax file */
6389 	need_highlight_changed = TRUE;
6390 
6391 	return;
6392     }
6393 
6394     if (doclear)
6395     {
6396 	/*
6397 	 * ":highlight clear [group]" command.
6398 	 */
6399 	line = linep;
6400 	if (ends_excmd(*line))
6401 	{
6402 #ifdef FEAT_GUI
6403 	    /* First, we do not destroy the old values, but allocate the new
6404 	     * ones and update the display. THEN we destroy the old values.
6405 	     * If we destroy the old values first, then the old values
6406 	     * (such as GuiFont's or GuiFontset's) will still be displayed but
6407 	     * invalid because they were free'd.
6408 	     */
6409 	    if (gui.in_use)
6410 	    {
6411 # ifdef FEAT_BEVAL_TIP
6412 		gui_init_tooltip_font();
6413 # endif
6414 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6415 		gui_init_menu_font();
6416 # endif
6417 	    }
6418 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6419 	    gui_mch_def_colors();
6420 # endif
6421 # ifdef FEAT_GUI_X11
6422 #  ifdef FEAT_MENU
6423 
6424 	    /* This only needs to be done when there is no Menu highlight
6425 	     * group defined by default, which IS currently the case.
6426 	     */
6427 	    gui_mch_new_menu_colors();
6428 #  endif
6429 	    if (gui.in_use)
6430 	    {
6431 		gui_new_scrollbar_colors();
6432 #  ifdef FEAT_BEVAL
6433 		gui_mch_new_tooltip_colors();
6434 #  endif
6435 #  ifdef FEAT_MENU
6436 		gui_mch_new_menu_font();
6437 #  endif
6438 	    }
6439 # endif
6440 
6441 	    /* Ok, we're done allocating the new default graphics items.
6442 	     * The screen should already be refreshed at this point.
6443 	     * It is now Ok to clear out the old data.
6444 	     */
6445 #endif
6446 #ifdef FEAT_EVAL
6447 	    do_unlet((char_u *)"colors_name", TRUE);
6448 #endif
6449 	    restore_cterm_colors();
6450 
6451 	    /*
6452 	     * Clear all default highlight groups and load the defaults.
6453 	     */
6454 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6455 		highlight_clear(idx);
6456 	    init_highlight(TRUE, TRUE);
6457 #ifdef FEAT_GUI
6458 	    if (gui.in_use)
6459 		highlight_gui_started();
6460 #endif
6461 	    highlight_changed();
6462 	    redraw_later_clear();
6463 	    return;
6464 	}
6465 	name_end = skiptowhite(line);
6466 	linep = skipwhite(name_end);
6467     }
6468 
6469     /*
6470      * Find the group name in the table.  If it does not exist yet, add it.
6471      */
6472     id = syn_check_group(line, (int)(name_end - line));
6473     if (id == 0)			/* failed (out of memory) */
6474 	return;
6475     idx = id - 1;			/* index is ID minus one */
6476 
6477     /* Return if "default" was used and the group already has settings. */
6478     if (dodefault && hl_has_settings(idx, TRUE))
6479 	return;
6480 
6481     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6482 	is_normal_group = TRUE;
6483 #ifdef FEAT_GUI_X11
6484     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6485 	is_menu_group = TRUE;
6486     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6487 	is_scrollbar_group = TRUE;
6488     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6489 	is_tooltip_group = TRUE;
6490 #endif
6491 
6492     /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6493     if (doclear || (forceit && init))
6494     {
6495 	highlight_clear(idx);
6496 	if (!doclear)
6497 	    HL_TABLE()[idx].sg_set = 0;
6498     }
6499 
6500     if (!doclear)
6501       while (!ends_excmd(*linep))
6502       {
6503 	key_start = linep;
6504 	if (*linep == '=')
6505 	{
6506 	    EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6507 	    error = TRUE;
6508 	    break;
6509 	}
6510 
6511 	/*
6512 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6513 	 * "guibg").
6514 	 */
6515 	while (*linep && !vim_iswhite(*linep) && *linep != '=')
6516 	    ++linep;
6517 	vim_free(key);
6518 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
6519 	if (key == NULL)
6520 	{
6521 	    error = TRUE;
6522 	    break;
6523 	}
6524 	linep = skipwhite(linep);
6525 
6526 	if (STRCMP(key, "NONE") == 0)
6527 	{
6528 	    if (!init || HL_TABLE()[idx].sg_set == 0)
6529 	    {
6530 		if (!init)
6531 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6532 		highlight_clear(idx);
6533 	    }
6534 	    continue;
6535 	}
6536 
6537 	/*
6538 	 * Check for the equal sign.
6539 	 */
6540 	if (*linep != '=')
6541 	{
6542 	    EMSG2(_("E416: missing equal sign: %s"), key_start);
6543 	    error = TRUE;
6544 	    break;
6545 	}
6546 	++linep;
6547 
6548 	/*
6549 	 * Isolate the argument.
6550 	 */
6551 	linep = skipwhite(linep);
6552 	if (*linep == '\'')		/* guifg='color name' */
6553 	{
6554 	    arg_start = ++linep;
6555 	    linep = vim_strchr(linep, '\'');
6556 	    if (linep == NULL)
6557 	    {
6558 		EMSG2(_(e_invarg2), key_start);
6559 		error = TRUE;
6560 		break;
6561 	    }
6562 	}
6563 	else
6564 	{
6565 	    arg_start = linep;
6566 	    linep = skiptowhite(linep);
6567 	}
6568 	if (linep == arg_start)
6569 	{
6570 	    EMSG2(_("E417: missing argument: %s"), key_start);
6571 	    error = TRUE;
6572 	    break;
6573 	}
6574 	vim_free(arg);
6575 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6576 	if (arg == NULL)
6577 	{
6578 	    error = TRUE;
6579 	    break;
6580 	}
6581 	if (*linep == '\'')
6582 	    ++linep;
6583 
6584 	/*
6585 	 * Store the argument.
6586 	 */
6587 	if (  STRCMP(key, "TERM") == 0
6588 		|| STRCMP(key, "CTERM") == 0
6589 		|| STRCMP(key, "GUI") == 0)
6590 	{
6591 	    attr = 0;
6592 	    off = 0;
6593 	    while (arg[off] != NUL)
6594 	    {
6595 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6596 		{
6597 		    len = (int)STRLEN(hl_name_table[i]);
6598 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6599 		    {
6600 			attr |= hl_attr_table[i];
6601 			off += len;
6602 			break;
6603 		    }
6604 		}
6605 		if (i < 0)
6606 		{
6607 		    EMSG2(_("E418: Illegal value: %s"), arg);
6608 		    error = TRUE;
6609 		    break;
6610 		}
6611 		if (arg[off] == ',')		/* another one follows */
6612 		    ++off;
6613 	    }
6614 	    if (error)
6615 		break;
6616 	    if (*key == 'T')
6617 	    {
6618 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6619 		{
6620 		    if (!init)
6621 			HL_TABLE()[idx].sg_set |= SG_TERM;
6622 		    HL_TABLE()[idx].sg_term = attr;
6623 		}
6624 	    }
6625 	    else if (*key == 'C')
6626 	    {
6627 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6628 		{
6629 		    if (!init)
6630 			HL_TABLE()[idx].sg_set |= SG_CTERM;
6631 		    HL_TABLE()[idx].sg_cterm = attr;
6632 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
6633 		}
6634 	    }
6635 #ifdef FEAT_GUI
6636 	    else
6637 	    {
6638 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6639 		{
6640 		    if (!init)
6641 			HL_TABLE()[idx].sg_set |= SG_GUI;
6642 		    HL_TABLE()[idx].sg_gui = attr;
6643 		}
6644 	    }
6645 #endif
6646 	}
6647 	else if (STRCMP(key, "FONT") == 0)
6648 	{
6649 	    /* in non-GUI fonts are simply ignored */
6650 #ifdef FEAT_GUI
6651 	    if (!gui.shell_created)
6652 	    {
6653 		/* GUI not started yet, always accept the name. */
6654 		vim_free(HL_TABLE()[idx].sg_font_name);
6655 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6656 	    }
6657 	    else
6658 	    {
6659 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6660 # ifdef FEAT_XFONTSET
6661 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6662 # endif
6663 		/* First, save the current font/fontset.
6664 		 * Then try to allocate the font/fontset.
6665 		 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6666 		 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6667 		 */
6668 
6669 		HL_TABLE()[idx].sg_font = NOFONT;
6670 # ifdef FEAT_XFONTSET
6671 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
6672 # endif
6673 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
6674 							    is_tooltip_group);
6675 
6676 # ifdef FEAT_XFONTSET
6677 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6678 		{
6679 		    /* New fontset was accepted. Free the old one, if there was
6680 		     * one.
6681 		     */
6682 		    gui_mch_free_fontset(temp_sg_fontset);
6683 		    vim_free(HL_TABLE()[idx].sg_font_name);
6684 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6685 		}
6686 		else
6687 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6688 # endif
6689 		if (HL_TABLE()[idx].sg_font != NOFONT)
6690 		{
6691 		    /* New font was accepted. Free the old one, if there was
6692 		     * one.
6693 		     */
6694 		    gui_mch_free_font(temp_sg_font);
6695 		    vim_free(HL_TABLE()[idx].sg_font_name);
6696 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6697 		}
6698 		else
6699 		    HL_TABLE()[idx].sg_font = temp_sg_font;
6700 	    }
6701 #endif
6702 	}
6703 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6704 	{
6705 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6706 	  {
6707 	    if (!init)
6708 		HL_TABLE()[idx].sg_set |= SG_CTERM;
6709 
6710 	    /* When setting the foreground color, and previously the "bold"
6711 	     * flag was set for a light color, reset it now */
6712 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6713 	    {
6714 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6715 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
6716 	    }
6717 
6718 	    if (VIM_ISDIGIT(*arg))
6719 		color = atoi((char *)arg);
6720 	    else if (STRICMP(arg, "fg") == 0)
6721 	    {
6722 		if (cterm_normal_fg_color)
6723 		    color = cterm_normal_fg_color - 1;
6724 		else
6725 		{
6726 		    EMSG(_("E419: FG color unknown"));
6727 		    error = TRUE;
6728 		    break;
6729 		}
6730 	    }
6731 	    else if (STRICMP(arg, "bg") == 0)
6732 	    {
6733 		if (cterm_normal_bg_color > 0)
6734 		    color = cterm_normal_bg_color - 1;
6735 		else
6736 		{
6737 		    EMSG(_("E420: BG color unknown"));
6738 		    error = TRUE;
6739 		    break;
6740 		}
6741 	    }
6742 	    else
6743 	    {
6744 		static char *(color_names[28]) = {
6745 			    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6746 			    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6747 			    "Gray", "Grey",
6748 			    "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6749 			    "Blue", "LightBlue", "Green", "LightGreen",
6750 			    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6751 			    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6752 		static int color_numbers_16[28] = {0, 1, 2, 3,
6753 						 4, 5, 6, 6,
6754 						 7, 7,
6755 						 7, 7, 8, 8,
6756 						 9, 9, 10, 10,
6757 						 11, 11, 12, 12, 13,
6758 						 13, 14, 14, 15, -1};
6759 		/* for xterm with 88 colors... */
6760 		static int color_numbers_88[28] = {0, 4, 2, 6,
6761 						 1, 5, 32, 72,
6762 						 84, 84,
6763 						 7, 7, 82, 82,
6764 						 12, 43, 10, 61,
6765 						 14, 63, 9, 74, 13,
6766 						 75, 11, 78, 15, -1};
6767 		/* for xterm with 256 colors... */
6768 		static int color_numbers_256[28] = {0, 4, 2, 6,
6769 						 1, 5, 130, 130,
6770 						 248, 248,
6771 						 7, 7, 242, 242,
6772 						 12, 81, 10, 121,
6773 						 14, 159, 9, 224, 13,
6774 						 225, 11, 229, 15, -1};
6775 		/* for terminals with less than 16 colors... */
6776 		static int color_numbers_8[28] = {0, 4, 2, 6,
6777 						 1, 5, 3, 3,
6778 						 7, 7,
6779 						 7, 7, 0+8, 0+8,
6780 						 4+8, 4+8, 2+8, 2+8,
6781 						 6+8, 6+8, 1+8, 1+8, 5+8,
6782 						 5+8, 3+8, 3+8, 7+8, -1};
6783 #if defined(__QNXNTO__)
6784 		static int *color_numbers_8_qansi = color_numbers_8;
6785 		/* On qnx, the 8 & 16 color arrays are the same */
6786 		if (STRNCMP(T_NAME, "qansi", 5) == 0)
6787 		    color_numbers_8_qansi = color_numbers_16;
6788 #endif
6789 
6790 		/* reduce calls to STRICMP a bit, it can be slow */
6791 		off = TOUPPER_ASC(*arg);
6792 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6793 		    if (off == color_names[i][0]
6794 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6795 			break;
6796 		if (i < 0)
6797 		{
6798 		    EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6799 		    error = TRUE;
6800 		    break;
6801 		}
6802 
6803 		/* Use the _16 table to check if its a valid color name. */
6804 		color = color_numbers_16[i];
6805 		if (color >= 0)
6806 		{
6807 		    if (t_colors == 8)
6808 		    {
6809 			/* t_Co is 8: use the 8 colors table */
6810 #if defined(__QNXNTO__)
6811 			color = color_numbers_8_qansi[i];
6812 #else
6813 			color = color_numbers_8[i];
6814 #endif
6815 			if (key[5] == 'F')
6816 			{
6817 			    /* set/reset bold attribute to get light foreground
6818 			     * colors (on some terminals, e.g. "linux") */
6819 			    if (color & 8)
6820 			    {
6821 				HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6822 				HL_TABLE()[idx].sg_cterm_bold = TRUE;
6823 			    }
6824 			    else
6825 				HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6826 			}
6827 			color &= 7;	/* truncate to 8 colors */
6828 		    }
6829 		    else if (t_colors == 16 || t_colors == 88
6830 							   || t_colors == 256)
6831 		    {
6832 			/*
6833 			 * Guess: if the termcap entry ends in 'm', it is
6834 			 * probably an xterm-like terminal.  Use the changed
6835 			 * order for colors.
6836 			 */
6837 			if (*T_CAF != NUL)
6838 			    p = T_CAF;
6839 			else
6840 			    p = T_CSF;
6841 			if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6842 			    switch (t_colors)
6843 			    {
6844 				case 16:
6845 				    color = color_numbers_8[i];
6846 				    break;
6847 				case 88:
6848 				    color = color_numbers_88[i];
6849 				    break;
6850 				case 256:
6851 				    color = color_numbers_256[i];
6852 				    break;
6853 			    }
6854 		    }
6855 		}
6856 	    }
6857 	    /* Add one to the argument, to avoid zero */
6858 	    if (key[5] == 'F')
6859 	    {
6860 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
6861 		if (is_normal_group)
6862 		{
6863 		    cterm_normal_fg_color = color + 1;
6864 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6865 #ifdef FEAT_GUI
6866 		    /* Don't do this if the GUI is used. */
6867 		    if (!gui.in_use && !gui.starting)
6868 #endif
6869 		    {
6870 			must_redraw = CLEAR;
6871 			if (termcap_active)
6872 			    term_fg_color(color);
6873 		    }
6874 		}
6875 	    }
6876 	    else
6877 	    {
6878 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
6879 		if (is_normal_group)
6880 		{
6881 		    cterm_normal_bg_color = color + 1;
6882 #ifdef FEAT_GUI
6883 		    /* Don't mess with 'background' if the GUI is used. */
6884 		    if (!gui.in_use && !gui.starting)
6885 #endif
6886 		    {
6887 			must_redraw = CLEAR;
6888 			if (termcap_active)
6889 			    term_bg_color(color);
6890 			if (t_colors < 16)
6891 			    i = (color == 0 || color == 4);
6892 			else
6893 			    i = (color < 7 || color == 8);
6894 			/* Set the 'background' option if the value is wrong. */
6895 			if (i != (*p_bg == 'd'))
6896 			    set_option_value((char_u *)"bg", 0L,
6897 				 i ? (char_u *)"dark" : (char_u *)"light", 0);
6898 		    }
6899 		}
6900 	    }
6901 	  }
6902 	}
6903 	else if (STRCMP(key, "GUIFG") == 0)
6904 	{
6905 #ifdef FEAT_GUI	    /* in non-GUI guifg colors are simply ignored */
6906 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6907 	    {
6908 		if (!init)
6909 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6910 
6911 		i = color_name2handle(arg);
6912 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6913 		{
6914 		    HL_TABLE()[idx].sg_gui_fg = i;
6915 		    vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6916 		    if (STRCMP(arg, "NONE"))
6917 			HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6918 		    else
6919 			HL_TABLE()[idx].sg_gui_fg_name = NULL;
6920 # ifdef FEAT_GUI_X11
6921 		    if (is_menu_group)
6922 			gui.menu_fg_pixel = i;
6923 		    if (is_scrollbar_group)
6924 			gui.scroll_fg_pixel = i;
6925 #  ifdef FEAT_BEVAL
6926 		    if (is_tooltip_group)
6927 			gui.tooltip_fg_pixel = i;
6928 #  endif
6929 		    do_colors = TRUE;
6930 # endif
6931 		}
6932 	    }
6933 #endif
6934 	}
6935 	else if (STRCMP(key, "GUIBG") == 0)
6936 	{
6937 #ifdef FEAT_GUI	    /* in non-GUI guibg colors are simply ignored */
6938 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6939 	    {
6940 		if (!init)
6941 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6942 
6943 		i = color_name2handle(arg);
6944 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6945 		{
6946 		    HL_TABLE()[idx].sg_gui_bg = i;
6947 		    vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6948 		    if (STRCMP(arg, "NONE") != 0)
6949 			HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6950 		    else
6951 			HL_TABLE()[idx].sg_gui_bg_name = NULL;
6952 # ifdef FEAT_GUI_X11
6953 		    if (is_menu_group)
6954 			gui.menu_bg_pixel = i;
6955 		    if (is_scrollbar_group)
6956 			gui.scroll_bg_pixel = i;
6957 #  ifdef FEAT_BEVAL
6958 		    if (is_tooltip_group)
6959 			gui.tooltip_bg_pixel = i;
6960 #  endif
6961 		    do_colors = TRUE;
6962 # endif
6963 		}
6964 	    }
6965 #endif
6966 	}
6967 	else if (STRCMP(key, "GUISP") == 0)
6968 	{
6969 #ifdef FEAT_GUI	    /* in non-GUI guisp colors are simply ignored */
6970 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6971 	    {
6972 		if (!init)
6973 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6974 
6975 		i = color_name2handle(arg);
6976 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6977 		{
6978 		    HL_TABLE()[idx].sg_gui_sp = i;
6979 		    vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6980 		    if (STRCMP(arg, "NONE") != 0)
6981 			HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6982 		    else
6983 			HL_TABLE()[idx].sg_gui_sp_name = NULL;
6984 		}
6985 	    }
6986 #endif
6987 	}
6988 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6989 	{
6990 	    char_u	buf[100];
6991 	    char_u	*tname;
6992 
6993 	    if (!init)
6994 		HL_TABLE()[idx].sg_set |= SG_TERM;
6995 
6996 	    /*
6997 	     * The "start" and "stop"  arguments can be a literal escape
6998 	     * sequence, or a comma seperated list of terminal codes.
6999 	     */
7000 	    if (STRNCMP(arg, "t_", 2) == 0)
7001 	    {
7002 		off = 0;
7003 		buf[0] = 0;
7004 		while (arg[off] != NUL)
7005 		{
7006 		    /* Isolate one termcap name */
7007 		    for (len = 0; arg[off + len] &&
7008 						 arg[off + len] != ','; ++len)
7009 			;
7010 		    tname = vim_strnsave(arg + off, len);
7011 		    if (tname == NULL)		/* out of memory */
7012 		    {
7013 			error = TRUE;
7014 			break;
7015 		    }
7016 		    /* lookup the escape sequence for the item */
7017 		    p = get_term_code(tname);
7018 		    vim_free(tname);
7019 		    if (p == NULL)	    /* ignore non-existing things */
7020 			p = (char_u *)"";
7021 
7022 		    /* Append it to the already found stuff */
7023 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7024 		    {
7025 			EMSG2(_("E422: terminal code too long: %s"), arg);
7026 			error = TRUE;
7027 			break;
7028 		    }
7029 		    STRCAT(buf, p);
7030 
7031 		    /* Advance to the next item */
7032 		    off += len;
7033 		    if (arg[off] == ',')	    /* another one follows */
7034 			++off;
7035 		}
7036 	    }
7037 	    else
7038 	    {
7039 		/*
7040 		 * Copy characters from arg[] to buf[], translating <> codes.
7041 		 */
7042 		for (p = arg, off = 0; off < 100 && *p; )
7043 		{
7044 		    len = trans_special(&p, buf + off, FALSE);
7045 		    if (len)		    /* recognized special char */
7046 			off += len;
7047 		    else		    /* copy as normal char */
7048 			buf[off++] = *p++;
7049 		}
7050 		buf[off] = NUL;
7051 	    }
7052 	    if (error)
7053 		break;
7054 
7055 	    if (STRCMP(buf, "NONE") == 0)	/* resetting the value */
7056 		p = NULL;
7057 	    else
7058 		p = vim_strsave(buf);
7059 	    if (key[2] == 'A')
7060 	    {
7061 		vim_free(HL_TABLE()[idx].sg_start);
7062 		HL_TABLE()[idx].sg_start = p;
7063 	    }
7064 	    else
7065 	    {
7066 		vim_free(HL_TABLE()[idx].sg_stop);
7067 		HL_TABLE()[idx].sg_stop = p;
7068 	    }
7069 	}
7070 	else
7071 	{
7072 	    EMSG2(_("E423: Illegal argument: %s"), key_start);
7073 	    error = TRUE;
7074 	    break;
7075 	}
7076 
7077 	/*
7078 	 * When highlighting has been given for a group, don't link it.
7079 	 */
7080 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7081 	    HL_TABLE()[idx].sg_link = 0;
7082 
7083 	/*
7084 	 * Continue with next argument.
7085 	 */
7086 	linep = skipwhite(linep);
7087       }
7088 
7089     /*
7090      * If there is an error, and it's a new entry, remove it from the table.
7091      */
7092     if (error && idx == highlight_ga.ga_len)
7093 	syn_unadd_group();
7094     else
7095     {
7096 	if (is_normal_group)
7097 	{
7098 	    HL_TABLE()[idx].sg_term_attr = 0;
7099 	    HL_TABLE()[idx].sg_cterm_attr = 0;
7100 #ifdef FEAT_GUI
7101 	    HL_TABLE()[idx].sg_gui_attr = 0;
7102 	    /*
7103 	     * Need to update all groups, because they might be using "bg"
7104 	     * and/or "fg", which have been changed now.
7105 	     */
7106 	    if (gui.in_use)
7107 		highlight_gui_started();
7108 #endif
7109 	}
7110 #ifdef FEAT_GUI_X11
7111 # ifdef FEAT_MENU
7112 	else if (is_menu_group)
7113 	{
7114 	    if (gui.in_use && do_colors)
7115 		gui_mch_new_menu_colors();
7116 	}
7117 # endif
7118 	else if (is_scrollbar_group)
7119 	{
7120 	    if (gui.in_use && do_colors)
7121 		gui_new_scrollbar_colors();
7122 	}
7123 # ifdef FEAT_BEVAL
7124 	else if (is_tooltip_group)
7125 	{
7126 	    if (gui.in_use && do_colors)
7127 		gui_mch_new_tooltip_colors();
7128 	}
7129 # endif
7130 #endif
7131 	else
7132 	    set_hl_attr(idx);
7133 #ifdef FEAT_EVAL
7134 	HL_TABLE()[idx].sg_scriptID = current_SID;
7135 #endif
7136 	redraw_all_later(NOT_VALID);
7137     }
7138     vim_free(key);
7139     vim_free(arg);
7140 
7141     /* Only call highlight_changed() once, after sourcing a syntax file */
7142     need_highlight_changed = TRUE;
7143 }
7144 
7145 #if defined(EXITFREE) || defined(PROTO)
7146     void
7147 free_highlight()
7148 {
7149     int	    i;
7150 
7151     for (i = 0; i < highlight_ga.ga_len; ++i)
7152     {
7153 	highlight_clear(i);
7154 	vim_free(HL_TABLE()[i].sg_name);
7155 	vim_free(HL_TABLE()[i].sg_name_u);
7156     }
7157     ga_clear(&highlight_ga);
7158 }
7159 #endif
7160 
7161 /*
7162  * Reset the cterm colors to what they were before Vim was started, if
7163  * possible.  Otherwise reset them to zero.
7164  */
7165     void
7166 restore_cterm_colors()
7167 {
7168 #if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7169     /* Since t_me has been set, this probably means that the user
7170      * wants to use this as default colors.  Need to reset default
7171      * background/foreground colors. */
7172     mch_set_normal_colors();
7173 #else
7174     cterm_normal_fg_color = 0;
7175     cterm_normal_fg_bold = 0;
7176     cterm_normal_bg_color = 0;
7177 #endif
7178 }
7179 
7180 /*
7181  * Return TRUE if highlight group "idx" has any settings.
7182  * When "check_link" is TRUE also check for an existing link.
7183  */
7184     static int
7185 hl_has_settings(idx, check_link)
7186     int		idx;
7187     int		check_link;
7188 {
7189     return (   HL_TABLE()[idx].sg_term_attr != 0
7190 	    || HL_TABLE()[idx].sg_cterm_attr != 0
7191 #ifdef FEAT_GUI
7192 	    || HL_TABLE()[idx].sg_gui_attr != 0
7193 #endif
7194 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7195 }
7196 
7197 /*
7198  * Clear highlighting for one group.
7199  */
7200     static void
7201 highlight_clear(idx)
7202     int idx;
7203 {
7204     HL_TABLE()[idx].sg_term = 0;
7205     vim_free(HL_TABLE()[idx].sg_start);
7206     HL_TABLE()[idx].sg_start = NULL;
7207     vim_free(HL_TABLE()[idx].sg_stop);
7208     HL_TABLE()[idx].sg_stop = NULL;
7209     HL_TABLE()[idx].sg_term_attr = 0;
7210     HL_TABLE()[idx].sg_cterm = 0;
7211     HL_TABLE()[idx].sg_cterm_bold = FALSE;
7212     HL_TABLE()[idx].sg_cterm_fg = 0;
7213     HL_TABLE()[idx].sg_cterm_bg = 0;
7214     HL_TABLE()[idx].sg_cterm_attr = 0;
7215 #ifdef FEAT_GUI	    /* in non-GUI fonts are simply ignored */
7216     HL_TABLE()[idx].sg_gui = 0;
7217     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7218     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7219     HL_TABLE()[idx].sg_gui_fg_name = NULL;
7220     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7221     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7222     HL_TABLE()[idx].sg_gui_bg_name = NULL;
7223     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7224     vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7225     HL_TABLE()[idx].sg_gui_sp_name = NULL;
7226     gui_mch_free_font(HL_TABLE()[idx].sg_font);
7227     HL_TABLE()[idx].sg_font = NOFONT;
7228 # ifdef FEAT_XFONTSET
7229     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7230     HL_TABLE()[idx].sg_fontset = NOFONTSET;
7231 # endif
7232     vim_free(HL_TABLE()[idx].sg_font_name);
7233     HL_TABLE()[idx].sg_font_name = NULL;
7234     HL_TABLE()[idx].sg_gui_attr = 0;
7235 #endif
7236 #ifdef FEAT_EVAL
7237     /* Clear the script ID only when there is no link, since that is not
7238      * cleared. */
7239     if (HL_TABLE()[idx].sg_link == 0)
7240 	HL_TABLE()[idx].sg_scriptID = 0;
7241 #endif
7242 }
7243 
7244 #if defined(FEAT_GUI) || defined(PROTO)
7245 /*
7246  * Set the normal foreground and background colors according to the "Normal"
7247  * highlighighting group.  For X11 also set "Menu", "Scrollbar", and
7248  * "Tooltip" colors.
7249  */
7250     void
7251 set_normal_colors()
7252 {
7253     if (set_group_colors((char_u *)"Normal",
7254 			     &gui.norm_pixel, &gui.back_pixel,
7255 			     FALSE, TRUE, FALSE))
7256     {
7257 	gui_mch_new_colors();
7258 	must_redraw = CLEAR;
7259     }
7260 #ifdef FEAT_GUI_X11
7261     if (set_group_colors((char_u *)"Menu",
7262 			 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7263 			 TRUE, FALSE, FALSE))
7264     {
7265 # ifdef FEAT_MENU
7266 	gui_mch_new_menu_colors();
7267 # endif
7268 	must_redraw = CLEAR;
7269     }
7270 # ifdef FEAT_BEVAL
7271     if (set_group_colors((char_u *)"Tooltip",
7272 			 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7273 			 FALSE, FALSE, TRUE))
7274     {
7275 # ifdef FEAT_TOOLBAR
7276 	gui_mch_new_tooltip_colors();
7277 # endif
7278 	must_redraw = CLEAR;
7279     }
7280 #endif
7281     if (set_group_colors((char_u *)"Scrollbar",
7282 		    &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7283 		    FALSE, FALSE, FALSE))
7284     {
7285 	gui_new_scrollbar_colors();
7286 	must_redraw = CLEAR;
7287     }
7288 #endif
7289 }
7290 
7291 /*
7292  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7293  */
7294     static int
7295 set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7296     char_u	*name;
7297     guicolor_T	*fgp;
7298     guicolor_T	*bgp;
7299     int		do_menu;
7300     int		use_norm;
7301     int		do_tooltip;
7302 {
7303     int		idx;
7304 
7305     idx = syn_name2id(name) - 1;
7306     if (idx >= 0)
7307     {
7308 	gui_do_one_color(idx, do_menu, do_tooltip);
7309 
7310 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7311 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
7312 	else if (use_norm)
7313 	    *fgp = gui.def_norm_pixel;
7314 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7315 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
7316 	else if (use_norm)
7317 	    *bgp = gui.def_back_pixel;
7318 	return TRUE;
7319     }
7320     return FALSE;
7321 }
7322 
7323 /*
7324  * Get the font of the "Normal" group.
7325  * Returns "" when it's not found or not set.
7326  */
7327     char_u *
7328 hl_get_font_name()
7329 {
7330     int		id;
7331     char_u	*s;
7332 
7333     id = syn_name2id((char_u *)"Normal");
7334     if (id > 0)
7335     {
7336 	s = HL_TABLE()[id - 1].sg_font_name;
7337 	if (s != NULL)
7338 	    return s;
7339     }
7340     return (char_u *)"";
7341 }
7342 
7343 /*
7344  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
7345  * actually chosen to be used.
7346  */
7347     void
7348 hl_set_font_name(font_name)
7349     char_u	*font_name;
7350 {
7351     int	    id;
7352 
7353     id = syn_name2id((char_u *)"Normal");
7354     if (id > 0)
7355     {
7356 	vim_free(HL_TABLE()[id - 1].sg_font_name);
7357 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7358     }
7359 }
7360 
7361 /*
7362  * Set background color for "Normal" group.  Called by gui_set_bg_color()
7363  * when the color is known.
7364  */
7365     void
7366 hl_set_bg_color_name(name)
7367     char_u  *name;	    /* must have been allocated */
7368 {
7369     int	    id;
7370 
7371     if (name != NULL)
7372     {
7373 	id = syn_name2id((char_u *)"Normal");
7374 	if (id > 0)
7375 	{
7376 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7377 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
7378 	}
7379     }
7380 }
7381 
7382 /*
7383  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
7384  * when the color is known.
7385  */
7386     void
7387 hl_set_fg_color_name(name)
7388     char_u  *name;	    /* must have been allocated */
7389 {
7390     int	    id;
7391 
7392     if (name != NULL)
7393     {
7394 	id = syn_name2id((char_u *)"Normal");
7395 	if (id > 0)
7396 	{
7397 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7398 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
7399 	}
7400     }
7401 }
7402 
7403 /*
7404  * Return the handle for a color name.
7405  * Returns INVALCOLOR when failed.
7406  */
7407     static guicolor_T
7408 color_name2handle(name)
7409     char_u  *name;
7410 {
7411     if (STRCMP(name, "NONE") == 0)
7412 	return INVALCOLOR;
7413 
7414     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7415 	return gui.norm_pixel;
7416     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7417 	return gui.back_pixel;
7418 
7419     return gui_get_color(name);
7420 }
7421 
7422 /*
7423  * Return the handle for a font name.
7424  * Returns NOFONT when failed.
7425  */
7426     static GuiFont
7427 font_name2handle(name)
7428     char_u  *name;
7429 {
7430     if (STRCMP(name, "NONE") == 0)
7431 	return NOFONT;
7432 
7433     return gui_mch_get_font(name, TRUE);
7434 }
7435 
7436 # ifdef FEAT_XFONTSET
7437 /*
7438  * Return the handle for a fontset name.
7439  * Returns NOFONTSET when failed.
7440  */
7441     static GuiFontset
7442 fontset_name2handle(name, fixed_width)
7443     char_u	*name;
7444     int		fixed_width;
7445 {
7446     if (STRCMP(name, "NONE") == 0)
7447 	return NOFONTSET;
7448 
7449     return gui_mch_get_fontset(name, TRUE, fixed_width);
7450 }
7451 # endif
7452 
7453 /*
7454  * Get the font or fontset for one highlight group.
7455  */
7456 /*ARGSUSED*/
7457     static void
7458 hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7459     int		idx;
7460     char_u	*arg;
7461     int		do_normal;	/* set normal font */
7462     int		do_menu;	/* set menu font */
7463     int		do_tooltip;	/* set tooltip font */
7464 {
7465 # ifdef FEAT_XFONTSET
7466     /* If 'guifontset' is not empty, first try using the name as a
7467      * fontset.  If that doesn't work, use it as a font name. */
7468     if (*p_guifontset != NUL
7469 #  ifdef FONTSET_ALWAYS
7470 	|| do_menu
7471 #  endif
7472 #  ifdef FEAT_BEVAL_TIP
7473 	/* In Athena & Motif, the Tooltip highlight group is always a fontset */
7474 	|| do_tooltip
7475 #  endif
7476 	    )
7477 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7478 #  ifdef FONTSET_ALWAYS
7479 		|| do_menu
7480 #  endif
7481 #  ifdef FEAT_BEVAL_TIP
7482 		|| do_tooltip
7483 #  endif
7484 		);
7485     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7486     {
7487 	/* If it worked and it's the Normal group, use it as the
7488 	 * normal fontset.  Same for the Menu group. */
7489 	if (do_normal)
7490 	    gui_init_font(arg, TRUE);
7491 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7492 	if (do_menu)
7493 	{
7494 #    ifdef FONTSET_ALWAYS
7495 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7496 #    else
7497 	    /* YIKES!  This is a bug waiting to crash the program */
7498 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
7499 #    endif
7500 	    gui_mch_new_menu_font();
7501 	}
7502 #    ifdef FEAT_BEVAL
7503 	if (do_tooltip)
7504 	{
7505 	    /* The Athena widget set cannot currently handle switching between
7506 	     * displaying a single font and a fontset.
7507 	     * If the XtNinternational resource is set to True at widget
7508 	     * creation, then a fontset is always used, othwise an
7509 	     * XFontStruct is used.
7510 	     */
7511 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7512 	    gui_mch_new_tooltip_font();
7513 	}
7514 #    endif
7515 #   endif
7516     }
7517     else
7518 # endif
7519     {
7520 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
7521 	/* If it worked and it's the Normal group, use it as the
7522 	 * normal font.  Same for the Menu group. */
7523 	if (HL_TABLE()[idx].sg_font != NOFONT)
7524 	{
7525 	    if (do_normal)
7526 		gui_init_font(arg, FALSE);
7527 #ifndef FONTSET_ALWAYS
7528 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7529 	    if (do_menu)
7530 	    {
7531 		gui.menu_font = HL_TABLE()[idx].sg_font;
7532 		gui_mch_new_menu_font();
7533 	    }
7534 # endif
7535 #endif
7536 	}
7537     }
7538 }
7539 
7540 #endif /* FEAT_GUI */
7541 
7542 /*
7543  * Table with the specifications for an attribute number.
7544  * Note that this table is used by ALL buffers.  This is required because the
7545  * GUI can redraw at any time for any buffer.
7546  */
7547 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
7548 
7549 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7550 
7551 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
7552 
7553 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7554 
7555 #ifdef FEAT_GUI
7556 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
7557 
7558 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7559 #endif
7560 
7561 /*
7562  * Return the attr number for a set of colors and font.
7563  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7564  * if the combination is new.
7565  * Return 0 for error (no more room).
7566  */
7567     static int
7568 get_attr_entry(table, aep)
7569     garray_T	*table;
7570     attrentry_T	*aep;
7571 {
7572     int		i;
7573     attrentry_T	*taep;
7574     static int	recursive = FALSE;
7575 
7576     /*
7577      * Init the table, in case it wasn't done yet.
7578      */
7579     table->ga_itemsize = sizeof(attrentry_T);
7580     table->ga_growsize = 7;
7581 
7582     /*
7583      * Try to find an entry with the same specifications.
7584      */
7585     for (i = 0; i < table->ga_len; ++i)
7586     {
7587 	taep = &(((attrentry_T *)table->ga_data)[i]);
7588 	if (	   aep->ae_attr == taep->ae_attr
7589 		&& (
7590 #ifdef FEAT_GUI
7591 		       (table == &gui_attr_table
7592 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7593 			    && aep->ae_u.gui.bg_color
7594 						    == taep->ae_u.gui.bg_color
7595 			    && aep->ae_u.gui.sp_color
7596 						    == taep->ae_u.gui.sp_color
7597 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
7598 #  ifdef FEAT_XFONTSET
7599 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
7600 #  endif
7601 			    ))
7602 		    ||
7603 #endif
7604 		       (table == &term_attr_table
7605 			&& (aep->ae_u.term.start == NULL)
7606 					    == (taep->ae_u.term.start == NULL)
7607 			&& (aep->ae_u.term.start == NULL
7608 			    || STRCMP(aep->ae_u.term.start,
7609 						  taep->ae_u.term.start) == 0)
7610 			&& (aep->ae_u.term.stop == NULL)
7611 					     == (taep->ae_u.term.stop == NULL)
7612 			&& (aep->ae_u.term.stop == NULL
7613 			    || STRCMP(aep->ae_u.term.stop,
7614 						  taep->ae_u.term.stop) == 0))
7615 		    || (table == &cterm_attr_table
7616 			    && aep->ae_u.cterm.fg_color
7617 						  == taep->ae_u.cterm.fg_color
7618 			    && aep->ae_u.cterm.bg_color
7619 						 == taep->ae_u.cterm.bg_color)
7620 		     ))
7621 
7622 	return i + ATTR_OFF;
7623     }
7624 
7625     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
7626     {
7627 	/*
7628 	 * Running out of attribute entries!  remove all attributes, and
7629 	 * compute new ones for all groups.
7630 	 * When called recursively, we are really out of numbers.
7631 	 */
7632 	if (recursive)
7633 	{
7634 	    EMSG(_("E424: Too many different highlighting attributes in use"));
7635 	    return 0;
7636 	}
7637 	recursive = TRUE;
7638 
7639 	clear_hl_tables();
7640 
7641 	must_redraw = CLEAR;
7642 
7643 	for (i = 0; i < highlight_ga.ga_len; ++i)
7644 	    set_hl_attr(i);
7645 
7646 	recursive = FALSE;
7647     }
7648 
7649     /*
7650      * This is a new combination of colors and font, add an entry.
7651      */
7652     if (ga_grow(table, 1) == FAIL)
7653 	return 0;
7654 
7655     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7656     vim_memset(taep, 0, sizeof(attrentry_T));
7657     taep->ae_attr = aep->ae_attr;
7658 #ifdef FEAT_GUI
7659     if (table == &gui_attr_table)
7660     {
7661 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7662 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7663 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7664 	taep->ae_u.gui.font = aep->ae_u.gui.font;
7665 # ifdef FEAT_XFONTSET
7666 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7667 # endif
7668     }
7669 #endif
7670     if (table == &term_attr_table)
7671     {
7672 	if (aep->ae_u.term.start == NULL)
7673 	    taep->ae_u.term.start = NULL;
7674 	else
7675 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7676 	if (aep->ae_u.term.stop == NULL)
7677 	    taep->ae_u.term.stop = NULL;
7678 	else
7679 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7680     }
7681     else if (table == &cterm_attr_table)
7682     {
7683 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7684 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7685     }
7686     ++table->ga_len;
7687     return (table->ga_len - 1 + ATTR_OFF);
7688 }
7689 
7690 /*
7691  * Clear all highlight tables.
7692  */
7693     void
7694 clear_hl_tables()
7695 {
7696     int		i;
7697     attrentry_T	*taep;
7698 
7699 #ifdef FEAT_GUI
7700     ga_clear(&gui_attr_table);
7701 #endif
7702     for (i = 0; i < term_attr_table.ga_len; ++i)
7703     {
7704 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7705 	vim_free(taep->ae_u.term.start);
7706 	vim_free(taep->ae_u.term.stop);
7707     }
7708     ga_clear(&term_attr_table);
7709     ga_clear(&cterm_attr_table);
7710 }
7711 
7712 #if defined(FEAT_SYN_HL) || defined(PROTO)
7713 /*
7714  * Combine special attributes (e.g., for spelling) with other attributes
7715  * (e.g., for syntax highlighting).
7716  * "prim_attr" overrules "char_attr".
7717  * This creates a new group when required.
7718  * Since we expect there to be few spelling mistakes we don't cache the
7719  * result.
7720  * Return the resulting attributes.
7721  */
7722     int
7723 hl_combine_attr(char_attr, prim_attr)
7724     int	    char_attr;
7725     int	    prim_attr;
7726 {
7727     attrentry_T *char_aep = NULL;
7728     attrentry_T *spell_aep;
7729     attrentry_T new_en;
7730 
7731     if (char_attr == 0)
7732 	return prim_attr;
7733     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7734 	return char_attr | prim_attr;
7735 #ifdef FEAT_GUI
7736     if (gui.in_use)
7737     {
7738 	if (char_attr > HL_ALL)
7739 	    char_aep = syn_gui_attr2entry(char_attr);
7740 	if (char_aep != NULL)
7741 	    new_en = *char_aep;
7742 	else
7743 	{
7744 	    vim_memset(&new_en, 0, sizeof(new_en));
7745 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
7746 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
7747 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
7748 	    if (char_attr <= HL_ALL)
7749 		new_en.ae_attr = char_attr;
7750 	}
7751 
7752 	if (prim_attr <= HL_ALL)
7753 	    new_en.ae_attr |= prim_attr;
7754 	else
7755 	{
7756 	    spell_aep = syn_gui_attr2entry(prim_attr);
7757 	    if (spell_aep != NULL)
7758 	    {
7759 		new_en.ae_attr |= spell_aep->ae_attr;
7760 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7761 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7762 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7763 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7764 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7765 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7766 		if (spell_aep->ae_u.gui.font != NOFONT)
7767 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7768 # ifdef FEAT_XFONTSET
7769 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7770 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7771 # endif
7772 	    }
7773 	}
7774 	return get_attr_entry(&gui_attr_table, &new_en);
7775     }
7776 #endif
7777 
7778     if (t_colors > 1)
7779     {
7780 	if (char_attr > HL_ALL)
7781 	    char_aep = syn_cterm_attr2entry(char_attr);
7782 	if (char_aep != NULL)
7783 	    new_en = *char_aep;
7784 	else
7785 	{
7786 	    vim_memset(&new_en, 0, sizeof(new_en));
7787 	    if (char_attr <= HL_ALL)
7788 		new_en.ae_attr = char_attr;
7789 	}
7790 
7791 	if (prim_attr <= HL_ALL)
7792 	    new_en.ae_attr |= prim_attr;
7793 	else
7794 	{
7795 	    spell_aep = syn_cterm_attr2entry(prim_attr);
7796 	    if (spell_aep != NULL)
7797 	    {
7798 		new_en.ae_attr |= spell_aep->ae_attr;
7799 		if (spell_aep->ae_u.cterm.fg_color > 0)
7800 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7801 		if (spell_aep->ae_u.cterm.bg_color > 0)
7802 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7803 	    }
7804 	}
7805 	return get_attr_entry(&cterm_attr_table, &new_en);
7806     }
7807 
7808     if (char_attr > HL_ALL)
7809 	char_aep = syn_term_attr2entry(char_attr);
7810     if (char_aep != NULL)
7811 	new_en = *char_aep;
7812     else
7813     {
7814 	vim_memset(&new_en, 0, sizeof(new_en));
7815 	if (char_attr <= HL_ALL)
7816 	    new_en.ae_attr = char_attr;
7817     }
7818 
7819     if (prim_attr <= HL_ALL)
7820 	new_en.ae_attr |= prim_attr;
7821     else
7822     {
7823 	spell_aep = syn_term_attr2entry(prim_attr);
7824 	if (spell_aep != NULL)
7825 	{
7826 	    new_en.ae_attr |= spell_aep->ae_attr;
7827 	    if (spell_aep->ae_u.term.start != NULL)
7828 	    {
7829 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
7830 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
7831 	    }
7832 	}
7833     }
7834     return get_attr_entry(&term_attr_table, &new_en);
7835 }
7836 #endif
7837 
7838 #ifdef FEAT_GUI
7839 
7840     attrentry_T *
7841 syn_gui_attr2entry(attr)
7842     int		    attr;
7843 {
7844     attr -= ATTR_OFF;
7845     if (attr >= gui_attr_table.ga_len)	    /* did ":syntax clear" */
7846 	return NULL;
7847     return &(GUI_ATTR_ENTRY(attr));
7848 }
7849 
7850 #endif /* FEAT_GUI */
7851 
7852     attrentry_T *
7853 syn_term_attr2entry(attr)
7854     int		    attr;
7855 {
7856     attr -= ATTR_OFF;
7857     if (attr >= term_attr_table.ga_len)	    /* did ":syntax clear" */
7858 	return NULL;
7859     return &(TERM_ATTR_ENTRY(attr));
7860 }
7861 
7862     attrentry_T *
7863 syn_cterm_attr2entry(attr)
7864     int		    attr;
7865 {
7866     attr -= ATTR_OFF;
7867     if (attr >= cterm_attr_table.ga_len)	/* did ":syntax clear" */
7868 	return NULL;
7869     return &(CTERM_ATTR_ENTRY(attr));
7870 }
7871 
7872 #define LIST_ATTR   1
7873 #define LIST_STRING 2
7874 #define LIST_INT    3
7875 
7876     static void
7877 highlight_list_one(id)
7878     int		id;
7879 {
7880     struct hl_group	*sgp;
7881     int			didh = FALSE;
7882 
7883     sgp = &HL_TABLE()[id - 1];	    /* index is ID minus one */
7884 
7885     didh = highlight_list_arg(id, didh, LIST_ATTR,
7886 				    sgp->sg_term, NULL, "term");
7887     didh = highlight_list_arg(id, didh, LIST_STRING,
7888 				    0, sgp->sg_start, "start");
7889     didh = highlight_list_arg(id, didh, LIST_STRING,
7890 				    0, sgp->sg_stop, "stop");
7891 
7892     didh = highlight_list_arg(id, didh, LIST_ATTR,
7893 				    sgp->sg_cterm, NULL, "cterm");
7894     didh = highlight_list_arg(id, didh, LIST_INT,
7895 				    sgp->sg_cterm_fg, NULL, "ctermfg");
7896     didh = highlight_list_arg(id, didh, LIST_INT,
7897 				    sgp->sg_cterm_bg, NULL, "ctermbg");
7898 
7899 #ifdef FEAT_GUI
7900     didh = highlight_list_arg(id, didh, LIST_ATTR,
7901 				    sgp->sg_gui, NULL, "gui");
7902     didh = highlight_list_arg(id, didh, LIST_STRING,
7903 				    0, sgp->sg_gui_fg_name, "guifg");
7904     didh = highlight_list_arg(id, didh, LIST_STRING,
7905 				    0, sgp->sg_gui_bg_name, "guibg");
7906     didh = highlight_list_arg(id, didh, LIST_STRING,
7907 				    0, sgp->sg_gui_sp_name, "guisp");
7908     didh = highlight_list_arg(id, didh, LIST_STRING,
7909 				    0, sgp->sg_font_name, "font");
7910 #endif
7911 
7912     if (sgp->sg_link && !got_int)
7913     {
7914 	(void)syn_list_header(didh, 9999, id);
7915 	didh = TRUE;
7916 	msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7917 	msg_putchar(' ');
7918 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7919     }
7920 
7921 #ifdef FEAT_EVAL
7922     if (didh && p_verbose > 0)
7923 	last_set_msg(sgp->sg_scriptID);
7924 #endif
7925 }
7926 
7927     static int
7928 highlight_list_arg(id, didh, type, iarg, sarg, name)
7929     int		id;
7930     int		didh;
7931     int		type;
7932     int		iarg;
7933     char_u	*sarg;
7934     char	*name;
7935 {
7936     char_u	buf[100];
7937     char_u	*ts;
7938     int		i;
7939 
7940     if (got_int)
7941 	return FALSE;
7942     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7943     {
7944 	ts = buf;
7945 	if (type == LIST_INT)
7946 	    sprintf((char *)buf, "%d", iarg - 1);
7947 	else if (type == LIST_STRING)
7948 	    ts = sarg;
7949 	else /* type == LIST_ATTR */
7950 	{
7951 	    buf[0] = NUL;
7952 	    for (i = 0; hl_attr_table[i] != 0; ++i)
7953 	    {
7954 		if (iarg & hl_attr_table[i])
7955 		{
7956 		    if (buf[0] != NUL)
7957 			STRCAT(buf, ",");
7958 		    STRCAT(buf, hl_name_table[i]);
7959 		    iarg &= ~hl_attr_table[i];	    /* don't want "inverse" */
7960 		}
7961 	    }
7962 	}
7963 
7964 	(void)syn_list_header(didh,
7965 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7966 	didh = TRUE;
7967 	if (!got_int)
7968 	{
7969 	    MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7970 	    MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7971 	    msg_outtrans(ts);
7972 	}
7973     }
7974     return didh;
7975 }
7976 
7977 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7978 /*
7979  * Return "1" if highlight group "id" has attribute "flag".
7980  * Return NULL otherwise.
7981  */
7982     char_u *
7983 highlight_has_attr(id, flag, modec)
7984     int		id;
7985     int		flag;
7986     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
7987 {
7988     int		attr;
7989 
7990     if (id <= 0 || id > highlight_ga.ga_len)
7991 	return NULL;
7992 
7993 #ifdef FEAT_GUI
7994     if (modec == 'g')
7995 	attr = HL_TABLE()[id - 1].sg_gui;
7996     else
7997 #endif
7998 	 if (modec == 'c')
7999 	attr = HL_TABLE()[id - 1].sg_cterm;
8000     else
8001 	attr = HL_TABLE()[id - 1].sg_term;
8002 
8003     if (attr & flag)
8004 	return (char_u *)"1";
8005     return NULL;
8006 }
8007 #endif
8008 
8009 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8010 /*
8011  * Return color name of highlight group "id".
8012  */
8013     char_u *
8014 highlight_color(id, what, modec)
8015     int		id;
8016     char_u	*what;	/* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
8017     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
8018 {
8019     static char_u	name[20];
8020     int			n;
8021     int			fg = FALSE;
8022 # ifdef FEAT_GUI
8023     int			sp = FALSE;
8024 # endif
8025 
8026     if (id <= 0 || id > highlight_ga.ga_len)
8027 	return NULL;
8028 
8029     if (TOLOWER_ASC(what[0]) == 'f')
8030 	fg = TRUE;
8031 # ifdef FEAT_GUI
8032     else if (TOLOWER_ASC(what[0]) == 's')
8033 	sp = TRUE;
8034     if (modec == 'g')
8035     {
8036 	/* return #RRGGBB form (only possible when GUI is running) */
8037 	if (gui.in_use && what[1] && what[2] == '#')
8038 	{
8039 	    guicolor_T		color;
8040 	    long_u		rgb;
8041 	    static char_u	buf[10];
8042 
8043 	    if (fg)
8044 		color = HL_TABLE()[id - 1].sg_gui_fg;
8045 	    else if (sp)
8046 		color = HL_TABLE()[id - 1].sg_gui_sp;
8047 	    else
8048 		color = HL_TABLE()[id - 1].sg_gui_bg;
8049 	    if (color == INVALCOLOR)
8050 		return NULL;
8051 	    rgb = gui_mch_get_rgb(color);
8052 	    sprintf((char *)buf, "#%02x%02x%02x",
8053 				      (unsigned)(rgb >> 16),
8054 				      (unsigned)(rgb >> 8) & 255,
8055 				      (unsigned)rgb & 255);
8056 	    return buf;
8057 	}
8058 	if (fg)
8059 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
8060 	if (sp)
8061 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
8062 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
8063     }
8064 # endif
8065     if (modec == 'c')
8066     {
8067 	if (fg)
8068 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8069 	else
8070 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8071 	sprintf((char *)name, "%d", n);
8072 	return name;
8073     }
8074     /* term doesn't have color */
8075     return NULL;
8076 }
8077 #endif
8078 
8079 #if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8080 	|| defined(PROTO)
8081 /*
8082  * Return color name of highlight group "id" as RGB value.
8083  */
8084     long_u
8085 highlight_gui_color_rgb(id, fg)
8086     int		id;
8087     int		fg;	/* TRUE = fg, FALSE = bg */
8088 {
8089     guicolor_T	color;
8090 
8091     if (id <= 0 || id > highlight_ga.ga_len)
8092 	return 0L;
8093 
8094     if (fg)
8095 	color = HL_TABLE()[id - 1].sg_gui_fg;
8096     else
8097 	color = HL_TABLE()[id - 1].sg_gui_bg;
8098 
8099     if (color == INVALCOLOR)
8100 	return 0L;
8101 
8102     return gui_mch_get_rgb(color);
8103 }
8104 #endif
8105 
8106 /*
8107  * Output the syntax list header.
8108  * Return TRUE when started a new line.
8109  */
8110     static int
8111 syn_list_header(did_header, outlen, id)
8112     int	    did_header;		/* did header already */
8113     int	    outlen;		/* length of string that comes */
8114     int	    id;			/* highlight group id */
8115 {
8116     int	    endcol = 19;
8117     int	    newline = TRUE;
8118 
8119     if (!did_header)
8120     {
8121 	msg_putchar('\n');
8122 	if (got_int)
8123 	    return TRUE;
8124 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
8125 	endcol = 15;
8126     }
8127     else if (msg_col + outlen + 1 >= Columns)
8128     {
8129 	msg_putchar('\n');
8130 	if (got_int)
8131 	    return TRUE;
8132     }
8133     else
8134     {
8135 	if (msg_col >= endcol)	/* wrap around is like starting a new line */
8136 	    newline = FALSE;
8137     }
8138 
8139     if (msg_col >= endcol)	/* output at least one space */
8140 	endcol = msg_col + 1;
8141     if (Columns <= endcol)	/* avoid hang for tiny window */
8142 	endcol = Columns - 1;
8143 
8144     msg_advance(endcol);
8145 
8146     /* Show "xxx" with the attributes. */
8147     if (!did_header)
8148     {
8149 	msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8150 	msg_putchar(' ');
8151     }
8152 
8153     return newline;
8154 }
8155 
8156 /*
8157  * Set the attribute numbers for a highlight group.
8158  * Called after one of the attributes has changed.
8159  */
8160     static void
8161 set_hl_attr(idx)
8162     int		idx;	    /* index in array */
8163 {
8164     attrentry_T		at_en;
8165     struct hl_group	*sgp = HL_TABLE() + idx;
8166 
8167     /* The "Normal" group doesn't need an attribute number */
8168     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8169 	return;
8170 
8171 #ifdef FEAT_GUI
8172     /*
8173      * For the GUI mode: If there are other than "normal" highlighting
8174      * attributes, need to allocate an attr number.
8175      */
8176     if (sgp->sg_gui_fg == INVALCOLOR
8177 	    && sgp->sg_gui_bg == INVALCOLOR
8178 	    && sgp->sg_gui_sp == INVALCOLOR
8179 	    && sgp->sg_font == NOFONT
8180 # ifdef FEAT_XFONTSET
8181 	    && sgp->sg_fontset == NOFONTSET
8182 # endif
8183 	    )
8184     {
8185 	sgp->sg_gui_attr = sgp->sg_gui;
8186     }
8187     else
8188     {
8189 	at_en.ae_attr = sgp->sg_gui;
8190 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8191 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
8192 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
8193 	at_en.ae_u.gui.font = sgp->sg_font;
8194 # ifdef FEAT_XFONTSET
8195 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
8196 # endif
8197 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8198     }
8199 #endif
8200     /*
8201      * For the term mode: If there are other than "normal" highlighting
8202      * attributes, need to allocate an attr number.
8203      */
8204     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8205 	sgp->sg_term_attr = sgp->sg_term;
8206     else
8207     {
8208 	at_en.ae_attr = sgp->sg_term;
8209 	at_en.ae_u.term.start = sgp->sg_start;
8210 	at_en.ae_u.term.stop = sgp->sg_stop;
8211 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8212     }
8213 
8214     /*
8215      * For the color term mode: If there are other than "normal"
8216      * highlighting attributes, need to allocate an attr number.
8217      */
8218     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8219 	sgp->sg_cterm_attr = sgp->sg_cterm;
8220     else
8221     {
8222 	at_en.ae_attr = sgp->sg_cterm;
8223 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8224 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8225 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8226     }
8227 }
8228 
8229 /*
8230  * Lookup a highlight group name and return it's ID.
8231  * If it is not found, 0 is returned.
8232  */
8233     int
8234 syn_name2id(name)
8235     char_u	*name;
8236 {
8237     int		i;
8238     char_u	name_u[200];
8239 
8240     /* Avoid using stricmp() too much, it's slow on some systems */
8241     /* Avoid alloc()/free(), these are slow too.  ID names over 200 chars
8242      * don't deserve to be found! */
8243     vim_strncpy(name_u, name, 199);
8244     vim_strup(name_u);
8245     for (i = highlight_ga.ga_len; --i >= 0; )
8246 	if (HL_TABLE()[i].sg_name_u != NULL
8247 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8248 	    break;
8249     return i + 1;
8250 }
8251 
8252 #if defined(FEAT_EVAL) || defined(PROTO)
8253 /*
8254  * Return TRUE if highlight group "name" exists.
8255  */
8256     int
8257 highlight_exists(name)
8258     char_u	*name;
8259 {
8260     return (syn_name2id(name) > 0);
8261 }
8262 #endif
8263 
8264 /*
8265  * Like syn_name2id(), but take a pointer + length argument.
8266  */
8267     int
8268 syn_namen2id(linep, len)
8269     char_u  *linep;
8270     int	    len;
8271 {
8272     char_u  *name;
8273     int	    id = 0;
8274 
8275     name = vim_strnsave(linep, len);
8276     if (name != NULL)
8277     {
8278 	id = syn_name2id(name);
8279 	vim_free(name);
8280     }
8281     return id;
8282 }
8283 
8284 /*
8285  * Find highlight group name in the table and return it's ID.
8286  * The argument is a pointer to the name and the length of the name.
8287  * If it doesn't exist yet, a new entry is created.
8288  * Return 0 for failure.
8289  */
8290     int
8291 syn_check_group(pp, len)
8292     char_u		*pp;
8293     int			len;
8294 {
8295     int	    id;
8296     char_u  *name;
8297 
8298     name = vim_strnsave(pp, len);
8299     if (name == NULL)
8300 	return 0;
8301 
8302     id = syn_name2id(name);
8303     if (id == 0)			/* doesn't exist yet */
8304 	id = syn_add_group(name);
8305     else
8306 	vim_free(name);
8307     return id;
8308 }
8309 
8310 /*
8311  * Add new highlight group and return it's ID.
8312  * "name" must be an allocated string, it will be consumed.
8313  * Return 0 for failure.
8314  */
8315     static int
8316 syn_add_group(name)
8317     char_u	*name;
8318 {
8319     char_u	*p;
8320 
8321     /* Check that the name is ASCII letters, digits and underscore. */
8322     for (p = name; *p != NUL; ++p)
8323     {
8324 	if (!vim_isprintc(*p))
8325 	{
8326 	    EMSG(_("E669: Unprintable character in group name"));
8327 	    return 0;
8328 	}
8329 	else if (!ASCII_ISALNUM(*p) && *p != '_')
8330 	{
8331 	    /* This is an error, but since there previously was no check only
8332 	     * give a warning. */
8333 	    msg_source(hl_attr(HLF_W));
8334 	    MSG(_("W18: Invalid character in group name"));
8335 	    break;
8336 	}
8337     }
8338 
8339     /*
8340      * First call for this growarray: init growing array.
8341      */
8342     if (highlight_ga.ga_data == NULL)
8343     {
8344 	highlight_ga.ga_itemsize = sizeof(struct hl_group);
8345 	highlight_ga.ga_growsize = 10;
8346     }
8347 
8348     /*
8349      * Make room for at least one other syntax_highlight entry.
8350      */
8351     if (ga_grow(&highlight_ga, 1) == FAIL)
8352     {
8353 	vim_free(name);
8354 	return 0;
8355     }
8356 
8357     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8358     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8359     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8360 #ifdef FEAT_GUI
8361     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8362     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
8363     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
8364 #endif
8365     ++highlight_ga.ga_len;
8366 
8367     return highlight_ga.ga_len;		    /* ID is index plus one */
8368 }
8369 
8370 /*
8371  * When, just after calling syn_add_group(), an error is discovered, this
8372  * function deletes the new name.
8373  */
8374     static void
8375 syn_unadd_group()
8376 {
8377     --highlight_ga.ga_len;
8378     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8379     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8380 }
8381 
8382 /*
8383  * Translate a group ID to highlight attributes.
8384  */
8385     int
8386 syn_id2attr(hl_id)
8387     int			hl_id;
8388 {
8389     int			attr;
8390     struct hl_group	*sgp;
8391 
8392     hl_id = syn_get_final_id(hl_id);
8393     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8394 
8395 #ifdef FEAT_GUI
8396     /*
8397      * Only use GUI attr when the GUI is being used.
8398      */
8399     if (gui.in_use)
8400 	attr = sgp->sg_gui_attr;
8401     else
8402 #endif
8403 	if (t_colors > 1)
8404 	    attr = sgp->sg_cterm_attr;
8405 	else
8406 	    attr = sgp->sg_term_attr;
8407 
8408     return attr;
8409 }
8410 
8411 #ifdef FEAT_GUI
8412 /*
8413  * Get the GUI colors and attributes for a group ID.
8414  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8415  */
8416     int
8417 syn_id2colors(hl_id, fgp, bgp)
8418     int		hl_id;
8419     guicolor_T	*fgp;
8420     guicolor_T	*bgp;
8421 {
8422     struct hl_group	*sgp;
8423 
8424     hl_id = syn_get_final_id(hl_id);
8425     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8426 
8427     *fgp = sgp->sg_gui_fg;
8428     *bgp = sgp->sg_gui_bg;
8429     return sgp->sg_gui;
8430 }
8431 #endif
8432 
8433 /*
8434  * Translate a group ID to the final group ID (following links).
8435  */
8436     int
8437 syn_get_final_id(hl_id)
8438     int			hl_id;
8439 {
8440     int			count;
8441     struct hl_group	*sgp;
8442 
8443     if (hl_id > highlight_ga.ga_len || hl_id < 1)
8444 	return 0;			/* Can be called from eval!! */
8445 
8446     /*
8447      * Follow links until there is no more.
8448      * Look out for loops!  Break after 100 links.
8449      */
8450     for (count = 100; --count >= 0; )
8451     {
8452 	sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8453 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8454 	    break;
8455 	hl_id = sgp->sg_link;
8456     }
8457 
8458     return hl_id;
8459 }
8460 
8461 #ifdef FEAT_GUI
8462 /*
8463  * Call this function just after the GUI has started.
8464  * It finds the font and color handles for the highlighting groups.
8465  */
8466     void
8467 highlight_gui_started()
8468 {
8469     int	    idx;
8470 
8471     /* First get the colors from the "Normal" and "Menu" group, if set */
8472     set_normal_colors();
8473 
8474     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8475 	gui_do_one_color(idx, FALSE, FALSE);
8476 
8477     highlight_changed();
8478 }
8479 
8480     static void
8481 gui_do_one_color(idx, do_menu, do_tooltip)
8482     int		idx;
8483     int		do_menu;	/* TRUE: might set the menu font */
8484     int		do_tooltip;	/* TRUE: might set the tooltip font */
8485 {
8486     int		didit = FALSE;
8487 
8488     if (HL_TABLE()[idx].sg_font_name != NULL)
8489     {
8490 	hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8491 		   do_tooltip);
8492 	didit = TRUE;
8493     }
8494     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8495     {
8496 	HL_TABLE()[idx].sg_gui_fg =
8497 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8498 	didit = TRUE;
8499     }
8500     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8501     {
8502 	HL_TABLE()[idx].sg_gui_bg =
8503 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8504 	didit = TRUE;
8505     }
8506     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8507     {
8508 	HL_TABLE()[idx].sg_gui_sp =
8509 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8510 	didit = TRUE;
8511     }
8512     if (didit)	/* need to get a new attr number */
8513 	set_hl_attr(idx);
8514 }
8515 
8516 #endif
8517 
8518 /*
8519  * Translate the 'highlight' option into attributes in highlight_attr[] and
8520  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
8521  * corresponding highlights to use on top of HLF_SNC is computed.
8522  * Called only when the 'highlight' option has been changed and upon first
8523  * screen redraw after any :highlight command.
8524  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
8525  */
8526     int
8527 highlight_changed()
8528 {
8529     int		hlf;
8530     int		i;
8531     char_u	*p;
8532     int		attr;
8533     char_u	*end;
8534     int		id;
8535 #ifdef USER_HIGHLIGHT
8536     char_u      userhl[10];
8537 # ifdef FEAT_STL_OPT
8538     int		id_SNC = -1;
8539     int		id_S = -1;
8540     int		hlcnt;
8541 # endif
8542 #endif
8543     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
8544 
8545     need_highlight_changed = FALSE;
8546 
8547     /*
8548      * Clear all attributes.
8549      */
8550     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8551 	highlight_attr[hlf] = 0;
8552 
8553     /*
8554      * First set all attributes to their default value.
8555      * Then use the attributes from the 'highlight' option.
8556      */
8557     for (i = 0; i < 2; ++i)
8558     {
8559 	if (i)
8560 	    p = p_hl;
8561 	else
8562 	    p = get_highlight_default();
8563 	if (p == NULL)	    /* just in case */
8564 	    continue;
8565 
8566 	while (*p)
8567 	{
8568 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8569 		if (hl_flags[hlf] == *p)
8570 		    break;
8571 	    ++p;
8572 	    if (hlf == (int)HLF_COUNT || *p == NUL)
8573 		return FAIL;
8574 
8575 	    /*
8576 	     * Allow several hl_flags to be combined, like "bu" for
8577 	     * bold-underlined.
8578 	     */
8579 	    attr = 0;
8580 	    for ( ; *p && *p != ','; ++p)	    /* parse upto comma */
8581 	    {
8582 		if (vim_iswhite(*p))		    /* ignore white space */
8583 		    continue;
8584 
8585 		if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
8586 		    return FAIL;
8587 
8588 		switch (*p)
8589 		{
8590 		    case 'b':	attr |= HL_BOLD;
8591 				break;
8592 		    case 'i':	attr |= HL_ITALIC;
8593 				break;
8594 		    case '-':
8595 		    case 'n':			    /* no highlighting */
8596 				break;
8597 		    case 'r':	attr |= HL_INVERSE;
8598 				break;
8599 		    case 's':	attr |= HL_STANDOUT;
8600 				break;
8601 		    case 'u':	attr |= HL_UNDERLINE;
8602 				break;
8603 		    case 'c':	attr |= HL_UNDERCURL;
8604 				break;
8605 		    case ':':	++p;		    /* highlight group name */
8606 				if (attr || *p == NUL)	 /* no combinations */
8607 				    return FAIL;
8608 				end = vim_strchr(p, ',');
8609 				if (end == NULL)
8610 				    end = p + STRLEN(p);
8611 				id = syn_check_group(p, (int)(end - p));
8612 				if (id == 0)
8613 				    return FAIL;
8614 				attr = syn_id2attr(id);
8615 				p = end - 1;
8616 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8617 				if (hlf == (int)HLF_SNC)
8618 				    id_SNC = syn_get_final_id(id);
8619 				else if (hlf == (int)HLF_S)
8620 				    id_S = syn_get_final_id(id);
8621 #endif
8622 				break;
8623 		    default:	return FAIL;
8624 		}
8625 	    }
8626 	    highlight_attr[hlf] = attr;
8627 
8628 	    p = skip_to_option_part(p);	    /* skip comma and spaces */
8629 	}
8630     }
8631 
8632 #ifdef USER_HIGHLIGHT
8633     /* Setup the user highlights
8634      *
8635      * Temporarily  utilize 10 more hl entries.  Have to be in there
8636      * simultaneously in case of table overflows in get_attr_entry()
8637      */
8638 # ifdef FEAT_STL_OPT
8639     if (ga_grow(&highlight_ga, 10) == FAIL)
8640 	return FAIL;
8641     hlcnt = highlight_ga.ga_len;
8642     if (id_S == 0)
8643     {		    /* Make sure id_S is always valid to simplify code below */
8644 	memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8645 	HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8646 	id_S = hlcnt + 10;
8647     }
8648 # endif
8649     for (i = 0; i < 9; i++)
8650     {
8651 	sprintf((char *)userhl, "User%d", i + 1);
8652 	id = syn_name2id(userhl);
8653 	if (id == 0)
8654 	{
8655 	    highlight_user[i] = 0;
8656 # ifdef FEAT_STL_OPT
8657 	    highlight_stlnc[i] = 0;
8658 # endif
8659 	}
8660 	else
8661 	{
8662 # ifdef FEAT_STL_OPT
8663 	    struct hl_group *hlt = HL_TABLE();
8664 # endif
8665 
8666 	    highlight_user[i] = syn_id2attr(id);
8667 # ifdef FEAT_STL_OPT
8668 	    if (id_SNC == 0)
8669 	    {
8670 		memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8671 		hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8672 		hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8673 #  ifdef FEAT_GUI
8674 		hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8675 #  endif
8676 	    }
8677 	    else
8678 		mch_memmove(&hlt[hlcnt + i],
8679 			    &hlt[id_SNC - 1],
8680 			    sizeof(struct hl_group));
8681 	    hlt[hlcnt + i].sg_link = 0;
8682 
8683 	    /* Apply difference between UserX and HLF_S to HLF_SNC */
8684 	    hlt[hlcnt + i].sg_term ^=
8685 		hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8686 	    if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8687 		hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8688 	    if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8689 		hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8690 	    hlt[hlcnt + i].sg_cterm ^=
8691 		hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8692 	    if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8693 		hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8694 	    if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8695 		hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8696 #  ifdef FEAT_GUI
8697 	    hlt[hlcnt + i].sg_gui ^=
8698 		hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8699 	    if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8700 		hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8701 	    if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8702 		hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
8703 	    if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8704 		hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
8705 	    if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8706 		hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8707 #   ifdef FEAT_XFONTSET
8708 	    if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8709 		hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8710 #   endif
8711 #  endif
8712 	    highlight_ga.ga_len = hlcnt + i + 1;
8713 	    set_hl_attr(hlcnt + i);	/* At long last we can apply */
8714 	    highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8715 # endif
8716 	}
8717     }
8718 # ifdef FEAT_STL_OPT
8719     highlight_ga.ga_len = hlcnt;
8720 # endif
8721 
8722 #endif /* USER_HIGHLIGHT */
8723 
8724     return OK;
8725 }
8726 
8727 #ifdef FEAT_CMDL_COMPL
8728 
8729 static void highlight_list __ARGS((void));
8730 static void highlight_list_two __ARGS((int cnt, int attr));
8731 
8732 /*
8733  * Handle command line completion for :highlight command.
8734  */
8735     void
8736 set_context_in_highlight_cmd(xp, arg)
8737     expand_T	*xp;
8738     char_u	*arg;
8739 {
8740     char_u	*p;
8741 
8742     /* Default: expand group names */
8743     xp->xp_context = EXPAND_HIGHLIGHT;
8744     xp->xp_pattern = arg;
8745     include_link = TRUE;
8746     include_default = TRUE;
8747 
8748     /* (part of) subcommand already typed */
8749     if (*arg != NUL)
8750     {
8751 	p = skiptowhite(arg);
8752 	if (*p != NUL)			/* past "default" or group name */
8753 	{
8754 	    include_default = FALSE;
8755 	    if (STRNCMP("default", arg, p - arg) == 0)
8756 	    {
8757 		arg = skipwhite(p);
8758 		xp->xp_pattern = arg;
8759 		p = skiptowhite(arg);
8760 	    }
8761 	    if (*p != NUL)			/* past group name */
8762 	    {
8763 		include_link = FALSE;
8764 		if (arg[1] == 'i' && arg[0] == 'N')
8765 		    highlight_list();
8766 		if (STRNCMP("link", arg, p - arg) == 0
8767 			|| STRNCMP("clear", arg, p - arg) == 0)
8768 		{
8769 		    xp->xp_pattern = skipwhite(p);
8770 		    p = skiptowhite(xp->xp_pattern);
8771 		    if (*p != NUL)		/* past first group name */
8772 		    {
8773 			xp->xp_pattern = skipwhite(p);
8774 			p = skiptowhite(xp->xp_pattern);
8775 		    }
8776 		}
8777 		if (*p != NUL)			/* past group name(s) */
8778 		    xp->xp_context = EXPAND_NOTHING;
8779 	    }
8780 	}
8781     }
8782 }
8783 
8784 /*
8785  * List highlighting matches in a nice way.
8786  */
8787     static void
8788 highlight_list()
8789 {
8790     int		i;
8791 
8792     for (i = 10; --i >= 0; )
8793 	highlight_list_two(i, hl_attr(HLF_D));
8794     for (i = 40; --i >= 0; )
8795 	highlight_list_two(99, 0);
8796 }
8797 
8798     static void
8799 highlight_list_two(cnt, attr)
8800     int	    cnt;
8801     int	    attr;
8802 {
8803     msg_puts_attr((char_u *)("N \bI \b!  \b" + cnt / 11), attr);
8804     msg_clr_eos();
8805     out_flush();
8806     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8807 }
8808 
8809 #endif /* FEAT_CMDL_COMPL */
8810 
8811 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8812     || defined(FEAT_SIGNS) || defined(PROTO)
8813 /*
8814  * Function given to ExpandGeneric() to obtain the list of group names.
8815  * Also used for synIDattr() function.
8816  */
8817 /*ARGSUSED*/
8818     char_u *
8819 get_highlight_name(xp, idx)
8820     expand_T	*xp;
8821     int		idx;
8822 {
8823     if (idx == highlight_ga.ga_len
8824 #ifdef FEAT_CMDL_COMPL
8825 	    && include_link
8826 #endif
8827 	    )
8828 	return (char_u *)"link";
8829     if (idx == highlight_ga.ga_len + 1
8830 #ifdef FEAT_CMDL_COMPL
8831 	    && include_link
8832 #endif
8833 	    )
8834 	return (char_u *)"clear";
8835     if (idx == highlight_ga.ga_len + 2
8836 #ifdef FEAT_CMDL_COMPL
8837 	    && include_default
8838 #endif
8839 	    )
8840 	return (char_u *)"default";
8841     if (idx < 0 || idx >= highlight_ga.ga_len)
8842 	return NULL;
8843     return HL_TABLE()[idx].sg_name;
8844 }
8845 #endif
8846 
8847 #ifdef FEAT_GUI
8848 /*
8849  * Free all the highlight group fonts.
8850  * Used when quitting for systems which need it.
8851  */
8852     void
8853 free_highlight_fonts()
8854 {
8855     int	    idx;
8856 
8857     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8858     {
8859 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
8860 	HL_TABLE()[idx].sg_font = NOFONT;
8861 # ifdef FEAT_XFONTSET
8862 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8863 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
8864 # endif
8865     }
8866 
8867     gui_mch_free_font(gui.norm_font);
8868 # ifdef FEAT_XFONTSET
8869     gui_mch_free_fontset(gui.fontset);
8870 # endif
8871 # ifndef HAVE_GTK2
8872     gui_mch_free_font(gui.bold_font);
8873     gui_mch_free_font(gui.ital_font);
8874     gui_mch_free_font(gui.boldital_font);
8875 # endif
8876 }
8877 #endif
8878 
8879 /**************************************
8880  *  End of Highlighting stuff	      *
8881  **************************************/
8882