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