xref: /vim-8.2.3635/src/syntax.c (revision 81f1ecbc)
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 	NULL
6069     };
6070 
6071 static char *(highlight_init_light[]) =
6072     {
6073 	"Directory term=bold ctermfg=DarkBlue guifg=Blue",
6074 	"LineNr term=underline ctermfg=Brown guifg=Brown",
6075 	"MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6076 	"Normal gui=NONE",
6077 	"Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6078 	"Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
6079 	"SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl",
6080 	"SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl",
6081 	"SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl",
6082 	"SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl",
6083 	"SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
6084 	"Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
6085 	"WarningMsg term=standout ctermfg=DarkRed guifg=Red",
6086 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6087 	"Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
6088 	"FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6089 	"SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6090 	"Visual term=reverse ctermbg=LightGrey guibg=LightGrey",
6091 	"DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
6092 	"DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
6093 	"DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
6094 	NULL
6095     };
6096 
6097 static char *(highlight_init_dark[]) =
6098     {
6099 	"Directory term=bold ctermfg=LightCyan guifg=Cyan",
6100 	"LineNr term=underline ctermfg=Yellow guifg=Yellow",
6101 	"MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
6102 	"Normal gui=NONE",
6103 	"Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
6104 	"Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6105 	"SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
6106 	"SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl",
6107 	"SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl",
6108 	"SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl",
6109 	"SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl",
6110 	"Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
6111 	"WarningMsg term=standout ctermfg=LightRed guifg=Red",
6112 	"WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6113 	"Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
6114 	"FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6115 	"SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6116 	"Visual term=reverse ctermbg=DarkGrey guibg=DarkGrey",
6117 	"DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6118 	"DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6119 	"DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6120 	NULL
6121     };
6122 
6123     void
6124 init_highlight(both, reset)
6125     int		both;	    /* include groups where 'bg' doesn't matter */
6126     int		reset;	    /* clear group first */
6127 {
6128     int		i;
6129     char	**pp;
6130     static int	had_both = FALSE;
6131 #ifdef FEAT_EVAL
6132     char_u	*p;
6133 
6134     /*
6135      * Try finding the color scheme file.  Used when a color file was loaded
6136      * and 'background' or 't_Co' is changed.
6137      */
6138     p = get_var_value((char_u *)"g:colors_name");
6139     if (p != NULL && load_colors(p) == OK)
6140 	return;
6141 #endif
6142 
6143     /*
6144      * Didn't use a color file, use the compiled-in colors.
6145      */
6146     if (both)
6147     {
6148 	had_both = TRUE;
6149 	pp = highlight_init_both;
6150 	for (i = 0; pp[i] != NULL; ++i)
6151 	    do_highlight((char_u *)pp[i], reset, TRUE);
6152     }
6153     else if (!had_both)
6154 	/* Don't do anything before the call with both == TRUE from main().
6155 	 * Not everything has been setup then, and that call will overrule
6156 	 * everything anyway. */
6157 	return;
6158 
6159     if (*p_bg == 'l')
6160 	pp = highlight_init_light;
6161     else
6162 	pp = highlight_init_dark;
6163     for (i = 0; pp[i] != NULL; ++i)
6164 	do_highlight((char_u *)pp[i], reset, TRUE);
6165 
6166 #ifdef FEAT_SYN_HL
6167     /*
6168      * If syntax highlighting is enabled load the highlighting for it.
6169      */
6170     if (get_var_value((char_u *)"g:syntax_on") != NULL)
6171     {
6172 	static int	recursive = 0;
6173 
6174 	if (recursive >= 5)
6175 	    EMSG(_("E679: recursive loop loading syncolor.vim"));
6176 	else
6177 	{
6178 	    ++recursive;
6179 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6180 	    --recursive;
6181 	}
6182     }
6183 #endif
6184 }
6185 
6186 /*
6187  * Load color file "name".
6188  * Return OK for success, FAIL for failure.
6189  */
6190     int
6191 load_colors(name)
6192     char_u	*name;
6193 {
6194     char_u	*buf;
6195     int		retval = FAIL;
6196     static int	recursive = FALSE;
6197 
6198     /* When being called recursively, this is probably because setting
6199      * 'background' caused the highlighting to be reloaded.  This means it is
6200      * working, thus we should return OK. */
6201     if (recursive)
6202 	return OK;
6203 
6204     recursive = TRUE;
6205     buf = alloc((unsigned)(STRLEN(name) + 12));
6206     if (buf != NULL)
6207     {
6208 	sprintf((char *)buf, "colors/%s.vim", name);
6209 	retval = source_runtime(buf, FALSE);
6210 	vim_free(buf);
6211 #ifdef FEAT_AUTOCMD
6212 	apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6213 #endif
6214     }
6215     recursive = FALSE;
6216 
6217     return retval;
6218 }
6219 
6220 /*
6221  * Handle the ":highlight .." command.
6222  * When using ":hi clear" this is called recursively for each group with
6223  * "forceit" and "init" both TRUE.
6224  */
6225     void
6226 do_highlight(line, forceit, init)
6227     char_u	*line;
6228     int		forceit;
6229     int		init;	    /* TRUE when called for initializing */
6230 {
6231     char_u	*name_end;
6232     char_u	*p;
6233     char_u	*linep;
6234     char_u	*key_start;
6235     char_u	*arg_start;
6236     char_u	*key = NULL, *arg = NULL;
6237     long	i;
6238     int		off;
6239     int		len;
6240     int		attr;
6241     int		id;
6242     int		idx;
6243     int		dodefault = FALSE;
6244     int		doclear = FALSE;
6245     int		dolink = FALSE;
6246     int		error = FALSE;
6247     int		color;
6248     int		is_normal_group = FALSE;	/* "Normal" group */
6249 #ifdef FEAT_GUI_X11
6250     int		is_menu_group = FALSE;		/* "Menu" group */
6251     int		is_scrollbar_group = FALSE;	/* "Scrollbar" group */
6252     int		is_tooltip_group = FALSE;	/* "Tooltip" group */
6253     int		do_colors = FALSE;		/* need to update colors? */
6254 #else
6255 # define is_menu_group 0
6256 # define is_tooltip_group 0
6257 #endif
6258 
6259     /*
6260      * If no argument, list current highlighting.
6261      */
6262     if (ends_excmd(*line))
6263     {
6264 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6265 	    /* TODO: only call when the group has attributes set */
6266 	    highlight_list_one((int)i);
6267 	return;
6268     }
6269 
6270     /*
6271      * Isolate the name.
6272      */
6273     name_end = skiptowhite(line);
6274     linep = skipwhite(name_end);
6275 
6276     /*
6277      * Check for "default" argument.
6278      */
6279     if (STRNCMP(line, "default", name_end - line) == 0)
6280     {
6281 	dodefault = TRUE;
6282 	line = linep;
6283 	name_end = skiptowhite(line);
6284 	linep = skipwhite(name_end);
6285     }
6286 
6287     /*
6288      * Check for "clear" or "link" argument.
6289      */
6290     if (STRNCMP(line, "clear", name_end - line) == 0)
6291 	doclear = TRUE;
6292     if (STRNCMP(line, "link", name_end - line) == 0)
6293 	dolink = TRUE;
6294 
6295     /*
6296      * ":highlight {group-name}": list highlighting for one group.
6297      */
6298     if (!doclear && !dolink && ends_excmd(*linep))
6299     {
6300 	id = syn_namen2id(line, (int)(name_end - line));
6301 	if (id == 0)
6302 	    EMSG2(_("E411: highlight group not found: %s"), line);
6303 	else
6304 	    highlight_list_one(id);
6305 	return;
6306     }
6307 
6308     /*
6309      * Handle ":highlight link {from} {to}" command.
6310      */
6311     if (dolink)
6312     {
6313 	char_u	    *from_start = linep;
6314 	char_u	    *from_end;
6315 	char_u	    *to_start;
6316 	char_u	    *to_end;
6317 	int	    from_id;
6318 	int	    to_id;
6319 
6320 	from_end = skiptowhite(from_start);
6321 	to_start = skipwhite(from_end);
6322 	to_end	 = skiptowhite(to_start);
6323 
6324 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
6325 	{
6326 	    EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6327 								  from_start);
6328 	    return;
6329 	}
6330 
6331 	if (!ends_excmd(*skipwhite(to_end)))
6332 	{
6333 	    EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6334 	    return;
6335 	}
6336 
6337 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
6338 	if (STRNCMP(to_start, "NONE", 4) == 0)
6339 	    to_id = 0;
6340 	else
6341 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
6342 
6343 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6344 	{
6345 	    /*
6346 	     * Don't allow a link when there already is some highlighting
6347 	     * for the group, unless '!' is used
6348 	     */
6349 	    if (to_id > 0 && !forceit && !init
6350 				   && hl_has_settings(from_id - 1, dodefault))
6351 	    {
6352 		if (sourcing_name == NULL && !dodefault)
6353 		    EMSG(_("E414: group has settings, highlight link ignored"));
6354 	    }
6355 	    else
6356 	    {
6357 		if (!init)
6358 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6359 		HL_TABLE()[from_id - 1].sg_link = to_id;
6360 #ifdef FEAT_EVAL
6361 		HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6362 #endif
6363 		redraw_all_later(NOT_VALID);
6364 	    }
6365 	}
6366 
6367 	/* Only call highlight_changed() once, after sourcing a syntax file */
6368 	need_highlight_changed = TRUE;
6369 
6370 	return;
6371     }
6372 
6373     if (doclear)
6374     {
6375 	/*
6376 	 * ":highlight clear [group]" command.
6377 	 */
6378 	line = linep;
6379 	if (ends_excmd(*line))
6380 	{
6381 #ifdef FEAT_GUI
6382 	    /* First, we do not destroy the old values, but allocate the new
6383 	     * ones and update the display. THEN we destroy the old values.
6384 	     * If we destroy the old values first, then the old values
6385 	     * (such as GuiFont's or GuiFontset's) will still be displayed but
6386 	     * invalid because they were free'd.
6387 	     */
6388 	    if (gui.in_use)
6389 	    {
6390 # ifdef FEAT_BEVAL_TIP
6391 		gui_init_tooltip_font();
6392 # endif
6393 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6394 		gui_init_menu_font();
6395 # endif
6396 	    }
6397 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6398 	    gui_mch_def_colors();
6399 # endif
6400 # ifdef FEAT_GUI_X11
6401 #  ifdef FEAT_MENU
6402 
6403 	    /* This only needs to be done when there is no Menu highlight
6404 	     * group defined by default, which IS currently the case.
6405 	     */
6406 	    gui_mch_new_menu_colors();
6407 #  endif
6408 	    if (gui.in_use)
6409 	    {
6410 		gui_new_scrollbar_colors();
6411 #  ifdef FEAT_BEVAL
6412 		gui_mch_new_tooltip_colors();
6413 #  endif
6414 #  ifdef FEAT_MENU
6415 		gui_mch_new_menu_font();
6416 #  endif
6417 	    }
6418 # endif
6419 
6420 	    /* Ok, we're done allocating the new default graphics items.
6421 	     * The screen should already be refreshed at this point.
6422 	     * It is now Ok to clear out the old data.
6423 	     */
6424 #endif
6425 #ifdef FEAT_EVAL
6426 	    do_unlet((char_u *)"colors_name", TRUE);
6427 #endif
6428 	    restore_cterm_colors();
6429 
6430 	    /*
6431 	     * Clear all default highlight groups and load the defaults.
6432 	     */
6433 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6434 		highlight_clear(idx);
6435 	    init_highlight(TRUE, TRUE);
6436 #ifdef FEAT_GUI
6437 	    if (gui.in_use)
6438 		highlight_gui_started();
6439 #endif
6440 	    highlight_changed();
6441 	    redraw_later_clear();
6442 	    return;
6443 	}
6444 	name_end = skiptowhite(line);
6445 	linep = skipwhite(name_end);
6446     }
6447 
6448     /*
6449      * Find the group name in the table.  If it does not exist yet, add it.
6450      */
6451     id = syn_check_group(line, (int)(name_end - line));
6452     if (id == 0)			/* failed (out of memory) */
6453 	return;
6454     idx = id - 1;			/* index is ID minus one */
6455 
6456     /* Return if "default" was used and the group already has settings. */
6457     if (dodefault && hl_has_settings(idx, TRUE))
6458 	return;
6459 
6460     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6461 	is_normal_group = TRUE;
6462 #ifdef FEAT_GUI_X11
6463     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6464 	is_menu_group = TRUE;
6465     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6466 	is_scrollbar_group = TRUE;
6467     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6468 	is_tooltip_group = TRUE;
6469 #endif
6470 
6471     /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6472     if (doclear || (forceit && init))
6473     {
6474 	highlight_clear(idx);
6475 	if (!doclear)
6476 	    HL_TABLE()[idx].sg_set = 0;
6477     }
6478 
6479     if (!doclear)
6480       while (!ends_excmd(*linep))
6481       {
6482 	key_start = linep;
6483 	if (*linep == '=')
6484 	{
6485 	    EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6486 	    error = TRUE;
6487 	    break;
6488 	}
6489 
6490 	/*
6491 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6492 	 * "guibg").
6493 	 */
6494 	while (*linep && !vim_iswhite(*linep) && *linep != '=')
6495 	    ++linep;
6496 	vim_free(key);
6497 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
6498 	if (key == NULL)
6499 	{
6500 	    error = TRUE;
6501 	    break;
6502 	}
6503 	linep = skipwhite(linep);
6504 
6505 	if (STRCMP(key, "NONE") == 0)
6506 	{
6507 	    if (!init || HL_TABLE()[idx].sg_set == 0)
6508 	    {
6509 		if (!init)
6510 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6511 		highlight_clear(idx);
6512 	    }
6513 	    continue;
6514 	}
6515 
6516 	/*
6517 	 * Check for the equal sign.
6518 	 */
6519 	if (*linep != '=')
6520 	{
6521 	    EMSG2(_("E416: missing equal sign: %s"), key_start);
6522 	    error = TRUE;
6523 	    break;
6524 	}
6525 	++linep;
6526 
6527 	/*
6528 	 * Isolate the argument.
6529 	 */
6530 	linep = skipwhite(linep);
6531 	if (*linep == '\'')		/* guifg='color name' */
6532 	{
6533 	    arg_start = ++linep;
6534 	    linep = vim_strchr(linep, '\'');
6535 	    if (linep == NULL)
6536 	    {
6537 		EMSG2(_(e_invarg2), key_start);
6538 		error = TRUE;
6539 		break;
6540 	    }
6541 	}
6542 	else
6543 	{
6544 	    arg_start = linep;
6545 	    linep = skiptowhite(linep);
6546 	}
6547 	if (linep == arg_start)
6548 	{
6549 	    EMSG2(_("E417: missing argument: %s"), key_start);
6550 	    error = TRUE;
6551 	    break;
6552 	}
6553 	vim_free(arg);
6554 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6555 	if (arg == NULL)
6556 	{
6557 	    error = TRUE;
6558 	    break;
6559 	}
6560 	if (*linep == '\'')
6561 	    ++linep;
6562 
6563 	/*
6564 	 * Store the argument.
6565 	 */
6566 	if (  STRCMP(key, "TERM") == 0
6567 		|| STRCMP(key, "CTERM") == 0
6568 		|| STRCMP(key, "GUI") == 0)
6569 	{
6570 	    attr = 0;
6571 	    off = 0;
6572 	    while (arg[off] != NUL)
6573 	    {
6574 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6575 		{
6576 		    len = (int)STRLEN(hl_name_table[i]);
6577 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6578 		    {
6579 			attr |= hl_attr_table[i];
6580 			off += len;
6581 			break;
6582 		    }
6583 		}
6584 		if (i < 0)
6585 		{
6586 		    EMSG2(_("E418: Illegal value: %s"), arg);
6587 		    error = TRUE;
6588 		    break;
6589 		}
6590 		if (arg[off] == ',')		/* another one follows */
6591 		    ++off;
6592 	    }
6593 	    if (error)
6594 		break;
6595 	    if (*key == 'T')
6596 	    {
6597 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6598 		{
6599 		    if (!init)
6600 			HL_TABLE()[idx].sg_set |= SG_TERM;
6601 		    HL_TABLE()[idx].sg_term = attr;
6602 		}
6603 	    }
6604 	    else if (*key == 'C')
6605 	    {
6606 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6607 		{
6608 		    if (!init)
6609 			HL_TABLE()[idx].sg_set |= SG_CTERM;
6610 		    HL_TABLE()[idx].sg_cterm = attr;
6611 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
6612 		}
6613 	    }
6614 #ifdef FEAT_GUI
6615 	    else
6616 	    {
6617 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6618 		{
6619 		    if (!init)
6620 			HL_TABLE()[idx].sg_set |= SG_GUI;
6621 		    HL_TABLE()[idx].sg_gui = attr;
6622 		}
6623 	    }
6624 #endif
6625 	}
6626 	else if (STRCMP(key, "FONT") == 0)
6627 	{
6628 	    /* in non-GUI fonts are simply ignored */
6629 #ifdef FEAT_GUI
6630 	    if (!gui.shell_created)
6631 	    {
6632 		/* GUI not started yet, always accept the name. */
6633 		vim_free(HL_TABLE()[idx].sg_font_name);
6634 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6635 	    }
6636 	    else
6637 	    {
6638 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6639 # ifdef FEAT_XFONTSET
6640 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6641 # endif
6642 		/* First, save the current font/fontset.
6643 		 * Then try to allocate the font/fontset.
6644 		 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6645 		 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6646 		 */
6647 
6648 		HL_TABLE()[idx].sg_font = NOFONT;
6649 # ifdef FEAT_XFONTSET
6650 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
6651 # endif
6652 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
6653 							    is_tooltip_group);
6654 
6655 # ifdef FEAT_XFONTSET
6656 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6657 		{
6658 		    /* New fontset was accepted. Free the old one, if there was
6659 		     * one.
6660 		     */
6661 		    gui_mch_free_fontset(temp_sg_fontset);
6662 		    vim_free(HL_TABLE()[idx].sg_font_name);
6663 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6664 		}
6665 		else
6666 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6667 # endif
6668 		if (HL_TABLE()[idx].sg_font != NOFONT)
6669 		{
6670 		    /* New font was accepted. Free the old one, if there was
6671 		     * one.
6672 		     */
6673 		    gui_mch_free_font(temp_sg_font);
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_font = temp_sg_font;
6679 	    }
6680 #endif
6681 	}
6682 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6683 	{
6684 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6685 	  {
6686 	    if (!init)
6687 		HL_TABLE()[idx].sg_set |= SG_CTERM;
6688 
6689 	    /* When setting the foreground color, and previously the "bold"
6690 	     * flag was set for a light color, reset it now */
6691 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6692 	    {
6693 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6694 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
6695 	    }
6696 
6697 	    if (VIM_ISDIGIT(*arg))
6698 		color = atoi((char *)arg);
6699 	    else if (STRICMP(arg, "fg") == 0)
6700 	    {
6701 		if (cterm_normal_fg_color)
6702 		    color = cterm_normal_fg_color - 1;
6703 		else
6704 		{
6705 		    EMSG(_("E419: FG color unknown"));
6706 		    error = TRUE;
6707 		    break;
6708 		}
6709 	    }
6710 	    else if (STRICMP(arg, "bg") == 0)
6711 	    {
6712 		if (cterm_normal_bg_color > 0)
6713 		    color = cterm_normal_bg_color - 1;
6714 		else
6715 		{
6716 		    EMSG(_("E420: BG color unknown"));
6717 		    error = TRUE;
6718 		    break;
6719 		}
6720 	    }
6721 	    else
6722 	    {
6723 		static char *(color_names[28]) = {
6724 			    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6725 			    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6726 			    "Gray", "Grey",
6727 			    "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6728 			    "Blue", "LightBlue", "Green", "LightGreen",
6729 			    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6730 			    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6731 		static int color_numbers_16[28] = {0, 1, 2, 3,
6732 						 4, 5, 6, 6,
6733 						 7, 7,
6734 						 7, 7, 8, 8,
6735 						 9, 9, 10, 10,
6736 						 11, 11, 12, 12, 13,
6737 						 13, 14, 14, 15, -1};
6738 		/* for xterm with 88 colors... */
6739 		static int color_numbers_88[28] = {0, 4, 2, 6,
6740 						 1, 5, 32, 72,
6741 						 84, 84,
6742 						 7, 7, 82, 82,
6743 						 12, 43, 10, 61,
6744 						 14, 63, 9, 74, 13,
6745 						 75, 11, 78, 15, -1};
6746 		/* for xterm with 256 colors... */
6747 		static int color_numbers_256[28] = {0, 4, 2, 6,
6748 						 1, 5, 130, 130,
6749 						 248, 248,
6750 						 7, 7, 242, 242,
6751 						 12, 81, 10, 121,
6752 						 14, 159, 9, 224, 13,
6753 						 225, 11, 229, 15, -1};
6754 		/* for terminals with less than 16 colors... */
6755 		static int color_numbers_8[28] = {0, 4, 2, 6,
6756 						 1, 5, 3, 3,
6757 						 7, 7,
6758 						 7, 7, 0+8, 0+8,
6759 						 4+8, 4+8, 2+8, 2+8,
6760 						 6+8, 6+8, 1+8, 1+8, 5+8,
6761 						 5+8, 3+8, 3+8, 7+8, -1};
6762 #if defined(__QNXNTO__)
6763 		static int *color_numbers_8_qansi = color_numbers_8;
6764 		/* On qnx, the 8 & 16 color arrays are the same */
6765 		if (STRNCMP(T_NAME, "qansi", 5) == 0)
6766 		    color_numbers_8_qansi = color_numbers_16;
6767 #endif
6768 
6769 		/* reduce calls to STRICMP a bit, it can be slow */
6770 		off = TOUPPER_ASC(*arg);
6771 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6772 		    if (off == color_names[i][0]
6773 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6774 			break;
6775 		if (i < 0)
6776 		{
6777 		    EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6778 		    error = TRUE;
6779 		    break;
6780 		}
6781 
6782 		/* Use the _16 table to check if its a valid color name. */
6783 		color = color_numbers_16[i];
6784 		if (color >= 0)
6785 		{
6786 		    if (t_colors == 8)
6787 		    {
6788 			/* t_Co is 8: use the 8 colors table */
6789 #if defined(__QNXNTO__)
6790 			color = color_numbers_8_qansi[i];
6791 #else
6792 			color = color_numbers_8[i];
6793 #endif
6794 			if (key[5] == 'F')
6795 			{
6796 			    /* set/reset bold attribute to get light foreground
6797 			     * colors (on some terminals, e.g. "linux") */
6798 			    if (color & 8)
6799 			    {
6800 				HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6801 				HL_TABLE()[idx].sg_cterm_bold = TRUE;
6802 			    }
6803 			    else
6804 				HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6805 			}
6806 			color &= 7;	/* truncate to 8 colors */
6807 		    }
6808 		    else if (t_colors == 16 || t_colors == 88
6809 							   || t_colors == 256)
6810 		    {
6811 			/*
6812 			 * Guess: if the termcap entry ends in 'm', it is
6813 			 * probably an xterm-like terminal.  Use the changed
6814 			 * order for colors.
6815 			 */
6816 			if (*T_CAF != NUL)
6817 			    p = T_CAF;
6818 			else
6819 			    p = T_CSF;
6820 			if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6821 			    switch (t_colors)
6822 			    {
6823 				case 16:
6824 				    color = color_numbers_8[i];
6825 				    break;
6826 				case 88:
6827 				    color = color_numbers_88[i];
6828 				    break;
6829 				case 256:
6830 				    color = color_numbers_256[i];
6831 				    break;
6832 			    }
6833 		    }
6834 		}
6835 	    }
6836 	    /* Add one to the argument, to avoid zero */
6837 	    if (key[5] == 'F')
6838 	    {
6839 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
6840 		if (is_normal_group)
6841 		{
6842 		    cterm_normal_fg_color = color + 1;
6843 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6844 #ifdef FEAT_GUI
6845 		    /* Don't do this if the GUI is used. */
6846 		    if (!gui.in_use && !gui.starting)
6847 #endif
6848 		    {
6849 			must_redraw = CLEAR;
6850 			if (termcap_active)
6851 			    term_fg_color(color);
6852 		    }
6853 		}
6854 	    }
6855 	    else
6856 	    {
6857 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
6858 		if (is_normal_group)
6859 		{
6860 		    cterm_normal_bg_color = color + 1;
6861 #ifdef FEAT_GUI
6862 		    /* Don't mess with 'background' if the GUI is used. */
6863 		    if (!gui.in_use && !gui.starting)
6864 #endif
6865 		    {
6866 			must_redraw = CLEAR;
6867 			if (termcap_active)
6868 			    term_bg_color(color);
6869 			if (t_colors < 16)
6870 			    i = (color == 0 || color == 4);
6871 			else
6872 			    i = (color < 7 || color == 8);
6873 			/* Set the 'background' option if the value is wrong. */
6874 			if (i != (*p_bg == 'd'))
6875 			    set_option_value((char_u *)"bg", 0L,
6876 				 i ? (char_u *)"dark" : (char_u *)"light", 0);
6877 		    }
6878 		}
6879 	    }
6880 	  }
6881 	}
6882 	else if (STRCMP(key, "GUIFG") == 0)
6883 	{
6884 #ifdef FEAT_GUI	    /* in non-GUI guifg colors are simply ignored */
6885 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6886 	    {
6887 		if (!init)
6888 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6889 
6890 		i = color_name2handle(arg);
6891 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6892 		{
6893 		    HL_TABLE()[idx].sg_gui_fg = i;
6894 		    vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6895 		    if (STRCMP(arg, "NONE"))
6896 			HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6897 		    else
6898 			HL_TABLE()[idx].sg_gui_fg_name = NULL;
6899 # ifdef FEAT_GUI_X11
6900 		    if (is_menu_group)
6901 			gui.menu_fg_pixel = i;
6902 		    if (is_scrollbar_group)
6903 			gui.scroll_fg_pixel = i;
6904 #  ifdef FEAT_BEVAL
6905 		    if (is_tooltip_group)
6906 			gui.tooltip_fg_pixel = i;
6907 #  endif
6908 		    do_colors = TRUE;
6909 # endif
6910 		}
6911 	    }
6912 #endif
6913 	}
6914 	else if (STRCMP(key, "GUIBG") == 0)
6915 	{
6916 #ifdef FEAT_GUI	    /* in non-GUI guibg colors are simply ignored */
6917 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6918 	    {
6919 		if (!init)
6920 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6921 
6922 		i = color_name2handle(arg);
6923 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6924 		{
6925 		    HL_TABLE()[idx].sg_gui_bg = i;
6926 		    vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6927 		    if (STRCMP(arg, "NONE") != 0)
6928 			HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6929 		    else
6930 			HL_TABLE()[idx].sg_gui_bg_name = NULL;
6931 # ifdef FEAT_GUI_X11
6932 		    if (is_menu_group)
6933 			gui.menu_bg_pixel = i;
6934 		    if (is_scrollbar_group)
6935 			gui.scroll_bg_pixel = i;
6936 #  ifdef FEAT_BEVAL
6937 		    if (is_tooltip_group)
6938 			gui.tooltip_bg_pixel = i;
6939 #  endif
6940 		    do_colors = TRUE;
6941 # endif
6942 		}
6943 	    }
6944 #endif
6945 	}
6946 	else if (STRCMP(key, "GUISP") == 0)
6947 	{
6948 #ifdef FEAT_GUI	    /* in non-GUI guisp colors are simply ignored */
6949 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6950 	    {
6951 		if (!init)
6952 		    HL_TABLE()[idx].sg_set |= SG_GUI;
6953 
6954 		i = color_name2handle(arg);
6955 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6956 		{
6957 		    HL_TABLE()[idx].sg_gui_sp = i;
6958 		    vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6959 		    if (STRCMP(arg, "NONE") != 0)
6960 			HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6961 		    else
6962 			HL_TABLE()[idx].sg_gui_sp_name = NULL;
6963 		}
6964 	    }
6965 #endif
6966 	}
6967 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6968 	{
6969 	    char_u	buf[100];
6970 	    char_u	*tname;
6971 
6972 	    if (!init)
6973 		HL_TABLE()[idx].sg_set |= SG_TERM;
6974 
6975 	    /*
6976 	     * The "start" and "stop"  arguments can be a literal escape
6977 	     * sequence, or a comma seperated list of terminal codes.
6978 	     */
6979 	    if (STRNCMP(arg, "t_", 2) == 0)
6980 	    {
6981 		off = 0;
6982 		buf[0] = 0;
6983 		while (arg[off] != NUL)
6984 		{
6985 		    /* Isolate one termcap name */
6986 		    for (len = 0; arg[off + len] &&
6987 						 arg[off + len] != ','; ++len)
6988 			;
6989 		    tname = vim_strnsave(arg + off, len);
6990 		    if (tname == NULL)		/* out of memory */
6991 		    {
6992 			error = TRUE;
6993 			break;
6994 		    }
6995 		    /* lookup the escape sequence for the item */
6996 		    p = get_term_code(tname);
6997 		    vim_free(tname);
6998 		    if (p == NULL)	    /* ignore non-existing things */
6999 			p = (char_u *)"";
7000 
7001 		    /* Append it to the already found stuff */
7002 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7003 		    {
7004 			EMSG2(_("E422: terminal code too long: %s"), arg);
7005 			error = TRUE;
7006 			break;
7007 		    }
7008 		    STRCAT(buf, p);
7009 
7010 		    /* Advance to the next item */
7011 		    off += len;
7012 		    if (arg[off] == ',')	    /* another one follows */
7013 			++off;
7014 		}
7015 	    }
7016 	    else
7017 	    {
7018 		/*
7019 		 * Copy characters from arg[] to buf[], translating <> codes.
7020 		 */
7021 		for (p = arg, off = 0; off < 100 && *p; )
7022 		{
7023 		    len = trans_special(&p, buf + off, FALSE);
7024 		    if (len)		    /* recognized special char */
7025 			off += len;
7026 		    else		    /* copy as normal char */
7027 			buf[off++] = *p++;
7028 		}
7029 		buf[off] = NUL;
7030 	    }
7031 	    if (error)
7032 		break;
7033 
7034 	    if (STRCMP(buf, "NONE") == 0)	/* resetting the value */
7035 		p = NULL;
7036 	    else
7037 		p = vim_strsave(buf);
7038 	    if (key[2] == 'A')
7039 	    {
7040 		vim_free(HL_TABLE()[idx].sg_start);
7041 		HL_TABLE()[idx].sg_start = p;
7042 	    }
7043 	    else
7044 	    {
7045 		vim_free(HL_TABLE()[idx].sg_stop);
7046 		HL_TABLE()[idx].sg_stop = p;
7047 	    }
7048 	}
7049 	else
7050 	{
7051 	    EMSG2(_("E423: Illegal argument: %s"), key_start);
7052 	    error = TRUE;
7053 	    break;
7054 	}
7055 
7056 	/*
7057 	 * When highlighting has been given for a group, don't link it.
7058 	 */
7059 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7060 	    HL_TABLE()[idx].sg_link = 0;
7061 
7062 	/*
7063 	 * Continue with next argument.
7064 	 */
7065 	linep = skipwhite(linep);
7066       }
7067 
7068     /*
7069      * If there is an error, and it's a new entry, remove it from the table.
7070      */
7071     if (error && idx == highlight_ga.ga_len)
7072 	syn_unadd_group();
7073     else
7074     {
7075 	if (is_normal_group)
7076 	{
7077 	    HL_TABLE()[idx].sg_term_attr = 0;
7078 	    HL_TABLE()[idx].sg_cterm_attr = 0;
7079 #ifdef FEAT_GUI
7080 	    HL_TABLE()[idx].sg_gui_attr = 0;
7081 	    /*
7082 	     * Need to update all groups, because they might be using "bg"
7083 	     * and/or "fg", which have been changed now.
7084 	     */
7085 	    if (gui.in_use)
7086 		highlight_gui_started();
7087 #endif
7088 	}
7089 #ifdef FEAT_GUI_X11
7090 # ifdef FEAT_MENU
7091 	else if (is_menu_group)
7092 	{
7093 	    if (gui.in_use && do_colors)
7094 		gui_mch_new_menu_colors();
7095 	}
7096 # endif
7097 	else if (is_scrollbar_group)
7098 	{
7099 	    if (gui.in_use && do_colors)
7100 		gui_new_scrollbar_colors();
7101 	}
7102 # ifdef FEAT_BEVAL
7103 	else if (is_tooltip_group)
7104 	{
7105 	    if (gui.in_use && do_colors)
7106 		gui_mch_new_tooltip_colors();
7107 	}
7108 # endif
7109 #endif
7110 	else
7111 	    set_hl_attr(idx);
7112 #ifdef FEAT_EVAL
7113 	HL_TABLE()[idx].sg_scriptID = current_SID;
7114 #endif
7115 	redraw_all_later(NOT_VALID);
7116     }
7117     vim_free(key);
7118     vim_free(arg);
7119 
7120     /* Only call highlight_changed() once, after sourcing a syntax file */
7121     need_highlight_changed = TRUE;
7122 }
7123 
7124 #if defined(EXITFREE) || defined(PROTO)
7125     void
7126 free_highlight()
7127 {
7128     int	    i;
7129 
7130     for (i = 0; i < highlight_ga.ga_len; ++i)
7131     {
7132 	highlight_clear(i);
7133 	vim_free(HL_TABLE()[i].sg_name);
7134 	vim_free(HL_TABLE()[i].sg_name_u);
7135     }
7136     ga_clear(&highlight_ga);
7137 }
7138 #endif
7139 
7140 /*
7141  * Reset the cterm colors to what they were before Vim was started, if
7142  * possible.  Otherwise reset them to zero.
7143  */
7144     void
7145 restore_cterm_colors()
7146 {
7147 #if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7148     /* Since t_me has been set, this probably means that the user
7149      * wants to use this as default colors.  Need to reset default
7150      * background/foreground colors. */
7151     mch_set_normal_colors();
7152 #else
7153     cterm_normal_fg_color = 0;
7154     cterm_normal_fg_bold = 0;
7155     cterm_normal_bg_color = 0;
7156 #endif
7157 }
7158 
7159 /*
7160  * Return TRUE if highlight group "idx" has any settings.
7161  * When "check_link" is TRUE also check for an existing link.
7162  */
7163     static int
7164 hl_has_settings(idx, check_link)
7165     int		idx;
7166     int		check_link;
7167 {
7168     return (   HL_TABLE()[idx].sg_term_attr != 0
7169 	    || HL_TABLE()[idx].sg_cterm_attr != 0
7170 #ifdef FEAT_GUI
7171 	    || HL_TABLE()[idx].sg_gui_attr != 0
7172 #endif
7173 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7174 }
7175 
7176 /*
7177  * Clear highlighting for one group.
7178  */
7179     static void
7180 highlight_clear(idx)
7181     int idx;
7182 {
7183     HL_TABLE()[idx].sg_term = 0;
7184     vim_free(HL_TABLE()[idx].sg_start);
7185     HL_TABLE()[idx].sg_start = NULL;
7186     vim_free(HL_TABLE()[idx].sg_stop);
7187     HL_TABLE()[idx].sg_stop = NULL;
7188     HL_TABLE()[idx].sg_term_attr = 0;
7189     HL_TABLE()[idx].sg_cterm = 0;
7190     HL_TABLE()[idx].sg_cterm_bold = FALSE;
7191     HL_TABLE()[idx].sg_cterm_fg = 0;
7192     HL_TABLE()[idx].sg_cterm_bg = 0;
7193     HL_TABLE()[idx].sg_cterm_attr = 0;
7194 #ifdef FEAT_GUI	    /* in non-GUI fonts are simply ignored */
7195     HL_TABLE()[idx].sg_gui = 0;
7196     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7197     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7198     HL_TABLE()[idx].sg_gui_fg_name = NULL;
7199     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7200     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7201     HL_TABLE()[idx].sg_gui_bg_name = NULL;
7202     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7203     vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7204     HL_TABLE()[idx].sg_gui_sp_name = NULL;
7205     gui_mch_free_font(HL_TABLE()[idx].sg_font);
7206     HL_TABLE()[idx].sg_font = NOFONT;
7207 # ifdef FEAT_XFONTSET
7208     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7209     HL_TABLE()[idx].sg_fontset = NOFONTSET;
7210 # endif
7211     vim_free(HL_TABLE()[idx].sg_font_name);
7212     HL_TABLE()[idx].sg_font_name = NULL;
7213     HL_TABLE()[idx].sg_gui_attr = 0;
7214 #endif
7215 #ifdef FEAT_EVAL
7216     /* Clear the script ID only when there is no link, since that is not
7217      * cleared. */
7218     if (HL_TABLE()[idx].sg_link == 0)
7219 	HL_TABLE()[idx].sg_scriptID = 0;
7220 #endif
7221 }
7222 
7223 #if defined(FEAT_GUI) || defined(PROTO)
7224 /*
7225  * Set the normal foreground and background colors according to the "Normal"
7226  * highlighighting group.  For X11 also set "Menu", "Scrollbar", and
7227  * "Tooltip" colors.
7228  */
7229     void
7230 set_normal_colors()
7231 {
7232     if (set_group_colors((char_u *)"Normal",
7233 			     &gui.norm_pixel, &gui.back_pixel,
7234 			     FALSE, TRUE, FALSE))
7235     {
7236 	gui_mch_new_colors();
7237 	must_redraw = CLEAR;
7238     }
7239 #ifdef FEAT_GUI_X11
7240     if (set_group_colors((char_u *)"Menu",
7241 			 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7242 			 TRUE, FALSE, FALSE))
7243     {
7244 # ifdef FEAT_MENU
7245 	gui_mch_new_menu_colors();
7246 # endif
7247 	must_redraw = CLEAR;
7248     }
7249 # ifdef FEAT_BEVAL
7250     if (set_group_colors((char_u *)"Tooltip",
7251 			 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7252 			 FALSE, FALSE, TRUE))
7253     {
7254 # ifdef FEAT_TOOLBAR
7255 	gui_mch_new_tooltip_colors();
7256 # endif
7257 	must_redraw = CLEAR;
7258     }
7259 #endif
7260     if (set_group_colors((char_u *)"Scrollbar",
7261 		    &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7262 		    FALSE, FALSE, FALSE))
7263     {
7264 	gui_new_scrollbar_colors();
7265 	must_redraw = CLEAR;
7266     }
7267 #endif
7268 }
7269 
7270 /*
7271  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7272  */
7273     static int
7274 set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7275     char_u	*name;
7276     guicolor_T	*fgp;
7277     guicolor_T	*bgp;
7278     int		do_menu;
7279     int		use_norm;
7280     int		do_tooltip;
7281 {
7282     int		idx;
7283 
7284     idx = syn_name2id(name) - 1;
7285     if (idx >= 0)
7286     {
7287 	gui_do_one_color(idx, do_menu, do_tooltip);
7288 
7289 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7290 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
7291 	else if (use_norm)
7292 	    *fgp = gui.def_norm_pixel;
7293 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7294 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
7295 	else if (use_norm)
7296 	    *bgp = gui.def_back_pixel;
7297 	return TRUE;
7298     }
7299     return FALSE;
7300 }
7301 
7302 /*
7303  * Get the font of the "Normal" group.
7304  * Returns "" when it's not found or not set.
7305  */
7306     char_u *
7307 hl_get_font_name()
7308 {
7309     int		id;
7310     char_u	*s;
7311 
7312     id = syn_name2id((char_u *)"Normal");
7313     if (id > 0)
7314     {
7315 	s = HL_TABLE()[id - 1].sg_font_name;
7316 	if (s != NULL)
7317 	    return s;
7318     }
7319     return (char_u *)"";
7320 }
7321 
7322 /*
7323  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
7324  * actually chosen to be used.
7325  */
7326     void
7327 hl_set_font_name(font_name)
7328     char_u	*font_name;
7329 {
7330     int	    id;
7331 
7332     id = syn_name2id((char_u *)"Normal");
7333     if (id > 0)
7334     {
7335 	vim_free(HL_TABLE()[id - 1].sg_font_name);
7336 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7337     }
7338 }
7339 
7340 /*
7341  * Set background color for "Normal" group.  Called by gui_set_bg_color()
7342  * when the color is known.
7343  */
7344     void
7345 hl_set_bg_color_name(name)
7346     char_u  *name;	    /* must have been allocated */
7347 {
7348     int	    id;
7349 
7350     if (name != NULL)
7351     {
7352 	id = syn_name2id((char_u *)"Normal");
7353 	if (id > 0)
7354 	{
7355 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7356 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
7357 	}
7358     }
7359 }
7360 
7361 /*
7362  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
7363  * when the color is known.
7364  */
7365     void
7366 hl_set_fg_color_name(name)
7367     char_u  *name;	    /* must have been allocated */
7368 {
7369     int	    id;
7370 
7371     if (name != NULL)
7372     {
7373 	id = syn_name2id((char_u *)"Normal");
7374 	if (id > 0)
7375 	{
7376 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7377 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
7378 	}
7379     }
7380 }
7381 
7382 /*
7383  * Return the handle for a color name.
7384  * Returns INVALCOLOR when failed.
7385  */
7386     static guicolor_T
7387 color_name2handle(name)
7388     char_u  *name;
7389 {
7390     if (STRCMP(name, "NONE") == 0)
7391 	return INVALCOLOR;
7392 
7393     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7394 	return gui.norm_pixel;
7395     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7396 	return gui.back_pixel;
7397 
7398     return gui_get_color(name);
7399 }
7400 
7401 /*
7402  * Return the handle for a font name.
7403  * Returns NOFONT when failed.
7404  */
7405     static GuiFont
7406 font_name2handle(name)
7407     char_u  *name;
7408 {
7409     if (STRCMP(name, "NONE") == 0)
7410 	return NOFONT;
7411 
7412     return gui_mch_get_font(name, TRUE);
7413 }
7414 
7415 # ifdef FEAT_XFONTSET
7416 /*
7417  * Return the handle for a fontset name.
7418  * Returns NOFONTSET when failed.
7419  */
7420     static GuiFontset
7421 fontset_name2handle(name, fixed_width)
7422     char_u	*name;
7423     int		fixed_width;
7424 {
7425     if (STRCMP(name, "NONE") == 0)
7426 	return NOFONTSET;
7427 
7428     return gui_mch_get_fontset(name, TRUE, fixed_width);
7429 }
7430 # endif
7431 
7432 /*
7433  * Get the font or fontset for one highlight group.
7434  */
7435 /*ARGSUSED*/
7436     static void
7437 hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7438     int		idx;
7439     char_u	*arg;
7440     int		do_normal;	/* set normal font */
7441     int		do_menu;	/* set menu font */
7442     int		do_tooltip;	/* set tooltip font */
7443 {
7444 # ifdef FEAT_XFONTSET
7445     /* If 'guifontset' is not empty, first try using the name as a
7446      * fontset.  If that doesn't work, use it as a font name. */
7447     if (*p_guifontset != NUL
7448 #  ifdef FONTSET_ALWAYS
7449 	|| do_menu
7450 #  endif
7451 #  ifdef FEAT_BEVAL_TIP
7452 	/* In Athena & Motif, the Tooltip highlight group is always a fontset */
7453 	|| do_tooltip
7454 #  endif
7455 	    )
7456 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7457 #  ifdef FONTSET_ALWAYS
7458 		|| do_menu
7459 #  endif
7460 #  ifdef FEAT_BEVAL_TIP
7461 		|| do_tooltip
7462 #  endif
7463 		);
7464     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7465     {
7466 	/* If it worked and it's the Normal group, use it as the
7467 	 * normal fontset.  Same for the Menu group. */
7468 	if (do_normal)
7469 	    gui_init_font(arg, TRUE);
7470 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7471 	if (do_menu)
7472 	{
7473 #    ifdef FONTSET_ALWAYS
7474 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7475 #    else
7476 	    /* YIKES!  This is a bug waiting to crash the program */
7477 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
7478 #    endif
7479 	    gui_mch_new_menu_font();
7480 	}
7481 #    ifdef FEAT_BEVAL
7482 	if (do_tooltip)
7483 	{
7484 	    /* The Athena widget set cannot currently handle switching between
7485 	     * displaying a single font and a fontset.
7486 	     * If the XtNinternational resource is set to True at widget
7487 	     * creation, then a fontset is always used, othwise an
7488 	     * XFontStruct is used.
7489 	     */
7490 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7491 	    gui_mch_new_tooltip_font();
7492 	}
7493 #    endif
7494 #   endif
7495     }
7496     else
7497 # endif
7498     {
7499 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
7500 	/* If it worked and it's the Normal group, use it as the
7501 	 * normal font.  Same for the Menu group. */
7502 	if (HL_TABLE()[idx].sg_font != NOFONT)
7503 	{
7504 	    if (do_normal)
7505 		gui_init_font(arg, FALSE);
7506 #ifndef FONTSET_ALWAYS
7507 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7508 	    if (do_menu)
7509 	    {
7510 		gui.menu_font = HL_TABLE()[idx].sg_font;
7511 		gui_mch_new_menu_font();
7512 	    }
7513 # endif
7514 #endif
7515 	}
7516     }
7517 }
7518 
7519 #endif /* FEAT_GUI */
7520 
7521 /*
7522  * Table with the specifications for an attribute number.
7523  * Note that this table is used by ALL buffers.  This is required because the
7524  * GUI can redraw at any time for any buffer.
7525  */
7526 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
7527 
7528 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7529 
7530 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
7531 
7532 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7533 
7534 #ifdef FEAT_GUI
7535 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
7536 
7537 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7538 #endif
7539 
7540 /*
7541  * Return the attr number for a set of colors and font.
7542  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7543  * if the combination is new.
7544  * Return 0 for error (no more room).
7545  */
7546     static int
7547 get_attr_entry(table, aep)
7548     garray_T	*table;
7549     attrentry_T	*aep;
7550 {
7551     int		i;
7552     attrentry_T	*taep;
7553     static int	recursive = FALSE;
7554 
7555     /*
7556      * Init the table, in case it wasn't done yet.
7557      */
7558     table->ga_itemsize = sizeof(attrentry_T);
7559     table->ga_growsize = 7;
7560 
7561     /*
7562      * Try to find an entry with the same specifications.
7563      */
7564     for (i = 0; i < table->ga_len; ++i)
7565     {
7566 	taep = &(((attrentry_T *)table->ga_data)[i]);
7567 	if (	   aep->ae_attr == taep->ae_attr
7568 		&& (
7569 #ifdef FEAT_GUI
7570 		       (table == &gui_attr_table
7571 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7572 			    && aep->ae_u.gui.bg_color
7573 						    == taep->ae_u.gui.bg_color
7574 			    && aep->ae_u.gui.sp_color
7575 						    == taep->ae_u.gui.sp_color
7576 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
7577 #  ifdef FEAT_XFONTSET
7578 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
7579 #  endif
7580 			    ))
7581 		    ||
7582 #endif
7583 		       (table == &term_attr_table
7584 			&& (aep->ae_u.term.start == NULL)
7585 					    == (taep->ae_u.term.start == NULL)
7586 			&& (aep->ae_u.term.start == NULL
7587 			    || STRCMP(aep->ae_u.term.start,
7588 						  taep->ae_u.term.start) == 0)
7589 			&& (aep->ae_u.term.stop == NULL)
7590 					     == (taep->ae_u.term.stop == NULL)
7591 			&& (aep->ae_u.term.stop == NULL
7592 			    || STRCMP(aep->ae_u.term.stop,
7593 						  taep->ae_u.term.stop) == 0))
7594 		    || (table == &cterm_attr_table
7595 			    && aep->ae_u.cterm.fg_color
7596 						  == taep->ae_u.cterm.fg_color
7597 			    && aep->ae_u.cterm.bg_color
7598 						 == taep->ae_u.cterm.bg_color)
7599 		     ))
7600 
7601 	return i + ATTR_OFF;
7602     }
7603 
7604     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
7605     {
7606 	/*
7607 	 * Running out of attribute entries!  remove all attributes, and
7608 	 * compute new ones for all groups.
7609 	 * When called recursively, we are really out of numbers.
7610 	 */
7611 	if (recursive)
7612 	{
7613 	    EMSG(_("E424: Too many different highlighting attributes in use"));
7614 	    return 0;
7615 	}
7616 	recursive = TRUE;
7617 
7618 	clear_hl_tables();
7619 
7620 	must_redraw = CLEAR;
7621 
7622 	for (i = 0; i < highlight_ga.ga_len; ++i)
7623 	    set_hl_attr(i);
7624 
7625 	recursive = FALSE;
7626     }
7627 
7628     /*
7629      * This is a new combination of colors and font, add an entry.
7630      */
7631     if (ga_grow(table, 1) == FAIL)
7632 	return 0;
7633 
7634     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7635     vim_memset(taep, 0, sizeof(attrentry_T));
7636     taep->ae_attr = aep->ae_attr;
7637 #ifdef FEAT_GUI
7638     if (table == &gui_attr_table)
7639     {
7640 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7641 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7642 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7643 	taep->ae_u.gui.font = aep->ae_u.gui.font;
7644 # ifdef FEAT_XFONTSET
7645 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7646 # endif
7647     }
7648 #endif
7649     if (table == &term_attr_table)
7650     {
7651 	if (aep->ae_u.term.start == NULL)
7652 	    taep->ae_u.term.start = NULL;
7653 	else
7654 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7655 	if (aep->ae_u.term.stop == NULL)
7656 	    taep->ae_u.term.stop = NULL;
7657 	else
7658 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7659     }
7660     else if (table == &cterm_attr_table)
7661     {
7662 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7663 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7664     }
7665     ++table->ga_len;
7666     return (table->ga_len - 1 + ATTR_OFF);
7667 }
7668 
7669 /*
7670  * Clear all highlight tables.
7671  */
7672     void
7673 clear_hl_tables()
7674 {
7675     int		i;
7676     attrentry_T	*taep;
7677 
7678 #ifdef FEAT_GUI
7679     ga_clear(&gui_attr_table);
7680 #endif
7681     for (i = 0; i < term_attr_table.ga_len; ++i)
7682     {
7683 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7684 	vim_free(taep->ae_u.term.start);
7685 	vim_free(taep->ae_u.term.stop);
7686     }
7687     ga_clear(&term_attr_table);
7688     ga_clear(&cterm_attr_table);
7689 }
7690 
7691 #if defined(FEAT_SYN_HL) || defined(PROTO)
7692 /*
7693  * Combine special attributes (e.g., for spelling) with other attributes
7694  * (e.g., for syntax highlighting).
7695  * "prim_attr" overrules "char_attr".
7696  * This creates a new group when required.
7697  * Since we expect there to be few spelling mistakes we don't cache the
7698  * result.
7699  * Return the resulting attributes.
7700  */
7701     int
7702 hl_combine_attr(char_attr, prim_attr)
7703     int	    char_attr;
7704     int	    prim_attr;
7705 {
7706     attrentry_T *char_aep = NULL;
7707     attrentry_T *spell_aep;
7708     attrentry_T new_en;
7709 
7710     if (char_attr == 0)
7711 	return prim_attr;
7712     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7713 	return char_attr | prim_attr;
7714 #ifdef FEAT_GUI
7715     if (gui.in_use)
7716     {
7717 	if (char_attr > HL_ALL)
7718 	    char_aep = syn_gui_attr2entry(char_attr);
7719 	if (char_aep != NULL)
7720 	    new_en = *char_aep;
7721 	else
7722 	{
7723 	    vim_memset(&new_en, 0, sizeof(new_en));
7724 	    if (char_attr <= HL_ALL)
7725 		new_en.ae_attr = char_attr;
7726 	}
7727 
7728 	if (prim_attr <= HL_ALL)
7729 	    new_en.ae_attr |= prim_attr;
7730 	else
7731 	{
7732 	    spell_aep = syn_gui_attr2entry(prim_attr);
7733 	    if (spell_aep != NULL)
7734 	    {
7735 		new_en.ae_attr |= spell_aep->ae_attr;
7736 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7737 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7738 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7739 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7740 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7741 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7742 		if (spell_aep->ae_u.gui.font != NOFONT)
7743 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7744 # ifdef FEAT_XFONTSET
7745 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7746 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7747 # endif
7748 	    }
7749 	}
7750 	return get_attr_entry(&gui_attr_table, &new_en);
7751     }
7752 #endif
7753 
7754     if (t_colors > 1)
7755     {
7756 	if (char_attr > HL_ALL)
7757 	    char_aep = syn_cterm_attr2entry(char_attr);
7758 	if (char_aep != NULL)
7759 	    new_en = *char_aep;
7760 	else
7761 	{
7762 	    vim_memset(&new_en, 0, sizeof(new_en));
7763 	    if (char_attr <= HL_ALL)
7764 		new_en.ae_attr = char_attr;
7765 	}
7766 
7767 	if (prim_attr <= HL_ALL)
7768 	    new_en.ae_attr |= prim_attr;
7769 	else
7770 	{
7771 	    spell_aep = syn_cterm_attr2entry(prim_attr);
7772 	    if (spell_aep != NULL)
7773 	    {
7774 		new_en.ae_attr |= spell_aep->ae_attr;
7775 		if (spell_aep->ae_u.cterm.fg_color > 0)
7776 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7777 		if (spell_aep->ae_u.cterm.bg_color > 0)
7778 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7779 	    }
7780 	}
7781 	return get_attr_entry(&cterm_attr_table, &new_en);
7782     }
7783 
7784     if (char_attr > HL_ALL)
7785 	char_aep = syn_term_attr2entry(char_attr);
7786     if (char_aep != NULL)
7787 	new_en = *char_aep;
7788     else
7789     {
7790 	vim_memset(&new_en, 0, sizeof(new_en));
7791 	if (char_attr <= HL_ALL)
7792 	    new_en.ae_attr = char_attr;
7793     }
7794 
7795     if (prim_attr <= HL_ALL)
7796 	new_en.ae_attr |= prim_attr;
7797     else
7798     {
7799 	spell_aep = syn_cterm_attr2entry(prim_attr);
7800 	if (spell_aep != NULL)
7801 	{
7802 	    new_en.ae_attr |= spell_aep->ae_attr;
7803 	    if (spell_aep->ae_u.term.start != NULL)
7804 	    {
7805 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
7806 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
7807 	    }
7808 	}
7809     }
7810     return get_attr_entry(&term_attr_table, &new_en);
7811 }
7812 #endif
7813 
7814 #ifdef FEAT_GUI
7815 
7816     attrentry_T *
7817 syn_gui_attr2entry(attr)
7818     int		    attr;
7819 {
7820     attr -= ATTR_OFF;
7821     if (attr >= gui_attr_table.ga_len)	    /* did ":syntax clear" */
7822 	return NULL;
7823     return &(GUI_ATTR_ENTRY(attr));
7824 }
7825 
7826 #endif /* FEAT_GUI */
7827 
7828     attrentry_T *
7829 syn_term_attr2entry(attr)
7830     int		    attr;
7831 {
7832     attr -= ATTR_OFF;
7833     if (attr >= term_attr_table.ga_len)	    /* did ":syntax clear" */
7834 	return NULL;
7835     return &(TERM_ATTR_ENTRY(attr));
7836 }
7837 
7838     attrentry_T *
7839 syn_cterm_attr2entry(attr)
7840     int		    attr;
7841 {
7842     attr -= ATTR_OFF;
7843     if (attr >= cterm_attr_table.ga_len)	/* did ":syntax clear" */
7844 	return NULL;
7845     return &(CTERM_ATTR_ENTRY(attr));
7846 }
7847 
7848 #define LIST_ATTR   1
7849 #define LIST_STRING 2
7850 #define LIST_INT    3
7851 
7852     static void
7853 highlight_list_one(id)
7854     int		id;
7855 {
7856     struct hl_group	*sgp;
7857     int			didh = FALSE;
7858 
7859     sgp = &HL_TABLE()[id - 1];	    /* index is ID minus one */
7860 
7861     didh = highlight_list_arg(id, didh, LIST_ATTR,
7862 				    sgp->sg_term, NULL, "term");
7863     didh = highlight_list_arg(id, didh, LIST_STRING,
7864 				    0, sgp->sg_start, "start");
7865     didh = highlight_list_arg(id, didh, LIST_STRING,
7866 				    0, sgp->sg_stop, "stop");
7867 
7868     didh = highlight_list_arg(id, didh, LIST_ATTR,
7869 				    sgp->sg_cterm, NULL, "cterm");
7870     didh = highlight_list_arg(id, didh, LIST_INT,
7871 				    sgp->sg_cterm_fg, NULL, "ctermfg");
7872     didh = highlight_list_arg(id, didh, LIST_INT,
7873 				    sgp->sg_cterm_bg, NULL, "ctermbg");
7874 
7875 #ifdef FEAT_GUI
7876     didh = highlight_list_arg(id, didh, LIST_ATTR,
7877 				    sgp->sg_gui, NULL, "gui");
7878     didh = highlight_list_arg(id, didh, LIST_STRING,
7879 				    0, sgp->sg_gui_fg_name, "guifg");
7880     didh = highlight_list_arg(id, didh, LIST_STRING,
7881 				    0, sgp->sg_gui_bg_name, "guibg");
7882     didh = highlight_list_arg(id, didh, LIST_STRING,
7883 				    0, sgp->sg_gui_sp_name, "guisp");
7884     didh = highlight_list_arg(id, didh, LIST_STRING,
7885 				    0, sgp->sg_font_name, "font");
7886 #endif
7887 
7888     if (sgp->sg_link && !got_int)
7889     {
7890 	(void)syn_list_header(didh, 9999, id);
7891 	didh = TRUE;
7892 	msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7893 	msg_putchar(' ');
7894 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7895     }
7896 
7897 #ifdef FEAT_EVAL
7898     if (didh && p_verbose > 0)
7899 	last_set_msg(sgp->sg_scriptID);
7900 #endif
7901 }
7902 
7903     static int
7904 highlight_list_arg(id, didh, type, iarg, sarg, name)
7905     int		id;
7906     int		didh;
7907     int		type;
7908     int		iarg;
7909     char_u	*sarg;
7910     char	*name;
7911 {
7912     char_u	buf[100];
7913     char_u	*ts;
7914     int		i;
7915 
7916     if (got_int)
7917 	return FALSE;
7918     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7919     {
7920 	ts = buf;
7921 	if (type == LIST_INT)
7922 	    sprintf((char *)buf, "%d", iarg - 1);
7923 	else if (type == LIST_STRING)
7924 	    ts = sarg;
7925 	else /* type == LIST_ATTR */
7926 	{
7927 	    buf[0] = NUL;
7928 	    for (i = 0; hl_attr_table[i] != 0; ++i)
7929 	    {
7930 		if (iarg & hl_attr_table[i])
7931 		{
7932 		    if (buf[0] != NUL)
7933 			STRCAT(buf, ",");
7934 		    STRCAT(buf, hl_name_table[i]);
7935 		    iarg &= ~hl_attr_table[i];	    /* don't want "inverse" */
7936 		}
7937 	    }
7938 	}
7939 
7940 	(void)syn_list_header(didh,
7941 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7942 	didh = TRUE;
7943 	if (!got_int)
7944 	{
7945 	    MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7946 	    MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7947 	    msg_outtrans(ts);
7948 	}
7949     }
7950     return didh;
7951 }
7952 
7953 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7954 /*
7955  * Return "1" if highlight group "id" has attribute "flag".
7956  * Return NULL otherwise.
7957  */
7958     char_u *
7959 highlight_has_attr(id, flag, modec)
7960     int		id;
7961     int		flag;
7962     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
7963 {
7964     int		attr;
7965 
7966     if (id <= 0 || id > highlight_ga.ga_len)
7967 	return NULL;
7968 
7969 #ifdef FEAT_GUI
7970     if (modec == 'g')
7971 	attr = HL_TABLE()[id - 1].sg_gui;
7972     else
7973 #endif
7974 	 if (modec == 'c')
7975 	attr = HL_TABLE()[id - 1].sg_cterm;
7976     else
7977 	attr = HL_TABLE()[id - 1].sg_term;
7978 
7979     if (attr & flag)
7980 	return (char_u *)"1";
7981     return NULL;
7982 }
7983 #endif
7984 
7985 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
7986 /*
7987  * Return color name of highlight group "id".
7988  */
7989     char_u *
7990 highlight_color(id, what, modec)
7991     int		id;
7992     char_u	*what;	/* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
7993     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
7994 {
7995     static char_u	name[20];
7996     int			n;
7997     int			fg = FALSE;
7998 # ifdef FEAT_GUI
7999     int			sp = FALSE;
8000 # endif
8001 
8002     if (id <= 0 || id > highlight_ga.ga_len)
8003 	return NULL;
8004 
8005     if (TOLOWER_ASC(what[0]) == 'f')
8006 	fg = TRUE;
8007 # ifdef FEAT_GUI
8008     else if (TOLOWER_ASC(what[0]) == 's')
8009 	sp = TRUE;
8010     if (modec == 'g')
8011     {
8012 	/* return #RRGGBB form (only possible when GUI is running) */
8013 	if (gui.in_use && what[1] && what[2] == '#')
8014 	{
8015 	    guicolor_T		color;
8016 	    long_u		rgb;
8017 	    static char_u	buf[10];
8018 
8019 	    if (fg)
8020 		color = HL_TABLE()[id - 1].sg_gui_fg;
8021 	    else if (sp)
8022 		color = HL_TABLE()[id - 1].sg_gui_sp;
8023 	    else
8024 		color = HL_TABLE()[id - 1].sg_gui_bg;
8025 	    if (color == INVALCOLOR)
8026 		return NULL;
8027 	    rgb = gui_mch_get_rgb(color);
8028 	    sprintf((char *)buf, "#%02x%02x%02x",
8029 				      (unsigned)(rgb >> 16),
8030 				      (unsigned)(rgb >> 8) & 255,
8031 				      (unsigned)rgb & 255);
8032 	    return buf;
8033 	}
8034 	if (fg)
8035 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
8036 	if (sp)
8037 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
8038 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
8039     }
8040 # endif
8041     if (modec == 'c')
8042     {
8043 	if (fg)
8044 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8045 	else
8046 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8047 	sprintf((char *)name, "%d", n);
8048 	return name;
8049     }
8050     /* term doesn't have color */
8051     return NULL;
8052 }
8053 #endif
8054 
8055 #if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8056 	|| defined(PROTO)
8057 /*
8058  * Return color name of highlight group "id" as RGB value.
8059  */
8060     long_u
8061 highlight_gui_color_rgb(id, fg)
8062     int		id;
8063     int		fg;	/* TRUE = fg, FALSE = bg */
8064 {
8065     guicolor_T	color;
8066 
8067     if (id <= 0 || id > highlight_ga.ga_len)
8068 	return 0L;
8069 
8070     if (fg)
8071 	color = HL_TABLE()[id - 1].sg_gui_fg;
8072     else
8073 	color = HL_TABLE()[id - 1].sg_gui_bg;
8074 
8075     if (color == INVALCOLOR)
8076 	return 0L;
8077 
8078     return gui_mch_get_rgb(color);
8079 }
8080 #endif
8081 
8082 /*
8083  * Output the syntax list header.
8084  * Return TRUE when started a new line.
8085  */
8086     static int
8087 syn_list_header(did_header, outlen, id)
8088     int	    did_header;		/* did header already */
8089     int	    outlen;		/* length of string that comes */
8090     int	    id;			/* highlight group id */
8091 {
8092     int	    endcol = 19;
8093     int	    newline = TRUE;
8094 
8095     if (!did_header)
8096     {
8097 	msg_putchar('\n');
8098 	if (got_int)
8099 	    return TRUE;
8100 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
8101 	endcol = 15;
8102     }
8103     else if (msg_col + outlen + 1 >= Columns)
8104     {
8105 	msg_putchar('\n');
8106 	if (got_int)
8107 	    return TRUE;
8108     }
8109     else
8110     {
8111 	if (msg_col >= endcol)	/* wrap around is like starting a new line */
8112 	    newline = FALSE;
8113     }
8114 
8115     if (msg_col >= endcol)	/* output at least one space */
8116 	endcol = msg_col + 1;
8117     if (Columns <= endcol)	/* avoid hang for tiny window */
8118 	endcol = Columns - 1;
8119 
8120     msg_advance(endcol);
8121 
8122     /* Show "xxx" with the attributes. */
8123     if (!did_header)
8124     {
8125 	msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8126 	msg_putchar(' ');
8127     }
8128 
8129     return newline;
8130 }
8131 
8132 /*
8133  * Set the attribute numbers for a highlight group.
8134  * Called after one of the attributes has changed.
8135  */
8136     static void
8137 set_hl_attr(idx)
8138     int		idx;	    /* index in array */
8139 {
8140     attrentry_T		at_en;
8141     struct hl_group	*sgp = HL_TABLE() + idx;
8142 
8143     /* The "Normal" group doesn't need an attribute number */
8144     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8145 	return;
8146 
8147 #ifdef FEAT_GUI
8148     /*
8149      * For the GUI mode: If there are other than "normal" highlighting
8150      * attributes, need to allocate an attr number.
8151      */
8152     if (sgp->sg_gui_fg == INVALCOLOR
8153 	    && sgp->sg_gui_bg == INVALCOLOR
8154 	    && sgp->sg_gui_sp == INVALCOLOR
8155 	    && sgp->sg_font == NOFONT
8156 # ifdef FEAT_XFONTSET
8157 	    && sgp->sg_fontset == NOFONTSET
8158 # endif
8159 	    )
8160     {
8161 	sgp->sg_gui_attr = sgp->sg_gui;
8162     }
8163     else
8164     {
8165 	at_en.ae_attr = sgp->sg_gui;
8166 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8167 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
8168 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
8169 	at_en.ae_u.gui.font = sgp->sg_font;
8170 # ifdef FEAT_XFONTSET
8171 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
8172 # endif
8173 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8174     }
8175 #endif
8176     /*
8177      * For the term mode: If there are other than "normal" highlighting
8178      * attributes, need to allocate an attr number.
8179      */
8180     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8181 	sgp->sg_term_attr = sgp->sg_term;
8182     else
8183     {
8184 	at_en.ae_attr = sgp->sg_term;
8185 	at_en.ae_u.term.start = sgp->sg_start;
8186 	at_en.ae_u.term.stop = sgp->sg_stop;
8187 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8188     }
8189 
8190     /*
8191      * For the color term mode: If there are other than "normal"
8192      * highlighting attributes, need to allocate an attr number.
8193      */
8194     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8195 	sgp->sg_cterm_attr = sgp->sg_cterm;
8196     else
8197     {
8198 	at_en.ae_attr = sgp->sg_cterm;
8199 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8200 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8201 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8202     }
8203 }
8204 
8205 /*
8206  * Lookup a highlight group name and return it's ID.
8207  * If it is not found, 0 is returned.
8208  */
8209     int
8210 syn_name2id(name)
8211     char_u	*name;
8212 {
8213     int		i;
8214     char_u	name_u[200];
8215 
8216     /* Avoid using stricmp() too much, it's slow on some systems */
8217     /* Avoid alloc()/free(), these are slow too.  ID names over 200 chars
8218      * don't deserve to be found! */
8219     vim_strncpy(name_u, name, 199);
8220     vim_strup(name_u);
8221     for (i = highlight_ga.ga_len; --i >= 0; )
8222 	if (HL_TABLE()[i].sg_name_u != NULL
8223 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8224 	    break;
8225     return i + 1;
8226 }
8227 
8228 #if defined(FEAT_EVAL) || defined(PROTO)
8229 /*
8230  * Return TRUE if highlight group "name" exists.
8231  */
8232     int
8233 highlight_exists(name)
8234     char_u	*name;
8235 {
8236     return (syn_name2id(name) > 0);
8237 }
8238 #endif
8239 
8240 /*
8241  * Like syn_name2id(), but take a pointer + length argument.
8242  */
8243     int
8244 syn_namen2id(linep, len)
8245     char_u  *linep;
8246     int	    len;
8247 {
8248     char_u  *name;
8249     int	    id = 0;
8250 
8251     name = vim_strnsave(linep, len);
8252     if (name != NULL)
8253     {
8254 	id = syn_name2id(name);
8255 	vim_free(name);
8256     }
8257     return id;
8258 }
8259 
8260 /*
8261  * Find highlight group name in the table and return it's ID.
8262  * The argument is a pointer to the name and the length of the name.
8263  * If it doesn't exist yet, a new entry is created.
8264  * Return 0 for failure.
8265  */
8266     int
8267 syn_check_group(pp, len)
8268     char_u		*pp;
8269     int			len;
8270 {
8271     int	    id;
8272     char_u  *name;
8273 
8274     name = vim_strnsave(pp, len);
8275     if (name == NULL)
8276 	return 0;
8277 
8278     id = syn_name2id(name);
8279     if (id == 0)			/* doesn't exist yet */
8280 	id = syn_add_group(name);
8281     else
8282 	vim_free(name);
8283     return id;
8284 }
8285 
8286 /*
8287  * Add new highlight group and return it's ID.
8288  * "name" must be an allocated string, it will be consumed.
8289  * Return 0 for failure.
8290  */
8291     static int
8292 syn_add_group(name)
8293     char_u	*name;
8294 {
8295     char_u	*p;
8296 
8297     /* Check that the name is ASCII letters, digits and underscore. */
8298     for (p = name; *p != NUL; ++p)
8299     {
8300 	if (!vim_isprintc(*p))
8301 	{
8302 	    EMSG(_("E669: Unprintable character in group name"));
8303 	    return 0;
8304 	}
8305 	else if (!ASCII_ISALNUM(*p) && *p != '_')
8306 	{
8307 	    /* This is an error, but since there previously was no check only
8308 	     * give a warning. */
8309 	    msg_source(hl_attr(HLF_W));
8310 	    MSG(_("W18: Invalid character in group name"));
8311 	    break;
8312 	}
8313     }
8314 
8315     /*
8316      * First call for this growarray: init growing array.
8317      */
8318     if (highlight_ga.ga_data == NULL)
8319     {
8320 	highlight_ga.ga_itemsize = sizeof(struct hl_group);
8321 	highlight_ga.ga_growsize = 10;
8322     }
8323 
8324     /*
8325      * Make room for at least one other syntax_highlight entry.
8326      */
8327     if (ga_grow(&highlight_ga, 1) == FAIL)
8328     {
8329 	vim_free(name);
8330 	return 0;
8331     }
8332 
8333     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8334     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8335     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8336 #ifdef FEAT_GUI
8337     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8338     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
8339     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
8340 #endif
8341     ++highlight_ga.ga_len;
8342 
8343     return highlight_ga.ga_len;		    /* ID is index plus one */
8344 }
8345 
8346 /*
8347  * When, just after calling syn_add_group(), an error is discovered, this
8348  * function deletes the new name.
8349  */
8350     static void
8351 syn_unadd_group()
8352 {
8353     --highlight_ga.ga_len;
8354     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8355     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8356 }
8357 
8358 /*
8359  * Translate a group ID to highlight attributes.
8360  */
8361     int
8362 syn_id2attr(hl_id)
8363     int			hl_id;
8364 {
8365     int			attr;
8366     struct hl_group	*sgp;
8367 
8368     hl_id = syn_get_final_id(hl_id);
8369     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8370 
8371 #ifdef FEAT_GUI
8372     /*
8373      * Only use GUI attr when the GUI is being used.
8374      */
8375     if (gui.in_use)
8376 	attr = sgp->sg_gui_attr;
8377     else
8378 #endif
8379 	if (t_colors > 1)
8380 	    attr = sgp->sg_cterm_attr;
8381 	else
8382 	    attr = sgp->sg_term_attr;
8383 
8384     return attr;
8385 }
8386 
8387 #ifdef FEAT_GUI
8388 /*
8389  * Get the GUI colors and attributes for a group ID.
8390  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8391  */
8392     int
8393 syn_id2colors(hl_id, fgp, bgp)
8394     int		hl_id;
8395     guicolor_T	*fgp;
8396     guicolor_T	*bgp;
8397 {
8398     struct hl_group	*sgp;
8399 
8400     hl_id = syn_get_final_id(hl_id);
8401     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8402 
8403     *fgp = sgp->sg_gui_fg;
8404     *bgp = sgp->sg_gui_bg;
8405     return sgp->sg_gui;
8406 }
8407 #endif
8408 
8409 /*
8410  * Translate a group ID to the final group ID (following links).
8411  */
8412     int
8413 syn_get_final_id(hl_id)
8414     int			hl_id;
8415 {
8416     int			count;
8417     struct hl_group	*sgp;
8418 
8419     if (hl_id > highlight_ga.ga_len || hl_id < 1)
8420 	return 0;			/* Can be called from eval!! */
8421 
8422     /*
8423      * Follow links until there is no more.
8424      * Look out for loops!  Break after 100 links.
8425      */
8426     for (count = 100; --count >= 0; )
8427     {
8428 	sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
8429 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8430 	    break;
8431 	hl_id = sgp->sg_link;
8432     }
8433 
8434     return hl_id;
8435 }
8436 
8437 #ifdef FEAT_GUI
8438 /*
8439  * Call this function just after the GUI has started.
8440  * It finds the font and color handles for the highlighting groups.
8441  */
8442     void
8443 highlight_gui_started()
8444 {
8445     int	    idx;
8446 
8447     /* First get the colors from the "Normal" and "Menu" group, if set */
8448     set_normal_colors();
8449 
8450     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8451 	gui_do_one_color(idx, FALSE, FALSE);
8452 
8453     highlight_changed();
8454 }
8455 
8456     static void
8457 gui_do_one_color(idx, do_menu, do_tooltip)
8458     int		idx;
8459     int		do_menu;	/* TRUE: might set the menu font */
8460     int		do_tooltip;	/* TRUE: might set the tooltip font */
8461 {
8462     int		didit = FALSE;
8463 
8464     if (HL_TABLE()[idx].sg_font_name != NULL)
8465     {
8466 	hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8467 		   do_tooltip);
8468 	didit = TRUE;
8469     }
8470     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8471     {
8472 	HL_TABLE()[idx].sg_gui_fg =
8473 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8474 	didit = TRUE;
8475     }
8476     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8477     {
8478 	HL_TABLE()[idx].sg_gui_bg =
8479 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8480 	didit = TRUE;
8481     }
8482     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8483     {
8484 	HL_TABLE()[idx].sg_gui_sp =
8485 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8486 	didit = TRUE;
8487     }
8488     if (didit)	/* need to get a new attr number */
8489 	set_hl_attr(idx);
8490 }
8491 
8492 #endif
8493 
8494 /*
8495  * Translate the 'highlight' option into attributes in highlight_attr[] and
8496  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
8497  * corresponding highlights to use on top of HLF_SNC is computed.
8498  * Called only when the 'highlight' option has been changed and upon first
8499  * screen redraw after any :highlight command.
8500  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
8501  */
8502     int
8503 highlight_changed()
8504 {
8505     int		hlf;
8506     int		i;
8507     char_u	*p;
8508     int		attr;
8509     char_u	*end;
8510     int		id;
8511 #ifdef USER_HIGHLIGHT
8512     char_u      userhl[10];
8513 # ifdef FEAT_STL_OPT
8514     int		id_SNC = -1;
8515     int		id_S = -1;
8516     int		hlcnt;
8517 # endif
8518 #endif
8519     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
8520 
8521     need_highlight_changed = FALSE;
8522 
8523     /*
8524      * Clear all attributes.
8525      */
8526     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8527 	highlight_attr[hlf] = 0;
8528 
8529     /*
8530      * First set all attributes to their default value.
8531      * Then use the attributes from the 'highlight' option.
8532      */
8533     for (i = 0; i < 2; ++i)
8534     {
8535 	if (i)
8536 	    p = p_hl;
8537 	else
8538 	    p = get_highlight_default();
8539 	if (p == NULL)	    /* just in case */
8540 	    continue;
8541 
8542 	while (*p)
8543 	{
8544 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8545 		if (hl_flags[hlf] == *p)
8546 		    break;
8547 	    ++p;
8548 	    if (hlf == (int)HLF_COUNT || *p == NUL)
8549 		return FAIL;
8550 
8551 	    /*
8552 	     * Allow several hl_flags to be combined, like "bu" for
8553 	     * bold-underlined.
8554 	     */
8555 	    attr = 0;
8556 	    for ( ; *p && *p != ','; ++p)	    /* parse upto comma */
8557 	    {
8558 		if (vim_iswhite(*p))		    /* ignore white space */
8559 		    continue;
8560 
8561 		if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
8562 		    return FAIL;
8563 
8564 		switch (*p)
8565 		{
8566 		    case 'b':	attr |= HL_BOLD;
8567 				break;
8568 		    case 'i':	attr |= HL_ITALIC;
8569 				break;
8570 		    case '-':
8571 		    case 'n':			    /* no highlighting */
8572 				break;
8573 		    case 'r':	attr |= HL_INVERSE;
8574 				break;
8575 		    case 's':	attr |= HL_STANDOUT;
8576 				break;
8577 		    case 'u':	attr |= HL_UNDERLINE;
8578 				break;
8579 		    case 'c':	attr |= HL_UNDERCURL;
8580 				break;
8581 		    case ':':	++p;		    /* highlight group name */
8582 				if (attr || *p == NUL)	 /* no combinations */
8583 				    return FAIL;
8584 				end = vim_strchr(p, ',');
8585 				if (end == NULL)
8586 				    end = p + STRLEN(p);
8587 				id = syn_check_group(p, (int)(end - p));
8588 				if (id == 0)
8589 				    return FAIL;
8590 				attr = syn_id2attr(id);
8591 				p = end - 1;
8592 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8593 				if (hlf == (int)HLF_SNC)
8594 				    id_SNC = syn_get_final_id(id);
8595 				else if (hlf == (int)HLF_S)
8596 				    id_S = syn_get_final_id(id);
8597 #endif
8598 				break;
8599 		    default:	return FAIL;
8600 		}
8601 	    }
8602 	    highlight_attr[hlf] = attr;
8603 
8604 	    p = skip_to_option_part(p);	    /* skip comma and spaces */
8605 	}
8606     }
8607 
8608 #ifdef USER_HIGHLIGHT
8609     /* Setup the user highlights
8610      *
8611      * Temporarily  utilize 10 more hl entries.  Have to be in there
8612      * simultaneously in case of table overflows in get_attr_entry()
8613      */
8614 # ifdef FEAT_STL_OPT
8615     if (ga_grow(&highlight_ga, 10) == FAIL)
8616 	return FAIL;
8617     hlcnt = highlight_ga.ga_len;
8618     if (id_S == 0)
8619     {		    /* Make sure id_S is always valid to simplify code below */
8620 	memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8621 	HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8622 	id_S = hlcnt + 10;
8623     }
8624 # endif
8625     for (i = 0; i < 9; i++)
8626     {
8627 	sprintf((char *)userhl, "User%d", i + 1);
8628 	id = syn_name2id(userhl);
8629 	if (id == 0)
8630 	{
8631 	    highlight_user[i] = 0;
8632 # ifdef FEAT_STL_OPT
8633 	    highlight_stlnc[i] = 0;
8634 # endif
8635 	}
8636 	else
8637 	{
8638 # ifdef FEAT_STL_OPT
8639 	    struct hl_group *hlt = HL_TABLE();
8640 # endif
8641 
8642 	    highlight_user[i] = syn_id2attr(id);
8643 # ifdef FEAT_STL_OPT
8644 	    if (id_SNC == 0)
8645 	    {
8646 		memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8647 		hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8648 		hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8649 #  ifdef FEAT_GUI
8650 		hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8651 #  endif
8652 	    }
8653 	    else
8654 		mch_memmove(&hlt[hlcnt + i],
8655 			    &hlt[id_SNC - 1],
8656 			    sizeof(struct hl_group));
8657 	    hlt[hlcnt + i].sg_link = 0;
8658 
8659 	    /* Apply difference between UserX and HLF_S to HLF_SNC */
8660 	    hlt[hlcnt + i].sg_term ^=
8661 		hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8662 	    if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8663 		hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8664 	    if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8665 		hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8666 	    hlt[hlcnt + i].sg_cterm ^=
8667 		hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8668 	    if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8669 		hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8670 	    if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8671 		hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8672 #  ifdef FEAT_GUI
8673 	    hlt[hlcnt + i].sg_gui ^=
8674 		hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8675 	    if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8676 		hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8677 	    if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8678 		hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
8679 	    if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8680 		hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
8681 	    if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8682 		hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8683 #   ifdef FEAT_XFONTSET
8684 	    if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8685 		hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8686 #   endif
8687 #  endif
8688 	    highlight_ga.ga_len = hlcnt + i + 1;
8689 	    set_hl_attr(hlcnt + i);	/* At long last we can apply */
8690 	    highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8691 # endif
8692 	}
8693     }
8694 # ifdef FEAT_STL_OPT
8695     highlight_ga.ga_len = hlcnt;
8696 # endif
8697 
8698 #endif /* USER_HIGHLIGHT */
8699 
8700     return OK;
8701 }
8702 
8703 #ifdef FEAT_CMDL_COMPL
8704 
8705 static void highlight_list __ARGS((void));
8706 static void highlight_list_two __ARGS((int cnt, int attr));
8707 
8708 /*
8709  * Handle command line completion for :highlight command.
8710  */
8711     void
8712 set_context_in_highlight_cmd(xp, arg)
8713     expand_T	*xp;
8714     char_u	*arg;
8715 {
8716     char_u	*p;
8717 
8718     /* Default: expand group names */
8719     xp->xp_context = EXPAND_HIGHLIGHT;
8720     xp->xp_pattern = arg;
8721     include_link = TRUE;
8722     include_default = TRUE;
8723 
8724     /* (part of) subcommand already typed */
8725     if (*arg != NUL)
8726     {
8727 	p = skiptowhite(arg);
8728 	if (*p != NUL)			/* past "default" or group name */
8729 	{
8730 	    include_default = FALSE;
8731 	    if (STRNCMP("default", arg, p - arg) == 0)
8732 	    {
8733 		arg = skipwhite(p);
8734 		xp->xp_pattern = arg;
8735 		p = skiptowhite(arg);
8736 	    }
8737 	    if (*p != NUL)			/* past group name */
8738 	    {
8739 		include_link = FALSE;
8740 		if (arg[1] == 'i' && arg[0] == 'N')
8741 		    highlight_list();
8742 		if (STRNCMP("link", arg, p - arg) == 0
8743 			|| STRNCMP("clear", arg, p - arg) == 0)
8744 		{
8745 		    xp->xp_pattern = skipwhite(p);
8746 		    p = skiptowhite(xp->xp_pattern);
8747 		    if (*p != NUL)		/* past first group name */
8748 		    {
8749 			xp->xp_pattern = skipwhite(p);
8750 			p = skiptowhite(xp->xp_pattern);
8751 		    }
8752 		}
8753 		if (*p != NUL)			/* past group name(s) */
8754 		    xp->xp_context = EXPAND_NOTHING;
8755 	    }
8756 	}
8757     }
8758 }
8759 
8760 /*
8761  * List highlighting matches in a nice way.
8762  */
8763     static void
8764 highlight_list()
8765 {
8766     int		i;
8767 
8768     for (i = 10; --i >= 0; )
8769 	highlight_list_two(i, hl_attr(HLF_D));
8770     for (i = 40; --i >= 0; )
8771 	highlight_list_two(99, 0);
8772 }
8773 
8774     static void
8775 highlight_list_two(cnt, attr)
8776     int	    cnt;
8777     int	    attr;
8778 {
8779     msg_puts_attr((char_u *)("N \bI \b!  \b" + cnt / 11), attr);
8780     msg_clr_eos();
8781     out_flush();
8782     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8783 }
8784 
8785 #endif /* FEAT_CMDL_COMPL */
8786 
8787 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8788     || defined(FEAT_SIGNS) || defined(PROTO)
8789 /*
8790  * Function given to ExpandGeneric() to obtain the list of group names.
8791  * Also used for synIDattr() function.
8792  */
8793 /*ARGSUSED*/
8794     char_u *
8795 get_highlight_name(xp, idx)
8796     expand_T	*xp;
8797     int		idx;
8798 {
8799     if (idx == highlight_ga.ga_len
8800 #ifdef FEAT_CMDL_COMPL
8801 	    && include_link
8802 #endif
8803 	    )
8804 	return (char_u *)"link";
8805     if (idx == highlight_ga.ga_len + 1
8806 #ifdef FEAT_CMDL_COMPL
8807 	    && include_link
8808 #endif
8809 	    )
8810 	return (char_u *)"clear";
8811     if (idx == highlight_ga.ga_len + 2
8812 #ifdef FEAT_CMDL_COMPL
8813 	    && include_default
8814 #endif
8815 	    )
8816 	return (char_u *)"default";
8817     if (idx < 0 || idx >= highlight_ga.ga_len)
8818 	return NULL;
8819     return HL_TABLE()[idx].sg_name;
8820 }
8821 #endif
8822 
8823 #ifdef FEAT_GUI
8824 /*
8825  * Free all the highlight group fonts.
8826  * Used when quitting for systems which need it.
8827  */
8828     void
8829 free_highlight_fonts()
8830 {
8831     int	    idx;
8832 
8833     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8834     {
8835 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
8836 	HL_TABLE()[idx].sg_font = NOFONT;
8837 # ifdef FEAT_XFONTSET
8838 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8839 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
8840 # endif
8841     }
8842 
8843     gui_mch_free_font(gui.norm_font);
8844 # ifdef FEAT_XFONTSET
8845     gui_mch_free_fontset(gui.fontset);
8846 # endif
8847 # ifndef HAVE_GTK2
8848     gui_mch_free_font(gui.bold_font);
8849     gui_mch_free_font(gui.ital_font);
8850     gui_mch_free_font(gui.boldital_font);
8851 # endif
8852 }
8853 #endif
8854 
8855 /**************************************
8856  *  End of Highlighting stuff	      *
8857  **************************************/
8858