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