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