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