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