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