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