xref: /vim-8.2.3635/src/syntax.c (revision f4cd3e80)
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_error = FALSE;	    /* clear previous error */
3186     buf->b_syn_ic = FALSE;	    /* Use case, by default */
3187     buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3188     buf->b_syn_containedin = FALSE;
3189 
3190     /* free the keywords */
3191     clear_keywtab(&buf->b_keywtab);
3192     clear_keywtab(&buf->b_keywtab_ic);
3193 
3194     /* free the syntax patterns */
3195     for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3196 	syn_clear_pattern(buf, i);
3197     ga_clear(&buf->b_syn_patterns);
3198 
3199     /* free the syntax clusters */
3200     for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3201 	syn_clear_cluster(buf, i);
3202     ga_clear(&buf->b_syn_clusters);
3203     buf->b_spell_cluster_id = 0;
3204     buf->b_nospell_cluster_id = 0;
3205 
3206     buf->b_syn_sync_flags = 0;
3207     buf->b_syn_sync_minlines = 0;
3208     buf->b_syn_sync_maxlines = 0;
3209     buf->b_syn_sync_linebreaks = 0;
3210 
3211     vim_free(buf->b_syn_linecont_prog);
3212     buf->b_syn_linecont_prog = NULL;
3213     vim_free(buf->b_syn_linecont_pat);
3214     buf->b_syn_linecont_pat = NULL;
3215 #ifdef FEAT_FOLDING
3216     buf->b_syn_folditems = 0;
3217 #endif
3218 
3219     /* free the stored states */
3220     syn_stack_free_all(buf);
3221     invalidate_current_state();
3222 }
3223 
3224 /*
3225  * Clear syncing info for one buffer.
3226  */
3227     static void
3228 syntax_sync_clear()
3229 {
3230     int i;
3231 
3232     /* free the syntax patterns */
3233     for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3234 	if (SYN_ITEMS(curbuf)[i].sp_syncing)
3235 	    syn_remove_pattern(curbuf, i);
3236 
3237     curbuf->b_syn_sync_flags = 0;
3238     curbuf->b_syn_sync_minlines = 0;
3239     curbuf->b_syn_sync_maxlines = 0;
3240     curbuf->b_syn_sync_linebreaks = 0;
3241 
3242     vim_free(curbuf->b_syn_linecont_prog);
3243     curbuf->b_syn_linecont_prog = NULL;
3244     vim_free(curbuf->b_syn_linecont_pat);
3245     curbuf->b_syn_linecont_pat = NULL;
3246 
3247     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3248 }
3249 
3250 /*
3251  * Remove one pattern from the buffer's pattern list.
3252  */
3253     static void
3254 syn_remove_pattern(buf, idx)
3255     buf_T	*buf;
3256     int		idx;
3257 {
3258     synpat_T	*spp;
3259 
3260     spp = &(SYN_ITEMS(buf)[idx]);
3261 #ifdef FEAT_FOLDING
3262     if (spp->sp_flags & HL_FOLD)
3263 	--buf->b_syn_folditems;
3264 #endif
3265     syn_clear_pattern(buf, idx);
3266     mch_memmove(spp, spp + 1,
3267 		   sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3268     --buf->b_syn_patterns.ga_len;
3269 }
3270 
3271 /*
3272  * Clear and free one syntax pattern.  When clearing all, must be called from
3273  * last to first!
3274  */
3275     static void
3276 syn_clear_pattern(buf, i)
3277     buf_T	*buf;
3278     int		i;
3279 {
3280     vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3281     vim_free(SYN_ITEMS(buf)[i].sp_prog);
3282     /* Only free sp_cont_list and sp_next_list of first start pattern */
3283     if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3284     {
3285 	vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3286 	vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3287     }
3288 }
3289 
3290 /*
3291  * Clear and free one syntax cluster.
3292  */
3293     static void
3294 syn_clear_cluster(buf, i)
3295     buf_T	*buf;
3296     int		i;
3297 {
3298     vim_free(SYN_CLSTR(buf)[i].scl_name);
3299     vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3300     vim_free(SYN_CLSTR(buf)[i].scl_list);
3301 }
3302 
3303 /*
3304  * Handle ":syntax clear" command.
3305  */
3306     static void
3307 syn_cmd_clear(eap, syncing)
3308     exarg_T	*eap;
3309     int		syncing;
3310 {
3311     char_u	*arg = eap->arg;
3312     char_u	*arg_end;
3313     int		id;
3314 
3315     eap->nextcmd = find_nextcmd(arg);
3316     if (eap->skip)
3317 	return;
3318 
3319     /*
3320      * We have to disable this within ":syn include @group filename",
3321      * because otherwise @group would get deleted.
3322      * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3323      * clear".
3324      */
3325     if (curbuf->b_syn_topgrp != 0)
3326 	return;
3327 
3328     if (ends_excmd(*arg))
3329     {
3330 	/*
3331 	 * No argument: Clear all syntax items.
3332 	 */
3333 	if (syncing)
3334 	    syntax_sync_clear();
3335 	else
3336 	{
3337 	    syntax_clear(curbuf);
3338 	    do_unlet((char_u *)"b:current_syntax", TRUE);
3339 	}
3340     }
3341     else
3342     {
3343 	/*
3344 	 * Clear the group IDs that are in the argument.
3345 	 */
3346 	while (!ends_excmd(*arg))
3347 	{
3348 	    arg_end = skiptowhite(arg);
3349 	    if (*arg == '@')
3350 	    {
3351 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3352 		if (id == 0)
3353 		{
3354 		    EMSG2(_("E391: No such syntax cluster: %s"), arg);
3355 		    break;
3356 		}
3357 		else
3358 		{
3359 		    /*
3360 		     * We can't physically delete a cluster without changing
3361 		     * the IDs of other clusters, so we do the next best thing
3362 		     * and make it empty.
3363 		     */
3364 		    short scl_id = id - SYNID_CLUSTER;
3365 
3366 		    vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3367 		    SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3368 		}
3369 	    }
3370 	    else
3371 	    {
3372 		id = syn_namen2id(arg, (int)(arg_end - arg));
3373 		if (id == 0)
3374 		{
3375 		    EMSG2(_(e_nogroup), arg);
3376 		    break;
3377 		}
3378 		else
3379 		    syn_clear_one(id, syncing);
3380 	    }
3381 	    arg = skipwhite(arg_end);
3382 	}
3383     }
3384     redraw_curbuf_later(NOT_VALID);
3385     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
3386 }
3387 
3388 /*
3389  * Clear one syntax group for the current buffer.
3390  */
3391     static void
3392 syn_clear_one(id, syncing)
3393     int		id;
3394     int		syncing;
3395 {
3396     synpat_T	*spp;
3397     int		idx;
3398 
3399     /* Clear keywords only when not ":syn sync clear group-name" */
3400     if (!syncing)
3401     {
3402 	(void)syn_clear_keyword(id, &curbuf->b_keywtab);
3403 	(void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
3404     }
3405 
3406     /* clear the patterns for "id" */
3407     for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3408     {
3409 	spp = &(SYN_ITEMS(curbuf)[idx]);
3410 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3411 	    continue;
3412 	syn_remove_pattern(curbuf, idx);
3413     }
3414 }
3415 
3416 /*
3417  * Handle ":syntax on" command.
3418  */
3419 /* ARGSUSED */
3420     static void
3421 syn_cmd_on(eap, syncing)
3422     exarg_T	*eap;
3423     int		syncing;	/* not used */
3424 {
3425     syn_cmd_onoff(eap, "syntax");
3426 }
3427 
3428 /*
3429  * Handle ":syntax enable" command.
3430  */
3431 /* ARGSUSED */
3432     static void
3433 syn_cmd_enable(eap, syncing)
3434     exarg_T	*eap;
3435     int		syncing;	/* not used */
3436 {
3437     set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3438     syn_cmd_onoff(eap, "syntax");
3439     do_unlet((char_u *)"g:syntax_cmd", TRUE);
3440 }
3441 
3442 /*
3443  * Handle ":syntax reset" command.
3444  */
3445 /* ARGSUSED */
3446     static void
3447 syn_cmd_reset(eap, syncing)
3448     exarg_T	*eap;
3449     int		syncing;	/* not used */
3450 {
3451     eap->nextcmd = check_nextcmd(eap->arg);
3452     if (!eap->skip)
3453     {
3454 	set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3455 	do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
3456 	do_unlet((char_u *)"g:syntax_cmd", TRUE);
3457     }
3458 }
3459 
3460 /*
3461  * Handle ":syntax manual" command.
3462  */
3463 /* ARGSUSED */
3464     static void
3465 syn_cmd_manual(eap, syncing)
3466     exarg_T	*eap;
3467     int		syncing;	/* not used */
3468 {
3469     syn_cmd_onoff(eap, "manual");
3470 }
3471 
3472 /*
3473  * Handle ":syntax off" command.
3474  */
3475 /* ARGSUSED */
3476     static void
3477 syn_cmd_off(eap, syncing)
3478     exarg_T	*eap;
3479     int		syncing;	/* not used */
3480 {
3481     syn_cmd_onoff(eap, "nosyntax");
3482 }
3483 
3484     static void
3485 syn_cmd_onoff(eap, name)
3486     exarg_T	*eap;
3487     char	*name;
3488 {
3489     char_u	buf[100];
3490 
3491     eap->nextcmd = check_nextcmd(eap->arg);
3492     if (!eap->skip)
3493     {
3494 	STRCPY(buf, "so ");
3495 	vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
3496 	do_cmdline_cmd(buf);
3497     }
3498 }
3499 
3500 /*
3501  * Handle ":syntax [list]" command: list current syntax words.
3502  */
3503     static void
3504 syn_cmd_list(eap, syncing)
3505     exarg_T	*eap;
3506     int		syncing;	    /* when TRUE: list syncing items */
3507 {
3508     char_u	*arg = eap->arg;
3509     int		id;
3510     char_u	*arg_end;
3511 
3512     eap->nextcmd = find_nextcmd(arg);
3513     if (eap->skip)
3514 	return;
3515 
3516     if (!syntax_present(curbuf))
3517     {
3518 	MSG(_("No Syntax items defined for this buffer"));
3519 	return;
3520     }
3521 
3522     if (syncing)
3523     {
3524 	if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3525 	{
3526 	    MSG_PUTS(_("syncing on C-style comments"));
3527 	    syn_lines_msg();
3528 	    syn_match_msg();
3529 	    return;
3530 	}
3531 	else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3532 	{
3533 	    if (curbuf->b_syn_sync_minlines == 0)
3534 		MSG_PUTS(_("no syncing"));
3535 	    else
3536 	    {
3537 		MSG_PUTS(_("syncing starts "));
3538 		msg_outnum(curbuf->b_syn_sync_minlines);
3539 		MSG_PUTS(_(" lines before top line"));
3540 		syn_match_msg();
3541 	    }
3542 	    return;
3543 	}
3544 	MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3545 	if (curbuf->b_syn_sync_minlines > 0
3546 		|| curbuf->b_syn_sync_maxlines > 0
3547 		|| curbuf->b_syn_sync_linebreaks > 0)
3548 	{
3549 	    MSG_PUTS(_("\nsyncing on items"));
3550 	    syn_lines_msg();
3551 	    syn_match_msg();
3552 	}
3553     }
3554     else
3555 	MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3556     if (ends_excmd(*arg))
3557     {
3558 	/*
3559 	 * No argument: List all group IDs and all syntax clusters.
3560 	 */
3561 	for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3562 	    syn_list_one(id, syncing, FALSE);
3563 	for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3564 	    syn_list_cluster(id);
3565     }
3566     else
3567     {
3568 	/*
3569 	 * List the group IDs and syntax clusters that are in the argument.
3570 	 */
3571 	while (!ends_excmd(*arg) && !got_int)
3572 	{
3573 	    arg_end = skiptowhite(arg);
3574 	    if (*arg == '@')
3575 	    {
3576 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3577 		if (id == 0)
3578 		    EMSG2(_("E392: No such syntax cluster: %s"), arg);
3579 		else
3580 		    syn_list_cluster(id - SYNID_CLUSTER);
3581 	    }
3582 	    else
3583 	    {
3584 		id = syn_namen2id(arg, (int)(arg_end - arg));
3585 		if (id == 0)
3586 		    EMSG2(_(e_nogroup), arg);
3587 		else
3588 		    syn_list_one(id, syncing, TRUE);
3589 	    }
3590 	    arg = skipwhite(arg_end);
3591 	}
3592     }
3593     eap->nextcmd = check_nextcmd(arg);
3594 }
3595 
3596     static void
3597 syn_lines_msg()
3598 {
3599     if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3600     {
3601 	MSG_PUTS("; ");
3602 	if (curbuf->b_syn_sync_minlines > 0)
3603 	{
3604 	    MSG_PUTS(_("minimal "));
3605 	    msg_outnum(curbuf->b_syn_sync_minlines);
3606 	    if (curbuf->b_syn_sync_maxlines)
3607 		MSG_PUTS(", ");
3608 	}
3609 	if (curbuf->b_syn_sync_maxlines > 0)
3610 	{
3611 	    MSG_PUTS(_("maximal "));
3612 	    msg_outnum(curbuf->b_syn_sync_maxlines);
3613 	}
3614 	MSG_PUTS(_(" lines before top line"));
3615     }
3616 }
3617 
3618     static void
3619 syn_match_msg()
3620 {
3621     if (curbuf->b_syn_sync_linebreaks > 0)
3622     {
3623 	MSG_PUTS(_("; match "));
3624 	msg_outnum(curbuf->b_syn_sync_linebreaks);
3625 	MSG_PUTS(_(" line breaks"));
3626     }
3627 }
3628 
3629 static int  last_matchgroup;
3630 
3631 struct name_list
3632 {
3633     int		flag;
3634     char	*name;
3635 };
3636 
3637 static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3638 
3639 /*
3640  * List one syntax item, for ":syntax" or "syntax list syntax_name".
3641  */
3642     static void
3643 syn_list_one(id, syncing, link_only)
3644     int		id;
3645     int		syncing;	    /* when TRUE: list syncing items */
3646     int		link_only;	    /* when TRUE; list link-only too */
3647 {
3648     int		attr;
3649     int		idx;
3650     int		did_header = FALSE;
3651     synpat_T	*spp;
3652     static struct name_list namelist1[] =
3653 		{
3654 		    {HL_DISPLAY, "display"},
3655 		    {HL_CONTAINED, "contained"},
3656 		    {HL_ONELINE, "oneline"},
3657 		    {HL_KEEPEND, "keepend"},
3658 		    {HL_EXTEND, "extend"},
3659 		    {HL_EXCLUDENL, "excludenl"},
3660 		    {HL_TRANSP, "transparent"},
3661 		    {HL_FOLD, "fold"},
3662 		    {0, NULL}
3663 		};
3664     static struct name_list namelist2[] =
3665 		{
3666 		    {HL_SKIPWHITE, "skipwhite"},
3667 		    {HL_SKIPNL, "skipnl"},
3668 		    {HL_SKIPEMPTY, "skipempty"},
3669 		    {0, NULL}
3670 		};
3671 
3672     attr = hl_attr(HLF_D);		/* highlight like directories */
3673 
3674     /* list the keywords for "id" */
3675     if (!syncing)
3676     {
3677 	did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3678 	did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
3679 							    did_header, attr);
3680     }
3681 
3682     /* list the patterns for "id" */
3683     for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3684     {
3685 	spp = &(SYN_ITEMS(curbuf)[idx]);
3686 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3687 	    continue;
3688 
3689 	(void)syn_list_header(did_header, 999, id);
3690 	did_header = TRUE;
3691 	last_matchgroup = 0;
3692 	if (spp->sp_type == SPTYPE_MATCH)
3693 	{
3694 	    put_pattern("match", ' ', spp, attr);
3695 	    msg_putchar(' ');
3696 	}
3697 	else if (spp->sp_type == SPTYPE_START)
3698 	{
3699 	    while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3700 		put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3701 	    if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3702 		put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3703 	    while (idx < curbuf->b_syn_patterns.ga_len
3704 			      && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3705 		put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3706 	    --idx;
3707 	    msg_putchar(' ');
3708 	}
3709 	syn_list_flags(namelist1, spp->sp_flags, attr);
3710 
3711 	if (spp->sp_cont_list != NULL)
3712 	    put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3713 
3714 	if (spp->sp_syn.cont_in_list != NULL)
3715 	    put_id_list((char_u *)"containedin",
3716 					      spp->sp_syn.cont_in_list, attr);
3717 
3718 	if (spp->sp_next_list != NULL)
3719 	{
3720 	    put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3721 	    syn_list_flags(namelist2, spp->sp_flags, attr);
3722 	}
3723 	if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3724 	{
3725 	    if (spp->sp_flags & HL_SYNC_HERE)
3726 		msg_puts_attr((char_u *)"grouphere", attr);
3727 	    else
3728 		msg_puts_attr((char_u *)"groupthere", attr);
3729 	    msg_putchar(' ');
3730 	    if (spp->sp_sync_idx >= 0)
3731 		msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3732 				   [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3733 	    else
3734 		MSG_PUTS("NONE");
3735 	    msg_putchar(' ');
3736 	}
3737     }
3738 
3739     /* list the link, if there is one */
3740     if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3741     {
3742 	(void)syn_list_header(did_header, 999, id);
3743 	msg_puts_attr((char_u *)"links to", attr);
3744 	msg_putchar(' ');
3745 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3746     }
3747 }
3748 
3749     static void
3750 syn_list_flags(nl, flags, attr)
3751     struct name_list	*nl;
3752     int			flags;
3753     int			attr;
3754 {
3755     int		i;
3756 
3757     for (i = 0; nl[i].flag != 0; ++i)
3758 	if (flags & nl[i].flag)
3759 	{
3760 	    msg_puts_attr((char_u *)nl[i].name, attr);
3761 	    msg_putchar(' ');
3762 	}
3763 }
3764 
3765 /*
3766  * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3767  */
3768     static void
3769 syn_list_cluster(id)
3770     int id;
3771 {
3772     int	    endcol = 15;
3773 
3774     /* slight hack:  roughly duplicate the guts of syn_list_header() */
3775     msg_putchar('\n');
3776     msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3777 
3778     if (msg_col >= endcol)	/* output at least one space */
3779 	endcol = msg_col + 1;
3780     if (Columns <= endcol)	/* avoid hang for tiny window */
3781 	endcol = Columns - 1;
3782 
3783     msg_advance(endcol);
3784     if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3785     {
3786 	put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3787 		    hl_attr(HLF_D));
3788     }
3789     else
3790     {
3791 	msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3792 	msg_puts((char_u *)"=NONE");
3793     }
3794 }
3795 
3796     static void
3797 put_id_list(name, list, attr)
3798     char_u	*name;
3799     short	*list;
3800     int		attr;
3801 {
3802     short		*p;
3803 
3804     msg_puts_attr(name, attr);
3805     msg_putchar('=');
3806     for (p = list; *p; ++p)
3807     {
3808 	if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3809 	{
3810 	    if (p[1])
3811 		MSG_PUTS("ALLBUT");
3812 	    else
3813 		MSG_PUTS("ALL");
3814 	}
3815 	else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3816 	{
3817 	    MSG_PUTS("TOP");
3818 	}
3819 	else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3820 	{
3821 	    MSG_PUTS("CONTAINED");
3822 	}
3823 	else if (*p >= SYNID_CLUSTER)
3824 	{
3825 	    short scl_id = *p - SYNID_CLUSTER;
3826 
3827 	    msg_putchar('@');
3828 	    msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3829 	}
3830 	else
3831 	    msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3832 	if (p[1])
3833 	    msg_putchar(',');
3834     }
3835     msg_putchar(' ');
3836 }
3837 
3838     static void
3839 put_pattern(s, c, spp, attr)
3840     char	*s;
3841     int		c;
3842     synpat_T	*spp;
3843     int		attr;
3844 {
3845     long	n;
3846     int		mask;
3847     int		first;
3848     static char	*sepchars = "/+=-#@\"|'^&";
3849     int		i;
3850 
3851     /* May have to write "matchgroup=group" */
3852     if (last_matchgroup != spp->sp_syn_match_id)
3853     {
3854 	last_matchgroup = spp->sp_syn_match_id;
3855 	msg_puts_attr((char_u *)"matchgroup", attr);
3856 	msg_putchar('=');
3857 	if (last_matchgroup == 0)
3858 	    msg_outtrans((char_u *)"NONE");
3859 	else
3860 	    msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3861 	msg_putchar(' ');
3862     }
3863 
3864     /* output the name of the pattern and an '=' or ' ' */
3865     msg_puts_attr((char_u *)s, attr);
3866     msg_putchar(c);
3867 
3868     /* output the pattern, in between a char that is not in the pattern */
3869     for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3870 	if (sepchars[++i] == NUL)
3871 	{
3872 	    i = 0;	/* no good char found, just use the first one */
3873 	    break;
3874 	}
3875     msg_putchar(sepchars[i]);
3876     msg_outtrans(spp->sp_pattern);
3877     msg_putchar(sepchars[i]);
3878 
3879     /* output any pattern options */
3880     first = TRUE;
3881     for (i = 0; i < SPO_COUNT; ++i)
3882     {
3883 	mask = (1 << i);
3884 	if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3885 	{
3886 	    if (!first)
3887 		msg_putchar(',');	/* separate with commas */
3888 	    msg_puts((char_u *)spo_name_tab[i]);
3889 	    n = spp->sp_offsets[i];
3890 	    if (i != SPO_LC_OFF)
3891 	    {
3892 		if (spp->sp_off_flags & mask)
3893 		    msg_putchar('s');
3894 		else
3895 		    msg_putchar('e');
3896 		if (n > 0)
3897 		    msg_putchar('+');
3898 	    }
3899 	    if (n || i == SPO_LC_OFF)
3900 		msg_outnum(n);
3901 	    first = FALSE;
3902 	}
3903     }
3904     msg_putchar(' ');
3905 }
3906 
3907 /*
3908  * List or clear the keywords for one syntax group.
3909  * Return TRUE if the header has been printed.
3910  */
3911     static int
3912 syn_list_keywords(id, ht, did_header, attr)
3913     int		id;
3914     hashtab_T	*ht;
3915     int		did_header;		/* header has already been printed */
3916     int		attr;
3917 {
3918     int		outlen;
3919     hashitem_T	*hi;
3920     keyentry_T	*kp;
3921     int		todo;
3922     int		prev_contained = 0;
3923     short	*prev_next_list = NULL;
3924     short	*prev_cont_in_list = NULL;
3925     int		prev_skipnl = 0;
3926     int		prev_skipwhite = 0;
3927     int		prev_skipempty = 0;
3928 
3929     /*
3930      * Unfortunately, this list of keywords is not sorted on alphabet but on
3931      * hash value...
3932      */
3933     todo = ht->ht_used;
3934     for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
3935     {
3936 	if (!HASHITEM_EMPTY(hi))
3937 	{
3938 	    --todo;
3939 	    for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
3940 	    {
3941 		if (kp->k_syn.id == id)
3942 		{
3943 		    if (prev_contained != (kp->flags & HL_CONTAINED)
3944 			    || prev_skipnl != (kp->flags & HL_SKIPNL)
3945 			    || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3946 			    || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3947 			    || prev_cont_in_list != kp->k_syn.cont_in_list
3948 			    || prev_next_list != kp->next_list)
3949 			outlen = 9999;
3950 		    else
3951 			outlen = (int)STRLEN(kp->keyword);
3952 		    /* output "contained" and "nextgroup" on each line */
3953 		    if (syn_list_header(did_header, outlen, id))
3954 		    {
3955 			prev_contained = 0;
3956 			prev_next_list = NULL;
3957 			prev_cont_in_list = NULL;
3958 			prev_skipnl = 0;
3959 			prev_skipwhite = 0;
3960 			prev_skipempty = 0;
3961 		    }
3962 		    did_header = TRUE;
3963 		    if (prev_contained != (kp->flags & HL_CONTAINED))
3964 		    {
3965 			msg_puts_attr((char_u *)"contained", attr);
3966 			msg_putchar(' ');
3967 			prev_contained = (kp->flags & HL_CONTAINED);
3968 		    }
3969 		    if (kp->k_syn.cont_in_list != prev_cont_in_list)
3970 		    {
3971 			put_id_list((char_u *)"containedin",
3972 						kp->k_syn.cont_in_list, attr);
3973 			msg_putchar(' ');
3974 			prev_cont_in_list = kp->k_syn.cont_in_list;
3975 		    }
3976 		    if (kp->next_list != prev_next_list)
3977 		    {
3978 			put_id_list((char_u *)"nextgroup", kp->next_list, attr);
3979 			msg_putchar(' ');
3980 			prev_next_list = kp->next_list;
3981 			if (kp->flags & HL_SKIPNL)
3982 			{
3983 			    msg_puts_attr((char_u *)"skipnl", attr);
3984 			    msg_putchar(' ');
3985 			    prev_skipnl = (kp->flags & HL_SKIPNL);
3986 			}
3987 			if (kp->flags & HL_SKIPWHITE)
3988 			{
3989 			    msg_puts_attr((char_u *)"skipwhite", attr);
3990 			    msg_putchar(' ');
3991 			    prev_skipwhite = (kp->flags & HL_SKIPWHITE);
3992 			}
3993 			if (kp->flags & HL_SKIPEMPTY)
3994 			{
3995 			    msg_puts_attr((char_u *)"skipempty", attr);
3996 			    msg_putchar(' ');
3997 			    prev_skipempty = (kp->flags & HL_SKIPEMPTY);
3998 			}
3999 		    }
4000 		    msg_outtrans(kp->keyword);
4001 		}
4002 	    }
4003 	}
4004     }
4005 
4006     return did_header;
4007 }
4008 
4009     static void
4010 syn_clear_keyword(id, ht)
4011     int		id;
4012     hashtab_T	*ht;
4013 {
4014     hashitem_T	*hi;
4015     keyentry_T	*kp;
4016     keyentry_T	*kp_prev;
4017     keyentry_T	*kp_next;
4018     int		todo;
4019 
4020     hash_lock(ht);
4021     todo = ht->ht_used;
4022     for (hi = ht->ht_array; todo > 0; ++hi)
4023     {
4024 	if (!HASHITEM_EMPTY(hi))
4025 	{
4026 	    --todo;
4027 	    kp_prev = NULL;
4028 	    for (kp = HI2KE(hi); kp != NULL; )
4029 	    {
4030 		if (kp->k_syn.id == id)
4031 		{
4032 		    kp_next = kp->ke_next;
4033 		    if (kp_prev == NULL)
4034 		    {
4035 			if (kp_next == NULL)
4036 			    hash_remove(ht, hi);
4037 			else
4038 			    hi->hi_key = KE2HIKEY(kp_next);
4039 		    }
4040 		    else
4041 			kp_prev->ke_next = kp_next;
4042 		    vim_free(kp->next_list);
4043 		    vim_free(kp->k_syn.cont_in_list);
4044 		    vim_free(kp);
4045 		    kp = kp_next;
4046 		}
4047 		else
4048 		{
4049 		    kp_prev = kp;
4050 		    kp = kp->ke_next;
4051 		}
4052 	    }
4053 	}
4054     }
4055     hash_unlock(ht);
4056 }
4057 
4058 /*
4059  * Clear a whole keyword table.
4060  */
4061     static void
4062 clear_keywtab(ht)
4063     hashtab_T	*ht;
4064 {
4065     hashitem_T	*hi;
4066     int		todo;
4067     keyentry_T	*kp;
4068     keyentry_T	*kp_next;
4069 
4070     todo = ht->ht_used;
4071     for (hi = ht->ht_array; todo > 0; ++hi)
4072     {
4073 	if (!HASHITEM_EMPTY(hi))
4074 	{
4075 	    --todo;
4076 	    kp = HI2KE(hi);
4077 	    for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
4078 	    {
4079 		kp_next = kp->ke_next;
4080 		vim_free(kp->next_list);
4081 		vim_free(kp->k_syn.cont_in_list);
4082 		vim_free(kp);
4083 	    }
4084 	}
4085     }
4086     hash_clear(ht);
4087     hash_init(ht);
4088 }
4089 
4090 /*
4091  * Add a keyword to the list of keywords.
4092  */
4093     static void
4094 add_keyword(name, id, flags, cont_in_list, next_list)
4095     char_u	*name;	    /* name of keyword */
4096     int		id;	    /* group ID for this keyword */
4097     int		flags;	    /* flags for this keyword */
4098     short	*cont_in_list; /* containedin for this keyword */
4099     short	*next_list; /* nextgroup for this keyword */
4100 {
4101     keyentry_T	*kp;
4102     hashtab_T	*ht;
4103     hashitem_T	*hi;
4104     char_u	*name_ic;
4105     long_u	hash;
4106     char_u	name_folded[MAXKEYWLEN + 1];
4107 
4108     if (curbuf->b_syn_ic)
4109 	name_ic = str_foldcase(name, (int)STRLEN(name),
4110 						 name_folded, MAXKEYWLEN + 1);
4111     else
4112 	name_ic = name;
4113     kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4114     if (kp == NULL)
4115 	return;
4116     STRCPY(kp->keyword, name_ic);
4117     kp->k_syn.id = id;
4118     kp->k_syn.inc_tag = current_syn_inc_tag;
4119     kp->flags = flags;
4120     kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
4121     if (cont_in_list != NULL)
4122 	curbuf->b_syn_containedin = TRUE;
4123     kp->next_list = copy_id_list(next_list);
4124 
4125     if (curbuf->b_syn_ic)
4126 	ht = &curbuf->b_keywtab_ic;
4127     else
4128 	ht = &curbuf->b_keywtab;
4129 
4130     hash = hash_hash(kp->keyword);
4131     hi = hash_lookup(ht, kp->keyword, hash);
4132     if (HASHITEM_EMPTY(hi))
4133     {
4134 	/* new keyword, add to hashtable */
4135 	kp->ke_next = NULL;
4136 	hash_add_item(ht, hi, kp->keyword, hash);
4137     }
4138     else
4139     {
4140 	/* keyword already exists, prepend to list */
4141 	kp->ke_next = HI2KE(hi);
4142 	hi->hi_key = KE2HIKEY(kp);
4143     }
4144 }
4145 
4146 /*
4147  * Get the start and end of the group name argument.
4148  * Return a pointer to the first argument.
4149  * Return NULL if the end of the command was found instead of further args.
4150  */
4151     static char_u *
4152 get_group_name(arg, name_end)
4153     char_u	*arg;		/* start of the argument */
4154     char_u	**name_end;	/* pointer to end of the name */
4155 {
4156     char_u	*rest;
4157 
4158     *name_end = skiptowhite(arg);
4159     rest = skipwhite(*name_end);
4160 
4161     /*
4162      * Check if there are enough arguments.  The first argument may be a
4163      * pattern, where '|' is allowed, so only check for NUL.
4164      */
4165     if (ends_excmd(*arg) || *rest == NUL)
4166 	return NULL;
4167     return rest;
4168 }
4169 
4170 /*
4171  * Check for syntax command option arguments.
4172  * This can be called at any place in the list of arguments, and just picks
4173  * out the arguments that are known.  Can be called several times in a row to
4174  * collect all options in between other arguments.
4175  * Return a pointer to the next argument (which isn't an option).
4176  * Return NULL for any error;
4177  */
4178     static char_u *
4179 get_syn_options(arg, opt)
4180     char_u	    *arg;		/* next argument to be checked */
4181     syn_opt_arg_T   *opt;		/* various things */
4182 {
4183     char_u	*gname_start, *gname;
4184     int		syn_id;
4185     int		len;
4186     char	*p;
4187     int		i;
4188     int		fidx;
4189     static struct flag
4190     {
4191 	char	*name;
4192 	int	argtype;
4193 	int	flags;
4194     } flagtab[] = { {"cCoOnNtTaAiInNeEdD",	0,	HL_CONTAINED},
4195 		    {"oOnNeElLiInNeE",		0,	HL_ONELINE},
4196 		    {"kKeEeEpPeEnNdD",		0,	HL_KEEPEND},
4197 		    {"eExXtTeEnNdD",		0,	HL_EXTEND},
4198 		    {"eExXcClLuUdDeEnNlL",	0,	HL_EXCLUDENL},
4199 		    {"tTrRaAnNsSpPaArReEnNtT",	0,	HL_TRANSP},
4200 		    {"sSkKiIpPnNlL",		0,	HL_SKIPNL},
4201 		    {"sSkKiIpPwWhHiItTeE",	0,	HL_SKIPWHITE},
4202 		    {"sSkKiIpPeEmMpPtTyY",	0,	HL_SKIPEMPTY},
4203 		    {"gGrRoOuUpPhHeErReE",	0,	HL_SYNC_HERE},
4204 		    {"gGrRoOuUpPtThHeErReE",	0,	HL_SYNC_THERE},
4205 		    {"dDiIsSpPlLaAyY",		0,	HL_DISPLAY},
4206 		    {"fFoOlLdD",		0,	HL_FOLD},
4207 		    {"cCoOnNtTaAiInNsS",	1,	0},
4208 		    {"cCoOnNtTaAiInNeEdDiInN",	2,	0},
4209 		    {"nNeExXtTgGrRoOuUpP",	3,	0},
4210 		};
4211     static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
4212 
4213     if (arg == NULL)		/* already detected error */
4214 	return NULL;
4215 
4216     for (;;)
4217     {
4218 	/*
4219 	 * This is used very often when a large number of keywords is defined.
4220 	 * Need to skip quickly when no option name is found.
4221 	 * Also avoid tolower(), it's slow.
4222 	 */
4223 	if (strchr(first_letters, *arg) == NULL)
4224 	    break;
4225 
4226 	for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4227 	{
4228 	    p = flagtab[fidx].name;
4229 	    for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4230 		if (arg[len] != p[i] && arg[len] != p[i + 1])
4231 		    break;
4232 	    if (p[i] == NUL && (vim_iswhite(arg[len])
4233 				    || (flagtab[fidx].argtype > 0
4234 					 ? arg[len] == '='
4235 					 : ends_excmd(arg[len]))))
4236 	    {
4237 		if (opt->keyword
4238 			&& (flagtab[fidx].flags == HL_DISPLAY
4239 			    || flagtab[fidx].flags == HL_FOLD
4240 			    || flagtab[fidx].flags == HL_EXTEND))
4241 		    /* treat "display", "fold" and "extend" as a keyword */
4242 		    fidx = -1;
4243 		break;
4244 	    }
4245 	}
4246 	if (fidx < 0)	    /* no match found */
4247 	    break;
4248 
4249 	if (flagtab[fidx].argtype == 1)
4250 	{
4251 	    if (!opt->has_cont_list)
4252 	    {
4253 		EMSG(_("E395: contains argument not accepted here"));
4254 		return NULL;
4255 	    }
4256 	    if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
4257 		return NULL;
4258 	}
4259 	else if (flagtab[fidx].argtype == 2)
4260 	{
4261 #if 0	    /* cannot happen */
4262 	    if (opt->cont_in_list == NULL)
4263 	    {
4264 		EMSG(_("E396: containedin argument not accepted here"));
4265 		return NULL;
4266 	    }
4267 #endif
4268 	    if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
4269 		return NULL;
4270 	}
4271 	else if (flagtab[fidx].argtype == 3)
4272 	{
4273 	    if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
4274 		return NULL;
4275 	}
4276 	else
4277 	{
4278 	    opt->flags |= flagtab[fidx].flags;
4279 	    arg = skipwhite(arg + len);
4280 
4281 	    if (flagtab[fidx].flags == HL_SYNC_HERE
4282 		    || flagtab[fidx].flags == HL_SYNC_THERE)
4283 	    {
4284 		if (opt->sync_idx == NULL)
4285 		{
4286 		    EMSG(_("E393: group[t]here not accepted here"));
4287 		    return NULL;
4288 		}
4289 		gname_start = arg;
4290 		arg = skiptowhite(arg);
4291 		if (gname_start == arg)
4292 		    return NULL;
4293 		gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4294 		if (gname == NULL)
4295 		    return NULL;
4296 		if (STRCMP(gname, "NONE") == 0)
4297 		    *opt->sync_idx = NONE_IDX;
4298 		else
4299 		{
4300 		    syn_id = syn_name2id(gname);
4301 		    for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4302 			if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4303 			      && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4304 			{
4305 			    *opt->sync_idx = i;
4306 			    break;
4307 			}
4308 		    if (i < 0)
4309 		    {
4310 			EMSG2(_("E394: Didn't find region item for %s"), gname);
4311 			vim_free(gname);
4312 			return NULL;
4313 		    }
4314 		}
4315 
4316 		vim_free(gname);
4317 		arg = skipwhite(arg);
4318 	    }
4319 #ifdef FEAT_FOLDING
4320 	    else if (flagtab[fidx].flags == HL_FOLD
4321 						&& foldmethodIsSyntax(curwin))
4322 		/* Need to update folds later. */
4323 		foldUpdateAll(curwin);
4324 #endif
4325 	}
4326     }
4327 
4328     return arg;
4329 }
4330 
4331 /*
4332  * Adjustments to syntax item when declared in a ":syn include"'d file.
4333  * Set the contained flag, and if the item is not already contained, add it
4334  * to the specified top-level group, if any.
4335  */
4336     static void
4337 syn_incl_toplevel(id, flagsp)
4338     int		id;
4339     int		*flagsp;
4340 {
4341     if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4342 	return;
4343     *flagsp |= HL_CONTAINED;
4344     if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4345     {
4346 	/* We have to alloc this, because syn_combine_list() will free it. */
4347 	short	    *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4348 	int	    tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4349 
4350 	if (grp_list != NULL)
4351 	{
4352 	    grp_list[0] = id;
4353 	    grp_list[1] = 0;
4354 	    syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4355 			 CLUSTER_ADD);
4356 	}
4357     }
4358 }
4359 
4360 /*
4361  * Handle ":syntax include [@{group-name}] filename" command.
4362  */
4363 /* ARGSUSED */
4364     static void
4365 syn_cmd_include(eap, syncing)
4366     exarg_T	*eap;
4367     int		syncing;	    /* not used */
4368 {
4369     char_u	*arg = eap->arg;
4370     int		sgl_id = 1;
4371     char_u	*group_name_end;
4372     char_u	*rest;
4373     char_u	*errormsg = NULL;
4374     int		prev_toplvl_grp;
4375     int		prev_syn_inc_tag;
4376     int		source = FALSE;
4377 
4378     eap->nextcmd = find_nextcmd(arg);
4379     if (eap->skip)
4380 	return;
4381 
4382     if (arg[0] == '@')
4383     {
4384 	++arg;
4385 	rest = get_group_name(arg, &group_name_end);
4386 	if (rest == NULL)
4387 	{
4388 	    EMSG((char_u *)_("E397: Filename required"));
4389 	    return;
4390 	}
4391 	sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4392 	/* separate_nextcmd() and expand_filename() depend on this */
4393 	eap->arg = rest;
4394     }
4395 
4396     /*
4397      * Everything that's left, up to the next command, should be the
4398      * filename to include.
4399      */
4400     eap->argt |= (XFILE | NOSPC);
4401     separate_nextcmd(eap);
4402     if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4403     {
4404 	/* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4405 	 * file.  Need to expand the file name first.  In other cases
4406 	 * ":runtime!" is used. */
4407 	source = TRUE;
4408 	if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4409 	{
4410 	    if (errormsg != NULL)
4411 		EMSG(errormsg);
4412 	    return;
4413 	}
4414     }
4415 
4416     /*
4417      * Save and restore the existing top-level grouplist id and ":syn
4418      * include" tag around the actual inclusion.
4419      */
4420     prev_syn_inc_tag = current_syn_inc_tag;
4421     current_syn_inc_tag = ++running_syn_inc_tag;
4422     prev_toplvl_grp = curbuf->b_syn_topgrp;
4423     curbuf->b_syn_topgrp = sgl_id;
4424     if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
4425 				     : source_runtime(eap->arg, TRUE) == FAIL)
4426 	EMSG2(_(e_notopen), eap->arg);
4427     curbuf->b_syn_topgrp = prev_toplvl_grp;
4428     current_syn_inc_tag = prev_syn_inc_tag;
4429 }
4430 
4431 /*
4432  * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4433  */
4434 /* ARGSUSED */
4435     static void
4436 syn_cmd_keyword(eap, syncing)
4437     exarg_T	*eap;
4438     int		syncing;	    /* not used */
4439 {
4440     char_u	*arg = eap->arg;
4441     char_u	*group_name_end;
4442     int		syn_id;
4443     char_u	*rest;
4444     char_u	*keyword_copy;
4445     char_u	*p;
4446     char_u	*kw;
4447     syn_opt_arg_T syn_opt_arg;
4448     int		cnt;
4449 
4450     rest = get_group_name(arg, &group_name_end);
4451 
4452     if (rest != NULL)
4453     {
4454 	syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4455 
4456 	/* allocate a buffer, for removing the backslashes in the keyword */
4457 	keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4458 	if (keyword_copy != NULL)
4459 	{
4460 	    syn_opt_arg.flags = 0;
4461 	    syn_opt_arg.keyword = TRUE;
4462 	    syn_opt_arg.sync_idx = NULL;
4463 	    syn_opt_arg.has_cont_list = FALSE;
4464 	    syn_opt_arg.cont_in_list = NULL;
4465 	    syn_opt_arg.next_list = NULL;
4466 
4467 	    /*
4468 	     * The options given apply to ALL keywords, so all options must be
4469 	     * found before keywords can be created.
4470 	     * 1: collect the options and copy the keywords to keyword_copy.
4471 	     */
4472 	    cnt = 0;
4473 	    p = keyword_copy;
4474 	    for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
4475 	    {
4476 		rest = get_syn_options(rest, &syn_opt_arg);
4477 		if (rest == NULL || ends_excmd(*rest))
4478 		    break;
4479 		/* Copy the keyword, removing backslashes, and add a NUL. */
4480 		while (*rest != NUL && !vim_iswhite(*rest))
4481 		{
4482 		    if (*rest == '\\' && rest[1] != NUL)
4483 			++rest;
4484 		    *p++ = *rest++;
4485 		}
4486 		*p++ = NUL;
4487 		++cnt;
4488 	    }
4489 
4490 	    if (!eap->skip)
4491 	    {
4492 		/* Adjust flags for use of ":syn include". */
4493 		syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4494 
4495 		/*
4496 		 * 2: Add an entry for each keyword.
4497 		 */
4498 		for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4499 		{
4500 		    for (p = vim_strchr(kw, '['); ; )
4501 		    {
4502 			if (p != NULL)
4503 			    *p = NUL;
4504 			add_keyword(kw, syn_id, syn_opt_arg.flags,
4505 						     syn_opt_arg.cont_in_list,
4506 						       syn_opt_arg.next_list);
4507 			if (p == NULL)
4508 			    break;
4509 			if (p[1] == NUL)
4510 			{
4511 			    EMSG2(_("E747: Missing ']': %s"), kw);
4512 			    kw = p + 2;		/* skip over the NUL */
4513 			    break;
4514 			}
4515 			if (p[1] == ']')
4516 			{
4517 			    kw = p + 1;		/* skip over the "]" */
4518 			    break;
4519 			}
4520 #ifdef FEAT_MBYTE
4521 			if (has_mbyte)
4522 			{
4523 			    int l = (*mb_ptr2len)(p + 1);
4524 
4525 			    mch_memmove(p, p + 1, l);
4526 			    p += l;
4527 			}
4528 			else
4529 #endif
4530 			{
4531 			    p[0] = p[1];
4532 			    ++p;
4533 			}
4534 		    }
4535 		}
4536 	    }
4537 
4538 	    vim_free(keyword_copy);
4539 	    vim_free(syn_opt_arg.cont_in_list);
4540 	    vim_free(syn_opt_arg.next_list);
4541 	}
4542     }
4543 
4544     if (rest != NULL)
4545 	eap->nextcmd = check_nextcmd(rest);
4546     else
4547 	EMSG2(_(e_invarg2), arg);
4548 
4549     redraw_curbuf_later(NOT_VALID);
4550     syn_stack_free_all(curbuf);		/* Need to recompute all syntax. */
4551 }
4552 
4553 /*
4554  * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4555  *
4556  * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4557  */
4558     static void
4559 syn_cmd_match(eap, syncing)
4560     exarg_T	*eap;
4561     int		syncing;	    /* TRUE for ":syntax sync match .. " */
4562 {
4563     char_u	*arg = eap->arg;
4564     char_u	*group_name_end;
4565     char_u	*rest;
4566     synpat_T	item;		/* the item found in the line */
4567     int		syn_id;
4568     int		idx;
4569     syn_opt_arg_T syn_opt_arg;
4570     int		sync_idx = 0;
4571 
4572     /* Isolate the group name, check for validity */
4573     rest = get_group_name(arg, &group_name_end);
4574 
4575     /* Get options before the pattern */
4576     syn_opt_arg.flags = 0;
4577     syn_opt_arg.keyword = FALSE;
4578     syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4579     syn_opt_arg.has_cont_list = TRUE;
4580     syn_opt_arg.cont_list = NULL;
4581     syn_opt_arg.cont_in_list = NULL;
4582     syn_opt_arg.next_list = NULL;
4583     rest = get_syn_options(rest, &syn_opt_arg);
4584 
4585     /* get the pattern. */
4586     init_syn_patterns();
4587     vim_memset(&item, 0, sizeof(item));
4588     rest = get_syn_pattern(rest, &item);
4589     if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4590 	syn_opt_arg.flags |= HL_HAS_EOL;
4591 
4592     /* Get options after the pattern */
4593     rest = get_syn_options(rest, &syn_opt_arg);
4594 
4595     if (rest != NULL)		/* all arguments are valid */
4596     {
4597 	/*
4598 	 * Check for trailing command and illegal trailing arguments.
4599 	 */
4600 	eap->nextcmd = check_nextcmd(rest);
4601 	if (!ends_excmd(*rest) || eap->skip)
4602 	    rest = NULL;
4603 	else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4604 		&& (syn_id = syn_check_group(arg,
4605 					   (int)(group_name_end - arg))) != 0)
4606 	{
4607 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4608 	    /*
4609 	     * Store the pattern in the syn_items list
4610 	     */
4611 	    idx = curbuf->b_syn_patterns.ga_len;
4612 	    SYN_ITEMS(curbuf)[idx] = item;
4613 	    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4614 	    SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4615 	    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4616 	    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4617 	    SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
4618 	    SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
4619 	    SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4620 	    SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4621 						     syn_opt_arg.cont_in_list;
4622 	    if (syn_opt_arg.cont_in_list != NULL)
4623 		curbuf->b_syn_containedin = TRUE;
4624 	    SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
4625 	    ++curbuf->b_syn_patterns.ga_len;
4626 
4627 	    /* remember that we found a match for syncing on */
4628 	    if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4629 		curbuf->b_syn_sync_flags |= SF_MATCH;
4630 #ifdef FEAT_FOLDING
4631 	    if (syn_opt_arg.flags & HL_FOLD)
4632 		++curbuf->b_syn_folditems;
4633 #endif
4634 
4635 	    redraw_curbuf_later(NOT_VALID);
4636 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4637 	    return;	/* don't free the progs and patterns now */
4638 	}
4639     }
4640 
4641     /*
4642      * Something failed, free the allocated memory.
4643      */
4644     vim_free(item.sp_prog);
4645     vim_free(item.sp_pattern);
4646     vim_free(syn_opt_arg.cont_list);
4647     vim_free(syn_opt_arg.cont_in_list);
4648     vim_free(syn_opt_arg.next_list);
4649 
4650     if (rest == NULL)
4651 	EMSG2(_(e_invarg2), arg);
4652 }
4653 
4654 /*
4655  * Handle ":syntax region {group-name} [matchgroup={group-name}]
4656  *		start {start} .. [skip {skip}] end {end} .. [{options}]".
4657  */
4658     static void
4659 syn_cmd_region(eap, syncing)
4660     exarg_T	*eap;
4661     int		syncing;	    /* TRUE for ":syntax sync region .." */
4662 {
4663     char_u		*arg = eap->arg;
4664     char_u		*group_name_end;
4665     char_u		*rest;			/* next arg, NULL on error */
4666     char_u		*key_end;
4667     char_u		*key = NULL;
4668     char_u		*p;
4669     int			item;
4670 #define ITEM_START	    0
4671 #define ITEM_SKIP	    1
4672 #define ITEM_END	    2
4673 #define ITEM_MATCHGROUP	    3
4674     struct pat_ptr
4675     {
4676 	synpat_T	*pp_synp;		/* pointer to syn_pattern */
4677 	int		pp_matchgroup_id;	/* matchgroup ID */
4678 	struct pat_ptr	*pp_next;		/* pointer to next pat_ptr */
4679     }			*(pat_ptrs[3]);
4680 					/* patterns found in the line */
4681     struct pat_ptr	*ppp;
4682     struct pat_ptr	*ppp_next;
4683     int			pat_count = 0;		/* nr of syn_patterns found */
4684     int			syn_id;
4685     int			matchgroup_id = 0;
4686     int			not_enough = FALSE;	/* not enough arguments */
4687     int			illegal = FALSE;	/* illegal arguments */
4688     int			success = FALSE;
4689     int			idx;
4690     syn_opt_arg_T	syn_opt_arg;
4691 
4692     /* Isolate the group name, check for validity */
4693     rest = get_group_name(arg, &group_name_end);
4694 
4695     pat_ptrs[0] = NULL;
4696     pat_ptrs[1] = NULL;
4697     pat_ptrs[2] = NULL;
4698 
4699     init_syn_patterns();
4700 
4701     syn_opt_arg.flags = 0;
4702     syn_opt_arg.keyword = FALSE;
4703     syn_opt_arg.sync_idx = NULL;
4704     syn_opt_arg.has_cont_list = TRUE;
4705     syn_opt_arg.cont_list = NULL;
4706     syn_opt_arg.cont_in_list = NULL;
4707     syn_opt_arg.next_list = NULL;
4708 
4709     /*
4710      * get the options, patterns and matchgroup.
4711      */
4712     while (rest != NULL && !ends_excmd(*rest))
4713     {
4714 	/* Check for option arguments */
4715 	rest = get_syn_options(rest, &syn_opt_arg);
4716 	if (rest == NULL || ends_excmd(*rest))
4717 	    break;
4718 
4719 	/* must be a pattern or matchgroup then */
4720 	key_end = rest;
4721 	while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4722 	    ++key_end;
4723 	vim_free(key);
4724 	key = vim_strnsave_up(rest, (int)(key_end - rest));
4725 	if (key == NULL)			/* out of memory */
4726 	{
4727 	    rest = NULL;
4728 	    break;
4729 	}
4730 	if (STRCMP(key, "MATCHGROUP") == 0)
4731 	    item = ITEM_MATCHGROUP;
4732 	else if (STRCMP(key, "START") == 0)
4733 	    item = ITEM_START;
4734 	else if (STRCMP(key, "END") == 0)
4735 	    item = ITEM_END;
4736 	else if (STRCMP(key, "SKIP") == 0)
4737 	{
4738 	    if (pat_ptrs[ITEM_SKIP] != NULL)	/* one skip pattern allowed */
4739 	    {
4740 		illegal = TRUE;
4741 		break;
4742 	    }
4743 	    item = ITEM_SKIP;
4744 	}
4745 	else
4746 	    break;
4747 	rest = skipwhite(key_end);
4748 	if (*rest != '=')
4749 	{
4750 	    rest = NULL;
4751 	    EMSG2(_("E398: Missing '=': %s"), arg);
4752 	    break;
4753 	}
4754 	rest = skipwhite(rest + 1);
4755 	if (*rest == NUL)
4756 	{
4757 	    not_enough = TRUE;
4758 	    break;
4759 	}
4760 
4761 	if (item == ITEM_MATCHGROUP)
4762 	{
4763 	    p = skiptowhite(rest);
4764 	    if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4765 		matchgroup_id = 0;
4766 	    else
4767 	    {
4768 		matchgroup_id = syn_check_group(rest, (int)(p - rest));
4769 		if (matchgroup_id == 0)
4770 		{
4771 		    illegal = TRUE;
4772 		    break;
4773 		}
4774 	    }
4775 	    rest = skipwhite(p);
4776 	}
4777 	else
4778 	{
4779 	    /*
4780 	     * Allocate room for a syn_pattern, and link it in the list of
4781 	     * syn_patterns for this item, at the start (because the list is
4782 	     * used from end to start).
4783 	     */
4784 	    ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4785 	    if (ppp == NULL)
4786 	    {
4787 		rest = NULL;
4788 		break;
4789 	    }
4790 	    ppp->pp_next = pat_ptrs[item];
4791 	    pat_ptrs[item] = ppp;
4792 	    ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4793 	    if (ppp->pp_synp == NULL)
4794 	    {
4795 		rest = NULL;
4796 		break;
4797 	    }
4798 
4799 	    /*
4800 	     * Get the syntax pattern and the following offset(s).
4801 	     */
4802 	    /* Enable the appropriate \z specials. */
4803 	    if (item == ITEM_START)
4804 		reg_do_extmatch = REX_SET;
4805 	    else if (item == ITEM_SKIP || item == ITEM_END)
4806 		reg_do_extmatch = REX_USE;
4807 	    rest = get_syn_pattern(rest, ppp->pp_synp);
4808 	    reg_do_extmatch = 0;
4809 	    if (item == ITEM_END && vim_regcomp_had_eol()
4810 				       && !(syn_opt_arg.flags & HL_EXCLUDENL))
4811 		ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4812 	    ppp->pp_matchgroup_id = matchgroup_id;
4813 	    ++pat_count;
4814 	}
4815     }
4816     vim_free(key);
4817     if (illegal || not_enough)
4818 	rest = NULL;
4819 
4820     /*
4821      * Must have a "start" and "end" pattern.
4822      */
4823     if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4824 						  pat_ptrs[ITEM_END] == NULL))
4825     {
4826 	not_enough = TRUE;
4827 	rest = NULL;
4828     }
4829 
4830     if (rest != NULL)
4831     {
4832 	/*
4833 	 * Check for trailing garbage or command.
4834 	 * If OK, add the item.
4835 	 */
4836 	eap->nextcmd = check_nextcmd(rest);
4837 	if (!ends_excmd(*rest) || eap->skip)
4838 	    rest = NULL;
4839 	else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4840 		&& (syn_id = syn_check_group(arg,
4841 					   (int)(group_name_end - arg))) != 0)
4842 	{
4843 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4844 	    /*
4845 	     * Store the start/skip/end in the syn_items list
4846 	     */
4847 	    idx = curbuf->b_syn_patterns.ga_len;
4848 	    for (item = ITEM_START; item <= ITEM_END; ++item)
4849 	    {
4850 		for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4851 		{
4852 		    SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4853 		    SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4854 		    SYN_ITEMS(curbuf)[idx].sp_type =
4855 			    (item == ITEM_START) ? SPTYPE_START :
4856 			    (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
4857 		    SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
4858 		    SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4859 		    SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4860 		    SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4861 							ppp->pp_matchgroup_id;
4862 		    if (item == ITEM_START)
4863 		    {
4864 			SYN_ITEMS(curbuf)[idx].sp_cont_list =
4865 							syn_opt_arg.cont_list;
4866 			SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4867 						     syn_opt_arg.cont_in_list;
4868 			if (syn_opt_arg.cont_in_list != NULL)
4869 			    curbuf->b_syn_containedin = TRUE;
4870 			SYN_ITEMS(curbuf)[idx].sp_next_list =
4871 							syn_opt_arg.next_list;
4872 		    }
4873 		    ++curbuf->b_syn_patterns.ga_len;
4874 		    ++idx;
4875 #ifdef FEAT_FOLDING
4876 		    if (syn_opt_arg.flags & HL_FOLD)
4877 			++curbuf->b_syn_folditems;
4878 #endif
4879 		}
4880 	    }
4881 
4882 	    redraw_curbuf_later(NOT_VALID);
4883 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
4884 	    success = TRUE;	    /* don't free the progs and patterns now */
4885 	}
4886     }
4887 
4888     /*
4889      * Free the allocated memory.
4890      */
4891     for (item = ITEM_START; item <= ITEM_END; ++item)
4892 	for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4893 	{
4894 	    if (!success)
4895 	    {
4896 		vim_free(ppp->pp_synp->sp_prog);
4897 		vim_free(ppp->pp_synp->sp_pattern);
4898 	    }
4899 	    vim_free(ppp->pp_synp);
4900 	    ppp_next = ppp->pp_next;
4901 	    vim_free(ppp);
4902 	}
4903 
4904     if (!success)
4905     {
4906 	vim_free(syn_opt_arg.cont_list);
4907 	vim_free(syn_opt_arg.cont_in_list);
4908 	vim_free(syn_opt_arg.next_list);
4909 	if (not_enough)
4910 	    EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4911 	else if (illegal || rest == NULL)
4912 	    EMSG2(_(e_invarg2), arg);
4913     }
4914 }
4915 
4916 /*
4917  * A simple syntax group ID comparison function suitable for use in qsort()
4918  */
4919     static int
4920 #ifdef __BORLANDC__
4921 _RTLENTRYF
4922 #endif
4923 syn_compare_stub(v1, v2)
4924     const void	*v1;
4925     const void	*v2;
4926 {
4927     const short	*s1 = v1;
4928     const short	*s2 = v2;
4929 
4930     return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4931 }
4932 
4933 /*
4934  * Combines lists of syntax clusters.
4935  * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4936  */
4937     static void
4938 syn_combine_list(clstr1, clstr2, list_op)
4939     short	**clstr1;
4940     short	**clstr2;
4941     int		list_op;
4942 {
4943     int		count1 = 0;
4944     int		count2 = 0;
4945     short	*g1;
4946     short	*g2;
4947     short	*clstr = NULL;
4948     int		count;
4949     int		round;
4950 
4951     /*
4952      * Handle degenerate cases.
4953      */
4954     if (*clstr2 == NULL)
4955 	return;
4956     if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4957     {
4958 	if (list_op == CLUSTER_REPLACE)
4959 	    vim_free(*clstr1);
4960 	if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
4961 	    *clstr1 = *clstr2;
4962 	else
4963 	    vim_free(*clstr2);
4964 	return;
4965     }
4966 
4967     for (g1 = *clstr1; *g1; g1++)
4968 	++count1;
4969     for (g2 = *clstr2; *g2; g2++)
4970 	++count2;
4971 
4972     /*
4973      * For speed purposes, sort both lists.
4974      */
4975     qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
4976     qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
4977 
4978     /*
4979      * We proceed in two passes; in round 1, we count the elements to place
4980      * in the new list, and in round 2, we allocate and populate the new
4981      * list.  For speed, we use a mergesort-like method, adding the smaller
4982      * of the current elements in each list to the new list.
4983      */
4984     for (round = 1; round <= 2; round++)
4985     {
4986 	g1 = *clstr1;
4987 	g2 = *clstr2;
4988 	count = 0;
4989 
4990 	/*
4991 	 * First, loop through the lists until one of them is empty.
4992 	 */
4993 	while (*g1 && *g2)
4994 	{
4995 	    /*
4996 	     * We always want to add from the first list.
4997 	     */
4998 	    if (*g1 < *g2)
4999 	    {
5000 		if (round == 2)
5001 		    clstr[count] = *g1;
5002 		count++;
5003 		g1++;
5004 		continue;
5005 	    }
5006 	    /*
5007 	     * We only want to add from the second list if we're adding the
5008 	     * lists.
5009 	     */
5010 	    if (list_op == CLUSTER_ADD)
5011 	    {
5012 		if (round == 2)
5013 		    clstr[count] = *g2;
5014 		count++;
5015 	    }
5016 	    if (*g1 == *g2)
5017 		g1++;
5018 	    g2++;
5019 	}
5020 
5021 	/*
5022 	 * Now add the leftovers from whichever list didn't get finished
5023 	 * first.  As before, we only want to add from the second list if
5024 	 * we're adding the lists.
5025 	 */
5026 	for (; *g1; g1++, count++)
5027 	    if (round == 2)
5028 		clstr[count] = *g1;
5029 	if (list_op == CLUSTER_ADD)
5030 	    for (; *g2; g2++, count++)
5031 		if (round == 2)
5032 		    clstr[count] = *g2;
5033 
5034 	if (round == 1)
5035 	{
5036 	    /*
5037 	     * If the group ended up empty, we don't need to allocate any
5038 	     * space for it.
5039 	     */
5040 	    if (count == 0)
5041 	    {
5042 		clstr = NULL;
5043 		break;
5044 	    }
5045 	    clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5046 	    if (clstr == NULL)
5047 		break;
5048 	    clstr[count] = 0;
5049 	}
5050     }
5051 
5052     /*
5053      * Finally, put the new list in place.
5054      */
5055     vim_free(*clstr1);
5056     vim_free(*clstr2);
5057     *clstr1 = clstr;
5058 }
5059 
5060 /*
5061  * Lookup a syntax cluster name and return it's ID.
5062  * If it is not found, 0 is returned.
5063  */
5064     static int
5065 syn_scl_name2id(name)
5066     char_u	*name;
5067 {
5068     int		i;
5069     char_u	*name_u;
5070 
5071     /* Avoid using stricmp() too much, it's slow on some systems */
5072     name_u = vim_strsave_up(name);
5073     if (name_u == NULL)
5074 	return 0;
5075     for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5076 	if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5077 		&& STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5078 	    break;
5079     vim_free(name_u);
5080     return (i < 0 ? 0 : i + SYNID_CLUSTER);
5081 }
5082 
5083 /*
5084  * Like syn_scl_name2id(), but take a pointer + length argument.
5085  */
5086     static int
5087 syn_scl_namen2id(linep, len)
5088     char_u  *linep;
5089     int	    len;
5090 {
5091     char_u  *name;
5092     int	    id = 0;
5093 
5094     name = vim_strnsave(linep, len);
5095     if (name != NULL)
5096     {
5097 	id = syn_scl_name2id(name);
5098 	vim_free(name);
5099     }
5100     return id;
5101 }
5102 
5103 /*
5104  * Find syntax cluster name in the table and return it's ID.
5105  * The argument is a pointer to the name and the length of the name.
5106  * If it doesn't exist yet, a new entry is created.
5107  * Return 0 for failure.
5108  */
5109     static int
5110 syn_check_cluster(pp, len)
5111     char_u	*pp;
5112     int		len;
5113 {
5114     int		id;
5115     char_u	*name;
5116 
5117     name = vim_strnsave(pp, len);
5118     if (name == NULL)
5119 	return 0;
5120 
5121     id = syn_scl_name2id(name);
5122     if (id == 0)			/* doesn't exist yet */
5123 	id = syn_add_cluster(name);
5124     else
5125 	vim_free(name);
5126     return id;
5127 }
5128 
5129 /*
5130  * Add new syntax cluster and return it's ID.
5131  * "name" must be an allocated string, it will be consumed.
5132  * Return 0 for failure.
5133  */
5134     static int
5135 syn_add_cluster(name)
5136     char_u	*name;
5137 {
5138     int		len;
5139 
5140     /*
5141      * First call for this growarray: init growing array.
5142      */
5143     if (curbuf->b_syn_clusters.ga_data == NULL)
5144     {
5145 	curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5146 	curbuf->b_syn_clusters.ga_growsize = 10;
5147     }
5148 
5149     /*
5150      * Make room for at least one other cluster entry.
5151      */
5152     if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5153     {
5154 	vim_free(name);
5155 	return 0;
5156     }
5157     len = curbuf->b_syn_clusters.ga_len;
5158 
5159     vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
5160     SYN_CLSTR(curbuf)[len].scl_name = name;
5161     SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5162     SYN_CLSTR(curbuf)[len].scl_list = NULL;
5163     ++curbuf->b_syn_clusters.ga_len;
5164 
5165     if (STRICMP(name, "Spell") == 0)
5166 	curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
5167     if (STRICMP(name, "NoSpell") == 0)
5168 	curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
5169 
5170     return len + SYNID_CLUSTER;
5171 }
5172 
5173 /*
5174  * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5175  *		[add={groupname},..] [remove={groupname},..]".
5176  */
5177 /* ARGSUSED */
5178     static void
5179 syn_cmd_cluster(eap, syncing)
5180     exarg_T	*eap;
5181     int		syncing;	    /* not used */
5182 {
5183     char_u	*arg = eap->arg;
5184     char_u	*group_name_end;
5185     char_u	*rest;
5186     int		scl_id;
5187     short	*clstr_list;
5188     int		got_clstr = FALSE;
5189     int		opt_len;
5190     int		list_op;
5191 
5192     eap->nextcmd = find_nextcmd(arg);
5193     if (eap->skip)
5194 	return;
5195 
5196     rest = get_group_name(arg, &group_name_end);
5197 
5198     if (rest != NULL)
5199     {
5200 	scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
5201 							      - SYNID_CLUSTER;
5202 
5203 	for (;;)
5204 	{
5205 	    if (STRNICMP(rest, "add", 3) == 0
5206 		    && (vim_iswhite(rest[3]) || rest[3] == '='))
5207 	    {
5208 		opt_len = 3;
5209 		list_op = CLUSTER_ADD;
5210 	    }
5211 	    else if (STRNICMP(rest, "remove", 6) == 0
5212 		    && (vim_iswhite(rest[6]) || rest[6] == '='))
5213 	    {
5214 		opt_len = 6;
5215 		list_op = CLUSTER_SUBTRACT;
5216 	    }
5217 	    else if (STRNICMP(rest, "contains", 8) == 0
5218 			&& (vim_iswhite(rest[8]) || rest[8] == '='))
5219 	    {
5220 		opt_len = 8;
5221 		list_op = CLUSTER_REPLACE;
5222 	    }
5223 	    else
5224 		break;
5225 
5226 	    clstr_list = NULL;
5227 	    if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5228 	    {
5229 		EMSG2(_(e_invarg2), rest);
5230 		break;
5231 	    }
5232 	    syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5233 			     &clstr_list, list_op);
5234 	    got_clstr = TRUE;
5235 	}
5236 
5237 	if (got_clstr)
5238 	{
5239 	    redraw_curbuf_later(NOT_VALID);
5240 	    syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5241 	}
5242     }
5243 
5244     if (!got_clstr)
5245 	EMSG(_("E400: No cluster specified"));
5246     if (rest == NULL || !ends_excmd(*rest))
5247 	EMSG2(_(e_invarg2), arg);
5248 }
5249 
5250 /*
5251  * On first call for current buffer: Init growing array.
5252  */
5253     static void
5254 init_syn_patterns()
5255 {
5256     curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5257     curbuf->b_syn_patterns.ga_growsize = 10;
5258 }
5259 
5260 /*
5261  * Get one pattern for a ":syntax match" or ":syntax region" command.
5262  * Stores the pattern and program in a synpat_T.
5263  * Returns a pointer to the next argument, or NULL in case of an error.
5264  */
5265     static char_u *
5266 get_syn_pattern(arg, ci)
5267     char_u	*arg;
5268     synpat_T	*ci;
5269 {
5270     char_u	*end;
5271     int		*p;
5272     int		idx;
5273     char_u	*cpo_save;
5274 
5275     /* need at least three chars */
5276     if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5277 	return NULL;
5278 
5279     end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5280     if (*end != *arg)			    /* end delimiter not found */
5281     {
5282 	EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5283 	return NULL;
5284     }
5285     /* store the pattern and compiled regexp program */
5286     if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5287 	return NULL;
5288 
5289     /* Make 'cpoptions' empty, to avoid the 'l' flag */
5290     cpo_save = p_cpo;
5291     p_cpo = (char_u *)"";
5292     ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5293     p_cpo = cpo_save;
5294 
5295     if (ci->sp_prog == NULL)
5296 	return NULL;
5297     ci->sp_ic = curbuf->b_syn_ic;
5298 
5299     /*
5300      * Check for a match, highlight or region offset.
5301      */
5302     ++end;
5303     do
5304     {
5305 	for (idx = SPO_COUNT; --idx >= 0; )
5306 	    if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5307 		break;
5308 	if (idx >= 0)
5309 	{
5310 	    p = &(ci->sp_offsets[idx]);
5311 	    if (idx != SPO_LC_OFF)
5312 		switch (end[3])
5313 		{
5314 		    case 's':   break;
5315 		    case 'b':   break;
5316 		    case 'e':   idx += SPO_COUNT; break;
5317 		    default:    idx = -1; break;
5318 		}
5319 	    if (idx >= 0)
5320 	    {
5321 		ci->sp_off_flags |= (1 << idx);
5322 		if (idx == SPO_LC_OFF)	    /* lc=99 */
5323 		{
5324 		    end += 3;
5325 		    *p = getdigits(&end);
5326 
5327 		    /* "lc=" offset automatically sets "ms=" offset */
5328 		    if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5329 		    {
5330 			ci->sp_off_flags |= (1 << SPO_MS_OFF);
5331 			ci->sp_offsets[SPO_MS_OFF] = *p;
5332 		    }
5333 		}
5334 		else			    /* yy=x+99 */
5335 		{
5336 		    end += 4;
5337 		    if (*end == '+')
5338 		    {
5339 			++end;
5340 			*p = getdigits(&end);		/* positive offset */
5341 		    }
5342 		    else if (*end == '-')
5343 		    {
5344 			++end;
5345 			*p = -getdigits(&end);		/* negative offset */
5346 		    }
5347 		}
5348 		if (*end != ',')
5349 		    break;
5350 		++end;
5351 	    }
5352 	}
5353     } while (idx >= 0);
5354 
5355     if (!ends_excmd(*end) && !vim_iswhite(*end))
5356     {
5357 	EMSG2(_("E402: Garbage after pattern: %s"), arg);
5358 	return NULL;
5359     }
5360     return skipwhite(end);
5361 }
5362 
5363 /*
5364  * Handle ":syntax sync .." command.
5365  */
5366 /* ARGSUSED */
5367     static void
5368 syn_cmd_sync(eap, syncing)
5369     exarg_T	*eap;
5370     int		syncing;	    /* not used */
5371 {
5372     char_u	*arg_start = eap->arg;
5373     char_u	*arg_end;
5374     char_u	*key = NULL;
5375     char_u	*next_arg;
5376     int		illegal = FALSE;
5377     int		finished = FALSE;
5378     long	n;
5379     char_u	*cpo_save;
5380 
5381     if (ends_excmd(*arg_start))
5382     {
5383 	syn_cmd_list(eap, TRUE);
5384 	return;
5385     }
5386 
5387     while (!ends_excmd(*arg_start))
5388     {
5389 	arg_end = skiptowhite(arg_start);
5390 	next_arg = skipwhite(arg_end);
5391 	vim_free(key);
5392 	key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5393 	if (STRCMP(key, "CCOMMENT") == 0)
5394 	{
5395 	    if (!eap->skip)
5396 		curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5397 	    if (!ends_excmd(*next_arg))
5398 	    {
5399 		arg_end = skiptowhite(next_arg);
5400 		if (!eap->skip)
5401 		    curbuf->b_syn_sync_id = syn_check_group(next_arg,
5402 						   (int)(arg_end - next_arg));
5403 		next_arg = skipwhite(arg_end);
5404 	    }
5405 	    else if (!eap->skip)
5406 		curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5407 	}
5408 	else if (  STRNCMP(key, "LINES", 5) == 0
5409 		|| STRNCMP(key, "MINLINES", 8) == 0
5410 		|| STRNCMP(key, "MAXLINES", 8) == 0
5411 		|| STRNCMP(key, "LINEBREAKS", 10) == 0)
5412 	{
5413 	    if (key[4] == 'S')
5414 		arg_end = key + 6;
5415 	    else if (key[0] == 'L')
5416 		arg_end = key + 11;
5417 	    else
5418 		arg_end = key + 9;
5419 	    if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5420 	    {
5421 		illegal = TRUE;
5422 		break;
5423 	    }
5424 	    n = getdigits(&arg_end);
5425 	    if (!eap->skip)
5426 	    {
5427 		if (key[4] == 'B')
5428 		    curbuf->b_syn_sync_linebreaks = n;
5429 		else if (key[1] == 'A')
5430 		    curbuf->b_syn_sync_maxlines = n;
5431 		else
5432 		    curbuf->b_syn_sync_minlines = n;
5433 	    }
5434 	}
5435 	else if (STRCMP(key, "FROMSTART") == 0)
5436 	{
5437 	    if (!eap->skip)
5438 	    {
5439 		curbuf->b_syn_sync_minlines = MAXLNUM;
5440 		curbuf->b_syn_sync_maxlines = 0;
5441 	    }
5442 	}
5443 	else if (STRCMP(key, "LINECONT") == 0)
5444 	{
5445 	    if (curbuf->b_syn_linecont_pat != NULL)
5446 	    {
5447 		EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5448 		finished = TRUE;
5449 		break;
5450 	    }
5451 	    arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5452 	    if (*arg_end != *next_arg)	    /* end delimiter not found */
5453 	    {
5454 		illegal = TRUE;
5455 		break;
5456 	    }
5457 
5458 	    if (!eap->skip)
5459 	    {
5460 		/* store the pattern and compiled regexp program */
5461 		if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5462 				      (int)(arg_end - next_arg - 1))) == NULL)
5463 		{
5464 		    finished = TRUE;
5465 		    break;
5466 		}
5467 		curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5468 
5469 		/* Make 'cpoptions' empty, to avoid the 'l' flag */
5470 		cpo_save = p_cpo;
5471 		p_cpo = (char_u *)"";
5472 		curbuf->b_syn_linecont_prog =
5473 			    vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5474 		p_cpo = cpo_save;
5475 
5476 		if (curbuf->b_syn_linecont_prog == NULL)
5477 		{
5478 		    vim_free(curbuf->b_syn_linecont_pat);
5479 		    curbuf->b_syn_linecont_pat = NULL;
5480 		    finished = TRUE;
5481 		    break;
5482 		}
5483 	    }
5484 	    next_arg = skipwhite(arg_end + 1);
5485 	}
5486 	else
5487 	{
5488 	    eap->arg = next_arg;
5489 	    if (STRCMP(key, "MATCH") == 0)
5490 		syn_cmd_match(eap, TRUE);
5491 	    else if (STRCMP(key, "REGION") == 0)
5492 		syn_cmd_region(eap, TRUE);
5493 	    else if (STRCMP(key, "CLEAR") == 0)
5494 		syn_cmd_clear(eap, TRUE);
5495 	    else
5496 		illegal = TRUE;
5497 	    finished = TRUE;
5498 	    break;
5499 	}
5500 	arg_start = next_arg;
5501     }
5502     vim_free(key);
5503     if (illegal)
5504 	EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5505     else if (!finished)
5506     {
5507 	eap->nextcmd = check_nextcmd(arg_start);
5508 	redraw_curbuf_later(NOT_VALID);
5509 	syn_stack_free_all(curbuf);	/* Need to recompute all syntax. */
5510     }
5511 }
5512 
5513 /*
5514  * Convert a line of highlight group names into a list of group ID numbers.
5515  * "arg" should point to the "contains" or "nextgroup" keyword.
5516  * "arg" is advanced to after the last group name.
5517  * Careful: the argument is modified (NULs added).
5518  * returns FAIL for some error, OK for success.
5519  */
5520     static int
5521 get_id_list(arg, keylen, list)
5522     char_u	**arg;
5523     int		keylen;		/* length of keyword */
5524     short	**list;		/* where to store the resulting list, if not
5525 				   NULL, the list is silently skipped! */
5526 {
5527     char_u	*p = NULL;
5528     char_u	*end;
5529     int		round;
5530     int		count;
5531     int		total_count = 0;
5532     short	*retval = NULL;
5533     char_u	*name;
5534     regmatch_T	regmatch;
5535     int		id;
5536     int		i;
5537     int		failed = FALSE;
5538 
5539     /*
5540      * We parse the list twice:
5541      * round == 1: count the number of items, allocate the array.
5542      * round == 2: fill the array with the items.
5543      * In round 1 new groups may be added, causing the number of items to
5544      * grow when a regexp is used.  In that case round 1 is done once again.
5545      */
5546     for (round = 1; round <= 2; ++round)
5547     {
5548 	/*
5549 	 * skip "contains"
5550 	 */
5551 	p = skipwhite(*arg + keylen);
5552 	if (*p != '=')
5553 	{
5554 	    EMSG2(_("E405: Missing equal sign: %s"), *arg);
5555 	    break;
5556 	}
5557 	p = skipwhite(p + 1);
5558 	if (ends_excmd(*p))
5559 	{
5560 	    EMSG2(_("E406: Empty argument: %s"), *arg);
5561 	    break;
5562 	}
5563 
5564 	/*
5565 	 * parse the arguments after "contains"
5566 	 */
5567 	count = 0;
5568 	while (!ends_excmd(*p))
5569 	{
5570 	    for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5571 		;
5572 	    name = alloc((int)(end - p + 3));	    /* leave room for "^$" */
5573 	    if (name == NULL)
5574 	    {
5575 		failed = TRUE;
5576 		break;
5577 	    }
5578 	    vim_strncpy(name + 1, p, end - p);
5579 	    if (       STRCMP(name + 1, "ALLBUT") == 0
5580 		    || STRCMP(name + 1, "ALL") == 0
5581 		    || STRCMP(name + 1, "TOP") == 0
5582 		    || STRCMP(name + 1, "CONTAINED") == 0)
5583 	    {
5584 		if (TOUPPER_ASC(**arg) != 'C')
5585 		{
5586 		    EMSG2(_("E407: %s not allowed here"), name + 1);
5587 		    failed = TRUE;
5588 		    vim_free(name);
5589 		    break;
5590 		}
5591 		if (count != 0)
5592 		{
5593 		    EMSG2(_("E408: %s must be first in contains list"), name + 1);
5594 		    failed = TRUE;
5595 		    vim_free(name);
5596 		    break;
5597 		}
5598 		if (name[1] == 'A')
5599 		    id = SYNID_ALLBUT;
5600 		else if (name[1] == 'T')
5601 		    id = SYNID_TOP;
5602 		else
5603 		    id = SYNID_CONTAINED;
5604 		id += current_syn_inc_tag;
5605 	    }
5606 	    else if (name[1] == '@')
5607 	    {
5608 		id = syn_check_cluster(name + 2, (int)(end - p - 1));
5609 	    }
5610 	    else
5611 	    {
5612 		/*
5613 		 * Handle full group name.
5614 		 */
5615 		if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5616 		    id = syn_check_group(name + 1, (int)(end - p));
5617 		else
5618 		{
5619 		    /*
5620 		     * Handle match of regexp with group names.
5621 		     */
5622 		    *name = '^';
5623 		    STRCAT(name, "$");
5624 		    regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5625 		    if (regmatch.regprog == NULL)
5626 		    {
5627 			failed = TRUE;
5628 			vim_free(name);
5629 			break;
5630 		    }
5631 
5632 		    regmatch.rm_ic = TRUE;
5633 		    id = 0;
5634 		    for (i = highlight_ga.ga_len; --i >= 0; )
5635 		    {
5636 			if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5637 								  (colnr_T)0))
5638 			{
5639 			    if (round == 2)
5640 			    {
5641 				/* Got more items than expected; can happen
5642 				 * when adding items that match:
5643 				 * "contains=a.*b,axb".
5644 				 * Go back to first round */
5645 				if (count >= total_count)
5646 				{
5647 				    vim_free(retval);
5648 				    round = 1;
5649 				}
5650 				else
5651 				    retval[count] = i + 1;
5652 			    }
5653 			    ++count;
5654 			    id = -1;	    /* remember that we found one */
5655 			}
5656 		    }
5657 		    vim_free(regmatch.regprog);
5658 		}
5659 	    }
5660 	    vim_free(name);
5661 	    if (id == 0)
5662 	    {
5663 		EMSG2(_("E409: Unknown group name: %s"), p);
5664 		failed = TRUE;
5665 		break;
5666 	    }
5667 	    if (id > 0)
5668 	    {
5669 		if (round == 2)
5670 		{
5671 		    /* Got more items than expected, go back to first round */
5672 		    if (count >= total_count)
5673 		    {
5674 			vim_free(retval);
5675 			round = 1;
5676 		    }
5677 		    else
5678 			retval[count] = id;
5679 		}
5680 		++count;
5681 	    }
5682 	    p = skipwhite(end);
5683 	    if (*p != ',')
5684 		break;
5685 	    p = skipwhite(p + 1);	/* skip comma in between arguments */
5686 	}
5687 	if (failed)
5688 	    break;
5689 	if (round == 1)
5690 	{
5691 	    retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5692 	    if (retval == NULL)
5693 		break;
5694 	    retval[count] = 0;	    /* zero means end of the list */
5695 	    total_count = count;
5696 	}
5697     }
5698 
5699     *arg = p;
5700     if (failed || retval == NULL)
5701     {
5702 	vim_free(retval);
5703 	return FAIL;
5704     }
5705 
5706     if (*list == NULL)
5707 	*list = retval;
5708     else
5709 	vim_free(retval);	/* list already found, don't overwrite it */
5710 
5711     return OK;
5712 }
5713 
5714 /*
5715  * Make a copy of an ID list.
5716  */
5717     static short *
5718 copy_id_list(list)
5719     short   *list;
5720 {
5721     int	    len;
5722     int	    count;
5723     short   *retval;
5724 
5725     if (list == NULL)
5726 	return NULL;
5727 
5728     for (count = 0; list[count]; ++count)
5729 	;
5730     len = (count + 1) * sizeof(short);
5731     retval = (short *)alloc((unsigned)len);
5732     if (retval != NULL)
5733 	mch_memmove(retval, list, (size_t)len);
5734 
5735     return retval;
5736 }
5737 
5738 /*
5739  * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5740  * "cur_si" can be NULL if not checking the "containedin" list.
5741  * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5742  * the current item.
5743  * This function is called very often, keep it fast!!
5744  */
5745     static int
5746 in_id_list(cur_si, list, ssp, contained)
5747     stateitem_T	*cur_si;	/* current item or NULL */
5748     short	*list;		/* id list */
5749     struct sp_syn *ssp;		/* group id and ":syn include" tag of group */
5750     int		contained;	/* group id is contained */
5751 {
5752     int		retval;
5753     short	*scl_list;
5754     short	item;
5755     short	id = ssp->id;
5756     static int	depth = 0;
5757     int		r;
5758 
5759     /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
5760     if (cur_si != NULL && ssp->cont_in_list != NULL
5761 					    && !(cur_si->si_flags & HL_MATCH))
5762     {
5763 	/* Ignore transparent items without a contains argument.  Double check
5764 	 * that we don't go back past the first one. */
5765 	while ((cur_si->si_flags & HL_TRANS_CONT)
5766 		&& cur_si > (stateitem_T *)(current_state.ga_data))
5767 	    --cur_si;
5768 	/* cur_si->si_idx is -1 for keywords, these never contain anything. */
5769 	if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5770 		&(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5771 		  SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5772 	    return TRUE;
5773     }
5774 
5775     if (list == NULL)
5776 	return FALSE;
5777 
5778     /*
5779      * If list is ID_LIST_ALL, we are in a transparent item that isn't
5780      * inside anything.  Only allow not-contained groups.
5781      */
5782     if (list == ID_LIST_ALL)
5783 	return !contained;
5784 
5785     /*
5786      * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5787      * contains list.  We also require that "id" is at the same ":syn include"
5788      * level as the list.
5789      */
5790     item = *list;
5791     if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5792     {
5793 	if (item < SYNID_TOP)
5794 	{
5795 	    /* ALL or ALLBUT: accept all groups in the same file */
5796 	    if (item - SYNID_ALLBUT != ssp->inc_tag)
5797 		return FALSE;
5798 	}
5799 	else if (item < SYNID_CONTAINED)
5800 	{
5801 	    /* TOP: accept all not-contained groups in the same file */
5802 	    if (item - SYNID_TOP != ssp->inc_tag || contained)
5803 		return FALSE;
5804 	}
5805 	else
5806 	{
5807 	    /* CONTAINED: accept all contained groups in the same file */
5808 	    if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5809 		return FALSE;
5810 	}
5811 	item = *++list;
5812 	retval = FALSE;
5813     }
5814     else
5815 	retval = TRUE;
5816 
5817     /*
5818      * Return "retval" if id is in the contains list.
5819      */
5820     while (item != 0)
5821     {
5822 	if (item == id)
5823 	    return retval;
5824 	if (item >= SYNID_CLUSTER)
5825 	{
5826 	    scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5827 	    /* restrict recursiveness to 30 to avoid an endless loop for a
5828 	     * cluster that includes itself (indirectly) */
5829 	    if (scl_list != NULL && depth < 30)
5830 	    {
5831 		++depth;
5832 		r = in_id_list(NULL, scl_list, ssp, contained);
5833 		--depth;
5834 		if (r)
5835 		    return retval;
5836 	    }
5837 	}
5838 	item = *++list;
5839     }
5840     return !retval;
5841 }
5842 
5843 struct subcommand
5844 {
5845     char    *name;				/* subcommand name */
5846     void    (*func)__ARGS((exarg_T *, int));	/* function to call */
5847 };
5848 
5849 static struct subcommand subcommands[] =
5850 {
5851     {"case",		syn_cmd_case},
5852     {"clear",		syn_cmd_clear},
5853     {"cluster",		syn_cmd_cluster},
5854     {"enable",		syn_cmd_enable},
5855     {"include",		syn_cmd_include},
5856     {"keyword",		syn_cmd_keyword},
5857     {"list",		syn_cmd_list},
5858     {"manual",		syn_cmd_manual},
5859     {"match",		syn_cmd_match},
5860     {"on",		syn_cmd_on},
5861     {"off",		syn_cmd_off},
5862     {"region",		syn_cmd_region},
5863     {"reset",		syn_cmd_reset},
5864     {"spell",		syn_cmd_spell},
5865     {"sync",		syn_cmd_sync},
5866     {"",		syn_cmd_list},
5867     {NULL, NULL}
5868 };
5869 
5870 /*
5871  * ":syntax".
5872  * This searches the subcommands[] table for the subcommand name, and calls a
5873  * syntax_subcommand() function to do the rest.
5874  */
5875     void
5876 ex_syntax(eap)
5877     exarg_T	*eap;
5878 {
5879     char_u	*arg = eap->arg;
5880     char_u	*subcmd_end;
5881     char_u	*subcmd_name;
5882     int		i;
5883 
5884     syn_cmdlinep = eap->cmdlinep;
5885 
5886     /* isolate subcommand name */
5887     for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5888 	;
5889     subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5890     if (subcmd_name != NULL)
5891     {
5892 	if (eap->skip)		/* skip error messages for all subcommands */
5893 	    ++emsg_skip;
5894 	for (i = 0; ; ++i)
5895 	{
5896 	    if (subcommands[i].name == NULL)
5897 	    {
5898 		EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5899 		break;
5900 	    }
5901 	    if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5902 	    {
5903 		eap->arg = skipwhite(subcmd_end);
5904 		(subcommands[i].func)(eap, FALSE);
5905 		break;
5906 	    }
5907 	}
5908 	vim_free(subcmd_name);
5909 	if (eap->skip)
5910 	    --emsg_skip;
5911     }
5912 }
5913 
5914     int
5915 syntax_present(buf)
5916     buf_T	*buf;
5917 {
5918     return (buf->b_syn_patterns.ga_len != 0
5919 	    || buf->b_syn_clusters.ga_len != 0
5920 	    || curbuf->b_keywtab.ht_used > 0
5921 	    || curbuf->b_keywtab_ic.ht_used > 0);
5922 }
5923 
5924 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5925 
5926 static enum
5927 {
5928     EXP_SUBCMD,	    /* expand ":syn" sub-commands */
5929     EXP_CASE	    /* expand ":syn case" arguments */
5930 } expand_what;
5931 
5932 
5933 /*
5934  * Handle command line completion for :syntax command.
5935  */
5936     void
5937 set_context_in_syntax_cmd(xp, arg)
5938     expand_T	*xp;
5939     char_u	*arg;
5940 {
5941     char_u	*p;
5942 
5943     /* Default: expand subcommands */
5944     xp->xp_context = EXPAND_SYNTAX;
5945     expand_what = EXP_SUBCMD;
5946     xp->xp_pattern = arg;
5947     include_link = FALSE;
5948     include_default = FALSE;
5949 
5950     /* (part of) subcommand already typed */
5951     if (*arg != NUL)
5952     {
5953 	p = skiptowhite(arg);
5954 	if (*p != NUL)		    /* past first word */
5955 	{
5956 	    xp->xp_pattern = skipwhite(p);
5957 	    if (*skiptowhite(xp->xp_pattern) != NUL)
5958 		xp->xp_context = EXPAND_NOTHING;
5959 	    else if (STRNICMP(arg, "case", p - arg) == 0)
5960 		expand_what = EXP_CASE;
5961 	    else if (  STRNICMP(arg, "keyword", p - arg) == 0
5962 		    || STRNICMP(arg, "region", p - arg) == 0
5963 		    || STRNICMP(arg, "match", p - arg) == 0
5964 		    || STRNICMP(arg, "list", p - arg) == 0)
5965 		xp->xp_context = EXPAND_HIGHLIGHT;
5966 	    else
5967 		xp->xp_context = EXPAND_NOTHING;
5968 	}
5969     }
5970 }
5971 
5972 static char *(case_args[]) = {"match", "ignore", NULL};
5973 
5974 /*
5975  * Function given to ExpandGeneric() to obtain the list syntax names for
5976  * expansion.
5977  */
5978 /*ARGSUSED*/
5979     char_u *
5980 get_syntax_name(xp, idx)
5981     expand_T	*xp;
5982     int		idx;
5983 {
5984     if (expand_what == EXP_SUBCMD)
5985 	return (char_u *)subcommands[idx].name;
5986     return (char_u *)case_args[idx];
5987 }
5988 
5989 #endif /* FEAT_CMDL_COMPL */
5990 
5991 /*
5992  * Function called for expression evaluation: get syntax ID at file position.
5993  */
5994     int
5995 syn_get_id(wp, lnum, col, trans, spellp)
5996     win_T	*wp;
5997     long	lnum;
5998     colnr_T	col;
5999     int		trans;	    /* remove transparancy */
6000     int		*spellp;    /* return: can do spell checking */
6001 {
6002     /* When the position is not after the current position and in the same
6003      * line of the same buffer, need to restart parsing. */
6004     if (wp->w_buffer != syn_buf
6005 	    || lnum != current_lnum
6006 	    || col < current_col)
6007 	syntax_start(wp, lnum);
6008 
6009     (void)get_syntax_attr(col, spellp);
6010 
6011     return (trans ? current_trans_id : current_id);
6012 }
6013 
6014 #if defined(FEAT_FOLDING) || defined(PROTO)
6015 /*
6016  * Function called to get folding level for line "lnum" in window "wp".
6017  */
6018     int
6019 syn_get_foldlevel(wp, lnum)
6020     win_T	*wp;
6021     long	lnum;
6022 {
6023     int		level = 0;
6024     int		i;
6025 
6026     /* Return quickly when there are no fold items at all. */
6027     if (wp->w_buffer->b_syn_folditems != 0)
6028     {
6029 	syntax_start(wp, lnum);
6030 
6031 	for (i = 0; i < current_state.ga_len; ++i)
6032 	    if (CUR_STATE(i).si_flags & HL_FOLD)
6033 		++level;
6034     }
6035     if (level > wp->w_p_fdn)
6036 	level = wp->w_p_fdn;
6037     return level;
6038 }
6039 #endif
6040 
6041 #endif /* FEAT_SYN_HL */
6042 
6043 
6044 /**************************************
6045  *  Highlighting stuff		      *
6046  **************************************/
6047 
6048 /*
6049  * The default highlight groups.  These are compiled-in for fast startup and
6050  * they still work when the runtime files can't be found.
6051  * When making changes here, also change runtime/colors/default.vim!
6052  */
6053 static char *(highlight_init_both[]) =
6054     {
6055 #ifdef FEAT_GUI
6056 	"Cursor guibg=fg guifg=bg",
6057 	"lCursor guibg=fg guifg=bg",	/* should be different, but what? */
6058 #endif
6059 	"ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
6060 	"IncSearch term=reverse cterm=reverse gui=reverse",
6061 	"ModeMsg term=bold cterm=bold gui=bold",
6062 	"NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
6063 	"StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
6064 	"StatusLineNC term=reverse cterm=reverse gui=reverse",
6065 	"VertSplit term=reverse cterm=reverse gui=reverse",
6066 	"VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold",
6067 	"DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
6068 	"PmenuThumb cterm=reverse gui=reverse",
6069 	"PmenuSbar ctermbg=Grey guibg=Grey",
6070 	NULL
6071     };
6072 
6073 static char *(highlight_init_light[]) =
6074     {
6075 	"Directory term=bold ctermfg=DarkBlue guifg=Blue",
6076 	"LineNr term=underline ctermfg=Brown guifg=Brown",
6077 	"MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6078 	"Normal gui=NONE",
6079 	"Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6080 	"Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
6081 	"SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl",
6082 	"SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl",
6083 	"SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl",
6084 	"SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl",
6085 	"Pmenu ctermbg=LightMagenta guibg=LightMagenta",
6086 	"PmenuSel ctermbg=LightGrey guibg=Grey",
6087 	"SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
6088 	"Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
6089 	"WarningMsg term=standout ctermfg=DarkRed guifg=Red",
6090 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6091 	"Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
6092 	"FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6093 	"SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6094 	"Visual term=reverse ctermbg=Magenta guibg=LightGrey",
6095 	"DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
6096 	"DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
6097 	"DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
6098 	NULL
6099     };
6100 
6101 static char *(highlight_init_dark[]) =
6102     {
6103 	"Directory term=bold ctermfg=LightCyan guifg=Cyan",
6104 	"LineNr term=underline ctermfg=Yellow guifg=Yellow",
6105 	"MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
6106 	"Normal gui=NONE",
6107 	"Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
6108 	"Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6109 	"SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
6110 	"SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl",
6111 	"SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl",
6112 	"SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl",
6113 	"SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl",
6114 	"Pmenu ctermbg=Magenta guibg=Magenta",
6115 	"PmenuSel ctermbg=Grey guibg=Grey",
6116 	"Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
6117 	"WarningMsg term=standout ctermfg=LightRed guifg=Red",
6118 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6119 	"Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
6120 	"FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6121 	"SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6122 	"Visual term=reverse ctermbg=Magenta guibg=DarkGrey",
6123 	"DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6124 	"DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6125 	"DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6126 	NULL
6127     };
6128 
6129     void
6130 init_highlight(both, reset)
6131     int		both;	    /* include groups where 'bg' doesn't matter */
6132     int		reset;	    /* clear group first */
6133 {
6134     int		i;
6135     char	**pp;
6136     static int	had_both = FALSE;
6137 #ifdef FEAT_EVAL
6138     char_u	*p;
6139 
6140     /*
6141      * Try finding the color scheme file.  Used when a color file was loaded
6142      * and 'background' or 't_Co' is changed.
6143      */
6144     p = get_var_value((char_u *)"g:colors_name");
6145     if (p != NULL && load_colors(p) == OK)
6146 	return;
6147 #endif
6148 
6149     /*
6150      * Didn't use a color file, use the compiled-in colors.
6151      */
6152     if (both)
6153     {
6154 	had_both = TRUE;
6155 	pp = highlight_init_both;
6156 	for (i = 0; pp[i] != NULL; ++i)
6157 	    do_highlight((char_u *)pp[i], reset, TRUE);
6158     }
6159     else if (!had_both)
6160 	/* Don't do anything before the call with both == TRUE from main().
6161 	 * Not everything has been setup then, and that call will overrule
6162 	 * everything anyway. */
6163 	return;
6164 
6165     if (*p_bg == 'l')
6166 	pp = highlight_init_light;
6167     else
6168 	pp = highlight_init_dark;
6169     for (i = 0; pp[i] != NULL; ++i)
6170 	do_highlight((char_u *)pp[i], reset, TRUE);
6171 
6172     /* Magenta background looks ugly, but grey may not work for 8 colors.
6173      * Thus let it depend on the number of colors available. */
6174     if (t_colors > 8)
6175 	do_highlight((char_u *)(*p_bg == 'l' ? "Visual ctermbg=LightGrey"
6176 				   : "Visual ctermbg=DarkGrey"), reset, TRUE);
6177 
6178 #ifdef FEAT_SYN_HL
6179     /*
6180      * If syntax highlighting is enabled load the highlighting for it.
6181      */
6182     if (get_var_value((char_u *)"g:syntax_on") != NULL)
6183     {
6184 	static int	recursive = 0;
6185 
6186 	if (recursive >= 5)
6187 	    EMSG(_("E679: recursive loop loading syncolor.vim"));
6188 	else
6189 	{
6190 	    ++recursive;
6191 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6192 	    --recursive;
6193 	}
6194     }
6195 #endif
6196 }
6197 
6198 /*
6199  * Load color file "name".
6200  * Return OK for success, FAIL for failure.
6201  */
6202     int
6203 load_colors(name)
6204     char_u	*name;
6205 {
6206     char_u	*buf;
6207     int		retval = FAIL;
6208     static int	recursive = FALSE;
6209 
6210     /* When being called recursively, this is probably because setting
6211      * 'background' caused the highlighting to be reloaded.  This means it is
6212      * working, thus we should return OK. */
6213     if (recursive)
6214 	return OK;
6215 
6216     recursive = TRUE;
6217     buf = alloc((unsigned)(STRLEN(name) + 12));
6218     if (buf != NULL)
6219     {
6220 	sprintf((char *)buf, "colors/%s.vim", name);
6221 	retval = source_runtime(buf, FALSE);
6222 	vim_free(buf);
6223 #ifdef FEAT_AUTOCMD
6224 	apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6225 #endif
6226     }
6227     recursive = FALSE;
6228 
6229     return retval;
6230 }
6231 
6232 /*
6233  * Handle the ":highlight .." command.
6234  * When using ":hi clear" this is called recursively for each group with
6235  * "forceit" and "init" both TRUE.
6236  */
6237     void
6238 do_highlight(line, forceit, init)
6239     char_u	*line;
6240     int		forceit;
6241     int		init;	    /* TRUE when called for initializing */
6242 {
6243     char_u	*name_end;
6244     char_u	*p;
6245     char_u	*linep;
6246     char_u	*key_start;
6247     char_u	*arg_start;
6248     char_u	*key = NULL, *arg = NULL;
6249     long	i;
6250     int		off;
6251     int		len;
6252     int		attr;
6253     int		id;
6254     int		idx;
6255     int		dodefault = FALSE;
6256     int		doclear = FALSE;
6257     int		dolink = FALSE;
6258     int		error = FALSE;
6259     int		color;
6260     int		is_normal_group = FALSE;	/* "Normal" group */
6261 #ifdef FEAT_GUI_X11
6262     int		is_menu_group = FALSE;		/* "Menu" group */
6263     int		is_scrollbar_group = FALSE;	/* "Scrollbar" group */
6264     int		is_tooltip_group = FALSE;	/* "Tooltip" group */
6265     int		do_colors = FALSE;		/* need to update colors? */
6266 #else
6267 # define is_menu_group 0
6268 # define is_tooltip_group 0
6269 #endif
6270 
6271     /*
6272      * If no argument, list current highlighting.
6273      */
6274     if (ends_excmd(*line))
6275     {
6276 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6277 	    /* TODO: only call when the group has attributes set */
6278 	    highlight_list_one((int)i);
6279 	return;
6280     }
6281 
6282     /*
6283      * Isolate the name.
6284      */
6285     name_end = skiptowhite(line);
6286     linep = skipwhite(name_end);
6287 
6288     /*
6289      * Check for "default" argument.
6290      */
6291     if (STRNCMP(line, "default", name_end - line) == 0)
6292     {
6293 	dodefault = TRUE;
6294 	line = linep;
6295 	name_end = skiptowhite(line);
6296 	linep = skipwhite(name_end);
6297     }
6298 
6299     /*
6300      * Check for "clear" or "link" argument.
6301      */
6302     if (STRNCMP(line, "clear", name_end - line) == 0)
6303 	doclear = TRUE;
6304     if (STRNCMP(line, "link", name_end - line) == 0)
6305 	dolink = TRUE;
6306 
6307     /*
6308      * ":highlight {group-name}": list highlighting for one group.
6309      */
6310     if (!doclear && !dolink && ends_excmd(*linep))
6311     {
6312 	id = syn_namen2id(line, (int)(name_end - line));
6313 	if (id == 0)
6314 	    EMSG2(_("E411: highlight group not found: %s"), line);
6315 	else
6316 	    highlight_list_one(id);
6317 	return;
6318     }
6319 
6320     /*
6321      * Handle ":highlight link {from} {to}" command.
6322      */
6323     if (dolink)
6324     {
6325 	char_u	    *from_start = linep;
6326 	char_u	    *from_end;
6327 	char_u	    *to_start;
6328 	char_u	    *to_end;
6329 	int	    from_id;
6330 	int	    to_id;
6331 
6332 	from_end = skiptowhite(from_start);
6333 	to_start = skipwhite(from_end);
6334 	to_end	 = skiptowhite(to_start);
6335 
6336 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
6337 	{
6338 	    EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6339 								  from_start);
6340 	    return;
6341 	}
6342 
6343 	if (!ends_excmd(*skipwhite(to_end)))
6344 	{
6345 	    EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6346 	    return;
6347 	}
6348 
6349 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
6350 	if (STRNCMP(to_start, "NONE", 4) == 0)
6351 	    to_id = 0;
6352 	else
6353 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
6354 
6355 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6356 	{
6357 	    /*
6358 	     * Don't allow a link when there already is some highlighting
6359 	     * for the group, unless '!' is used
6360 	     */
6361 	    if (to_id > 0 && !forceit && !init
6362 				   && hl_has_settings(from_id - 1, dodefault))
6363 	    {
6364 		if (sourcing_name == NULL && !dodefault)
6365 		    EMSG(_("E414: group has settings, highlight link ignored"));
6366 	    }
6367 	    else
6368 	    {
6369 		if (!init)
6370 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6371 		HL_TABLE()[from_id - 1].sg_link = to_id;
6372 #ifdef FEAT_EVAL
6373 		HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6374 #endif
6375 		redraw_all_later(NOT_VALID);
6376 	    }
6377 	}
6378 
6379 	/* Only call highlight_changed() once, after sourcing a syntax file */
6380 	need_highlight_changed = TRUE;
6381 
6382 	return;
6383     }
6384 
6385     if (doclear)
6386     {
6387 	/*
6388 	 * ":highlight clear [group]" command.
6389 	 */
6390 	line = linep;
6391 	if (ends_excmd(*line))
6392 	{
6393 #ifdef FEAT_GUI
6394 	    /* First, we do not destroy the old values, but allocate the new
6395 	     * ones and update the display. THEN we destroy the old values.
6396 	     * If we destroy the old values first, then the old values
6397 	     * (such as GuiFont's or GuiFontset's) will still be displayed but
6398 	     * invalid because they were free'd.
6399 	     */
6400 	    if (gui.in_use)
6401 	    {
6402 # ifdef FEAT_BEVAL_TIP
6403 		gui_init_tooltip_font();
6404 # endif
6405 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6406 		gui_init_menu_font();
6407 # endif
6408 	    }
6409 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6410 	    gui_mch_def_colors();
6411 # endif
6412 # ifdef FEAT_GUI_X11
6413 #  ifdef FEAT_MENU
6414 
6415 	    /* This only needs to be done when there is no Menu highlight
6416 	     * group defined by default, which IS currently the case.
6417 	     */
6418 	    gui_mch_new_menu_colors();
6419 #  endif
6420 	    if (gui.in_use)
6421 	    {
6422 		gui_new_scrollbar_colors();
6423 #  ifdef FEAT_BEVAL
6424 		gui_mch_new_tooltip_colors();
6425 #  endif
6426 #  ifdef FEAT_MENU
6427 		gui_mch_new_menu_font();
6428 #  endif
6429 	    }
6430 # endif
6431 
6432 	    /* Ok, we're done allocating the new default graphics items.
6433 	     * The screen should already be refreshed at this point.
6434 	     * It is now Ok to clear out the old data.
6435 	     */
6436 #endif
6437 #ifdef FEAT_EVAL
6438 	    do_unlet((char_u *)"colors_name", TRUE);
6439 #endif
6440 	    restore_cterm_colors();
6441 
6442 	    /*
6443 	     * Clear all default highlight groups and load the defaults.
6444 	     */
6445 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6446 		highlight_clear(idx);
6447 	    init_highlight(TRUE, TRUE);
6448 #ifdef FEAT_GUI
6449 	    if (gui.in_use)
6450 		highlight_gui_started();
6451 #endif
6452 	    highlight_changed();
6453 	    redraw_later_clear();
6454 	    return;
6455 	}
6456 	name_end = skiptowhite(line);
6457 	linep = skipwhite(name_end);
6458     }
6459 
6460     /*
6461      * Find the group name in the table.  If it does not exist yet, add it.
6462      */
6463     id = syn_check_group(line, (int)(name_end - line));
6464     if (id == 0)			/* failed (out of memory) */
6465 	return;
6466     idx = id - 1;			/* index is ID minus one */
6467 
6468     /* Return if "default" was used and the group already has settings. */
6469     if (dodefault && hl_has_settings(idx, TRUE))
6470 	return;
6471 
6472     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6473 	is_normal_group = TRUE;
6474 #ifdef FEAT_GUI_X11
6475     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6476 	is_menu_group = TRUE;
6477     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6478 	is_scrollbar_group = TRUE;
6479     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6480 	is_tooltip_group = TRUE;
6481 #endif
6482 
6483     /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6484     if (doclear || (forceit && init))
6485     {
6486 	highlight_clear(idx);
6487 	if (!doclear)
6488 	    HL_TABLE()[idx].sg_set = 0;
6489     }
6490 
6491     if (!doclear)
6492       while (!ends_excmd(*linep))
6493       {
6494 	key_start = linep;
6495 	if (*linep == '=')
6496 	{
6497 	    EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6498 	    error = TRUE;
6499 	    break;
6500 	}
6501 
6502 	/*
6503 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6504 	 * "guibg").
6505 	 */
6506 	while (*linep && !vim_iswhite(*linep) && *linep != '=')
6507 	    ++linep;
6508 	vim_free(key);
6509 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
6510 	if (key == NULL)
6511 	{
6512 	    error = TRUE;
6513 	    break;
6514 	}
6515 	linep = skipwhite(linep);
6516 
6517 	if (STRCMP(key, "NONE") == 0)
6518 	{
6519 	    if (!init || HL_TABLE()[idx].sg_set == 0)
6520 	    {
6521 		if (!init)
6522 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6523 		highlight_clear(idx);
6524 	    }
6525 	    continue;
6526 	}
6527 
6528 	/*
6529 	 * Check for the equal sign.
6530 	 */
6531 	if (*linep != '=')
6532 	{
6533 	    EMSG2(_("E416: missing equal sign: %s"), key_start);
6534 	    error = TRUE;
6535 	    break;
6536 	}
6537 	++linep;
6538 
6539 	/*
6540 	 * Isolate the argument.
6541 	 */
6542 	linep = skipwhite(linep);
6543 	if (*linep == '\'')		/* guifg='color name' */
6544 	{
6545 	    arg_start = ++linep;
6546 	    linep = vim_strchr(linep, '\'');
6547 	    if (linep == NULL)
6548 	    {
6549 		EMSG2(_(e_invarg2), key_start);
6550 		error = TRUE;
6551 		break;
6552 	    }
6553 	}
6554 	else
6555 	{
6556 	    arg_start = linep;
6557 	    linep = skiptowhite(linep);
6558 	}
6559 	if (linep == arg_start)
6560 	{
6561 	    EMSG2(_("E417: missing argument: %s"), key_start);
6562 	    error = TRUE;
6563 	    break;
6564 	}
6565 	vim_free(arg);
6566 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6567 	if (arg == NULL)
6568 	{
6569 	    error = TRUE;
6570 	    break;
6571 	}
6572 	if (*linep == '\'')
6573 	    ++linep;
6574 
6575 	/*
6576 	 * Store the argument.
6577 	 */
6578 	if (  STRCMP(key, "TERM") == 0
6579 		|| STRCMP(key, "CTERM") == 0
6580 		|| STRCMP(key, "GUI") == 0)
6581 	{
6582 	    attr = 0;
6583 	    off = 0;
6584 	    while (arg[off] != NUL)
6585 	    {
6586 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6587 		{
6588 		    len = (int)STRLEN(hl_name_table[i]);
6589 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6590 		    {
6591 			attr |= hl_attr_table[i];
6592 			off += len;
6593 			break;
6594 		    }
6595 		}
6596 		if (i < 0)
6597 		{
6598 		    EMSG2(_("E418: Illegal value: %s"), arg);
6599 		    error = TRUE;
6600 		    break;
6601 		}
6602 		if (arg[off] == ',')		/* another one follows */
6603 		    ++off;
6604 	    }
6605 	    if (error)
6606 		break;
6607 	    if (*key == 'T')
6608 	    {
6609 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6610 		{
6611 		    if (!init)
6612 			HL_TABLE()[idx].sg_set |= SG_TERM;
6613 		    HL_TABLE()[idx].sg_term = attr;
6614 		}
6615 	    }
6616 	    else if (*key == 'C')
6617 	    {
6618 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6619 		{
6620 		    if (!init)
6621 			HL_TABLE()[idx].sg_set |= SG_CTERM;
6622 		    HL_TABLE()[idx].sg_cterm = attr;
6623 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
6624 		}
6625 	    }
6626 #ifdef FEAT_GUI
6627 	    else
6628 	    {
6629 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6630 		{
6631 		    if (!init)
6632 			HL_TABLE()[idx].sg_set |= SG_GUI;
6633 		    HL_TABLE()[idx].sg_gui = attr;
6634 		}
6635 	    }
6636 #endif
6637 	}
6638 	else if (STRCMP(key, "FONT") == 0)
6639 	{
6640 	    /* in non-GUI fonts are simply ignored */
6641 #ifdef FEAT_GUI
6642 	    if (!gui.shell_created)
6643 	    {
6644 		/* GUI not started yet, always accept the name. */
6645 		vim_free(HL_TABLE()[idx].sg_font_name);
6646 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6647 	    }
6648 	    else
6649 	    {
6650 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6651 # ifdef FEAT_XFONTSET
6652 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6653 # endif
6654 		/* First, save the current font/fontset.
6655 		 * Then try to allocate the font/fontset.
6656 		 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6657 		 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6658 		 */
6659 
6660 		HL_TABLE()[idx].sg_font = NOFONT;
6661 # ifdef FEAT_XFONTSET
6662 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
6663 # endif
6664 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
6665 							    is_tooltip_group);
6666 
6667 # ifdef FEAT_XFONTSET
6668 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6669 		{
6670 		    /* New fontset was accepted. Free the old one, if there was
6671 		     * one.
6672 		     */
6673 		    gui_mch_free_fontset(temp_sg_fontset);
6674 		    vim_free(HL_TABLE()[idx].sg_font_name);
6675 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6676 		}
6677 		else
6678 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6679 # endif
6680 		if (HL_TABLE()[idx].sg_font != NOFONT)
6681 		{
6682 		    /* New font was accepted. Free the old one, if there was
6683 		     * one.
6684 		     */
6685 		    gui_mch_free_font(temp_sg_font);
6686 		    vim_free(HL_TABLE()[idx].sg_font_name);
6687 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6688 		}
6689 		else
6690 		    HL_TABLE()[idx].sg_font = temp_sg_font;
6691 	    }
6692 #endif
6693 	}
6694 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6695 	{
6696 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6697 	  {
6698 	    if (!init)
6699 		HL_TABLE()[idx].sg_set |= SG_CTERM;
6700 
6701 	    /* When setting the foreground color, and previously the "bold"
6702 	     * flag was set for a light color, reset it now */
6703 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6704 	    {
6705 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6706 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
6707 	    }
6708 
6709 	    if (VIM_ISDIGIT(*arg))
6710 		color = atoi((char *)arg);
6711 	    else if (STRICMP(arg, "fg") == 0)
6712 	    {
6713 		if (cterm_normal_fg_color)
6714 		    color = cterm_normal_fg_color - 1;
6715 		else
6716 		{
6717 		    EMSG(_("E419: FG color unknown"));
6718 		    error = TRUE;
6719 		    break;
6720 		}
6721 	    }
6722 	    else if (STRICMP(arg, "bg") == 0)
6723 	    {
6724 		if (cterm_normal_bg_color > 0)
6725 		    color = cterm_normal_bg_color - 1;
6726 		else
6727 		{
6728 		    EMSG(_("E420: BG color unknown"));
6729 		    error = TRUE;
6730 		    break;
6731 		}
6732 	    }
6733 	    else
6734 	    {
6735 		static char *(color_names[28]) = {
6736 			    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6737 			    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6738 			    "Gray", "Grey",
6739 			    "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6740 			    "Blue", "LightBlue", "Green", "LightGreen",
6741 			    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6742 			    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6743 		static int color_numbers_16[28] = {0, 1, 2, 3,
6744 						 4, 5, 6, 6,
6745 						 7, 7,
6746 						 7, 7, 8, 8,
6747 						 9, 9, 10, 10,
6748 						 11, 11, 12, 12, 13,
6749 						 13, 14, 14, 15, -1};
6750 		/* for xterm with 88 colors... */
6751 		static int color_numbers_88[28] = {0, 4, 2, 6,
6752 						 1, 5, 32, 72,
6753 						 84, 84,
6754 						 7, 7, 82, 82,
6755 						 12, 43, 10, 61,
6756 						 14, 63, 9, 74, 13,
6757 						 75, 11, 78, 15, -1};
6758 		/* for xterm with 256 colors... */
6759 		static int color_numbers_256[28] = {0, 4, 2, 6,
6760 						 1, 5, 130, 130,
6761 						 248, 248,
6762 						 7, 7, 242, 242,
6763 						 12, 81, 10, 121,
6764 						 14, 159, 9, 224, 13,
6765 						 225, 11, 229, 15, -1};
6766 		/* for terminals with less than 16 colors... */
6767 		static int color_numbers_8[28] = {0, 4, 2, 6,
6768 						 1, 5, 3, 3,
6769 						 7, 7,
6770 						 7, 7, 0+8, 0+8,
6771 						 4+8, 4+8, 2+8, 2+8,
6772 						 6+8, 6+8, 1+8, 1+8, 5+8,
6773 						 5+8, 3+8, 3+8, 7+8, -1};
6774 #if defined(__QNXNTO__)
6775 		static int *color_numbers_8_qansi = color_numbers_8;
6776 		/* On qnx, the 8 & 16 color arrays are the same */
6777 		if (STRNCMP(T_NAME, "qansi", 5) == 0)
6778 		    color_numbers_8_qansi = color_numbers_16;
6779 #endif
6780 
6781 		/* reduce calls to STRICMP a bit, it can be slow */
6782 		off = TOUPPER_ASC(*arg);
6783 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6784 		    if (off == color_names[i][0]
6785 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6786 			break;
6787 		if (i < 0)
6788 		{
6789 		    EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6790 		    error = TRUE;
6791 		    break;
6792 		}
6793 
6794 		/* Use the _16 table to check if its a valid color name. */
6795 		color = color_numbers_16[i];
6796 		if (color >= 0)
6797 		{
6798 		    if (t_colors == 8)
6799 		    {
6800 			/* t_Co is 8: use the 8 colors table */
6801 #if defined(__QNXNTO__)
6802 			color = color_numbers_8_qansi[i];
6803 #else
6804 			color = color_numbers_8[i];
6805 #endif
6806 			if (key[5] == 'F')
6807 			{
6808 			    /* set/reset bold attribute to get light foreground
6809 			     * colors (on some terminals, e.g. "linux") */
6810 			    if (color & 8)
6811 			    {
6812 				HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6813 				HL_TABLE()[idx].sg_cterm_bold = TRUE;
6814 			    }
6815 			    else
6816 				HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6817 			}
6818 			color &= 7;	/* truncate to 8 colors */
6819 		    }
6820 		    else if (t_colors == 16 || t_colors == 88
6821 							   || t_colors == 256)
6822 		    {
6823 			/*
6824 			 * Guess: if the termcap entry ends in 'm', it is
6825 			 * probably an xterm-like terminal.  Use the changed
6826 			 * order for colors.
6827 			 */
6828 			if (*T_CAF != NUL)
6829 			    p = T_CAF;
6830 			else
6831 			    p = T_CSF;
6832 			if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6833 			    switch (t_colors)
6834 			    {
6835 				case 16:
6836 				    color = color_numbers_8[i];
6837 				    break;
6838 				case 88:
6839 				    color = color_numbers_88[i];
6840 				    break;
6841 				case 256:
6842 				    color = color_numbers_256[i];
6843 				    break;
6844 			    }
6845 		    }
6846 		}
6847 	    }
6848 	    /* Add one to the argument, to avoid zero */
6849 	    if (key[5] == 'F')
6850 	    {
6851 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
6852 		if (is_normal_group)
6853 		{
6854 		    cterm_normal_fg_color = color + 1;
6855 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6856 #ifdef FEAT_GUI
6857 		    /* Don't do this if the GUI is used. */
6858 		    if (!gui.in_use && !gui.starting)
6859 #endif
6860 		    {
6861 			must_redraw = CLEAR;
6862 			if (termcap_active)
6863 			    term_fg_color(color);
6864 		    }
6865 		}
6866 	    }
6867 	    else
6868 	    {
6869 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
6870 		if (is_normal_group)
6871 		{
6872 		    cterm_normal_bg_color = color + 1;
6873 #ifdef FEAT_GUI
6874 		    /* Don't mess with 'background' if the GUI is used. */
6875 		    if (!gui.in_use && !gui.starting)
6876 #endif
6877 		    {
6878 			must_redraw = CLEAR;
6879 			if (termcap_active)
6880 			    term_bg_color(color);
6881 			if (t_colors < 16)
6882 			    i = (color == 0 || color == 4);
6883 			else
6884 			    i = (color < 7 || color == 8);
6885 			/* Set the 'background' option if the value is wrong. */
6886 			if (i != (*p_bg == 'd'))
6887 			    set_option_value((char_u *)"bg", 0L,
6888 				 i ? (char_u *)"dark" : (char_u *)"light", 0);
6889 		    }
6890 		}
6891 	    }
6892 	  }
6893 	}
6894 	else if (STRCMP(key, "GUIFG") == 0)
6895 	{
6896 #ifdef FEAT_GUI	    /* in non-GUI guifg colors are simply ignored */
6897 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6898 	    {
6899 		if (!init)
6900 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6901 
6902 		i = color_name2handle(arg);
6903 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6904 		{
6905 		    HL_TABLE()[idx].sg_gui_fg = i;
6906 		    vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6907 		    if (STRCMP(arg, "NONE"))
6908 			HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6909 		    else
6910 			HL_TABLE()[idx].sg_gui_fg_name = NULL;
6911 # ifdef FEAT_GUI_X11
6912 		    if (is_menu_group)
6913 			gui.menu_fg_pixel = i;
6914 		    if (is_scrollbar_group)
6915 			gui.scroll_fg_pixel = i;
6916 #  ifdef FEAT_BEVAL
6917 		    if (is_tooltip_group)
6918 			gui.tooltip_fg_pixel = i;
6919 #  endif
6920 		    do_colors = TRUE;
6921 # endif
6922 		}
6923 	    }
6924 #endif
6925 	}
6926 	else if (STRCMP(key, "GUIBG") == 0)
6927 	{
6928 #ifdef FEAT_GUI	    /* in non-GUI guibg colors are simply ignored */
6929 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6930 	    {
6931 		if (!init)
6932 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6933 
6934 		i = color_name2handle(arg);
6935 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6936 		{
6937 		    HL_TABLE()[idx].sg_gui_bg = i;
6938 		    vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6939 		    if (STRCMP(arg, "NONE") != 0)
6940 			HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6941 		    else
6942 			HL_TABLE()[idx].sg_gui_bg_name = NULL;
6943 # ifdef FEAT_GUI_X11
6944 		    if (is_menu_group)
6945 			gui.menu_bg_pixel = i;
6946 		    if (is_scrollbar_group)
6947 			gui.scroll_bg_pixel = i;
6948 #  ifdef FEAT_BEVAL
6949 		    if (is_tooltip_group)
6950 			gui.tooltip_bg_pixel = i;
6951 #  endif
6952 		    do_colors = TRUE;
6953 # endif
6954 		}
6955 	    }
6956 #endif
6957 	}
6958 	else if (STRCMP(key, "GUISP") == 0)
6959 	{
6960 #ifdef FEAT_GUI	    /* in non-GUI guisp colors are simply ignored */
6961 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6962 	    {
6963 		if (!init)
6964 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6965 
6966 		i = color_name2handle(arg);
6967 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6968 		{
6969 		    HL_TABLE()[idx].sg_gui_sp = i;
6970 		    vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6971 		    if (STRCMP(arg, "NONE") != 0)
6972 			HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6973 		    else
6974 			HL_TABLE()[idx].sg_gui_sp_name = NULL;
6975 		}
6976 	    }
6977 #endif
6978 	}
6979 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6980 	{
6981 	    char_u	buf[100];
6982 	    char_u	*tname;
6983 
6984 	    if (!init)
6985 		HL_TABLE()[idx].sg_set |= SG_TERM;
6986 
6987 	    /*
6988 	     * The "start" and "stop"  arguments can be a literal escape
6989 	     * sequence, or a comma seperated list of terminal codes.
6990 	     */
6991 	    if (STRNCMP(arg, "t_", 2) == 0)
6992 	    {
6993 		off = 0;
6994 		buf[0] = 0;
6995 		while (arg[off] != NUL)
6996 		{
6997 		    /* Isolate one termcap name */
6998 		    for (len = 0; arg[off + len] &&
6999 						 arg[off + len] != ','; ++len)
7000 			;
7001 		    tname = vim_strnsave(arg + off, len);
7002 		    if (tname == NULL)		/* out of memory */
7003 		    {
7004 			error = TRUE;
7005 			break;
7006 		    }
7007 		    /* lookup the escape sequence for the item */
7008 		    p = get_term_code(tname);
7009 		    vim_free(tname);
7010 		    if (p == NULL)	    /* ignore non-existing things */
7011 			p = (char_u *)"";
7012 
7013 		    /* Append it to the already found stuff */
7014 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7015 		    {
7016 			EMSG2(_("E422: terminal code too long: %s"), arg);
7017 			error = TRUE;
7018 			break;
7019 		    }
7020 		    STRCAT(buf, p);
7021 
7022 		    /* Advance to the next item */
7023 		    off += len;
7024 		    if (arg[off] == ',')	    /* another one follows */
7025 			++off;
7026 		}
7027 	    }
7028 	    else
7029 	    {
7030 		/*
7031 		 * Copy characters from arg[] to buf[], translating <> codes.
7032 		 */
7033 		for (p = arg, off = 0; off < 100 && *p; )
7034 		{
7035 		    len = trans_special(&p, buf + off, FALSE);
7036 		    if (len)		    /* recognized special char */
7037 			off += len;
7038 		    else		    /* copy as normal char */
7039 			buf[off++] = *p++;
7040 		}
7041 		buf[off] = NUL;
7042 	    }
7043 	    if (error)
7044 		break;
7045 
7046 	    if (STRCMP(buf, "NONE") == 0)	/* resetting the value */
7047 		p = NULL;
7048 	    else
7049 		p = vim_strsave(buf);
7050 	    if (key[2] == 'A')
7051 	    {
7052 		vim_free(HL_TABLE()[idx].sg_start);
7053 		HL_TABLE()[idx].sg_start = p;
7054 	    }
7055 	    else
7056 	    {
7057 		vim_free(HL_TABLE()[idx].sg_stop);
7058 		HL_TABLE()[idx].sg_stop = p;
7059 	    }
7060 	}
7061 	else
7062 	{
7063 	    EMSG2(_("E423: Illegal argument: %s"), key_start);
7064 	    error = TRUE;
7065 	    break;
7066 	}
7067 
7068 	/*
7069 	 * When highlighting has been given for a group, don't link it.
7070 	 */
7071 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7072 	    HL_TABLE()[idx].sg_link = 0;
7073 
7074 	/*
7075 	 * Continue with next argument.
7076 	 */
7077 	linep = skipwhite(linep);
7078       }
7079 
7080     /*
7081      * If there is an error, and it's a new entry, remove it from the table.
7082      */
7083     if (error && idx == highlight_ga.ga_len)
7084 	syn_unadd_group();
7085     else
7086     {
7087 	if (is_normal_group)
7088 	{
7089 	    HL_TABLE()[idx].sg_term_attr = 0;
7090 	    HL_TABLE()[idx].sg_cterm_attr = 0;
7091 #ifdef FEAT_GUI
7092 	    HL_TABLE()[idx].sg_gui_attr = 0;
7093 	    /*
7094 	     * Need to update all groups, because they might be using "bg"
7095 	     * and/or "fg", which have been changed now.
7096 	     */
7097 	    if (gui.in_use)
7098 		highlight_gui_started();
7099 #endif
7100 	}
7101 #ifdef FEAT_GUI_X11
7102 # ifdef FEAT_MENU
7103 	else if (is_menu_group)
7104 	{
7105 	    if (gui.in_use && do_colors)
7106 		gui_mch_new_menu_colors();
7107 	}
7108 # endif
7109 	else if (is_scrollbar_group)
7110 	{
7111 	    if (gui.in_use && do_colors)
7112 		gui_new_scrollbar_colors();
7113 	}
7114 # ifdef FEAT_BEVAL
7115 	else if (is_tooltip_group)
7116 	{
7117 	    if (gui.in_use && do_colors)
7118 		gui_mch_new_tooltip_colors();
7119 	}
7120 # endif
7121 #endif
7122 	else
7123 	    set_hl_attr(idx);
7124 #ifdef FEAT_EVAL
7125 	HL_TABLE()[idx].sg_scriptID = current_SID;
7126 #endif
7127 	redraw_all_later(NOT_VALID);
7128     }
7129     vim_free(key);
7130     vim_free(arg);
7131 
7132     /* Only call highlight_changed() once, after sourcing a syntax file */
7133     need_highlight_changed = TRUE;
7134 }
7135 
7136 #if defined(EXITFREE) || defined(PROTO)
7137     void
7138 free_highlight()
7139 {
7140     int	    i;
7141 
7142     for (i = 0; i < highlight_ga.ga_len; ++i)
7143     {
7144 	highlight_clear(i);
7145 	vim_free(HL_TABLE()[i].sg_name);
7146 	vim_free(HL_TABLE()[i].sg_name_u);
7147     }
7148     ga_clear(&highlight_ga);
7149 }
7150 #endif
7151 
7152 /*
7153  * Reset the cterm colors to what they were before Vim was started, if
7154  * possible.  Otherwise reset them to zero.
7155  */
7156     void
7157 restore_cterm_colors()
7158 {
7159 #if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7160     /* Since t_me has been set, this probably means that the user
7161      * wants to use this as default colors.  Need to reset default
7162      * background/foreground colors. */
7163     mch_set_normal_colors();
7164 #else
7165     cterm_normal_fg_color = 0;
7166     cterm_normal_fg_bold = 0;
7167     cterm_normal_bg_color = 0;
7168 #endif
7169 }
7170 
7171 /*
7172  * Return TRUE if highlight group "idx" has any settings.
7173  * When "check_link" is TRUE also check for an existing link.
7174  */
7175     static int
7176 hl_has_settings(idx, check_link)
7177     int		idx;
7178     int		check_link;
7179 {
7180     return (   HL_TABLE()[idx].sg_term_attr != 0
7181 	    || HL_TABLE()[idx].sg_cterm_attr != 0
7182 #ifdef FEAT_GUI
7183 	    || HL_TABLE()[idx].sg_gui_attr != 0
7184 #endif
7185 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7186 }
7187 
7188 /*
7189  * Clear highlighting for one group.
7190  */
7191     static void
7192 highlight_clear(idx)
7193     int idx;
7194 {
7195     HL_TABLE()[idx].sg_term = 0;
7196     vim_free(HL_TABLE()[idx].sg_start);
7197     HL_TABLE()[idx].sg_start = NULL;
7198     vim_free(HL_TABLE()[idx].sg_stop);
7199     HL_TABLE()[idx].sg_stop = NULL;
7200     HL_TABLE()[idx].sg_term_attr = 0;
7201     HL_TABLE()[idx].sg_cterm = 0;
7202     HL_TABLE()[idx].sg_cterm_bold = FALSE;
7203     HL_TABLE()[idx].sg_cterm_fg = 0;
7204     HL_TABLE()[idx].sg_cterm_bg = 0;
7205     HL_TABLE()[idx].sg_cterm_attr = 0;
7206 #ifdef FEAT_GUI	    /* in non-GUI fonts are simply ignored */
7207     HL_TABLE()[idx].sg_gui = 0;
7208     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7209     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7210     HL_TABLE()[idx].sg_gui_fg_name = NULL;
7211     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7212     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7213     HL_TABLE()[idx].sg_gui_bg_name = NULL;
7214     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7215     vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7216     HL_TABLE()[idx].sg_gui_sp_name = NULL;
7217     gui_mch_free_font(HL_TABLE()[idx].sg_font);
7218     HL_TABLE()[idx].sg_font = NOFONT;
7219 # ifdef FEAT_XFONTSET
7220     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7221     HL_TABLE()[idx].sg_fontset = NOFONTSET;
7222 # endif
7223     vim_free(HL_TABLE()[idx].sg_font_name);
7224     HL_TABLE()[idx].sg_font_name = NULL;
7225     HL_TABLE()[idx].sg_gui_attr = 0;
7226 #endif
7227 #ifdef FEAT_EVAL
7228     /* Clear the script ID only when there is no link, since that is not
7229      * cleared. */
7230     if (HL_TABLE()[idx].sg_link == 0)
7231 	HL_TABLE()[idx].sg_scriptID = 0;
7232 #endif
7233 }
7234 
7235 #if defined(FEAT_GUI) || defined(PROTO)
7236 /*
7237  * Set the normal foreground and background colors according to the "Normal"
7238  * highlighighting group.  For X11 also set "Menu", "Scrollbar", and
7239  * "Tooltip" colors.
7240  */
7241     void
7242 set_normal_colors()
7243 {
7244     if (set_group_colors((char_u *)"Normal",
7245 			     &gui.norm_pixel, &gui.back_pixel,
7246 			     FALSE, TRUE, FALSE))
7247     {
7248 	gui_mch_new_colors();
7249 	must_redraw = CLEAR;
7250     }
7251 #ifdef FEAT_GUI_X11
7252     if (set_group_colors((char_u *)"Menu",
7253 			 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7254 			 TRUE, FALSE, FALSE))
7255     {
7256 # ifdef FEAT_MENU
7257 	gui_mch_new_menu_colors();
7258 # endif
7259 	must_redraw = CLEAR;
7260     }
7261 # ifdef FEAT_BEVAL
7262     if (set_group_colors((char_u *)"Tooltip",
7263 			 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7264 			 FALSE, FALSE, TRUE))
7265     {
7266 # ifdef FEAT_TOOLBAR
7267 	gui_mch_new_tooltip_colors();
7268 # endif
7269 	must_redraw = CLEAR;
7270     }
7271 #endif
7272     if (set_group_colors((char_u *)"Scrollbar",
7273 		    &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7274 		    FALSE, FALSE, FALSE))
7275     {
7276 	gui_new_scrollbar_colors();
7277 	must_redraw = CLEAR;
7278     }
7279 #endif
7280 }
7281 
7282 /*
7283  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7284  */
7285     static int
7286 set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7287     char_u	*name;
7288     guicolor_T	*fgp;
7289     guicolor_T	*bgp;
7290     int		do_menu;
7291     int		use_norm;
7292     int		do_tooltip;
7293 {
7294     int		idx;
7295 
7296     idx = syn_name2id(name) - 1;
7297     if (idx >= 0)
7298     {
7299 	gui_do_one_color(idx, do_menu, do_tooltip);
7300 
7301 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7302 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
7303 	else if (use_norm)
7304 	    *fgp = gui.def_norm_pixel;
7305 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7306 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
7307 	else if (use_norm)
7308 	    *bgp = gui.def_back_pixel;
7309 	return TRUE;
7310     }
7311     return FALSE;
7312 }
7313 
7314 /*
7315  * Get the font of the "Normal" group.
7316  * Returns "" when it's not found or not set.
7317  */
7318     char_u *
7319 hl_get_font_name()
7320 {
7321     int		id;
7322     char_u	*s;
7323 
7324     id = syn_name2id((char_u *)"Normal");
7325     if (id > 0)
7326     {
7327 	s = HL_TABLE()[id - 1].sg_font_name;
7328 	if (s != NULL)
7329 	    return s;
7330     }
7331     return (char_u *)"";
7332 }
7333 
7334 /*
7335  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
7336  * actually chosen to be used.
7337  */
7338     void
7339 hl_set_font_name(font_name)
7340     char_u	*font_name;
7341 {
7342     int	    id;
7343 
7344     id = syn_name2id((char_u *)"Normal");
7345     if (id > 0)
7346     {
7347 	vim_free(HL_TABLE()[id - 1].sg_font_name);
7348 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7349     }
7350 }
7351 
7352 /*
7353  * Set background color for "Normal" group.  Called by gui_set_bg_color()
7354  * when the color is known.
7355  */
7356     void
7357 hl_set_bg_color_name(name)
7358     char_u  *name;	    /* must have been allocated */
7359 {
7360     int	    id;
7361 
7362     if (name != NULL)
7363     {
7364 	id = syn_name2id((char_u *)"Normal");
7365 	if (id > 0)
7366 	{
7367 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7368 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
7369 	}
7370     }
7371 }
7372 
7373 /*
7374  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
7375  * when the color is known.
7376  */
7377     void
7378 hl_set_fg_color_name(name)
7379     char_u  *name;	    /* must have been allocated */
7380 {
7381     int	    id;
7382 
7383     if (name != NULL)
7384     {
7385 	id = syn_name2id((char_u *)"Normal");
7386 	if (id > 0)
7387 	{
7388 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7389 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
7390 	}
7391     }
7392 }
7393 
7394 /*
7395  * Return the handle for a color name.
7396  * Returns INVALCOLOR when failed.
7397  */
7398     static guicolor_T
7399 color_name2handle(name)
7400     char_u  *name;
7401 {
7402     if (STRCMP(name, "NONE") == 0)
7403 	return INVALCOLOR;
7404 
7405     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7406 	return gui.norm_pixel;
7407     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7408 	return gui.back_pixel;
7409 
7410     return gui_get_color(name);
7411 }
7412 
7413 /*
7414  * Return the handle for a font name.
7415  * Returns NOFONT when failed.
7416  */
7417     static GuiFont
7418 font_name2handle(name)
7419     char_u  *name;
7420 {
7421     if (STRCMP(name, "NONE") == 0)
7422 	return NOFONT;
7423 
7424     return gui_mch_get_font(name, TRUE);
7425 }
7426 
7427 # ifdef FEAT_XFONTSET
7428 /*
7429  * Return the handle for a fontset name.
7430  * Returns NOFONTSET when failed.
7431  */
7432     static GuiFontset
7433 fontset_name2handle(name, fixed_width)
7434     char_u	*name;
7435     int		fixed_width;
7436 {
7437     if (STRCMP(name, "NONE") == 0)
7438 	return NOFONTSET;
7439 
7440     return gui_mch_get_fontset(name, TRUE, fixed_width);
7441 }
7442 # endif
7443 
7444 /*
7445  * Get the font or fontset for one highlight group.
7446  */
7447 /*ARGSUSED*/
7448     static void
7449 hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7450     int		idx;
7451     char_u	*arg;
7452     int		do_normal;	/* set normal font */
7453     int		do_menu;	/* set menu font */
7454     int		do_tooltip;	/* set tooltip font */
7455 {
7456 # ifdef FEAT_XFONTSET
7457     /* If 'guifontset' is not empty, first try using the name as a
7458      * fontset.  If that doesn't work, use it as a font name. */
7459     if (*p_guifontset != NUL
7460 #  ifdef FONTSET_ALWAYS
7461 	|| do_menu
7462 #  endif
7463 #  ifdef FEAT_BEVAL_TIP
7464 	/* In Athena & Motif, the Tooltip highlight group is always a fontset */
7465 	|| do_tooltip
7466 #  endif
7467 	    )
7468 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7469 #  ifdef FONTSET_ALWAYS
7470 		|| do_menu
7471 #  endif
7472 #  ifdef FEAT_BEVAL_TIP
7473 		|| do_tooltip
7474 #  endif
7475 		);
7476     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7477     {
7478 	/* If it worked and it's the Normal group, use it as the
7479 	 * normal fontset.  Same for the Menu group. */
7480 	if (do_normal)
7481 	    gui_init_font(arg, TRUE);
7482 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7483 	if (do_menu)
7484 	{
7485 #    ifdef FONTSET_ALWAYS
7486 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7487 #    else
7488 	    /* YIKES!  This is a bug waiting to crash the program */
7489 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
7490 #    endif
7491 	    gui_mch_new_menu_font();
7492 	}
7493 #    ifdef FEAT_BEVAL
7494 	if (do_tooltip)
7495 	{
7496 	    /* The Athena widget set cannot currently handle switching between
7497 	     * displaying a single font and a fontset.
7498 	     * If the XtNinternational resource is set to True at widget
7499 	     * creation, then a fontset is always used, othwise an
7500 	     * XFontStruct is used.
7501 	     */
7502 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7503 	    gui_mch_new_tooltip_font();
7504 	}
7505 #    endif
7506 #   endif
7507     }
7508     else
7509 # endif
7510     {
7511 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
7512 	/* If it worked and it's the Normal group, use it as the
7513 	 * normal font.  Same for the Menu group. */
7514 	if (HL_TABLE()[idx].sg_font != NOFONT)
7515 	{
7516 	    if (do_normal)
7517 		gui_init_font(arg, FALSE);
7518 #ifndef FONTSET_ALWAYS
7519 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7520 	    if (do_menu)
7521 	    {
7522 		gui.menu_font = HL_TABLE()[idx].sg_font;
7523 		gui_mch_new_menu_font();
7524 	    }
7525 # endif
7526 #endif
7527 	}
7528     }
7529 }
7530 
7531 #endif /* FEAT_GUI */
7532 
7533 /*
7534  * Table with the specifications for an attribute number.
7535  * Note that this table is used by ALL buffers.  This is required because the
7536  * GUI can redraw at any time for any buffer.
7537  */
7538 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
7539 
7540 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7541 
7542 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
7543 
7544 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7545 
7546 #ifdef FEAT_GUI
7547 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
7548 
7549 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7550 #endif
7551 
7552 /*
7553  * Return the attr number for a set of colors and font.
7554  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7555  * if the combination is new.
7556  * Return 0 for error (no more room).
7557  */
7558     static int
7559 get_attr_entry(table, aep)
7560     garray_T	*table;
7561     attrentry_T	*aep;
7562 {
7563     int		i;
7564     attrentry_T	*taep;
7565     static int	recursive = FALSE;
7566 
7567     /*
7568      * Init the table, in case it wasn't done yet.
7569      */
7570     table->ga_itemsize = sizeof(attrentry_T);
7571     table->ga_growsize = 7;
7572 
7573     /*
7574      * Try to find an entry with the same specifications.
7575      */
7576     for (i = 0; i < table->ga_len; ++i)
7577     {
7578 	taep = &(((attrentry_T *)table->ga_data)[i]);
7579 	if (	   aep->ae_attr == taep->ae_attr
7580 		&& (
7581 #ifdef FEAT_GUI
7582 		       (table == &gui_attr_table
7583 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7584 			    && aep->ae_u.gui.bg_color
7585 						    == taep->ae_u.gui.bg_color
7586 			    && aep->ae_u.gui.sp_color
7587 						    == taep->ae_u.gui.sp_color
7588 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
7589 #  ifdef FEAT_XFONTSET
7590 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
7591 #  endif
7592 			    ))
7593 		    ||
7594 #endif
7595 		       (table == &term_attr_table
7596 			&& (aep->ae_u.term.start == NULL)
7597 					    == (taep->ae_u.term.start == NULL)
7598 			&& (aep->ae_u.term.start == NULL
7599 			    || STRCMP(aep->ae_u.term.start,
7600 						  taep->ae_u.term.start) == 0)
7601 			&& (aep->ae_u.term.stop == NULL)
7602 					     == (taep->ae_u.term.stop == NULL)
7603 			&& (aep->ae_u.term.stop == NULL
7604 			    || STRCMP(aep->ae_u.term.stop,
7605 						  taep->ae_u.term.stop) == 0))
7606 		    || (table == &cterm_attr_table
7607 			    && aep->ae_u.cterm.fg_color
7608 						  == taep->ae_u.cterm.fg_color
7609 			    && aep->ae_u.cterm.bg_color
7610 						 == taep->ae_u.cterm.bg_color)
7611 		     ))
7612 
7613 	return i + ATTR_OFF;
7614     }
7615 
7616     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
7617     {
7618 	/*
7619 	 * Running out of attribute entries!  remove all attributes, and
7620 	 * compute new ones for all groups.
7621 	 * When called recursively, we are really out of numbers.
7622 	 */
7623 	if (recursive)
7624 	{
7625 	    EMSG(_("E424: Too many different highlighting attributes in use"));
7626 	    return 0;
7627 	}
7628 	recursive = TRUE;
7629 
7630 	clear_hl_tables();
7631 
7632 	must_redraw = CLEAR;
7633 
7634 	for (i = 0; i < highlight_ga.ga_len; ++i)
7635 	    set_hl_attr(i);
7636 
7637 	recursive = FALSE;
7638     }
7639 
7640     /*
7641      * This is a new combination of colors and font, add an entry.
7642      */
7643     if (ga_grow(table, 1) == FAIL)
7644 	return 0;
7645 
7646     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7647     vim_memset(taep, 0, sizeof(attrentry_T));
7648     taep->ae_attr = aep->ae_attr;
7649 #ifdef FEAT_GUI
7650     if (table == &gui_attr_table)
7651     {
7652 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7653 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7654 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7655 	taep->ae_u.gui.font = aep->ae_u.gui.font;
7656 # ifdef FEAT_XFONTSET
7657 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7658 # endif
7659     }
7660 #endif
7661     if (table == &term_attr_table)
7662     {
7663 	if (aep->ae_u.term.start == NULL)
7664 	    taep->ae_u.term.start = NULL;
7665 	else
7666 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7667 	if (aep->ae_u.term.stop == NULL)
7668 	    taep->ae_u.term.stop = NULL;
7669 	else
7670 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7671     }
7672     else if (table == &cterm_attr_table)
7673     {
7674 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7675 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7676     }
7677     ++table->ga_len;
7678     return (table->ga_len - 1 + ATTR_OFF);
7679 }
7680 
7681 /*
7682  * Clear all highlight tables.
7683  */
7684     void
7685 clear_hl_tables()
7686 {
7687     int		i;
7688     attrentry_T	*taep;
7689 
7690 #ifdef FEAT_GUI
7691     ga_clear(&gui_attr_table);
7692 #endif
7693     for (i = 0; i < term_attr_table.ga_len; ++i)
7694     {
7695 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7696 	vim_free(taep->ae_u.term.start);
7697 	vim_free(taep->ae_u.term.stop);
7698     }
7699     ga_clear(&term_attr_table);
7700     ga_clear(&cterm_attr_table);
7701 }
7702 
7703 #if defined(FEAT_SYN_HL) || defined(PROTO)
7704 /*
7705  * Combine special attributes (e.g., for spelling) with other attributes
7706  * (e.g., for syntax highlighting).
7707  * "prim_attr" overrules "char_attr".
7708  * This creates a new group when required.
7709  * Since we expect there to be few spelling mistakes we don't cache the
7710  * result.
7711  * Return the resulting attributes.
7712  */
7713     int
7714 hl_combine_attr(char_attr, prim_attr)
7715     int	    char_attr;
7716     int	    prim_attr;
7717 {
7718     attrentry_T *char_aep = NULL;
7719     attrentry_T *spell_aep;
7720     attrentry_T new_en;
7721 
7722     if (char_attr == 0)
7723 	return prim_attr;
7724     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7725 	return char_attr | prim_attr;
7726 #ifdef FEAT_GUI
7727     if (gui.in_use)
7728     {
7729 	if (char_attr > HL_ALL)
7730 	    char_aep = syn_gui_attr2entry(char_attr);
7731 	if (char_aep != NULL)
7732 	    new_en = *char_aep;
7733 	else
7734 	{
7735 	    vim_memset(&new_en, 0, sizeof(new_en));
7736 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
7737 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
7738 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
7739 	    if (char_attr <= HL_ALL)
7740 		new_en.ae_attr = char_attr;
7741 	}
7742 
7743 	if (prim_attr <= HL_ALL)
7744 	    new_en.ae_attr |= prim_attr;
7745 	else
7746 	{
7747 	    spell_aep = syn_gui_attr2entry(prim_attr);
7748 	    if (spell_aep != NULL)
7749 	    {
7750 		new_en.ae_attr |= spell_aep->ae_attr;
7751 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7752 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7753 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7754 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7755 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7756 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7757 		if (spell_aep->ae_u.gui.font != NOFONT)
7758 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7759 # ifdef FEAT_XFONTSET
7760 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7761 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7762 # endif
7763 	    }
7764 	}
7765 	return get_attr_entry(&gui_attr_table, &new_en);
7766     }
7767 #endif
7768 
7769     if (t_colors > 1)
7770     {
7771 	if (char_attr > HL_ALL)
7772 	    char_aep = syn_cterm_attr2entry(char_attr);
7773 	if (char_aep != NULL)
7774 	    new_en = *char_aep;
7775 	else
7776 	{
7777 	    vim_memset(&new_en, 0, sizeof(new_en));
7778 	    if (char_attr <= HL_ALL)
7779 		new_en.ae_attr = char_attr;
7780 	}
7781 
7782 	if (prim_attr <= HL_ALL)
7783 	    new_en.ae_attr |= prim_attr;
7784 	else
7785 	{
7786 	    spell_aep = syn_cterm_attr2entry(prim_attr);
7787 	    if (spell_aep != NULL)
7788 	    {
7789 		new_en.ae_attr |= spell_aep->ae_attr;
7790 		if (spell_aep->ae_u.cterm.fg_color > 0)
7791 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7792 		if (spell_aep->ae_u.cterm.bg_color > 0)
7793 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7794 	    }
7795 	}
7796 	return get_attr_entry(&cterm_attr_table, &new_en);
7797     }
7798 
7799     if (char_attr > HL_ALL)
7800 	char_aep = syn_term_attr2entry(char_attr);
7801     if (char_aep != NULL)
7802 	new_en = *char_aep;
7803     else
7804     {
7805 	vim_memset(&new_en, 0, sizeof(new_en));
7806 	if (char_attr <= HL_ALL)
7807 	    new_en.ae_attr = char_attr;
7808     }
7809 
7810     if (prim_attr <= HL_ALL)
7811 	new_en.ae_attr |= prim_attr;
7812     else
7813     {
7814 	spell_aep = syn_term_attr2entry(prim_attr);
7815 	if (spell_aep != NULL)
7816 	{
7817 	    new_en.ae_attr |= spell_aep->ae_attr;
7818 	    if (spell_aep->ae_u.term.start != NULL)
7819 	    {
7820 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
7821 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
7822 	    }
7823 	}
7824     }
7825     return get_attr_entry(&term_attr_table, &new_en);
7826 }
7827 #endif
7828 
7829 #ifdef FEAT_GUI
7830 
7831     attrentry_T *
7832 syn_gui_attr2entry(attr)
7833     int		    attr;
7834 {
7835     attr -= ATTR_OFF;
7836     if (attr >= gui_attr_table.ga_len)	    /* did ":syntax clear" */
7837 	return NULL;
7838     return &(GUI_ATTR_ENTRY(attr));
7839 }
7840 
7841 #endif /* FEAT_GUI */
7842 
7843     attrentry_T *
7844 syn_term_attr2entry(attr)
7845     int		    attr;
7846 {
7847     attr -= ATTR_OFF;
7848     if (attr >= term_attr_table.ga_len)	    /* did ":syntax clear" */
7849 	return NULL;
7850     return &(TERM_ATTR_ENTRY(attr));
7851 }
7852 
7853     attrentry_T *
7854 syn_cterm_attr2entry(attr)
7855     int		    attr;
7856 {
7857     attr -= ATTR_OFF;
7858     if (attr >= cterm_attr_table.ga_len)	/* did ":syntax clear" */
7859 	return NULL;
7860     return &(CTERM_ATTR_ENTRY(attr));
7861 }
7862 
7863 #define LIST_ATTR   1
7864 #define LIST_STRING 2
7865 #define LIST_INT    3
7866 
7867     static void
7868 highlight_list_one(id)
7869     int		id;
7870 {
7871     struct hl_group	*sgp;
7872     int			didh = FALSE;
7873 
7874     sgp = &HL_TABLE()[id - 1];	    /* index is ID minus one */
7875 
7876     didh = highlight_list_arg(id, didh, LIST_ATTR,
7877 				    sgp->sg_term, NULL, "term");
7878     didh = highlight_list_arg(id, didh, LIST_STRING,
7879 				    0, sgp->sg_start, "start");
7880     didh = highlight_list_arg(id, didh, LIST_STRING,
7881 				    0, sgp->sg_stop, "stop");
7882 
7883     didh = highlight_list_arg(id, didh, LIST_ATTR,
7884 				    sgp->sg_cterm, NULL, "cterm");
7885     didh = highlight_list_arg(id, didh, LIST_INT,
7886 				    sgp->sg_cterm_fg, NULL, "ctermfg");
7887     didh = highlight_list_arg(id, didh, LIST_INT,
7888 				    sgp->sg_cterm_bg, NULL, "ctermbg");
7889 
7890 #ifdef FEAT_GUI
7891     didh = highlight_list_arg(id, didh, LIST_ATTR,
7892 				    sgp->sg_gui, NULL, "gui");
7893     didh = highlight_list_arg(id, didh, LIST_STRING,
7894 				    0, sgp->sg_gui_fg_name, "guifg");
7895     didh = highlight_list_arg(id, didh, LIST_STRING,
7896 				    0, sgp->sg_gui_bg_name, "guibg");
7897     didh = highlight_list_arg(id, didh, LIST_STRING,
7898 				    0, sgp->sg_gui_sp_name, "guisp");
7899     didh = highlight_list_arg(id, didh, LIST_STRING,
7900 				    0, sgp->sg_font_name, "font");
7901 #endif
7902 
7903     if (sgp->sg_link && !got_int)
7904     {
7905 	(void)syn_list_header(didh, 9999, id);
7906 	didh = TRUE;
7907 	msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7908 	msg_putchar(' ');
7909 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7910     }
7911 
7912 #ifdef FEAT_EVAL
7913     if (didh && p_verbose > 0)
7914 	last_set_msg(sgp->sg_scriptID);
7915 #endif
7916 }
7917 
7918     static int
7919 highlight_list_arg(id, didh, type, iarg, sarg, name)
7920     int		id;
7921     int		didh;
7922     int		type;
7923     int		iarg;
7924     char_u	*sarg;
7925     char	*name;
7926 {
7927     char_u	buf[100];
7928     char_u	*ts;
7929     int		i;
7930 
7931     if (got_int)
7932 	return FALSE;
7933     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7934     {
7935 	ts = buf;
7936 	if (type == LIST_INT)
7937 	    sprintf((char *)buf, "%d", iarg - 1);
7938 	else if (type == LIST_STRING)
7939 	    ts = sarg;
7940 	else /* type == LIST_ATTR */
7941 	{
7942 	    buf[0] = NUL;
7943 	    for (i = 0; hl_attr_table[i] != 0; ++i)
7944 	    {
7945 		if (iarg & hl_attr_table[i])
7946 		{
7947 		    if (buf[0] != NUL)
7948 			STRCAT(buf, ",");
7949 		    STRCAT(buf, hl_name_table[i]);
7950 		    iarg &= ~hl_attr_table[i];	    /* don't want "inverse" */
7951 		}
7952 	    }
7953 	}
7954 
7955 	(void)syn_list_header(didh,
7956 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7957 	didh = TRUE;
7958 	if (!got_int)
7959 	{
7960 	    MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7961 	    MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7962 	    msg_outtrans(ts);
7963 	}
7964     }
7965     return didh;
7966 }
7967 
7968 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7969 /*
7970  * Return "1" if highlight group "id" has attribute "flag".
7971  * Return NULL otherwise.
7972  */
7973     char_u *
7974 highlight_has_attr(id, flag, modec)
7975     int		id;
7976     int		flag;
7977     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
7978 {
7979     int		attr;
7980 
7981     if (id <= 0 || id > highlight_ga.ga_len)
7982 	return NULL;
7983 
7984 #ifdef FEAT_GUI
7985     if (modec == 'g')
7986 	attr = HL_TABLE()[id - 1].sg_gui;
7987     else
7988 #endif
7989 	 if (modec == 'c')
7990 	attr = HL_TABLE()[id - 1].sg_cterm;
7991     else
7992 	attr = HL_TABLE()[id - 1].sg_term;
7993 
7994     if (attr & flag)
7995 	return (char_u *)"1";
7996     return NULL;
7997 }
7998 #endif
7999 
8000 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8001 /*
8002  * Return color name of highlight group "id".
8003  */
8004     char_u *
8005 highlight_color(id, what, modec)
8006     int		id;
8007     char_u	*what;	/* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
8008     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
8009 {
8010     static char_u	name[20];
8011     int			n;
8012     int			fg = FALSE;
8013 # ifdef FEAT_GUI
8014     int			sp = FALSE;
8015 # endif
8016 
8017     if (id <= 0 || id > highlight_ga.ga_len)
8018 	return NULL;
8019 
8020     if (TOLOWER_ASC(what[0]) == 'f')
8021 	fg = TRUE;
8022 # ifdef FEAT_GUI
8023     else if (TOLOWER_ASC(what[0]) == 's')
8024 	sp = TRUE;
8025     if (modec == 'g')
8026     {
8027 	/* return #RRGGBB form (only possible when GUI is running) */
8028 	if (gui.in_use && what[1] && what[2] == '#')
8029 	{
8030 	    guicolor_T		color;
8031 	    long_u		rgb;
8032 	    static char_u	buf[10];
8033 
8034 	    if (fg)
8035 		color = HL_TABLE()[id - 1].sg_gui_fg;
8036 	    else if (sp)
8037 		color = HL_TABLE()[id - 1].sg_gui_sp;
8038 	    else
8039 		color = HL_TABLE()[id - 1].sg_gui_bg;
8040 	    if (color == INVALCOLOR)
8041 		return NULL;
8042 	    rgb = gui_mch_get_rgb(color);
8043 	    sprintf((char *)buf, "#%02x%02x%02x",
8044 				      (unsigned)(rgb >> 16),
8045 				      (unsigned)(rgb >> 8) & 255,
8046 				      (unsigned)rgb & 255);
8047 	    return buf;
8048 	}
8049 	if (fg)
8050 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
8051 	if (sp)
8052 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
8053 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
8054     }
8055 # endif
8056     if (modec == 'c')
8057     {
8058 	if (fg)
8059 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8060 	else
8061 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8062 	sprintf((char *)name, "%d", n);
8063 	return name;
8064     }
8065     /* term doesn't have color */
8066     return NULL;
8067 }
8068 #endif
8069 
8070 #if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8071 	|| defined(PROTO)
8072 /*
8073  * Return color name of highlight group "id" as RGB value.
8074  */
8075     long_u
8076 highlight_gui_color_rgb(id, fg)
8077     int		id;
8078     int		fg;	/* TRUE = fg, FALSE = bg */
8079 {
8080     guicolor_T	color;
8081 
8082     if (id <= 0 || id > highlight_ga.ga_len)
8083 	return 0L;
8084 
8085     if (fg)
8086 	color = HL_TABLE()[id - 1].sg_gui_fg;
8087     else
8088 	color = HL_TABLE()[id - 1].sg_gui_bg;
8089 
8090     if (color == INVALCOLOR)
8091 	return 0L;
8092 
8093     return gui_mch_get_rgb(color);
8094 }
8095 #endif
8096 
8097 /*
8098  * Output the syntax list header.
8099  * Return TRUE when started a new line.
8100  */
8101     static int
8102 syn_list_header(did_header, outlen, id)
8103     int	    did_header;		/* did header already */
8104     int	    outlen;		/* length of string that comes */
8105     int	    id;			/* highlight group id */
8106 {
8107     int	    endcol = 19;
8108     int	    newline = TRUE;
8109 
8110     if (!did_header)
8111     {
8112 	msg_putchar('\n');
8113 	if (got_int)
8114 	    return TRUE;
8115 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
8116 	endcol = 15;
8117     }
8118     else if (msg_col + outlen + 1 >= Columns)
8119     {
8120 	msg_putchar('\n');
8121 	if (got_int)
8122 	    return TRUE;
8123     }
8124     else
8125     {
8126 	if (msg_col >= endcol)	/* wrap around is like starting a new line */
8127 	    newline = FALSE;
8128     }
8129 
8130     if (msg_col >= endcol)	/* output at least one space */
8131 	endcol = msg_col + 1;
8132     if (Columns <= endcol)	/* avoid hang for tiny window */
8133 	endcol = Columns - 1;
8134 
8135     msg_advance(endcol);
8136 
8137     /* Show "xxx" with the attributes. */
8138     if (!did_header)
8139     {
8140 	msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8141 	msg_putchar(' ');
8142     }
8143 
8144     return newline;
8145 }
8146 
8147 /*
8148  * Set the attribute numbers for a highlight group.
8149  * Called after one of the attributes has changed.
8150  */
8151     static void
8152 set_hl_attr(idx)
8153     int		idx;	    /* index in array */
8154 {
8155     attrentry_T		at_en;
8156     struct hl_group	*sgp = HL_TABLE() + idx;
8157 
8158     /* The "Normal" group doesn't need an attribute number */
8159     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8160 	return;
8161 
8162 #ifdef FEAT_GUI
8163     /*
8164      * For the GUI mode: If there are other than "normal" highlighting
8165      * attributes, need to allocate an attr number.
8166      */
8167     if (sgp->sg_gui_fg == INVALCOLOR
8168 	    && sgp->sg_gui_bg == INVALCOLOR
8169 	    && sgp->sg_gui_sp == INVALCOLOR
8170 	    && sgp->sg_font == NOFONT
8171 # ifdef FEAT_XFONTSET
8172 	    && sgp->sg_fontset == NOFONTSET
8173 # endif
8174 	    )
8175     {
8176 	sgp->sg_gui_attr = sgp->sg_gui;
8177     }
8178     else
8179     {
8180 	at_en.ae_attr = sgp->sg_gui;
8181 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8182 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
8183 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
8184 	at_en.ae_u.gui.font = sgp->sg_font;
8185 # ifdef FEAT_XFONTSET
8186 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
8187 # endif
8188 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8189     }
8190 #endif
8191     /*
8192      * For the term mode: If there are other than "normal" highlighting
8193      * attributes, need to allocate an attr number.
8194      */
8195     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8196 	sgp->sg_term_attr = sgp->sg_term;
8197     else
8198     {
8199 	at_en.ae_attr = sgp->sg_term;
8200 	at_en.ae_u.term.start = sgp->sg_start;
8201 	at_en.ae_u.term.stop = sgp->sg_stop;
8202 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8203     }
8204 
8205     /*
8206      * For the color term mode: If there are other than "normal"
8207      * highlighting attributes, need to allocate an attr number.
8208      */
8209     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8210 	sgp->sg_cterm_attr = sgp->sg_cterm;
8211     else
8212     {
8213 	at_en.ae_attr = sgp->sg_cterm;
8214 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8215 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8216 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8217     }
8218 }
8219 
8220 /*
8221  * Lookup a highlight group name and return it's ID.
8222  * If it is not found, 0 is returned.
8223  */
8224     int
8225 syn_name2id(name)
8226     char_u	*name;
8227 {
8228     int		i;
8229     char_u	name_u[200];
8230 
8231     /* Avoid using stricmp() too much, it's slow on some systems */
8232     /* Avoid alloc()/free(), these are slow too.  ID names over 200 chars
8233      * don't deserve to be found! */
8234     vim_strncpy(name_u, name, 199);
8235     vim_strup(name_u);
8236     for (i = highlight_ga.ga_len; --i >= 0; )
8237 	if (HL_TABLE()[i].sg_name_u != NULL
8238 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8239 	    break;
8240     return i + 1;
8241 }
8242 
8243 #if defined(FEAT_EVAL) || defined(PROTO)
8244 /*
8245  * Return TRUE if highlight group "name" exists.
8246  */
8247     int
8248 highlight_exists(name)
8249     char_u	*name;
8250 {
8251     return (syn_name2id(name) > 0);
8252 }
8253 #endif
8254 
8255 /*
8256  * Like syn_name2id(), but take a pointer + length argument.
8257  */
8258     int
8259 syn_namen2id(linep, len)
8260     char_u  *linep;
8261     int	    len;
8262 {
8263     char_u  *name;
8264     int	    id = 0;
8265 
8266     name = vim_strnsave(linep, len);
8267     if (name != NULL)
8268     {
8269 	id = syn_name2id(name);
8270 	vim_free(name);
8271     }
8272     return id;
8273 }
8274 
8275 /*
8276  * Find highlight group name in the table and return it's ID.
8277  * The argument is a pointer to the name and the length of the name.
8278  * If it doesn't exist yet, a new entry is created.
8279  * Return 0 for failure.
8280  */
8281     int
8282 syn_check_group(pp, len)
8283     char_u		*pp;
8284     int			len;
8285 {
8286     int	    id;
8287     char_u  *name;
8288 
8289     name = vim_strnsave(pp, len);
8290     if (name == NULL)
8291 	return 0;
8292 
8293     id = syn_name2id(name);
8294     if (id == 0)			/* doesn't exist yet */
8295 	id = syn_add_group(name);
8296     else
8297 	vim_free(name);
8298     return id;
8299 }
8300 
8301 /*
8302  * Add new highlight group and return it's ID.
8303  * "name" must be an allocated string, it will be consumed.
8304  * Return 0 for failure.
8305  */
8306     static int
8307 syn_add_group(name)
8308     char_u	*name;
8309 {
8310     char_u	*p;
8311 
8312     /* Check that the name is ASCII letters, digits and underscore. */
8313     for (p = name; *p != NUL; ++p)
8314     {
8315 	if (!vim_isprintc(*p))
8316 	{
8317 	    EMSG(_("E669: Unprintable character in group name"));
8318 	    return 0;
8319 	}
8320 	else if (!ASCII_ISALNUM(*p) && *p != '_')
8321 	{
8322 	    /* This is an error, but since there previously was no check only
8323 	     * give a warning. */
8324 	    msg_source(hl_attr(HLF_W));
8325 	    MSG(_("W18: Invalid character in group name"));
8326 	    break;
8327 	}
8328     }
8329 
8330     /*
8331      * First call for this growarray: init growing array.
8332      */
8333     if (highlight_ga.ga_data == NULL)
8334     {
8335 	highlight_ga.ga_itemsize = sizeof(struct hl_group);
8336 	highlight_ga.ga_growsize = 10;
8337     }
8338 
8339     /*
8340      * Make room for at least one other syntax_highlight entry.
8341      */
8342     if (ga_grow(&highlight_ga, 1) == FAIL)
8343     {
8344 	vim_free(name);
8345 	return 0;
8346     }
8347 
8348     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8349     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8350     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8351 #ifdef FEAT_GUI
8352     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8353     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
8354     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
8355 #endif
8356     ++highlight_ga.ga_len;
8357 
8358     return highlight_ga.ga_len;		    /* ID is index plus one */
8359 }
8360 
8361 /*
8362  * When, just after calling syn_add_group(), an error is discovered, this
8363  * function deletes the new name.
8364  */
8365     static void
8366 syn_unadd_group()
8367 {
8368     --highlight_ga.ga_len;
8369     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8370     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8371 }
8372 
8373 /*
8374  * Translate a group ID to highlight attributes.
8375  */
8376     int
8377 syn_id2attr(hl_id)
8378     int			hl_id;
8379 {
8380     int			attr;
8381     struct hl_group	*sgp;
8382 
8383     hl_id = syn_get_final_id(hl_id);
8384     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8385 
8386 #ifdef FEAT_GUI
8387     /*
8388      * Only use GUI attr when the GUI is being used.
8389      */
8390     if (gui.in_use)
8391 	attr = sgp->sg_gui_attr;
8392     else
8393 #endif
8394 	if (t_colors > 1)
8395 	    attr = sgp->sg_cterm_attr;
8396 	else
8397 	    attr = sgp->sg_term_attr;
8398 
8399     return attr;
8400 }
8401 
8402 #ifdef FEAT_GUI
8403 /*
8404  * Get the GUI colors and attributes for a group ID.
8405  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8406  */
8407     int
8408 syn_id2colors(hl_id, fgp, bgp)
8409     int		hl_id;
8410     guicolor_T	*fgp;
8411     guicolor_T	*bgp;
8412 {
8413     struct hl_group	*sgp;
8414 
8415     hl_id = syn_get_final_id(hl_id);
8416     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8417 
8418     *fgp = sgp->sg_gui_fg;
8419     *bgp = sgp->sg_gui_bg;
8420     return sgp->sg_gui;
8421 }
8422 #endif
8423 
8424 /*
8425  * Translate a group ID to the final group ID (following links).
8426  */
8427     int
8428 syn_get_final_id(hl_id)
8429     int			hl_id;
8430 {
8431     int			count;
8432     struct hl_group	*sgp;
8433 
8434     if (hl_id > highlight_ga.ga_len || hl_id < 1)
8435 	return 0;			/* Can be called from eval!! */
8436 
8437     /*
8438      * Follow links until there is no more.
8439      * Look out for loops!  Break after 100 links.
8440      */
8441     for (count = 100; --count >= 0; )
8442     {
8443 	sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8444 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8445 	    break;
8446 	hl_id = sgp->sg_link;
8447     }
8448 
8449     return hl_id;
8450 }
8451 
8452 #ifdef FEAT_GUI
8453 /*
8454  * Call this function just after the GUI has started.
8455  * It finds the font and color handles for the highlighting groups.
8456  */
8457     void
8458 highlight_gui_started()
8459 {
8460     int	    idx;
8461 
8462     /* First get the colors from the "Normal" and "Menu" group, if set */
8463     set_normal_colors();
8464 
8465     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8466 	gui_do_one_color(idx, FALSE, FALSE);
8467 
8468     highlight_changed();
8469 }
8470 
8471     static void
8472 gui_do_one_color(idx, do_menu, do_tooltip)
8473     int		idx;
8474     int		do_menu;	/* TRUE: might set the menu font */
8475     int		do_tooltip;	/* TRUE: might set the tooltip font */
8476 {
8477     int		didit = FALSE;
8478 
8479     if (HL_TABLE()[idx].sg_font_name != NULL)
8480     {
8481 	hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8482 		   do_tooltip);
8483 	didit = TRUE;
8484     }
8485     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8486     {
8487 	HL_TABLE()[idx].sg_gui_fg =
8488 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8489 	didit = TRUE;
8490     }
8491     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8492     {
8493 	HL_TABLE()[idx].sg_gui_bg =
8494 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8495 	didit = TRUE;
8496     }
8497     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8498     {
8499 	HL_TABLE()[idx].sg_gui_sp =
8500 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8501 	didit = TRUE;
8502     }
8503     if (didit)	/* need to get a new attr number */
8504 	set_hl_attr(idx);
8505 }
8506 
8507 #endif
8508 
8509 /*
8510  * Translate the 'highlight' option into attributes in highlight_attr[] and
8511  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
8512  * corresponding highlights to use on top of HLF_SNC is computed.
8513  * Called only when the 'highlight' option has been changed and upon first
8514  * screen redraw after any :highlight command.
8515  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
8516  */
8517     int
8518 highlight_changed()
8519 {
8520     int		hlf;
8521     int		i;
8522     char_u	*p;
8523     int		attr;
8524     char_u	*end;
8525     int		id;
8526 #ifdef USER_HIGHLIGHT
8527     char_u      userhl[10];
8528 # ifdef FEAT_STL_OPT
8529     int		id_SNC = -1;
8530     int		id_S = -1;
8531     int		hlcnt;
8532 # endif
8533 #endif
8534     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
8535 
8536     need_highlight_changed = FALSE;
8537 
8538     /*
8539      * Clear all attributes.
8540      */
8541     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8542 	highlight_attr[hlf] = 0;
8543 
8544     /*
8545      * First set all attributes to their default value.
8546      * Then use the attributes from the 'highlight' option.
8547      */
8548     for (i = 0; i < 2; ++i)
8549     {
8550 	if (i)
8551 	    p = p_hl;
8552 	else
8553 	    p = get_highlight_default();
8554 	if (p == NULL)	    /* just in case */
8555 	    continue;
8556 
8557 	while (*p)
8558 	{
8559 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8560 		if (hl_flags[hlf] == *p)
8561 		    break;
8562 	    ++p;
8563 	    if (hlf == (int)HLF_COUNT || *p == NUL)
8564 		return FAIL;
8565 
8566 	    /*
8567 	     * Allow several hl_flags to be combined, like "bu" for
8568 	     * bold-underlined.
8569 	     */
8570 	    attr = 0;
8571 	    for ( ; *p && *p != ','; ++p)	    /* parse upto comma */
8572 	    {
8573 		if (vim_iswhite(*p))		    /* ignore white space */
8574 		    continue;
8575 
8576 		if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
8577 		    return FAIL;
8578 
8579 		switch (*p)
8580 		{
8581 		    case 'b':	attr |= HL_BOLD;
8582 				break;
8583 		    case 'i':	attr |= HL_ITALIC;
8584 				break;
8585 		    case '-':
8586 		    case 'n':			    /* no highlighting */
8587 				break;
8588 		    case 'r':	attr |= HL_INVERSE;
8589 				break;
8590 		    case 's':	attr |= HL_STANDOUT;
8591 				break;
8592 		    case 'u':	attr |= HL_UNDERLINE;
8593 				break;
8594 		    case 'c':	attr |= HL_UNDERCURL;
8595 				break;
8596 		    case ':':	++p;		    /* highlight group name */
8597 				if (attr || *p == NUL)	 /* no combinations */
8598 				    return FAIL;
8599 				end = vim_strchr(p, ',');
8600 				if (end == NULL)
8601 				    end = p + STRLEN(p);
8602 				id = syn_check_group(p, (int)(end - p));
8603 				if (id == 0)
8604 				    return FAIL;
8605 				attr = syn_id2attr(id);
8606 				p = end - 1;
8607 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8608 				if (hlf == (int)HLF_SNC)
8609 				    id_SNC = syn_get_final_id(id);
8610 				else if (hlf == (int)HLF_S)
8611 				    id_S = syn_get_final_id(id);
8612 #endif
8613 				break;
8614 		    default:	return FAIL;
8615 		}
8616 	    }
8617 	    highlight_attr[hlf] = attr;
8618 
8619 	    p = skip_to_option_part(p);	    /* skip comma and spaces */
8620 	}
8621     }
8622 
8623 #ifdef USER_HIGHLIGHT
8624     /* Setup the user highlights
8625      *
8626      * Temporarily  utilize 10 more hl entries.  Have to be in there
8627      * simultaneously in case of table overflows in get_attr_entry()
8628      */
8629 # ifdef FEAT_STL_OPT
8630     if (ga_grow(&highlight_ga, 10) == FAIL)
8631 	return FAIL;
8632     hlcnt = highlight_ga.ga_len;
8633     if (id_S == 0)
8634     {		    /* Make sure id_S is always valid to simplify code below */
8635 	memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8636 	HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8637 	id_S = hlcnt + 10;
8638     }
8639 # endif
8640     for (i = 0; i < 9; i++)
8641     {
8642 	sprintf((char *)userhl, "User%d", i + 1);
8643 	id = syn_name2id(userhl);
8644 	if (id == 0)
8645 	{
8646 	    highlight_user[i] = 0;
8647 # ifdef FEAT_STL_OPT
8648 	    highlight_stlnc[i] = 0;
8649 # endif
8650 	}
8651 	else
8652 	{
8653 # ifdef FEAT_STL_OPT
8654 	    struct hl_group *hlt = HL_TABLE();
8655 # endif
8656 
8657 	    highlight_user[i] = syn_id2attr(id);
8658 # ifdef FEAT_STL_OPT
8659 	    if (id_SNC == 0)
8660 	    {
8661 		memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8662 		hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8663 		hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8664 #  ifdef FEAT_GUI
8665 		hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8666 #  endif
8667 	    }
8668 	    else
8669 		mch_memmove(&hlt[hlcnt + i],
8670 			    &hlt[id_SNC - 1],
8671 			    sizeof(struct hl_group));
8672 	    hlt[hlcnt + i].sg_link = 0;
8673 
8674 	    /* Apply difference between UserX and HLF_S to HLF_SNC */
8675 	    hlt[hlcnt + i].sg_term ^=
8676 		hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8677 	    if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8678 		hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8679 	    if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8680 		hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8681 	    hlt[hlcnt + i].sg_cterm ^=
8682 		hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8683 	    if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8684 		hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8685 	    if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8686 		hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8687 #  ifdef FEAT_GUI
8688 	    hlt[hlcnt + i].sg_gui ^=
8689 		hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8690 	    if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8691 		hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8692 	    if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8693 		hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
8694 	    if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8695 		hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
8696 	    if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8697 		hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8698 #   ifdef FEAT_XFONTSET
8699 	    if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8700 		hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8701 #   endif
8702 #  endif
8703 	    highlight_ga.ga_len = hlcnt + i + 1;
8704 	    set_hl_attr(hlcnt + i);	/* At long last we can apply */
8705 	    highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8706 # endif
8707 	}
8708     }
8709 # ifdef FEAT_STL_OPT
8710     highlight_ga.ga_len = hlcnt;
8711 # endif
8712 
8713 #endif /* USER_HIGHLIGHT */
8714 
8715     return OK;
8716 }
8717 
8718 #ifdef FEAT_CMDL_COMPL
8719 
8720 static void highlight_list __ARGS((void));
8721 static void highlight_list_two __ARGS((int cnt, int attr));
8722 
8723 /*
8724  * Handle command line completion for :highlight command.
8725  */
8726     void
8727 set_context_in_highlight_cmd(xp, arg)
8728     expand_T	*xp;
8729     char_u	*arg;
8730 {
8731     char_u	*p;
8732 
8733     /* Default: expand group names */
8734     xp->xp_context = EXPAND_HIGHLIGHT;
8735     xp->xp_pattern = arg;
8736     include_link = TRUE;
8737     include_default = TRUE;
8738 
8739     /* (part of) subcommand already typed */
8740     if (*arg != NUL)
8741     {
8742 	p = skiptowhite(arg);
8743 	if (*p != NUL)			/* past "default" or group name */
8744 	{
8745 	    include_default = FALSE;
8746 	    if (STRNCMP("default", arg, p - arg) == 0)
8747 	    {
8748 		arg = skipwhite(p);
8749 		xp->xp_pattern = arg;
8750 		p = skiptowhite(arg);
8751 	    }
8752 	    if (*p != NUL)			/* past group name */
8753 	    {
8754 		include_link = FALSE;
8755 		if (arg[1] == 'i' && arg[0] == 'N')
8756 		    highlight_list();
8757 		if (STRNCMP("link", arg, p - arg) == 0
8758 			|| STRNCMP("clear", arg, p - arg) == 0)
8759 		{
8760 		    xp->xp_pattern = skipwhite(p);
8761 		    p = skiptowhite(xp->xp_pattern);
8762 		    if (*p != NUL)		/* past first group name */
8763 		    {
8764 			xp->xp_pattern = skipwhite(p);
8765 			p = skiptowhite(xp->xp_pattern);
8766 		    }
8767 		}
8768 		if (*p != NUL)			/* past group name(s) */
8769 		    xp->xp_context = EXPAND_NOTHING;
8770 	    }
8771 	}
8772     }
8773 }
8774 
8775 /*
8776  * List highlighting matches in a nice way.
8777  */
8778     static void
8779 highlight_list()
8780 {
8781     int		i;
8782 
8783     for (i = 10; --i >= 0; )
8784 	highlight_list_two(i, hl_attr(HLF_D));
8785     for (i = 40; --i >= 0; )
8786 	highlight_list_two(99, 0);
8787 }
8788 
8789     static void
8790 highlight_list_two(cnt, attr)
8791     int	    cnt;
8792     int	    attr;
8793 {
8794     msg_puts_attr((char_u *)("N \bI \b!  \b" + cnt / 11), attr);
8795     msg_clr_eos();
8796     out_flush();
8797     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8798 }
8799 
8800 #endif /* FEAT_CMDL_COMPL */
8801 
8802 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8803     || defined(FEAT_SIGNS) || defined(PROTO)
8804 /*
8805  * Function given to ExpandGeneric() to obtain the list of group names.
8806  * Also used for synIDattr() function.
8807  */
8808 /*ARGSUSED*/
8809     char_u *
8810 get_highlight_name(xp, idx)
8811     expand_T	*xp;
8812     int		idx;
8813 {
8814     if (idx == highlight_ga.ga_len
8815 #ifdef FEAT_CMDL_COMPL
8816 	    && include_link
8817 #endif
8818 	    )
8819 	return (char_u *)"link";
8820     if (idx == highlight_ga.ga_len + 1
8821 #ifdef FEAT_CMDL_COMPL
8822 	    && include_link
8823 #endif
8824 	    )
8825 	return (char_u *)"clear";
8826     if (idx == highlight_ga.ga_len + 2
8827 #ifdef FEAT_CMDL_COMPL
8828 	    && include_default
8829 #endif
8830 	    )
8831 	return (char_u *)"default";
8832     if (idx < 0 || idx >= highlight_ga.ga_len)
8833 	return NULL;
8834     return HL_TABLE()[idx].sg_name;
8835 }
8836 #endif
8837 
8838 #ifdef FEAT_GUI
8839 /*
8840  * Free all the highlight group fonts.
8841  * Used when quitting for systems which need it.
8842  */
8843     void
8844 free_highlight_fonts()
8845 {
8846     int	    idx;
8847 
8848     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8849     {
8850 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
8851 	HL_TABLE()[idx].sg_font = NOFONT;
8852 # ifdef FEAT_XFONTSET
8853 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8854 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
8855 # endif
8856     }
8857 
8858     gui_mch_free_font(gui.norm_font);
8859 # ifdef FEAT_XFONTSET
8860     gui_mch_free_fontset(gui.fontset);
8861 # endif
8862 # ifndef HAVE_GTK2
8863     gui_mch_free_font(gui.bold_font);
8864     gui_mch_free_font(gui.ital_font);
8865     gui_mch_free_font(gui.boldital_font);
8866 # endif
8867 }
8868 #endif
8869 
8870 /**************************************
8871  *  End of Highlighting stuff	      *
8872  **************************************/
8873