xref: /vim-8.2.3635/src/syntax.c (revision 00a927d6)
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 (result->lnum > syn_buf->b_ml.ml_line_count)
3090     {
3091 	/* a "\n" at the end of the pattern may take us below the last line */
3092 	result->lnum = syn_buf->b_ml.ml_line_count;
3093 	col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
3094     }
3095     if (off != 0)
3096     {
3097 	base = ml_get_buf(syn_buf, result->lnum, FALSE);
3098 	p = base + col;
3099 	if (off > 0)
3100 	{
3101 	    while (off-- && *p != NUL)
3102 		mb_ptr_adv(p);
3103 	}
3104 	else if (off < 0)
3105 	{
3106 	    while (off++ && base < p)
3107 		mb_ptr_back(base, p);
3108 	}
3109 	col = (int)(p - base);
3110     }
3111     result->col = col;
3112 }
3113 
3114 /*
3115  * Get current line in syntax buffer.
3116  */
3117     static char_u *
3118 syn_getcurline()
3119 {
3120     return ml_get_buf(syn_buf, current_lnum, FALSE);
3121 }
3122 
3123 /*
3124  * Call vim_regexec() to find a match with "rmp" in "syn_buf".
3125  * Returns TRUE when there is a match.
3126  */
3127     static int
3128 syn_regexec(rmp, lnum, col)
3129     regmmatch_T	*rmp;
3130     linenr_T	lnum;
3131     colnr_T	col;
3132 {
3133     rmp->rmm_maxcol = syn_buf->b_p_smc;
3134     if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
3135     {
3136 	rmp->startpos[0].lnum += lnum;
3137 	rmp->endpos[0].lnum += lnum;
3138 	return TRUE;
3139     }
3140     return FALSE;
3141 }
3142 
3143 /*
3144  * Check one position in a line for a matching keyword.
3145  * The caller must check if a keyword can start at startcol.
3146  * Return it's ID if found, 0 otherwise.
3147  */
3148     static int
3149 check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3150     char_u	*line;
3151     int		startcol;	/* position in line to check for keyword */
3152     int		*endcolp;	/* return: character after found keyword */
3153     long	*flagsp;	/* return: flags of matching keyword */
3154     short	**next_listp;	/* return: next_list of matching keyword */
3155     stateitem_T	*cur_si;	/* item at the top of the stack */
3156 {
3157     keyentry_T	*kp;
3158     char_u	*kwp;
3159     int		round;
3160     int		kwlen;
3161     char_u	keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
3162     hashtab_T	*ht;
3163     hashitem_T	*hi;
3164 
3165     /* Find first character after the keyword.  First character was already
3166      * checked. */
3167     kwp = line + startcol;
3168     kwlen = 0;
3169     do
3170     {
3171 #ifdef FEAT_MBYTE
3172 	if (has_mbyte)
3173 	    kwlen += (*mb_ptr2len)(kwp + kwlen);
3174 	else
3175 #endif
3176 	    ++kwlen;
3177     }
3178     while (vim_iswordc_buf(kwp + kwlen, syn_buf));
3179 
3180     if (kwlen > MAXKEYWLEN)
3181 	return 0;
3182 
3183     /*
3184      * Must make a copy of the keyword, so we can add a NUL and make it
3185      * lowercase.
3186      */
3187     vim_strncpy(keyword, kwp, kwlen);
3188 
3189     /*
3190      * Try twice:
3191      * 1. matching case
3192      * 2. ignoring case
3193      */
3194     for (round = 1; round <= 2; ++round)
3195     {
3196 	ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3197 	if (ht->ht_used == 0)
3198 	    continue;
3199 	if (round == 2)	/* ignore case */
3200 	    (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
3201 
3202 	/*
3203 	 * Find keywords that match.  There can be several with different
3204 	 * attributes.
3205 	 * When current_next_list is non-zero accept only that group, otherwise:
3206 	 *  Accept a not-contained keyword at toplevel.
3207 	 *  Accept a keyword at other levels only if it is in the contains list.
3208 	 */
3209 	hi = hash_find(ht, keyword);
3210 	if (!HASHITEM_EMPTY(hi))
3211 	    for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
3212 	    {
3213 		if (current_next_list != 0
3214 			? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3215 			: (cur_si == NULL
3216 			    ? !(kp->flags & HL_CONTAINED)
3217 			    : in_id_list(cur_si, cur_si->si_cont_list,
3218 				      &kp->k_syn, kp->flags & HL_CONTAINED)))
3219 		{
3220 		    *endcolp = startcol + kwlen;
3221 		    *flagsp = kp->flags;
3222 		    *next_listp = kp->next_list;
3223 		    return kp->k_syn.id;
3224 		}
3225 	    }
3226     }
3227     return 0;
3228 }
3229 
3230 /*
3231  * Handle ":syntax case" command.
3232  */
3233     static void
3234 syn_cmd_case(eap, syncing)
3235     exarg_T	*eap;
3236     int		syncing UNUSED;
3237 {
3238     char_u	*arg = eap->arg;
3239     char_u	*next;
3240 
3241     eap->nextcmd = find_nextcmd(arg);
3242     if (eap->skip)
3243 	return;
3244 
3245     next = skiptowhite(arg);
3246     if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3247 	curbuf->b_syn_ic = FALSE;
3248     else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3249 	curbuf->b_syn_ic = TRUE;
3250     else
3251 	EMSG2(_("E390: Illegal argument: %s"), arg);
3252 }
3253 
3254 /*
3255  * Handle ":syntax spell" command.
3256  */
3257     static void
3258 syn_cmd_spell(eap, syncing)
3259     exarg_T	*eap;
3260     int		syncing UNUSED;
3261 {
3262     char_u	*arg = eap->arg;
3263     char_u	*next;
3264 
3265     eap->nextcmd = find_nextcmd(arg);
3266     if (eap->skip)
3267 	return;
3268 
3269     next = skiptowhite(arg);
3270     if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3271 	curbuf->b_syn_spell = SYNSPL_TOP;
3272     else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3273 	curbuf->b_syn_spell = SYNSPL_NOTOP;
3274     else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
3275 	curbuf->b_syn_spell = SYNSPL_DEFAULT;
3276     else
3277 	EMSG2(_("E390: Illegal argument: %s"), arg);
3278 }
3279 
3280 /*
3281  * Clear all syntax info for one buffer.
3282  */
3283     void
3284 syntax_clear(buf)
3285     buf_T	*buf;
3286 {
3287     int i;
3288 
3289     buf->b_syn_error = FALSE;	    /* clear previous error */
3290     buf->b_syn_ic = FALSE;	    /* Use case, by default */
3291     buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3292     buf->b_syn_containedin = FALSE;
3293 
3294     /* free the keywords */
3295     clear_keywtab(&buf->b_keywtab);
3296     clear_keywtab(&buf->b_keywtab_ic);
3297 
3298     /* free the syntax patterns */
3299     for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3300 	syn_clear_pattern(buf, i);
3301     ga_clear(&buf->b_syn_patterns);
3302 
3303     /* free the syntax clusters */
3304     for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3305 	syn_clear_cluster(buf, i);
3306     ga_clear(&buf->b_syn_clusters);
3307     buf->b_spell_cluster_id = 0;
3308     buf->b_nospell_cluster_id = 0;
3309 
3310     buf->b_syn_sync_flags = 0;
3311     buf->b_syn_sync_minlines = 0;
3312     buf->b_syn_sync_maxlines = 0;
3313     buf->b_syn_sync_linebreaks = 0;
3314 
3315     vim_free(buf->b_syn_linecont_prog);
3316     buf->b_syn_linecont_prog = NULL;
3317     vim_free(buf->b_syn_linecont_pat);
3318     buf->b_syn_linecont_pat = NULL;
3319 #ifdef FEAT_FOLDING
3320     buf->b_syn_folditems = 0;
3321 #endif
3322 
3323     /* free the stored states */
3324     syn_stack_free_all(buf);
3325     invalidate_current_state();
3326 }
3327 
3328 /*
3329  * Clear syncing info for one buffer.
3330  */
3331     static void
3332 syntax_sync_clear()
3333 {
3334     int i;
3335 
3336     /* free the syntax patterns */
3337     for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3338 	if (SYN_ITEMS(curbuf)[i].sp_syncing)
3339 	    syn_remove_pattern(curbuf, i);
3340 
3341     curbuf->b_syn_sync_flags = 0;
3342     curbuf->b_syn_sync_minlines = 0;
3343     curbuf->b_syn_sync_maxlines = 0;
3344     curbuf->b_syn_sync_linebreaks = 0;
3345 
3346     vim_free(curbuf->b_syn_linecont_prog);
3347     curbuf->b_syn_linecont_prog = NULL;
3348     vim_free(curbuf->b_syn_linecont_pat);
3349     curbuf->b_syn_linecont_pat = NULL;
3350 
3351     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3352 }
3353 
3354 /*
3355  * Remove one pattern from the buffer's pattern list.
3356  */
3357     static void
3358 syn_remove_pattern(buf, idx)
3359     buf_T	*buf;
3360     int		idx;
3361 {
3362     synpat_T	*spp;
3363 
3364     spp = &(SYN_ITEMS(buf)[idx]);
3365 #ifdef FEAT_FOLDING
3366     if (spp->sp_flags & HL_FOLD)
3367 	--buf->b_syn_folditems;
3368 #endif
3369     syn_clear_pattern(buf, idx);
3370     mch_memmove(spp, spp + 1,
3371 		   sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3372     --buf->b_syn_patterns.ga_len;
3373 }
3374 
3375 /*
3376  * Clear and free one syntax pattern.  When clearing all, must be called from
3377  * last to first!
3378  */
3379     static void
3380 syn_clear_pattern(buf, i)
3381     buf_T	*buf;
3382     int		i;
3383 {
3384     vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3385     vim_free(SYN_ITEMS(buf)[i].sp_prog);
3386     /* Only free sp_cont_list and sp_next_list of first start pattern */
3387     if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3388     {
3389 	vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3390 	vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3391 	vim_free(SYN_ITEMS(buf)[i].sp_syn.cont_in_list);
3392     }
3393 }
3394 
3395 /*
3396  * Clear and free one syntax cluster.
3397  */
3398     static void
3399 syn_clear_cluster(buf, i)
3400     buf_T	*buf;
3401     int		i;
3402 {
3403     vim_free(SYN_CLSTR(buf)[i].scl_name);
3404     vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3405     vim_free(SYN_CLSTR(buf)[i].scl_list);
3406 }
3407 
3408 /*
3409  * Handle ":syntax clear" command.
3410  */
3411     static void
3412 syn_cmd_clear(eap, syncing)
3413     exarg_T	*eap;
3414     int		syncing;
3415 {
3416     char_u	*arg = eap->arg;
3417     char_u	*arg_end;
3418     int		id;
3419 
3420     eap->nextcmd = find_nextcmd(arg);
3421     if (eap->skip)
3422 	return;
3423 
3424     /*
3425      * We have to disable this within ":syn include @group filename",
3426      * because otherwise @group would get deleted.
3427      * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3428      * clear".
3429      */
3430     if (curbuf->b_syn_topgrp != 0)
3431 	return;
3432 
3433     if (ends_excmd(*arg))
3434     {
3435 	/*
3436 	 * No argument: Clear all syntax items.
3437 	 */
3438 	if (syncing)
3439 	    syntax_sync_clear();
3440 	else
3441 	{
3442 	    syntax_clear(curbuf);
3443 	    do_unlet((char_u *)"b:current_syntax", TRUE);
3444 	}
3445     }
3446     else
3447     {
3448 	/*
3449 	 * Clear the group IDs that are in the argument.
3450 	 */
3451 	while (!ends_excmd(*arg))
3452 	{
3453 	    arg_end = skiptowhite(arg);
3454 	    if (*arg == '@')
3455 	    {
3456 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3457 		if (id == 0)
3458 		{
3459 		    EMSG2(_("E391: No such syntax cluster: %s"), arg);
3460 		    break;
3461 		}
3462 		else
3463 		{
3464 		    /*
3465 		     * We can't physically delete a cluster without changing
3466 		     * the IDs of other clusters, so we do the next best thing
3467 		     * and make it empty.
3468 		     */
3469 		    short scl_id = id - SYNID_CLUSTER;
3470 
3471 		    vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3472 		    SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3473 		}
3474 	    }
3475 	    else
3476 	    {
3477 		id = syn_namen2id(arg, (int)(arg_end - arg));
3478 		if (id == 0)
3479 		{
3480 		    EMSG2(_(e_nogroup), arg);
3481 		    break;
3482 		}
3483 		else
3484 		    syn_clear_one(id, syncing);
3485 	    }
3486 	    arg = skipwhite(arg_end);
3487 	}
3488     }
3489     redraw_curbuf_later(SOME_VALID);
3490     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3491 }
3492 
3493 /*
3494  * Clear one syntax group for the current buffer.
3495  */
3496     static void
3497 syn_clear_one(id, syncing)
3498     int		id;
3499     int		syncing;
3500 {
3501     synpat_T	*spp;
3502     int		idx;
3503 
3504     /* Clear keywords only when not ":syn sync clear group-name" */
3505     if (!syncing)
3506     {
3507 	(void)syn_clear_keyword(id, &curbuf->b_keywtab);
3508 	(void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
3509     }
3510 
3511     /* clear the patterns for "id" */
3512     for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3513     {
3514 	spp = &(SYN_ITEMS(curbuf)[idx]);
3515 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3516 	    continue;
3517 	syn_remove_pattern(curbuf, idx);
3518     }
3519 }
3520 
3521 /*
3522  * Handle ":syntax on" command.
3523  */
3524     static void
3525 syn_cmd_on(eap, syncing)
3526     exarg_T	*eap;
3527     int		syncing UNUSED;
3528 {
3529     syn_cmd_onoff(eap, "syntax");
3530 }
3531 
3532 /*
3533  * Handle ":syntax enable" command.
3534  */
3535     static void
3536 syn_cmd_enable(eap, syncing)
3537     exarg_T	*eap;
3538     int		syncing UNUSED;
3539 {
3540     set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3541     syn_cmd_onoff(eap, "syntax");
3542     do_unlet((char_u *)"g:syntax_cmd", TRUE);
3543 }
3544 
3545 /*
3546  * Handle ":syntax reset" command.
3547  */
3548     static void
3549 syn_cmd_reset(eap, syncing)
3550     exarg_T	*eap;
3551     int		syncing UNUSED;
3552 {
3553     eap->nextcmd = check_nextcmd(eap->arg);
3554     if (!eap->skip)
3555     {
3556 	set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3557 	do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
3558 	do_unlet((char_u *)"g:syntax_cmd", TRUE);
3559     }
3560 }
3561 
3562 /*
3563  * Handle ":syntax manual" command.
3564  */
3565     static void
3566 syn_cmd_manual(eap, syncing)
3567     exarg_T	*eap;
3568     int		syncing UNUSED;
3569 {
3570     syn_cmd_onoff(eap, "manual");
3571 }
3572 
3573 /*
3574  * Handle ":syntax off" command.
3575  */
3576     static void
3577 syn_cmd_off(eap, syncing)
3578     exarg_T	*eap;
3579     int		syncing UNUSED;
3580 {
3581     syn_cmd_onoff(eap, "nosyntax");
3582 }
3583 
3584     static void
3585 syn_cmd_onoff(eap, name)
3586     exarg_T	*eap;
3587     char	*name;
3588 {
3589     char_u	buf[100];
3590 
3591     eap->nextcmd = check_nextcmd(eap->arg);
3592     if (!eap->skip)
3593     {
3594 	STRCPY(buf, "so ");
3595 	vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
3596 	do_cmdline_cmd(buf);
3597     }
3598 }
3599 
3600 /*
3601  * Handle ":syntax [list]" command: list current syntax words.
3602  */
3603     static void
3604 syn_cmd_list(eap, syncing)
3605     exarg_T	*eap;
3606     int		syncing;	    /* when TRUE: list syncing items */
3607 {
3608     char_u	*arg = eap->arg;
3609     int		id;
3610     char_u	*arg_end;
3611 
3612     eap->nextcmd = find_nextcmd(arg);
3613     if (eap->skip)
3614 	return;
3615 
3616     if (!syntax_present(curbuf))
3617     {
3618 	MSG(_("No Syntax items defined for this buffer"));
3619 	return;
3620     }
3621 
3622     if (syncing)
3623     {
3624 	if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3625 	{
3626 	    MSG_PUTS(_("syncing on C-style comments"));
3627 	    syn_lines_msg();
3628 	    syn_match_msg();
3629 	    return;
3630 	}
3631 	else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3632 	{
3633 	    if (curbuf->b_syn_sync_minlines == 0)
3634 		MSG_PUTS(_("no syncing"));
3635 	    else
3636 	    {
3637 		MSG_PUTS(_("syncing starts "));
3638 		msg_outnum(curbuf->b_syn_sync_minlines);
3639 		MSG_PUTS(_(" lines before top line"));
3640 		syn_match_msg();
3641 	    }
3642 	    return;
3643 	}
3644 	MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3645 	if (curbuf->b_syn_sync_minlines > 0
3646 		|| curbuf->b_syn_sync_maxlines > 0
3647 		|| curbuf->b_syn_sync_linebreaks > 0)
3648 	{
3649 	    MSG_PUTS(_("\nsyncing on items"));
3650 	    syn_lines_msg();
3651 	    syn_match_msg();
3652 	}
3653     }
3654     else
3655 	MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3656     if (ends_excmd(*arg))
3657     {
3658 	/*
3659 	 * No argument: List all group IDs and all syntax clusters.
3660 	 */
3661 	for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3662 	    syn_list_one(id, syncing, FALSE);
3663 	for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3664 	    syn_list_cluster(id);
3665     }
3666     else
3667     {
3668 	/*
3669 	 * List the group IDs and syntax clusters that are in the argument.
3670 	 */
3671 	while (!ends_excmd(*arg) && !got_int)
3672 	{
3673 	    arg_end = skiptowhite(arg);
3674 	    if (*arg == '@')
3675 	    {
3676 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3677 		if (id == 0)
3678 		    EMSG2(_("E392: No such syntax cluster: %s"), arg);
3679 		else
3680 		    syn_list_cluster(id - SYNID_CLUSTER);
3681 	    }
3682 	    else
3683 	    {
3684 		id = syn_namen2id(arg, (int)(arg_end - arg));
3685 		if (id == 0)
3686 		    EMSG2(_(e_nogroup), arg);
3687 		else
3688 		    syn_list_one(id, syncing, TRUE);
3689 	    }
3690 	    arg = skipwhite(arg_end);
3691 	}
3692     }
3693     eap->nextcmd = check_nextcmd(arg);
3694 }
3695 
3696     static void
3697 syn_lines_msg()
3698 {
3699     if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3700     {
3701 	MSG_PUTS("; ");
3702 	if (curbuf->b_syn_sync_minlines > 0)
3703 	{
3704 	    MSG_PUTS(_("minimal "));
3705 	    msg_outnum(curbuf->b_syn_sync_minlines);
3706 	    if (curbuf->b_syn_sync_maxlines)
3707 		MSG_PUTS(", ");
3708 	}
3709 	if (curbuf->b_syn_sync_maxlines > 0)
3710 	{
3711 	    MSG_PUTS(_("maximal "));
3712 	    msg_outnum(curbuf->b_syn_sync_maxlines);
3713 	}
3714 	MSG_PUTS(_(" lines before top line"));
3715     }
3716 }
3717 
3718     static void
3719 syn_match_msg()
3720 {
3721     if (curbuf->b_syn_sync_linebreaks > 0)
3722     {
3723 	MSG_PUTS(_("; match "));
3724 	msg_outnum(curbuf->b_syn_sync_linebreaks);
3725 	MSG_PUTS(_(" line breaks"));
3726     }
3727 }
3728 
3729 static int  last_matchgroup;
3730 
3731 struct name_list
3732 {
3733     int		flag;
3734     char	*name;
3735 };
3736 
3737 static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3738 
3739 /*
3740  * List one syntax item, for ":syntax" or "syntax list syntax_name".
3741  */
3742     static void
3743 syn_list_one(id, syncing, link_only)
3744     int		id;
3745     int		syncing;	    /* when TRUE: list syncing items */
3746     int		link_only;	    /* when TRUE; list link-only too */
3747 {
3748     int		attr;
3749     int		idx;
3750     int		did_header = FALSE;
3751     synpat_T	*spp;
3752     static struct name_list namelist1[] =
3753 		{
3754 		    {HL_DISPLAY, "display"},
3755 		    {HL_CONTAINED, "contained"},
3756 		    {HL_ONELINE, "oneline"},
3757 		    {HL_KEEPEND, "keepend"},
3758 		    {HL_EXTEND, "extend"},
3759 		    {HL_EXCLUDENL, "excludenl"},
3760 		    {HL_TRANSP, "transparent"},
3761 		    {HL_FOLD, "fold"},
3762 		    {0, NULL}
3763 		};
3764     static struct name_list namelist2[] =
3765 		{
3766 		    {HL_SKIPWHITE, "skipwhite"},
3767 		    {HL_SKIPNL, "skipnl"},
3768 		    {HL_SKIPEMPTY, "skipempty"},
3769 		    {0, NULL}
3770 		};
3771 
3772     attr = hl_attr(HLF_D);		/* highlight like directories */
3773 
3774     /* list the keywords for "id" */
3775     if (!syncing)
3776     {
3777 	did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3778 	did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
3779 							    did_header, attr);
3780     }
3781 
3782     /* list the patterns for "id" */
3783     for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3784     {
3785 	spp = &(SYN_ITEMS(curbuf)[idx]);
3786 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3787 	    continue;
3788 
3789 	(void)syn_list_header(did_header, 999, id);
3790 	did_header = TRUE;
3791 	last_matchgroup = 0;
3792 	if (spp->sp_type == SPTYPE_MATCH)
3793 	{
3794 	    put_pattern("match", ' ', spp, attr);
3795 	    msg_putchar(' ');
3796 	}
3797 	else if (spp->sp_type == SPTYPE_START)
3798 	{
3799 	    while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3800 		put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3801 	    if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3802 		put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3803 	    while (idx < curbuf->b_syn_patterns.ga_len
3804 			      && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3805 		put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3806 	    --idx;
3807 	    msg_putchar(' ');
3808 	}
3809 	syn_list_flags(namelist1, spp->sp_flags, attr);
3810 
3811 	if (spp->sp_cont_list != NULL)
3812 	    put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3813 
3814 	if (spp->sp_syn.cont_in_list != NULL)
3815 	    put_id_list((char_u *)"containedin",
3816 					      spp->sp_syn.cont_in_list, attr);
3817 
3818 	if (spp->sp_next_list != NULL)
3819 	{
3820 	    put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3821 	    syn_list_flags(namelist2, spp->sp_flags, attr);
3822 	}
3823 	if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3824 	{
3825 	    if (spp->sp_flags & HL_SYNC_HERE)
3826 		msg_puts_attr((char_u *)"grouphere", attr);
3827 	    else
3828 		msg_puts_attr((char_u *)"groupthere", attr);
3829 	    msg_putchar(' ');
3830 	    if (spp->sp_sync_idx >= 0)
3831 		msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3832 				   [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3833 	    else
3834 		MSG_PUTS("NONE");
3835 	    msg_putchar(' ');
3836 	}
3837     }
3838 
3839     /* list the link, if there is one */
3840     if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3841     {
3842 	(void)syn_list_header(did_header, 999, id);
3843 	msg_puts_attr((char_u *)"links to", attr);
3844 	msg_putchar(' ');
3845 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3846     }
3847 }
3848 
3849     static void
3850 syn_list_flags(nl, flags, attr)
3851     struct name_list	*nl;
3852     int			flags;
3853     int			attr;
3854 {
3855     int		i;
3856 
3857     for (i = 0; nl[i].flag != 0; ++i)
3858 	if (flags & nl[i].flag)
3859 	{
3860 	    msg_puts_attr((char_u *)nl[i].name, attr);
3861 	    msg_putchar(' ');
3862 	}
3863 }
3864 
3865 /*
3866  * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3867  */
3868     static void
3869 syn_list_cluster(id)
3870     int id;
3871 {
3872     int	    endcol = 15;
3873 
3874     /* slight hack:  roughly duplicate the guts of syn_list_header() */
3875     msg_putchar('\n');
3876     msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3877 
3878     if (msg_col >= endcol)	/* output at least one space */
3879 	endcol = msg_col + 1;
3880     if (Columns <= endcol)	/* avoid hang for tiny window */
3881 	endcol = Columns - 1;
3882 
3883     msg_advance(endcol);
3884     if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3885     {
3886 	put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3887 		    hl_attr(HLF_D));
3888     }
3889     else
3890     {
3891 	msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3892 	msg_puts((char_u *)"=NONE");
3893     }
3894 }
3895 
3896     static void
3897 put_id_list(name, list, attr)
3898     char_u	*name;
3899     short	*list;
3900     int		attr;
3901 {
3902     short		*p;
3903 
3904     msg_puts_attr(name, attr);
3905     msg_putchar('=');
3906     for (p = list; *p; ++p)
3907     {
3908 	if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3909 	{
3910 	    if (p[1])
3911 		MSG_PUTS("ALLBUT");
3912 	    else
3913 		MSG_PUTS("ALL");
3914 	}
3915 	else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3916 	{
3917 	    MSG_PUTS("TOP");
3918 	}
3919 	else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3920 	{
3921 	    MSG_PUTS("CONTAINED");
3922 	}
3923 	else if (*p >= SYNID_CLUSTER)
3924 	{
3925 	    short scl_id = *p - SYNID_CLUSTER;
3926 
3927 	    msg_putchar('@');
3928 	    msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3929 	}
3930 	else
3931 	    msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3932 	if (p[1])
3933 	    msg_putchar(',');
3934     }
3935     msg_putchar(' ');
3936 }
3937 
3938     static void
3939 put_pattern(s, c, spp, attr)
3940     char	*s;
3941     int		c;
3942     synpat_T	*spp;
3943     int		attr;
3944 {
3945     long	n;
3946     int		mask;
3947     int		first;
3948     static char	*sepchars = "/+=-#@\"|'^&";
3949     int		i;
3950 
3951     /* May have to write "matchgroup=group" */
3952     if (last_matchgroup != spp->sp_syn_match_id)
3953     {
3954 	last_matchgroup = spp->sp_syn_match_id;
3955 	msg_puts_attr((char_u *)"matchgroup", attr);
3956 	msg_putchar('=');
3957 	if (last_matchgroup == 0)
3958 	    msg_outtrans((char_u *)"NONE");
3959 	else
3960 	    msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3961 	msg_putchar(' ');
3962     }
3963 
3964     /* output the name of the pattern and an '=' or ' ' */
3965     msg_puts_attr((char_u *)s, attr);
3966     msg_putchar(c);
3967 
3968     /* output the pattern, in between a char that is not in the pattern */
3969     for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3970 	if (sepchars[++i] == NUL)
3971 	{
3972 	    i = 0;	/* no good char found, just use the first one */
3973 	    break;
3974 	}
3975     msg_putchar(sepchars[i]);
3976     msg_outtrans(spp->sp_pattern);
3977     msg_putchar(sepchars[i]);
3978 
3979     /* output any pattern options */
3980     first = TRUE;
3981     for (i = 0; i < SPO_COUNT; ++i)
3982     {
3983 	mask = (1 << i);
3984 	if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3985 	{
3986 	    if (!first)
3987 		msg_putchar(',');	/* separate with commas */
3988 	    msg_puts((char_u *)spo_name_tab[i]);
3989 	    n = spp->sp_offsets[i];
3990 	    if (i != SPO_LC_OFF)
3991 	    {
3992 		if (spp->sp_off_flags & mask)
3993 		    msg_putchar('s');
3994 		else
3995 		    msg_putchar('e');
3996 		if (n > 0)
3997 		    msg_putchar('+');
3998 	    }
3999 	    if (n || i == SPO_LC_OFF)
4000 		msg_outnum(n);
4001 	    first = FALSE;
4002 	}
4003     }
4004     msg_putchar(' ');
4005 }
4006 
4007 /*
4008  * List or clear the keywords for one syntax group.
4009  * Return TRUE if the header has been printed.
4010  */
4011     static int
4012 syn_list_keywords(id, ht, did_header, attr)
4013     int		id;
4014     hashtab_T	*ht;
4015     int		did_header;		/* header has already been printed */
4016     int		attr;
4017 {
4018     int		outlen;
4019     hashitem_T	*hi;
4020     keyentry_T	*kp;
4021     int		todo;
4022     int		prev_contained = 0;
4023     short	*prev_next_list = NULL;
4024     short	*prev_cont_in_list = NULL;
4025     int		prev_skipnl = 0;
4026     int		prev_skipwhite = 0;
4027     int		prev_skipempty = 0;
4028 
4029     /*
4030      * Unfortunately, this list of keywords is not sorted on alphabet but on
4031      * hash value...
4032      */
4033     todo = (int)ht->ht_used;
4034     for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
4035     {
4036 	if (!HASHITEM_EMPTY(hi))
4037 	{
4038 	    --todo;
4039 	    for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
4040 	    {
4041 		if (kp->k_syn.id == id)
4042 		{
4043 		    if (prev_contained != (kp->flags & HL_CONTAINED)
4044 			    || prev_skipnl != (kp->flags & HL_SKIPNL)
4045 			    || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4046 			    || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4047 			    || prev_cont_in_list != kp->k_syn.cont_in_list
4048 			    || prev_next_list != kp->next_list)
4049 			outlen = 9999;
4050 		    else
4051 			outlen = (int)STRLEN(kp->keyword);
4052 		    /* output "contained" and "nextgroup" on each line */
4053 		    if (syn_list_header(did_header, outlen, id))
4054 		    {
4055 			prev_contained = 0;
4056 			prev_next_list = NULL;
4057 			prev_cont_in_list = NULL;
4058 			prev_skipnl = 0;
4059 			prev_skipwhite = 0;
4060 			prev_skipempty = 0;
4061 		    }
4062 		    did_header = TRUE;
4063 		    if (prev_contained != (kp->flags & HL_CONTAINED))
4064 		    {
4065 			msg_puts_attr((char_u *)"contained", attr);
4066 			msg_putchar(' ');
4067 			prev_contained = (kp->flags & HL_CONTAINED);
4068 		    }
4069 		    if (kp->k_syn.cont_in_list != prev_cont_in_list)
4070 		    {
4071 			put_id_list((char_u *)"containedin",
4072 						kp->k_syn.cont_in_list, attr);
4073 			msg_putchar(' ');
4074 			prev_cont_in_list = kp->k_syn.cont_in_list;
4075 		    }
4076 		    if (kp->next_list != prev_next_list)
4077 		    {
4078 			put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4079 			msg_putchar(' ');
4080 			prev_next_list = kp->next_list;
4081 			if (kp->flags & HL_SKIPNL)
4082 			{
4083 			    msg_puts_attr((char_u *)"skipnl", attr);
4084 			    msg_putchar(' ');
4085 			    prev_skipnl = (kp->flags & HL_SKIPNL);
4086 			}
4087 			if (kp->flags & HL_SKIPWHITE)
4088 			{
4089 			    msg_puts_attr((char_u *)"skipwhite", attr);
4090 			    msg_putchar(' ');
4091 			    prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4092 			}
4093 			if (kp->flags & HL_SKIPEMPTY)
4094 			{
4095 			    msg_puts_attr((char_u *)"skipempty", attr);
4096 			    msg_putchar(' ');
4097 			    prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4098 			}
4099 		    }
4100 		    msg_outtrans(kp->keyword);
4101 		}
4102 	    }
4103 	}
4104     }
4105 
4106     return did_header;
4107 }
4108 
4109     static void
4110 syn_clear_keyword(id, ht)
4111     int		id;
4112     hashtab_T	*ht;
4113 {
4114     hashitem_T	*hi;
4115     keyentry_T	*kp;
4116     keyentry_T	*kp_prev;
4117     keyentry_T	*kp_next;
4118     int		todo;
4119 
4120     hash_lock(ht);
4121     todo = (int)ht->ht_used;
4122     for (hi = ht->ht_array; todo > 0; ++hi)
4123     {
4124 	if (!HASHITEM_EMPTY(hi))
4125 	{
4126 	    --todo;
4127 	    kp_prev = NULL;
4128 	    for (kp = HI2KE(hi); kp != NULL; )
4129 	    {
4130 		if (kp->k_syn.id == id)
4131 		{
4132 		    kp_next = kp->ke_next;
4133 		    if (kp_prev == NULL)
4134 		    {
4135 			if (kp_next == NULL)
4136 			    hash_remove(ht, hi);
4137 			else
4138 			    hi->hi_key = KE2HIKEY(kp_next);
4139 		    }
4140 		    else
4141 			kp_prev->ke_next = kp_next;
4142 		    vim_free(kp->next_list);
4143 		    vim_free(kp->k_syn.cont_in_list);
4144 		    vim_free(kp);
4145 		    kp = kp_next;
4146 		}
4147 		else
4148 		{
4149 		    kp_prev = kp;
4150 		    kp = kp->ke_next;
4151 		}
4152 	    }
4153 	}
4154     }
4155     hash_unlock(ht);
4156 }
4157 
4158 /*
4159  * Clear a whole keyword table.
4160  */
4161     static void
4162 clear_keywtab(ht)
4163     hashtab_T	*ht;
4164 {
4165     hashitem_T	*hi;
4166     int		todo;
4167     keyentry_T	*kp;
4168     keyentry_T	*kp_next;
4169 
4170     todo = (int)ht->ht_used;
4171     for (hi = ht->ht_array; todo > 0; ++hi)
4172     {
4173 	if (!HASHITEM_EMPTY(hi))
4174 	{
4175 	    --todo;
4176 	    for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
4177 	    {
4178 		kp_next = kp->ke_next;
4179 		vim_free(kp->next_list);
4180 		vim_free(kp->k_syn.cont_in_list);
4181 		vim_free(kp);
4182 	    }
4183 	}
4184     }
4185     hash_clear(ht);
4186     hash_init(ht);
4187 }
4188 
4189 /*
4190  * Add a keyword to the list of keywords.
4191  */
4192     static void
4193 add_keyword(name, id, flags, cont_in_list, next_list)
4194     char_u	*name;	    /* name of keyword */
4195     int		id;	    /* group ID for this keyword */
4196     int		flags;	    /* flags for this keyword */
4197     short	*cont_in_list; /* containedin for this keyword */
4198     short	*next_list; /* nextgroup for this keyword */
4199 {
4200     keyentry_T	*kp;
4201     hashtab_T	*ht;
4202     hashitem_T	*hi;
4203     char_u	*name_ic;
4204     long_u	hash;
4205     char_u	name_folded[MAXKEYWLEN + 1];
4206 
4207     if (curbuf->b_syn_ic)
4208 	name_ic = str_foldcase(name, (int)STRLEN(name),
4209 						 name_folded, MAXKEYWLEN + 1);
4210     else
4211 	name_ic = name;
4212     kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4213     if (kp == NULL)
4214 	return;
4215     STRCPY(kp->keyword, name_ic);
4216     kp->k_syn.id = id;
4217     kp->k_syn.inc_tag = current_syn_inc_tag;
4218     kp->flags = flags;
4219     kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
4220     if (cont_in_list != NULL)
4221 	curbuf->b_syn_containedin = TRUE;
4222     kp->next_list = copy_id_list(next_list);
4223 
4224     if (curbuf->b_syn_ic)
4225 	ht = &curbuf->b_keywtab_ic;
4226     else
4227 	ht = &curbuf->b_keywtab;
4228 
4229     hash = hash_hash(kp->keyword);
4230     hi = hash_lookup(ht, kp->keyword, hash);
4231     if (HASHITEM_EMPTY(hi))
4232     {
4233 	/* new keyword, add to hashtable */
4234 	kp->ke_next = NULL;
4235 	hash_add_item(ht, hi, kp->keyword, hash);
4236     }
4237     else
4238     {
4239 	/* keyword already exists, prepend to list */
4240 	kp->ke_next = HI2KE(hi);
4241 	hi->hi_key = KE2HIKEY(kp);
4242     }
4243 }
4244 
4245 /*
4246  * Get the start and end of the group name argument.
4247  * Return a pointer to the first argument.
4248  * Return NULL if the end of the command was found instead of further args.
4249  */
4250     static char_u *
4251 get_group_name(arg, name_end)
4252     char_u	*arg;		/* start of the argument */
4253     char_u	**name_end;	/* pointer to end of the name */
4254 {
4255     char_u	*rest;
4256 
4257     *name_end = skiptowhite(arg);
4258     rest = skipwhite(*name_end);
4259 
4260     /*
4261      * Check if there are enough arguments.  The first argument may be a
4262      * pattern, where '|' is allowed, so only check for NUL.
4263      */
4264     if (ends_excmd(*arg) || *rest == NUL)
4265 	return NULL;
4266     return rest;
4267 }
4268 
4269 /*
4270  * Check for syntax command option arguments.
4271  * This can be called at any place in the list of arguments, and just picks
4272  * out the arguments that are known.  Can be called several times in a row to
4273  * collect all options in between other arguments.
4274  * Return a pointer to the next argument (which isn't an option).
4275  * Return NULL for any error;
4276  */
4277     static char_u *
4278 get_syn_options(arg, opt)
4279     char_u	    *arg;		/* next argument to be checked */
4280     syn_opt_arg_T   *opt;		/* various things */
4281 {
4282     char_u	*gname_start, *gname;
4283     int		syn_id;
4284     int		len;
4285     char	*p;
4286     int		i;
4287     int		fidx;
4288     static struct flag
4289     {
4290 	char	*name;
4291 	int	argtype;
4292 	int	flags;
4293     } flagtab[] = { {"cCoOnNtTaAiInNeEdD",	0,	HL_CONTAINED},
4294 		    {"oOnNeElLiInNeE",		0,	HL_ONELINE},
4295 		    {"kKeEeEpPeEnNdD",		0,	HL_KEEPEND},
4296 		    {"eExXtTeEnNdD",		0,	HL_EXTEND},
4297 		    {"eExXcClLuUdDeEnNlL",	0,	HL_EXCLUDENL},
4298 		    {"tTrRaAnNsSpPaArReEnNtT",	0,	HL_TRANSP},
4299 		    {"sSkKiIpPnNlL",		0,	HL_SKIPNL},
4300 		    {"sSkKiIpPwWhHiItTeE",	0,	HL_SKIPWHITE},
4301 		    {"sSkKiIpPeEmMpPtTyY",	0,	HL_SKIPEMPTY},
4302 		    {"gGrRoOuUpPhHeErReE",	0,	HL_SYNC_HERE},
4303 		    {"gGrRoOuUpPtThHeErReE",	0,	HL_SYNC_THERE},
4304 		    {"dDiIsSpPlLaAyY",		0,	HL_DISPLAY},
4305 		    {"fFoOlLdD",		0,	HL_FOLD},
4306 		    {"cCoOnNtTaAiInNsS",	1,	0},
4307 		    {"cCoOnNtTaAiInNeEdDiInN",	2,	0},
4308 		    {"nNeExXtTgGrRoOuUpP",	3,	0},
4309 		};
4310     static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
4311 
4312     if (arg == NULL)		/* already detected error */
4313 	return NULL;
4314 
4315     for (;;)
4316     {
4317 	/*
4318 	 * This is used very often when a large number of keywords is defined.
4319 	 * Need to skip quickly when no option name is found.
4320 	 * Also avoid tolower(), it's slow.
4321 	 */
4322 	if (strchr(first_letters, *arg) == NULL)
4323 	    break;
4324 
4325 	for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4326 	{
4327 	    p = flagtab[fidx].name;
4328 	    for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4329 		if (arg[len] != p[i] && arg[len] != p[i + 1])
4330 		    break;
4331 	    if (p[i] == NUL && (vim_iswhite(arg[len])
4332 				    || (flagtab[fidx].argtype > 0
4333 					 ? arg[len] == '='
4334 					 : ends_excmd(arg[len]))))
4335 	    {
4336 		if (opt->keyword
4337 			&& (flagtab[fidx].flags == HL_DISPLAY
4338 			    || flagtab[fidx].flags == HL_FOLD
4339 			    || flagtab[fidx].flags == HL_EXTEND))
4340 		    /* treat "display", "fold" and "extend" as a keyword */
4341 		    fidx = -1;
4342 		break;
4343 	    }
4344 	}
4345 	if (fidx < 0)	    /* no match found */
4346 	    break;
4347 
4348 	if (flagtab[fidx].argtype == 1)
4349 	{
4350 	    if (!opt->has_cont_list)
4351 	    {
4352 		EMSG(_("E395: contains argument not accepted here"));
4353 		return NULL;
4354 	    }
4355 	    if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
4356 		return NULL;
4357 	}
4358 	else if (flagtab[fidx].argtype == 2)
4359 	{
4360 #if 0	    /* cannot happen */
4361 	    if (opt->cont_in_list == NULL)
4362 	    {
4363 		EMSG(_("E396: containedin argument not accepted here"));
4364 		return NULL;
4365 	    }
4366 #endif
4367 	    if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
4368 		return NULL;
4369 	}
4370 	else if (flagtab[fidx].argtype == 3)
4371 	{
4372 	    if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
4373 		return NULL;
4374 	}
4375 	else
4376 	{
4377 	    opt->flags |= flagtab[fidx].flags;
4378 	    arg = skipwhite(arg + len);
4379 
4380 	    if (flagtab[fidx].flags == HL_SYNC_HERE
4381 		    || flagtab[fidx].flags == HL_SYNC_THERE)
4382 	    {
4383 		if (opt->sync_idx == NULL)
4384 		{
4385 		    EMSG(_("E393: group[t]here not accepted here"));
4386 		    return NULL;
4387 		}
4388 		gname_start = arg;
4389 		arg = skiptowhite(arg);
4390 		if (gname_start == arg)
4391 		    return NULL;
4392 		gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4393 		if (gname == NULL)
4394 		    return NULL;
4395 		if (STRCMP(gname, "NONE") == 0)
4396 		    *opt->sync_idx = NONE_IDX;
4397 		else
4398 		{
4399 		    syn_id = syn_name2id(gname);
4400 		    for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4401 			if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4402 			      && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4403 			{
4404 			    *opt->sync_idx = i;
4405 			    break;
4406 			}
4407 		    if (i < 0)
4408 		    {
4409 			EMSG2(_("E394: Didn't find region item for %s"), gname);
4410 			vim_free(gname);
4411 			return NULL;
4412 		    }
4413 		}
4414 
4415 		vim_free(gname);
4416 		arg = skipwhite(arg);
4417 	    }
4418 #ifdef FEAT_FOLDING
4419 	    else if (flagtab[fidx].flags == HL_FOLD
4420 						&& foldmethodIsSyntax(curwin))
4421 		/* Need to update folds later. */
4422 		foldUpdateAll(curwin);
4423 #endif
4424 	}
4425     }
4426 
4427     return arg;
4428 }
4429 
4430 /*
4431  * Adjustments to syntax item when declared in a ":syn include"'d file.
4432  * Set the contained flag, and if the item is not already contained, add it
4433  * to the specified top-level group, if any.
4434  */
4435     static void
4436 syn_incl_toplevel(id, flagsp)
4437     int		id;
4438     int		*flagsp;
4439 {
4440     if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4441 	return;
4442     *flagsp |= HL_CONTAINED;
4443     if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4444     {
4445 	/* We have to alloc this, because syn_combine_list() will free it. */
4446 	short	    *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4447 	int	    tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4448 
4449 	if (grp_list != NULL)
4450 	{
4451 	    grp_list[0] = id;
4452 	    grp_list[1] = 0;
4453 	    syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4454 			 CLUSTER_ADD);
4455 	}
4456     }
4457 }
4458 
4459 /*
4460  * Handle ":syntax include [@{group-name}] filename" command.
4461  */
4462     static void
4463 syn_cmd_include(eap, syncing)
4464     exarg_T	*eap;
4465     int		syncing UNUSED;
4466 {
4467     char_u	*arg = eap->arg;
4468     int		sgl_id = 1;
4469     char_u	*group_name_end;
4470     char_u	*rest;
4471     char_u	*errormsg = NULL;
4472     int		prev_toplvl_grp;
4473     int		prev_syn_inc_tag;
4474     int		source = FALSE;
4475 
4476     eap->nextcmd = find_nextcmd(arg);
4477     if (eap->skip)
4478 	return;
4479 
4480     if (arg[0] == '@')
4481     {
4482 	++arg;
4483 	rest = get_group_name(arg, &group_name_end);
4484 	if (rest == NULL)
4485 	{
4486 	    EMSG((char_u *)_("E397: Filename required"));
4487 	    return;
4488 	}
4489 	sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4490 	/* separate_nextcmd() and expand_filename() depend on this */
4491 	eap->arg = rest;
4492     }
4493 
4494     /*
4495      * Everything that's left, up to the next command, should be the
4496      * filename to include.
4497      */
4498     eap->argt |= (XFILE | NOSPC);
4499     separate_nextcmd(eap);
4500     if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4501     {
4502 	/* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4503 	 * file.  Need to expand the file name first.  In other cases
4504 	 * ":runtime!" is used. */
4505 	source = TRUE;
4506 	if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4507 	{
4508 	    if (errormsg != NULL)
4509 		EMSG(errormsg);
4510 	    return;
4511 	}
4512     }
4513 
4514     /*
4515      * Save and restore the existing top-level grouplist id and ":syn
4516      * include" tag around the actual inclusion.
4517      */
4518     prev_syn_inc_tag = current_syn_inc_tag;
4519     current_syn_inc_tag = ++running_syn_inc_tag;
4520     prev_toplvl_grp = curbuf->b_syn_topgrp;
4521     curbuf->b_syn_topgrp = sgl_id;
4522     if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4523 				: source_runtime(eap->arg, TRUE) == FAIL)
4524 	EMSG2(_(e_notopen), eap->arg);
4525     curbuf->b_syn_topgrp = prev_toplvl_grp;
4526     current_syn_inc_tag = prev_syn_inc_tag;
4527 }
4528 
4529 /*
4530  * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4531  */
4532     static void
4533 syn_cmd_keyword(eap, syncing)
4534     exarg_T	*eap;
4535     int		syncing UNUSED;
4536 {
4537     char_u	*arg = eap->arg;
4538     char_u	*group_name_end;
4539     int		syn_id;
4540     char_u	*rest;
4541     char_u	*keyword_copy;
4542     char_u	*p;
4543     char_u	*kw;
4544     syn_opt_arg_T syn_opt_arg;
4545     int		cnt;
4546 
4547     rest = get_group_name(arg, &group_name_end);
4548 
4549     if (rest != NULL)
4550     {
4551 	syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4552 
4553 	/* allocate a buffer, for removing the backslashes in the keyword */
4554 	keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4555 	if (keyword_copy != NULL)
4556 	{
4557 	    syn_opt_arg.flags = 0;
4558 	    syn_opt_arg.keyword = TRUE;
4559 	    syn_opt_arg.sync_idx = NULL;
4560 	    syn_opt_arg.has_cont_list = FALSE;
4561 	    syn_opt_arg.cont_in_list = NULL;
4562 	    syn_opt_arg.next_list = NULL;
4563 
4564 	    /*
4565 	     * The options given apply to ALL keywords, so all options must be
4566 	     * found before keywords can be created.
4567 	     * 1: collect the options and copy the keywords to keyword_copy.
4568 	     */
4569 	    cnt = 0;
4570 	    p = keyword_copy;
4571 	    for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
4572 	    {
4573 		rest = get_syn_options(rest, &syn_opt_arg);
4574 		if (rest == NULL || ends_excmd(*rest))
4575 		    break;
4576 		/* Copy the keyword, removing backslashes, and add a NUL. */
4577 		while (*rest != NUL && !vim_iswhite(*rest))
4578 		{
4579 		    if (*rest == '\\' && rest[1] != NUL)
4580 			++rest;
4581 		    *p++ = *rest++;
4582 		}
4583 		*p++ = NUL;
4584 		++cnt;
4585 	    }
4586 
4587 	    if (!eap->skip)
4588 	    {
4589 		/* Adjust flags for use of ":syn include". */
4590 		syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4591 
4592 		/*
4593 		 * 2: Add an entry for each keyword.
4594 		 */
4595 		for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4596 		{
4597 		    for (p = vim_strchr(kw, '['); ; )
4598 		    {
4599 			if (p != NULL)
4600 			    *p = NUL;
4601 			add_keyword(kw, syn_id, syn_opt_arg.flags,
4602 						     syn_opt_arg.cont_in_list,
4603 						       syn_opt_arg.next_list);
4604 			if (p == NULL)
4605 			    break;
4606 			if (p[1] == NUL)
4607 			{
4608 			    EMSG2(_("E789: Missing ']': %s"), kw);
4609 			    kw = p + 2;		/* skip over the NUL */
4610 			    break;
4611 			}
4612 			if (p[1] == ']')
4613 			{
4614 			    kw = p + 1;		/* skip over the "]" */
4615 			    break;
4616 			}
4617 #ifdef FEAT_MBYTE
4618 			if (has_mbyte)
4619 			{
4620 			    int l = (*mb_ptr2len)(p + 1);
4621 
4622 			    mch_memmove(p, p + 1, l);
4623 			    p += l;
4624 			}
4625 			else
4626 #endif
4627 			{
4628 			    p[0] = p[1];
4629 			    ++p;
4630 			}
4631 		    }
4632 		}
4633 	    }
4634 
4635 	    vim_free(keyword_copy);
4636 	    vim_free(syn_opt_arg.cont_in_list);
4637 	    vim_free(syn_opt_arg.next_list);
4638 	}
4639     }
4640 
4641     if (rest != NULL)
4642 	eap->nextcmd = check_nextcmd(rest);
4643     else
4644 	EMSG2(_(e_invarg2), arg);
4645 
4646     redraw_curbuf_later(SOME_VALID);
4647     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
4648 }
4649 
4650 /*
4651  * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4652  *
4653  * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4654  */
4655     static void
4656 syn_cmd_match(eap, syncing)
4657     exarg_T	*eap;
4658     int		syncing;	    /* TRUE for ":syntax sync match .. " */
4659 {
4660     char_u	*arg = eap->arg;
4661     char_u	*group_name_end;
4662     char_u	*rest;
4663     synpat_T	item;		/* the item found in the line */
4664     int		syn_id;
4665     int		idx;
4666     syn_opt_arg_T syn_opt_arg;
4667     int		sync_idx = 0;
4668 
4669     /* Isolate the group name, check for validity */
4670     rest = get_group_name(arg, &group_name_end);
4671 
4672     /* Get options before the pattern */
4673     syn_opt_arg.flags = 0;
4674     syn_opt_arg.keyword = FALSE;
4675     syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4676     syn_opt_arg.has_cont_list = TRUE;
4677     syn_opt_arg.cont_list = NULL;
4678     syn_opt_arg.cont_in_list = NULL;
4679     syn_opt_arg.next_list = NULL;
4680     rest = get_syn_options(rest, &syn_opt_arg);
4681 
4682     /* get the pattern. */
4683     init_syn_patterns();
4684     vim_memset(&item, 0, sizeof(item));
4685     rest = get_syn_pattern(rest, &item);
4686     if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4687 	syn_opt_arg.flags |= HL_HAS_EOL;
4688 
4689     /* Get options after the pattern */
4690     rest = get_syn_options(rest, &syn_opt_arg);
4691 
4692     if (rest != NULL)		/* all arguments are valid */
4693     {
4694 	/*
4695 	 * Check for trailing command and illegal trailing arguments.
4696 	 */
4697 	eap->nextcmd = check_nextcmd(rest);
4698 	if (!ends_excmd(*rest) || eap->skip)
4699 	    rest = NULL;
4700 	else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4701 		&& (syn_id = syn_check_group(arg,
4702 					   (int)(group_name_end - arg))) != 0)
4703 	{
4704 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4705 	    /*
4706 	     * Store the pattern in the syn_items list
4707 	     */
4708 	    idx = curbuf->b_syn_patterns.ga_len;
4709 	    SYN_ITEMS(curbuf)[idx] = item;
4710 	    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4711 	    SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4712 	    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4713 	    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4714 	    SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
4715 	    SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
4716 	    SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4717 	    SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4718 						     syn_opt_arg.cont_in_list;
4719 	    if (syn_opt_arg.cont_in_list != NULL)
4720 		curbuf->b_syn_containedin = TRUE;
4721 	    SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
4722 	    ++curbuf->b_syn_patterns.ga_len;
4723 
4724 	    /* remember that we found a match for syncing on */
4725 	    if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4726 		curbuf->b_syn_sync_flags |= SF_MATCH;
4727 #ifdef FEAT_FOLDING
4728 	    if (syn_opt_arg.flags & HL_FOLD)
4729 		++curbuf->b_syn_folditems;
4730 #endif
4731 
4732 	    redraw_curbuf_later(SOME_VALID);
4733 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4734 	    return;	/* don't free the progs and patterns now */
4735 	}
4736     }
4737 
4738     /*
4739      * Something failed, free the allocated memory.
4740      */
4741     vim_free(item.sp_prog);
4742     vim_free(item.sp_pattern);
4743     vim_free(syn_opt_arg.cont_list);
4744     vim_free(syn_opt_arg.cont_in_list);
4745     vim_free(syn_opt_arg.next_list);
4746 
4747     if (rest == NULL)
4748 	EMSG2(_(e_invarg2), arg);
4749 }
4750 
4751 /*
4752  * Handle ":syntax region {group-name} [matchgroup={group-name}]
4753  *		start {start} .. [skip {skip}] end {end} .. [{options}]".
4754  */
4755     static void
4756 syn_cmd_region(eap, syncing)
4757     exarg_T	*eap;
4758     int		syncing;	    /* TRUE for ":syntax sync region .." */
4759 {
4760     char_u		*arg = eap->arg;
4761     char_u		*group_name_end;
4762     char_u		*rest;			/* next arg, NULL on error */
4763     char_u		*key_end;
4764     char_u		*key = NULL;
4765     char_u		*p;
4766     int			item;
4767 #define ITEM_START	    0
4768 #define ITEM_SKIP	    1
4769 #define ITEM_END	    2
4770 #define ITEM_MATCHGROUP	    3
4771     struct pat_ptr
4772     {
4773 	synpat_T	*pp_synp;		/* pointer to syn_pattern */
4774 	int		pp_matchgroup_id;	/* matchgroup ID */
4775 	struct pat_ptr	*pp_next;		/* pointer to next pat_ptr */
4776     }			*(pat_ptrs[3]);
4777 					/* patterns found in the line */
4778     struct pat_ptr	*ppp;
4779     struct pat_ptr	*ppp_next;
4780     int			pat_count = 0;		/* nr of syn_patterns found */
4781     int			syn_id;
4782     int			matchgroup_id = 0;
4783     int			not_enough = FALSE;	/* not enough arguments */
4784     int			illegal = FALSE;	/* illegal arguments */
4785     int			success = FALSE;
4786     int			idx;
4787     syn_opt_arg_T	syn_opt_arg;
4788 
4789     /* Isolate the group name, check for validity */
4790     rest = get_group_name(arg, &group_name_end);
4791 
4792     pat_ptrs[0] = NULL;
4793     pat_ptrs[1] = NULL;
4794     pat_ptrs[2] = NULL;
4795 
4796     init_syn_patterns();
4797 
4798     syn_opt_arg.flags = 0;
4799     syn_opt_arg.keyword = FALSE;
4800     syn_opt_arg.sync_idx = NULL;
4801     syn_opt_arg.has_cont_list = TRUE;
4802     syn_opt_arg.cont_list = NULL;
4803     syn_opt_arg.cont_in_list = NULL;
4804     syn_opt_arg.next_list = NULL;
4805 
4806     /*
4807      * get the options, patterns and matchgroup.
4808      */
4809     while (rest != NULL && !ends_excmd(*rest))
4810     {
4811 	/* Check for option arguments */
4812 	rest = get_syn_options(rest, &syn_opt_arg);
4813 	if (rest == NULL || ends_excmd(*rest))
4814 	    break;
4815 
4816 	/* must be a pattern or matchgroup then */
4817 	key_end = rest;
4818 	while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4819 	    ++key_end;
4820 	vim_free(key);
4821 	key = vim_strnsave_up(rest, (int)(key_end - rest));
4822 	if (key == NULL)			/* out of memory */
4823 	{
4824 	    rest = NULL;
4825 	    break;
4826 	}
4827 	if (STRCMP(key, "MATCHGROUP") == 0)
4828 	    item = ITEM_MATCHGROUP;
4829 	else if (STRCMP(key, "START") == 0)
4830 	    item = ITEM_START;
4831 	else if (STRCMP(key, "END") == 0)
4832 	    item = ITEM_END;
4833 	else if (STRCMP(key, "SKIP") == 0)
4834 	{
4835 	    if (pat_ptrs[ITEM_SKIP] != NULL)	/* one skip pattern allowed */
4836 	    {
4837 		illegal = TRUE;
4838 		break;
4839 	    }
4840 	    item = ITEM_SKIP;
4841 	}
4842 	else
4843 	    break;
4844 	rest = skipwhite(key_end);
4845 	if (*rest != '=')
4846 	{
4847 	    rest = NULL;
4848 	    EMSG2(_("E398: Missing '=': %s"), arg);
4849 	    break;
4850 	}
4851 	rest = skipwhite(rest + 1);
4852 	if (*rest == NUL)
4853 	{
4854 	    not_enough = TRUE;
4855 	    break;
4856 	}
4857 
4858 	if (item == ITEM_MATCHGROUP)
4859 	{
4860 	    p = skiptowhite(rest);
4861 	    if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4862 		matchgroup_id = 0;
4863 	    else
4864 	    {
4865 		matchgroup_id = syn_check_group(rest, (int)(p - rest));
4866 		if (matchgroup_id == 0)
4867 		{
4868 		    illegal = TRUE;
4869 		    break;
4870 		}
4871 	    }
4872 	    rest = skipwhite(p);
4873 	}
4874 	else
4875 	{
4876 	    /*
4877 	     * Allocate room for a syn_pattern, and link it in the list of
4878 	     * syn_patterns for this item, at the start (because the list is
4879 	     * used from end to start).
4880 	     */
4881 	    ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4882 	    if (ppp == NULL)
4883 	    {
4884 		rest = NULL;
4885 		break;
4886 	    }
4887 	    ppp->pp_next = pat_ptrs[item];
4888 	    pat_ptrs[item] = ppp;
4889 	    ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4890 	    if (ppp->pp_synp == NULL)
4891 	    {
4892 		rest = NULL;
4893 		break;
4894 	    }
4895 
4896 	    /*
4897 	     * Get the syntax pattern and the following offset(s).
4898 	     */
4899 	    /* Enable the appropriate \z specials. */
4900 	    if (item == ITEM_START)
4901 		reg_do_extmatch = REX_SET;
4902 	    else if (item == ITEM_SKIP || item == ITEM_END)
4903 		reg_do_extmatch = REX_USE;
4904 	    rest = get_syn_pattern(rest, ppp->pp_synp);
4905 	    reg_do_extmatch = 0;
4906 	    if (item == ITEM_END && vim_regcomp_had_eol()
4907 				       && !(syn_opt_arg.flags & HL_EXCLUDENL))
4908 		ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4909 	    ppp->pp_matchgroup_id = matchgroup_id;
4910 	    ++pat_count;
4911 	}
4912     }
4913     vim_free(key);
4914     if (illegal || not_enough)
4915 	rest = NULL;
4916 
4917     /*
4918      * Must have a "start" and "end" pattern.
4919      */
4920     if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4921 						  pat_ptrs[ITEM_END] == NULL))
4922     {
4923 	not_enough = TRUE;
4924 	rest = NULL;
4925     }
4926 
4927     if (rest != NULL)
4928     {
4929 	/*
4930 	 * Check for trailing garbage or command.
4931 	 * If OK, add the item.
4932 	 */
4933 	eap->nextcmd = check_nextcmd(rest);
4934 	if (!ends_excmd(*rest) || eap->skip)
4935 	    rest = NULL;
4936 	else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4937 		&& (syn_id = syn_check_group(arg,
4938 					   (int)(group_name_end - arg))) != 0)
4939 	{
4940 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4941 	    /*
4942 	     * Store the start/skip/end in the syn_items list
4943 	     */
4944 	    idx = curbuf->b_syn_patterns.ga_len;
4945 	    for (item = ITEM_START; item <= ITEM_END; ++item)
4946 	    {
4947 		for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4948 		{
4949 		    SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4950 		    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4951 		    SYN_ITEMS(curbuf)[idx].sp_type =
4952 			    (item == ITEM_START) ? SPTYPE_START :
4953 			    (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
4954 		    SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
4955 		    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4956 		    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4957 		    SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4958 							ppp->pp_matchgroup_id;
4959 		    if (item == ITEM_START)
4960 		    {
4961 			SYN_ITEMS(curbuf)[idx].sp_cont_list =
4962 							syn_opt_arg.cont_list;
4963 			SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4964 						     syn_opt_arg.cont_in_list;
4965 			if (syn_opt_arg.cont_in_list != NULL)
4966 			    curbuf->b_syn_containedin = TRUE;
4967 			SYN_ITEMS(curbuf)[idx].sp_next_list =
4968 							syn_opt_arg.next_list;
4969 		    }
4970 		    ++curbuf->b_syn_patterns.ga_len;
4971 		    ++idx;
4972 #ifdef FEAT_FOLDING
4973 		    if (syn_opt_arg.flags & HL_FOLD)
4974 			++curbuf->b_syn_folditems;
4975 #endif
4976 		}
4977 	    }
4978 
4979 	    redraw_curbuf_later(SOME_VALID);
4980 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4981 	    success = TRUE;	    /* don't free the progs and patterns now */
4982 	}
4983     }
4984 
4985     /*
4986      * Free the allocated memory.
4987      */
4988     for (item = ITEM_START; item <= ITEM_END; ++item)
4989 	for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4990 	{
4991 	    if (!success)
4992 	    {
4993 		vim_free(ppp->pp_synp->sp_prog);
4994 		vim_free(ppp->pp_synp->sp_pattern);
4995 	    }
4996 	    vim_free(ppp->pp_synp);
4997 	    ppp_next = ppp->pp_next;
4998 	    vim_free(ppp);
4999 	}
5000 
5001     if (!success)
5002     {
5003 	vim_free(syn_opt_arg.cont_list);
5004 	vim_free(syn_opt_arg.cont_in_list);
5005 	vim_free(syn_opt_arg.next_list);
5006 	if (not_enough)
5007 	    EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5008 	else if (illegal || rest == NULL)
5009 	    EMSG2(_(e_invarg2), arg);
5010     }
5011 }
5012 
5013 /*
5014  * A simple syntax group ID comparison function suitable for use in qsort()
5015  */
5016     static int
5017 #ifdef __BORLANDC__
5018 _RTLENTRYF
5019 #endif
5020 syn_compare_stub(v1, v2)
5021     const void	*v1;
5022     const void	*v2;
5023 {
5024     const short	*s1 = v1;
5025     const short	*s2 = v2;
5026 
5027     return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5028 }
5029 
5030 /*
5031  * Combines lists of syntax clusters.
5032  * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5033  */
5034     static void
5035 syn_combine_list(clstr1, clstr2, list_op)
5036     short	**clstr1;
5037     short	**clstr2;
5038     int		list_op;
5039 {
5040     int		count1 = 0;
5041     int		count2 = 0;
5042     short	*g1;
5043     short	*g2;
5044     short	*clstr = NULL;
5045     int		count;
5046     int		round;
5047 
5048     /*
5049      * Handle degenerate cases.
5050      */
5051     if (*clstr2 == NULL)
5052 	return;
5053     if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5054     {
5055 	if (list_op == CLUSTER_REPLACE)
5056 	    vim_free(*clstr1);
5057 	if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5058 	    *clstr1 = *clstr2;
5059 	else
5060 	    vim_free(*clstr2);
5061 	return;
5062     }
5063 
5064     for (g1 = *clstr1; *g1; g1++)
5065 	++count1;
5066     for (g2 = *clstr2; *g2; g2++)
5067 	++count2;
5068 
5069     /*
5070      * For speed purposes, sort both lists.
5071      */
5072     qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5073     qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5074 
5075     /*
5076      * We proceed in two passes; in round 1, we count the elements to place
5077      * in the new list, and in round 2, we allocate and populate the new
5078      * list.  For speed, we use a mergesort-like method, adding the smaller
5079      * of the current elements in each list to the new list.
5080      */
5081     for (round = 1; round <= 2; round++)
5082     {
5083 	g1 = *clstr1;
5084 	g2 = *clstr2;
5085 	count = 0;
5086 
5087 	/*
5088 	 * First, loop through the lists until one of them is empty.
5089 	 */
5090 	while (*g1 && *g2)
5091 	{
5092 	    /*
5093 	     * We always want to add from the first list.
5094 	     */
5095 	    if (*g1 < *g2)
5096 	    {
5097 		if (round == 2)
5098 		    clstr[count] = *g1;
5099 		count++;
5100 		g1++;
5101 		continue;
5102 	    }
5103 	    /*
5104 	     * We only want to add from the second list if we're adding the
5105 	     * lists.
5106 	     */
5107 	    if (list_op == CLUSTER_ADD)
5108 	    {
5109 		if (round == 2)
5110 		    clstr[count] = *g2;
5111 		count++;
5112 	    }
5113 	    if (*g1 == *g2)
5114 		g1++;
5115 	    g2++;
5116 	}
5117 
5118 	/*
5119 	 * Now add the leftovers from whichever list didn't get finished
5120 	 * first.  As before, we only want to add from the second list if
5121 	 * we're adding the lists.
5122 	 */
5123 	for (; *g1; g1++, count++)
5124 	    if (round == 2)
5125 		clstr[count] = *g1;
5126 	if (list_op == CLUSTER_ADD)
5127 	    for (; *g2; g2++, count++)
5128 		if (round == 2)
5129 		    clstr[count] = *g2;
5130 
5131 	if (round == 1)
5132 	{
5133 	    /*
5134 	     * If the group ended up empty, we don't need to allocate any
5135 	     * space for it.
5136 	     */
5137 	    if (count == 0)
5138 	    {
5139 		clstr = NULL;
5140 		break;
5141 	    }
5142 	    clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5143 	    if (clstr == NULL)
5144 		break;
5145 	    clstr[count] = 0;
5146 	}
5147     }
5148 
5149     /*
5150      * Finally, put the new list in place.
5151      */
5152     vim_free(*clstr1);
5153     vim_free(*clstr2);
5154     *clstr1 = clstr;
5155 }
5156 
5157 /*
5158  * Lookup a syntax cluster name and return it's ID.
5159  * If it is not found, 0 is returned.
5160  */
5161     static int
5162 syn_scl_name2id(name)
5163     char_u	*name;
5164 {
5165     int		i;
5166     char_u	*name_u;
5167 
5168     /* Avoid using stricmp() too much, it's slow on some systems */
5169     name_u = vim_strsave_up(name);
5170     if (name_u == NULL)
5171 	return 0;
5172     for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5173 	if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5174 		&& STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5175 	    break;
5176     vim_free(name_u);
5177     return (i < 0 ? 0 : i + SYNID_CLUSTER);
5178 }
5179 
5180 /*
5181  * Like syn_scl_name2id(), but take a pointer + length argument.
5182  */
5183     static int
5184 syn_scl_namen2id(linep, len)
5185     char_u  *linep;
5186     int	    len;
5187 {
5188     char_u  *name;
5189     int	    id = 0;
5190 
5191     name = vim_strnsave(linep, len);
5192     if (name != NULL)
5193     {
5194 	id = syn_scl_name2id(name);
5195 	vim_free(name);
5196     }
5197     return id;
5198 }
5199 
5200 /*
5201  * Find syntax cluster name in the table and return it's ID.
5202  * The argument is a pointer to the name and the length of the name.
5203  * If it doesn't exist yet, a new entry is created.
5204  * Return 0 for failure.
5205  */
5206     static int
5207 syn_check_cluster(pp, len)
5208     char_u	*pp;
5209     int		len;
5210 {
5211     int		id;
5212     char_u	*name;
5213 
5214     name = vim_strnsave(pp, len);
5215     if (name == NULL)
5216 	return 0;
5217 
5218     id = syn_scl_name2id(name);
5219     if (id == 0)			/* doesn't exist yet */
5220 	id = syn_add_cluster(name);
5221     else
5222 	vim_free(name);
5223     return id;
5224 }
5225 
5226 /*
5227  * Add new syntax cluster and return it's ID.
5228  * "name" must be an allocated string, it will be consumed.
5229  * Return 0 for failure.
5230  */
5231     static int
5232 syn_add_cluster(name)
5233     char_u	*name;
5234 {
5235     int		len;
5236 
5237     /*
5238      * First call for this growarray: init growing array.
5239      */
5240     if (curbuf->b_syn_clusters.ga_data == NULL)
5241     {
5242 	curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5243 	curbuf->b_syn_clusters.ga_growsize = 10;
5244     }
5245 
5246     /*
5247      * Make room for at least one other cluster entry.
5248      */
5249     if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5250     {
5251 	vim_free(name);
5252 	return 0;
5253     }
5254     len = curbuf->b_syn_clusters.ga_len;
5255 
5256     vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
5257     SYN_CLSTR(curbuf)[len].scl_name = name;
5258     SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5259     SYN_CLSTR(curbuf)[len].scl_list = NULL;
5260     ++curbuf->b_syn_clusters.ga_len;
5261 
5262     if (STRICMP(name, "Spell") == 0)
5263 	curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
5264     if (STRICMP(name, "NoSpell") == 0)
5265 	curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
5266 
5267     return len + SYNID_CLUSTER;
5268 }
5269 
5270 /*
5271  * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5272  *		[add={groupname},..] [remove={groupname},..]".
5273  */
5274     static void
5275 syn_cmd_cluster(eap, syncing)
5276     exarg_T	*eap;
5277     int		syncing UNUSED;
5278 {
5279     char_u	*arg = eap->arg;
5280     char_u	*group_name_end;
5281     char_u	*rest;
5282     int		scl_id;
5283     short	*clstr_list;
5284     int		got_clstr = FALSE;
5285     int		opt_len;
5286     int		list_op;
5287 
5288     eap->nextcmd = find_nextcmd(arg);
5289     if (eap->skip)
5290 	return;
5291 
5292     rest = get_group_name(arg, &group_name_end);
5293 
5294     if (rest != NULL)
5295     {
5296 	scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
5297 							      - SYNID_CLUSTER;
5298 
5299 	for (;;)
5300 	{
5301 	    if (STRNICMP(rest, "add", 3) == 0
5302 		    && (vim_iswhite(rest[3]) || rest[3] == '='))
5303 	    {
5304 		opt_len = 3;
5305 		list_op = CLUSTER_ADD;
5306 	    }
5307 	    else if (STRNICMP(rest, "remove", 6) == 0
5308 		    && (vim_iswhite(rest[6]) || rest[6] == '='))
5309 	    {
5310 		opt_len = 6;
5311 		list_op = CLUSTER_SUBTRACT;
5312 	    }
5313 	    else if (STRNICMP(rest, "contains", 8) == 0
5314 			&& (vim_iswhite(rest[8]) || rest[8] == '='))
5315 	    {
5316 		opt_len = 8;
5317 		list_op = CLUSTER_REPLACE;
5318 	    }
5319 	    else
5320 		break;
5321 
5322 	    clstr_list = NULL;
5323 	    if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5324 	    {
5325 		EMSG2(_(e_invarg2), rest);
5326 		break;
5327 	    }
5328 	    syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5329 			     &clstr_list, list_op);
5330 	    got_clstr = TRUE;
5331 	}
5332 
5333 	if (got_clstr)
5334 	{
5335 	    redraw_curbuf_later(SOME_VALID);
5336 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5337 	}
5338     }
5339 
5340     if (!got_clstr)
5341 	EMSG(_("E400: No cluster specified"));
5342     if (rest == NULL || !ends_excmd(*rest))
5343 	EMSG2(_(e_invarg2), arg);
5344 }
5345 
5346 /*
5347  * On first call for current buffer: Init growing array.
5348  */
5349     static void
5350 init_syn_patterns()
5351 {
5352     curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5353     curbuf->b_syn_patterns.ga_growsize = 10;
5354 }
5355 
5356 /*
5357  * Get one pattern for a ":syntax match" or ":syntax region" command.
5358  * Stores the pattern and program in a synpat_T.
5359  * Returns a pointer to the next argument, or NULL in case of an error.
5360  */
5361     static char_u *
5362 get_syn_pattern(arg, ci)
5363     char_u	*arg;
5364     synpat_T	*ci;
5365 {
5366     char_u	*end;
5367     int		*p;
5368     int		idx;
5369     char_u	*cpo_save;
5370 
5371     /* need at least three chars */
5372     if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5373 	return NULL;
5374 
5375     end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5376     if (*end != *arg)			    /* end delimiter not found */
5377     {
5378 	EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5379 	return NULL;
5380     }
5381     /* store the pattern and compiled regexp program */
5382     if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5383 	return NULL;
5384 
5385     /* Make 'cpoptions' empty, to avoid the 'l' flag */
5386     cpo_save = p_cpo;
5387     p_cpo = (char_u *)"";
5388     ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5389     p_cpo = cpo_save;
5390 
5391     if (ci->sp_prog == NULL)
5392 	return NULL;
5393     ci->sp_ic = curbuf->b_syn_ic;
5394 
5395     /*
5396      * Check for a match, highlight or region offset.
5397      */
5398     ++end;
5399     do
5400     {
5401 	for (idx = SPO_COUNT; --idx >= 0; )
5402 	    if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5403 		break;
5404 	if (idx >= 0)
5405 	{
5406 	    p = &(ci->sp_offsets[idx]);
5407 	    if (idx != SPO_LC_OFF)
5408 		switch (end[3])
5409 		{
5410 		    case 's':   break;
5411 		    case 'b':   break;
5412 		    case 'e':   idx += SPO_COUNT; break;
5413 		    default:    idx = -1; break;
5414 		}
5415 	    if (idx >= 0)
5416 	    {
5417 		ci->sp_off_flags |= (1 << idx);
5418 		if (idx == SPO_LC_OFF)	    /* lc=99 */
5419 		{
5420 		    end += 3;
5421 		    *p = getdigits(&end);
5422 
5423 		    /* "lc=" offset automatically sets "ms=" offset */
5424 		    if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5425 		    {
5426 			ci->sp_off_flags |= (1 << SPO_MS_OFF);
5427 			ci->sp_offsets[SPO_MS_OFF] = *p;
5428 		    }
5429 		}
5430 		else			    /* yy=x+99 */
5431 		{
5432 		    end += 4;
5433 		    if (*end == '+')
5434 		    {
5435 			++end;
5436 			*p = getdigits(&end);		/* positive offset */
5437 		    }
5438 		    else if (*end == '-')
5439 		    {
5440 			++end;
5441 			*p = -getdigits(&end);		/* negative offset */
5442 		    }
5443 		}
5444 		if (*end != ',')
5445 		    break;
5446 		++end;
5447 	    }
5448 	}
5449     } while (idx >= 0);
5450 
5451     if (!ends_excmd(*end) && !vim_iswhite(*end))
5452     {
5453 	EMSG2(_("E402: Garbage after pattern: %s"), arg);
5454 	return NULL;
5455     }
5456     return skipwhite(end);
5457 }
5458 
5459 /*
5460  * Handle ":syntax sync .." command.
5461  */
5462     static void
5463 syn_cmd_sync(eap, syncing)
5464     exarg_T	*eap;
5465     int		syncing UNUSED;
5466 {
5467     char_u	*arg_start = eap->arg;
5468     char_u	*arg_end;
5469     char_u	*key = NULL;
5470     char_u	*next_arg;
5471     int		illegal = FALSE;
5472     int		finished = FALSE;
5473     long	n;
5474     char_u	*cpo_save;
5475 
5476     if (ends_excmd(*arg_start))
5477     {
5478 	syn_cmd_list(eap, TRUE);
5479 	return;
5480     }
5481 
5482     while (!ends_excmd(*arg_start))
5483     {
5484 	arg_end = skiptowhite(arg_start);
5485 	next_arg = skipwhite(arg_end);
5486 	vim_free(key);
5487 	key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5488 	if (STRCMP(key, "CCOMMENT") == 0)
5489 	{
5490 	    if (!eap->skip)
5491 		curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5492 	    if (!ends_excmd(*next_arg))
5493 	    {
5494 		arg_end = skiptowhite(next_arg);
5495 		if (!eap->skip)
5496 		    curbuf->b_syn_sync_id = syn_check_group(next_arg,
5497 						   (int)(arg_end - next_arg));
5498 		next_arg = skipwhite(arg_end);
5499 	    }
5500 	    else if (!eap->skip)
5501 		curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5502 	}
5503 	else if (  STRNCMP(key, "LINES", 5) == 0
5504 		|| STRNCMP(key, "MINLINES", 8) == 0
5505 		|| STRNCMP(key, "MAXLINES", 8) == 0
5506 		|| STRNCMP(key, "LINEBREAKS", 10) == 0)
5507 	{
5508 	    if (key[4] == 'S')
5509 		arg_end = key + 6;
5510 	    else if (key[0] == 'L')
5511 		arg_end = key + 11;
5512 	    else
5513 		arg_end = key + 9;
5514 	    if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5515 	    {
5516 		illegal = TRUE;
5517 		break;
5518 	    }
5519 	    n = getdigits(&arg_end);
5520 	    if (!eap->skip)
5521 	    {
5522 		if (key[4] == 'B')
5523 		    curbuf->b_syn_sync_linebreaks = n;
5524 		else if (key[1] == 'A')
5525 		    curbuf->b_syn_sync_maxlines = n;
5526 		else
5527 		    curbuf->b_syn_sync_minlines = n;
5528 	    }
5529 	}
5530 	else if (STRCMP(key, "FROMSTART") == 0)
5531 	{
5532 	    if (!eap->skip)
5533 	    {
5534 		curbuf->b_syn_sync_minlines = MAXLNUM;
5535 		curbuf->b_syn_sync_maxlines = 0;
5536 	    }
5537 	}
5538 	else if (STRCMP(key, "LINECONT") == 0)
5539 	{
5540 	    if (curbuf->b_syn_linecont_pat != NULL)
5541 	    {
5542 		EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5543 		finished = TRUE;
5544 		break;
5545 	    }
5546 	    arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5547 	    if (*arg_end != *next_arg)	    /* end delimiter not found */
5548 	    {
5549 		illegal = TRUE;
5550 		break;
5551 	    }
5552 
5553 	    if (!eap->skip)
5554 	    {
5555 		/* store the pattern and compiled regexp program */
5556 		if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5557 				      (int)(arg_end - next_arg - 1))) == NULL)
5558 		{
5559 		    finished = TRUE;
5560 		    break;
5561 		}
5562 		curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5563 
5564 		/* Make 'cpoptions' empty, to avoid the 'l' flag */
5565 		cpo_save = p_cpo;
5566 		p_cpo = (char_u *)"";
5567 		curbuf->b_syn_linecont_prog =
5568 			    vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5569 		p_cpo = cpo_save;
5570 
5571 		if (curbuf->b_syn_linecont_prog == NULL)
5572 		{
5573 		    vim_free(curbuf->b_syn_linecont_pat);
5574 		    curbuf->b_syn_linecont_pat = NULL;
5575 		    finished = TRUE;
5576 		    break;
5577 		}
5578 	    }
5579 	    next_arg = skipwhite(arg_end + 1);
5580 	}
5581 	else
5582 	{
5583 	    eap->arg = next_arg;
5584 	    if (STRCMP(key, "MATCH") == 0)
5585 		syn_cmd_match(eap, TRUE);
5586 	    else if (STRCMP(key, "REGION") == 0)
5587 		syn_cmd_region(eap, TRUE);
5588 	    else if (STRCMP(key, "CLEAR") == 0)
5589 		syn_cmd_clear(eap, TRUE);
5590 	    else
5591 		illegal = TRUE;
5592 	    finished = TRUE;
5593 	    break;
5594 	}
5595 	arg_start = next_arg;
5596     }
5597     vim_free(key);
5598     if (illegal)
5599 	EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5600     else if (!finished)
5601     {
5602 	eap->nextcmd = check_nextcmd(arg_start);
5603 	redraw_curbuf_later(SOME_VALID);
5604 	syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5605     }
5606 }
5607 
5608 /*
5609  * Convert a line of highlight group names into a list of group ID numbers.
5610  * "arg" should point to the "contains" or "nextgroup" keyword.
5611  * "arg" is advanced to after the last group name.
5612  * Careful: the argument is modified (NULs added).
5613  * returns FAIL for some error, OK for success.
5614  */
5615     static int
5616 get_id_list(arg, keylen, list)
5617     char_u	**arg;
5618     int		keylen;		/* length of keyword */
5619     short	**list;		/* where to store the resulting list, if not
5620 				   NULL, the list is silently skipped! */
5621 {
5622     char_u	*p = NULL;
5623     char_u	*end;
5624     int		round;
5625     int		count;
5626     int		total_count = 0;
5627     short	*retval = NULL;
5628     char_u	*name;
5629     regmatch_T	regmatch;
5630     int		id;
5631     int		i;
5632     int		failed = FALSE;
5633 
5634     /*
5635      * We parse the list twice:
5636      * round == 1: count the number of items, allocate the array.
5637      * round == 2: fill the array with the items.
5638      * In round 1 new groups may be added, causing the number of items to
5639      * grow when a regexp is used.  In that case round 1 is done once again.
5640      */
5641     for (round = 1; round <= 2; ++round)
5642     {
5643 	/*
5644 	 * skip "contains"
5645 	 */
5646 	p = skipwhite(*arg + keylen);
5647 	if (*p != '=')
5648 	{
5649 	    EMSG2(_("E405: Missing equal sign: %s"), *arg);
5650 	    break;
5651 	}
5652 	p = skipwhite(p + 1);
5653 	if (ends_excmd(*p))
5654 	{
5655 	    EMSG2(_("E406: Empty argument: %s"), *arg);
5656 	    break;
5657 	}
5658 
5659 	/*
5660 	 * parse the arguments after "contains"
5661 	 */
5662 	count = 0;
5663 	while (!ends_excmd(*p))
5664 	{
5665 	    for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5666 		;
5667 	    name = alloc((int)(end - p + 3));	    /* leave room for "^$" */
5668 	    if (name == NULL)
5669 	    {
5670 		failed = TRUE;
5671 		break;
5672 	    }
5673 	    vim_strncpy(name + 1, p, end - p);
5674 	    if (       STRCMP(name + 1, "ALLBUT") == 0
5675 		    || STRCMP(name + 1, "ALL") == 0
5676 		    || STRCMP(name + 1, "TOP") == 0
5677 		    || STRCMP(name + 1, "CONTAINED") == 0)
5678 	    {
5679 		if (TOUPPER_ASC(**arg) != 'C')
5680 		{
5681 		    EMSG2(_("E407: %s not allowed here"), name + 1);
5682 		    failed = TRUE;
5683 		    vim_free(name);
5684 		    break;
5685 		}
5686 		if (count != 0)
5687 		{
5688 		    EMSG2(_("E408: %s must be first in contains list"), name + 1);
5689 		    failed = TRUE;
5690 		    vim_free(name);
5691 		    break;
5692 		}
5693 		if (name[1] == 'A')
5694 		    id = SYNID_ALLBUT;
5695 		else if (name[1] == 'T')
5696 		    id = SYNID_TOP;
5697 		else
5698 		    id = SYNID_CONTAINED;
5699 		id += current_syn_inc_tag;
5700 	    }
5701 	    else if (name[1] == '@')
5702 	    {
5703 		id = syn_check_cluster(name + 2, (int)(end - p - 1));
5704 	    }
5705 	    else
5706 	    {
5707 		/*
5708 		 * Handle full group name.
5709 		 */
5710 		if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5711 		    id = syn_check_group(name + 1, (int)(end - p));
5712 		else
5713 		{
5714 		    /*
5715 		     * Handle match of regexp with group names.
5716 		     */
5717 		    *name = '^';
5718 		    STRCAT(name, "$");
5719 		    regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5720 		    if (regmatch.regprog == NULL)
5721 		    {
5722 			failed = TRUE;
5723 			vim_free(name);
5724 			break;
5725 		    }
5726 
5727 		    regmatch.rm_ic = TRUE;
5728 		    id = 0;
5729 		    for (i = highlight_ga.ga_len; --i >= 0; )
5730 		    {
5731 			if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5732 								  (colnr_T)0))
5733 			{
5734 			    if (round == 2)
5735 			    {
5736 				/* Got more items than expected; can happen
5737 				 * when adding items that match:
5738 				 * "contains=a.*b,axb".
5739 				 * Go back to first round */
5740 				if (count >= total_count)
5741 				{
5742 				    vim_free(retval);
5743 				    round = 1;
5744 				}
5745 				else
5746 				    retval[count] = i + 1;
5747 			    }
5748 			    ++count;
5749 			    id = -1;	    /* remember that we found one */
5750 			}
5751 		    }
5752 		    vim_free(regmatch.regprog);
5753 		}
5754 	    }
5755 	    vim_free(name);
5756 	    if (id == 0)
5757 	    {
5758 		EMSG2(_("E409: Unknown group name: %s"), p);
5759 		failed = TRUE;
5760 		break;
5761 	    }
5762 	    if (id > 0)
5763 	    {
5764 		if (round == 2)
5765 		{
5766 		    /* Got more items than expected, go back to first round */
5767 		    if (count >= total_count)
5768 		    {
5769 			vim_free(retval);
5770 			round = 1;
5771 		    }
5772 		    else
5773 			retval[count] = id;
5774 		}
5775 		++count;
5776 	    }
5777 	    p = skipwhite(end);
5778 	    if (*p != ',')
5779 		break;
5780 	    p = skipwhite(p + 1);	/* skip comma in between arguments */
5781 	}
5782 	if (failed)
5783 	    break;
5784 	if (round == 1)
5785 	{
5786 	    retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5787 	    if (retval == NULL)
5788 		break;
5789 	    retval[count] = 0;	    /* zero means end of the list */
5790 	    total_count = count;
5791 	}
5792     }
5793 
5794     *arg = p;
5795     if (failed || retval == NULL)
5796     {
5797 	vim_free(retval);
5798 	return FAIL;
5799     }
5800 
5801     if (*list == NULL)
5802 	*list = retval;
5803     else
5804 	vim_free(retval);	/* list already found, don't overwrite it */
5805 
5806     return OK;
5807 }
5808 
5809 /*
5810  * Make a copy of an ID list.
5811  */
5812     static short *
5813 copy_id_list(list)
5814     short   *list;
5815 {
5816     int	    len;
5817     int	    count;
5818     short   *retval;
5819 
5820     if (list == NULL)
5821 	return NULL;
5822 
5823     for (count = 0; list[count]; ++count)
5824 	;
5825     len = (count + 1) * sizeof(short);
5826     retval = (short *)alloc((unsigned)len);
5827     if (retval != NULL)
5828 	mch_memmove(retval, list, (size_t)len);
5829 
5830     return retval;
5831 }
5832 
5833 /*
5834  * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5835  * "cur_si" can be NULL if not checking the "containedin" list.
5836  * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5837  * the current item.
5838  * This function is called very often, keep it fast!!
5839  */
5840     static int
5841 in_id_list(cur_si, list, ssp, contained)
5842     stateitem_T	*cur_si;	/* current item or NULL */
5843     short	*list;		/* id list */
5844     struct sp_syn *ssp;		/* group id and ":syn include" tag of group */
5845     int		contained;	/* group id is contained */
5846 {
5847     int		retval;
5848     short	*scl_list;
5849     short	item;
5850     short	id = ssp->id;
5851     static int	depth = 0;
5852     int		r;
5853 
5854     /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
5855     if (cur_si != NULL && ssp->cont_in_list != NULL
5856 					    && !(cur_si->si_flags & HL_MATCH))
5857     {
5858 	/* Ignore transparent items without a contains argument.  Double check
5859 	 * that we don't go back past the first one. */
5860 	while ((cur_si->si_flags & HL_TRANS_CONT)
5861 		&& cur_si > (stateitem_T *)(current_state.ga_data))
5862 	    --cur_si;
5863 	/* cur_si->si_idx is -1 for keywords, these never contain anything. */
5864 	if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5865 		&(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5866 		  SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5867 	    return TRUE;
5868     }
5869 
5870     if (list == NULL)
5871 	return FALSE;
5872 
5873     /*
5874      * If list is ID_LIST_ALL, we are in a transparent item that isn't
5875      * inside anything.  Only allow not-contained groups.
5876      */
5877     if (list == ID_LIST_ALL)
5878 	return !contained;
5879 
5880     /*
5881      * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5882      * contains list.  We also require that "id" is at the same ":syn include"
5883      * level as the list.
5884      */
5885     item = *list;
5886     if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5887     {
5888 	if (item < SYNID_TOP)
5889 	{
5890 	    /* ALL or ALLBUT: accept all groups in the same file */
5891 	    if (item - SYNID_ALLBUT != ssp->inc_tag)
5892 		return FALSE;
5893 	}
5894 	else if (item < SYNID_CONTAINED)
5895 	{
5896 	    /* TOP: accept all not-contained groups in the same file */
5897 	    if (item - SYNID_TOP != ssp->inc_tag || contained)
5898 		return FALSE;
5899 	}
5900 	else
5901 	{
5902 	    /* CONTAINED: accept all contained groups in the same file */
5903 	    if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5904 		return FALSE;
5905 	}
5906 	item = *++list;
5907 	retval = FALSE;
5908     }
5909     else
5910 	retval = TRUE;
5911 
5912     /*
5913      * Return "retval" if id is in the contains list.
5914      */
5915     while (item != 0)
5916     {
5917 	if (item == id)
5918 	    return retval;
5919 	if (item >= SYNID_CLUSTER)
5920 	{
5921 	    scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5922 	    /* restrict recursiveness to 30 to avoid an endless loop for a
5923 	     * cluster that includes itself (indirectly) */
5924 	    if (scl_list != NULL && depth < 30)
5925 	    {
5926 		++depth;
5927 		r = in_id_list(NULL, scl_list, ssp, contained);
5928 		--depth;
5929 		if (r)
5930 		    return retval;
5931 	    }
5932 	}
5933 	item = *++list;
5934     }
5935     return !retval;
5936 }
5937 
5938 struct subcommand
5939 {
5940     char    *name;				/* subcommand name */
5941     void    (*func)__ARGS((exarg_T *, int));	/* function to call */
5942 };
5943 
5944 static struct subcommand subcommands[] =
5945 {
5946     {"case",		syn_cmd_case},
5947     {"clear",		syn_cmd_clear},
5948     {"cluster",		syn_cmd_cluster},
5949     {"enable",		syn_cmd_enable},
5950     {"include",		syn_cmd_include},
5951     {"keyword",		syn_cmd_keyword},
5952     {"list",		syn_cmd_list},
5953     {"manual",		syn_cmd_manual},
5954     {"match",		syn_cmd_match},
5955     {"on",		syn_cmd_on},
5956     {"off",		syn_cmd_off},
5957     {"region",		syn_cmd_region},
5958     {"reset",		syn_cmd_reset},
5959     {"spell",		syn_cmd_spell},
5960     {"sync",		syn_cmd_sync},
5961     {"",		syn_cmd_list},
5962     {NULL, NULL}
5963 };
5964 
5965 /*
5966  * ":syntax".
5967  * This searches the subcommands[] table for the subcommand name, and calls a
5968  * syntax_subcommand() function to do the rest.
5969  */
5970     void
5971 ex_syntax(eap)
5972     exarg_T	*eap;
5973 {
5974     char_u	*arg = eap->arg;
5975     char_u	*subcmd_end;
5976     char_u	*subcmd_name;
5977     int		i;
5978 
5979     syn_cmdlinep = eap->cmdlinep;
5980 
5981     /* isolate subcommand name */
5982     for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5983 	;
5984     subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5985     if (subcmd_name != NULL)
5986     {
5987 	if (eap->skip)		/* skip error messages for all subcommands */
5988 	    ++emsg_skip;
5989 	for (i = 0; ; ++i)
5990 	{
5991 	    if (subcommands[i].name == NULL)
5992 	    {
5993 		EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5994 		break;
5995 	    }
5996 	    if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5997 	    {
5998 		eap->arg = skipwhite(subcmd_end);
5999 		(subcommands[i].func)(eap, FALSE);
6000 		break;
6001 	    }
6002 	}
6003 	vim_free(subcmd_name);
6004 	if (eap->skip)
6005 	    --emsg_skip;
6006     }
6007 }
6008 
6009     int
6010 syntax_present(buf)
6011     buf_T	*buf;
6012 {
6013     return (buf->b_syn_patterns.ga_len != 0
6014 	    || buf->b_syn_clusters.ga_len != 0
6015 	    || buf->b_keywtab.ht_used > 0
6016 	    || buf->b_keywtab_ic.ht_used > 0);
6017 }
6018 
6019 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6020 
6021 static enum
6022 {
6023     EXP_SUBCMD,	    /* expand ":syn" sub-commands */
6024     EXP_CASE	    /* expand ":syn case" arguments */
6025 } expand_what;
6026 
6027 /*
6028  * Reset include_link, include_default, include_none to 0.
6029  * Called when we are done expanding.
6030  */
6031     void
6032 reset_expand_highlight()
6033 {
6034     include_link = include_default = include_none = 0;
6035 }
6036 
6037 /*
6038  * Handle command line completion for :match and :echohl command: Add "None"
6039  * as highlight group.
6040  */
6041     void
6042 set_context_in_echohl_cmd(xp, arg)
6043     expand_T	*xp;
6044     char_u	*arg;
6045 {
6046     xp->xp_context = EXPAND_HIGHLIGHT;
6047     xp->xp_pattern = arg;
6048     include_none = 1;
6049 }
6050 
6051 /*
6052  * Handle command line completion for :syntax command.
6053  */
6054     void
6055 set_context_in_syntax_cmd(xp, arg)
6056     expand_T	*xp;
6057     char_u	*arg;
6058 {
6059     char_u	*p;
6060 
6061     /* Default: expand subcommands */
6062     xp->xp_context = EXPAND_SYNTAX;
6063     expand_what = EXP_SUBCMD;
6064     xp->xp_pattern = arg;
6065     include_link = 0;
6066     include_default = 0;
6067 
6068     /* (part of) subcommand already typed */
6069     if (*arg != NUL)
6070     {
6071 	p = skiptowhite(arg);
6072 	if (*p != NUL)		    /* past first word */
6073 	{
6074 	    xp->xp_pattern = skipwhite(p);
6075 	    if (*skiptowhite(xp->xp_pattern) != NUL)
6076 		xp->xp_context = EXPAND_NOTHING;
6077 	    else if (STRNICMP(arg, "case", p - arg) == 0)
6078 		expand_what = EXP_CASE;
6079 	    else if (  STRNICMP(arg, "keyword", p - arg) == 0
6080 		    || STRNICMP(arg, "region", p - arg) == 0
6081 		    || STRNICMP(arg, "match", p - arg) == 0
6082 		    || STRNICMP(arg, "list", p - arg) == 0)
6083 		xp->xp_context = EXPAND_HIGHLIGHT;
6084 	    else
6085 		xp->xp_context = EXPAND_NOTHING;
6086 	}
6087     }
6088 }
6089 
6090 static char *(case_args[]) = {"match", "ignore", NULL};
6091 
6092 /*
6093  * Function given to ExpandGeneric() to obtain the list syntax names for
6094  * expansion.
6095  */
6096     char_u *
6097 get_syntax_name(xp, idx)
6098     expand_T	*xp UNUSED;
6099     int		idx;
6100 {
6101     if (expand_what == EXP_SUBCMD)
6102 	return (char_u *)subcommands[idx].name;
6103     return (char_u *)case_args[idx];
6104 }
6105 
6106 #endif /* FEAT_CMDL_COMPL */
6107 
6108 /*
6109  * Function called for expression evaluation: get syntax ID at file position.
6110  */
6111     int
6112 syn_get_id(wp, lnum, col, trans, spellp, keep_state)
6113     win_T	*wp;
6114     long	lnum;
6115     colnr_T	col;
6116     int		trans;	     /* remove transparency */
6117     int		*spellp;     /* return: can do spell checking */
6118     int		keep_state;  /* keep state of char at "col" */
6119 {
6120     /* When the position is not after the current position and in the same
6121      * line of the same buffer, need to restart parsing. */
6122     if (wp->w_buffer != syn_buf
6123 	    || lnum != current_lnum
6124 	    || col < current_col)
6125 	syntax_start(wp, lnum);
6126 
6127     (void)get_syntax_attr(col, spellp, keep_state);
6128 
6129     return (trans ? current_trans_id : current_id);
6130 }
6131 
6132 #if defined(FEAT_EVAL) || defined(PROTO)
6133 /*
6134  * Return the syntax ID at position "i" in the current stack.
6135  * The caller must have called syn_get_id() before to fill the stack.
6136  * Returns -1 when "i" is out of range.
6137  */
6138     int
6139 syn_get_stack_item(i)
6140     int i;
6141 {
6142     if (i >= current_state.ga_len)
6143     {
6144 	/* Need to invalidate the state, because we didn't properly finish it
6145 	 * for the last character, "keep_state" was TRUE. */
6146 	invalidate_current_state();
6147 	current_col = MAXCOL;
6148 	return -1;
6149     }
6150     return CUR_STATE(i).si_id;
6151 }
6152 #endif
6153 
6154 #if defined(FEAT_FOLDING) || defined(PROTO)
6155 /*
6156  * Function called to get folding level for line "lnum" in window "wp".
6157  */
6158     int
6159 syn_get_foldlevel(wp, lnum)
6160     win_T	*wp;
6161     long	lnum;
6162 {
6163     int		level = 0;
6164     int		i;
6165 
6166     /* Return quickly when there are no fold items at all. */
6167     if (wp->w_buffer->b_syn_folditems != 0)
6168     {
6169 	syntax_start(wp, lnum);
6170 
6171 	for (i = 0; i < current_state.ga_len; ++i)
6172 	    if (CUR_STATE(i).si_flags & HL_FOLD)
6173 		++level;
6174     }
6175     if (level > wp->w_p_fdn)
6176     {
6177 	level = wp->w_p_fdn;
6178 	if (level < 0)
6179 	    level = 0;
6180     }
6181     return level;
6182 }
6183 #endif
6184 
6185 #endif /* FEAT_SYN_HL */
6186 
6187 
6188 /**************************************
6189  *  Highlighting stuff		      *
6190  **************************************/
6191 
6192 /*
6193  * The default highlight groups.  These are compiled-in for fast startup and
6194  * they still work when the runtime files can't be found.
6195  * When making changes here, also change runtime/colors/default.vim!
6196  * The #ifdefs are needed to reduce the amount of static data.  Helps to make
6197  * the 16 bit DOS (museum) version compile.
6198  */
6199 #ifdef FEAT_GUI
6200 # define CENT(a, b) b
6201 #else
6202 # define CENT(a, b) a
6203 #endif
6204 static char *(highlight_init_both[]) =
6205     {
6206 	CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6207 	     "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6208 	CENT("IncSearch term=reverse cterm=reverse",
6209 	     "IncSearch term=reverse cterm=reverse gui=reverse"),
6210 	CENT("ModeMsg term=bold cterm=bold",
6211 	     "ModeMsg term=bold cterm=bold gui=bold"),
6212 	CENT("NonText term=bold ctermfg=Blue",
6213 	     "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6214 	CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6215 	     "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6216 	CENT("StatusLineNC term=reverse cterm=reverse",
6217 	     "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6218 #ifdef FEAT_VERTSPLIT
6219 	CENT("VertSplit term=reverse cterm=reverse",
6220 	     "VertSplit term=reverse cterm=reverse gui=reverse"),
6221 #endif
6222 #ifdef FEAT_CLIPBOARD
6223 	CENT("VisualNOS term=underline,bold cterm=underline,bold",
6224 	     "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
6225 #endif
6226 #ifdef FEAT_DIFF
6227 	CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6228 	     "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
6229 #endif
6230 #ifdef FEAT_INS_EXPAND
6231 	CENT("PmenuThumb cterm=reverse",
6232 	     "PmenuThumb cterm=reverse gui=reverse"),
6233 	CENT("PmenuSbar ctermbg=Grey",
6234 	     "PmenuSbar ctermbg=Grey guibg=Grey"),
6235 #endif
6236 #ifdef FEAT_WINDOWS
6237 	CENT("TabLineSel term=bold cterm=bold",
6238 	     "TabLineSel term=bold cterm=bold gui=bold"),
6239 	CENT("TabLineFill term=reverse cterm=reverse",
6240 	     "TabLineFill term=reverse cterm=reverse gui=reverse"),
6241 #endif
6242 #ifdef FEAT_GUI
6243 	"Cursor guibg=fg guifg=bg",
6244 	"lCursor guibg=fg guifg=bg", /* should be different, but what? */
6245 #endif
6246 	NULL
6247     };
6248 
6249 static char *(highlight_init_light[]) =
6250     {
6251 	CENT("Directory term=bold ctermfg=DarkBlue",
6252 	     "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6253 	CENT("LineNr term=underline ctermfg=Brown",
6254 	     "LineNr term=underline ctermfg=Brown guifg=Brown"),
6255 	CENT("MoreMsg term=bold ctermfg=DarkGreen",
6256 	     "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6257 	CENT("Question term=standout ctermfg=DarkGreen",
6258 	     "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6259 	CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6260 	     "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
6261 #ifdef FEAT_SPELL
6262 	CENT("SpellBad term=reverse ctermbg=LightRed",
6263 	     "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6264 	CENT("SpellCap term=reverse ctermbg=LightBlue",
6265 	     "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6266 	CENT("SpellRare term=reverse ctermbg=LightMagenta",
6267 	     "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6268 	CENT("SpellLocal term=underline ctermbg=Cyan",
6269 	     "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
6270 #endif
6271 #ifdef FEAT_INS_EXPAND
6272 	CENT("Pmenu ctermbg=LightMagenta",
6273 	     "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6274 	CENT("PmenuSel ctermbg=LightGrey",
6275 	     "PmenuSel ctermbg=LightGrey guibg=Grey"),
6276 #endif
6277 	CENT("SpecialKey term=bold ctermfg=DarkBlue",
6278 	     "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6279 	CENT("Title term=bold ctermfg=DarkMagenta",
6280 	     "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6281 	CENT("WarningMsg term=standout ctermfg=DarkRed",
6282 	     "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
6283 #ifdef FEAT_WILDMENU
6284 	CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6285 	     "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6286 #endif
6287 #ifdef FEAT_FOLDING
6288 	CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6289 	     "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6290 	CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6291 	     "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
6292 #endif
6293 #ifdef FEAT_SIGNS
6294 	CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6295 	     "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
6296 #endif
6297 #ifdef FEAT_VISUAL
6298 	CENT("Visual term=reverse",
6299 	     "Visual term=reverse guibg=LightGrey"),
6300 #endif
6301 #ifdef FEAT_DIFF
6302 	CENT("DiffAdd term=bold ctermbg=LightBlue",
6303 	     "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6304 	CENT("DiffChange term=bold ctermbg=LightMagenta",
6305 	     "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6306 	CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6307 	     "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
6308 #endif
6309 #ifdef FEAT_WINDOWS
6310 	CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6311 	     "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
6312 #endif
6313 #ifdef FEAT_SYN_HL
6314 	CENT("CursorColumn term=reverse ctermbg=LightGrey",
6315 	     "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6316 	CENT("CursorLine term=underline cterm=underline",
6317 	     "CursorLine term=underline cterm=underline guibg=Grey90"),
6318 #endif
6319 #ifdef FEAT_AUTOCMD
6320 	CENT("MatchParen term=reverse ctermbg=Cyan",
6321 	     "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6322 #endif
6323 #ifdef FEAT_GUI
6324 	"Normal gui=NONE",
6325 #endif
6326 	NULL
6327     };
6328 
6329 static char *(highlight_init_dark[]) =
6330     {
6331 	CENT("Directory term=bold ctermfg=LightCyan",
6332 	     "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6333 	CENT("LineNr term=underline ctermfg=Yellow",
6334 	     "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6335 	CENT("MoreMsg term=bold ctermfg=LightGreen",
6336 	     "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6337 	CENT("Question term=standout ctermfg=LightGreen",
6338 	     "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6339 	CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6340 	     "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6341 	CENT("SpecialKey term=bold ctermfg=LightBlue",
6342 	     "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
6343 #ifdef FEAT_SPELL
6344 	CENT("SpellBad term=reverse ctermbg=Red",
6345 	     "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6346 	CENT("SpellCap term=reverse ctermbg=Blue",
6347 	     "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6348 	CENT("SpellRare term=reverse ctermbg=Magenta",
6349 	     "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6350 	CENT("SpellLocal term=underline ctermbg=Cyan",
6351 	     "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
6352 #endif
6353 #ifdef FEAT_INS_EXPAND
6354 	CENT("Pmenu ctermbg=Magenta",
6355 	     "Pmenu ctermbg=Magenta guibg=Magenta"),
6356 	CENT("PmenuSel ctermbg=DarkGrey",
6357 	     "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
6358 #endif
6359 	CENT("Title term=bold ctermfg=LightMagenta",
6360 	     "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6361 	CENT("WarningMsg term=standout ctermfg=LightRed",
6362 	     "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
6363 #ifdef FEAT_WILDMENU
6364 	CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6365 	     "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6366 #endif
6367 #ifdef FEAT_FOLDING
6368 	CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6369 	     "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6370 	CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6371 	     "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
6372 #endif
6373 #ifdef FEAT_SIGNS
6374 	CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6375 	     "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
6376 #endif
6377 #ifdef FEAT_VISUAL
6378 	CENT("Visual term=reverse",
6379 	     "Visual term=reverse guibg=DarkGrey"),
6380 #endif
6381 #ifdef FEAT_DIFF
6382 	CENT("DiffAdd term=bold ctermbg=DarkBlue",
6383 	     "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6384 	CENT("DiffChange term=bold ctermbg=DarkMagenta",
6385 	     "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6386 	CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6387 	     "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
6388 #endif
6389 #ifdef FEAT_WINDOWS
6390 	CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6391 	     "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
6392 #endif
6393 #ifdef FEAT_SYN_HL
6394 	CENT("CursorColumn term=reverse ctermbg=DarkGrey",
6395 	     "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
6396 	CENT("CursorLine term=underline cterm=underline",
6397 	     "CursorLine term=underline cterm=underline guibg=Grey40"),
6398 #endif
6399 #ifdef FEAT_AUTOCMD
6400 	CENT("MatchParen term=reverse ctermbg=DarkCyan",
6401 	     "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
6402 #endif
6403 #ifdef FEAT_GUI
6404 	"Normal gui=NONE",
6405 #endif
6406 	NULL
6407     };
6408 
6409     void
6410 init_highlight(both, reset)
6411     int		both;	    /* include groups where 'bg' doesn't matter */
6412     int		reset;	    /* clear group first */
6413 {
6414     int		i;
6415     char	**pp;
6416     static int	had_both = FALSE;
6417 #ifdef FEAT_EVAL
6418     char_u	*p;
6419 
6420     /*
6421      * Try finding the color scheme file.  Used when a color file was loaded
6422      * and 'background' or 't_Co' is changed.
6423      */
6424     p = get_var_value((char_u *)"g:colors_name");
6425     if (p != NULL && load_colors(p) == OK)
6426 	return;
6427 #endif
6428 
6429     /*
6430      * Didn't use a color file, use the compiled-in colors.
6431      */
6432     if (both)
6433     {
6434 	had_both = TRUE;
6435 	pp = highlight_init_both;
6436 	for (i = 0; pp[i] != NULL; ++i)
6437 	    do_highlight((char_u *)pp[i], reset, TRUE);
6438     }
6439     else if (!had_both)
6440 	/* Don't do anything before the call with both == TRUE from main().
6441 	 * Not everything has been setup then, and that call will overrule
6442 	 * everything anyway. */
6443 	return;
6444 
6445     if (*p_bg == 'l')
6446 	pp = highlight_init_light;
6447     else
6448 	pp = highlight_init_dark;
6449     for (i = 0; pp[i] != NULL; ++i)
6450 	do_highlight((char_u *)pp[i], reset, TRUE);
6451 
6452     /* Reverse looks ugly, but grey may not work for 8 colors.  Thus let it
6453      * depend on the number of colors available.
6454      * With 8 colors brown is equal to yellow, need to use black for Search fg
6455      * to avoid Statement highlighted text disappears.
6456      * Clear the attributes, needed when changing the t_Co value. */
6457     if (t_colors > 8)
6458 	do_highlight((char_u *)(*p_bg == 'l'
6459 		    ? "Visual cterm=NONE ctermbg=LightGrey"
6460 		    : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
6461     else
6462     {
6463 	do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6464 								 FALSE, TRUE);
6465 	if (*p_bg == 'l')
6466 	    do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6467     }
6468 
6469 #ifdef FEAT_SYN_HL
6470     /*
6471      * If syntax highlighting is enabled load the highlighting for it.
6472      */
6473     if (get_var_value((char_u *)"g:syntax_on") != NULL)
6474     {
6475 	static int	recursive = 0;
6476 
6477 	if (recursive >= 5)
6478 	    EMSG(_("E679: recursive loop loading syncolor.vim"));
6479 	else
6480 	{
6481 	    ++recursive;
6482 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6483 	    --recursive;
6484 	}
6485     }
6486 #endif
6487 }
6488 
6489 /*
6490  * Load color file "name".
6491  * Return OK for success, FAIL for failure.
6492  */
6493     int
6494 load_colors(name)
6495     char_u	*name;
6496 {
6497     char_u	*buf;
6498     int		retval = FAIL;
6499     static int	recursive = FALSE;
6500 
6501     /* When being called recursively, this is probably because setting
6502      * 'background' caused the highlighting to be reloaded.  This means it is
6503      * working, thus we should return OK. */
6504     if (recursive)
6505 	return OK;
6506 
6507     recursive = TRUE;
6508     buf = alloc((unsigned)(STRLEN(name) + 12));
6509     if (buf != NULL)
6510     {
6511 	sprintf((char *)buf, "colors/%s.vim", name);
6512 	retval = source_runtime(buf, FALSE);
6513 	vim_free(buf);
6514 #ifdef FEAT_AUTOCMD
6515 	apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6516 #endif
6517     }
6518     recursive = FALSE;
6519 
6520     return retval;
6521 }
6522 
6523 /*
6524  * Handle the ":highlight .." command.
6525  * When using ":hi clear" this is called recursively for each group with
6526  * "forceit" and "init" both TRUE.
6527  */
6528     void
6529 do_highlight(line, forceit, init)
6530     char_u	*line;
6531     int		forceit;
6532     int		init;	    /* TRUE when called for initializing */
6533 {
6534     char_u	*name_end;
6535     char_u	*p;
6536     char_u	*linep;
6537     char_u	*key_start;
6538     char_u	*arg_start;
6539     char_u	*key = NULL, *arg = NULL;
6540     long	i;
6541     int		off;
6542     int		len;
6543     int		attr;
6544     int		id;
6545     int		idx;
6546     int		dodefault = FALSE;
6547     int		doclear = FALSE;
6548     int		dolink = FALSE;
6549     int		error = FALSE;
6550     int		color;
6551     int		is_normal_group = FALSE;	/* "Normal" group */
6552 #ifdef FEAT_GUI_X11
6553     int		is_menu_group = FALSE;		/* "Menu" group */
6554     int		is_scrollbar_group = FALSE;	/* "Scrollbar" group */
6555     int		is_tooltip_group = FALSE;	/* "Tooltip" group */
6556     int		do_colors = FALSE;		/* need to update colors? */
6557 #else
6558 # define is_menu_group 0
6559 # define is_tooltip_group 0
6560 #endif
6561 
6562     /*
6563      * If no argument, list current highlighting.
6564      */
6565     if (ends_excmd(*line))
6566     {
6567 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6568 	    /* TODO: only call when the group has attributes set */
6569 	    highlight_list_one((int)i);
6570 	return;
6571     }
6572 
6573     /*
6574      * Isolate the name.
6575      */
6576     name_end = skiptowhite(line);
6577     linep = skipwhite(name_end);
6578 
6579     /*
6580      * Check for "default" argument.
6581      */
6582     if (STRNCMP(line, "default", name_end - line) == 0)
6583     {
6584 	dodefault = TRUE;
6585 	line = linep;
6586 	name_end = skiptowhite(line);
6587 	linep = skipwhite(name_end);
6588     }
6589 
6590     /*
6591      * Check for "clear" or "link" argument.
6592      */
6593     if (STRNCMP(line, "clear", name_end - line) == 0)
6594 	doclear = TRUE;
6595     if (STRNCMP(line, "link", name_end - line) == 0)
6596 	dolink = TRUE;
6597 
6598     /*
6599      * ":highlight {group-name}": list highlighting for one group.
6600      */
6601     if (!doclear && !dolink && ends_excmd(*linep))
6602     {
6603 	id = syn_namen2id(line, (int)(name_end - line));
6604 	if (id == 0)
6605 	    EMSG2(_("E411: highlight group not found: %s"), line);
6606 	else
6607 	    highlight_list_one(id);
6608 	return;
6609     }
6610 
6611     /*
6612      * Handle ":highlight link {from} {to}" command.
6613      */
6614     if (dolink)
6615     {
6616 	char_u	    *from_start = linep;
6617 	char_u	    *from_end;
6618 	char_u	    *to_start;
6619 	char_u	    *to_end;
6620 	int	    from_id;
6621 	int	    to_id;
6622 
6623 	from_end = skiptowhite(from_start);
6624 	to_start = skipwhite(from_end);
6625 	to_end	 = skiptowhite(to_start);
6626 
6627 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
6628 	{
6629 	    EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6630 								  from_start);
6631 	    return;
6632 	}
6633 
6634 	if (!ends_excmd(*skipwhite(to_end)))
6635 	{
6636 	    EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6637 	    return;
6638 	}
6639 
6640 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
6641 	if (STRNCMP(to_start, "NONE", 4) == 0)
6642 	    to_id = 0;
6643 	else
6644 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
6645 
6646 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6647 	{
6648 	    /*
6649 	     * Don't allow a link when there already is some highlighting
6650 	     * for the group, unless '!' is used
6651 	     */
6652 	    if (to_id > 0 && !forceit && !init
6653 				   && hl_has_settings(from_id - 1, dodefault))
6654 	    {
6655 		if (sourcing_name == NULL && !dodefault)
6656 		    EMSG(_("E414: group has settings, highlight link ignored"));
6657 	    }
6658 	    else
6659 	    {
6660 		if (!init)
6661 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6662 		HL_TABLE()[from_id - 1].sg_link = to_id;
6663 #ifdef FEAT_EVAL
6664 		HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6665 #endif
6666 		redraw_all_later(SOME_VALID);
6667 	    }
6668 	}
6669 
6670 	/* Only call highlight_changed() once, after sourcing a syntax file */
6671 	need_highlight_changed = TRUE;
6672 
6673 	return;
6674     }
6675 
6676     if (doclear)
6677     {
6678 	/*
6679 	 * ":highlight clear [group]" command.
6680 	 */
6681 	line = linep;
6682 	if (ends_excmd(*line))
6683 	{
6684 #ifdef FEAT_GUI
6685 	    /* First, we do not destroy the old values, but allocate the new
6686 	     * ones and update the display. THEN we destroy the old values.
6687 	     * If we destroy the old values first, then the old values
6688 	     * (such as GuiFont's or GuiFontset's) will still be displayed but
6689 	     * invalid because they were free'd.
6690 	     */
6691 	    if (gui.in_use)
6692 	    {
6693 # ifdef FEAT_BEVAL_TIP
6694 		gui_init_tooltip_font();
6695 # endif
6696 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6697 		gui_init_menu_font();
6698 # endif
6699 	    }
6700 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6701 	    gui_mch_def_colors();
6702 # endif
6703 # ifdef FEAT_GUI_X11
6704 #  ifdef FEAT_MENU
6705 
6706 	    /* This only needs to be done when there is no Menu highlight
6707 	     * group defined by default, which IS currently the case.
6708 	     */
6709 	    gui_mch_new_menu_colors();
6710 #  endif
6711 	    if (gui.in_use)
6712 	    {
6713 		gui_new_scrollbar_colors();
6714 #  ifdef FEAT_BEVAL
6715 		gui_mch_new_tooltip_colors();
6716 #  endif
6717 #  ifdef FEAT_MENU
6718 		gui_mch_new_menu_font();
6719 #  endif
6720 	    }
6721 # endif
6722 
6723 	    /* Ok, we're done allocating the new default graphics items.
6724 	     * The screen should already be refreshed at this point.
6725 	     * It is now Ok to clear out the old data.
6726 	     */
6727 #endif
6728 #ifdef FEAT_EVAL
6729 	    do_unlet((char_u *)"colors_name", TRUE);
6730 #endif
6731 	    restore_cterm_colors();
6732 
6733 	    /*
6734 	     * Clear all default highlight groups and load the defaults.
6735 	     */
6736 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6737 		highlight_clear(idx);
6738 	    init_highlight(TRUE, TRUE);
6739 #ifdef FEAT_GUI
6740 	    if (gui.in_use)
6741 		highlight_gui_started();
6742 #endif
6743 	    highlight_changed();
6744 	    redraw_later_clear();
6745 	    return;
6746 	}
6747 	name_end = skiptowhite(line);
6748 	linep = skipwhite(name_end);
6749     }
6750 
6751     /*
6752      * Find the group name in the table.  If it does not exist yet, add it.
6753      */
6754     id = syn_check_group(line, (int)(name_end - line));
6755     if (id == 0)			/* failed (out of memory) */
6756 	return;
6757     idx = id - 1;			/* index is ID minus one */
6758 
6759     /* Return if "default" was used and the group already has settings. */
6760     if (dodefault && hl_has_settings(idx, TRUE))
6761 	return;
6762 
6763     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6764 	is_normal_group = TRUE;
6765 #ifdef FEAT_GUI_X11
6766     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6767 	is_menu_group = TRUE;
6768     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6769 	is_scrollbar_group = TRUE;
6770     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6771 	is_tooltip_group = TRUE;
6772 #endif
6773 
6774     /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6775     if (doclear || (forceit && init))
6776     {
6777 	highlight_clear(idx);
6778 	if (!doclear)
6779 	    HL_TABLE()[idx].sg_set = 0;
6780     }
6781 
6782     if (!doclear)
6783       while (!ends_excmd(*linep))
6784       {
6785 	key_start = linep;
6786 	if (*linep == '=')
6787 	{
6788 	    EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6789 	    error = TRUE;
6790 	    break;
6791 	}
6792 
6793 	/*
6794 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6795 	 * "guibg").
6796 	 */
6797 	while (*linep && !vim_iswhite(*linep) && *linep != '=')
6798 	    ++linep;
6799 	vim_free(key);
6800 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
6801 	if (key == NULL)
6802 	{
6803 	    error = TRUE;
6804 	    break;
6805 	}
6806 	linep = skipwhite(linep);
6807 
6808 	if (STRCMP(key, "NONE") == 0)
6809 	{
6810 	    if (!init || HL_TABLE()[idx].sg_set == 0)
6811 	    {
6812 		if (!init)
6813 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6814 		highlight_clear(idx);
6815 	    }
6816 	    continue;
6817 	}
6818 
6819 	/*
6820 	 * Check for the equal sign.
6821 	 */
6822 	if (*linep != '=')
6823 	{
6824 	    EMSG2(_("E416: missing equal sign: %s"), key_start);
6825 	    error = TRUE;
6826 	    break;
6827 	}
6828 	++linep;
6829 
6830 	/*
6831 	 * Isolate the argument.
6832 	 */
6833 	linep = skipwhite(linep);
6834 	if (*linep == '\'')		/* guifg='color name' */
6835 	{
6836 	    arg_start = ++linep;
6837 	    linep = vim_strchr(linep, '\'');
6838 	    if (linep == NULL)
6839 	    {
6840 		EMSG2(_(e_invarg2), key_start);
6841 		error = TRUE;
6842 		break;
6843 	    }
6844 	}
6845 	else
6846 	{
6847 	    arg_start = linep;
6848 	    linep = skiptowhite(linep);
6849 	}
6850 	if (linep == arg_start)
6851 	{
6852 	    EMSG2(_("E417: missing argument: %s"), key_start);
6853 	    error = TRUE;
6854 	    break;
6855 	}
6856 	vim_free(arg);
6857 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6858 	if (arg == NULL)
6859 	{
6860 	    error = TRUE;
6861 	    break;
6862 	}
6863 	if (*linep == '\'')
6864 	    ++linep;
6865 
6866 	/*
6867 	 * Store the argument.
6868 	 */
6869 	if (  STRCMP(key, "TERM") == 0
6870 		|| STRCMP(key, "CTERM") == 0
6871 		|| STRCMP(key, "GUI") == 0)
6872 	{
6873 	    attr = 0;
6874 	    off = 0;
6875 	    while (arg[off] != NUL)
6876 	    {
6877 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6878 		{
6879 		    len = (int)STRLEN(hl_name_table[i]);
6880 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6881 		    {
6882 			attr |= hl_attr_table[i];
6883 			off += len;
6884 			break;
6885 		    }
6886 		}
6887 		if (i < 0)
6888 		{
6889 		    EMSG2(_("E418: Illegal value: %s"), arg);
6890 		    error = TRUE;
6891 		    break;
6892 		}
6893 		if (arg[off] == ',')		/* another one follows */
6894 		    ++off;
6895 	    }
6896 	    if (error)
6897 		break;
6898 	    if (*key == 'T')
6899 	    {
6900 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6901 		{
6902 		    if (!init)
6903 			HL_TABLE()[idx].sg_set |= SG_TERM;
6904 		    HL_TABLE()[idx].sg_term = attr;
6905 		}
6906 	    }
6907 	    else if (*key == 'C')
6908 	    {
6909 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6910 		{
6911 		    if (!init)
6912 			HL_TABLE()[idx].sg_set |= SG_CTERM;
6913 		    HL_TABLE()[idx].sg_cterm = attr;
6914 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
6915 		}
6916 	    }
6917 #ifdef FEAT_GUI
6918 	    else
6919 	    {
6920 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6921 		{
6922 		    if (!init)
6923 			HL_TABLE()[idx].sg_set |= SG_GUI;
6924 		    HL_TABLE()[idx].sg_gui = attr;
6925 		}
6926 	    }
6927 #endif
6928 	}
6929 	else if (STRCMP(key, "FONT") == 0)
6930 	{
6931 	    /* in non-GUI fonts are simply ignored */
6932 #ifdef FEAT_GUI
6933 	    if (!gui.shell_created)
6934 	    {
6935 		/* GUI not started yet, always accept the name. */
6936 		vim_free(HL_TABLE()[idx].sg_font_name);
6937 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6938 	    }
6939 	    else
6940 	    {
6941 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6942 # ifdef FEAT_XFONTSET
6943 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6944 # endif
6945 		/* First, save the current font/fontset.
6946 		 * Then try to allocate the font/fontset.
6947 		 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6948 		 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6949 		 */
6950 
6951 		HL_TABLE()[idx].sg_font = NOFONT;
6952 # ifdef FEAT_XFONTSET
6953 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
6954 # endif
6955 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
6956 							    is_tooltip_group);
6957 
6958 # ifdef FEAT_XFONTSET
6959 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6960 		{
6961 		    /* New fontset was accepted. Free the old one, if there was
6962 		     * one.
6963 		     */
6964 		    gui_mch_free_fontset(temp_sg_fontset);
6965 		    vim_free(HL_TABLE()[idx].sg_font_name);
6966 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6967 		}
6968 		else
6969 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6970 # endif
6971 		if (HL_TABLE()[idx].sg_font != NOFONT)
6972 		{
6973 		    /* New font was accepted. Free the old one, if there was
6974 		     * one.
6975 		     */
6976 		    gui_mch_free_font(temp_sg_font);
6977 		    vim_free(HL_TABLE()[idx].sg_font_name);
6978 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6979 		}
6980 		else
6981 		    HL_TABLE()[idx].sg_font = temp_sg_font;
6982 	    }
6983 #endif
6984 	}
6985 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6986 	{
6987 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6988 	  {
6989 	    if (!init)
6990 		HL_TABLE()[idx].sg_set |= SG_CTERM;
6991 
6992 	    /* When setting the foreground color, and previously the "bold"
6993 	     * flag was set for a light color, reset it now */
6994 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6995 	    {
6996 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6997 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
6998 	    }
6999 
7000 	    if (VIM_ISDIGIT(*arg))
7001 		color = atoi((char *)arg);
7002 	    else if (STRICMP(arg, "fg") == 0)
7003 	    {
7004 		if (cterm_normal_fg_color)
7005 		    color = cterm_normal_fg_color - 1;
7006 		else
7007 		{
7008 		    EMSG(_("E419: FG color unknown"));
7009 		    error = TRUE;
7010 		    break;
7011 		}
7012 	    }
7013 	    else if (STRICMP(arg, "bg") == 0)
7014 	    {
7015 		if (cterm_normal_bg_color > 0)
7016 		    color = cterm_normal_bg_color - 1;
7017 		else
7018 		{
7019 		    EMSG(_("E420: BG color unknown"));
7020 		    error = TRUE;
7021 		    break;
7022 		}
7023 	    }
7024 	    else
7025 	    {
7026 		static char *(color_names[28]) = {
7027 			    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7028 			    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7029 			    "Gray", "Grey",
7030 			    "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7031 			    "Blue", "LightBlue", "Green", "LightGreen",
7032 			    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7033 			    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7034 		static int color_numbers_16[28] = {0, 1, 2, 3,
7035 						 4, 5, 6, 6,
7036 						 7, 7,
7037 						 7, 7, 8, 8,
7038 						 9, 9, 10, 10,
7039 						 11, 11, 12, 12, 13,
7040 						 13, 14, 14, 15, -1};
7041 		/* for xterm with 88 colors... */
7042 		static int color_numbers_88[28] = {0, 4, 2, 6,
7043 						 1, 5, 32, 72,
7044 						 84, 84,
7045 						 7, 7, 82, 82,
7046 						 12, 43, 10, 61,
7047 						 14, 63, 9, 74, 13,
7048 						 75, 11, 78, 15, -1};
7049 		/* for xterm with 256 colors... */
7050 		static int color_numbers_256[28] = {0, 4, 2, 6,
7051 						 1, 5, 130, 130,
7052 						 248, 248,
7053 						 7, 7, 242, 242,
7054 						 12, 81, 10, 121,
7055 						 14, 159, 9, 224, 13,
7056 						 225, 11, 229, 15, -1};
7057 		/* for terminals with less than 16 colors... */
7058 		static int color_numbers_8[28] = {0, 4, 2, 6,
7059 						 1, 5, 3, 3,
7060 						 7, 7,
7061 						 7, 7, 0+8, 0+8,
7062 						 4+8, 4+8, 2+8, 2+8,
7063 						 6+8, 6+8, 1+8, 1+8, 5+8,
7064 						 5+8, 3+8, 3+8, 7+8, -1};
7065 #if defined(__QNXNTO__)
7066 		static int *color_numbers_8_qansi = color_numbers_8;
7067 		/* On qnx, the 8 & 16 color arrays are the same */
7068 		if (STRNCMP(T_NAME, "qansi", 5) == 0)
7069 		    color_numbers_8_qansi = color_numbers_16;
7070 #endif
7071 
7072 		/* reduce calls to STRICMP a bit, it can be slow */
7073 		off = TOUPPER_ASC(*arg);
7074 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7075 		    if (off == color_names[i][0]
7076 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7077 			break;
7078 		if (i < 0)
7079 		{
7080 		    EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7081 		    error = TRUE;
7082 		    break;
7083 		}
7084 
7085 		/* Use the _16 table to check if its a valid color name. */
7086 		color = color_numbers_16[i];
7087 		if (color >= 0)
7088 		{
7089 		    if (t_colors == 8)
7090 		    {
7091 			/* t_Co is 8: use the 8 colors table */
7092 #if defined(__QNXNTO__)
7093 			color = color_numbers_8_qansi[i];
7094 #else
7095 			color = color_numbers_8[i];
7096 #endif
7097 			if (key[5] == 'F')
7098 			{
7099 			    /* set/reset bold attribute to get light foreground
7100 			     * colors (on some terminals, e.g. "linux") */
7101 			    if (color & 8)
7102 			    {
7103 				HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7104 				HL_TABLE()[idx].sg_cterm_bold = TRUE;
7105 			    }
7106 			    else
7107 				HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7108 			}
7109 			color &= 7;	/* truncate to 8 colors */
7110 		    }
7111 		    else if (t_colors == 16 || t_colors == 88
7112 							   || t_colors == 256)
7113 		    {
7114 			/*
7115 			 * Guess: if the termcap entry ends in 'm', it is
7116 			 * probably an xterm-like terminal.  Use the changed
7117 			 * order for colors.
7118 			 */
7119 			if (*T_CAF != NUL)
7120 			    p = T_CAF;
7121 			else
7122 			    p = T_CSF;
7123 			if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7124 			    switch (t_colors)
7125 			    {
7126 				case 16:
7127 				    color = color_numbers_8[i];
7128 				    break;
7129 				case 88:
7130 				    color = color_numbers_88[i];
7131 				    break;
7132 				case 256:
7133 				    color = color_numbers_256[i];
7134 				    break;
7135 			    }
7136 		    }
7137 		}
7138 	    }
7139 	    /* Add one to the argument, to avoid zero.  Zero is used for
7140 	     * "NONE", then "color" is -1. */
7141 	    if (key[5] == 'F')
7142 	    {
7143 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
7144 		if (is_normal_group)
7145 		{
7146 		    cterm_normal_fg_color = color + 1;
7147 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7148 #ifdef FEAT_GUI
7149 		    /* Don't do this if the GUI is used. */
7150 		    if (!gui.in_use && !gui.starting)
7151 #endif
7152 		    {
7153 			must_redraw = CLEAR;
7154 			if (termcap_active && color >= 0)
7155 			    term_fg_color(color);
7156 		    }
7157 		}
7158 	    }
7159 	    else
7160 	    {
7161 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
7162 		if (is_normal_group)
7163 		{
7164 		    cterm_normal_bg_color = color + 1;
7165 #ifdef FEAT_GUI
7166 		    /* Don't mess with 'background' if the GUI is used. */
7167 		    if (!gui.in_use && !gui.starting)
7168 #endif
7169 		    {
7170 			must_redraw = CLEAR;
7171 			if (color >= 0)
7172 			{
7173 			    if (termcap_active)
7174 				term_bg_color(color);
7175 			    if (t_colors < 16)
7176 				i = (color == 0 || color == 4);
7177 			    else
7178 				i = (color < 7 || color == 8);
7179 			    /* Set the 'background' option if the value is
7180 			     * wrong. */
7181 			    if (i != (*p_bg == 'd'))
7182 				set_option_value((char_u *)"bg", 0L,
7183 					i ?  (char_u *)"dark"
7184 					  : (char_u *)"light", 0);
7185 			}
7186 		    }
7187 		}
7188 	    }
7189 	  }
7190 	}
7191 	else if (STRCMP(key, "GUIFG") == 0)
7192 	{
7193 #ifdef FEAT_GUI	    /* in non-GUI guifg colors are simply ignored */
7194 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7195 	    {
7196 		if (!init)
7197 		    HL_TABLE()[idx].sg_set |= SG_GUI;
7198 
7199 		i = color_name2handle(arg);
7200 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7201 		{
7202 		    HL_TABLE()[idx].sg_gui_fg = i;
7203 		    vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7204 		    if (STRCMP(arg, "NONE"))
7205 			HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7206 		    else
7207 			HL_TABLE()[idx].sg_gui_fg_name = NULL;
7208 # ifdef FEAT_GUI_X11
7209 		    if (is_menu_group)
7210 			gui.menu_fg_pixel = i;
7211 		    if (is_scrollbar_group)
7212 			gui.scroll_fg_pixel = i;
7213 #  ifdef FEAT_BEVAL
7214 		    if (is_tooltip_group)
7215 			gui.tooltip_fg_pixel = i;
7216 #  endif
7217 		    do_colors = TRUE;
7218 # endif
7219 		}
7220 	    }
7221 #endif
7222 	}
7223 	else if (STRCMP(key, "GUIBG") == 0)
7224 	{
7225 #ifdef FEAT_GUI	    /* in non-GUI guibg colors are simply ignored */
7226 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7227 	    {
7228 		if (!init)
7229 		    HL_TABLE()[idx].sg_set |= SG_GUI;
7230 
7231 		i = color_name2handle(arg);
7232 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7233 		{
7234 		    HL_TABLE()[idx].sg_gui_bg = i;
7235 		    vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7236 		    if (STRCMP(arg, "NONE") != 0)
7237 			HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7238 		    else
7239 			HL_TABLE()[idx].sg_gui_bg_name = NULL;
7240 # ifdef FEAT_GUI_X11
7241 		    if (is_menu_group)
7242 			gui.menu_bg_pixel = i;
7243 		    if (is_scrollbar_group)
7244 			gui.scroll_bg_pixel = i;
7245 #  ifdef FEAT_BEVAL
7246 		    if (is_tooltip_group)
7247 			gui.tooltip_bg_pixel = i;
7248 #  endif
7249 		    do_colors = TRUE;
7250 # endif
7251 		}
7252 	    }
7253 #endif
7254 	}
7255 	else if (STRCMP(key, "GUISP") == 0)
7256 	{
7257 #ifdef FEAT_GUI	    /* in non-GUI guisp colors are simply ignored */
7258 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7259 	    {
7260 		if (!init)
7261 		    HL_TABLE()[idx].sg_set |= SG_GUI;
7262 
7263 		i = color_name2handle(arg);
7264 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7265 		{
7266 		    HL_TABLE()[idx].sg_gui_sp = i;
7267 		    vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7268 		    if (STRCMP(arg, "NONE") != 0)
7269 			HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7270 		    else
7271 			HL_TABLE()[idx].sg_gui_sp_name = NULL;
7272 		}
7273 	    }
7274 #endif
7275 	}
7276 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7277 	{
7278 	    char_u	buf[100];
7279 	    char_u	*tname;
7280 
7281 	    if (!init)
7282 		HL_TABLE()[idx].sg_set |= SG_TERM;
7283 
7284 	    /*
7285 	     * The "start" and "stop"  arguments can be a literal escape
7286 	     * sequence, or a comma separated list of terminal codes.
7287 	     */
7288 	    if (STRNCMP(arg, "t_", 2) == 0)
7289 	    {
7290 		off = 0;
7291 		buf[0] = 0;
7292 		while (arg[off] != NUL)
7293 		{
7294 		    /* Isolate one termcap name */
7295 		    for (len = 0; arg[off + len] &&
7296 						 arg[off + len] != ','; ++len)
7297 			;
7298 		    tname = vim_strnsave(arg + off, len);
7299 		    if (tname == NULL)		/* out of memory */
7300 		    {
7301 			error = TRUE;
7302 			break;
7303 		    }
7304 		    /* lookup the escape sequence for the item */
7305 		    p = get_term_code(tname);
7306 		    vim_free(tname);
7307 		    if (p == NULL)	    /* ignore non-existing things */
7308 			p = (char_u *)"";
7309 
7310 		    /* Append it to the already found stuff */
7311 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7312 		    {
7313 			EMSG2(_("E422: terminal code too long: %s"), arg);
7314 			error = TRUE;
7315 			break;
7316 		    }
7317 		    STRCAT(buf, p);
7318 
7319 		    /* Advance to the next item */
7320 		    off += len;
7321 		    if (arg[off] == ',')	    /* another one follows */
7322 			++off;
7323 		}
7324 	    }
7325 	    else
7326 	    {
7327 		/*
7328 		 * Copy characters from arg[] to buf[], translating <> codes.
7329 		 */
7330 		for (p = arg, off = 0; off < 100 && *p; )
7331 		{
7332 		    len = trans_special(&p, buf + off, FALSE);
7333 		    if (len)		    /* recognized special char */
7334 			off += len;
7335 		    else		    /* copy as normal char */
7336 			buf[off++] = *p++;
7337 		}
7338 		buf[off] = NUL;
7339 	    }
7340 	    if (error)
7341 		break;
7342 
7343 	    if (STRCMP(buf, "NONE") == 0)	/* resetting the value */
7344 		p = NULL;
7345 	    else
7346 		p = vim_strsave(buf);
7347 	    if (key[2] == 'A')
7348 	    {
7349 		vim_free(HL_TABLE()[idx].sg_start);
7350 		HL_TABLE()[idx].sg_start = p;
7351 	    }
7352 	    else
7353 	    {
7354 		vim_free(HL_TABLE()[idx].sg_stop);
7355 		HL_TABLE()[idx].sg_stop = p;
7356 	    }
7357 	}
7358 	else
7359 	{
7360 	    EMSG2(_("E423: Illegal argument: %s"), key_start);
7361 	    error = TRUE;
7362 	    break;
7363 	}
7364 
7365 	/*
7366 	 * When highlighting has been given for a group, don't link it.
7367 	 */
7368 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7369 	    HL_TABLE()[idx].sg_link = 0;
7370 
7371 	/*
7372 	 * Continue with next argument.
7373 	 */
7374 	linep = skipwhite(linep);
7375       }
7376 
7377     /*
7378      * If there is an error, and it's a new entry, remove it from the table.
7379      */
7380     if (error && idx == highlight_ga.ga_len)
7381 	syn_unadd_group();
7382     else
7383     {
7384 	if (is_normal_group)
7385 	{
7386 	    HL_TABLE()[idx].sg_term_attr = 0;
7387 	    HL_TABLE()[idx].sg_cterm_attr = 0;
7388 #ifdef FEAT_GUI
7389 	    HL_TABLE()[idx].sg_gui_attr = 0;
7390 	    /*
7391 	     * Need to update all groups, because they might be using "bg"
7392 	     * and/or "fg", which have been changed now.
7393 	     */
7394 	    if (gui.in_use)
7395 		highlight_gui_started();
7396 #endif
7397 	}
7398 #ifdef FEAT_GUI_X11
7399 # ifdef FEAT_MENU
7400 	else if (is_menu_group)
7401 	{
7402 	    if (gui.in_use && do_colors)
7403 		gui_mch_new_menu_colors();
7404 	}
7405 # endif
7406 	else if (is_scrollbar_group)
7407 	{
7408 	    if (gui.in_use && do_colors)
7409 		gui_new_scrollbar_colors();
7410 	}
7411 # ifdef FEAT_BEVAL
7412 	else if (is_tooltip_group)
7413 	{
7414 	    if (gui.in_use && do_colors)
7415 		gui_mch_new_tooltip_colors();
7416 	}
7417 # endif
7418 #endif
7419 	else
7420 	    set_hl_attr(idx);
7421 #ifdef FEAT_EVAL
7422 	HL_TABLE()[idx].sg_scriptID = current_SID;
7423 #endif
7424 	redraw_all_later(NOT_VALID);
7425     }
7426     vim_free(key);
7427     vim_free(arg);
7428 
7429     /* Only call highlight_changed() once, after sourcing a syntax file */
7430     need_highlight_changed = TRUE;
7431 }
7432 
7433 #if defined(EXITFREE) || defined(PROTO)
7434     void
7435 free_highlight()
7436 {
7437     int	    i;
7438 
7439     for (i = 0; i < highlight_ga.ga_len; ++i)
7440     {
7441 	highlight_clear(i);
7442 	vim_free(HL_TABLE()[i].sg_name);
7443 	vim_free(HL_TABLE()[i].sg_name_u);
7444     }
7445     ga_clear(&highlight_ga);
7446 }
7447 #endif
7448 
7449 /*
7450  * Reset the cterm colors to what they were before Vim was started, if
7451  * possible.  Otherwise reset them to zero.
7452  */
7453     void
7454 restore_cterm_colors()
7455 {
7456 #if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7457     /* Since t_me has been set, this probably means that the user
7458      * wants to use this as default colors.  Need to reset default
7459      * background/foreground colors. */
7460     mch_set_normal_colors();
7461 #else
7462     cterm_normal_fg_color = 0;
7463     cterm_normal_fg_bold = 0;
7464     cterm_normal_bg_color = 0;
7465 #endif
7466 }
7467 
7468 /*
7469  * Return TRUE if highlight group "idx" has any settings.
7470  * When "check_link" is TRUE also check for an existing link.
7471  */
7472     static int
7473 hl_has_settings(idx, check_link)
7474     int		idx;
7475     int		check_link;
7476 {
7477     return (   HL_TABLE()[idx].sg_term_attr != 0
7478 	    || HL_TABLE()[idx].sg_cterm_attr != 0
7479 #ifdef FEAT_GUI
7480 	    || HL_TABLE()[idx].sg_gui_attr != 0
7481 #endif
7482 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7483 }
7484 
7485 /*
7486  * Clear highlighting for one group.
7487  */
7488     static void
7489 highlight_clear(idx)
7490     int idx;
7491 {
7492     HL_TABLE()[idx].sg_term = 0;
7493     vim_free(HL_TABLE()[idx].sg_start);
7494     HL_TABLE()[idx].sg_start = NULL;
7495     vim_free(HL_TABLE()[idx].sg_stop);
7496     HL_TABLE()[idx].sg_stop = NULL;
7497     HL_TABLE()[idx].sg_term_attr = 0;
7498     HL_TABLE()[idx].sg_cterm = 0;
7499     HL_TABLE()[idx].sg_cterm_bold = FALSE;
7500     HL_TABLE()[idx].sg_cterm_fg = 0;
7501     HL_TABLE()[idx].sg_cterm_bg = 0;
7502     HL_TABLE()[idx].sg_cterm_attr = 0;
7503 #ifdef FEAT_GUI	    /* in non-GUI fonts are simply ignored */
7504     HL_TABLE()[idx].sg_gui = 0;
7505     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7506     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7507     HL_TABLE()[idx].sg_gui_fg_name = NULL;
7508     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7509     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7510     HL_TABLE()[idx].sg_gui_bg_name = NULL;
7511     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7512     vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7513     HL_TABLE()[idx].sg_gui_sp_name = NULL;
7514     gui_mch_free_font(HL_TABLE()[idx].sg_font);
7515     HL_TABLE()[idx].sg_font = NOFONT;
7516 # ifdef FEAT_XFONTSET
7517     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7518     HL_TABLE()[idx].sg_fontset = NOFONTSET;
7519 # endif
7520     vim_free(HL_TABLE()[idx].sg_font_name);
7521     HL_TABLE()[idx].sg_font_name = NULL;
7522     HL_TABLE()[idx].sg_gui_attr = 0;
7523 #endif
7524 #ifdef FEAT_EVAL
7525     /* Clear the script ID only when there is no link, since that is not
7526      * cleared. */
7527     if (HL_TABLE()[idx].sg_link == 0)
7528 	HL_TABLE()[idx].sg_scriptID = 0;
7529 #endif
7530 }
7531 
7532 #if defined(FEAT_GUI) || defined(PROTO)
7533 /*
7534  * Set the normal foreground and background colors according to the "Normal"
7535  * highlighting group.  For X11 also set "Menu", "Scrollbar", and
7536  * "Tooltip" colors.
7537  */
7538     void
7539 set_normal_colors()
7540 {
7541     if (set_group_colors((char_u *)"Normal",
7542 			     &gui.norm_pixel, &gui.back_pixel,
7543 			     FALSE, TRUE, FALSE))
7544     {
7545 	gui_mch_new_colors();
7546 	must_redraw = CLEAR;
7547     }
7548 #ifdef FEAT_GUI_X11
7549     if (set_group_colors((char_u *)"Menu",
7550 			 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7551 			 TRUE, FALSE, FALSE))
7552     {
7553 # ifdef FEAT_MENU
7554 	gui_mch_new_menu_colors();
7555 # endif
7556 	must_redraw = CLEAR;
7557     }
7558 # ifdef FEAT_BEVAL
7559     if (set_group_colors((char_u *)"Tooltip",
7560 			 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7561 			 FALSE, FALSE, TRUE))
7562     {
7563 # ifdef FEAT_TOOLBAR
7564 	gui_mch_new_tooltip_colors();
7565 # endif
7566 	must_redraw = CLEAR;
7567     }
7568 #endif
7569     if (set_group_colors((char_u *)"Scrollbar",
7570 		    &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7571 		    FALSE, FALSE, FALSE))
7572     {
7573 	gui_new_scrollbar_colors();
7574 	must_redraw = CLEAR;
7575     }
7576 #endif
7577 }
7578 
7579 /*
7580  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7581  */
7582     static int
7583 set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7584     char_u	*name;
7585     guicolor_T	*fgp;
7586     guicolor_T	*bgp;
7587     int		do_menu;
7588     int		use_norm;
7589     int		do_tooltip;
7590 {
7591     int		idx;
7592 
7593     idx = syn_name2id(name) - 1;
7594     if (idx >= 0)
7595     {
7596 	gui_do_one_color(idx, do_menu, do_tooltip);
7597 
7598 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7599 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
7600 	else if (use_norm)
7601 	    *fgp = gui.def_norm_pixel;
7602 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7603 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
7604 	else if (use_norm)
7605 	    *bgp = gui.def_back_pixel;
7606 	return TRUE;
7607     }
7608     return FALSE;
7609 }
7610 
7611 /*
7612  * Get the font of the "Normal" group.
7613  * Returns "" when it's not found or not set.
7614  */
7615     char_u *
7616 hl_get_font_name()
7617 {
7618     int		id;
7619     char_u	*s;
7620 
7621     id = syn_name2id((char_u *)"Normal");
7622     if (id > 0)
7623     {
7624 	s = HL_TABLE()[id - 1].sg_font_name;
7625 	if (s != NULL)
7626 	    return s;
7627     }
7628     return (char_u *)"";
7629 }
7630 
7631 /*
7632  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
7633  * actually chosen to be used.
7634  */
7635     void
7636 hl_set_font_name(font_name)
7637     char_u	*font_name;
7638 {
7639     int	    id;
7640 
7641     id = syn_name2id((char_u *)"Normal");
7642     if (id > 0)
7643     {
7644 	vim_free(HL_TABLE()[id - 1].sg_font_name);
7645 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7646     }
7647 }
7648 
7649 /*
7650  * Set background color for "Normal" group.  Called by gui_set_bg_color()
7651  * when the color is known.
7652  */
7653     void
7654 hl_set_bg_color_name(name)
7655     char_u  *name;	    /* must have been allocated */
7656 {
7657     int	    id;
7658 
7659     if (name != NULL)
7660     {
7661 	id = syn_name2id((char_u *)"Normal");
7662 	if (id > 0)
7663 	{
7664 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7665 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
7666 	}
7667     }
7668 }
7669 
7670 /*
7671  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
7672  * when the color is known.
7673  */
7674     void
7675 hl_set_fg_color_name(name)
7676     char_u  *name;	    /* must have been allocated */
7677 {
7678     int	    id;
7679 
7680     if (name != NULL)
7681     {
7682 	id = syn_name2id((char_u *)"Normal");
7683 	if (id > 0)
7684 	{
7685 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7686 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
7687 	}
7688     }
7689 }
7690 
7691 /*
7692  * Return the handle for a color name.
7693  * Returns INVALCOLOR when failed.
7694  */
7695     static guicolor_T
7696 color_name2handle(name)
7697     char_u  *name;
7698 {
7699     if (STRCMP(name, "NONE") == 0)
7700 	return INVALCOLOR;
7701 
7702     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7703 	return gui.norm_pixel;
7704     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7705 	return gui.back_pixel;
7706 
7707     return gui_get_color(name);
7708 }
7709 
7710 /*
7711  * Return the handle for a font name.
7712  * Returns NOFONT when failed.
7713  */
7714     static GuiFont
7715 font_name2handle(name)
7716     char_u  *name;
7717 {
7718     if (STRCMP(name, "NONE") == 0)
7719 	return NOFONT;
7720 
7721     return gui_mch_get_font(name, TRUE);
7722 }
7723 
7724 # ifdef FEAT_XFONTSET
7725 /*
7726  * Return the handle for a fontset name.
7727  * Returns NOFONTSET when failed.
7728  */
7729     static GuiFontset
7730 fontset_name2handle(name, fixed_width)
7731     char_u	*name;
7732     int		fixed_width;
7733 {
7734     if (STRCMP(name, "NONE") == 0)
7735 	return NOFONTSET;
7736 
7737     return gui_mch_get_fontset(name, TRUE, fixed_width);
7738 }
7739 # endif
7740 
7741 /*
7742  * Get the font or fontset for one highlight group.
7743  */
7744     static void
7745 hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7746     int		idx;
7747     char_u	*arg;
7748     int		do_normal;		/* set normal font */
7749     int		do_menu UNUSED;		/* set menu font */
7750     int		do_tooltip UNUSED;	/* set tooltip font */
7751 {
7752 # ifdef FEAT_XFONTSET
7753     /* If 'guifontset' is not empty, first try using the name as a
7754      * fontset.  If that doesn't work, use it as a font name. */
7755     if (*p_guifontset != NUL
7756 #  ifdef FONTSET_ALWAYS
7757 	|| do_menu
7758 #  endif
7759 #  ifdef FEAT_BEVAL_TIP
7760 	/* In Athena & Motif, the Tooltip highlight group is always a fontset */
7761 	|| do_tooltip
7762 #  endif
7763 	    )
7764 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7765 #  ifdef FONTSET_ALWAYS
7766 		|| do_menu
7767 #  endif
7768 #  ifdef FEAT_BEVAL_TIP
7769 		|| do_tooltip
7770 #  endif
7771 		);
7772     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7773     {
7774 	/* If it worked and it's the Normal group, use it as the
7775 	 * normal fontset.  Same for the Menu group. */
7776 	if (do_normal)
7777 	    gui_init_font(arg, TRUE);
7778 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7779 	if (do_menu)
7780 	{
7781 #    ifdef FONTSET_ALWAYS
7782 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7783 #    else
7784 	    /* YIKES!  This is a bug waiting to crash the program */
7785 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
7786 #    endif
7787 	    gui_mch_new_menu_font();
7788 	}
7789 #    ifdef FEAT_BEVAL
7790 	if (do_tooltip)
7791 	{
7792 	    /* The Athena widget set cannot currently handle switching between
7793 	     * displaying a single font and a fontset.
7794 	     * If the XtNinternational resource is set to True at widget
7795 	     * creation, then a fontset is always used, otherwise an
7796 	     * XFontStruct is used.
7797 	     */
7798 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7799 	    gui_mch_new_tooltip_font();
7800 	}
7801 #    endif
7802 #   endif
7803     }
7804     else
7805 # endif
7806     {
7807 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
7808 	/* If it worked and it's the Normal group, use it as the
7809 	 * normal font.  Same for the Menu group. */
7810 	if (HL_TABLE()[idx].sg_font != NOFONT)
7811 	{
7812 	    if (do_normal)
7813 		gui_init_font(arg, FALSE);
7814 #ifndef FONTSET_ALWAYS
7815 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7816 	    if (do_menu)
7817 	    {
7818 		gui.menu_font = HL_TABLE()[idx].sg_font;
7819 		gui_mch_new_menu_font();
7820 	    }
7821 # endif
7822 #endif
7823 	}
7824     }
7825 }
7826 
7827 #endif /* FEAT_GUI */
7828 
7829 /*
7830  * Table with the specifications for an attribute number.
7831  * Note that this table is used by ALL buffers.  This is required because the
7832  * GUI can redraw at any time for any buffer.
7833  */
7834 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
7835 
7836 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7837 
7838 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
7839 
7840 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7841 
7842 #ifdef FEAT_GUI
7843 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
7844 
7845 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7846 #endif
7847 
7848 /*
7849  * Return the attr number for a set of colors and font.
7850  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7851  * if the combination is new.
7852  * Return 0 for error (no more room).
7853  */
7854     static int
7855 get_attr_entry(table, aep)
7856     garray_T	*table;
7857     attrentry_T	*aep;
7858 {
7859     int		i;
7860     attrentry_T	*taep;
7861     static int	recursive = FALSE;
7862 
7863     /*
7864      * Init the table, in case it wasn't done yet.
7865      */
7866     table->ga_itemsize = sizeof(attrentry_T);
7867     table->ga_growsize = 7;
7868 
7869     /*
7870      * Try to find an entry with the same specifications.
7871      */
7872     for (i = 0; i < table->ga_len; ++i)
7873     {
7874 	taep = &(((attrentry_T *)table->ga_data)[i]);
7875 	if (	   aep->ae_attr == taep->ae_attr
7876 		&& (
7877 #ifdef FEAT_GUI
7878 		       (table == &gui_attr_table
7879 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7880 			    && aep->ae_u.gui.bg_color
7881 						    == taep->ae_u.gui.bg_color
7882 			    && aep->ae_u.gui.sp_color
7883 						    == taep->ae_u.gui.sp_color
7884 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
7885 #  ifdef FEAT_XFONTSET
7886 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
7887 #  endif
7888 			    ))
7889 		    ||
7890 #endif
7891 		       (table == &term_attr_table
7892 			&& (aep->ae_u.term.start == NULL)
7893 					    == (taep->ae_u.term.start == NULL)
7894 			&& (aep->ae_u.term.start == NULL
7895 			    || STRCMP(aep->ae_u.term.start,
7896 						  taep->ae_u.term.start) == 0)
7897 			&& (aep->ae_u.term.stop == NULL)
7898 					     == (taep->ae_u.term.stop == NULL)
7899 			&& (aep->ae_u.term.stop == NULL
7900 			    || STRCMP(aep->ae_u.term.stop,
7901 						  taep->ae_u.term.stop) == 0))
7902 		    || (table == &cterm_attr_table
7903 			    && aep->ae_u.cterm.fg_color
7904 						  == taep->ae_u.cterm.fg_color
7905 			    && aep->ae_u.cterm.bg_color
7906 						 == taep->ae_u.cterm.bg_color)
7907 		     ))
7908 
7909 	return i + ATTR_OFF;
7910     }
7911 
7912     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
7913     {
7914 	/*
7915 	 * Running out of attribute entries!  remove all attributes, and
7916 	 * compute new ones for all groups.
7917 	 * When called recursively, we are really out of numbers.
7918 	 */
7919 	if (recursive)
7920 	{
7921 	    EMSG(_("E424: Too many different highlighting attributes in use"));
7922 	    return 0;
7923 	}
7924 	recursive = TRUE;
7925 
7926 	clear_hl_tables();
7927 
7928 	must_redraw = CLEAR;
7929 
7930 	for (i = 0; i < highlight_ga.ga_len; ++i)
7931 	    set_hl_attr(i);
7932 
7933 	recursive = FALSE;
7934     }
7935 
7936     /*
7937      * This is a new combination of colors and font, add an entry.
7938      */
7939     if (ga_grow(table, 1) == FAIL)
7940 	return 0;
7941 
7942     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7943     vim_memset(taep, 0, sizeof(attrentry_T));
7944     taep->ae_attr = aep->ae_attr;
7945 #ifdef FEAT_GUI
7946     if (table == &gui_attr_table)
7947     {
7948 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7949 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7950 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7951 	taep->ae_u.gui.font = aep->ae_u.gui.font;
7952 # ifdef FEAT_XFONTSET
7953 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7954 # endif
7955     }
7956 #endif
7957     if (table == &term_attr_table)
7958     {
7959 	if (aep->ae_u.term.start == NULL)
7960 	    taep->ae_u.term.start = NULL;
7961 	else
7962 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7963 	if (aep->ae_u.term.stop == NULL)
7964 	    taep->ae_u.term.stop = NULL;
7965 	else
7966 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7967     }
7968     else if (table == &cterm_attr_table)
7969     {
7970 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7971 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7972     }
7973     ++table->ga_len;
7974     return (table->ga_len - 1 + ATTR_OFF);
7975 }
7976 
7977 /*
7978  * Clear all highlight tables.
7979  */
7980     void
7981 clear_hl_tables()
7982 {
7983     int		i;
7984     attrentry_T	*taep;
7985 
7986 #ifdef FEAT_GUI
7987     ga_clear(&gui_attr_table);
7988 #endif
7989     for (i = 0; i < term_attr_table.ga_len; ++i)
7990     {
7991 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7992 	vim_free(taep->ae_u.term.start);
7993 	vim_free(taep->ae_u.term.stop);
7994     }
7995     ga_clear(&term_attr_table);
7996     ga_clear(&cterm_attr_table);
7997 }
7998 
7999 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
8000 /*
8001  * Combine special attributes (e.g., for spelling) with other attributes
8002  * (e.g., for syntax highlighting).
8003  * "prim_attr" overrules "char_attr".
8004  * This creates a new group when required.
8005  * Since we expect there to be few spelling mistakes we don't cache the
8006  * result.
8007  * Return the resulting attributes.
8008  */
8009     int
8010 hl_combine_attr(char_attr, prim_attr)
8011     int	    char_attr;
8012     int	    prim_attr;
8013 {
8014     attrentry_T *char_aep = NULL;
8015     attrentry_T *spell_aep;
8016     attrentry_T new_en;
8017 
8018     if (char_attr == 0)
8019 	return prim_attr;
8020     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8021 	return char_attr | prim_attr;
8022 #ifdef FEAT_GUI
8023     if (gui.in_use)
8024     {
8025 	if (char_attr > HL_ALL)
8026 	    char_aep = syn_gui_attr2entry(char_attr);
8027 	if (char_aep != NULL)
8028 	    new_en = *char_aep;
8029 	else
8030 	{
8031 	    vim_memset(&new_en, 0, sizeof(new_en));
8032 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
8033 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
8034 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
8035 	    if (char_attr <= HL_ALL)
8036 		new_en.ae_attr = char_attr;
8037 	}
8038 
8039 	if (prim_attr <= HL_ALL)
8040 	    new_en.ae_attr |= prim_attr;
8041 	else
8042 	{
8043 	    spell_aep = syn_gui_attr2entry(prim_attr);
8044 	    if (spell_aep != NULL)
8045 	    {
8046 		new_en.ae_attr |= spell_aep->ae_attr;
8047 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8048 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8049 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8050 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8051 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8052 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8053 		if (spell_aep->ae_u.gui.font != NOFONT)
8054 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8055 # ifdef FEAT_XFONTSET
8056 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8057 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8058 # endif
8059 	    }
8060 	}
8061 	return get_attr_entry(&gui_attr_table, &new_en);
8062     }
8063 #endif
8064 
8065     if (t_colors > 1)
8066     {
8067 	if (char_attr > HL_ALL)
8068 	    char_aep = syn_cterm_attr2entry(char_attr);
8069 	if (char_aep != NULL)
8070 	    new_en = *char_aep;
8071 	else
8072 	{
8073 	    vim_memset(&new_en, 0, sizeof(new_en));
8074 	    if (char_attr <= HL_ALL)
8075 		new_en.ae_attr = char_attr;
8076 	}
8077 
8078 	if (prim_attr <= HL_ALL)
8079 	    new_en.ae_attr |= prim_attr;
8080 	else
8081 	{
8082 	    spell_aep = syn_cterm_attr2entry(prim_attr);
8083 	    if (spell_aep != NULL)
8084 	    {
8085 		new_en.ae_attr |= spell_aep->ae_attr;
8086 		if (spell_aep->ae_u.cterm.fg_color > 0)
8087 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8088 		if (spell_aep->ae_u.cterm.bg_color > 0)
8089 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8090 	    }
8091 	}
8092 	return get_attr_entry(&cterm_attr_table, &new_en);
8093     }
8094 
8095     if (char_attr > HL_ALL)
8096 	char_aep = syn_term_attr2entry(char_attr);
8097     if (char_aep != NULL)
8098 	new_en = *char_aep;
8099     else
8100     {
8101 	vim_memset(&new_en, 0, sizeof(new_en));
8102 	if (char_attr <= HL_ALL)
8103 	    new_en.ae_attr = char_attr;
8104     }
8105 
8106     if (prim_attr <= HL_ALL)
8107 	new_en.ae_attr |= prim_attr;
8108     else
8109     {
8110 	spell_aep = syn_term_attr2entry(prim_attr);
8111 	if (spell_aep != NULL)
8112 	{
8113 	    new_en.ae_attr |= spell_aep->ae_attr;
8114 	    if (spell_aep->ae_u.term.start != NULL)
8115 	    {
8116 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8117 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8118 	    }
8119 	}
8120     }
8121     return get_attr_entry(&term_attr_table, &new_en);
8122 }
8123 #endif
8124 
8125 #ifdef FEAT_GUI
8126 
8127     attrentry_T *
8128 syn_gui_attr2entry(attr)
8129     int		    attr;
8130 {
8131     attr -= ATTR_OFF;
8132     if (attr >= gui_attr_table.ga_len)	    /* did ":syntax clear" */
8133 	return NULL;
8134     return &(GUI_ATTR_ENTRY(attr));
8135 }
8136 #endif /* FEAT_GUI */
8137 
8138 /*
8139  * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8140  * Only to be used when "attr" > HL_ALL.
8141  */
8142     int
8143 syn_attr2attr(attr)
8144     int	    attr;
8145 {
8146     attrentry_T	*aep;
8147 
8148 #ifdef FEAT_GUI
8149     if (gui.in_use)
8150 	aep = syn_gui_attr2entry(attr);
8151     else
8152 #endif
8153 	if (t_colors > 1)
8154 	aep = syn_cterm_attr2entry(attr);
8155     else
8156 	aep = syn_term_attr2entry(attr);
8157 
8158     if (aep == NULL)	    /* highlighting not set */
8159 	return 0;
8160     return aep->ae_attr;
8161 }
8162 
8163 
8164     attrentry_T *
8165 syn_term_attr2entry(attr)
8166     int		    attr;
8167 {
8168     attr -= ATTR_OFF;
8169     if (attr >= term_attr_table.ga_len)	    /* did ":syntax clear" */
8170 	return NULL;
8171     return &(TERM_ATTR_ENTRY(attr));
8172 }
8173 
8174     attrentry_T *
8175 syn_cterm_attr2entry(attr)
8176     int		    attr;
8177 {
8178     attr -= ATTR_OFF;
8179     if (attr >= cterm_attr_table.ga_len)	/* did ":syntax clear" */
8180 	return NULL;
8181     return &(CTERM_ATTR_ENTRY(attr));
8182 }
8183 
8184 #define LIST_ATTR   1
8185 #define LIST_STRING 2
8186 #define LIST_INT    3
8187 
8188     static void
8189 highlight_list_one(id)
8190     int		id;
8191 {
8192     struct hl_group	*sgp;
8193     int			didh = FALSE;
8194 
8195     sgp = &HL_TABLE()[id - 1];	    /* index is ID minus one */
8196 
8197     didh = highlight_list_arg(id, didh, LIST_ATTR,
8198 				    sgp->sg_term, NULL, "term");
8199     didh = highlight_list_arg(id, didh, LIST_STRING,
8200 				    0, sgp->sg_start, "start");
8201     didh = highlight_list_arg(id, didh, LIST_STRING,
8202 				    0, sgp->sg_stop, "stop");
8203 
8204     didh = highlight_list_arg(id, didh, LIST_ATTR,
8205 				    sgp->sg_cterm, NULL, "cterm");
8206     didh = highlight_list_arg(id, didh, LIST_INT,
8207 				    sgp->sg_cterm_fg, NULL, "ctermfg");
8208     didh = highlight_list_arg(id, didh, LIST_INT,
8209 				    sgp->sg_cterm_bg, NULL, "ctermbg");
8210 
8211 #ifdef FEAT_GUI
8212     didh = highlight_list_arg(id, didh, LIST_ATTR,
8213 				    sgp->sg_gui, NULL, "gui");
8214     didh = highlight_list_arg(id, didh, LIST_STRING,
8215 				    0, sgp->sg_gui_fg_name, "guifg");
8216     didh = highlight_list_arg(id, didh, LIST_STRING,
8217 				    0, sgp->sg_gui_bg_name, "guibg");
8218     didh = highlight_list_arg(id, didh, LIST_STRING,
8219 				    0, sgp->sg_gui_sp_name, "guisp");
8220     didh = highlight_list_arg(id, didh, LIST_STRING,
8221 				    0, sgp->sg_font_name, "font");
8222 #endif
8223 
8224     if (sgp->sg_link && !got_int)
8225     {
8226 	(void)syn_list_header(didh, 9999, id);
8227 	didh = TRUE;
8228 	msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8229 	msg_putchar(' ');
8230 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8231     }
8232 
8233     if (!didh)
8234 	highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
8235 #ifdef FEAT_EVAL
8236     if (p_verbose > 0)
8237 	last_set_msg(sgp->sg_scriptID);
8238 #endif
8239 }
8240 
8241     static int
8242 highlight_list_arg(id, didh, type, iarg, sarg, name)
8243     int		id;
8244     int		didh;
8245     int		type;
8246     int		iarg;
8247     char_u	*sarg;
8248     char	*name;
8249 {
8250     char_u	buf[100];
8251     char_u	*ts;
8252     int		i;
8253 
8254     if (got_int)
8255 	return FALSE;
8256     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8257     {
8258 	ts = buf;
8259 	if (type == LIST_INT)
8260 	    sprintf((char *)buf, "%d", iarg - 1);
8261 	else if (type == LIST_STRING)
8262 	    ts = sarg;
8263 	else /* type == LIST_ATTR */
8264 	{
8265 	    buf[0] = NUL;
8266 	    for (i = 0; hl_attr_table[i] != 0; ++i)
8267 	    {
8268 		if (iarg & hl_attr_table[i])
8269 		{
8270 		    if (buf[0] != NUL)
8271 			STRCAT(buf, ",");
8272 		    STRCAT(buf, hl_name_table[i]);
8273 		    iarg &= ~hl_attr_table[i];	    /* don't want "inverse" */
8274 		}
8275 	    }
8276 	}
8277 
8278 	(void)syn_list_header(didh,
8279 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8280 	didh = TRUE;
8281 	if (!got_int)
8282 	{
8283 	    if (*name != NUL)
8284 	    {
8285 		MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8286 		MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8287 	    }
8288 	    msg_outtrans(ts);
8289 	}
8290     }
8291     return didh;
8292 }
8293 
8294 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8295 /*
8296  * Return "1" if highlight group "id" has attribute "flag".
8297  * Return NULL otherwise.
8298  */
8299     char_u *
8300 highlight_has_attr(id, flag, modec)
8301     int		id;
8302     int		flag;
8303     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
8304 {
8305     int		attr;
8306 
8307     if (id <= 0 || id > highlight_ga.ga_len)
8308 	return NULL;
8309 
8310 #ifdef FEAT_GUI
8311     if (modec == 'g')
8312 	attr = HL_TABLE()[id - 1].sg_gui;
8313     else
8314 #endif
8315 	 if (modec == 'c')
8316 	attr = HL_TABLE()[id - 1].sg_cterm;
8317     else
8318 	attr = HL_TABLE()[id - 1].sg_term;
8319 
8320     if (attr & flag)
8321 	return (char_u *)"1";
8322     return NULL;
8323 }
8324 #endif
8325 
8326 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8327 /*
8328  * Return color name of highlight group "id".
8329  */
8330     char_u *
8331 highlight_color(id, what, modec)
8332     int		id;
8333     char_u	*what;	/* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
8334     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
8335 {
8336     static char_u	name[20];
8337     int			n;
8338     int			fg = FALSE;
8339 # ifdef FEAT_GUI
8340     int			sp = FALSE;
8341     int			font = FALSE;
8342 # endif
8343 
8344     if (id <= 0 || id > highlight_ga.ga_len)
8345 	return NULL;
8346 
8347     if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
8348 	fg = TRUE;
8349 # ifdef FEAT_GUI
8350     else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
8351              && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
8352 	font = TRUE;
8353     else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
8354 	sp = TRUE;
8355     else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8356 	return NULL;
8357     if (modec == 'g')
8358     {
8359 	/* return font name */
8360 	if (font)
8361 	    return HL_TABLE()[id - 1].sg_font_name;
8362 
8363 	/* return #RRGGBB form (only possible when GUI is running) */
8364 	if (gui.in_use && what[2] == '#')
8365 	{
8366 	    guicolor_T		color;
8367 	    long_u		rgb;
8368 	    static char_u	buf[10];
8369 
8370 	    if (fg)
8371 		color = HL_TABLE()[id - 1].sg_gui_fg;
8372 	    else if (sp)
8373 		color = HL_TABLE()[id - 1].sg_gui_sp;
8374 	    else
8375 		color = HL_TABLE()[id - 1].sg_gui_bg;
8376 	    if (color == INVALCOLOR)
8377 		return NULL;
8378 	    rgb = gui_mch_get_rgb(color);
8379 	    sprintf((char *)buf, "#%02x%02x%02x",
8380 				      (unsigned)(rgb >> 16),
8381 				      (unsigned)(rgb >> 8) & 255,
8382 				      (unsigned)rgb & 255);
8383 	    return buf;
8384 	}
8385 	if (fg)
8386 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
8387 	if (sp)
8388 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
8389 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
8390     }
8391     if (font || sp)
8392 	return NULL;
8393 # endif
8394     if (modec == 'c')
8395     {
8396 	if (fg)
8397 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8398 	else
8399 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8400 	sprintf((char *)name, "%d", n);
8401 	return name;
8402     }
8403     /* term doesn't have color */
8404     return NULL;
8405 }
8406 #endif
8407 
8408 #if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8409 	|| defined(PROTO)
8410 /*
8411  * Return color name of highlight group "id" as RGB value.
8412  */
8413     long_u
8414 highlight_gui_color_rgb(id, fg)
8415     int		id;
8416     int		fg;	/* TRUE = fg, FALSE = bg */
8417 {
8418     guicolor_T	color;
8419 
8420     if (id <= 0 || id > highlight_ga.ga_len)
8421 	return 0L;
8422 
8423     if (fg)
8424 	color = HL_TABLE()[id - 1].sg_gui_fg;
8425     else
8426 	color = HL_TABLE()[id - 1].sg_gui_bg;
8427 
8428     if (color == INVALCOLOR)
8429 	return 0L;
8430 
8431     return gui_mch_get_rgb(color);
8432 }
8433 #endif
8434 
8435 /*
8436  * Output the syntax list header.
8437  * Return TRUE when started a new line.
8438  */
8439     static int
8440 syn_list_header(did_header, outlen, id)
8441     int	    did_header;		/* did header already */
8442     int	    outlen;		/* length of string that comes */
8443     int	    id;			/* highlight group id */
8444 {
8445     int	    endcol = 19;
8446     int	    newline = TRUE;
8447 
8448     if (!did_header)
8449     {
8450 	msg_putchar('\n');
8451 	if (got_int)
8452 	    return TRUE;
8453 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
8454 	endcol = 15;
8455     }
8456     else if (msg_col + outlen + 1 >= Columns)
8457     {
8458 	msg_putchar('\n');
8459 	if (got_int)
8460 	    return TRUE;
8461     }
8462     else
8463     {
8464 	if (msg_col >= endcol)	/* wrap around is like starting a new line */
8465 	    newline = FALSE;
8466     }
8467 
8468     if (msg_col >= endcol)	/* output at least one space */
8469 	endcol = msg_col + 1;
8470     if (Columns <= endcol)	/* avoid hang for tiny window */
8471 	endcol = Columns - 1;
8472 
8473     msg_advance(endcol);
8474 
8475     /* Show "xxx" with the attributes. */
8476     if (!did_header)
8477     {
8478 	msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8479 	msg_putchar(' ');
8480     }
8481 
8482     return newline;
8483 }
8484 
8485 /*
8486  * Set the attribute numbers for a highlight group.
8487  * Called after one of the attributes has changed.
8488  */
8489     static void
8490 set_hl_attr(idx)
8491     int		idx;	    /* index in array */
8492 {
8493     attrentry_T		at_en;
8494     struct hl_group	*sgp = HL_TABLE() + idx;
8495 
8496     /* The "Normal" group doesn't need an attribute number */
8497     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8498 	return;
8499 
8500 #ifdef FEAT_GUI
8501     /*
8502      * For the GUI mode: If there are other than "normal" highlighting
8503      * attributes, need to allocate an attr number.
8504      */
8505     if (sgp->sg_gui_fg == INVALCOLOR
8506 	    && sgp->sg_gui_bg == INVALCOLOR
8507 	    && sgp->sg_gui_sp == INVALCOLOR
8508 	    && sgp->sg_font == NOFONT
8509 # ifdef FEAT_XFONTSET
8510 	    && sgp->sg_fontset == NOFONTSET
8511 # endif
8512 	    )
8513     {
8514 	sgp->sg_gui_attr = sgp->sg_gui;
8515     }
8516     else
8517     {
8518 	at_en.ae_attr = sgp->sg_gui;
8519 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8520 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
8521 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
8522 	at_en.ae_u.gui.font = sgp->sg_font;
8523 # ifdef FEAT_XFONTSET
8524 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
8525 # endif
8526 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8527     }
8528 #endif
8529     /*
8530      * For the term mode: If there are other than "normal" highlighting
8531      * attributes, need to allocate an attr number.
8532      */
8533     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8534 	sgp->sg_term_attr = sgp->sg_term;
8535     else
8536     {
8537 	at_en.ae_attr = sgp->sg_term;
8538 	at_en.ae_u.term.start = sgp->sg_start;
8539 	at_en.ae_u.term.stop = sgp->sg_stop;
8540 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8541     }
8542 
8543     /*
8544      * For the color term mode: If there are other than "normal"
8545      * highlighting attributes, need to allocate an attr number.
8546      */
8547     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8548 	sgp->sg_cterm_attr = sgp->sg_cterm;
8549     else
8550     {
8551 	at_en.ae_attr = sgp->sg_cterm;
8552 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8553 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8554 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8555     }
8556 }
8557 
8558 /*
8559  * Lookup a highlight group name and return it's ID.
8560  * If it is not found, 0 is returned.
8561  */
8562     int
8563 syn_name2id(name)
8564     char_u	*name;
8565 {
8566     int		i;
8567     char_u	name_u[200];
8568 
8569     /* Avoid using stricmp() too much, it's slow on some systems */
8570     /* Avoid alloc()/free(), these are slow too.  ID names over 200 chars
8571      * don't deserve to be found! */
8572     vim_strncpy(name_u, name, 199);
8573     vim_strup(name_u);
8574     for (i = highlight_ga.ga_len; --i >= 0; )
8575 	if (HL_TABLE()[i].sg_name_u != NULL
8576 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8577 	    break;
8578     return i + 1;
8579 }
8580 
8581 #if defined(FEAT_EVAL) || defined(PROTO)
8582 /*
8583  * Return TRUE if highlight group "name" exists.
8584  */
8585     int
8586 highlight_exists(name)
8587     char_u	*name;
8588 {
8589     return (syn_name2id(name) > 0);
8590 }
8591 
8592 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8593 /*
8594  * Return the name of highlight group "id".
8595  * When not a valid ID return an empty string.
8596  */
8597     char_u *
8598 syn_id2name(id)
8599     int		id;
8600 {
8601     if (id <= 0 || id > highlight_ga.ga_len)
8602 	return (char_u *)"";
8603     return HL_TABLE()[id - 1].sg_name;
8604 }
8605 # endif
8606 #endif
8607 
8608 /*
8609  * Like syn_name2id(), but take a pointer + length argument.
8610  */
8611     int
8612 syn_namen2id(linep, len)
8613     char_u  *linep;
8614     int	    len;
8615 {
8616     char_u  *name;
8617     int	    id = 0;
8618 
8619     name = vim_strnsave(linep, len);
8620     if (name != NULL)
8621     {
8622 	id = syn_name2id(name);
8623 	vim_free(name);
8624     }
8625     return id;
8626 }
8627 
8628 /*
8629  * Find highlight group name in the table and return it's ID.
8630  * The argument is a pointer to the name and the length of the name.
8631  * If it doesn't exist yet, a new entry is created.
8632  * Return 0 for failure.
8633  */
8634     int
8635 syn_check_group(pp, len)
8636     char_u		*pp;
8637     int			len;
8638 {
8639     int	    id;
8640     char_u  *name;
8641 
8642     name = vim_strnsave(pp, len);
8643     if (name == NULL)
8644 	return 0;
8645 
8646     id = syn_name2id(name);
8647     if (id == 0)			/* doesn't exist yet */
8648 	id = syn_add_group(name);
8649     else
8650 	vim_free(name);
8651     return id;
8652 }
8653 
8654 /*
8655  * Add new highlight group and return it's ID.
8656  * "name" must be an allocated string, it will be consumed.
8657  * Return 0 for failure.
8658  */
8659     static int
8660 syn_add_group(name)
8661     char_u	*name;
8662 {
8663     char_u	*p;
8664 
8665     /* Check that the name is ASCII letters, digits and underscore. */
8666     for (p = name; *p != NUL; ++p)
8667     {
8668 	if (!vim_isprintc(*p))
8669 	{
8670 	    EMSG(_("E669: Unprintable character in group name"));
8671 	    vim_free(name);
8672 	    return 0;
8673 	}
8674 	else if (!ASCII_ISALNUM(*p) && *p != '_')
8675 	{
8676 	    /* This is an error, but since there previously was no check only
8677 	     * give a warning. */
8678 	    msg_source(hl_attr(HLF_W));
8679 	    MSG(_("W18: Invalid character in group name"));
8680 	    break;
8681 	}
8682     }
8683 
8684     /*
8685      * First call for this growarray: init growing array.
8686      */
8687     if (highlight_ga.ga_data == NULL)
8688     {
8689 	highlight_ga.ga_itemsize = sizeof(struct hl_group);
8690 	highlight_ga.ga_growsize = 10;
8691     }
8692 
8693     /*
8694      * Make room for at least one other syntax_highlight entry.
8695      */
8696     if (ga_grow(&highlight_ga, 1) == FAIL)
8697     {
8698 	vim_free(name);
8699 	return 0;
8700     }
8701 
8702     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8703     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8704     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8705 #ifdef FEAT_GUI
8706     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8707     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
8708     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
8709 #endif
8710     ++highlight_ga.ga_len;
8711 
8712     return highlight_ga.ga_len;		    /* ID is index plus one */
8713 }
8714 
8715 /*
8716  * When, just after calling syn_add_group(), an error is discovered, this
8717  * function deletes the new name.
8718  */
8719     static void
8720 syn_unadd_group()
8721 {
8722     --highlight_ga.ga_len;
8723     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8724     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8725 }
8726 
8727 /*
8728  * Translate a group ID to highlight attributes.
8729  */
8730     int
8731 syn_id2attr(hl_id)
8732     int			hl_id;
8733 {
8734     int			attr;
8735     struct hl_group	*sgp;
8736 
8737     hl_id = syn_get_final_id(hl_id);
8738     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8739 
8740 #ifdef FEAT_GUI
8741     /*
8742      * Only use GUI attr when the GUI is being used.
8743      */
8744     if (gui.in_use)
8745 	attr = sgp->sg_gui_attr;
8746     else
8747 #endif
8748 	if (t_colors > 1)
8749 	    attr = sgp->sg_cterm_attr;
8750 	else
8751 	    attr = sgp->sg_term_attr;
8752 
8753     return attr;
8754 }
8755 
8756 #ifdef FEAT_GUI
8757 /*
8758  * Get the GUI colors and attributes for a group ID.
8759  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8760  */
8761     int
8762 syn_id2colors(hl_id, fgp, bgp)
8763     int		hl_id;
8764     guicolor_T	*fgp;
8765     guicolor_T	*bgp;
8766 {
8767     struct hl_group	*sgp;
8768 
8769     hl_id = syn_get_final_id(hl_id);
8770     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8771 
8772     *fgp = sgp->sg_gui_fg;
8773     *bgp = sgp->sg_gui_bg;
8774     return sgp->sg_gui;
8775 }
8776 #endif
8777 
8778 /*
8779  * Translate a group ID to the final group ID (following links).
8780  */
8781     int
8782 syn_get_final_id(hl_id)
8783     int			hl_id;
8784 {
8785     int			count;
8786     struct hl_group	*sgp;
8787 
8788     if (hl_id > highlight_ga.ga_len || hl_id < 1)
8789 	return 0;			/* Can be called from eval!! */
8790 
8791     /*
8792      * Follow links until there is no more.
8793      * Look out for loops!  Break after 100 links.
8794      */
8795     for (count = 100; --count >= 0; )
8796     {
8797 	sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8798 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8799 	    break;
8800 	hl_id = sgp->sg_link;
8801     }
8802 
8803     return hl_id;
8804 }
8805 
8806 #ifdef FEAT_GUI
8807 /*
8808  * Call this function just after the GUI has started.
8809  * It finds the font and color handles for the highlighting groups.
8810  */
8811     void
8812 highlight_gui_started()
8813 {
8814     int	    idx;
8815 
8816     /* First get the colors from the "Normal" and "Menu" group, if set */
8817     set_normal_colors();
8818 
8819     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8820 	gui_do_one_color(idx, FALSE, FALSE);
8821 
8822     highlight_changed();
8823 }
8824 
8825     static void
8826 gui_do_one_color(idx, do_menu, do_tooltip)
8827     int		idx;
8828     int		do_menu;	/* TRUE: might set the menu font */
8829     int		do_tooltip;	/* TRUE: might set the tooltip font */
8830 {
8831     int		didit = FALSE;
8832 
8833     if (HL_TABLE()[idx].sg_font_name != NULL)
8834     {
8835 	hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8836 		   do_tooltip);
8837 	didit = TRUE;
8838     }
8839     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8840     {
8841 	HL_TABLE()[idx].sg_gui_fg =
8842 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8843 	didit = TRUE;
8844     }
8845     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8846     {
8847 	HL_TABLE()[idx].sg_gui_bg =
8848 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8849 	didit = TRUE;
8850     }
8851     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8852     {
8853 	HL_TABLE()[idx].sg_gui_sp =
8854 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8855 	didit = TRUE;
8856     }
8857     if (didit)	/* need to get a new attr number */
8858 	set_hl_attr(idx);
8859 }
8860 
8861 #endif
8862 
8863 /*
8864  * Translate the 'highlight' option into attributes in highlight_attr[] and
8865  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
8866  * corresponding highlights to use on top of HLF_SNC is computed.
8867  * Called only when the 'highlight' option has been changed and upon first
8868  * screen redraw after any :highlight command.
8869  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
8870  */
8871     int
8872 highlight_changed()
8873 {
8874     int		hlf;
8875     int		i;
8876     char_u	*p;
8877     int		attr;
8878     char_u	*end;
8879     int		id;
8880 #ifdef USER_HIGHLIGHT
8881     char_u      userhl[10];
8882 # ifdef FEAT_STL_OPT
8883     int		id_SNC = -1;
8884     int		id_S = -1;
8885     int		hlcnt;
8886 # endif
8887 #endif
8888     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
8889 
8890     need_highlight_changed = FALSE;
8891 
8892     /*
8893      * Clear all attributes.
8894      */
8895     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8896 	highlight_attr[hlf] = 0;
8897 
8898     /*
8899      * First set all attributes to their default value.
8900      * Then use the attributes from the 'highlight' option.
8901      */
8902     for (i = 0; i < 2; ++i)
8903     {
8904 	if (i)
8905 	    p = p_hl;
8906 	else
8907 	    p = get_highlight_default();
8908 	if (p == NULL)	    /* just in case */
8909 	    continue;
8910 
8911 	while (*p)
8912 	{
8913 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8914 		if (hl_flags[hlf] == *p)
8915 		    break;
8916 	    ++p;
8917 	    if (hlf == (int)HLF_COUNT || *p == NUL)
8918 		return FAIL;
8919 
8920 	    /*
8921 	     * Allow several hl_flags to be combined, like "bu" for
8922 	     * bold-underlined.
8923 	     */
8924 	    attr = 0;
8925 	    for ( ; *p && *p != ','; ++p)	    /* parse upto comma */
8926 	    {
8927 		if (vim_iswhite(*p))		    /* ignore white space */
8928 		    continue;
8929 
8930 		if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
8931 		    return FAIL;
8932 
8933 		switch (*p)
8934 		{
8935 		    case 'b':	attr |= HL_BOLD;
8936 				break;
8937 		    case 'i':	attr |= HL_ITALIC;
8938 				break;
8939 		    case '-':
8940 		    case 'n':			    /* no highlighting */
8941 				break;
8942 		    case 'r':	attr |= HL_INVERSE;
8943 				break;
8944 		    case 's':	attr |= HL_STANDOUT;
8945 				break;
8946 		    case 'u':	attr |= HL_UNDERLINE;
8947 				break;
8948 		    case 'c':	attr |= HL_UNDERCURL;
8949 				break;
8950 		    case ':':	++p;		    /* highlight group name */
8951 				if (attr || *p == NUL)	 /* no combinations */
8952 				    return FAIL;
8953 				end = vim_strchr(p, ',');
8954 				if (end == NULL)
8955 				    end = p + STRLEN(p);
8956 				id = syn_check_group(p, (int)(end - p));
8957 				if (id == 0)
8958 				    return FAIL;
8959 				attr = syn_id2attr(id);
8960 				p = end - 1;
8961 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8962 				if (hlf == (int)HLF_SNC)
8963 				    id_SNC = syn_get_final_id(id);
8964 				else if (hlf == (int)HLF_S)
8965 				    id_S = syn_get_final_id(id);
8966 #endif
8967 				break;
8968 		    default:	return FAIL;
8969 		}
8970 	    }
8971 	    highlight_attr[hlf] = attr;
8972 
8973 	    p = skip_to_option_part(p);	    /* skip comma and spaces */
8974 	}
8975     }
8976 
8977 #ifdef USER_HIGHLIGHT
8978     /* Setup the user highlights
8979      *
8980      * Temporarily  utilize 10 more hl entries.  Have to be in there
8981      * simultaneously in case of table overflows in get_attr_entry()
8982      */
8983 # ifdef FEAT_STL_OPT
8984     if (ga_grow(&highlight_ga, 10) == FAIL)
8985 	return FAIL;
8986     hlcnt = highlight_ga.ga_len;
8987     if (id_S == 0)
8988     {		    /* Make sure id_S is always valid to simplify code below */
8989 	memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8990 	HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8991 	id_S = hlcnt + 10;
8992     }
8993 # endif
8994     for (i = 0; i < 9; i++)
8995     {
8996 	sprintf((char *)userhl, "User%d", i + 1);
8997 	id = syn_name2id(userhl);
8998 	if (id == 0)
8999 	{
9000 	    highlight_user[i] = 0;
9001 # ifdef FEAT_STL_OPT
9002 	    highlight_stlnc[i] = 0;
9003 # endif
9004 	}
9005 	else
9006 	{
9007 # ifdef FEAT_STL_OPT
9008 	    struct hl_group *hlt = HL_TABLE();
9009 # endif
9010 
9011 	    highlight_user[i] = syn_id2attr(id);
9012 # ifdef FEAT_STL_OPT
9013 	    if (id_SNC == 0)
9014 	    {
9015 		memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9016 		hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9017 		hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
9018 #  ifdef FEAT_GUI
9019 		hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9020 #  endif
9021 	    }
9022 	    else
9023 		mch_memmove(&hlt[hlcnt + i],
9024 			    &hlt[id_SNC - 1],
9025 			    sizeof(struct hl_group));
9026 	    hlt[hlcnt + i].sg_link = 0;
9027 
9028 	    /* Apply difference between UserX and HLF_S to HLF_SNC */
9029 	    hlt[hlcnt + i].sg_term ^=
9030 		hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9031 	    if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9032 		hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9033 	    if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9034 		hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9035 	    hlt[hlcnt + i].sg_cterm ^=
9036 		hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9037 	    if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9038 		hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9039 	    if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9040 		hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9041 #  ifdef FEAT_GUI
9042 	    hlt[hlcnt + i].sg_gui ^=
9043 		hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9044 	    if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9045 		hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9046 	    if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9047 		hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9048 	    if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9049 		hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9050 	    if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9051 		hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9052 #   ifdef FEAT_XFONTSET
9053 	    if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9054 		hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9055 #   endif
9056 #  endif
9057 	    highlight_ga.ga_len = hlcnt + i + 1;
9058 	    set_hl_attr(hlcnt + i);	/* At long last we can apply */
9059 	    highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9060 # endif
9061 	}
9062     }
9063 # ifdef FEAT_STL_OPT
9064     highlight_ga.ga_len = hlcnt;
9065 # endif
9066 
9067 #endif /* USER_HIGHLIGHT */
9068 
9069     return OK;
9070 }
9071 
9072 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
9073 
9074 static void highlight_list __ARGS((void));
9075 static void highlight_list_two __ARGS((int cnt, int attr));
9076 
9077 /*
9078  * Handle command line completion for :highlight command.
9079  */
9080     void
9081 set_context_in_highlight_cmd(xp, arg)
9082     expand_T	*xp;
9083     char_u	*arg;
9084 {
9085     char_u	*p;
9086 
9087     /* Default: expand group names */
9088     xp->xp_context = EXPAND_HIGHLIGHT;
9089     xp->xp_pattern = arg;
9090     include_link = 2;
9091     include_default = 1;
9092 
9093     /* (part of) subcommand already typed */
9094     if (*arg != NUL)
9095     {
9096 	p = skiptowhite(arg);
9097 	if (*p != NUL)			/* past "default" or group name */
9098 	{
9099 	    include_default = 0;
9100 	    if (STRNCMP("default", arg, p - arg) == 0)
9101 	    {
9102 		arg = skipwhite(p);
9103 		xp->xp_pattern = arg;
9104 		p = skiptowhite(arg);
9105 	    }
9106 	    if (*p != NUL)			/* past group name */
9107 	    {
9108 		include_link = 0;
9109 		if (arg[1] == 'i' && arg[0] == 'N')
9110 		    highlight_list();
9111 		if (STRNCMP("link", arg, p - arg) == 0
9112 			|| STRNCMP("clear", arg, p - arg) == 0)
9113 		{
9114 		    xp->xp_pattern = skipwhite(p);
9115 		    p = skiptowhite(xp->xp_pattern);
9116 		    if (*p != NUL)		/* past first group name */
9117 		    {
9118 			xp->xp_pattern = skipwhite(p);
9119 			p = skiptowhite(xp->xp_pattern);
9120 		    }
9121 		}
9122 		if (*p != NUL)			/* past group name(s) */
9123 		    xp->xp_context = EXPAND_NOTHING;
9124 	    }
9125 	}
9126     }
9127 }
9128 
9129 /*
9130  * List highlighting matches in a nice way.
9131  */
9132     static void
9133 highlight_list()
9134 {
9135     int		i;
9136 
9137     for (i = 10; --i >= 0; )
9138 	highlight_list_two(i, hl_attr(HLF_D));
9139     for (i = 40; --i >= 0; )
9140 	highlight_list_two(99, 0);
9141 }
9142 
9143     static void
9144 highlight_list_two(cnt, attr)
9145     int	    cnt;
9146     int	    attr;
9147 {
9148     msg_puts_attr((char_u *)("N \bI \b!  \b" + cnt / 11), attr);
9149     msg_clr_eos();
9150     out_flush();
9151     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9152 }
9153 
9154 #endif /* FEAT_CMDL_COMPL */
9155 
9156 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9157     || defined(FEAT_SIGNS) || defined(PROTO)
9158 /*
9159  * Function given to ExpandGeneric() to obtain the list of group names.
9160  * Also used for synIDattr() function.
9161  */
9162     char_u *
9163 get_highlight_name(xp, idx)
9164     expand_T	*xp UNUSED;
9165     int		idx;
9166 {
9167 #ifdef FEAT_CMDL_COMPL
9168     if (idx == highlight_ga.ga_len && include_none != 0)
9169 	return (char_u *)"none";
9170     if (idx == highlight_ga.ga_len + include_none && include_default != 0)
9171 	return (char_u *)"default";
9172     if (idx == highlight_ga.ga_len + include_none + include_default
9173 							 && include_link != 0)
9174 	return (char_u *)"link";
9175     if (idx == highlight_ga.ga_len + include_none + include_default + 1
9176 							 && include_link != 0)
9177 	return (char_u *)"clear";
9178 #endif
9179     if (idx < 0 || idx >= highlight_ga.ga_len)
9180 	return NULL;
9181     return HL_TABLE()[idx].sg_name;
9182 }
9183 #endif
9184 
9185 #if defined(FEAT_GUI) || defined(PROTO)
9186 /*
9187  * Free all the highlight group fonts.
9188  * Used when quitting for systems which need it.
9189  */
9190     void
9191 free_highlight_fonts()
9192 {
9193     int	    idx;
9194 
9195     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9196     {
9197 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
9198 	HL_TABLE()[idx].sg_font = NOFONT;
9199 # ifdef FEAT_XFONTSET
9200 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9201 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
9202 # endif
9203     }
9204 
9205     gui_mch_free_font(gui.norm_font);
9206 # ifdef FEAT_XFONTSET
9207     gui_mch_free_fontset(gui.fontset);
9208 # endif
9209 # ifndef HAVE_GTK2
9210     gui_mch_free_font(gui.bold_font);
9211     gui_mch_free_font(gui.ital_font);
9212     gui_mch_free_font(gui.boldital_font);
9213 # endif
9214 }
9215 #endif
9216 
9217 /**************************************
9218  *  End of Highlighting stuff	      *
9219  **************************************/
9220