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