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