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