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