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