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