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