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