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