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