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