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