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