xref: /vim-8.2.3635/src/syntax.c (revision 8ee2d36e)
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_TERMINAL
7395     int		is_terminal_group = FALSE;	/* "Terminal" group */
7396 #endif
7397 #ifdef FEAT_GUI_X11
7398     int		is_menu_group = FALSE;		/* "Menu" group */
7399     int		is_scrollbar_group = FALSE;	/* "Scrollbar" group */
7400     int		is_tooltip_group = FALSE;	/* "Tooltip" group */
7401     int		do_colors = FALSE;		/* need to update colors? */
7402 #else
7403 # define is_menu_group 0
7404 # define is_tooltip_group 0
7405 #endif
7406 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7407     int		did_highlight_changed = FALSE;
7408 #endif
7409 
7410     /*
7411      * If no argument, list current highlighting.
7412      */
7413     if (ends_excmd(*line))
7414     {
7415 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7416 	    /* TODO: only call when the group has attributes set */
7417 	    highlight_list_one((int)i);
7418 	return;
7419     }
7420 
7421     /*
7422      * Isolate the name.
7423      */
7424     name_end = skiptowhite(line);
7425     linep = skipwhite(name_end);
7426 
7427     /*
7428      * Check for "default" argument.
7429      */
7430     if (STRNCMP(line, "default", name_end - line) == 0)
7431     {
7432 	dodefault = TRUE;
7433 	line = linep;
7434 	name_end = skiptowhite(line);
7435 	linep = skipwhite(name_end);
7436     }
7437 
7438     /*
7439      * Check for "clear" or "link" argument.
7440      */
7441     if (STRNCMP(line, "clear", name_end - line) == 0)
7442 	doclear = TRUE;
7443     if (STRNCMP(line, "link", name_end - line) == 0)
7444 	dolink = TRUE;
7445 
7446     /*
7447      * ":highlight {group-name}": list highlighting for one group.
7448      */
7449     if (!doclear && !dolink && ends_excmd(*linep))
7450     {
7451 	id = syn_namen2id(line, (int)(name_end - line));
7452 	if (id == 0)
7453 	    EMSG2(_("E411: highlight group not found: %s"), line);
7454 	else
7455 	    highlight_list_one(id);
7456 	return;
7457     }
7458 
7459     /*
7460      * Handle ":highlight link {from} {to}" command.
7461      */
7462     if (dolink)
7463     {
7464 	char_u	    *from_start = linep;
7465 	char_u	    *from_end;
7466 	char_u	    *to_start;
7467 	char_u	    *to_end;
7468 	int	    from_id;
7469 	int	    to_id;
7470 
7471 	from_end = skiptowhite(from_start);
7472 	to_start = skipwhite(from_end);
7473 	to_end	 = skiptowhite(to_start);
7474 
7475 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
7476 	{
7477 	    EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7478 								  from_start);
7479 	    return;
7480 	}
7481 
7482 	if (!ends_excmd(*skipwhite(to_end)))
7483 	{
7484 	    EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7485 	    return;
7486 	}
7487 
7488 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
7489 	if (STRNCMP(to_start, "NONE", 4) == 0)
7490 	    to_id = 0;
7491 	else
7492 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
7493 
7494 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7495 	{
7496 	    /*
7497 	     * Don't allow a link when there already is some highlighting
7498 	     * for the group, unless '!' is used
7499 	     */
7500 	    if (to_id > 0 && !forceit && !init
7501 				   && hl_has_settings(from_id - 1, dodefault))
7502 	    {
7503 		if (sourcing_name == NULL && !dodefault)
7504 		    EMSG(_("E414: group has settings, highlight link ignored"));
7505 	    }
7506 	    else if (HL_TABLE()[from_id - 1].sg_link != to_id
7507 #ifdef FEAT_EVAL
7508 		    || HL_TABLE()[from_id - 1].sg_scriptID != current_SID
7509 #endif
7510 		    || HL_TABLE()[from_id - 1].sg_cleared)
7511 	    {
7512 		if (!init)
7513 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7514 		HL_TABLE()[from_id - 1].sg_link = to_id;
7515 #ifdef FEAT_EVAL
7516 		HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7517 #endif
7518 		HL_TABLE()[from_id - 1].sg_cleared = FALSE;
7519 		redraw_all_later(SOME_VALID);
7520 
7521 		/* Only call highlight_changed() once after multiple changes. */
7522 		need_highlight_changed = TRUE;
7523 	    }
7524 	}
7525 
7526 	return;
7527     }
7528 
7529     if (doclear)
7530     {
7531 	/*
7532 	 * ":highlight clear [group]" command.
7533 	 */
7534 	line = linep;
7535 	if (ends_excmd(*line))
7536 	{
7537 #ifdef FEAT_GUI
7538 	    /* First, we do not destroy the old values, but allocate the new
7539 	     * ones and update the display. THEN we destroy the old values.
7540 	     * If we destroy the old values first, then the old values
7541 	     * (such as GuiFont's or GuiFontset's) will still be displayed but
7542 	     * invalid because they were free'd.
7543 	     */
7544 	    if (gui.in_use)
7545 	    {
7546 # ifdef FEAT_BEVAL_TIP
7547 		gui_init_tooltip_font();
7548 # endif
7549 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7550 		gui_init_menu_font();
7551 # endif
7552 	    }
7553 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7554 	    gui_mch_def_colors();
7555 # endif
7556 # ifdef FEAT_GUI_X11
7557 #  ifdef FEAT_MENU
7558 
7559 	    /* This only needs to be done when there is no Menu highlight
7560 	     * group defined by default, which IS currently the case.
7561 	     */
7562 	    gui_mch_new_menu_colors();
7563 #  endif
7564 	    if (gui.in_use)
7565 	    {
7566 		gui_new_scrollbar_colors();
7567 #  ifdef FEAT_BEVAL_GUI
7568 		gui_mch_new_tooltip_colors();
7569 #  endif
7570 #  ifdef FEAT_MENU
7571 		gui_mch_new_menu_font();
7572 #  endif
7573 	    }
7574 # endif
7575 
7576 	    /* Ok, we're done allocating the new default graphics items.
7577 	     * The screen should already be refreshed at this point.
7578 	     * It is now Ok to clear out the old data.
7579 	     */
7580 #endif
7581 #ifdef FEAT_EVAL
7582 	    do_unlet((char_u *)"colors_name", TRUE);
7583 #endif
7584 	    restore_cterm_colors();
7585 
7586 	    /*
7587 	     * Clear all default highlight groups and load the defaults.
7588 	     */
7589 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7590 		highlight_clear(idx);
7591 	    init_highlight(TRUE, TRUE);
7592 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7593 	    if (USE_24BIT)
7594 		highlight_gui_started();
7595 	    else
7596 #endif
7597 		highlight_changed();
7598 	    redraw_later_clear();
7599 	    return;
7600 	}
7601 	name_end = skiptowhite(line);
7602 	linep = skipwhite(name_end);
7603     }
7604 
7605     /*
7606      * Find the group name in the table.  If it does not exist yet, add it.
7607      */
7608     id = syn_check_group(line, (int)(name_end - line));
7609     if (id == 0)			/* failed (out of memory) */
7610 	return;
7611     idx = id - 1;			/* index is ID minus one */
7612 
7613     /* Return if "default" was used and the group already has settings. */
7614     if (dodefault && hl_has_settings(idx, TRUE))
7615 	return;
7616 
7617     /* Make a copy so we can check if any attribute actually changed. */
7618     item_before = HL_TABLE()[idx];
7619 
7620     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7621 	is_normal_group = TRUE;
7622 #ifdef FEAT_TERMINAL
7623     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7624 	is_terminal_group = TRUE;
7625 #endif
7626 #ifdef FEAT_GUI_X11
7627     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7628 	is_menu_group = TRUE;
7629     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7630 	is_scrollbar_group = TRUE;
7631     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7632 	is_tooltip_group = TRUE;
7633 #endif
7634 
7635     /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7636     if (doclear || (forceit && init))
7637     {
7638 	highlight_clear(idx);
7639 	if (!doclear)
7640 	    HL_TABLE()[idx].sg_set = 0;
7641     }
7642 
7643     if (!doclear)
7644       while (!ends_excmd(*linep))
7645       {
7646 	key_start = linep;
7647 	if (*linep == '=')
7648 	{
7649 	    EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7650 	    error = TRUE;
7651 	    break;
7652 	}
7653 
7654 	/*
7655 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7656 	 * "guibg").
7657 	 */
7658 	while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
7659 	    ++linep;
7660 	vim_free(key);
7661 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
7662 	if (key == NULL)
7663 	{
7664 	    error = TRUE;
7665 	    break;
7666 	}
7667 	linep = skipwhite(linep);
7668 
7669 	if (STRCMP(key, "NONE") == 0)
7670 	{
7671 	    if (!init || HL_TABLE()[idx].sg_set == 0)
7672 	    {
7673 		if (!init)
7674 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7675 		highlight_clear(idx);
7676 	    }
7677 	    continue;
7678 	}
7679 
7680 	/*
7681 	 * Check for the equal sign.
7682 	 */
7683 	if (*linep != '=')
7684 	{
7685 	    EMSG2(_("E416: missing equal sign: %s"), key_start);
7686 	    error = TRUE;
7687 	    break;
7688 	}
7689 	++linep;
7690 
7691 	/*
7692 	 * Isolate the argument.
7693 	 */
7694 	linep = skipwhite(linep);
7695 	if (*linep == '\'')		/* guifg='color name' */
7696 	{
7697 	    arg_start = ++linep;
7698 	    linep = vim_strchr(linep, '\'');
7699 	    if (linep == NULL)
7700 	    {
7701 		EMSG2(_(e_invarg2), key_start);
7702 		error = TRUE;
7703 		break;
7704 	    }
7705 	}
7706 	else
7707 	{
7708 	    arg_start = linep;
7709 	    linep = skiptowhite(linep);
7710 	}
7711 	if (linep == arg_start)
7712 	{
7713 	    EMSG2(_("E417: missing argument: %s"), key_start);
7714 	    error = TRUE;
7715 	    break;
7716 	}
7717 	vim_free(arg);
7718 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7719 	if (arg == NULL)
7720 	{
7721 	    error = TRUE;
7722 	    break;
7723 	}
7724 	if (*linep == '\'')
7725 	    ++linep;
7726 
7727 	/*
7728 	 * Store the argument.
7729 	 */
7730 	if (  STRCMP(key, "TERM") == 0
7731 		|| STRCMP(key, "CTERM") == 0
7732 		|| STRCMP(key, "GUI") == 0)
7733 	{
7734 	    attr = 0;
7735 	    off = 0;
7736 	    while (arg[off] != NUL)
7737 	    {
7738 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7739 		{
7740 		    len = (int)STRLEN(hl_name_table[i]);
7741 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7742 		    {
7743 			attr |= hl_attr_table[i];
7744 			off += len;
7745 			break;
7746 		    }
7747 		}
7748 		if (i < 0)
7749 		{
7750 		    EMSG2(_("E418: Illegal value: %s"), arg);
7751 		    error = TRUE;
7752 		    break;
7753 		}
7754 		if (arg[off] == ',')		/* another one follows */
7755 		    ++off;
7756 	    }
7757 	    if (error)
7758 		break;
7759 	    if (*key == 'T')
7760 	    {
7761 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7762 		{
7763 		    if (!init)
7764 			HL_TABLE()[idx].sg_set |= SG_TERM;
7765 		    HL_TABLE()[idx].sg_term = attr;
7766 		}
7767 	    }
7768 	    else if (*key == 'C')
7769 	    {
7770 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7771 		{
7772 		    if (!init)
7773 			HL_TABLE()[idx].sg_set |= SG_CTERM;
7774 		    HL_TABLE()[idx].sg_cterm = attr;
7775 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
7776 		}
7777 	    }
7778 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
7779 	    else
7780 	    {
7781 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7782 		{
7783 		    if (!init)
7784 			HL_TABLE()[idx].sg_set |= SG_GUI;
7785 		    HL_TABLE()[idx].sg_gui = attr;
7786 		}
7787 	    }
7788 #endif
7789 	}
7790 	else if (STRCMP(key, "FONT") == 0)
7791 	{
7792 	    /* in non-GUI fonts are simply ignored */
7793 #ifdef FEAT_GUI
7794 	    if (HL_TABLE()[idx].sg_font_name != NULL
7795 			     && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
7796 	    {
7797 		/* Font name didn't change, ignore. */
7798 	    }
7799 	    else if (!gui.shell_created)
7800 	    {
7801 		/* GUI not started yet, always accept the name. */
7802 		vim_free(HL_TABLE()[idx].sg_font_name);
7803 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7804 		did_change = TRUE;
7805 	    }
7806 	    else
7807 	    {
7808 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7809 # ifdef FEAT_XFONTSET
7810 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7811 # endif
7812 		/* First, save the current font/fontset.
7813 		 * Then try to allocate the font/fontset.
7814 		 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7815 		 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7816 		 */
7817 
7818 		HL_TABLE()[idx].sg_font = NOFONT;
7819 # ifdef FEAT_XFONTSET
7820 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
7821 # endif
7822 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
7823 						     is_tooltip_group, FALSE);
7824 
7825 # ifdef FEAT_XFONTSET
7826 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7827 		{
7828 		    /* New fontset was accepted. Free the old one, if there
7829 		     * was one. */
7830 		    gui_mch_free_fontset(temp_sg_fontset);
7831 		    vim_free(HL_TABLE()[idx].sg_font_name);
7832 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7833 		    did_change = TRUE;
7834 		}
7835 		else
7836 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7837 # endif
7838 		if (HL_TABLE()[idx].sg_font != NOFONT)
7839 		{
7840 		    /* New font was accepted. Free the old one, if there was
7841 		     * one. */
7842 		    gui_mch_free_font(temp_sg_font);
7843 		    vim_free(HL_TABLE()[idx].sg_font_name);
7844 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7845 		    did_change = TRUE;
7846 		}
7847 		else
7848 		    HL_TABLE()[idx].sg_font = temp_sg_font;
7849 	    }
7850 #endif
7851 	}
7852 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7853 	{
7854 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7855 	  {
7856 	    if (!init)
7857 		HL_TABLE()[idx].sg_set |= SG_CTERM;
7858 
7859 	    /* When setting the foreground color, and previously the "bold"
7860 	     * flag was set for a light color, reset it now */
7861 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7862 	    {
7863 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7864 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
7865 	    }
7866 
7867 	    if (VIM_ISDIGIT(*arg))
7868 		color = atoi((char *)arg);
7869 	    else if (STRICMP(arg, "fg") == 0)
7870 	    {
7871 		if (cterm_normal_fg_color)
7872 		    color = cterm_normal_fg_color - 1;
7873 		else
7874 		{
7875 		    EMSG(_("E419: FG color unknown"));
7876 		    error = TRUE;
7877 		    break;
7878 		}
7879 	    }
7880 	    else if (STRICMP(arg, "bg") == 0)
7881 	    {
7882 		if (cterm_normal_bg_color > 0)
7883 		    color = cterm_normal_bg_color - 1;
7884 		else
7885 		{
7886 		    EMSG(_("E420: BG color unknown"));
7887 		    error = TRUE;
7888 		    break;
7889 		}
7890 	    }
7891 	    else
7892 	    {
7893 		int bold = MAYBE;
7894 
7895 #if defined(__QNXNTO__)
7896 		static int *color_numbers_8_qansi = color_numbers_8;
7897 		/* On qnx, the 8 & 16 color arrays are the same */
7898 		if (STRNCMP(T_NAME, "qansi", 5) == 0)
7899 		    color_numbers_8_qansi = color_numbers_16;
7900 #endif
7901 
7902 		/* reduce calls to STRICMP a bit, it can be slow */
7903 		off = TOUPPER_ASC(*arg);
7904 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7905 		    if (off == color_names[i][0]
7906 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7907 			break;
7908 		if (i < 0)
7909 		{
7910 		    EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7911 		    error = TRUE;
7912 		    break;
7913 		}
7914 
7915 		color = lookup_color(i, key[5] == 'F', &bold);
7916 
7917 		/* set/reset bold attribute to get light foreground
7918 		 * colors (on some terminals, e.g. "linux") */
7919 		if (bold == TRUE)
7920 		{
7921 		    HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7922 		    HL_TABLE()[idx].sg_cterm_bold = TRUE;
7923 		}
7924 		else if (bold == FALSE)
7925 		    HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7926 	    }
7927 
7928 	    /* Add one to the argument, to avoid zero.  Zero is used for
7929 	     * "NONE", then "color" is -1. */
7930 	    if (key[5] == 'F')
7931 	    {
7932 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
7933 		if (is_normal_group)
7934 		{
7935 		    cterm_normal_fg_color = color + 1;
7936 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7937 #ifdef FEAT_GUI
7938 		    /* Don't do this if the GUI is used. */
7939 		    if (!gui.in_use && !gui.starting)
7940 #endif
7941 		    {
7942 			must_redraw = CLEAR;
7943 			if (termcap_active && color >= 0)
7944 			    term_fg_color(color);
7945 		    }
7946 		}
7947 	    }
7948 	    else
7949 	    {
7950 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
7951 		if (is_normal_group)
7952 		{
7953 		    cterm_normal_bg_color = color + 1;
7954 #ifdef FEAT_GUI
7955 		    /* Don't mess with 'background' if the GUI is used. */
7956 		    if (!gui.in_use && !gui.starting)
7957 #endif
7958 		    {
7959 			must_redraw = CLEAR;
7960 			if (color >= 0)
7961 			{
7962 			    int dark = -1;
7963 
7964 			    if (termcap_active)
7965 				term_bg_color(color);
7966 			    if (t_colors < 16)
7967 				dark = (color == 0 || color == 4);
7968 			    /* Limit the heuristic to the standard 16 colors */
7969 			    else if (color < 16)
7970 				dark = (color < 7 || color == 8);
7971 			    /* Set the 'background' option if the value is
7972 			     * wrong. */
7973 			    if (dark != -1
7974 				    && dark != (*p_bg == 'd')
7975 				    && !option_was_set((char_u *)"bg"))
7976 			    {
7977 				set_option_value((char_u *)"bg", 0L,
7978 				       (char_u *)(dark ? "dark" : "light"), 0);
7979 				reset_option_was_set((char_u *)"bg");
7980 			    }
7981 			}
7982 		    }
7983 		}
7984 	    }
7985 	  }
7986 	}
7987 	else if (STRCMP(key, "GUIFG") == 0)
7988 	{
7989 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
7990 	    char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7991 
7992 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7993 	    {
7994 		if (!init)
7995 		    HL_TABLE()[idx].sg_set |= SG_GUI;
7996 
7997 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7998 		/* In GUI guifg colors are only used when recognized */
7999 		i = color_name2handle(arg);
8000 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
8001 		{
8002 		    HL_TABLE()[idx].sg_gui_fg = i;
8003 # endif
8004 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
8005 		    {
8006 			vim_free(*namep);
8007 			if (STRCMP(arg, "NONE") != 0)
8008 			    *namep = vim_strsave(arg);
8009 			else
8010 			    *namep = NULL;
8011 			did_change = TRUE;
8012 		    }
8013 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8014 #  ifdef FEAT_GUI_X11
8015 		    if (is_menu_group && gui.menu_fg_pixel != i)
8016 		    {
8017 			gui.menu_fg_pixel = i;
8018 			do_colors = TRUE;
8019 		    }
8020 		    if (is_scrollbar_group && gui.scroll_fg_pixel != i)
8021 		    {
8022 			gui.scroll_fg_pixel = i;
8023 			do_colors = TRUE;
8024 		    }
8025 #   ifdef FEAT_BEVAL_GUI
8026 		    if (is_tooltip_group && gui.tooltip_fg_pixel != i)
8027 		    {
8028 			gui.tooltip_fg_pixel = i;
8029 			do_colors = TRUE;
8030 		    }
8031 #   endif
8032 #  endif
8033 		}
8034 # endif
8035 	    }
8036 #endif
8037 	}
8038 	else if (STRCMP(key, "GUIBG") == 0)
8039 	{
8040 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
8041 	    char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8042 
8043 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
8044 	    {
8045 		if (!init)
8046 		    HL_TABLE()[idx].sg_set |= SG_GUI;
8047 
8048 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8049 		/* In GUI guifg colors are only used when recognized */
8050 		i = color_name2handle(arg);
8051 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
8052 		{
8053 		    HL_TABLE()[idx].sg_gui_bg = i;
8054 # endif
8055 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
8056 		    {
8057 			vim_free(*namep);
8058 			if (STRCMP(arg, "NONE") != 0)
8059 			    *namep = vim_strsave(arg);
8060 			else
8061 			    *namep = NULL;
8062 			did_change = TRUE;
8063 		    }
8064 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8065 #  ifdef FEAT_GUI_X11
8066 		    if (is_menu_group && gui.menu_bg_pixel != i)
8067 		    {
8068 			gui.menu_bg_pixel = i;
8069 			do_colors = TRUE;
8070 		    }
8071 		    if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8072 		    {
8073 			gui.scroll_bg_pixel = i;
8074 			do_colors = TRUE;
8075 		    }
8076 #   ifdef FEAT_BEVAL_GUI
8077 		    if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8078 		    {
8079 			gui.tooltip_bg_pixel = i;
8080 			do_colors = TRUE;
8081 		    }
8082 #   endif
8083 #  endif
8084 		}
8085 # endif
8086 	    }
8087 #endif
8088 	}
8089 	else if (STRCMP(key, "GUISP") == 0)
8090 	{
8091 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
8092 	    char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8093 
8094 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
8095 	    {
8096 		if (!init)
8097 		    HL_TABLE()[idx].sg_set |= SG_GUI;
8098 
8099 # ifdef FEAT_GUI
8100 		i = color_name2handle(arg);
8101 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8102 		{
8103 		    HL_TABLE()[idx].sg_gui_sp = i;
8104 # endif
8105 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
8106 		    {
8107 			vim_free(*namep);
8108 			if (STRCMP(arg, "NONE") != 0)
8109 			    *namep = vim_strsave(arg);
8110 			else
8111 			    *namep = NULL;
8112 			did_change = TRUE;
8113 		    }
8114 # ifdef FEAT_GUI
8115 		}
8116 # endif
8117 	    }
8118 #endif
8119 	}
8120 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8121 	{
8122 	    char_u	buf[100];
8123 	    char_u	*tname;
8124 
8125 	    if (!init)
8126 		HL_TABLE()[idx].sg_set |= SG_TERM;
8127 
8128 	    /*
8129 	     * The "start" and "stop"  arguments can be a literal escape
8130 	     * sequence, or a comma separated list of terminal codes.
8131 	     */
8132 	    if (STRNCMP(arg, "t_", 2) == 0)
8133 	    {
8134 		off = 0;
8135 		buf[0] = 0;
8136 		while (arg[off] != NUL)
8137 		{
8138 		    /* Isolate one termcap name */
8139 		    for (len = 0; arg[off + len] &&
8140 						 arg[off + len] != ','; ++len)
8141 			;
8142 		    tname = vim_strnsave(arg + off, len);
8143 		    if (tname == NULL)		/* out of memory */
8144 		    {
8145 			error = TRUE;
8146 			break;
8147 		    }
8148 		    /* lookup the escape sequence for the item */
8149 		    p = get_term_code(tname);
8150 		    vim_free(tname);
8151 		    if (p == NULL)	    /* ignore non-existing things */
8152 			p = (char_u *)"";
8153 
8154 		    /* Append it to the already found stuff */
8155 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8156 		    {
8157 			EMSG2(_("E422: terminal code too long: %s"), arg);
8158 			error = TRUE;
8159 			break;
8160 		    }
8161 		    STRCAT(buf, p);
8162 
8163 		    /* Advance to the next item */
8164 		    off += len;
8165 		    if (arg[off] == ',')	    /* another one follows */
8166 			++off;
8167 		}
8168 	    }
8169 	    else
8170 	    {
8171 		/*
8172 		 * Copy characters from arg[] to buf[], translating <> codes.
8173 		 */
8174 		for (p = arg, off = 0; off < 100 - 6 && *p; )
8175 		{
8176 		    len = trans_special(&p, buf + off, FALSE, FALSE);
8177 		    if (len > 0)	    /* recognized special char */
8178 			off += len;
8179 		    else		    /* copy as normal char */
8180 			buf[off++] = *p++;
8181 		}
8182 		buf[off] = NUL;
8183 	    }
8184 	    if (error)
8185 		break;
8186 
8187 	    if (STRCMP(buf, "NONE") == 0)	/* resetting the value */
8188 		p = NULL;
8189 	    else
8190 		p = vim_strsave(buf);
8191 	    if (key[2] == 'A')
8192 	    {
8193 		vim_free(HL_TABLE()[idx].sg_start);
8194 		HL_TABLE()[idx].sg_start = p;
8195 	    }
8196 	    else
8197 	    {
8198 		vim_free(HL_TABLE()[idx].sg_stop);
8199 		HL_TABLE()[idx].sg_stop = p;
8200 	    }
8201 	}
8202 	else
8203 	{
8204 	    EMSG2(_("E423: Illegal argument: %s"), key_start);
8205 	    error = TRUE;
8206 	    break;
8207 	}
8208 	HL_TABLE()[idx].sg_cleared = FALSE;
8209 
8210 	/*
8211 	 * When highlighting has been given for a group, don't link it.
8212 	 */
8213 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8214 	    HL_TABLE()[idx].sg_link = 0;
8215 
8216 	/*
8217 	 * Continue with next argument.
8218 	 */
8219 	linep = skipwhite(linep);
8220       }
8221 
8222     /*
8223      * If there is an error, and it's a new entry, remove it from the table.
8224      */
8225     if (error && idx == highlight_ga.ga_len)
8226 	syn_unadd_group();
8227     else
8228     {
8229 	if (is_normal_group)
8230 	{
8231 	    HL_TABLE()[idx].sg_term_attr = 0;
8232 	    HL_TABLE()[idx].sg_cterm_attr = 0;
8233 #ifdef FEAT_GUI
8234 	    HL_TABLE()[idx].sg_gui_attr = 0;
8235 	    /*
8236 	     * Need to update all groups, because they might be using "bg"
8237 	     * and/or "fg", which have been changed now.
8238 	     */
8239 #endif
8240 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8241 	    if (USE_24BIT)
8242 	    {
8243 		highlight_gui_started();
8244 		did_highlight_changed = TRUE;
8245 		redraw_all_later(NOT_VALID);
8246 	    }
8247 #endif
8248 	}
8249 #ifdef FEAT_TERMINAL
8250 	else if (is_terminal_group)
8251 	    set_terminal_default_colors(
8252 		    HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8253 #endif
8254 #ifdef FEAT_GUI_X11
8255 # ifdef FEAT_MENU
8256 	else if (is_menu_group)
8257 	{
8258 	    if (gui.in_use && do_colors)
8259 		gui_mch_new_menu_colors();
8260 	}
8261 # endif
8262 	else if (is_scrollbar_group)
8263 	{
8264 	    if (gui.in_use && do_colors)
8265 		gui_new_scrollbar_colors();
8266 	}
8267 # ifdef FEAT_BEVAL_GUI
8268 	else if (is_tooltip_group)
8269 	{
8270 	    if (gui.in_use && do_colors)
8271 		gui_mch_new_tooltip_colors();
8272 	}
8273 # endif
8274 #endif
8275 	else
8276 	    set_hl_attr(idx);
8277 #ifdef FEAT_EVAL
8278 	HL_TABLE()[idx].sg_scriptID = current_SID;
8279 #endif
8280     }
8281 
8282     vim_free(key);
8283     vim_free(arg);
8284 
8285     /* Only call highlight_changed() once, after a sequence of highlight
8286      * commands, and only if an attribute actually changed. */
8287     if ((did_change
8288 	   || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
8289 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8290 	    && !did_highlight_changed
8291 #endif
8292        )
8293     {
8294 	/* Do not trigger a redraw when highlighting is changed while
8295 	 * redrawing.  This may happen when evaluating 'statusline' changes the
8296 	 * StatusLine group. */
8297 	if (!updating_screen)
8298 	    redraw_all_later(NOT_VALID);
8299 	need_highlight_changed = TRUE;
8300     }
8301 }
8302 
8303 #if defined(EXITFREE) || defined(PROTO)
8304     void
8305 free_highlight(void)
8306 {
8307     int	    i;
8308 
8309     for (i = 0; i < highlight_ga.ga_len; ++i)
8310     {
8311 	highlight_clear(i);
8312 	vim_free(HL_TABLE()[i].sg_name);
8313 	vim_free(HL_TABLE()[i].sg_name_u);
8314     }
8315     ga_clear(&highlight_ga);
8316 }
8317 #endif
8318 
8319 /*
8320  * Reset the cterm colors to what they were before Vim was started, if
8321  * possible.  Otherwise reset them to zero.
8322  */
8323     void
8324 restore_cterm_colors(void)
8325 {
8326 #if defined(WIN3264) && !defined(FEAT_GUI_W32)
8327     /* Since t_me has been set, this probably means that the user
8328      * wants to use this as default colors.  Need to reset default
8329      * background/foreground colors. */
8330     mch_set_normal_colors();
8331 #else
8332     cterm_normal_fg_color = 0;
8333     cterm_normal_fg_bold = 0;
8334     cterm_normal_bg_color = 0;
8335 # ifdef FEAT_TERMGUICOLORS
8336     cterm_normal_fg_gui_color = INVALCOLOR;
8337     cterm_normal_bg_gui_color = INVALCOLOR;
8338 # endif
8339 #endif
8340 }
8341 
8342 /*
8343  * Return TRUE if highlight group "idx" has any settings.
8344  * When "check_link" is TRUE also check for an existing link.
8345  */
8346     static int
8347 hl_has_settings(int idx, int check_link)
8348 {
8349     return (   HL_TABLE()[idx].sg_term_attr != 0
8350 	    || HL_TABLE()[idx].sg_cterm_attr != 0
8351 	    || HL_TABLE()[idx].sg_cterm_fg != 0
8352 	    || HL_TABLE()[idx].sg_cterm_bg != 0
8353 #ifdef FEAT_GUI
8354 	    || HL_TABLE()[idx].sg_gui_attr != 0
8355 	    || HL_TABLE()[idx].sg_gui_fg_name != NULL
8356 	    || HL_TABLE()[idx].sg_gui_bg_name != NULL
8357 	    || HL_TABLE()[idx].sg_gui_sp_name != NULL
8358 	    || HL_TABLE()[idx].sg_font_name != NULL
8359 #endif
8360 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8361 }
8362 
8363 /*
8364  * Clear highlighting for one group.
8365  */
8366     static void
8367 highlight_clear(int idx)
8368 {
8369     HL_TABLE()[idx].sg_cleared = TRUE;
8370 
8371     HL_TABLE()[idx].sg_term = 0;
8372     vim_free(HL_TABLE()[idx].sg_start);
8373     HL_TABLE()[idx].sg_start = NULL;
8374     vim_free(HL_TABLE()[idx].sg_stop);
8375     HL_TABLE()[idx].sg_stop = NULL;
8376     HL_TABLE()[idx].sg_term_attr = 0;
8377     HL_TABLE()[idx].sg_cterm = 0;
8378     HL_TABLE()[idx].sg_cterm_bold = FALSE;
8379     HL_TABLE()[idx].sg_cterm_fg = 0;
8380     HL_TABLE()[idx].sg_cterm_bg = 0;
8381     HL_TABLE()[idx].sg_cterm_attr = 0;
8382 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
8383     HL_TABLE()[idx].sg_gui = 0;
8384     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8385     HL_TABLE()[idx].sg_gui_fg_name = NULL;
8386     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8387     HL_TABLE()[idx].sg_gui_bg_name = NULL;
8388     vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8389     HL_TABLE()[idx].sg_gui_sp_name = NULL;
8390 #endif
8391 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8392     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8393     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8394 #endif
8395 #ifdef FEAT_GUI
8396     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
8397     gui_mch_free_font(HL_TABLE()[idx].sg_font);
8398     HL_TABLE()[idx].sg_font = NOFONT;
8399 # ifdef FEAT_XFONTSET
8400     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8401     HL_TABLE()[idx].sg_fontset = NOFONTSET;
8402 # endif
8403     vim_free(HL_TABLE()[idx].sg_font_name);
8404     HL_TABLE()[idx].sg_font_name = NULL;
8405     HL_TABLE()[idx].sg_gui_attr = 0;
8406 #endif
8407 #ifdef FEAT_EVAL
8408     /* Clear the script ID only when there is no link, since that is not
8409      * cleared. */
8410     if (HL_TABLE()[idx].sg_link == 0)
8411 	HL_TABLE()[idx].sg_scriptID = 0;
8412 #endif
8413 }
8414 
8415 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8416 /*
8417  * Set the normal foreground and background colors according to the "Normal"
8418  * highlighting group.  For X11 also set "Menu", "Scrollbar", and
8419  * "Tooltip" colors.
8420  */
8421     void
8422 set_normal_colors(void)
8423 {
8424 #ifdef FEAT_GUI
8425 # ifdef FEAT_TERMGUICOLORS
8426     if (gui.in_use)
8427 # endif
8428     {
8429 	if (set_group_colors((char_u *)"Normal",
8430 				 &gui.norm_pixel, &gui.back_pixel,
8431 				 FALSE, TRUE, FALSE))
8432 	{
8433 	    gui_mch_new_colors();
8434 	    must_redraw = CLEAR;
8435 	}
8436 # ifdef FEAT_GUI_X11
8437 	if (set_group_colors((char_u *)"Menu",
8438 			     &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8439 			     TRUE, FALSE, FALSE))
8440 	{
8441 #  ifdef FEAT_MENU
8442 	    gui_mch_new_menu_colors();
8443 #  endif
8444 	    must_redraw = CLEAR;
8445 	}
8446 #  ifdef FEAT_BEVAL_GUI
8447 	if (set_group_colors((char_u *)"Tooltip",
8448 			     &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8449 			     FALSE, FALSE, TRUE))
8450 	{
8451 #   ifdef FEAT_TOOLBAR
8452 	    gui_mch_new_tooltip_colors();
8453 #   endif
8454 	    must_redraw = CLEAR;
8455 	}
8456 #  endif
8457 	if (set_group_colors((char_u *)"Scrollbar",
8458 			&gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8459 			FALSE, FALSE, FALSE))
8460 	{
8461 	    gui_new_scrollbar_colors();
8462 	    must_redraw = CLEAR;
8463 	}
8464 # endif
8465     }
8466 #endif
8467 #ifdef FEAT_TERMGUICOLORS
8468 # ifdef FEAT_GUI
8469     else
8470 # endif
8471     {
8472 	int		idx;
8473 
8474 	idx = syn_name2id((char_u *)"Normal") - 1;
8475 	if (idx >= 0)
8476 	{
8477 	    gui_do_one_color(idx, FALSE, FALSE);
8478 
8479 	    if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8480 	    {
8481 		cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8482 		must_redraw = CLEAR;
8483 	    }
8484 	    if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8485 	    {
8486 		cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8487 		must_redraw = CLEAR;
8488 	    }
8489 	}
8490     }
8491 #endif
8492 }
8493 #endif
8494 
8495 #if defined(FEAT_GUI) || defined(PROTO)
8496 /*
8497  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8498  */
8499     static int
8500 set_group_colors(
8501     char_u	*name,
8502     guicolor_T	*fgp,
8503     guicolor_T	*bgp,
8504     int		do_menu,
8505     int		use_norm,
8506     int		do_tooltip)
8507 {
8508     int		idx;
8509 
8510     idx = syn_name2id(name) - 1;
8511     if (idx >= 0)
8512     {
8513 	gui_do_one_color(idx, do_menu, do_tooltip);
8514 
8515 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8516 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
8517 	else if (use_norm)
8518 	    *fgp = gui.def_norm_pixel;
8519 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8520 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
8521 	else if (use_norm)
8522 	    *bgp = gui.def_back_pixel;
8523 	return TRUE;
8524     }
8525     return FALSE;
8526 }
8527 
8528 /*
8529  * Get the font of the "Normal" group.
8530  * Returns "" when it's not found or not set.
8531  */
8532     char_u *
8533 hl_get_font_name(void)
8534 {
8535     int		id;
8536     char_u	*s;
8537 
8538     id = syn_name2id((char_u *)"Normal");
8539     if (id > 0)
8540     {
8541 	s = HL_TABLE()[id - 1].sg_font_name;
8542 	if (s != NULL)
8543 	    return s;
8544     }
8545     return (char_u *)"";
8546 }
8547 
8548 /*
8549  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
8550  * actually chosen to be used.
8551  */
8552     void
8553 hl_set_font_name(char_u *font_name)
8554 {
8555     int	    id;
8556 
8557     id = syn_name2id((char_u *)"Normal");
8558     if (id > 0)
8559     {
8560 	vim_free(HL_TABLE()[id - 1].sg_font_name);
8561 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8562     }
8563 }
8564 
8565 /*
8566  * Set background color for "Normal" group.  Called by gui_set_bg_color()
8567  * when the color is known.
8568  */
8569     void
8570 hl_set_bg_color_name(
8571     char_u  *name)	    /* must have been allocated */
8572 {
8573     int	    id;
8574 
8575     if (name != NULL)
8576     {
8577 	id = syn_name2id((char_u *)"Normal");
8578 	if (id > 0)
8579 	{
8580 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8581 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
8582 	}
8583     }
8584 }
8585 
8586 /*
8587  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
8588  * when the color is known.
8589  */
8590     void
8591 hl_set_fg_color_name(
8592     char_u  *name)	    /* must have been allocated */
8593 {
8594     int	    id;
8595 
8596     if (name != NULL)
8597     {
8598 	id = syn_name2id((char_u *)"Normal");
8599 	if (id > 0)
8600 	{
8601 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8602 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
8603 	}
8604     }
8605 }
8606 
8607 /*
8608  * Return the handle for a font name.
8609  * Returns NOFONT when failed.
8610  */
8611     static GuiFont
8612 font_name2handle(char_u *name)
8613 {
8614     if (STRCMP(name, "NONE") == 0)
8615 	return NOFONT;
8616 
8617     return gui_mch_get_font(name, TRUE);
8618 }
8619 
8620 # ifdef FEAT_XFONTSET
8621 /*
8622  * Return the handle for a fontset name.
8623  * Returns NOFONTSET when failed.
8624  */
8625     static GuiFontset
8626 fontset_name2handle(char_u *name, int fixed_width)
8627 {
8628     if (STRCMP(name, "NONE") == 0)
8629 	return NOFONTSET;
8630 
8631     return gui_mch_get_fontset(name, TRUE, fixed_width);
8632 }
8633 # endif
8634 
8635 /*
8636  * Get the font or fontset for one highlight group.
8637  */
8638     static void
8639 hl_do_font(
8640     int		idx,
8641     char_u	*arg,
8642     int		do_normal,		/* set normal font */
8643     int		do_menu UNUSED,		/* set menu font */
8644     int		do_tooltip UNUSED,	/* set tooltip font */
8645     int		free_font)		/* free current font/fontset */
8646 {
8647 # ifdef FEAT_XFONTSET
8648     /* If 'guifontset' is not empty, first try using the name as a
8649      * fontset.  If that doesn't work, use it as a font name. */
8650     if (*p_guifontset != NUL
8651 #  ifdef FONTSET_ALWAYS
8652 	|| do_menu
8653 #  endif
8654 #  ifdef FEAT_BEVAL_TIP
8655 	/* In Athena & Motif, the Tooltip highlight group is always a fontset */
8656 	|| do_tooltip
8657 #  endif
8658 	    )
8659     {
8660 	if (free_font)
8661 	    gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8662 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8663 #  ifdef FONTSET_ALWAYS
8664 		|| do_menu
8665 #  endif
8666 #  ifdef FEAT_BEVAL_TIP
8667 		|| do_tooltip
8668 #  endif
8669 		);
8670     }
8671     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8672     {
8673 	/* If it worked and it's the Normal group, use it as the normal
8674 	 * fontset.  Same for the Menu group. */
8675 	if (do_normal)
8676 	    gui_init_font(arg, TRUE);
8677 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8678 	if (do_menu)
8679 	{
8680 #    ifdef FONTSET_ALWAYS
8681 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8682 #    else
8683 	    /* YIKES!  This is a bug waiting to crash the program */
8684 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
8685 #    endif
8686 	    gui_mch_new_menu_font();
8687 	}
8688 #    ifdef FEAT_BEVAL_GUI
8689 	if (do_tooltip)
8690 	{
8691 	    /* The Athena widget set cannot currently handle switching between
8692 	     * displaying a single font and a fontset.
8693 	     * If the XtNinternational resource is set to True at widget
8694 	     * creation, then a fontset is always used, otherwise an
8695 	     * XFontStruct is used.
8696 	     */
8697 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8698 	    gui_mch_new_tooltip_font();
8699 	}
8700 #    endif
8701 #   endif
8702     }
8703     else
8704 # endif
8705     {
8706 	if (free_font)
8707 	    gui_mch_free_font(HL_TABLE()[idx].sg_font);
8708 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
8709 	/* If it worked and it's the Normal group, use it as the
8710 	 * normal font.  Same for the Menu group. */
8711 	if (HL_TABLE()[idx].sg_font != NOFONT)
8712 	{
8713 	    if (do_normal)
8714 		gui_init_font(arg, FALSE);
8715 #ifndef FONTSET_ALWAYS
8716 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8717 	    if (do_menu)
8718 	    {
8719 		gui.menu_font = HL_TABLE()[idx].sg_font;
8720 		gui_mch_new_menu_font();
8721 	    }
8722 # endif
8723 #endif
8724 	}
8725     }
8726 }
8727 
8728 #endif /* FEAT_GUI */
8729 
8730 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8731 /*
8732  * Return the handle for a color name.
8733  * Returns INVALCOLOR when failed.
8734  */
8735     guicolor_T
8736 color_name2handle(char_u *name)
8737 {
8738     if (STRCMP(name, "NONE") == 0)
8739 	return INVALCOLOR;
8740 
8741     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8742     {
8743 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
8744 	if (gui.in_use)
8745 #endif
8746 #ifdef FEAT_GUI
8747 	    return gui.norm_pixel;
8748 #endif
8749 #ifdef FEAT_TERMGUICOLORS
8750 	if (cterm_normal_fg_gui_color != INVALCOLOR)
8751 	    return cterm_normal_fg_gui_color;
8752 	/* Guess that the foreground is black or white. */
8753 	return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
8754 #endif
8755     }
8756     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8757     {
8758 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
8759 	if (gui.in_use)
8760 #endif
8761 #ifdef FEAT_GUI
8762 	    return gui.back_pixel;
8763 #endif
8764 #ifdef FEAT_TERMGUICOLORS
8765 	if (cterm_normal_bg_gui_color != INVALCOLOR)
8766 	    return cterm_normal_bg_gui_color;
8767 	/* Guess that the background is white or black. */
8768 	return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
8769 #endif
8770     }
8771 
8772     return GUI_GET_COLOR(name);
8773 }
8774 #endif
8775 
8776 /*
8777  * Table with the specifications for an attribute number.
8778  * Note that this table is used by ALL buffers.  This is required because the
8779  * GUI can redraw at any time for any buffer.
8780  */
8781 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
8782 
8783 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8784 
8785 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
8786 
8787 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8788 
8789 #ifdef FEAT_GUI
8790 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
8791 
8792 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8793 #endif
8794 
8795 /*
8796  * Return the attr number for a set of colors and font.
8797  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8798  * if the combination is new.
8799  * Return 0 for error (no more room).
8800  */
8801     static int
8802 get_attr_entry(garray_T *table, attrentry_T *aep)
8803 {
8804     int		i;
8805     attrentry_T	*taep;
8806     static int	recursive = FALSE;
8807 
8808     /*
8809      * Init the table, in case it wasn't done yet.
8810      */
8811     table->ga_itemsize = sizeof(attrentry_T);
8812     table->ga_growsize = 7;
8813 
8814     /*
8815      * Try to find an entry with the same specifications.
8816      */
8817     for (i = 0; i < table->ga_len; ++i)
8818     {
8819 	taep = &(((attrentry_T *)table->ga_data)[i]);
8820 	if (	   aep->ae_attr == taep->ae_attr
8821 		&& (
8822 #ifdef FEAT_GUI
8823 		       (table == &gui_attr_table
8824 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8825 			    && aep->ae_u.gui.bg_color
8826 						    == taep->ae_u.gui.bg_color
8827 			    && aep->ae_u.gui.sp_color
8828 						    == taep->ae_u.gui.sp_color
8829 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
8830 #  ifdef FEAT_XFONTSET
8831 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
8832 #  endif
8833 			    ))
8834 		    ||
8835 #endif
8836 		       (table == &term_attr_table
8837 			&& (aep->ae_u.term.start == NULL)
8838 					    == (taep->ae_u.term.start == NULL)
8839 			&& (aep->ae_u.term.start == NULL
8840 			    || STRCMP(aep->ae_u.term.start,
8841 						  taep->ae_u.term.start) == 0)
8842 			&& (aep->ae_u.term.stop == NULL)
8843 					     == (taep->ae_u.term.stop == NULL)
8844 			&& (aep->ae_u.term.stop == NULL
8845 			    || STRCMP(aep->ae_u.term.stop,
8846 						  taep->ae_u.term.stop) == 0))
8847 		    || (table == &cterm_attr_table
8848 			    && aep->ae_u.cterm.fg_color
8849 						  == taep->ae_u.cterm.fg_color
8850 			    && aep->ae_u.cterm.bg_color
8851 						  == taep->ae_u.cterm.bg_color
8852 #ifdef FEAT_TERMGUICOLORS
8853 			    && aep->ae_u.cterm.fg_rgb
8854 						    == taep->ae_u.cterm.fg_rgb
8855 			    && aep->ae_u.cterm.bg_rgb
8856 						    == taep->ae_u.cterm.bg_rgb
8857 #endif
8858 		       )))
8859 
8860 	return i + ATTR_OFF;
8861     }
8862 
8863     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
8864     {
8865 	/*
8866 	 * Running out of attribute entries!  remove all attributes, and
8867 	 * compute new ones for all groups.
8868 	 * When called recursively, we are really out of numbers.
8869 	 */
8870 	if (recursive)
8871 	{
8872 	    EMSG(_("E424: Too many different highlighting attributes in use"));
8873 	    return 0;
8874 	}
8875 	recursive = TRUE;
8876 
8877 	clear_hl_tables();
8878 
8879 	must_redraw = CLEAR;
8880 
8881 	for (i = 0; i < highlight_ga.ga_len; ++i)
8882 	    set_hl_attr(i);
8883 
8884 	recursive = FALSE;
8885     }
8886 
8887     /*
8888      * This is a new combination of colors and font, add an entry.
8889      */
8890     if (ga_grow(table, 1) == FAIL)
8891 	return 0;
8892 
8893     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8894     vim_memset(taep, 0, sizeof(attrentry_T));
8895     taep->ae_attr = aep->ae_attr;
8896 #ifdef FEAT_GUI
8897     if (table == &gui_attr_table)
8898     {
8899 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8900 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8901 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8902 	taep->ae_u.gui.font = aep->ae_u.gui.font;
8903 # ifdef FEAT_XFONTSET
8904 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
8905 # endif
8906     }
8907 #endif
8908     if (table == &term_attr_table)
8909     {
8910 	if (aep->ae_u.term.start == NULL)
8911 	    taep->ae_u.term.start = NULL;
8912 	else
8913 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
8914 	if (aep->ae_u.term.stop == NULL)
8915 	    taep->ae_u.term.stop = NULL;
8916 	else
8917 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
8918     }
8919     else if (table == &cterm_attr_table)
8920     {
8921 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8922 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
8923 #ifdef FEAT_TERMGUICOLORS
8924 	taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8925 	taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8926 #endif
8927     }
8928     ++table->ga_len;
8929     return (table->ga_len - 1 + ATTR_OFF);
8930 }
8931 
8932 /*
8933  * Get an attribute index for a cterm entry.
8934  * Uses an existing entry when possible or adds one when needed.
8935  */
8936     int
8937 get_cterm_attr_idx(int attr, int fg, int bg)
8938 {
8939     attrentry_T		at_en;
8940 
8941     vim_memset(&at_en, 0, sizeof(attrentry_T));
8942     at_en.ae_attr = attr;
8943     at_en.ae_u.cterm.fg_color = fg;
8944     at_en.ae_u.cterm.bg_color = bg;
8945     return get_attr_entry(&cterm_attr_table, &at_en);
8946 }
8947 
8948 #if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8949 /*
8950  * Get an attribute index for a 'termguicolors' entry.
8951  * Uses an existing entry when possible or adds one when needed.
8952  */
8953     int
8954 get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8955 {
8956     attrentry_T		at_en;
8957 
8958     vim_memset(&at_en, 0, sizeof(attrentry_T));
8959     at_en.ae_attr = attr;
8960     at_en.ae_u.cterm.fg_rgb = fg;
8961     at_en.ae_u.cterm.bg_rgb = bg;
8962     return get_attr_entry(&cterm_attr_table, &at_en);
8963 }
8964 #endif
8965 
8966 #if defined(FEAT_GUI) || defined(PROTO)
8967 /*
8968  * Get an attribute index for a cterm entry.
8969  * Uses an existing entry when possible or adds one when needed.
8970  */
8971     int
8972 get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8973 {
8974     attrentry_T		at_en;
8975 
8976     vim_memset(&at_en, 0, sizeof(attrentry_T));
8977     at_en.ae_attr = attr;
8978     at_en.ae_u.gui.fg_color = fg;
8979     at_en.ae_u.gui.bg_color = bg;
8980     return get_attr_entry(&gui_attr_table, &at_en);
8981 }
8982 #endif
8983 
8984 /*
8985  * Clear all highlight tables.
8986  */
8987     void
8988 clear_hl_tables(void)
8989 {
8990     int		i;
8991     attrentry_T	*taep;
8992 
8993 #ifdef FEAT_GUI
8994     ga_clear(&gui_attr_table);
8995 #endif
8996     for (i = 0; i < term_attr_table.ga_len; ++i)
8997     {
8998 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8999 	vim_free(taep->ae_u.term.start);
9000 	vim_free(taep->ae_u.term.stop);
9001     }
9002     ga_clear(&term_attr_table);
9003     ga_clear(&cterm_attr_table);
9004 }
9005 
9006 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
9007 /*
9008  * Combine special attributes (e.g., for spelling) with other attributes
9009  * (e.g., for syntax highlighting).
9010  * "prim_attr" overrules "char_attr".
9011  * This creates a new group when required.
9012  * Since we expect there to be few spelling mistakes we don't cache the
9013  * result.
9014  * Return the resulting attributes.
9015  */
9016     int
9017 hl_combine_attr(int char_attr, int prim_attr)
9018 {
9019     attrentry_T *char_aep = NULL;
9020     attrentry_T *spell_aep;
9021     attrentry_T new_en;
9022 
9023     if (char_attr == 0)
9024 	return prim_attr;
9025     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
9026 	return ATTR_COMBINE(char_attr, prim_attr);
9027 #ifdef FEAT_GUI
9028     if (gui.in_use)
9029     {
9030 	if (char_attr > HL_ALL)
9031 	    char_aep = syn_gui_attr2entry(char_attr);
9032 	if (char_aep != NULL)
9033 	    new_en = *char_aep;
9034 	else
9035 	{
9036 	    vim_memset(&new_en, 0, sizeof(new_en));
9037 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
9038 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
9039 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
9040 	    if (char_attr <= HL_ALL)
9041 		new_en.ae_attr = char_attr;
9042 	}
9043 
9044 	if (prim_attr <= HL_ALL)
9045 	    new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
9046 	else
9047 	{
9048 	    spell_aep = syn_gui_attr2entry(prim_attr);
9049 	    if (spell_aep != NULL)
9050 	    {
9051 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9052 							   spell_aep->ae_attr);
9053 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9054 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9055 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9056 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9057 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9058 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9059 		if (spell_aep->ae_u.gui.font != NOFONT)
9060 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9061 # ifdef FEAT_XFONTSET
9062 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9063 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9064 # endif
9065 	    }
9066 	}
9067 	return get_attr_entry(&gui_attr_table, &new_en);
9068     }
9069 #endif
9070 
9071     if (IS_CTERM)
9072     {
9073 	if (char_attr > HL_ALL)
9074 	    char_aep = syn_cterm_attr2entry(char_attr);
9075 	if (char_aep != NULL)
9076 	    new_en = *char_aep;
9077 	else
9078 	{
9079 	    vim_memset(&new_en, 0, sizeof(new_en));
9080 #ifdef FEAT_TERMGUICOLORS
9081 	    new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9082 	    new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9083 #endif
9084 	    if (char_attr <= HL_ALL)
9085 		new_en.ae_attr = char_attr;
9086 	}
9087 
9088 	if (prim_attr <= HL_ALL)
9089 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
9090 	else
9091 	{
9092 	    spell_aep = syn_cterm_attr2entry(prim_attr);
9093 	    if (spell_aep != NULL)
9094 	    {
9095 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9096 							   spell_aep->ae_attr);
9097 		if (spell_aep->ae_u.cterm.fg_color > 0)
9098 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9099 		if (spell_aep->ae_u.cterm.bg_color > 0)
9100 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
9101 #ifdef FEAT_TERMGUICOLORS
9102 		if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
9103 		    new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
9104 		if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
9105 		    new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9106 #endif
9107 	    }
9108 	}
9109 	return get_attr_entry(&cterm_attr_table, &new_en);
9110     }
9111 
9112     if (char_attr > HL_ALL)
9113 	char_aep = syn_term_attr2entry(char_attr);
9114     if (char_aep != NULL)
9115 	new_en = *char_aep;
9116     else
9117     {
9118 	vim_memset(&new_en, 0, sizeof(new_en));
9119 	if (char_attr <= HL_ALL)
9120 	    new_en.ae_attr = char_attr;
9121     }
9122 
9123     if (prim_attr <= HL_ALL)
9124 	new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
9125     else
9126     {
9127 	spell_aep = syn_term_attr2entry(prim_attr);
9128 	if (spell_aep != NULL)
9129 	{
9130 	    new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
9131 	    if (spell_aep->ae_u.term.start != NULL)
9132 	    {
9133 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9134 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9135 	    }
9136 	}
9137     }
9138     return get_attr_entry(&term_attr_table, &new_en);
9139 }
9140 #endif
9141 
9142 #ifdef FEAT_GUI
9143 
9144     attrentry_T *
9145 syn_gui_attr2entry(int attr)
9146 {
9147     attr -= ATTR_OFF;
9148     if (attr >= gui_attr_table.ga_len)	    /* did ":syntax clear" */
9149 	return NULL;
9150     return &(GUI_ATTR_ENTRY(attr));
9151 }
9152 #endif /* FEAT_GUI */
9153 
9154 /*
9155  * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9156  * Only to be used when "attr" > HL_ALL.
9157  */
9158     int
9159 syn_attr2attr(int attr)
9160 {
9161     attrentry_T	*aep;
9162 
9163 #ifdef FEAT_GUI
9164     if (gui.in_use)
9165 	aep = syn_gui_attr2entry(attr);
9166     else
9167 #endif
9168 	if (IS_CTERM)
9169 	    aep = syn_cterm_attr2entry(attr);
9170 	else
9171 	    aep = syn_term_attr2entry(attr);
9172 
9173     if (aep == NULL)	    /* highlighting not set */
9174 	return 0;
9175     return aep->ae_attr;
9176 }
9177 
9178 
9179     attrentry_T *
9180 syn_term_attr2entry(int attr)
9181 {
9182     attr -= ATTR_OFF;
9183     if (attr >= term_attr_table.ga_len)	    /* did ":syntax clear" */
9184 	return NULL;
9185     return &(TERM_ATTR_ENTRY(attr));
9186 }
9187 
9188     attrentry_T *
9189 syn_cterm_attr2entry(int attr)
9190 {
9191     attr -= ATTR_OFF;
9192     if (attr >= cterm_attr_table.ga_len)	/* did ":syntax clear" */
9193 	return NULL;
9194     return &(CTERM_ATTR_ENTRY(attr));
9195 }
9196 
9197 #define LIST_ATTR   1
9198 #define LIST_STRING 2
9199 #define LIST_INT    3
9200 
9201     static void
9202 highlight_list_one(int id)
9203 {
9204     struct hl_group	*sgp;
9205     int			didh = FALSE;
9206 
9207     sgp = &HL_TABLE()[id - 1];	    /* index is ID minus one */
9208 
9209     didh = highlight_list_arg(id, didh, LIST_ATTR,
9210 				    sgp->sg_term, NULL, "term");
9211     didh = highlight_list_arg(id, didh, LIST_STRING,
9212 				    0, sgp->sg_start, "start");
9213     didh = highlight_list_arg(id, didh, LIST_STRING,
9214 				    0, sgp->sg_stop, "stop");
9215 
9216     didh = highlight_list_arg(id, didh, LIST_ATTR,
9217 				    sgp->sg_cterm, NULL, "cterm");
9218     didh = highlight_list_arg(id, didh, LIST_INT,
9219 				    sgp->sg_cterm_fg, NULL, "ctermfg");
9220     didh = highlight_list_arg(id, didh, LIST_INT,
9221 				    sgp->sg_cterm_bg, NULL, "ctermbg");
9222 
9223 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
9224     didh = highlight_list_arg(id, didh, LIST_ATTR,
9225 				    sgp->sg_gui, NULL, "gui");
9226     didh = highlight_list_arg(id, didh, LIST_STRING,
9227 				    0, sgp->sg_gui_fg_name, "guifg");
9228     didh = highlight_list_arg(id, didh, LIST_STRING,
9229 				    0, sgp->sg_gui_bg_name, "guibg");
9230     didh = highlight_list_arg(id, didh, LIST_STRING,
9231 				    0, sgp->sg_gui_sp_name, "guisp");
9232 #endif
9233 #ifdef FEAT_GUI
9234     didh = highlight_list_arg(id, didh, LIST_STRING,
9235 				    0, sgp->sg_font_name, "font");
9236 #endif
9237 
9238     if (sgp->sg_link && !got_int)
9239     {
9240 	(void)syn_list_header(didh, 9999, id);
9241 	didh = TRUE;
9242 	msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
9243 	msg_putchar(' ');
9244 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9245     }
9246 
9247     if (!didh)
9248 	highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
9249 #ifdef FEAT_EVAL
9250     if (p_verbose > 0)
9251 	last_set_msg(sgp->sg_scriptID);
9252 #endif
9253 }
9254 
9255     static int
9256 highlight_list_arg(
9257     int		id,
9258     int		didh,
9259     int		type,
9260     int		iarg,
9261     char_u	*sarg,
9262     char	*name)
9263 {
9264     char_u	buf[100];
9265     char_u	*ts;
9266     int		i;
9267 
9268     if (got_int)
9269 	return FALSE;
9270     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9271     {
9272 	ts = buf;
9273 	if (type == LIST_INT)
9274 	    sprintf((char *)buf, "%d", iarg - 1);
9275 	else if (type == LIST_STRING)
9276 	    ts = sarg;
9277 	else /* type == LIST_ATTR */
9278 	{
9279 	    buf[0] = NUL;
9280 	    for (i = 0; hl_attr_table[i] != 0; ++i)
9281 	    {
9282 		if (iarg & hl_attr_table[i])
9283 		{
9284 		    if (buf[0] != NUL)
9285 			vim_strcat(buf, (char_u *)",", 100);
9286 		    vim_strcat(buf, (char_u *)hl_name_table[i], 100);
9287 		    iarg &= ~hl_attr_table[i];	    /* don't want "inverse" */
9288 		}
9289 	    }
9290 	}
9291 
9292 	(void)syn_list_header(didh,
9293 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9294 	didh = TRUE;
9295 	if (!got_int)
9296 	{
9297 	    if (*name != NUL)
9298 	    {
9299 		MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9300 		MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
9301 	    }
9302 	    msg_outtrans(ts);
9303 	}
9304     }
9305     return didh;
9306 }
9307 
9308 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9309 /*
9310  * Return "1" if highlight group "id" has attribute "flag".
9311  * Return NULL otherwise.
9312  */
9313     char_u *
9314 highlight_has_attr(
9315     int		id,
9316     int		flag,
9317     int		modec)	/* 'g' for GUI, 'c' for cterm, 't' for term */
9318 {
9319     int		attr;
9320 
9321     if (id <= 0 || id > highlight_ga.ga_len)
9322 	return NULL;
9323 
9324 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
9325     if (modec == 'g')
9326 	attr = HL_TABLE()[id - 1].sg_gui;
9327     else
9328 #endif
9329 	 if (modec == 'c')
9330 	attr = HL_TABLE()[id - 1].sg_cterm;
9331     else
9332 	attr = HL_TABLE()[id - 1].sg_term;
9333 
9334     if (attr & flag)
9335 	return (char_u *)"1";
9336     return NULL;
9337 }
9338 #endif
9339 
9340 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9341 /*
9342  * Return color name of highlight group "id".
9343  */
9344     char_u *
9345 highlight_color(
9346     int		id,
9347     char_u	*what,	/* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9348     int		modec)	/* 'g' for GUI, 'c' for cterm, 't' for term */
9349 {
9350     static char_u	name[20];
9351     int			n;
9352     int			fg = FALSE;
9353     int			sp = FALSE;
9354     int			font = FALSE;
9355 
9356     if (id <= 0 || id > highlight_ga.ga_len)
9357 	return NULL;
9358 
9359     if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
9360 	fg = TRUE;
9361     else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
9362 	     && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
9363 	font = TRUE;
9364     else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
9365 	sp = TRUE;
9366     else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9367 	return NULL;
9368     if (modec == 'g')
9369     {
9370 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9371 #  ifdef FEAT_GUI
9372 	/* return font name */
9373 	if (font)
9374 	    return HL_TABLE()[id - 1].sg_font_name;
9375 #  endif
9376 
9377 	/* return #RRGGBB form (only possible when GUI is running) */
9378 	if ((USE_24BIT) && what[2] == '#')
9379 	{
9380 	    guicolor_T		color;
9381 	    long_u		rgb;
9382 	    static char_u	buf[10];
9383 
9384 	    if (fg)
9385 		color = HL_TABLE()[id - 1].sg_gui_fg;
9386 	    else if (sp)
9387 #  ifdef FEAT_GUI
9388 		color = HL_TABLE()[id - 1].sg_gui_sp;
9389 #  else
9390 		color = INVALCOLOR;
9391 #  endif
9392 	    else
9393 		color = HL_TABLE()[id - 1].sg_gui_bg;
9394 	    if (color == INVALCOLOR)
9395 		return NULL;
9396 	    rgb = (long_u)GUI_MCH_GET_RGB(color);
9397 	    sprintf((char *)buf, "#%02x%02x%02x",
9398 				      (unsigned)(rgb >> 16),
9399 				      (unsigned)(rgb >> 8) & 255,
9400 				      (unsigned)rgb & 255);
9401 	    return buf;
9402 	}
9403 # endif
9404 	if (fg)
9405 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
9406 	if (sp)
9407 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
9408 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
9409     }
9410     if (font || sp)
9411 	return NULL;
9412     if (modec == 'c')
9413     {
9414 	if (fg)
9415 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9416 	else
9417 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
9418 	if (n < 0)
9419 	    return NULL;
9420 	sprintf((char *)name, "%d", n);
9421 	return name;
9422     }
9423     /* term doesn't have color */
9424     return NULL;
9425 }
9426 #endif
9427 
9428 #if (defined(FEAT_SYN_HL) \
9429 	    && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
9430 	&& defined(FEAT_PRINTER)) || defined(PROTO)
9431 /*
9432  * Return color name of highlight group "id" as RGB value.
9433  */
9434     long_u
9435 highlight_gui_color_rgb(
9436     int		id,
9437     int		fg)	/* TRUE = fg, FALSE = bg */
9438 {
9439     guicolor_T	color;
9440 
9441     if (id <= 0 || id > highlight_ga.ga_len)
9442 	return 0L;
9443 
9444     if (fg)
9445 	color = HL_TABLE()[id - 1].sg_gui_fg;
9446     else
9447 	color = HL_TABLE()[id - 1].sg_gui_bg;
9448 
9449     if (color == INVALCOLOR)
9450 	return 0L;
9451 
9452     return GUI_MCH_GET_RGB(color);
9453 }
9454 #endif
9455 
9456 /*
9457  * Output the syntax list header.
9458  * Return TRUE when started a new line.
9459  */
9460     static int
9461 syn_list_header(
9462     int	    did_header,		/* did header already */
9463     int	    outlen,		/* length of string that comes */
9464     int	    id)			/* highlight group id */
9465 {
9466     int	    endcol = 19;
9467     int	    newline = TRUE;
9468 
9469     if (!did_header)
9470     {
9471 	msg_putchar('\n');
9472 	if (got_int)
9473 	    return TRUE;
9474 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
9475 	endcol = 15;
9476     }
9477     else if (msg_col + outlen + 1 >= Columns)
9478     {
9479 	msg_putchar('\n');
9480 	if (got_int)
9481 	    return TRUE;
9482     }
9483     else
9484     {
9485 	if (msg_col >= endcol)	/* wrap around is like starting a new line */
9486 	    newline = FALSE;
9487     }
9488 
9489     if (msg_col >= endcol)	/* output at least one space */
9490 	endcol = msg_col + 1;
9491     if (Columns <= endcol)	/* avoid hang for tiny window */
9492 	endcol = Columns - 1;
9493 
9494     msg_advance(endcol);
9495 
9496     /* Show "xxx" with the attributes. */
9497     if (!did_header)
9498     {
9499 	msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9500 	msg_putchar(' ');
9501     }
9502 
9503     return newline;
9504 }
9505 
9506 /*
9507  * Set the attribute numbers for a highlight group.
9508  * Called after one of the attributes has changed.
9509  */
9510     static void
9511 set_hl_attr(
9512     int		idx)	    /* index in array */
9513 {
9514     attrentry_T		at_en;
9515     struct hl_group	*sgp = HL_TABLE() + idx;
9516 
9517     /* The "Normal" group doesn't need an attribute number */
9518     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9519 	return;
9520 
9521 #ifdef FEAT_GUI
9522     /*
9523      * For the GUI mode: If there are other than "normal" highlighting
9524      * attributes, need to allocate an attr number.
9525      */
9526     if (sgp->sg_gui_fg == INVALCOLOR
9527 	    && sgp->sg_gui_bg == INVALCOLOR
9528 	    && sgp->sg_gui_sp == INVALCOLOR
9529 	    && sgp->sg_font == NOFONT
9530 # ifdef FEAT_XFONTSET
9531 	    && sgp->sg_fontset == NOFONTSET
9532 # endif
9533 	    )
9534     {
9535 	sgp->sg_gui_attr = sgp->sg_gui;
9536     }
9537     else
9538     {
9539 	at_en.ae_attr = sgp->sg_gui;
9540 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9541 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
9542 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
9543 	at_en.ae_u.gui.font = sgp->sg_font;
9544 # ifdef FEAT_XFONTSET
9545 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
9546 # endif
9547 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9548     }
9549 #endif
9550     /*
9551      * For the term mode: If there are other than "normal" highlighting
9552      * attributes, need to allocate an attr number.
9553      */
9554     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9555 	sgp->sg_term_attr = sgp->sg_term;
9556     else
9557     {
9558 	at_en.ae_attr = sgp->sg_term;
9559 	at_en.ae_u.term.start = sgp->sg_start;
9560 	at_en.ae_u.term.stop = sgp->sg_stop;
9561 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9562     }
9563 
9564     /*
9565      * For the color term mode: If there are other than "normal"
9566      * highlighting attributes, need to allocate an attr number.
9567      */
9568     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
9569 # ifdef FEAT_TERMGUICOLORS
9570 	    && sgp->sg_gui_fg == INVALCOLOR
9571 	    && sgp->sg_gui_bg == INVALCOLOR
9572 # endif
9573 	    )
9574 	sgp->sg_cterm_attr = sgp->sg_cterm;
9575     else
9576     {
9577 	at_en.ae_attr = sgp->sg_cterm;
9578 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9579 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9580 # ifdef FEAT_TERMGUICOLORS
9581 	at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9582 	at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
9583 # endif
9584 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9585     }
9586 }
9587 
9588 /*
9589  * Lookup a highlight group name and return its ID.
9590  * If it is not found, 0 is returned.
9591  */
9592     int
9593 syn_name2id(char_u *name)
9594 {
9595     int		i;
9596     char_u	name_u[200];
9597 
9598     /* Avoid using stricmp() too much, it's slow on some systems */
9599     /* Avoid alloc()/free(), these are slow too.  ID names over 200 chars
9600      * don't deserve to be found! */
9601     vim_strncpy(name_u, name, 199);
9602     vim_strup(name_u);
9603     for (i = highlight_ga.ga_len; --i >= 0; )
9604 	if (HL_TABLE()[i].sg_name_u != NULL
9605 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9606 	    break;
9607     return i + 1;
9608 }
9609 
9610 /*
9611  * Lookup a highlight group name and return its attributes.
9612  * Return zero if not found.
9613  */
9614     int
9615 syn_name2attr(char_u *name)
9616 {
9617     int id = syn_name2id(name);
9618 
9619     if (id != 0)
9620 	return syn_id2attr(id);
9621     return 0;
9622 }
9623 
9624 #if defined(FEAT_EVAL) || defined(PROTO)
9625 /*
9626  * Return TRUE if highlight group "name" exists.
9627  */
9628     int
9629 highlight_exists(char_u *name)
9630 {
9631     return (syn_name2id(name) > 0);
9632 }
9633 
9634 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9635 /*
9636  * Return the name of highlight group "id".
9637  * When not a valid ID return an empty string.
9638  */
9639     char_u *
9640 syn_id2name(int id)
9641 {
9642     if (id <= 0 || id > highlight_ga.ga_len)
9643 	return (char_u *)"";
9644     return HL_TABLE()[id - 1].sg_name;
9645 }
9646 # endif
9647 #endif
9648 
9649 /*
9650  * Like syn_name2id(), but take a pointer + length argument.
9651  */
9652     int
9653 syn_namen2id(char_u *linep, int len)
9654 {
9655     char_u  *name;
9656     int	    id = 0;
9657 
9658     name = vim_strnsave(linep, len);
9659     if (name != NULL)
9660     {
9661 	id = syn_name2id(name);
9662 	vim_free(name);
9663     }
9664     return id;
9665 }
9666 
9667 /*
9668  * Find highlight group name in the table and return its ID.
9669  * The argument is a pointer to the name and the length of the name.
9670  * If it doesn't exist yet, a new entry is created.
9671  * Return 0 for failure.
9672  */
9673     int
9674 syn_check_group(char_u *pp, int len)
9675 {
9676     int	    id;
9677     char_u  *name;
9678 
9679     name = vim_strnsave(pp, len);
9680     if (name == NULL)
9681 	return 0;
9682 
9683     id = syn_name2id(name);
9684     if (id == 0)			/* doesn't exist yet */
9685 	id = syn_add_group(name);
9686     else
9687 	vim_free(name);
9688     return id;
9689 }
9690 
9691 /*
9692  * Add new highlight group and return its ID.
9693  * "name" must be an allocated string, it will be consumed.
9694  * Return 0 for failure.
9695  */
9696     static int
9697 syn_add_group(char_u *name)
9698 {
9699     char_u	*p;
9700 
9701     /* Check that the name is ASCII letters, digits and underscore. */
9702     for (p = name; *p != NUL; ++p)
9703     {
9704 	if (!vim_isprintc(*p))
9705 	{
9706 	    EMSG(_("E669: Unprintable character in group name"));
9707 	    vim_free(name);
9708 	    return 0;
9709 	}
9710 	else if (!ASCII_ISALNUM(*p) && *p != '_')
9711 	{
9712 	    /* This is an error, but since there previously was no check only
9713 	     * give a warning. */
9714 	    msg_source(HL_ATTR(HLF_W));
9715 	    MSG(_("W18: Invalid character in group name"));
9716 	    break;
9717 	}
9718     }
9719 
9720     /*
9721      * First call for this growarray: init growing array.
9722      */
9723     if (highlight_ga.ga_data == NULL)
9724     {
9725 	highlight_ga.ga_itemsize = sizeof(struct hl_group);
9726 	highlight_ga.ga_growsize = 10;
9727     }
9728 
9729     if (highlight_ga.ga_len >= MAX_HL_ID)
9730     {
9731 	EMSG(_("E849: Too many highlight and syntax groups"));
9732 	vim_free(name);
9733 	return 0;
9734     }
9735 
9736     /*
9737      * Make room for at least one other syntax_highlight entry.
9738      */
9739     if (ga_grow(&highlight_ga, 1) == FAIL)
9740     {
9741 	vim_free(name);
9742 	return 0;
9743     }
9744 
9745     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9746     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9747     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9748 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9749     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9750     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
9751 # ifdef FEAT_GUI
9752     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
9753 # endif
9754 #endif
9755     ++highlight_ga.ga_len;
9756 
9757     return highlight_ga.ga_len;		    /* ID is index plus one */
9758 }
9759 
9760 /*
9761  * When, just after calling syn_add_group(), an error is discovered, this
9762  * function deletes the new name.
9763  */
9764     static void
9765 syn_unadd_group(void)
9766 {
9767     --highlight_ga.ga_len;
9768     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9769     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9770 }
9771 
9772 /*
9773  * Translate a group ID to highlight attributes.
9774  */
9775     int
9776 syn_id2attr(int hl_id)
9777 {
9778     int			attr;
9779     struct hl_group	*sgp;
9780 
9781     hl_id = syn_get_final_id(hl_id);
9782     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
9783 
9784 #ifdef FEAT_GUI
9785     /*
9786      * Only use GUI attr when the GUI is being used.
9787      */
9788     if (gui.in_use)
9789 	attr = sgp->sg_gui_attr;
9790     else
9791 #endif
9792 	if (IS_CTERM)
9793 	    attr = sgp->sg_cterm_attr;
9794 	else
9795 	    attr = sgp->sg_term_attr;
9796 
9797     return attr;
9798 }
9799 
9800 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
9801 /*
9802  * Get the GUI colors and attributes for a group ID.
9803  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9804  */
9805     int
9806 syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
9807 {
9808     struct hl_group	*sgp;
9809 
9810     hl_id = syn_get_final_id(hl_id);
9811     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
9812 
9813     *fgp = sgp->sg_gui_fg;
9814     *bgp = sgp->sg_gui_bg;
9815     return sgp->sg_gui;
9816 }
9817 #endif
9818 
9819 #if defined(FEAT_TERMINAL) || defined(PROTO)
9820     void
9821 syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9822 {
9823     struct hl_group	*sgp;
9824 
9825     hl_id = syn_get_final_id(hl_id);
9826     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
9827     *fgp = sgp->sg_cterm_fg - 1;
9828     *bgp = sgp->sg_cterm_bg - 1;
9829 }
9830 #endif
9831 
9832 /*
9833  * Translate a group ID to the final group ID (following links).
9834  */
9835     int
9836 syn_get_final_id(int hl_id)
9837 {
9838     int			count;
9839     struct hl_group	*sgp;
9840 
9841     if (hl_id > highlight_ga.ga_len || hl_id < 1)
9842 	return 0;			/* Can be called from eval!! */
9843 
9844     /*
9845      * Follow links until there is no more.
9846      * Look out for loops!  Break after 100 links.
9847      */
9848     for (count = 100; --count >= 0; )
9849     {
9850 	sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
9851 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9852 	    break;
9853 	hl_id = sgp->sg_link;
9854     }
9855 
9856     return hl_id;
9857 }
9858 
9859 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9860 /*
9861  * Call this function just after the GUI has started.
9862  * It finds the font and color handles for the highlighting groups.
9863  */
9864     void
9865 highlight_gui_started(void)
9866 {
9867     int	    idx;
9868 
9869     /* First get the colors from the "Normal" and "Menu" group, if set */
9870 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9871 #  ifdef FEAT_TERMGUICOLORS
9872     if (USE_24BIT)
9873 #  endif
9874 	set_normal_colors();
9875 # endif
9876 
9877     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9878 	gui_do_one_color(idx, FALSE, FALSE);
9879 
9880     highlight_changed();
9881 }
9882 
9883     static void
9884 gui_do_one_color(
9885     int		idx,
9886     int		do_menu UNUSED,	   /* TRUE: might set the menu font */
9887     int		do_tooltip UNUSED) /* TRUE: might set the tooltip font */
9888 {
9889     int		didit = FALSE;
9890 
9891 # ifdef FEAT_GUI
9892 #  ifdef FEAT_TERMGUICOLORS
9893     if (gui.in_use)
9894 #  endif
9895 	if (HL_TABLE()[idx].sg_font_name != NULL)
9896 	{
9897 	    hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9898 							    do_tooltip, TRUE);
9899 	    didit = TRUE;
9900 	}
9901 # endif
9902     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9903     {
9904 	HL_TABLE()[idx].sg_gui_fg =
9905 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9906 	didit = TRUE;
9907     }
9908     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9909     {
9910 	HL_TABLE()[idx].sg_gui_bg =
9911 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9912 	didit = TRUE;
9913     }
9914 # ifdef FEAT_GUI
9915     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9916     {
9917 	HL_TABLE()[idx].sg_gui_sp =
9918 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9919 	didit = TRUE;
9920     }
9921 # endif
9922     if (didit)	/* need to get a new attr number */
9923 	set_hl_attr(idx);
9924 }
9925 #endif
9926 
9927 #if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9928 /*
9929  * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
9930  */
9931     static void
9932 combine_stl_hlt(
9933 	int id,
9934 	int id_S,
9935 	int id_alt,
9936 	int hlcnt,
9937 	int i,
9938 	int hlf,
9939 	int *table)
9940 {
9941     struct hl_group *hlt = HL_TABLE();
9942 
9943     if (id_alt == 0)
9944     {
9945 	vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9946 	hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9947 	hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9948 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
9949 	hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9950 #  endif
9951     }
9952     else
9953 	mch_memmove(&hlt[hlcnt + i],
9954 		    &hlt[id_alt - 1],
9955 		    sizeof(struct hl_group));
9956     hlt[hlcnt + i].sg_link = 0;
9957 
9958     hlt[hlcnt + i].sg_term ^=
9959 	hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9960     if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9961 	hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9962     if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9963 	hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9964     hlt[hlcnt + i].sg_cterm ^=
9965 	hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9966     if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9967 	hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9968     if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9969 	hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9970 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
9971     hlt[hlcnt + i].sg_gui ^=
9972 	hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9973 #  endif
9974 #  ifdef FEAT_GUI
9975     if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9976 	hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9977     if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9978 	hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9979     if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9980 	hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9981     if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9982 	hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9983 #   ifdef FEAT_XFONTSET
9984     if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9985 	hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9986 #   endif
9987 #  endif
9988     highlight_ga.ga_len = hlcnt + i + 1;
9989     set_hl_attr(hlcnt + i);	/* At long last we can apply */
9990     table[i] = syn_id2attr(hlcnt + i + 1);
9991 }
9992 #endif
9993 
9994 /*
9995  * Translate the 'highlight' option into attributes in highlight_attr[] and
9996  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
9997  * corresponding highlights to use on top of HLF_SNC is computed.
9998  * Called only when the 'highlight' option has been changed and upon first
9999  * screen redraw after any :highlight command.
10000  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
10001  */
10002     int
10003 highlight_changed(void)
10004 {
10005     int		hlf;
10006     int		i;
10007     char_u	*p;
10008     int		attr;
10009     char_u	*end;
10010     int		id;
10011 #ifdef USER_HIGHLIGHT
10012     char_u      userhl[10];
10013 # ifdef FEAT_STL_OPT
10014     int		id_SNC = -1;
10015     int		id_S = -1;
10016 #  ifdef FEAT_TERMINAL
10017     int		id_ST = -1;
10018     int		id_STNC = -1;
10019 #  endif
10020     int		hlcnt;
10021 # endif
10022 #endif
10023     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
10024 
10025     need_highlight_changed = FALSE;
10026 
10027     /*
10028      * Clear all attributes.
10029      */
10030     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10031 	highlight_attr[hlf] = 0;
10032 
10033     /*
10034      * First set all attributes to their default value.
10035      * Then use the attributes from the 'highlight' option.
10036      */
10037     for (i = 0; i < 2; ++i)
10038     {
10039 	if (i)
10040 	    p = p_hl;
10041 	else
10042 	    p = get_highlight_default();
10043 	if (p == NULL)	    /* just in case */
10044 	    continue;
10045 
10046 	while (*p)
10047 	{
10048 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10049 		if (hl_flags[hlf] == *p)
10050 		    break;
10051 	    ++p;
10052 	    if (hlf == (int)HLF_COUNT || *p == NUL)
10053 		return FAIL;
10054 
10055 	    /*
10056 	     * Allow several hl_flags to be combined, like "bu" for
10057 	     * bold-underlined.
10058 	     */
10059 	    attr = 0;
10060 	    for ( ; *p && *p != ','; ++p)	    /* parse upto comma */
10061 	    {
10062 		if (VIM_ISWHITE(*p))		    /* ignore white space */
10063 		    continue;
10064 
10065 		if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
10066 		    return FAIL;
10067 
10068 		switch (*p)
10069 		{
10070 		    case 'b':	attr |= HL_BOLD;
10071 				break;
10072 		    case 'i':	attr |= HL_ITALIC;
10073 				break;
10074 		    case '-':
10075 		    case 'n':			    /* no highlighting */
10076 				break;
10077 		    case 'r':	attr |= HL_INVERSE;
10078 				break;
10079 		    case 's':	attr |= HL_STANDOUT;
10080 				break;
10081 		    case 'u':	attr |= HL_UNDERLINE;
10082 				break;
10083 		    case 'c':	attr |= HL_UNDERCURL;
10084 				break;
10085 		    case 't':	attr |= HL_STRIKETHROUGH;
10086 				break;
10087 		    case ':':	++p;		    /* highlight group name */
10088 				if (attr || *p == NUL)	 /* no combinations */
10089 				    return FAIL;
10090 				end = vim_strchr(p, ',');
10091 				if (end == NULL)
10092 				    end = p + STRLEN(p);
10093 				id = syn_check_group(p, (int)(end - p));
10094 				if (id == 0)
10095 				    return FAIL;
10096 				attr = syn_id2attr(id);
10097 				p = end - 1;
10098 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10099 				if (hlf == (int)HLF_SNC)
10100 				    id_SNC = syn_get_final_id(id);
10101 # ifdef FEAT_TERMINAL
10102 				else if (hlf == (int)HLF_ST)
10103 				    id_ST = syn_get_final_id(id);
10104 				else if (hlf == (int)HLF_STNC)
10105 				    id_STNC = syn_get_final_id(id);
10106 # endif
10107 				else if (hlf == (int)HLF_S)
10108 				    id_S = syn_get_final_id(id);
10109 #endif
10110 				break;
10111 		    default:	return FAIL;
10112 		}
10113 	    }
10114 	    highlight_attr[hlf] = attr;
10115 
10116 	    p = skip_to_option_part(p);	    /* skip comma and spaces */
10117 	}
10118     }
10119 
10120 #ifdef USER_HIGHLIGHT
10121     /* Setup the user highlights
10122      *
10123      * Temporarily utilize 28 more hl entries:
10124      * 9 for User1-User9 combined with StatusLineNC
10125      * 9 for User1-User9 combined with StatusLineTerm
10126      * 9 for User1-User9 combined with StatusLineTermNC
10127      * 1 for StatusLine default
10128      * Have to be in there simultaneously in case of table overflows in
10129      * get_attr_entry()
10130      */
10131 # ifdef FEAT_STL_OPT
10132     if (ga_grow(&highlight_ga, 28) == FAIL)
10133 	return FAIL;
10134     hlcnt = highlight_ga.ga_len;
10135     if (id_S == -1)
10136     {
10137 	/* Make sure id_S is always valid to simplify code below. Use the last
10138 	 * entry. */
10139 	vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
10140 	HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10141 	id_S = hlcnt + 19;
10142     }
10143 # endif
10144     for (i = 0; i < 9; i++)
10145     {
10146 	sprintf((char *)userhl, "User%d", i + 1);
10147 	id = syn_name2id(userhl);
10148 	if (id == 0)
10149 	{
10150 	    highlight_user[i] = 0;
10151 # ifdef FEAT_STL_OPT
10152 	    highlight_stlnc[i] = 0;
10153 #  ifdef FEAT_TERMINAL
10154 	    highlight_stlterm[i] = 0;
10155 	    highlight_stltermnc[i] = 0;
10156 #  endif
10157 # endif
10158 	}
10159 	else
10160 	{
10161 	    highlight_user[i] = syn_id2attr(id);
10162 # ifdef FEAT_STL_OPT
10163 	    combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10164 						     HLF_SNC, highlight_stlnc);
10165 #  ifdef FEAT_TERMINAL
10166 	    combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10167 						    HLF_ST, highlight_stlterm);
10168 	    combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10169 						HLF_STNC, highlight_stltermnc);
10170 #  endif
10171 # endif
10172 	}
10173     }
10174 # ifdef FEAT_STL_OPT
10175     highlight_ga.ga_len = hlcnt;
10176 # endif
10177 
10178 #endif /* USER_HIGHLIGHT */
10179 
10180     return OK;
10181 }
10182 
10183 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
10184 
10185 static void highlight_list(void);
10186 static void highlight_list_two(int cnt, int attr);
10187 
10188 /*
10189  * Handle command line completion for :highlight command.
10190  */
10191     void
10192 set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
10193 {
10194     char_u	*p;
10195 
10196     /* Default: expand group names */
10197     xp->xp_context = EXPAND_HIGHLIGHT;
10198     xp->xp_pattern = arg;
10199     include_link = 2;
10200     include_default = 1;
10201 
10202     /* (part of) subcommand already typed */
10203     if (*arg != NUL)
10204     {
10205 	p = skiptowhite(arg);
10206 	if (*p != NUL)			/* past "default" or group name */
10207 	{
10208 	    include_default = 0;
10209 	    if (STRNCMP("default", arg, p - arg) == 0)
10210 	    {
10211 		arg = skipwhite(p);
10212 		xp->xp_pattern = arg;
10213 		p = skiptowhite(arg);
10214 	    }
10215 	    if (*p != NUL)			/* past group name */
10216 	    {
10217 		include_link = 0;
10218 		if (arg[1] == 'i' && arg[0] == 'N')
10219 		    highlight_list();
10220 		if (STRNCMP("link", arg, p - arg) == 0
10221 			|| STRNCMP("clear", arg, p - arg) == 0)
10222 		{
10223 		    xp->xp_pattern = skipwhite(p);
10224 		    p = skiptowhite(xp->xp_pattern);
10225 		    if (*p != NUL)		/* past first group name */
10226 		    {
10227 			xp->xp_pattern = skipwhite(p);
10228 			p = skiptowhite(xp->xp_pattern);
10229 		    }
10230 		}
10231 		if (*p != NUL)			/* past group name(s) */
10232 		    xp->xp_context = EXPAND_NOTHING;
10233 	    }
10234 	}
10235     }
10236 }
10237 
10238 /*
10239  * List highlighting matches in a nice way.
10240  */
10241     static void
10242 highlight_list(void)
10243 {
10244     int		i;
10245 
10246     for (i = 10; --i >= 0; )
10247 	highlight_list_two(i, HL_ATTR(HLF_D));
10248     for (i = 40; --i >= 0; )
10249 	highlight_list_two(99, 0);
10250 }
10251 
10252     static void
10253 highlight_list_two(int cnt, int attr)
10254 {
10255     msg_puts_attr((char_u *)&("N \bI \b!  \b"[cnt / 11]), attr);
10256     msg_clr_eos();
10257     out_flush();
10258     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10259 }
10260 
10261 #endif /* FEAT_CMDL_COMPL */
10262 
10263 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10264     || defined(FEAT_SIGNS) || defined(PROTO)
10265 /*
10266  * Function given to ExpandGeneric() to obtain the list of group names.
10267  */
10268     char_u *
10269 get_highlight_name(expand_T *xp UNUSED, int idx)
10270 {
10271     return get_highlight_name_ext(xp, idx, TRUE);
10272 }
10273 
10274 /*
10275  * Obtain a highlight group name.
10276  * When "skip_cleared" is TRUE don't return a cleared entry.
10277  */
10278     char_u *
10279 get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10280 {
10281     if (idx < 0)
10282 	return NULL;
10283 
10284     /* Items are never removed from the table, skip the ones that were
10285      * cleared. */
10286     if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10287 	return (char_u *)"";
10288 
10289 #ifdef FEAT_CMDL_COMPL
10290     if (idx == highlight_ga.ga_len && include_none != 0)
10291 	return (char_u *)"none";
10292     if (idx == highlight_ga.ga_len + include_none && include_default != 0)
10293 	return (char_u *)"default";
10294     if (idx == highlight_ga.ga_len + include_none + include_default
10295 							 && include_link != 0)
10296 	return (char_u *)"link";
10297     if (idx == highlight_ga.ga_len + include_none + include_default + 1
10298 							 && include_link != 0)
10299 	return (char_u *)"clear";
10300 #endif
10301     if (idx >= highlight_ga.ga_len)
10302 	return NULL;
10303     return HL_TABLE()[idx].sg_name;
10304 }
10305 #endif
10306 
10307 #if defined(FEAT_GUI) || defined(PROTO)
10308 /*
10309  * Free all the highlight group fonts.
10310  * Used when quitting for systems which need it.
10311  */
10312     void
10313 free_highlight_fonts(void)
10314 {
10315     int	    idx;
10316 
10317     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10318     {
10319 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
10320 	HL_TABLE()[idx].sg_font = NOFONT;
10321 # ifdef FEAT_XFONTSET
10322 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10323 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
10324 # endif
10325     }
10326 
10327     gui_mch_free_font(gui.norm_font);
10328 # ifdef FEAT_XFONTSET
10329     gui_mch_free_fontset(gui.fontset);
10330 # endif
10331 # ifndef FEAT_GUI_GTK
10332     gui_mch_free_font(gui.bold_font);
10333     gui_mch_free_font(gui.ital_font);
10334     gui_mch_free_font(gui.boldital_font);
10335 # endif
10336 }
10337 #endif
10338 
10339 /**************************************
10340  *  End of Highlighting stuff	      *
10341  **************************************/
10342