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