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