xref: /vim-8.2.3635/src/syntax.c (revision abfa9efb)
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 		int line_len;
3026 
3027 		/* Add offset to skip pattern match */
3028 		syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3029 
3030 		/* If the skip pattern goes on to the next line, there is no
3031 		 * match with an end pattern in this line. */
3032 		if (pos.lnum > startpos->lnum)
3033 		    break;
3034 
3035 		line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
3036 		line_len = (int)STRLEN(line);
3037 
3038 		/* take care of an empty match or negative offset */
3039 		if (pos.col <= matchcol)
3040 		    ++matchcol;
3041 		else if (pos.col <= regmatch.endpos[0].col)
3042 		    matchcol = pos.col;
3043 		else
3044 		    /* Be careful not to jump over the NUL at the end-of-line */
3045 		    for (matchcol = regmatch.endpos[0].col;
3046 			    matchcol < line_len && matchcol < pos.col;
3047 								   ++matchcol)
3048 			;
3049 
3050 		/* if the skip pattern includes end-of-line, break here */
3051 		if (matchcol >= line_len)
3052 		    break;
3053 
3054 		continue;	    /* start with first end pattern again */
3055 	    }
3056 	}
3057 
3058 	/*
3059 	 * Match from start pattern to end pattern.
3060 	 * Correct for match and highlight offset of end pattern.
3061 	 */
3062 	spp = &(SYN_ITEMS(syn_block)[best_idx]);
3063 	syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3064 	/* can't end before the start */
3065 	if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3066 	    m_endpos->col = startpos->col;
3067 
3068 	syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3069 	/* can't end before the start */
3070 	if (end_endpos->lnum == startpos->lnum
3071 					   && end_endpos->col < startpos->col)
3072 	    end_endpos->col = startpos->col;
3073 	/* can't end after the match */
3074 	limit_pos(end_endpos, m_endpos);
3075 
3076 	/*
3077 	 * If the end group is highlighted differently, adjust the pointers.
3078 	 */
3079 	if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3080 	{
3081 	    *end_idx = best_idx;
3082 	    if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3083 	    {
3084 		hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3085 		hl_endpos->col = best_regmatch.endpos[0].col;
3086 	    }
3087 	    else
3088 	    {
3089 		hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3090 		hl_endpos->col = best_regmatch.startpos[0].col;
3091 	    }
3092 	    hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3093 
3094 	    /* can't end before the start */
3095 	    if (hl_endpos->lnum == startpos->lnum
3096 					    && hl_endpos->col < startpos->col)
3097 		hl_endpos->col = startpos->col;
3098 	    limit_pos(hl_endpos, m_endpos);
3099 
3100 	    /* now the match ends where the highlighting ends, it is turned
3101 	     * into the matchgroup for the end */
3102 	    *m_endpos = *hl_endpos;
3103 	}
3104 	else
3105 	{
3106 	    *end_idx = 0;
3107 	    *hl_endpos = *end_endpos;
3108 	}
3109 
3110 	*flagsp = spp->sp_flags;
3111 
3112 	had_match = TRUE;
3113 	break;
3114     }
3115 
3116     /* no match for an END pattern in this line */
3117     if (!had_match)
3118 	m_endpos->lnum = 0;
3119 
3120     /* Remove external matches. */
3121     unref_extmatch(re_extmatch_in);
3122     re_extmatch_in = NULL;
3123 }
3124 
3125 /*
3126  * Limit "pos" not to be after "limit".
3127  */
3128     static void
3129 limit_pos(pos, limit)
3130     lpos_T	*pos;
3131     lpos_T	*limit;
3132 {
3133     if (pos->lnum > limit->lnum)
3134 	*pos = *limit;
3135     else if (pos->lnum == limit->lnum && pos->col > limit->col)
3136 	pos->col = limit->col;
3137 }
3138 
3139 /*
3140  * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3141  */
3142     static void
3143 limit_pos_zero(pos, limit)
3144     lpos_T	*pos;
3145     lpos_T	*limit;
3146 {
3147     if (pos->lnum == 0)
3148 	*pos = *limit;
3149     else
3150 	limit_pos(pos, limit);
3151 }
3152 
3153 /*
3154  * Add offset to matched text for end of match or highlight.
3155  */
3156     static void
3157 syn_add_end_off(result, regmatch, spp, idx, extra)
3158     lpos_T	*result;	/* returned position */
3159     regmmatch_T	*regmatch;	/* start/end of match */
3160     synpat_T	*spp;		/* matched pattern */
3161     int		idx;		/* index of offset */
3162     int		extra;		/* extra chars for offset to start */
3163 {
3164     int		col;
3165     int		off;
3166     char_u	*base;
3167     char_u	*p;
3168 
3169     if (spp->sp_off_flags & (1 << idx))
3170     {
3171 	result->lnum = regmatch->startpos[0].lnum;
3172 	col = regmatch->startpos[0].col;
3173 	off = spp->sp_offsets[idx] + extra;
3174     }
3175     else
3176     {
3177 	result->lnum = regmatch->endpos[0].lnum;
3178 	col = regmatch->endpos[0].col;
3179 	off = spp->sp_offsets[idx];
3180     }
3181     /* Don't go past the end of the line.  Matters for "rs=e+2" when there
3182      * is a matchgroup. Watch out for match with last NL in the buffer. */
3183     if (result->lnum > syn_buf->b_ml.ml_line_count)
3184 	col = 0;
3185     else if (off != 0)
3186     {
3187 	base = ml_get_buf(syn_buf, result->lnum, FALSE);
3188 	p = base + col;
3189 	if (off > 0)
3190 	{
3191 	    while (off-- > 0 && *p != NUL)
3192 		mb_ptr_adv(p);
3193 	}
3194 	else if (off < 0)
3195 	{
3196 	    while (off++ < 0 && base < p)
3197 		mb_ptr_back(base, p);
3198 	}
3199 	col = (int)(p - base);
3200     }
3201     result->col = col;
3202 }
3203 
3204 /*
3205  * Add offset to matched text for start of match or highlight.
3206  * Avoid resulting column to become negative.
3207  */
3208     static void
3209 syn_add_start_off(result, regmatch, spp, idx, extra)
3210     lpos_T	*result;	/* returned position */
3211     regmmatch_T	*regmatch;	/* start/end of match */
3212     synpat_T	*spp;
3213     int		idx;
3214     int		extra;	    /* extra chars for offset to end */
3215 {
3216     int		col;
3217     int		off;
3218     char_u	*base;
3219     char_u	*p;
3220 
3221     if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3222     {
3223 	result->lnum = regmatch->endpos[0].lnum;
3224 	col = regmatch->endpos[0].col;
3225 	off = spp->sp_offsets[idx] + extra;
3226     }
3227     else
3228     {
3229 	result->lnum = regmatch->startpos[0].lnum;
3230 	col = regmatch->startpos[0].col;
3231 	off = spp->sp_offsets[idx];
3232     }
3233     if (result->lnum > syn_buf->b_ml.ml_line_count)
3234     {
3235 	/* a "\n" at the end of the pattern may take us below the last line */
3236 	result->lnum = syn_buf->b_ml.ml_line_count;
3237 	col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
3238     }
3239     if (off != 0)
3240     {
3241 	base = ml_get_buf(syn_buf, result->lnum, FALSE);
3242 	p = base + col;
3243 	if (off > 0)
3244 	{
3245 	    while (off-- && *p != NUL)
3246 		mb_ptr_adv(p);
3247 	}
3248 	else if (off < 0)
3249 	{
3250 	    while (off++ && base < p)
3251 		mb_ptr_back(base, p);
3252 	}
3253 	col = (int)(p - base);
3254     }
3255     result->col = col;
3256 }
3257 
3258 /*
3259  * Get current line in syntax buffer.
3260  */
3261     static char_u *
3262 syn_getcurline()
3263 {
3264     return ml_get_buf(syn_buf, current_lnum, FALSE);
3265 }
3266 
3267 /*
3268  * Call vim_regexec() to find a match with "rmp" in "syn_buf".
3269  * Returns TRUE when there is a match.
3270  */
3271     static int
3272 syn_regexec(rmp, lnum, col, st)
3273     regmmatch_T	*rmp;
3274     linenr_T	lnum;
3275     colnr_T	col;
3276     syn_time_T  *st UNUSED;
3277 {
3278     int r;
3279 #ifdef FEAT_PROFILE
3280     proftime_T	pt;
3281 
3282     if (syn_time_on)
3283 	profile_start(&pt);
3284 #endif
3285 
3286     rmp->rmm_maxcol = syn_buf->b_p_smc;
3287     r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3288 
3289 #ifdef FEAT_PROFILE
3290     if (syn_time_on)
3291     {
3292 	profile_end(&pt);
3293 	profile_add(&st->total, &pt);
3294 	if (profile_cmp(&pt, &st->slowest) < 0)
3295 	    st->slowest = pt;
3296 	++st->count;
3297 	if (r > 0)
3298 	    ++st->match;
3299     }
3300 #endif
3301 
3302     if (r > 0)
3303     {
3304 	rmp->startpos[0].lnum += lnum;
3305 	rmp->endpos[0].lnum += lnum;
3306 	return TRUE;
3307     }
3308     return FALSE;
3309 }
3310 
3311 /*
3312  * Check one position in a line for a matching keyword.
3313  * The caller must check if a keyword can start at startcol.
3314  * Return it's ID if found, 0 otherwise.
3315  */
3316     static int
3317 check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
3318     char_u	*line;
3319     int		startcol;	/* position in line to check for keyword */
3320     int		*endcolp;	/* return: character after found keyword */
3321     long	*flagsp;	/* return: flags of matching keyword */
3322     short	**next_listp;	/* return: next_list of matching keyword */
3323     stateitem_T	*cur_si;	/* item at the top of the stack */
3324     int		*ccharp UNUSED;	/* conceal substitution char */
3325 {
3326     keyentry_T	*kp;
3327     char_u	*kwp;
3328     int		round;
3329     int		kwlen;
3330     char_u	keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
3331     hashtab_T	*ht;
3332     hashitem_T	*hi;
3333 
3334     /* Find first character after the keyword.  First character was already
3335      * checked. */
3336     kwp = line + startcol;
3337     kwlen = 0;
3338     do
3339     {
3340 #ifdef FEAT_MBYTE
3341 	if (has_mbyte)
3342 	    kwlen += (*mb_ptr2len)(kwp + kwlen);
3343 	else
3344 #endif
3345 	    ++kwlen;
3346     }
3347     while (vim_iswordp_buf(kwp + kwlen, syn_buf));
3348 
3349     if (kwlen > MAXKEYWLEN)
3350 	return 0;
3351 
3352     /*
3353      * Must make a copy of the keyword, so we can add a NUL and make it
3354      * lowercase.
3355      */
3356     vim_strncpy(keyword, kwp, kwlen);
3357 
3358     /*
3359      * Try twice:
3360      * 1. matching case
3361      * 2. ignoring case
3362      */
3363     for (round = 1; round <= 2; ++round)
3364     {
3365 	ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
3366 	if (ht->ht_used == 0)
3367 	    continue;
3368 	if (round == 2)	/* ignore case */
3369 	    (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
3370 
3371 	/*
3372 	 * Find keywords that match.  There can be several with different
3373 	 * attributes.
3374 	 * When current_next_list is non-zero accept only that group, otherwise:
3375 	 *  Accept a not-contained keyword at toplevel.
3376 	 *  Accept a keyword at other levels only if it is in the contains list.
3377 	 */
3378 	hi = hash_find(ht, keyword);
3379 	if (!HASHITEM_EMPTY(hi))
3380 	    for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
3381 	    {
3382 		if (current_next_list != 0
3383 			? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3384 			: (cur_si == NULL
3385 			    ? !(kp->flags & HL_CONTAINED)
3386 			    : in_id_list(cur_si, cur_si->si_cont_list,
3387 				      &kp->k_syn, kp->flags & HL_CONTAINED)))
3388 		{
3389 		    *endcolp = startcol + kwlen;
3390 		    *flagsp = kp->flags;
3391 		    *next_listp = kp->next_list;
3392 #ifdef FEAT_CONCEAL
3393 		    *ccharp = kp->k_char;
3394 #endif
3395 		    return kp->k_syn.id;
3396 		}
3397 	    }
3398     }
3399     return 0;
3400 }
3401 
3402 /*
3403  * Handle ":syntax conceal" command.
3404  */
3405     static void
3406 syn_cmd_conceal(eap, syncing)
3407     exarg_T	*eap UNUSED;
3408     int		syncing UNUSED;
3409 {
3410 #ifdef FEAT_CONCEAL
3411     char_u	*arg = eap->arg;
3412     char_u	*next;
3413 
3414     eap->nextcmd = find_nextcmd(arg);
3415     if (eap->skip)
3416 	return;
3417 
3418     next = skiptowhite(arg);
3419     if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3420 	curwin->w_s->b_syn_conceal = TRUE;
3421     else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3422 	curwin->w_s->b_syn_conceal = FALSE;
3423     else
3424 	EMSG2(_("E390: Illegal argument: %s"), arg);
3425 #endif
3426 }
3427 
3428 /*
3429  * Handle ":syntax case" command.
3430  */
3431     static void
3432 syn_cmd_case(eap, syncing)
3433     exarg_T	*eap;
3434     int		syncing UNUSED;
3435 {
3436     char_u	*arg = eap->arg;
3437     char_u	*next;
3438 
3439     eap->nextcmd = find_nextcmd(arg);
3440     if (eap->skip)
3441 	return;
3442 
3443     next = skiptowhite(arg);
3444     if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3445 	curwin->w_s->b_syn_ic = FALSE;
3446     else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3447 	curwin->w_s->b_syn_ic = TRUE;
3448     else
3449 	EMSG2(_("E390: Illegal argument: %s"), arg);
3450 }
3451 
3452 /*
3453  * Handle ":syntax spell" command.
3454  */
3455     static void
3456 syn_cmd_spell(eap, syncing)
3457     exarg_T	*eap;
3458     int		syncing UNUSED;
3459 {
3460     char_u	*arg = eap->arg;
3461     char_u	*next;
3462 
3463     eap->nextcmd = find_nextcmd(arg);
3464     if (eap->skip)
3465 	return;
3466 
3467     next = skiptowhite(arg);
3468     if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3469 	curwin->w_s->b_syn_spell = SYNSPL_TOP;
3470     else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3471 	curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
3472     else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
3473 	curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
3474     else
3475     {
3476 	EMSG2(_("E390: Illegal argument: %s"), arg);
3477 	return;
3478     }
3479 
3480     /* assume spell checking changed, force a redraw */
3481     redraw_win_later(curwin, NOT_VALID);
3482 }
3483 
3484 /*
3485  * Clear all syntax info for one buffer.
3486  */
3487     void
3488 syntax_clear(block)
3489     synblock_T	*block;
3490 {
3491     int i;
3492 
3493     block->b_syn_error = FALSE;	    /* clear previous error */
3494     block->b_syn_ic = FALSE;	    /* Use case, by default */
3495     block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3496     block->b_syn_containedin = FALSE;
3497 
3498     /* free the keywords */
3499     clear_keywtab(&block->b_keywtab);
3500     clear_keywtab(&block->b_keywtab_ic);
3501 
3502     /* free the syntax patterns */
3503     for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3504 	syn_clear_pattern(block, i);
3505     ga_clear(&block->b_syn_patterns);
3506 
3507     /* free the syntax clusters */
3508     for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3509 	syn_clear_cluster(block, i);
3510     ga_clear(&block->b_syn_clusters);
3511     block->b_spell_cluster_id = 0;
3512     block->b_nospell_cluster_id = 0;
3513 
3514     block->b_syn_sync_flags = 0;
3515     block->b_syn_sync_minlines = 0;
3516     block->b_syn_sync_maxlines = 0;
3517     block->b_syn_sync_linebreaks = 0;
3518 
3519     vim_regfree(block->b_syn_linecont_prog);
3520     block->b_syn_linecont_prog = NULL;
3521     vim_free(block->b_syn_linecont_pat);
3522     block->b_syn_linecont_pat = NULL;
3523 #ifdef FEAT_FOLDING
3524     block->b_syn_folditems = 0;
3525 #endif
3526 
3527     /* free the stored states */
3528     syn_stack_free_all(block);
3529     invalidate_current_state();
3530 
3531     /* Reset the counter for ":syn include" */
3532     running_syn_inc_tag = 0;
3533 }
3534 
3535 /*
3536  * Get rid of ownsyntax for window "wp".
3537  */
3538     void
3539 reset_synblock(wp)
3540     win_T *wp;
3541 {
3542     if (wp->w_s != &wp->w_buffer->b_s)
3543     {
3544 	syntax_clear(wp->w_s);
3545 	vim_free(wp->w_s);
3546 	wp->w_s = &wp->w_buffer->b_s;
3547     }
3548 }
3549 
3550 /*
3551  * Clear syncing info for one buffer.
3552  */
3553     static void
3554 syntax_sync_clear()
3555 {
3556     int i;
3557 
3558     /* free the syntax patterns */
3559     for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3560 	if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3561 	    syn_remove_pattern(curwin->w_s, i);
3562 
3563     curwin->w_s->b_syn_sync_flags = 0;
3564     curwin->w_s->b_syn_sync_minlines = 0;
3565     curwin->w_s->b_syn_sync_maxlines = 0;
3566     curwin->w_s->b_syn_sync_linebreaks = 0;
3567 
3568     vim_regfree(curwin->w_s->b_syn_linecont_prog);
3569     curwin->w_s->b_syn_linecont_prog = NULL;
3570     vim_free(curwin->w_s->b_syn_linecont_pat);
3571     curwin->w_s->b_syn_linecont_pat = NULL;
3572 
3573     syn_stack_free_all(curwin->w_s);		/* Need to recompute all syntax. */
3574 }
3575 
3576 /*
3577  * Remove one pattern from the buffer's pattern list.
3578  */
3579     static void
3580 syn_remove_pattern(block, idx)
3581     synblock_T	*block;
3582     int		idx;
3583 {
3584     synpat_T	*spp;
3585 
3586     spp = &(SYN_ITEMS(block)[idx]);
3587 #ifdef FEAT_FOLDING
3588     if (spp->sp_flags & HL_FOLD)
3589 	--block->b_syn_folditems;
3590 #endif
3591     syn_clear_pattern(block, idx);
3592     mch_memmove(spp, spp + 1,
3593 		   sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3594     --block->b_syn_patterns.ga_len;
3595 }
3596 
3597 /*
3598  * Clear and free one syntax pattern.  When clearing all, must be called from
3599  * last to first!
3600  */
3601     static void
3602 syn_clear_pattern(block, i)
3603     synblock_T	*block;
3604     int		i;
3605 {
3606     vim_free(SYN_ITEMS(block)[i].sp_pattern);
3607     vim_regfree(SYN_ITEMS(block)[i].sp_prog);
3608     /* Only free sp_cont_list and sp_next_list of first start pattern */
3609     if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
3610     {
3611 	vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3612 	vim_free(SYN_ITEMS(block)[i].sp_next_list);
3613 	vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
3614     }
3615 }
3616 
3617 /*
3618  * Clear and free one syntax cluster.
3619  */
3620     static void
3621 syn_clear_cluster(block, i)
3622     synblock_T	*block;
3623     int		i;
3624 {
3625     vim_free(SYN_CLSTR(block)[i].scl_name);
3626     vim_free(SYN_CLSTR(block)[i].scl_name_u);
3627     vim_free(SYN_CLSTR(block)[i].scl_list);
3628 }
3629 
3630 /*
3631  * Handle ":syntax clear" command.
3632  */
3633     static void
3634 syn_cmd_clear(eap, syncing)
3635     exarg_T	*eap;
3636     int		syncing;
3637 {
3638     char_u	*arg = eap->arg;
3639     char_u	*arg_end;
3640     int		id;
3641 
3642     eap->nextcmd = find_nextcmd(arg);
3643     if (eap->skip)
3644 	return;
3645 
3646     /*
3647      * We have to disable this within ":syn include @group filename",
3648      * because otherwise @group would get deleted.
3649      * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3650      * clear".
3651      */
3652     if (curwin->w_s->b_syn_topgrp != 0)
3653 	return;
3654 
3655     if (ends_excmd(*arg))
3656     {
3657 	/*
3658 	 * No argument: Clear all syntax items.
3659 	 */
3660 	if (syncing)
3661 	    syntax_sync_clear();
3662 	else
3663 	{
3664 	    syntax_clear(curwin->w_s);
3665 	    if (curwin->w_s == &curwin->w_buffer->b_s)
3666 		do_unlet((char_u *)"b:current_syntax", TRUE);
3667 	    do_unlet((char_u *)"w:current_syntax", TRUE);
3668 	}
3669     }
3670     else
3671     {
3672 	/*
3673 	 * Clear the group IDs that are in the argument.
3674 	 */
3675 	while (!ends_excmd(*arg))
3676 	{
3677 	    arg_end = skiptowhite(arg);
3678 	    if (*arg == '@')
3679 	    {
3680 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3681 		if (id == 0)
3682 		{
3683 		    EMSG2(_("E391: No such syntax cluster: %s"), arg);
3684 		    break;
3685 		}
3686 		else
3687 		{
3688 		    /*
3689 		     * We can't physically delete a cluster without changing
3690 		     * the IDs of other clusters, so we do the next best thing
3691 		     * and make it empty.
3692 		     */
3693 		    short scl_id = id - SYNID_CLUSTER;
3694 
3695 		    vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3696 		    SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
3697 		}
3698 	    }
3699 	    else
3700 	    {
3701 		id = syn_namen2id(arg, (int)(arg_end - arg));
3702 		if (id == 0)
3703 		{
3704 		    EMSG2(_(e_nogroup), arg);
3705 		    break;
3706 		}
3707 		else
3708 		    syn_clear_one(id, syncing);
3709 	    }
3710 	    arg = skipwhite(arg_end);
3711 	}
3712     }
3713     redraw_curbuf_later(SOME_VALID);
3714     syn_stack_free_all(curwin->w_s);		/* Need to recompute all syntax. */
3715 }
3716 
3717 /*
3718  * Clear one syntax group for the current buffer.
3719  */
3720     static void
3721 syn_clear_one(id, syncing)
3722     int		id;
3723     int		syncing;
3724 {
3725     synpat_T	*spp;
3726     int		idx;
3727 
3728     /* Clear keywords only when not ":syn sync clear group-name" */
3729     if (!syncing)
3730     {
3731 	(void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3732 	(void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
3733     }
3734 
3735     /* clear the patterns for "id" */
3736     for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
3737     {
3738 	spp = &(SYN_ITEMS(curwin->w_s)[idx]);
3739 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3740 	    continue;
3741 	syn_remove_pattern(curwin->w_s, idx);
3742     }
3743 }
3744 
3745 /*
3746  * Handle ":syntax on" command.
3747  */
3748     static void
3749 syn_cmd_on(eap, syncing)
3750     exarg_T	*eap;
3751     int		syncing UNUSED;
3752 {
3753     syn_cmd_onoff(eap, "syntax");
3754 }
3755 
3756 /*
3757  * Handle ":syntax enable" command.
3758  */
3759     static void
3760 syn_cmd_enable(eap, syncing)
3761     exarg_T	*eap;
3762     int		syncing UNUSED;
3763 {
3764     set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3765     syn_cmd_onoff(eap, "syntax");
3766     do_unlet((char_u *)"g:syntax_cmd", TRUE);
3767 }
3768 
3769 /*
3770  * Handle ":syntax reset" command.
3771  */
3772     static void
3773 syn_cmd_reset(eap, syncing)
3774     exarg_T	*eap;
3775     int		syncing UNUSED;
3776 {
3777     eap->nextcmd = check_nextcmd(eap->arg);
3778     if (!eap->skip)
3779     {
3780 	set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3781 	do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
3782 	do_unlet((char_u *)"g:syntax_cmd", TRUE);
3783     }
3784 }
3785 
3786 /*
3787  * Handle ":syntax manual" command.
3788  */
3789     static void
3790 syn_cmd_manual(eap, syncing)
3791     exarg_T	*eap;
3792     int		syncing UNUSED;
3793 {
3794     syn_cmd_onoff(eap, "manual");
3795 }
3796 
3797 /*
3798  * Handle ":syntax off" command.
3799  */
3800     static void
3801 syn_cmd_off(eap, syncing)
3802     exarg_T	*eap;
3803     int		syncing UNUSED;
3804 {
3805     syn_cmd_onoff(eap, "nosyntax");
3806 }
3807 
3808     static void
3809 syn_cmd_onoff(eap, name)
3810     exarg_T	*eap;
3811     char	*name;
3812 {
3813     char_u	buf[100];
3814 
3815     eap->nextcmd = check_nextcmd(eap->arg);
3816     if (!eap->skip)
3817     {
3818 	STRCPY(buf, "so ");
3819 	vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
3820 	do_cmdline_cmd(buf);
3821     }
3822 }
3823 
3824 /*
3825  * Handle ":syntax [list]" command: list current syntax words.
3826  */
3827     static void
3828 syn_cmd_list(eap, syncing)
3829     exarg_T	*eap;
3830     int		syncing;	    /* when TRUE: list syncing items */
3831 {
3832     char_u	*arg = eap->arg;
3833     int		id;
3834     char_u	*arg_end;
3835 
3836     eap->nextcmd = find_nextcmd(arg);
3837     if (eap->skip)
3838 	return;
3839 
3840     if (!syntax_present(curwin))
3841     {
3842 	MSG(_(msg_no_items));
3843 	return;
3844     }
3845 
3846     if (syncing)
3847     {
3848 	if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
3849 	{
3850 	    MSG_PUTS(_("syncing on C-style comments"));
3851 	    syn_lines_msg();
3852 	    syn_match_msg();
3853 	    return;
3854 	}
3855 	else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
3856 	{
3857 	    if (curwin->w_s->b_syn_sync_minlines == 0)
3858 		MSG_PUTS(_("no syncing"));
3859 	    else
3860 	    {
3861 		MSG_PUTS(_("syncing starts "));
3862 		msg_outnum(curwin->w_s->b_syn_sync_minlines);
3863 		MSG_PUTS(_(" lines before top line"));
3864 		syn_match_msg();
3865 	    }
3866 	    return;
3867 	}
3868 	MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3869 	if (curwin->w_s->b_syn_sync_minlines > 0
3870 		|| curwin->w_s->b_syn_sync_maxlines > 0
3871 		|| curwin->w_s->b_syn_sync_linebreaks > 0)
3872 	{
3873 	    MSG_PUTS(_("\nsyncing on items"));
3874 	    syn_lines_msg();
3875 	    syn_match_msg();
3876 	}
3877     }
3878     else
3879 	MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3880     if (ends_excmd(*arg))
3881     {
3882 	/*
3883 	 * No argument: List all group IDs and all syntax clusters.
3884 	 */
3885 	for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3886 	    syn_list_one(id, syncing, FALSE);
3887 	for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
3888 	    syn_list_cluster(id);
3889     }
3890     else
3891     {
3892 	/*
3893 	 * List the group IDs and syntax clusters that are in the argument.
3894 	 */
3895 	while (!ends_excmd(*arg) && !got_int)
3896 	{
3897 	    arg_end = skiptowhite(arg);
3898 	    if (*arg == '@')
3899 	    {
3900 		id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3901 		if (id == 0)
3902 		    EMSG2(_("E392: No such syntax cluster: %s"), arg);
3903 		else
3904 		    syn_list_cluster(id - SYNID_CLUSTER);
3905 	    }
3906 	    else
3907 	    {
3908 		id = syn_namen2id(arg, (int)(arg_end - arg));
3909 		if (id == 0)
3910 		    EMSG2(_(e_nogroup), arg);
3911 		else
3912 		    syn_list_one(id, syncing, TRUE);
3913 	    }
3914 	    arg = skipwhite(arg_end);
3915 	}
3916     }
3917     eap->nextcmd = check_nextcmd(arg);
3918 }
3919 
3920     static void
3921 syn_lines_msg()
3922 {
3923     if (curwin->w_s->b_syn_sync_maxlines > 0
3924 				      || curwin->w_s->b_syn_sync_minlines > 0)
3925     {
3926 	MSG_PUTS("; ");
3927 	if (curwin->w_s->b_syn_sync_minlines > 0)
3928 	{
3929 	    MSG_PUTS(_("minimal "));
3930 	    msg_outnum(curwin->w_s->b_syn_sync_minlines);
3931 	    if (curwin->w_s->b_syn_sync_maxlines)
3932 		MSG_PUTS(", ");
3933 	}
3934 	if (curwin->w_s->b_syn_sync_maxlines > 0)
3935 	{
3936 	    MSG_PUTS(_("maximal "));
3937 	    msg_outnum(curwin->w_s->b_syn_sync_maxlines);
3938 	}
3939 	MSG_PUTS(_(" lines before top line"));
3940     }
3941 }
3942 
3943     static void
3944 syn_match_msg()
3945 {
3946     if (curwin->w_s->b_syn_sync_linebreaks > 0)
3947     {
3948 	MSG_PUTS(_("; match "));
3949 	msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
3950 	MSG_PUTS(_(" line breaks"));
3951     }
3952 }
3953 
3954 static int  last_matchgroup;
3955 
3956 struct name_list
3957 {
3958     int		flag;
3959     char	*name;
3960 };
3961 
3962 static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3963 
3964 /*
3965  * List one syntax item, for ":syntax" or "syntax list syntax_name".
3966  */
3967     static void
3968 syn_list_one(id, syncing, link_only)
3969     int		id;
3970     int		syncing;	    /* when TRUE: list syncing items */
3971     int		link_only;	    /* when TRUE; list link-only too */
3972 {
3973     int		attr;
3974     int		idx;
3975     int		did_header = FALSE;
3976     synpat_T	*spp;
3977     static struct name_list namelist1[] =
3978 		{
3979 		    {HL_DISPLAY, "display"},
3980 		    {HL_CONTAINED, "contained"},
3981 		    {HL_ONELINE, "oneline"},
3982 		    {HL_KEEPEND, "keepend"},
3983 		    {HL_EXTEND, "extend"},
3984 		    {HL_EXCLUDENL, "excludenl"},
3985 		    {HL_TRANSP, "transparent"},
3986 		    {HL_FOLD, "fold"},
3987 #ifdef FEAT_CONCEAL
3988 		    {HL_CONCEAL, "conceal"},
3989 		    {HL_CONCEALENDS, "concealends"},
3990 #endif
3991 		    {0, NULL}
3992 		};
3993     static struct name_list namelist2[] =
3994 		{
3995 		    {HL_SKIPWHITE, "skipwhite"},
3996 		    {HL_SKIPNL, "skipnl"},
3997 		    {HL_SKIPEMPTY, "skipempty"},
3998 		    {0, NULL}
3999 		};
4000 
4001     attr = hl_attr(HLF_D);		/* highlight like directories */
4002 
4003     /* list the keywords for "id" */
4004     if (!syncing)
4005     {
4006 	did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4007 	did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
4008 							    did_header, attr);
4009     }
4010 
4011     /* list the patterns for "id" */
4012     for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
4013     {
4014 	spp = &(SYN_ITEMS(curwin->w_s)[idx]);
4015 	if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4016 	    continue;
4017 
4018 	(void)syn_list_header(did_header, 999, id);
4019 	did_header = TRUE;
4020 	last_matchgroup = 0;
4021 	if (spp->sp_type == SPTYPE_MATCH)
4022 	{
4023 	    put_pattern("match", ' ', spp, attr);
4024 	    msg_putchar(' ');
4025 	}
4026 	else if (spp->sp_type == SPTYPE_START)
4027 	{
4028 	    while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4029 		put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4030 	    if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4031 		put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4032 	    while (idx < curwin->w_s->b_syn_patterns.ga_len
4033 			      && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4034 		put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4035 	    --idx;
4036 	    msg_putchar(' ');
4037 	}
4038 	syn_list_flags(namelist1, spp->sp_flags, attr);
4039 
4040 	if (spp->sp_cont_list != NULL)
4041 	    put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4042 
4043 	if (spp->sp_syn.cont_in_list != NULL)
4044 	    put_id_list((char_u *)"containedin",
4045 					      spp->sp_syn.cont_in_list, attr);
4046 
4047 	if (spp->sp_next_list != NULL)
4048 	{
4049 	    put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4050 	    syn_list_flags(namelist2, spp->sp_flags, attr);
4051 	}
4052 	if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4053 	{
4054 	    if (spp->sp_flags & HL_SYNC_HERE)
4055 		msg_puts_attr((char_u *)"grouphere", attr);
4056 	    else
4057 		msg_puts_attr((char_u *)"groupthere", attr);
4058 	    msg_putchar(' ');
4059 	    if (spp->sp_sync_idx >= 0)
4060 		msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
4061 				   [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4062 	    else
4063 		MSG_PUTS("NONE");
4064 	    msg_putchar(' ');
4065 	}
4066     }
4067 
4068     /* list the link, if there is one */
4069     if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4070     {
4071 	(void)syn_list_header(did_header, 999, id);
4072 	msg_puts_attr((char_u *)"links to", attr);
4073 	msg_putchar(' ');
4074 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4075     }
4076 }
4077 
4078     static void
4079 syn_list_flags(nlist, flags, attr)
4080     struct name_list	*nlist;
4081     int			flags;
4082     int			attr;
4083 {
4084     int		i;
4085 
4086     for (i = 0; nlist[i].flag != 0; ++i)
4087 	if (flags & nlist[i].flag)
4088 	{
4089 	    msg_puts_attr((char_u *)nlist[i].name, attr);
4090 	    msg_putchar(' ');
4091 	}
4092 }
4093 
4094 /*
4095  * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4096  */
4097     static void
4098 syn_list_cluster(id)
4099     int id;
4100 {
4101     int	    endcol = 15;
4102 
4103     /* slight hack:  roughly duplicate the guts of syn_list_header() */
4104     msg_putchar('\n');
4105     msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
4106 
4107     if (msg_col >= endcol)	/* output at least one space */
4108 	endcol = msg_col + 1;
4109     if (Columns <= endcol)	/* avoid hang for tiny window */
4110 	endcol = Columns - 1;
4111 
4112     msg_advance(endcol);
4113     if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
4114     {
4115 	put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
4116 		    hl_attr(HLF_D));
4117     }
4118     else
4119     {
4120 	msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4121 	msg_puts((char_u *)"=NONE");
4122     }
4123 }
4124 
4125     static void
4126 put_id_list(name, list, attr)
4127     char_u	*name;
4128     short	*list;
4129     int		attr;
4130 {
4131     short		*p;
4132 
4133     msg_puts_attr(name, attr);
4134     msg_putchar('=');
4135     for (p = list; *p; ++p)
4136     {
4137 	if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4138 	{
4139 	    if (p[1])
4140 		MSG_PUTS("ALLBUT");
4141 	    else
4142 		MSG_PUTS("ALL");
4143 	}
4144 	else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4145 	{
4146 	    MSG_PUTS("TOP");
4147 	}
4148 	else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4149 	{
4150 	    MSG_PUTS("CONTAINED");
4151 	}
4152 	else if (*p >= SYNID_CLUSTER)
4153 	{
4154 	    short scl_id = *p - SYNID_CLUSTER;
4155 
4156 	    msg_putchar('@');
4157 	    msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
4158 	}
4159 	else
4160 	    msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4161 	if (p[1])
4162 	    msg_putchar(',');
4163     }
4164     msg_putchar(' ');
4165 }
4166 
4167     static void
4168 put_pattern(s, c, spp, attr)
4169     char	*s;
4170     int		c;
4171     synpat_T	*spp;
4172     int		attr;
4173 {
4174     long	n;
4175     int		mask;
4176     int		first;
4177     static char	*sepchars = "/+=-#@\"|'^&";
4178     int		i;
4179 
4180     /* May have to write "matchgroup=group" */
4181     if (last_matchgroup != spp->sp_syn_match_id)
4182     {
4183 	last_matchgroup = spp->sp_syn_match_id;
4184 	msg_puts_attr((char_u *)"matchgroup", attr);
4185 	msg_putchar('=');
4186 	if (last_matchgroup == 0)
4187 	    msg_outtrans((char_u *)"NONE");
4188 	else
4189 	    msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4190 	msg_putchar(' ');
4191     }
4192 
4193     /* output the name of the pattern and an '=' or ' ' */
4194     msg_puts_attr((char_u *)s, attr);
4195     msg_putchar(c);
4196 
4197     /* output the pattern, in between a char that is not in the pattern */
4198     for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4199 	if (sepchars[++i] == NUL)
4200 	{
4201 	    i = 0;	/* no good char found, just use the first one */
4202 	    break;
4203 	}
4204     msg_putchar(sepchars[i]);
4205     msg_outtrans(spp->sp_pattern);
4206     msg_putchar(sepchars[i]);
4207 
4208     /* output any pattern options */
4209     first = TRUE;
4210     for (i = 0; i < SPO_COUNT; ++i)
4211     {
4212 	mask = (1 << i);
4213 	if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4214 	{
4215 	    if (!first)
4216 		msg_putchar(',');	/* separate with commas */
4217 	    msg_puts((char_u *)spo_name_tab[i]);
4218 	    n = spp->sp_offsets[i];
4219 	    if (i != SPO_LC_OFF)
4220 	    {
4221 		if (spp->sp_off_flags & mask)
4222 		    msg_putchar('s');
4223 		else
4224 		    msg_putchar('e');
4225 		if (n > 0)
4226 		    msg_putchar('+');
4227 	    }
4228 	    if (n || i == SPO_LC_OFF)
4229 		msg_outnum(n);
4230 	    first = FALSE;
4231 	}
4232     }
4233     msg_putchar(' ');
4234 }
4235 
4236 /*
4237  * List or clear the keywords for one syntax group.
4238  * Return TRUE if the header has been printed.
4239  */
4240     static int
4241 syn_list_keywords(id, ht, did_header, attr)
4242     int		id;
4243     hashtab_T	*ht;
4244     int		did_header;		/* header has already been printed */
4245     int		attr;
4246 {
4247     int		outlen;
4248     hashitem_T	*hi;
4249     keyentry_T	*kp;
4250     int		todo;
4251     int		prev_contained = 0;
4252     short	*prev_next_list = NULL;
4253     short	*prev_cont_in_list = NULL;
4254     int		prev_skipnl = 0;
4255     int		prev_skipwhite = 0;
4256     int		prev_skipempty = 0;
4257 
4258     /*
4259      * Unfortunately, this list of keywords is not sorted on alphabet but on
4260      * hash value...
4261      */
4262     todo = (int)ht->ht_used;
4263     for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
4264     {
4265 	if (!HASHITEM_EMPTY(hi))
4266 	{
4267 	    --todo;
4268 	    for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
4269 	    {
4270 		if (kp->k_syn.id == id)
4271 		{
4272 		    if (prev_contained != (kp->flags & HL_CONTAINED)
4273 			    || prev_skipnl != (kp->flags & HL_SKIPNL)
4274 			    || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4275 			    || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4276 			    || prev_cont_in_list != kp->k_syn.cont_in_list
4277 			    || prev_next_list != kp->next_list)
4278 			outlen = 9999;
4279 		    else
4280 			outlen = (int)STRLEN(kp->keyword);
4281 		    /* output "contained" and "nextgroup" on each line */
4282 		    if (syn_list_header(did_header, outlen, id))
4283 		    {
4284 			prev_contained = 0;
4285 			prev_next_list = NULL;
4286 			prev_cont_in_list = NULL;
4287 			prev_skipnl = 0;
4288 			prev_skipwhite = 0;
4289 			prev_skipempty = 0;
4290 		    }
4291 		    did_header = TRUE;
4292 		    if (prev_contained != (kp->flags & HL_CONTAINED))
4293 		    {
4294 			msg_puts_attr((char_u *)"contained", attr);
4295 			msg_putchar(' ');
4296 			prev_contained = (kp->flags & HL_CONTAINED);
4297 		    }
4298 		    if (kp->k_syn.cont_in_list != prev_cont_in_list)
4299 		    {
4300 			put_id_list((char_u *)"containedin",
4301 						kp->k_syn.cont_in_list, attr);
4302 			msg_putchar(' ');
4303 			prev_cont_in_list = kp->k_syn.cont_in_list;
4304 		    }
4305 		    if (kp->next_list != prev_next_list)
4306 		    {
4307 			put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4308 			msg_putchar(' ');
4309 			prev_next_list = kp->next_list;
4310 			if (kp->flags & HL_SKIPNL)
4311 			{
4312 			    msg_puts_attr((char_u *)"skipnl", attr);
4313 			    msg_putchar(' ');
4314 			    prev_skipnl = (kp->flags & HL_SKIPNL);
4315 			}
4316 			if (kp->flags & HL_SKIPWHITE)
4317 			{
4318 			    msg_puts_attr((char_u *)"skipwhite", attr);
4319 			    msg_putchar(' ');
4320 			    prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4321 			}
4322 			if (kp->flags & HL_SKIPEMPTY)
4323 			{
4324 			    msg_puts_attr((char_u *)"skipempty", attr);
4325 			    msg_putchar(' ');
4326 			    prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4327 			}
4328 		    }
4329 		    msg_outtrans(kp->keyword);
4330 		}
4331 	    }
4332 	}
4333     }
4334 
4335     return did_header;
4336 }
4337 
4338     static void
4339 syn_clear_keyword(id, ht)
4340     int		id;
4341     hashtab_T	*ht;
4342 {
4343     hashitem_T	*hi;
4344     keyentry_T	*kp;
4345     keyentry_T	*kp_prev;
4346     keyentry_T	*kp_next;
4347     int		todo;
4348 
4349     hash_lock(ht);
4350     todo = (int)ht->ht_used;
4351     for (hi = ht->ht_array; todo > 0; ++hi)
4352     {
4353 	if (!HASHITEM_EMPTY(hi))
4354 	{
4355 	    --todo;
4356 	    kp_prev = NULL;
4357 	    for (kp = HI2KE(hi); kp != NULL; )
4358 	    {
4359 		if (kp->k_syn.id == id)
4360 		{
4361 		    kp_next = kp->ke_next;
4362 		    if (kp_prev == NULL)
4363 		    {
4364 			if (kp_next == NULL)
4365 			    hash_remove(ht, hi);
4366 			else
4367 			    hi->hi_key = KE2HIKEY(kp_next);
4368 		    }
4369 		    else
4370 			kp_prev->ke_next = kp_next;
4371 		    vim_free(kp->next_list);
4372 		    vim_free(kp->k_syn.cont_in_list);
4373 		    vim_free(kp);
4374 		    kp = kp_next;
4375 		}
4376 		else
4377 		{
4378 		    kp_prev = kp;
4379 		    kp = kp->ke_next;
4380 		}
4381 	    }
4382 	}
4383     }
4384     hash_unlock(ht);
4385 }
4386 
4387 /*
4388  * Clear a whole keyword table.
4389  */
4390     static void
4391 clear_keywtab(ht)
4392     hashtab_T	*ht;
4393 {
4394     hashitem_T	*hi;
4395     int		todo;
4396     keyentry_T	*kp;
4397     keyentry_T	*kp_next;
4398 
4399     todo = (int)ht->ht_used;
4400     for (hi = ht->ht_array; todo > 0; ++hi)
4401     {
4402 	if (!HASHITEM_EMPTY(hi))
4403 	{
4404 	    --todo;
4405 	    for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
4406 	    {
4407 		kp_next = kp->ke_next;
4408 		vim_free(kp->next_list);
4409 		vim_free(kp->k_syn.cont_in_list);
4410 		vim_free(kp);
4411 	    }
4412 	}
4413     }
4414     hash_clear(ht);
4415     hash_init(ht);
4416 }
4417 
4418 /*
4419  * Add a keyword to the list of keywords.
4420  */
4421     static void
4422 add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
4423     char_u	*name;	    /* name of keyword */
4424     int		id;	    /* group ID for this keyword */
4425     int		flags;	    /* flags for this keyword */
4426     short	*cont_in_list; /* containedin for this keyword */
4427     short	*next_list; /* nextgroup for this keyword */
4428     int		conceal_char;
4429 {
4430     keyentry_T	*kp;
4431     hashtab_T	*ht;
4432     hashitem_T	*hi;
4433     char_u	*name_ic;
4434     long_u	hash;
4435     char_u	name_folded[MAXKEYWLEN + 1];
4436 
4437     if (curwin->w_s->b_syn_ic)
4438 	name_ic = str_foldcase(name, (int)STRLEN(name),
4439 						 name_folded, MAXKEYWLEN + 1);
4440     else
4441 	name_ic = name;
4442     kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4443     if (kp == NULL)
4444 	return;
4445     STRCPY(kp->keyword, name_ic);
4446     kp->k_syn.id = id;
4447     kp->k_syn.inc_tag = current_syn_inc_tag;
4448     kp->flags = flags;
4449     kp->k_char = conceal_char;
4450     kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
4451     if (cont_in_list != NULL)
4452 	curwin->w_s->b_syn_containedin = TRUE;
4453     kp->next_list = copy_id_list(next_list);
4454 
4455     if (curwin->w_s->b_syn_ic)
4456 	ht = &curwin->w_s->b_keywtab_ic;
4457     else
4458 	ht = &curwin->w_s->b_keywtab;
4459 
4460     hash = hash_hash(kp->keyword);
4461     hi = hash_lookup(ht, kp->keyword, hash);
4462     if (HASHITEM_EMPTY(hi))
4463     {
4464 	/* new keyword, add to hashtable */
4465 	kp->ke_next = NULL;
4466 	hash_add_item(ht, hi, kp->keyword, hash);
4467     }
4468     else
4469     {
4470 	/* keyword already exists, prepend to list */
4471 	kp->ke_next = HI2KE(hi);
4472 	hi->hi_key = KE2HIKEY(kp);
4473     }
4474 }
4475 
4476 /*
4477  * Get the start and end of the group name argument.
4478  * Return a pointer to the first argument.
4479  * Return NULL if the end of the command was found instead of further args.
4480  */
4481     static char_u *
4482 get_group_name(arg, name_end)
4483     char_u	*arg;		/* start of the argument */
4484     char_u	**name_end;	/* pointer to end of the name */
4485 {
4486     char_u	*rest;
4487 
4488     *name_end = skiptowhite(arg);
4489     rest = skipwhite(*name_end);
4490 
4491     /*
4492      * Check if there are enough arguments.  The first argument may be a
4493      * pattern, where '|' is allowed, so only check for NUL.
4494      */
4495     if (ends_excmd(*arg) || *rest == NUL)
4496 	return NULL;
4497     return rest;
4498 }
4499 
4500 /*
4501  * Check for syntax command option arguments.
4502  * This can be called at any place in the list of arguments, and just picks
4503  * out the arguments that are known.  Can be called several times in a row to
4504  * collect all options in between other arguments.
4505  * Return a pointer to the next argument (which isn't an option).
4506  * Return NULL for any error;
4507  */
4508     static char_u *
4509 get_syn_options(arg, opt, conceal_char)
4510     char_u	    *arg;		/* next argument to be checked */
4511     syn_opt_arg_T   *opt;		/* various things */
4512     int		    *conceal_char UNUSED;
4513 {
4514     char_u	*gname_start, *gname;
4515     int		syn_id;
4516     int		len;
4517     char	*p;
4518     int		i;
4519     int		fidx;
4520     static struct flag
4521     {
4522 	char	*name;
4523 	int	argtype;
4524 	int	flags;
4525     } flagtab[] = { {"cCoOnNtTaAiInNeEdD",	0,	HL_CONTAINED},
4526 		    {"oOnNeElLiInNeE",		0,	HL_ONELINE},
4527 		    {"kKeEeEpPeEnNdD",		0,	HL_KEEPEND},
4528 		    {"eExXtTeEnNdD",		0,	HL_EXTEND},
4529 		    {"eExXcClLuUdDeEnNlL",	0,	HL_EXCLUDENL},
4530 		    {"tTrRaAnNsSpPaArReEnNtT",	0,	HL_TRANSP},
4531 		    {"sSkKiIpPnNlL",		0,	HL_SKIPNL},
4532 		    {"sSkKiIpPwWhHiItTeE",	0,	HL_SKIPWHITE},
4533 		    {"sSkKiIpPeEmMpPtTyY",	0,	HL_SKIPEMPTY},
4534 		    {"gGrRoOuUpPhHeErReE",	0,	HL_SYNC_HERE},
4535 		    {"gGrRoOuUpPtThHeErReE",	0,	HL_SYNC_THERE},
4536 		    {"dDiIsSpPlLaAyY",		0,	HL_DISPLAY},
4537 		    {"fFoOlLdD",		0,	HL_FOLD},
4538 		    {"cCoOnNcCeEaAlL",		0,	HL_CONCEAL},
4539 		    {"cCoOnNcCeEaAlLeEnNdDsS",	0,	HL_CONCEALENDS},
4540 		    {"cCcChHaArR",		11,	0},
4541 		    {"cCoOnNtTaAiInNsS",	1,	0},
4542 		    {"cCoOnNtTaAiInNeEdDiInN",	2,	0},
4543 		    {"nNeExXtTgGrRoOuUpP",	3,	0},
4544 		};
4545     static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
4546 
4547     if (arg == NULL)		/* already detected error */
4548 	return NULL;
4549 
4550 #ifdef FEAT_CONCEAL
4551     if (curwin->w_s->b_syn_conceal)
4552 	opt->flags |= HL_CONCEAL;
4553 #endif
4554 
4555     for (;;)
4556     {
4557 	/*
4558 	 * This is used very often when a large number of keywords is defined.
4559 	 * Need to skip quickly when no option name is found.
4560 	 * Also avoid tolower(), it's slow.
4561 	 */
4562 	if (strchr(first_letters, *arg) == NULL)
4563 	    break;
4564 
4565 	for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4566 	{
4567 	    p = flagtab[fidx].name;
4568 	    for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4569 		if (arg[len] != p[i] && arg[len] != p[i + 1])
4570 		    break;
4571 	    if (p[i] == NUL && (vim_iswhite(arg[len])
4572 				    || (flagtab[fidx].argtype > 0
4573 					 ? arg[len] == '='
4574 					 : ends_excmd(arg[len]))))
4575 	    {
4576 		if (opt->keyword
4577 			&& (flagtab[fidx].flags == HL_DISPLAY
4578 			    || flagtab[fidx].flags == HL_FOLD
4579 			    || flagtab[fidx].flags == HL_EXTEND))
4580 		    /* treat "display", "fold" and "extend" as a keyword */
4581 		    fidx = -1;
4582 		break;
4583 	    }
4584 	}
4585 	if (fidx < 0)	    /* no match found */
4586 	    break;
4587 
4588 	if (flagtab[fidx].argtype == 1)
4589 	{
4590 	    if (!opt->has_cont_list)
4591 	    {
4592 		EMSG(_("E395: contains argument not accepted here"));
4593 		return NULL;
4594 	    }
4595 	    if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
4596 		return NULL;
4597 	}
4598 	else if (flagtab[fidx].argtype == 2)
4599 	{
4600 	    if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
4601 		return NULL;
4602 	}
4603 	else if (flagtab[fidx].argtype == 3)
4604 	{
4605 	    if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
4606 		return NULL;
4607 	}
4608 	else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4609 	{
4610 #ifdef FEAT_MBYTE
4611 	    /* cchar=? */
4612 	    if (has_mbyte)
4613 	    {
4614 # ifdef FEAT_CONCEAL
4615 		*conceal_char = mb_ptr2char(arg + 6);
4616 # endif
4617 		arg += mb_ptr2len(arg + 6) - 1;
4618 	    }
4619 	    else
4620 #endif
4621 	    {
4622 #ifdef FEAT_CONCEAL
4623 		*conceal_char = arg[6];
4624 #else
4625 		;
4626 #endif
4627 	    }
4628 #ifdef FEAT_CONCEAL
4629 	    if (!vim_isprintc_strict(*conceal_char))
4630 	    {
4631 		EMSG(_("E844: invalid cchar value"));
4632 		return NULL;
4633 	    }
4634 #endif
4635 	    arg = skipwhite(arg + 7);
4636 	}
4637 	else
4638 	{
4639 	    opt->flags |= flagtab[fidx].flags;
4640 	    arg = skipwhite(arg + len);
4641 
4642 	    if (flagtab[fidx].flags == HL_SYNC_HERE
4643 		    || flagtab[fidx].flags == HL_SYNC_THERE)
4644 	    {
4645 		if (opt->sync_idx == NULL)
4646 		{
4647 		    EMSG(_("E393: group[t]here not accepted here"));
4648 		    return NULL;
4649 		}
4650 		gname_start = arg;
4651 		arg = skiptowhite(arg);
4652 		if (gname_start == arg)
4653 		    return NULL;
4654 		gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4655 		if (gname == NULL)
4656 		    return NULL;
4657 		if (STRCMP(gname, "NONE") == 0)
4658 		    *opt->sync_idx = NONE_IDX;
4659 		else
4660 		{
4661 		    syn_id = syn_name2id(gname);
4662 		    for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4663 			if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4664 			      && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
4665 			{
4666 			    *opt->sync_idx = i;
4667 			    break;
4668 			}
4669 		    if (i < 0)
4670 		    {
4671 			EMSG2(_("E394: Didn't find region item for %s"), gname);
4672 			vim_free(gname);
4673 			return NULL;
4674 		    }
4675 		}
4676 
4677 		vim_free(gname);
4678 		arg = skipwhite(arg);
4679 	    }
4680 #ifdef FEAT_FOLDING
4681 	    else if (flagtab[fidx].flags == HL_FOLD
4682 						&& foldmethodIsSyntax(curwin))
4683 		/* Need to update folds later. */
4684 		foldUpdateAll(curwin);
4685 #endif
4686 	}
4687     }
4688 
4689     return arg;
4690 }
4691 
4692 /*
4693  * Adjustments to syntax item when declared in a ":syn include"'d file.
4694  * Set the contained flag, and if the item is not already contained, add it
4695  * to the specified top-level group, if any.
4696  */
4697     static void
4698 syn_incl_toplevel(id, flagsp)
4699     int		id;
4700     int		*flagsp;
4701 {
4702     if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
4703 	return;
4704     *flagsp |= HL_CONTAINED;
4705     if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
4706     {
4707 	/* We have to alloc this, because syn_combine_list() will free it. */
4708 	short	    *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4709 	int	    tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
4710 
4711 	if (grp_list != NULL)
4712 	{
4713 	    grp_list[0] = id;
4714 	    grp_list[1] = 0;
4715 	    syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
4716 			 CLUSTER_ADD);
4717 	}
4718     }
4719 }
4720 
4721 /*
4722  * Handle ":syntax include [@{group-name}] filename" command.
4723  */
4724     static void
4725 syn_cmd_include(eap, syncing)
4726     exarg_T	*eap;
4727     int		syncing UNUSED;
4728 {
4729     char_u	*arg = eap->arg;
4730     int		sgl_id = 1;
4731     char_u	*group_name_end;
4732     char_u	*rest;
4733     char_u	*errormsg = NULL;
4734     int		prev_toplvl_grp;
4735     int		prev_syn_inc_tag;
4736     int		source = FALSE;
4737 
4738     eap->nextcmd = find_nextcmd(arg);
4739     if (eap->skip)
4740 	return;
4741 
4742     if (arg[0] == '@')
4743     {
4744 	++arg;
4745 	rest = get_group_name(arg, &group_name_end);
4746 	if (rest == NULL)
4747 	{
4748 	    EMSG((char_u *)_("E397: Filename required"));
4749 	    return;
4750 	}
4751 	sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4752 	if (sgl_id == 0)
4753 	    return;
4754 	/* separate_nextcmd() and expand_filename() depend on this */
4755 	eap->arg = rest;
4756     }
4757 
4758     /*
4759      * Everything that's left, up to the next command, should be the
4760      * filename to include.
4761      */
4762     eap->argt |= (XFILE | NOSPC);
4763     separate_nextcmd(eap);
4764     if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4765     {
4766 	/* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4767 	 * file.  Need to expand the file name first.  In other cases
4768 	 * ":runtime!" is used. */
4769 	source = TRUE;
4770 	if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4771 	{
4772 	    if (errormsg != NULL)
4773 		EMSG(errormsg);
4774 	    return;
4775 	}
4776     }
4777 
4778     /*
4779      * Save and restore the existing top-level grouplist id and ":syn
4780      * include" tag around the actual inclusion.
4781      */
4782     if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4783     {
4784 	EMSG((char_u *)_("E847: Too many syntax includes"));
4785 	return;
4786     }
4787     prev_syn_inc_tag = current_syn_inc_tag;
4788     current_syn_inc_tag = ++running_syn_inc_tag;
4789     prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4790     curwin->w_s->b_syn_topgrp = sgl_id;
4791     if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4792 				: source_runtime(eap->arg, TRUE) == FAIL)
4793 	EMSG2(_(e_notopen), eap->arg);
4794     curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
4795     current_syn_inc_tag = prev_syn_inc_tag;
4796 }
4797 
4798 /*
4799  * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4800  */
4801     static void
4802 syn_cmd_keyword(eap, syncing)
4803     exarg_T	*eap;
4804     int		syncing UNUSED;
4805 {
4806     char_u	*arg = eap->arg;
4807     char_u	*group_name_end;
4808     int		syn_id;
4809     char_u	*rest;
4810     char_u	*keyword_copy = NULL;
4811     char_u	*p;
4812     char_u	*kw;
4813     syn_opt_arg_T syn_opt_arg;
4814     int		cnt;
4815     int		conceal_char = NUL;
4816 
4817     rest = get_group_name(arg, &group_name_end);
4818 
4819     if (rest != NULL)
4820     {
4821 	syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4822 	if (syn_id != 0)
4823 	    /* allocate a buffer, for removing backslashes in the keyword */
4824 	    keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4825 	if (keyword_copy != NULL)
4826 	{
4827 	    syn_opt_arg.flags = 0;
4828 	    syn_opt_arg.keyword = TRUE;
4829 	    syn_opt_arg.sync_idx = NULL;
4830 	    syn_opt_arg.has_cont_list = FALSE;
4831 	    syn_opt_arg.cont_in_list = NULL;
4832 	    syn_opt_arg.next_list = NULL;
4833 
4834 	    /*
4835 	     * The options given apply to ALL keywords, so all options must be
4836 	     * found before keywords can be created.
4837 	     * 1: collect the options and copy the keywords to keyword_copy.
4838 	     */
4839 	    cnt = 0;
4840 	    p = keyword_copy;
4841 	    for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
4842 	    {
4843 		rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
4844 		if (rest == NULL || ends_excmd(*rest))
4845 		    break;
4846 		/* Copy the keyword, removing backslashes, and add a NUL. */
4847 		while (*rest != NUL && !vim_iswhite(*rest))
4848 		{
4849 		    if (*rest == '\\' && rest[1] != NUL)
4850 			++rest;
4851 		    *p++ = *rest++;
4852 		}
4853 		*p++ = NUL;
4854 		++cnt;
4855 	    }
4856 
4857 	    if (!eap->skip)
4858 	    {
4859 		/* Adjust flags for use of ":syn include". */
4860 		syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4861 
4862 		/*
4863 		 * 2: Add an entry for each keyword.
4864 		 */
4865 		for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4866 		{
4867 		    for (p = vim_strchr(kw, '['); ; )
4868 		    {
4869 			if (p != NULL)
4870 			    *p = NUL;
4871 			add_keyword(kw, syn_id, syn_opt_arg.flags,
4872 				syn_opt_arg.cont_in_list,
4873 					 syn_opt_arg.next_list, conceal_char);
4874 			if (p == NULL)
4875 			    break;
4876 			if (p[1] == NUL)
4877 			{
4878 			    EMSG2(_("E789: Missing ']': %s"), kw);
4879 			    goto error;
4880 			}
4881 			if (p[1] == ']')
4882 			{
4883 			    if (p[2] != NUL)
4884 			    {
4885 				EMSG3(_("E890: trailing char after ']': %s]%s"),
4886 								kw, &p[2]);
4887 				goto error;
4888 			    }
4889 			    kw = p + 1;		/* skip over the "]" */
4890 			    break;
4891 			}
4892 #ifdef FEAT_MBYTE
4893 			if (has_mbyte)
4894 			{
4895 			    int l = (*mb_ptr2len)(p + 1);
4896 
4897 			    mch_memmove(p, p + 1, l);
4898 			    p += l;
4899 			}
4900 			else
4901 #endif
4902 			{
4903 			    p[0] = p[1];
4904 			    ++p;
4905 			}
4906 		    }
4907 		}
4908 	    }
4909 error:
4910 	    vim_free(keyword_copy);
4911 	    vim_free(syn_opt_arg.cont_in_list);
4912 	    vim_free(syn_opt_arg.next_list);
4913 	}
4914     }
4915 
4916     if (rest != NULL)
4917 	eap->nextcmd = check_nextcmd(rest);
4918     else
4919 	EMSG2(_(e_invarg2), arg);
4920 
4921     redraw_curbuf_later(SOME_VALID);
4922     syn_stack_free_all(curwin->w_s);		/* Need to recompute all syntax. */
4923 }
4924 
4925 /*
4926  * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4927  *
4928  * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4929  */
4930     static void
4931 syn_cmd_match(eap, syncing)
4932     exarg_T	*eap;
4933     int		syncing;	    /* TRUE for ":syntax sync match .. " */
4934 {
4935     char_u	*arg = eap->arg;
4936     char_u	*group_name_end;
4937     char_u	*rest;
4938     synpat_T	item;		/* the item found in the line */
4939     int		syn_id;
4940     int		idx;
4941     syn_opt_arg_T syn_opt_arg;
4942     int		sync_idx = 0;
4943     int		conceal_char = NUL;
4944 
4945     /* Isolate the group name, check for validity */
4946     rest = get_group_name(arg, &group_name_end);
4947 
4948     /* Get options before the pattern */
4949     syn_opt_arg.flags = 0;
4950     syn_opt_arg.keyword = FALSE;
4951     syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4952     syn_opt_arg.has_cont_list = TRUE;
4953     syn_opt_arg.cont_list = NULL;
4954     syn_opt_arg.cont_in_list = NULL;
4955     syn_opt_arg.next_list = NULL;
4956     rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
4957 
4958     /* get the pattern. */
4959     init_syn_patterns();
4960     vim_memset(&item, 0, sizeof(item));
4961     rest = get_syn_pattern(rest, &item);
4962     if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4963 	syn_opt_arg.flags |= HL_HAS_EOL;
4964 
4965     /* Get options after the pattern */
4966     rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
4967 
4968     if (rest != NULL)		/* all arguments are valid */
4969     {
4970 	/*
4971 	 * Check for trailing command and illegal trailing arguments.
4972 	 */
4973 	eap->nextcmd = check_nextcmd(rest);
4974 	if (!ends_excmd(*rest) || eap->skip)
4975 	    rest = NULL;
4976 	else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
4977 		&& (syn_id = syn_check_group(arg,
4978 					   (int)(group_name_end - arg))) != 0)
4979 	{
4980 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4981 	    /*
4982 	     * Store the pattern in the syn_items list
4983 	     */
4984 	    idx = curwin->w_s->b_syn_patterns.ga_len;
4985 	    SYN_ITEMS(curwin->w_s)[idx] = item;
4986 	    SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4987 	    SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4988 	    SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4989 	    SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4990 	    SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4991 	    SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4992 	    SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4993 	    SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
4994 						     syn_opt_arg.cont_in_list;
4995 #ifdef FEAT_CONCEAL
4996 	    SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
4997 #endif
4998 	    if (syn_opt_arg.cont_in_list != NULL)
4999 		curwin->w_s->b_syn_containedin = TRUE;
5000 	    SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5001 	    ++curwin->w_s->b_syn_patterns.ga_len;
5002 
5003 	    /* remember that we found a match for syncing on */
5004 	    if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
5005 		curwin->w_s->b_syn_sync_flags |= SF_MATCH;
5006 #ifdef FEAT_FOLDING
5007 	    if (syn_opt_arg.flags & HL_FOLD)
5008 		++curwin->w_s->b_syn_folditems;
5009 #endif
5010 
5011 	    redraw_curbuf_later(SOME_VALID);
5012 	    syn_stack_free_all(curwin->w_s);	/* Need to recompute all syntax. */
5013 	    return;	/* don't free the progs and patterns now */
5014 	}
5015     }
5016 
5017     /*
5018      * Something failed, free the allocated memory.
5019      */
5020     vim_regfree(item.sp_prog);
5021     vim_free(item.sp_pattern);
5022     vim_free(syn_opt_arg.cont_list);
5023     vim_free(syn_opt_arg.cont_in_list);
5024     vim_free(syn_opt_arg.next_list);
5025 
5026     if (rest == NULL)
5027 	EMSG2(_(e_invarg2), arg);
5028 }
5029 
5030 /*
5031  * Handle ":syntax region {group-name} [matchgroup={group-name}]
5032  *		start {start} .. [skip {skip}] end {end} .. [{options}]".
5033  */
5034     static void
5035 syn_cmd_region(eap, syncing)
5036     exarg_T	*eap;
5037     int		syncing;	    /* TRUE for ":syntax sync region .." */
5038 {
5039     char_u		*arg = eap->arg;
5040     char_u		*group_name_end;
5041     char_u		*rest;			/* next arg, NULL on error */
5042     char_u		*key_end;
5043     char_u		*key = NULL;
5044     char_u		*p;
5045     int			item;
5046 #define ITEM_START	    0
5047 #define ITEM_SKIP	    1
5048 #define ITEM_END	    2
5049 #define ITEM_MATCHGROUP	    3
5050     struct pat_ptr
5051     {
5052 	synpat_T	*pp_synp;		/* pointer to syn_pattern */
5053 	int		pp_matchgroup_id;	/* matchgroup ID */
5054 	struct pat_ptr	*pp_next;		/* pointer to next pat_ptr */
5055     }			*(pat_ptrs[3]);
5056 					/* patterns found in the line */
5057     struct pat_ptr	*ppp;
5058     struct pat_ptr	*ppp_next;
5059     int			pat_count = 0;		/* nr of syn_patterns found */
5060     int			syn_id;
5061     int			matchgroup_id = 0;
5062     int			not_enough = FALSE;	/* not enough arguments */
5063     int			illegal = FALSE;	/* illegal arguments */
5064     int			success = FALSE;
5065     int			idx;
5066     syn_opt_arg_T	syn_opt_arg;
5067     int			conceal_char = NUL;
5068 
5069     /* Isolate the group name, check for validity */
5070     rest = get_group_name(arg, &group_name_end);
5071 
5072     pat_ptrs[0] = NULL;
5073     pat_ptrs[1] = NULL;
5074     pat_ptrs[2] = NULL;
5075 
5076     init_syn_patterns();
5077 
5078     syn_opt_arg.flags = 0;
5079     syn_opt_arg.keyword = FALSE;
5080     syn_opt_arg.sync_idx = NULL;
5081     syn_opt_arg.has_cont_list = TRUE;
5082     syn_opt_arg.cont_list = NULL;
5083     syn_opt_arg.cont_in_list = NULL;
5084     syn_opt_arg.next_list = NULL;
5085 
5086     /*
5087      * get the options, patterns and matchgroup.
5088      */
5089     while (rest != NULL && !ends_excmd(*rest))
5090     {
5091 	/* Check for option arguments */
5092 	rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
5093 	if (rest == NULL || ends_excmd(*rest))
5094 	    break;
5095 
5096 	/* must be a pattern or matchgroup then */
5097 	key_end = rest;
5098 	while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5099 	    ++key_end;
5100 	vim_free(key);
5101 	key = vim_strnsave_up(rest, (int)(key_end - rest));
5102 	if (key == NULL)			/* out of memory */
5103 	{
5104 	    rest = NULL;
5105 	    break;
5106 	}
5107 	if (STRCMP(key, "MATCHGROUP") == 0)
5108 	    item = ITEM_MATCHGROUP;
5109 	else if (STRCMP(key, "START") == 0)
5110 	    item = ITEM_START;
5111 	else if (STRCMP(key, "END") == 0)
5112 	    item = ITEM_END;
5113 	else if (STRCMP(key, "SKIP") == 0)
5114 	{
5115 	    if (pat_ptrs[ITEM_SKIP] != NULL)	/* one skip pattern allowed */
5116 	    {
5117 		illegal = TRUE;
5118 		break;
5119 	    }
5120 	    item = ITEM_SKIP;
5121 	}
5122 	else
5123 	    break;
5124 	rest = skipwhite(key_end);
5125 	if (*rest != '=')
5126 	{
5127 	    rest = NULL;
5128 	    EMSG2(_("E398: Missing '=': %s"), arg);
5129 	    break;
5130 	}
5131 	rest = skipwhite(rest + 1);
5132 	if (*rest == NUL)
5133 	{
5134 	    not_enough = TRUE;
5135 	    break;
5136 	}
5137 
5138 	if (item == ITEM_MATCHGROUP)
5139 	{
5140 	    p = skiptowhite(rest);
5141 	    if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5142 		matchgroup_id = 0;
5143 	    else
5144 	    {
5145 		matchgroup_id = syn_check_group(rest, (int)(p - rest));
5146 		if (matchgroup_id == 0)
5147 		{
5148 		    illegal = TRUE;
5149 		    break;
5150 		}
5151 	    }
5152 	    rest = skipwhite(p);
5153 	}
5154 	else
5155 	{
5156 	    /*
5157 	     * Allocate room for a syn_pattern, and link it in the list of
5158 	     * syn_patterns for this item, at the start (because the list is
5159 	     * used from end to start).
5160 	     */
5161 	    ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5162 	    if (ppp == NULL)
5163 	    {
5164 		rest = NULL;
5165 		break;
5166 	    }
5167 	    ppp->pp_next = pat_ptrs[item];
5168 	    pat_ptrs[item] = ppp;
5169 	    ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5170 	    if (ppp->pp_synp == NULL)
5171 	    {
5172 		rest = NULL;
5173 		break;
5174 	    }
5175 
5176 	    /*
5177 	     * Get the syntax pattern and the following offset(s).
5178 	     */
5179 	    /* Enable the appropriate \z specials. */
5180 	    if (item == ITEM_START)
5181 		reg_do_extmatch = REX_SET;
5182 	    else if (item == ITEM_SKIP || item == ITEM_END)
5183 		reg_do_extmatch = REX_USE;
5184 	    rest = get_syn_pattern(rest, ppp->pp_synp);
5185 	    reg_do_extmatch = 0;
5186 	    if (item == ITEM_END && vim_regcomp_had_eol()
5187 				       && !(syn_opt_arg.flags & HL_EXCLUDENL))
5188 		ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5189 	    ppp->pp_matchgroup_id = matchgroup_id;
5190 	    ++pat_count;
5191 	}
5192     }
5193     vim_free(key);
5194     if (illegal || not_enough)
5195 	rest = NULL;
5196 
5197     /*
5198      * Must have a "start" and "end" pattern.
5199      */
5200     if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5201 						  pat_ptrs[ITEM_END] == NULL))
5202     {
5203 	not_enough = TRUE;
5204 	rest = NULL;
5205     }
5206 
5207     if (rest != NULL)
5208     {
5209 	/*
5210 	 * Check for trailing garbage or command.
5211 	 * If OK, add the item.
5212 	 */
5213 	eap->nextcmd = check_nextcmd(rest);
5214 	if (!ends_excmd(*rest) || eap->skip)
5215 	    rest = NULL;
5216 	else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
5217 		&& (syn_id = syn_check_group(arg,
5218 					   (int)(group_name_end - arg))) != 0)
5219 	{
5220 	    syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
5221 	    /*
5222 	     * Store the start/skip/end in the syn_items list
5223 	     */
5224 	    idx = curwin->w_s->b_syn_patterns.ga_len;
5225 	    for (item = ITEM_START; item <= ITEM_END; ++item)
5226 	    {
5227 		for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5228 		{
5229 		    SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5230 		    SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5231 		    SYN_ITEMS(curwin->w_s)[idx].sp_type =
5232 			    (item == ITEM_START) ? SPTYPE_START :
5233 			    (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
5234 		    SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5235 		    SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5236 		    SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5237 							  current_syn_inc_tag;
5238 		    SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
5239 							ppp->pp_matchgroup_id;
5240 #ifdef FEAT_CONCEAL
5241 		    SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
5242 #endif
5243 		    if (item == ITEM_START)
5244 		    {
5245 			SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
5246 							syn_opt_arg.cont_list;
5247 			SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
5248 						     syn_opt_arg.cont_in_list;
5249 			if (syn_opt_arg.cont_in_list != NULL)
5250 			    curwin->w_s->b_syn_containedin = TRUE;
5251 			SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
5252 							syn_opt_arg.next_list;
5253 		    }
5254 		    ++curwin->w_s->b_syn_patterns.ga_len;
5255 		    ++idx;
5256 #ifdef FEAT_FOLDING
5257 		    if (syn_opt_arg.flags & HL_FOLD)
5258 			++curwin->w_s->b_syn_folditems;
5259 #endif
5260 		}
5261 	    }
5262 
5263 	    redraw_curbuf_later(SOME_VALID);
5264 	    syn_stack_free_all(curwin->w_s);	/* Need to recompute all syntax. */
5265 	    success = TRUE;	    /* don't free the progs and patterns now */
5266 	}
5267     }
5268 
5269     /*
5270      * Free the allocated memory.
5271      */
5272     for (item = ITEM_START; item <= ITEM_END; ++item)
5273 	for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5274 	{
5275 	    if (!success)
5276 	    {
5277 		vim_regfree(ppp->pp_synp->sp_prog);
5278 		vim_free(ppp->pp_synp->sp_pattern);
5279 	    }
5280 	    vim_free(ppp->pp_synp);
5281 	    ppp_next = ppp->pp_next;
5282 	    vim_free(ppp);
5283 	}
5284 
5285     if (!success)
5286     {
5287 	vim_free(syn_opt_arg.cont_list);
5288 	vim_free(syn_opt_arg.cont_in_list);
5289 	vim_free(syn_opt_arg.next_list);
5290 	if (not_enough)
5291 	    EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5292 	else if (illegal || rest == NULL)
5293 	    EMSG2(_(e_invarg2), arg);
5294     }
5295 }
5296 
5297 /*
5298  * A simple syntax group ID comparison function suitable for use in qsort()
5299  */
5300     static int
5301 #ifdef __BORLANDC__
5302 _RTLENTRYF
5303 #endif
5304 syn_compare_stub(v1, v2)
5305     const void	*v1;
5306     const void	*v2;
5307 {
5308     const short	*s1 = v1;
5309     const short	*s2 = v2;
5310 
5311     return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5312 }
5313 
5314 /*
5315  * Combines lists of syntax clusters.
5316  * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5317  */
5318     static void
5319 syn_combine_list(clstr1, clstr2, list_op)
5320     short	**clstr1;
5321     short	**clstr2;
5322     int		list_op;
5323 {
5324     int		count1 = 0;
5325     int		count2 = 0;
5326     short	*g1;
5327     short	*g2;
5328     short	*clstr = NULL;
5329     int		count;
5330     int		round;
5331 
5332     /*
5333      * Handle degenerate cases.
5334      */
5335     if (*clstr2 == NULL)
5336 	return;
5337     if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5338     {
5339 	if (list_op == CLUSTER_REPLACE)
5340 	    vim_free(*clstr1);
5341 	if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5342 	    *clstr1 = *clstr2;
5343 	else
5344 	    vim_free(*clstr2);
5345 	return;
5346     }
5347 
5348     for (g1 = *clstr1; *g1; g1++)
5349 	++count1;
5350     for (g2 = *clstr2; *g2; g2++)
5351 	++count2;
5352 
5353     /*
5354      * For speed purposes, sort both lists.
5355      */
5356     qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5357     qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5358 
5359     /*
5360      * We proceed in two passes; in round 1, we count the elements to place
5361      * in the new list, and in round 2, we allocate and populate the new
5362      * list.  For speed, we use a mergesort-like method, adding the smaller
5363      * of the current elements in each list to the new list.
5364      */
5365     for (round = 1; round <= 2; round++)
5366     {
5367 	g1 = *clstr1;
5368 	g2 = *clstr2;
5369 	count = 0;
5370 
5371 	/*
5372 	 * First, loop through the lists until one of them is empty.
5373 	 */
5374 	while (*g1 && *g2)
5375 	{
5376 	    /*
5377 	     * We always want to add from the first list.
5378 	     */
5379 	    if (*g1 < *g2)
5380 	    {
5381 		if (round == 2)
5382 		    clstr[count] = *g1;
5383 		count++;
5384 		g1++;
5385 		continue;
5386 	    }
5387 	    /*
5388 	     * We only want to add from the second list if we're adding the
5389 	     * lists.
5390 	     */
5391 	    if (list_op == CLUSTER_ADD)
5392 	    {
5393 		if (round == 2)
5394 		    clstr[count] = *g2;
5395 		count++;
5396 	    }
5397 	    if (*g1 == *g2)
5398 		g1++;
5399 	    g2++;
5400 	}
5401 
5402 	/*
5403 	 * Now add the leftovers from whichever list didn't get finished
5404 	 * first.  As before, we only want to add from the second list if
5405 	 * we're adding the lists.
5406 	 */
5407 	for (; *g1; g1++, count++)
5408 	    if (round == 2)
5409 		clstr[count] = *g1;
5410 	if (list_op == CLUSTER_ADD)
5411 	    for (; *g2; g2++, count++)
5412 		if (round == 2)
5413 		    clstr[count] = *g2;
5414 
5415 	if (round == 1)
5416 	{
5417 	    /*
5418 	     * If the group ended up empty, we don't need to allocate any
5419 	     * space for it.
5420 	     */
5421 	    if (count == 0)
5422 	    {
5423 		clstr = NULL;
5424 		break;
5425 	    }
5426 	    clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5427 	    if (clstr == NULL)
5428 		break;
5429 	    clstr[count] = 0;
5430 	}
5431     }
5432 
5433     /*
5434      * Finally, put the new list in place.
5435      */
5436     vim_free(*clstr1);
5437     vim_free(*clstr2);
5438     *clstr1 = clstr;
5439 }
5440 
5441 /*
5442  * Lookup a syntax cluster name and return it's ID.
5443  * If it is not found, 0 is returned.
5444  */
5445     static int
5446 syn_scl_name2id(name)
5447     char_u	*name;
5448 {
5449     int		i;
5450     char_u	*name_u;
5451 
5452     /* Avoid using stricmp() too much, it's slow on some systems */
5453     name_u = vim_strsave_up(name);
5454     if (name_u == NULL)
5455 	return 0;
5456     for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5457 	if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5458 		&& STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
5459 	    break;
5460     vim_free(name_u);
5461     return (i < 0 ? 0 : i + SYNID_CLUSTER);
5462 }
5463 
5464 /*
5465  * Like syn_scl_name2id(), but take a pointer + length argument.
5466  */
5467     static int
5468 syn_scl_namen2id(linep, len)
5469     char_u  *linep;
5470     int	    len;
5471 {
5472     char_u  *name;
5473     int	    id = 0;
5474 
5475     name = vim_strnsave(linep, len);
5476     if (name != NULL)
5477     {
5478 	id = syn_scl_name2id(name);
5479 	vim_free(name);
5480     }
5481     return id;
5482 }
5483 
5484 /*
5485  * Find syntax cluster name in the table and return it's ID.
5486  * The argument is a pointer to the name and the length of the name.
5487  * If it doesn't exist yet, a new entry is created.
5488  * Return 0 for failure.
5489  */
5490     static int
5491 syn_check_cluster(pp, len)
5492     char_u	*pp;
5493     int		len;
5494 {
5495     int		id;
5496     char_u	*name;
5497 
5498     name = vim_strnsave(pp, len);
5499     if (name == NULL)
5500 	return 0;
5501 
5502     id = syn_scl_name2id(name);
5503     if (id == 0)			/* doesn't exist yet */
5504 	id = syn_add_cluster(name);
5505     else
5506 	vim_free(name);
5507     return id;
5508 }
5509 
5510 /*
5511  * Add new syntax cluster and return it's ID.
5512  * "name" must be an allocated string, it will be consumed.
5513  * Return 0 for failure.
5514  */
5515     static int
5516 syn_add_cluster(name)
5517     char_u	*name;
5518 {
5519     int		len;
5520 
5521     /*
5522      * First call for this growarray: init growing array.
5523      */
5524     if (curwin->w_s->b_syn_clusters.ga_data == NULL)
5525     {
5526 	curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5527 	curwin->w_s->b_syn_clusters.ga_growsize = 10;
5528     }
5529 
5530     len = curwin->w_s->b_syn_clusters.ga_len;
5531     if (len >= MAX_CLUSTER_ID)
5532     {
5533 	EMSG((char_u *)_("E848: Too many syntax clusters"));
5534 	vim_free(name);
5535 	return 0;
5536     }
5537 
5538     /*
5539      * Make room for at least one other cluster entry.
5540      */
5541     if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
5542     {
5543 	vim_free(name);
5544 	return 0;
5545     }
5546 
5547     vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5548     SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5549     SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5550     SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5551     ++curwin->w_s->b_syn_clusters.ga_len;
5552 
5553     if (STRICMP(name, "Spell") == 0)
5554 	curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
5555     if (STRICMP(name, "NoSpell") == 0)
5556 	curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
5557 
5558     return len + SYNID_CLUSTER;
5559 }
5560 
5561 /*
5562  * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5563  *		[add={groupname},..] [remove={groupname},..]".
5564  */
5565     static void
5566 syn_cmd_cluster(eap, syncing)
5567     exarg_T	*eap;
5568     int		syncing UNUSED;
5569 {
5570     char_u	*arg = eap->arg;
5571     char_u	*group_name_end;
5572     char_u	*rest;
5573     int		scl_id;
5574     short	*clstr_list;
5575     int		got_clstr = FALSE;
5576     int		opt_len;
5577     int		list_op;
5578 
5579     eap->nextcmd = find_nextcmd(arg);
5580     if (eap->skip)
5581 	return;
5582 
5583     rest = get_group_name(arg, &group_name_end);
5584 
5585     if (rest != NULL)
5586     {
5587 	scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5588 	if (scl_id == 0)
5589 	    return;
5590 	scl_id -= SYNID_CLUSTER;
5591 
5592 	for (;;)
5593 	{
5594 	    if (STRNICMP(rest, "add", 3) == 0
5595 		    && (vim_iswhite(rest[3]) || rest[3] == '='))
5596 	    {
5597 		opt_len = 3;
5598 		list_op = CLUSTER_ADD;
5599 	    }
5600 	    else if (STRNICMP(rest, "remove", 6) == 0
5601 		    && (vim_iswhite(rest[6]) || rest[6] == '='))
5602 	    {
5603 		opt_len = 6;
5604 		list_op = CLUSTER_SUBTRACT;
5605 	    }
5606 	    else if (STRNICMP(rest, "contains", 8) == 0
5607 			&& (vim_iswhite(rest[8]) || rest[8] == '='))
5608 	    {
5609 		opt_len = 8;
5610 		list_op = CLUSTER_REPLACE;
5611 	    }
5612 	    else
5613 		break;
5614 
5615 	    clstr_list = NULL;
5616 	    if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5617 	    {
5618 		EMSG2(_(e_invarg2), rest);
5619 		break;
5620 	    }
5621 	    syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
5622 			     &clstr_list, list_op);
5623 	    got_clstr = TRUE;
5624 	}
5625 
5626 	if (got_clstr)
5627 	{
5628 	    redraw_curbuf_later(SOME_VALID);
5629 	    syn_stack_free_all(curwin->w_s);	/* Need to recompute all. */
5630 	}
5631     }
5632 
5633     if (!got_clstr)
5634 	EMSG(_("E400: No cluster specified"));
5635     if (rest == NULL || !ends_excmd(*rest))
5636 	EMSG2(_(e_invarg2), arg);
5637 }
5638 
5639 /*
5640  * On first call for current buffer: Init growing array.
5641  */
5642     static void
5643 init_syn_patterns()
5644 {
5645     curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5646     curwin->w_s->b_syn_patterns.ga_growsize = 10;
5647 }
5648 
5649 /*
5650  * Get one pattern for a ":syntax match" or ":syntax region" command.
5651  * Stores the pattern and program in a synpat_T.
5652  * Returns a pointer to the next argument, or NULL in case of an error.
5653  */
5654     static char_u *
5655 get_syn_pattern(arg, ci)
5656     char_u	*arg;
5657     synpat_T	*ci;
5658 {
5659     char_u	*end;
5660     int		*p;
5661     int		idx;
5662     char_u	*cpo_save;
5663 
5664     /* need at least three chars */
5665     if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
5666 	return NULL;
5667 
5668     end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5669     if (*end != *arg)			    /* end delimiter not found */
5670     {
5671 	EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5672 	return NULL;
5673     }
5674     /* store the pattern and compiled regexp program */
5675     if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5676 	return NULL;
5677 
5678     /* Make 'cpoptions' empty, to avoid the 'l' flag */
5679     cpo_save = p_cpo;
5680     p_cpo = (char_u *)"";
5681     ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5682     p_cpo = cpo_save;
5683 
5684     if (ci->sp_prog == NULL)
5685 	return NULL;
5686     ci->sp_ic = curwin->w_s->b_syn_ic;
5687 #ifdef FEAT_PROFILE
5688     syn_clear_time(&ci->sp_time);
5689 #endif
5690 
5691     /*
5692      * Check for a match, highlight or region offset.
5693      */
5694     ++end;
5695     do
5696     {
5697 	for (idx = SPO_COUNT; --idx >= 0; )
5698 	    if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5699 		break;
5700 	if (idx >= 0)
5701 	{
5702 	    p = &(ci->sp_offsets[idx]);
5703 	    if (idx != SPO_LC_OFF)
5704 		switch (end[3])
5705 		{
5706 		    case 's':   break;
5707 		    case 'b':   break;
5708 		    case 'e':   idx += SPO_COUNT; break;
5709 		    default:    idx = -1; break;
5710 		}
5711 	    if (idx >= 0)
5712 	    {
5713 		ci->sp_off_flags |= (1 << idx);
5714 		if (idx == SPO_LC_OFF)	    /* lc=99 */
5715 		{
5716 		    end += 3;
5717 		    *p = getdigits(&end);
5718 
5719 		    /* "lc=" offset automatically sets "ms=" offset */
5720 		    if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5721 		    {
5722 			ci->sp_off_flags |= (1 << SPO_MS_OFF);
5723 			ci->sp_offsets[SPO_MS_OFF] = *p;
5724 		    }
5725 		}
5726 		else			    /* yy=x+99 */
5727 		{
5728 		    end += 4;
5729 		    if (*end == '+')
5730 		    {
5731 			++end;
5732 			*p = getdigits(&end);		/* positive offset */
5733 		    }
5734 		    else if (*end == '-')
5735 		    {
5736 			++end;
5737 			*p = -getdigits(&end);		/* negative offset */
5738 		    }
5739 		}
5740 		if (*end != ',')
5741 		    break;
5742 		++end;
5743 	    }
5744 	}
5745     } while (idx >= 0);
5746 
5747     if (!ends_excmd(*end) && !vim_iswhite(*end))
5748     {
5749 	EMSG2(_("E402: Garbage after pattern: %s"), arg);
5750 	return NULL;
5751     }
5752     return skipwhite(end);
5753 }
5754 
5755 /*
5756  * Handle ":syntax sync .." command.
5757  */
5758     static void
5759 syn_cmd_sync(eap, syncing)
5760     exarg_T	*eap;
5761     int		syncing UNUSED;
5762 {
5763     char_u	*arg_start = eap->arg;
5764     char_u	*arg_end;
5765     char_u	*key = NULL;
5766     char_u	*next_arg;
5767     int		illegal = FALSE;
5768     int		finished = FALSE;
5769     long	n;
5770     char_u	*cpo_save;
5771 
5772     if (ends_excmd(*arg_start))
5773     {
5774 	syn_cmd_list(eap, TRUE);
5775 	return;
5776     }
5777 
5778     while (!ends_excmd(*arg_start))
5779     {
5780 	arg_end = skiptowhite(arg_start);
5781 	next_arg = skipwhite(arg_end);
5782 	vim_free(key);
5783 	key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5784 	if (STRCMP(key, "CCOMMENT") == 0)
5785 	{
5786 	    if (!eap->skip)
5787 		curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
5788 	    if (!ends_excmd(*next_arg))
5789 	    {
5790 		arg_end = skiptowhite(next_arg);
5791 		if (!eap->skip)
5792 		    curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
5793 						   (int)(arg_end - next_arg));
5794 		next_arg = skipwhite(arg_end);
5795 	    }
5796 	    else if (!eap->skip)
5797 		curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5798 	}
5799 	else if (  STRNCMP(key, "LINES", 5) == 0
5800 		|| STRNCMP(key, "MINLINES", 8) == 0
5801 		|| STRNCMP(key, "MAXLINES", 8) == 0
5802 		|| STRNCMP(key, "LINEBREAKS", 10) == 0)
5803 	{
5804 	    if (key[4] == 'S')
5805 		arg_end = key + 6;
5806 	    else if (key[0] == 'L')
5807 		arg_end = key + 11;
5808 	    else
5809 		arg_end = key + 9;
5810 	    if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5811 	    {
5812 		illegal = TRUE;
5813 		break;
5814 	    }
5815 	    n = getdigits(&arg_end);
5816 	    if (!eap->skip)
5817 	    {
5818 		if (key[4] == 'B')
5819 		    curwin->w_s->b_syn_sync_linebreaks = n;
5820 		else if (key[1] == 'A')
5821 		    curwin->w_s->b_syn_sync_maxlines = n;
5822 		else
5823 		    curwin->w_s->b_syn_sync_minlines = n;
5824 	    }
5825 	}
5826 	else if (STRCMP(key, "FROMSTART") == 0)
5827 	{
5828 	    if (!eap->skip)
5829 	    {
5830 		curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5831 		curwin->w_s->b_syn_sync_maxlines = 0;
5832 	    }
5833 	}
5834 	else if (STRCMP(key, "LINECONT") == 0)
5835 	{
5836 	    if (*next_arg == NUL)	   /* missing pattern */
5837 	    {
5838 		illegal = TRUE;
5839 		break;
5840 	    }
5841 	    if (curwin->w_s->b_syn_linecont_pat != NULL)
5842 	    {
5843 		EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5844 		finished = TRUE;
5845 		break;
5846 	    }
5847 	    arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5848 	    if (*arg_end != *next_arg)	    /* end delimiter not found */
5849 	    {
5850 		illegal = TRUE;
5851 		break;
5852 	    }
5853 
5854 	    if (!eap->skip)
5855 	    {
5856 		/* store the pattern and compiled regexp program */
5857 		if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5858 				      (int)(arg_end - next_arg - 1))) == NULL)
5859 		{
5860 		    finished = TRUE;
5861 		    break;
5862 		}
5863 		curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
5864 
5865 		/* Make 'cpoptions' empty, to avoid the 'l' flag */
5866 		cpo_save = p_cpo;
5867 		p_cpo = (char_u *)"";
5868 		curwin->w_s->b_syn_linecont_prog =
5869 		       vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
5870 		p_cpo = cpo_save;
5871 #ifdef FEAT_PROFILE
5872 		syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5873 #endif
5874 
5875 		if (curwin->w_s->b_syn_linecont_prog == NULL)
5876 		{
5877 		    vim_free(curwin->w_s->b_syn_linecont_pat);
5878 		    curwin->w_s->b_syn_linecont_pat = NULL;
5879 		    finished = TRUE;
5880 		    break;
5881 		}
5882 	    }
5883 	    next_arg = skipwhite(arg_end + 1);
5884 	}
5885 	else
5886 	{
5887 	    eap->arg = next_arg;
5888 	    if (STRCMP(key, "MATCH") == 0)
5889 		syn_cmd_match(eap, TRUE);
5890 	    else if (STRCMP(key, "REGION") == 0)
5891 		syn_cmd_region(eap, TRUE);
5892 	    else if (STRCMP(key, "CLEAR") == 0)
5893 		syn_cmd_clear(eap, TRUE);
5894 	    else
5895 		illegal = TRUE;
5896 	    finished = TRUE;
5897 	    break;
5898 	}
5899 	arg_start = next_arg;
5900     }
5901     vim_free(key);
5902     if (illegal)
5903 	EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5904     else if (!finished)
5905     {
5906 	eap->nextcmd = check_nextcmd(arg_start);
5907 	redraw_curbuf_later(SOME_VALID);
5908 	syn_stack_free_all(curwin->w_s);	/* Need to recompute all syntax. */
5909     }
5910 }
5911 
5912 /*
5913  * Convert a line of highlight group names into a list of group ID numbers.
5914  * "arg" should point to the "contains" or "nextgroup" keyword.
5915  * "arg" is advanced to after the last group name.
5916  * Careful: the argument is modified (NULs added).
5917  * returns FAIL for some error, OK for success.
5918  */
5919     static int
5920 get_id_list(arg, keylen, list)
5921     char_u	**arg;
5922     int		keylen;		/* length of keyword */
5923     short	**list;		/* where to store the resulting list, if not
5924 				   NULL, the list is silently skipped! */
5925 {
5926     char_u	*p = NULL;
5927     char_u	*end;
5928     int		round;
5929     int		count;
5930     int		total_count = 0;
5931     short	*retval = NULL;
5932     char_u	*name;
5933     regmatch_T	regmatch;
5934     int		id;
5935     int		i;
5936     int		failed = FALSE;
5937 
5938     /*
5939      * We parse the list twice:
5940      * round == 1: count the number of items, allocate the array.
5941      * round == 2: fill the array with the items.
5942      * In round 1 new groups may be added, causing the number of items to
5943      * grow when a regexp is used.  In that case round 1 is done once again.
5944      */
5945     for (round = 1; round <= 2; ++round)
5946     {
5947 	/*
5948 	 * skip "contains"
5949 	 */
5950 	p = skipwhite(*arg + keylen);
5951 	if (*p != '=')
5952 	{
5953 	    EMSG2(_("E405: Missing equal sign: %s"), *arg);
5954 	    break;
5955 	}
5956 	p = skipwhite(p + 1);
5957 	if (ends_excmd(*p))
5958 	{
5959 	    EMSG2(_("E406: Empty argument: %s"), *arg);
5960 	    break;
5961 	}
5962 
5963 	/*
5964 	 * parse the arguments after "contains"
5965 	 */
5966 	count = 0;
5967 	while (!ends_excmd(*p))
5968 	{
5969 	    for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5970 		;
5971 	    name = alloc((int)(end - p + 3));	    /* leave room for "^$" */
5972 	    if (name == NULL)
5973 	    {
5974 		failed = TRUE;
5975 		break;
5976 	    }
5977 	    vim_strncpy(name + 1, p, end - p);
5978 	    if (       STRCMP(name + 1, "ALLBUT") == 0
5979 		    || STRCMP(name + 1, "ALL") == 0
5980 		    || STRCMP(name + 1, "TOP") == 0
5981 		    || STRCMP(name + 1, "CONTAINED") == 0)
5982 	    {
5983 		if (TOUPPER_ASC(**arg) != 'C')
5984 		{
5985 		    EMSG2(_("E407: %s not allowed here"), name + 1);
5986 		    failed = TRUE;
5987 		    vim_free(name);
5988 		    break;
5989 		}
5990 		if (count != 0)
5991 		{
5992 		    EMSG2(_("E408: %s must be first in contains list"), name + 1);
5993 		    failed = TRUE;
5994 		    vim_free(name);
5995 		    break;
5996 		}
5997 		if (name[1] == 'A')
5998 		    id = SYNID_ALLBUT;
5999 		else if (name[1] == 'T')
6000 		    id = SYNID_TOP;
6001 		else
6002 		    id = SYNID_CONTAINED;
6003 		id += current_syn_inc_tag;
6004 	    }
6005 	    else if (name[1] == '@')
6006 	    {
6007 		id = syn_check_cluster(name + 2, (int)(end - p - 1));
6008 	    }
6009 	    else
6010 	    {
6011 		/*
6012 		 * Handle full group name.
6013 		 */
6014 		if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6015 		    id = syn_check_group(name + 1, (int)(end - p));
6016 		else
6017 		{
6018 		    /*
6019 		     * Handle match of regexp with group names.
6020 		     */
6021 		    *name = '^';
6022 		    STRCAT(name, "$");
6023 		    regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6024 		    if (regmatch.regprog == NULL)
6025 		    {
6026 			failed = TRUE;
6027 			vim_free(name);
6028 			break;
6029 		    }
6030 
6031 		    regmatch.rm_ic = TRUE;
6032 		    id = 0;
6033 		    for (i = highlight_ga.ga_len; --i >= 0; )
6034 		    {
6035 			if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6036 								  (colnr_T)0))
6037 			{
6038 			    if (round == 2)
6039 			    {
6040 				/* Got more items than expected; can happen
6041 				 * when adding items that match:
6042 				 * "contains=a.*b,axb".
6043 				 * Go back to first round */
6044 				if (count >= total_count)
6045 				{
6046 				    vim_free(retval);
6047 				    round = 1;
6048 				}
6049 				else
6050 				    retval[count] = i + 1;
6051 			    }
6052 			    ++count;
6053 			    id = -1;	    /* remember that we found one */
6054 			}
6055 		    }
6056 		    vim_regfree(regmatch.regprog);
6057 		}
6058 	    }
6059 	    vim_free(name);
6060 	    if (id == 0)
6061 	    {
6062 		EMSG2(_("E409: Unknown group name: %s"), p);
6063 		failed = TRUE;
6064 		break;
6065 	    }
6066 	    if (id > 0)
6067 	    {
6068 		if (round == 2)
6069 		{
6070 		    /* Got more items than expected, go back to first round */
6071 		    if (count >= total_count)
6072 		    {
6073 			vim_free(retval);
6074 			round = 1;
6075 		    }
6076 		    else
6077 			retval[count] = id;
6078 		}
6079 		++count;
6080 	    }
6081 	    p = skipwhite(end);
6082 	    if (*p != ',')
6083 		break;
6084 	    p = skipwhite(p + 1);	/* skip comma in between arguments */
6085 	}
6086 	if (failed)
6087 	    break;
6088 	if (round == 1)
6089 	{
6090 	    retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6091 	    if (retval == NULL)
6092 		break;
6093 	    retval[count] = 0;	    /* zero means end of the list */
6094 	    total_count = count;
6095 	}
6096     }
6097 
6098     *arg = p;
6099     if (failed || retval == NULL)
6100     {
6101 	vim_free(retval);
6102 	return FAIL;
6103     }
6104 
6105     if (*list == NULL)
6106 	*list = retval;
6107     else
6108 	vim_free(retval);	/* list already found, don't overwrite it */
6109 
6110     return OK;
6111 }
6112 
6113 /*
6114  * Make a copy of an ID list.
6115  */
6116     static short *
6117 copy_id_list(list)
6118     short   *list;
6119 {
6120     int	    len;
6121     int	    count;
6122     short   *retval;
6123 
6124     if (list == NULL)
6125 	return NULL;
6126 
6127     for (count = 0; list[count]; ++count)
6128 	;
6129     len = (count + 1) * sizeof(short);
6130     retval = (short *)alloc((unsigned)len);
6131     if (retval != NULL)
6132 	mch_memmove(retval, list, (size_t)len);
6133 
6134     return retval;
6135 }
6136 
6137 /*
6138  * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6139  * "cur_si" can be NULL if not checking the "containedin" list.
6140  * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6141  * the current item.
6142  * This function is called very often, keep it fast!!
6143  */
6144     static int
6145 in_id_list(cur_si, list, ssp, contained)
6146     stateitem_T	*cur_si;	/* current item or NULL */
6147     short	*list;		/* id list */
6148     struct sp_syn *ssp;		/* group id and ":syn include" tag of group */
6149     int		contained;	/* group id is contained */
6150 {
6151     int		retval;
6152     short	*scl_list;
6153     short	item;
6154     short	id = ssp->id;
6155     static int	depth = 0;
6156     int		r;
6157 
6158     /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
6159     if (cur_si != NULL && ssp->cont_in_list != NULL
6160 					    && !(cur_si->si_flags & HL_MATCH))
6161     {
6162 	/* Ignore transparent items without a contains argument.  Double check
6163 	 * that we don't go back past the first one. */
6164 	while ((cur_si->si_flags & HL_TRANS_CONT)
6165 		&& cur_si > (stateitem_T *)(current_state.ga_data))
6166 	    --cur_si;
6167 	/* cur_si->si_idx is -1 for keywords, these never contain anything. */
6168 	if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
6169 		&(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6170 		  SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
6171 	    return TRUE;
6172     }
6173 
6174     if (list == NULL)
6175 	return FALSE;
6176 
6177     /*
6178      * If list is ID_LIST_ALL, we are in a transparent item that isn't
6179      * inside anything.  Only allow not-contained groups.
6180      */
6181     if (list == ID_LIST_ALL)
6182 	return !contained;
6183 
6184     /*
6185      * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6186      * contains list.  We also require that "id" is at the same ":syn include"
6187      * level as the list.
6188      */
6189     item = *list;
6190     if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6191     {
6192 	if (item < SYNID_TOP)
6193 	{
6194 	    /* ALL or ALLBUT: accept all groups in the same file */
6195 	    if (item - SYNID_ALLBUT != ssp->inc_tag)
6196 		return FALSE;
6197 	}
6198 	else if (item < SYNID_CONTAINED)
6199 	{
6200 	    /* TOP: accept all not-contained groups in the same file */
6201 	    if (item - SYNID_TOP != ssp->inc_tag || contained)
6202 		return FALSE;
6203 	}
6204 	else
6205 	{
6206 	    /* CONTAINED: accept all contained groups in the same file */
6207 	    if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6208 		return FALSE;
6209 	}
6210 	item = *++list;
6211 	retval = FALSE;
6212     }
6213     else
6214 	retval = TRUE;
6215 
6216     /*
6217      * Return "retval" if id is in the contains list.
6218      */
6219     while (item != 0)
6220     {
6221 	if (item == id)
6222 	    return retval;
6223 	if (item >= SYNID_CLUSTER)
6224 	{
6225 	    scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
6226 	    /* restrict recursiveness to 30 to avoid an endless loop for a
6227 	     * cluster that includes itself (indirectly) */
6228 	    if (scl_list != NULL && depth < 30)
6229 	    {
6230 		++depth;
6231 		r = in_id_list(NULL, scl_list, ssp, contained);
6232 		--depth;
6233 		if (r)
6234 		    return retval;
6235 	    }
6236 	}
6237 	item = *++list;
6238     }
6239     return !retval;
6240 }
6241 
6242 struct subcommand
6243 {
6244     char    *name;				/* subcommand name */
6245     void    (*func)__ARGS((exarg_T *, int));	/* function to call */
6246 };
6247 
6248 static struct subcommand subcommands[] =
6249 {
6250     {"case",		syn_cmd_case},
6251     {"clear",		syn_cmd_clear},
6252     {"cluster",		syn_cmd_cluster},
6253     {"conceal",		syn_cmd_conceal},
6254     {"enable",		syn_cmd_enable},
6255     {"include",		syn_cmd_include},
6256     {"keyword",		syn_cmd_keyword},
6257     {"list",		syn_cmd_list},
6258     {"manual",		syn_cmd_manual},
6259     {"match",		syn_cmd_match},
6260     {"on",		syn_cmd_on},
6261     {"off",		syn_cmd_off},
6262     {"region",		syn_cmd_region},
6263     {"reset",		syn_cmd_reset},
6264     {"spell",		syn_cmd_spell},
6265     {"sync",		syn_cmd_sync},
6266     {"",		syn_cmd_list},
6267     {NULL, NULL}
6268 };
6269 
6270 /*
6271  * ":syntax".
6272  * This searches the subcommands[] table for the subcommand name, and calls a
6273  * syntax_subcommand() function to do the rest.
6274  */
6275     void
6276 ex_syntax(eap)
6277     exarg_T	*eap;
6278 {
6279     char_u	*arg = eap->arg;
6280     char_u	*subcmd_end;
6281     char_u	*subcmd_name;
6282     int		i;
6283 
6284     syn_cmdlinep = eap->cmdlinep;
6285 
6286     /* isolate subcommand name */
6287     for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6288 	;
6289     subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6290     if (subcmd_name != NULL)
6291     {
6292 	if (eap->skip)		/* skip error messages for all subcommands */
6293 	    ++emsg_skip;
6294 	for (i = 0; ; ++i)
6295 	{
6296 	    if (subcommands[i].name == NULL)
6297 	    {
6298 		EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6299 		break;
6300 	    }
6301 	    if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6302 	    {
6303 		eap->arg = skipwhite(subcmd_end);
6304 		(subcommands[i].func)(eap, FALSE);
6305 		break;
6306 	    }
6307 	}
6308 	vim_free(subcmd_name);
6309 	if (eap->skip)
6310 	    --emsg_skip;
6311     }
6312 }
6313 
6314     void
6315 ex_ownsyntax(eap)
6316     exarg_T	*eap;
6317 {
6318     char_u	*old_value;
6319     char_u	*new_value;
6320 
6321     if (curwin->w_s == &curwin->w_buffer->b_s)
6322     {
6323 	curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6324 	memset(curwin->w_s, 0, sizeof(synblock_T));
6325 	hash_init(&curwin->w_s->b_keywtab);
6326 	hash_init(&curwin->w_s->b_keywtab_ic);
6327 #ifdef FEAT_SPELL
6328 	/* TODO: keep the spell checking as it was. */
6329 	curwin->w_p_spell = FALSE;	/* No spell checking */
6330 	clear_string_option(&curwin->w_s->b_p_spc);
6331 	clear_string_option(&curwin->w_s->b_p_spf);
6332 	clear_string_option(&curwin->w_s->b_p_spl);
6333 #endif
6334     }
6335 
6336     /* save value of b:current_syntax */
6337     old_value = get_var_value((char_u *)"b:current_syntax");
6338     if (old_value != NULL)
6339 	old_value = vim_strsave(old_value);
6340 
6341     /* Apply the "syntax" autocommand event, this finds and loads the syntax
6342      * file. */
6343     apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
6344 
6345     /* move value of b:current_syntax to w:current_syntax */
6346     new_value = get_var_value((char_u *)"b:current_syntax");
6347     if (new_value != NULL)
6348 	set_internal_string_var((char_u *)"w:current_syntax", new_value);
6349 
6350     /* restore value of b:current_syntax */
6351     if (old_value == NULL)
6352 	do_unlet((char_u *)"b:current_syntax", TRUE);
6353     else
6354     {
6355 	set_internal_string_var((char_u *)"b:current_syntax", old_value);
6356 	vim_free(old_value);
6357     }
6358 }
6359 
6360     int
6361 syntax_present(win)
6362     win_T	*win;
6363 {
6364     return (win->w_s->b_syn_patterns.ga_len != 0
6365 	    || win->w_s->b_syn_clusters.ga_len != 0
6366 	    || win->w_s->b_keywtab.ht_used > 0
6367 	    || win->w_s->b_keywtab_ic.ht_used > 0);
6368 }
6369 
6370 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6371 
6372 static enum
6373 {
6374     EXP_SUBCMD,	    /* expand ":syn" sub-commands */
6375     EXP_CASE	    /* expand ":syn case" arguments */
6376 } expand_what;
6377 
6378 /*
6379  * Reset include_link, include_default, include_none to 0.
6380  * Called when we are done expanding.
6381  */
6382     void
6383 reset_expand_highlight()
6384 {
6385     include_link = include_default = include_none = 0;
6386 }
6387 
6388 /*
6389  * Handle command line completion for :match and :echohl command: Add "None"
6390  * as highlight group.
6391  */
6392     void
6393 set_context_in_echohl_cmd(xp, arg)
6394     expand_T	*xp;
6395     char_u	*arg;
6396 {
6397     xp->xp_context = EXPAND_HIGHLIGHT;
6398     xp->xp_pattern = arg;
6399     include_none = 1;
6400 }
6401 
6402 /*
6403  * Handle command line completion for :syntax command.
6404  */
6405     void
6406 set_context_in_syntax_cmd(xp, arg)
6407     expand_T	*xp;
6408     char_u	*arg;
6409 {
6410     char_u	*p;
6411 
6412     /* Default: expand subcommands */
6413     xp->xp_context = EXPAND_SYNTAX;
6414     expand_what = EXP_SUBCMD;
6415     xp->xp_pattern = arg;
6416     include_link = 0;
6417     include_default = 0;
6418 
6419     /* (part of) subcommand already typed */
6420     if (*arg != NUL)
6421     {
6422 	p = skiptowhite(arg);
6423 	if (*p != NUL)		    /* past first word */
6424 	{
6425 	    xp->xp_pattern = skipwhite(p);
6426 	    if (*skiptowhite(xp->xp_pattern) != NUL)
6427 		xp->xp_context = EXPAND_NOTHING;
6428 	    else if (STRNICMP(arg, "case", p - arg) == 0)
6429 		expand_what = EXP_CASE;
6430 	    else if (  STRNICMP(arg, "keyword", p - arg) == 0
6431 		    || STRNICMP(arg, "region", p - arg) == 0
6432 		    || STRNICMP(arg, "match", p - arg) == 0
6433 		    || STRNICMP(arg, "list", p - arg) == 0)
6434 		xp->xp_context = EXPAND_HIGHLIGHT;
6435 	    else
6436 		xp->xp_context = EXPAND_NOTHING;
6437 	}
6438     }
6439 }
6440 
6441 static char *(case_args[]) = {"match", "ignore", NULL};
6442 
6443 /*
6444  * Function given to ExpandGeneric() to obtain the list syntax names for
6445  * expansion.
6446  */
6447     char_u *
6448 get_syntax_name(xp, idx)
6449     expand_T	*xp UNUSED;
6450     int		idx;
6451 {
6452     if (expand_what == EXP_SUBCMD)
6453 	return (char_u *)subcommands[idx].name;
6454     return (char_u *)case_args[idx];
6455 }
6456 
6457 #endif /* FEAT_CMDL_COMPL */
6458 
6459 /*
6460  * Function called for expression evaluation: get syntax ID at file position.
6461  */
6462     int
6463 syn_get_id(wp, lnum, col, trans, spellp, keep_state)
6464     win_T	*wp;
6465     long	lnum;
6466     colnr_T	col;
6467     int		trans;	     /* remove transparency */
6468     int		*spellp;     /* return: can do spell checking */
6469     int		keep_state;  /* keep state of char at "col" */
6470 {
6471     /* When the position is not after the current position and in the same
6472      * line of the same buffer, need to restart parsing. */
6473     if (wp->w_buffer != syn_buf
6474 	    || lnum != current_lnum
6475 	    || col < current_col)
6476 	syntax_start(wp, lnum);
6477 
6478     (void)get_syntax_attr(col, spellp, keep_state);
6479 
6480     return (trans ? current_trans_id : current_id);
6481 }
6482 
6483 #if defined(FEAT_CONCEAL) || defined(PROTO)
6484 /*
6485  * Get extra information about the syntax item.  Must be called right after
6486  * get_syntax_attr().
6487  * Stores the current item sequence nr in "*seqnrp".
6488  * Returns the current flags.
6489  */
6490     int
6491 get_syntax_info(seqnrp)
6492     int		*seqnrp;
6493 {
6494     *seqnrp = current_seqnr;
6495     return current_flags;
6496 }
6497 
6498 /*
6499  * Return conceal substitution character
6500  */
6501     int
6502 syn_get_sub_char()
6503 {
6504     return current_sub_char;
6505 }
6506 #endif
6507 
6508 #if defined(FEAT_EVAL) || defined(PROTO)
6509 /*
6510  * Return the syntax ID at position "i" in the current stack.
6511  * The caller must have called syn_get_id() before to fill the stack.
6512  * Returns -1 when "i" is out of range.
6513  */
6514     int
6515 syn_get_stack_item(i)
6516     int i;
6517 {
6518     if (i >= current_state.ga_len)
6519     {
6520 	/* Need to invalidate the state, because we didn't properly finish it
6521 	 * for the last character, "keep_state" was TRUE. */
6522 	invalidate_current_state();
6523 	current_col = MAXCOL;
6524 	return -1;
6525     }
6526     return CUR_STATE(i).si_id;
6527 }
6528 #endif
6529 
6530 #if defined(FEAT_FOLDING) || defined(PROTO)
6531 /*
6532  * Function called to get folding level for line "lnum" in window "wp".
6533  */
6534     int
6535 syn_get_foldlevel(wp, lnum)
6536     win_T	*wp;
6537     long	lnum;
6538 {
6539     int		level = 0;
6540     int		i;
6541 
6542     /* Return quickly when there are no fold items at all. */
6543     if (wp->w_s->b_syn_folditems != 0)
6544     {
6545 	syntax_start(wp, lnum);
6546 
6547 	for (i = 0; i < current_state.ga_len; ++i)
6548 	    if (CUR_STATE(i).si_flags & HL_FOLD)
6549 		++level;
6550     }
6551     if (level > wp->w_p_fdn)
6552     {
6553 	level = wp->w_p_fdn;
6554 	if (level < 0)
6555 	    level = 0;
6556     }
6557     return level;
6558 }
6559 #endif
6560 
6561 #if defined(FEAT_PROFILE) || defined(PROTO)
6562 /*
6563  * ":syntime".
6564  */
6565     void
6566 ex_syntime(eap)
6567     exarg_T	*eap;
6568 {
6569     if (STRCMP(eap->arg, "on") == 0)
6570 	syn_time_on = TRUE;
6571     else if (STRCMP(eap->arg, "off") == 0)
6572 	syn_time_on = FALSE;
6573     else if (STRCMP(eap->arg, "clear") == 0)
6574 	syntime_clear();
6575     else if (STRCMP(eap->arg, "report") == 0)
6576 	syntime_report();
6577     else
6578 	EMSG2(_(e_invarg2), eap->arg);
6579 }
6580 
6581     static void
6582 syn_clear_time(st)
6583     syn_time_T *st;
6584 {
6585     profile_zero(&st->total);
6586     profile_zero(&st->slowest);
6587     st->count = 0;
6588     st->match = 0;
6589 }
6590 
6591 /*
6592  * Clear the syntax timing for the current buffer.
6593  */
6594     static void
6595 syntime_clear()
6596 {
6597     int		idx;
6598     synpat_T	*spp;
6599 
6600     if (!syntax_present(curwin))
6601     {
6602 	MSG(_(msg_no_items));
6603 	return;
6604     }
6605     for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6606     {
6607 	spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6608 	syn_clear_time(&spp->sp_time);
6609     }
6610 }
6611 
6612 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6613 /*
6614  * Function given to ExpandGeneric() to obtain the possible arguments of the
6615  * ":syntime {on,off,clear,report}" command.
6616  */
6617     char_u *
6618 get_syntime_arg(xp, idx)
6619     expand_T	*xp UNUSED;
6620     int		idx;
6621 {
6622     switch (idx)
6623     {
6624 	case 0: return (char_u *)"on";
6625 	case 1: return (char_u *)"off";
6626 	case 2: return (char_u *)"clear";
6627 	case 3: return (char_u *)"report";
6628     }
6629     return NULL;
6630 }
6631 #endif
6632 
6633 typedef struct
6634 {
6635     proftime_T	total;
6636     int		count;
6637     int		match;
6638     proftime_T	slowest;
6639     proftime_T	average;
6640     int		id;
6641     char_u	*pattern;
6642 } time_entry_T;
6643 
6644     static int
6645 #ifdef __BORLANDC__
6646 _RTLENTRYF
6647 #endif
6648 syn_compare_syntime(v1, v2)
6649     const void	*v1;
6650     const void	*v2;
6651 {
6652     const time_entry_T	*s1 = v1;
6653     const time_entry_T	*s2 = v2;
6654 
6655     return profile_cmp(&s1->total, &s2->total);
6656 }
6657 
6658 /*
6659  * Clear the syntax timing for the current buffer.
6660  */
6661     static void
6662 syntime_report()
6663 {
6664     int		idx;
6665     synpat_T	*spp;
6666 # ifdef FEAT_FLOAT
6667     proftime_T	tm;
6668 # endif
6669     int		len;
6670     proftime_T	total_total;
6671     int		total_count = 0;
6672     garray_T    ga;
6673     time_entry_T *p;
6674 
6675     if (!syntax_present(curwin))
6676     {
6677 	MSG(_(msg_no_items));
6678 	return;
6679     }
6680 
6681     ga_init2(&ga, sizeof(time_entry_T), 50);
6682     profile_zero(&total_total);
6683     for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6684     {
6685 	spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6686 	if (spp->sp_time.count > 0)
6687 	{
6688 	    (void)ga_grow(&ga, 1);
6689 	    p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6690 	    p->total = spp->sp_time.total;
6691 	    profile_add(&total_total, &spp->sp_time.total);
6692 	    p->count = spp->sp_time.count;
6693 	    p->match = spp->sp_time.match;
6694 	    total_count += spp->sp_time.count;
6695 	    p->slowest = spp->sp_time.slowest;
6696 # ifdef FEAT_FLOAT
6697 	    profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6698 	    p->average = tm;
6699 # endif
6700 	    p->id = spp->sp_syn.id;
6701 	    p->pattern = spp->sp_pattern;
6702 	    ++ga.ga_len;
6703 	}
6704     }
6705 
6706     /* sort on total time */
6707     qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
6708 							 syn_compare_syntime);
6709 
6710     MSG_PUTS_TITLE(_("  TOTAL      COUNT  MATCH   SLOWEST     AVERAGE   NAME               PATTERN"));
6711     MSG_PUTS("\n");
6712     for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6713     {
6714 	spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6715 	p = ((time_entry_T *)ga.ga_data) + idx;
6716 
6717 	MSG_PUTS(profile_msg(&p->total));
6718 	MSG_PUTS(" "); /* make sure there is always a separating space */
6719 	msg_advance(13);
6720 	msg_outnum(p->count);
6721 	MSG_PUTS(" ");
6722 	msg_advance(20);
6723 	msg_outnum(p->match);
6724 	MSG_PUTS(" ");
6725 	msg_advance(26);
6726 	MSG_PUTS(profile_msg(&p->slowest));
6727 	MSG_PUTS(" ");
6728 	msg_advance(38);
6729 # ifdef FEAT_FLOAT
6730 	MSG_PUTS(profile_msg(&p->average));
6731 	MSG_PUTS(" ");
6732 # endif
6733 	msg_advance(50);
6734 	msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6735 	MSG_PUTS(" ");
6736 
6737 	msg_advance(69);
6738 	if (Columns < 80)
6739 	    len = 20; /* will wrap anyway */
6740 	else
6741 	    len = Columns - 70;
6742 	if (len > (int)STRLEN(p->pattern))
6743 	    len = (int)STRLEN(p->pattern);
6744 	msg_outtrans_len(p->pattern, len);
6745 	MSG_PUTS("\n");
6746     }
6747     ga_clear(&ga);
6748     if (!got_int)
6749     {
6750 	MSG_PUTS("\n");
6751 	MSG_PUTS(profile_msg(&total_total));
6752 	msg_advance(13);
6753 	msg_outnum(total_count);
6754 	MSG_PUTS("\n");
6755     }
6756 }
6757 #endif
6758 
6759 #endif /* FEAT_SYN_HL */
6760 
6761 /**************************************
6762  *  Highlighting stuff		      *
6763  **************************************/
6764 
6765 /*
6766  * The default highlight groups.  These are compiled-in for fast startup and
6767  * they still work when the runtime files can't be found.
6768  * When making changes here, also change runtime/colors/default.vim!
6769  * The #ifdefs are needed to reduce the amount of static data.  Helps to make
6770  * the 16 bit DOS (museum) version compile.
6771  */
6772 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
6773 # define CENT(a, b) b
6774 #else
6775 # define CENT(a, b) a
6776 #endif
6777 static char *(highlight_init_both[]) =
6778     {
6779 	CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6780 	     "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6781 	CENT("IncSearch term=reverse cterm=reverse",
6782 	     "IncSearch term=reverse cterm=reverse gui=reverse"),
6783 	CENT("ModeMsg term=bold cterm=bold",
6784 	     "ModeMsg term=bold cterm=bold gui=bold"),
6785 	CENT("NonText term=bold ctermfg=Blue",
6786 	     "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6787 	CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6788 	     "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6789 	CENT("StatusLineNC term=reverse cterm=reverse",
6790 	     "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6791 #ifdef FEAT_VERTSPLIT
6792 	CENT("VertSplit term=reverse cterm=reverse",
6793 	     "VertSplit term=reverse cterm=reverse gui=reverse"),
6794 #endif
6795 #ifdef FEAT_CLIPBOARD
6796 	CENT("VisualNOS term=underline,bold cterm=underline,bold",
6797 	     "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
6798 #endif
6799 #ifdef FEAT_DIFF
6800 	CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6801 	     "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
6802 #endif
6803 #ifdef FEAT_INS_EXPAND
6804 	CENT("PmenuSbar ctermbg=Grey",
6805 	     "PmenuSbar ctermbg=Grey guibg=Grey"),
6806 #endif
6807 #ifdef FEAT_WINDOWS
6808 	CENT("TabLineSel term=bold cterm=bold",
6809 	     "TabLineSel term=bold cterm=bold gui=bold"),
6810 	CENT("TabLineFill term=reverse cterm=reverse",
6811 	     "TabLineFill term=reverse cterm=reverse gui=reverse"),
6812 #endif
6813 #ifdef FEAT_GUI
6814 	"Cursor guibg=fg guifg=bg",
6815 	"lCursor guibg=fg guifg=bg", /* should be different, but what? */
6816 #endif
6817 	NULL
6818     };
6819 
6820 static char *(highlight_init_light[]) =
6821     {
6822 	CENT("Directory term=bold ctermfg=DarkBlue",
6823 	     "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6824 	CENT("LineNr term=underline ctermfg=Brown",
6825 	     "LineNr term=underline ctermfg=Brown guifg=Brown"),
6826 	CENT("CursorLineNr term=bold ctermfg=Brown",
6827 	     "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6828 	CENT("MoreMsg term=bold ctermfg=DarkGreen",
6829 	     "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6830 	CENT("Question term=standout ctermfg=DarkGreen",
6831 	     "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6832 	CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6833 	     "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
6834 #ifdef FEAT_SPELL
6835 	CENT("SpellBad term=reverse ctermbg=LightRed",
6836 	     "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6837 	CENT("SpellCap term=reverse ctermbg=LightBlue",
6838 	     "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6839 	CENT("SpellRare term=reverse ctermbg=LightMagenta",
6840 	     "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6841 	CENT("SpellLocal term=underline ctermbg=Cyan",
6842 	     "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
6843 #endif
6844 #ifdef FEAT_INS_EXPAND
6845 	CENT("PmenuThumb ctermbg=Black",
6846 	     "PmenuThumb ctermbg=Black guibg=Black"),
6847 	CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6848 	     "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6849 	CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6850 	     "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
6851 #endif
6852 	CENT("SpecialKey term=bold ctermfg=DarkBlue",
6853 	     "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6854 	CENT("Title term=bold ctermfg=DarkMagenta",
6855 	     "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6856 	CENT("WarningMsg term=standout ctermfg=DarkRed",
6857 	     "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
6858 #ifdef FEAT_WILDMENU
6859 	CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6860 	     "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6861 #endif
6862 #ifdef FEAT_FOLDING
6863 	CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6864 	     "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6865 	CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6866 	     "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
6867 #endif
6868 #ifdef FEAT_SIGNS
6869 	CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6870 	     "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
6871 #endif
6872 	CENT("Visual term=reverse",
6873 	     "Visual term=reverse guibg=LightGrey"),
6874 #ifdef FEAT_DIFF
6875 	CENT("DiffAdd term=bold ctermbg=LightBlue",
6876 	     "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6877 	CENT("DiffChange term=bold ctermbg=LightMagenta",
6878 	     "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6879 	CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6880 	     "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
6881 #endif
6882 #ifdef FEAT_WINDOWS
6883 	CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6884 	     "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
6885 #endif
6886 #ifdef FEAT_SYN_HL
6887 	CENT("CursorColumn term=reverse ctermbg=LightGrey",
6888 	     "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6889 	CENT("CursorLine term=underline cterm=underline",
6890 	     "CursorLine term=underline cterm=underline guibg=Grey90"),
6891 	CENT("ColorColumn term=reverse ctermbg=LightRed",
6892 	     "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
6893 #endif
6894 #ifdef FEAT_CONCEAL
6895 	CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6896 	     "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6897 #endif
6898 #ifdef FEAT_AUTOCMD
6899 	CENT("MatchParen term=reverse ctermbg=Cyan",
6900 	     "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6901 #endif
6902 #ifdef FEAT_GUI
6903 	"Normal gui=NONE",
6904 #endif
6905 	NULL
6906     };
6907 
6908 static char *(highlight_init_dark[]) =
6909     {
6910 	CENT("Directory term=bold ctermfg=LightCyan",
6911 	     "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6912 	CENT("LineNr term=underline ctermfg=Yellow",
6913 	     "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6914 	CENT("CursorLineNr term=bold ctermfg=Yellow",
6915 	     "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
6916 	CENT("MoreMsg term=bold ctermfg=LightGreen",
6917 	     "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6918 	CENT("Question term=standout ctermfg=LightGreen",
6919 	     "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6920 	CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6921 	     "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6922 	CENT("SpecialKey term=bold ctermfg=LightBlue",
6923 	     "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
6924 #ifdef FEAT_SPELL
6925 	CENT("SpellBad term=reverse ctermbg=Red",
6926 	     "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6927 	CENT("SpellCap term=reverse ctermbg=Blue",
6928 	     "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6929 	CENT("SpellRare term=reverse ctermbg=Magenta",
6930 	     "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6931 	CENT("SpellLocal term=underline ctermbg=Cyan",
6932 	     "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
6933 #endif
6934 #ifdef FEAT_INS_EXPAND
6935 	CENT("PmenuThumb ctermbg=White",
6936 	     "PmenuThumb ctermbg=White guibg=White"),
6937 	CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6938 	     "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
6939 	CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6940 	     "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
6941 #endif
6942 	CENT("Title term=bold ctermfg=LightMagenta",
6943 	     "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6944 	CENT("WarningMsg term=standout ctermfg=LightRed",
6945 	     "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
6946 #ifdef FEAT_WILDMENU
6947 	CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6948 	     "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6949 #endif
6950 #ifdef FEAT_FOLDING
6951 	CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6952 	     "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6953 	CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6954 	     "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
6955 #endif
6956 #ifdef FEAT_SIGNS
6957 	CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6958 	     "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
6959 #endif
6960 	CENT("Visual term=reverse",
6961 	     "Visual term=reverse guibg=DarkGrey"),
6962 #ifdef FEAT_DIFF
6963 	CENT("DiffAdd term=bold ctermbg=DarkBlue",
6964 	     "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6965 	CENT("DiffChange term=bold ctermbg=DarkMagenta",
6966 	     "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6967 	CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6968 	     "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
6969 #endif
6970 #ifdef FEAT_WINDOWS
6971 	CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6972 	     "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
6973 #endif
6974 #ifdef FEAT_SYN_HL
6975 	CENT("CursorColumn term=reverse ctermbg=DarkGrey",
6976 	     "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
6977 	CENT("CursorLine term=underline cterm=underline",
6978 	     "CursorLine term=underline cterm=underline guibg=Grey40"),
6979 	CENT("ColorColumn term=reverse ctermbg=DarkRed",
6980 	     "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
6981 #endif
6982 #ifdef FEAT_AUTOCMD
6983 	CENT("MatchParen term=reverse ctermbg=DarkCyan",
6984 	     "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
6985 #endif
6986 #ifdef FEAT_CONCEAL
6987 	CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6988 	     "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6989 #endif
6990 #ifdef FEAT_GUI
6991 	"Normal gui=NONE",
6992 #endif
6993 	NULL
6994     };
6995 
6996     void
6997 init_highlight(both, reset)
6998     int		both;	    /* include groups where 'bg' doesn't matter */
6999     int		reset;	    /* clear group first */
7000 {
7001     int		i;
7002     char	**pp;
7003     static int	had_both = FALSE;
7004 #ifdef FEAT_EVAL
7005     char_u	*p;
7006 
7007     /*
7008      * Try finding the color scheme file.  Used when a color file was loaded
7009      * and 'background' or 't_Co' is changed.
7010      */
7011     p = get_var_value((char_u *)"g:colors_name");
7012     if (p != NULL)
7013     {
7014        /* The value of g:colors_name could be freed when sourcing the script,
7015 	* making "p" invalid, so copy it. */
7016        char_u *copy_p = vim_strsave(p);
7017        int    r;
7018 
7019        if (copy_p != NULL)
7020        {
7021 	   r = load_colors(copy_p);
7022 	   vim_free(copy_p);
7023 	   if (r == OK)
7024 	       return;
7025        }
7026     }
7027 
7028 #endif
7029 
7030     /*
7031      * Didn't use a color file, use the compiled-in colors.
7032      */
7033     if (both)
7034     {
7035 	had_both = TRUE;
7036 	pp = highlight_init_both;
7037 	for (i = 0; pp[i] != NULL; ++i)
7038 	    do_highlight((char_u *)pp[i], reset, TRUE);
7039     }
7040     else if (!had_both)
7041 	/* Don't do anything before the call with both == TRUE from main().
7042 	 * Not everything has been setup then, and that call will overrule
7043 	 * everything anyway. */
7044 	return;
7045 
7046     if (*p_bg == 'l')
7047 	pp = highlight_init_light;
7048     else
7049 	pp = highlight_init_dark;
7050     for (i = 0; pp[i] != NULL; ++i)
7051 	do_highlight((char_u *)pp[i], reset, TRUE);
7052 
7053     /* Reverse looks ugly, but grey may not work for 8 colors.  Thus let it
7054      * depend on the number of colors available.
7055      * With 8 colors brown is equal to yellow, need to use black for Search fg
7056      * to avoid Statement highlighted text disappears.
7057      * Clear the attributes, needed when changing the t_Co value. */
7058     if (t_colors > 8)
7059 	do_highlight((char_u *)(*p_bg == 'l'
7060 		    ? "Visual cterm=NONE ctermbg=LightGrey"
7061 		    : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
7062     else
7063     {
7064 	do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7065 								 FALSE, TRUE);
7066 	if (*p_bg == 'l')
7067 	    do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7068     }
7069 
7070 #ifdef FEAT_SYN_HL
7071     /*
7072      * If syntax highlighting is enabled load the highlighting for it.
7073      */
7074     if (get_var_value((char_u *)"g:syntax_on") != NULL)
7075     {
7076 	static int	recursive = 0;
7077 
7078 	if (recursive >= 5)
7079 	    EMSG(_("E679: recursive loop loading syncolor.vim"));
7080 	else
7081 	{
7082 	    ++recursive;
7083 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
7084 	    --recursive;
7085 	}
7086     }
7087 #endif
7088 }
7089 
7090 /*
7091  * Load color file "name".
7092  * Return OK for success, FAIL for failure.
7093  */
7094     int
7095 load_colors(name)
7096     char_u	*name;
7097 {
7098     char_u	*buf;
7099     int		retval = FAIL;
7100     static int	recursive = FALSE;
7101 
7102     /* When being called recursively, this is probably because setting
7103      * 'background' caused the highlighting to be reloaded.  This means it is
7104      * working, thus we should return OK. */
7105     if (recursive)
7106 	return OK;
7107 
7108     recursive = TRUE;
7109     buf = alloc((unsigned)(STRLEN(name) + 12));
7110     if (buf != NULL)
7111     {
7112 	sprintf((char *)buf, "colors/%s.vim", name);
7113 	retval = source_runtime(buf, FALSE);
7114 	vim_free(buf);
7115 #ifdef FEAT_AUTOCMD
7116 	apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
7117 #endif
7118     }
7119     recursive = FALSE;
7120 
7121     return retval;
7122 }
7123 
7124 /*
7125  * Handle the ":highlight .." command.
7126  * When using ":hi clear" this is called recursively for each group with
7127  * "forceit" and "init" both TRUE.
7128  */
7129     void
7130 do_highlight(line, forceit, init)
7131     char_u	*line;
7132     int		forceit;
7133     int		init;	    /* TRUE when called for initializing */
7134 {
7135     char_u	*name_end;
7136     char_u	*p;
7137     char_u	*linep;
7138     char_u	*key_start;
7139     char_u	*arg_start;
7140     char_u	*key = NULL, *arg = NULL;
7141     long	i;
7142     int		off;
7143     int		len;
7144     int		attr;
7145     int		id;
7146     int		idx;
7147     int		dodefault = FALSE;
7148     int		doclear = FALSE;
7149     int		dolink = FALSE;
7150     int		error = FALSE;
7151     int		color;
7152     int		is_normal_group = FALSE;	/* "Normal" group */
7153 #ifdef FEAT_GUI_X11
7154     int		is_menu_group = FALSE;		/* "Menu" group */
7155     int		is_scrollbar_group = FALSE;	/* "Scrollbar" group */
7156     int		is_tooltip_group = FALSE;	/* "Tooltip" group */
7157     int		do_colors = FALSE;		/* need to update colors? */
7158 #else
7159 # define is_menu_group 0
7160 # define is_tooltip_group 0
7161 #endif
7162 
7163     /*
7164      * If no argument, list current highlighting.
7165      */
7166     if (ends_excmd(*line))
7167     {
7168 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7169 	    /* TODO: only call when the group has attributes set */
7170 	    highlight_list_one((int)i);
7171 	return;
7172     }
7173 
7174     /*
7175      * Isolate the name.
7176      */
7177     name_end = skiptowhite(line);
7178     linep = skipwhite(name_end);
7179 
7180     /*
7181      * Check for "default" argument.
7182      */
7183     if (STRNCMP(line, "default", name_end - line) == 0)
7184     {
7185 	dodefault = TRUE;
7186 	line = linep;
7187 	name_end = skiptowhite(line);
7188 	linep = skipwhite(name_end);
7189     }
7190 
7191     /*
7192      * Check for "clear" or "link" argument.
7193      */
7194     if (STRNCMP(line, "clear", name_end - line) == 0)
7195 	doclear = TRUE;
7196     if (STRNCMP(line, "link", name_end - line) == 0)
7197 	dolink = TRUE;
7198 
7199     /*
7200      * ":highlight {group-name}": list highlighting for one group.
7201      */
7202     if (!doclear && !dolink && ends_excmd(*linep))
7203     {
7204 	id = syn_namen2id(line, (int)(name_end - line));
7205 	if (id == 0)
7206 	    EMSG2(_("E411: highlight group not found: %s"), line);
7207 	else
7208 	    highlight_list_one(id);
7209 	return;
7210     }
7211 
7212     /*
7213      * Handle ":highlight link {from} {to}" command.
7214      */
7215     if (dolink)
7216     {
7217 	char_u	    *from_start = linep;
7218 	char_u	    *from_end;
7219 	char_u	    *to_start;
7220 	char_u	    *to_end;
7221 	int	    from_id;
7222 	int	    to_id;
7223 
7224 	from_end = skiptowhite(from_start);
7225 	to_start = skipwhite(from_end);
7226 	to_end	 = skiptowhite(to_start);
7227 
7228 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
7229 	{
7230 	    EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7231 								  from_start);
7232 	    return;
7233 	}
7234 
7235 	if (!ends_excmd(*skipwhite(to_end)))
7236 	{
7237 	    EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7238 	    return;
7239 	}
7240 
7241 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
7242 	if (STRNCMP(to_start, "NONE", 4) == 0)
7243 	    to_id = 0;
7244 	else
7245 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
7246 
7247 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7248 	{
7249 	    /*
7250 	     * Don't allow a link when there already is some highlighting
7251 	     * for the group, unless '!' is used
7252 	     */
7253 	    if (to_id > 0 && !forceit && !init
7254 				   && hl_has_settings(from_id - 1, dodefault))
7255 	    {
7256 		if (sourcing_name == NULL && !dodefault)
7257 		    EMSG(_("E414: group has settings, highlight link ignored"));
7258 	    }
7259 	    else
7260 	    {
7261 		if (!init)
7262 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7263 		HL_TABLE()[from_id - 1].sg_link = to_id;
7264 #ifdef FEAT_EVAL
7265 		HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7266 #endif
7267 		redraw_all_later(SOME_VALID);
7268 	    }
7269 	}
7270 
7271 	/* Only call highlight_changed() once, after sourcing a syntax file */
7272 	need_highlight_changed = TRUE;
7273 
7274 	return;
7275     }
7276 
7277     if (doclear)
7278     {
7279 	/*
7280 	 * ":highlight clear [group]" command.
7281 	 */
7282 	line = linep;
7283 	if (ends_excmd(*line))
7284 	{
7285 #ifdef FEAT_GUI
7286 	    /* First, we do not destroy the old values, but allocate the new
7287 	     * ones and update the display. THEN we destroy the old values.
7288 	     * If we destroy the old values first, then the old values
7289 	     * (such as GuiFont's or GuiFontset's) will still be displayed but
7290 	     * invalid because they were free'd.
7291 	     */
7292 	    if (gui.in_use)
7293 	    {
7294 # ifdef FEAT_BEVAL_TIP
7295 		gui_init_tooltip_font();
7296 # endif
7297 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7298 		gui_init_menu_font();
7299 # endif
7300 	    }
7301 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7302 	    gui_mch_def_colors();
7303 # endif
7304 # ifdef FEAT_GUI_X11
7305 #  ifdef FEAT_MENU
7306 
7307 	    /* This only needs to be done when there is no Menu highlight
7308 	     * group defined by default, which IS currently the case.
7309 	     */
7310 	    gui_mch_new_menu_colors();
7311 #  endif
7312 	    if (gui.in_use)
7313 	    {
7314 		gui_new_scrollbar_colors();
7315 #  ifdef FEAT_BEVAL
7316 		gui_mch_new_tooltip_colors();
7317 #  endif
7318 #  ifdef FEAT_MENU
7319 		gui_mch_new_menu_font();
7320 #  endif
7321 	    }
7322 # endif
7323 
7324 	    /* Ok, we're done allocating the new default graphics items.
7325 	     * The screen should already be refreshed at this point.
7326 	     * It is now Ok to clear out the old data.
7327 	     */
7328 #endif
7329 #ifdef FEAT_EVAL
7330 	    do_unlet((char_u *)"colors_name", TRUE);
7331 #endif
7332 	    restore_cterm_colors();
7333 
7334 	    /*
7335 	     * Clear all default highlight groups and load the defaults.
7336 	     */
7337 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7338 		highlight_clear(idx);
7339 	    init_highlight(TRUE, TRUE);
7340 #ifdef FEAT_GUI
7341 	    if (gui.in_use)
7342 		highlight_gui_started();
7343 #endif
7344 	    highlight_changed();
7345 	    redraw_later_clear();
7346 	    return;
7347 	}
7348 	name_end = skiptowhite(line);
7349 	linep = skipwhite(name_end);
7350     }
7351 
7352     /*
7353      * Find the group name in the table.  If it does not exist yet, add it.
7354      */
7355     id = syn_check_group(line, (int)(name_end - line));
7356     if (id == 0)			/* failed (out of memory) */
7357 	return;
7358     idx = id - 1;			/* index is ID minus one */
7359 
7360     /* Return if "default" was used and the group already has settings. */
7361     if (dodefault && hl_has_settings(idx, TRUE))
7362 	return;
7363 
7364     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7365 	is_normal_group = TRUE;
7366 #ifdef FEAT_GUI_X11
7367     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7368 	is_menu_group = TRUE;
7369     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7370 	is_scrollbar_group = TRUE;
7371     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7372 	is_tooltip_group = TRUE;
7373 #endif
7374 
7375     /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7376     if (doclear || (forceit && init))
7377     {
7378 	highlight_clear(idx);
7379 	if (!doclear)
7380 	    HL_TABLE()[idx].sg_set = 0;
7381     }
7382 
7383     if (!doclear)
7384       while (!ends_excmd(*linep))
7385       {
7386 	key_start = linep;
7387 	if (*linep == '=')
7388 	{
7389 	    EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7390 	    error = TRUE;
7391 	    break;
7392 	}
7393 
7394 	/*
7395 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7396 	 * "guibg").
7397 	 */
7398 	while (*linep && !vim_iswhite(*linep) && *linep != '=')
7399 	    ++linep;
7400 	vim_free(key);
7401 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
7402 	if (key == NULL)
7403 	{
7404 	    error = TRUE;
7405 	    break;
7406 	}
7407 	linep = skipwhite(linep);
7408 
7409 	if (STRCMP(key, "NONE") == 0)
7410 	{
7411 	    if (!init || HL_TABLE()[idx].sg_set == 0)
7412 	    {
7413 		if (!init)
7414 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7415 		highlight_clear(idx);
7416 	    }
7417 	    continue;
7418 	}
7419 
7420 	/*
7421 	 * Check for the equal sign.
7422 	 */
7423 	if (*linep != '=')
7424 	{
7425 	    EMSG2(_("E416: missing equal sign: %s"), key_start);
7426 	    error = TRUE;
7427 	    break;
7428 	}
7429 	++linep;
7430 
7431 	/*
7432 	 * Isolate the argument.
7433 	 */
7434 	linep = skipwhite(linep);
7435 	if (*linep == '\'')		/* guifg='color name' */
7436 	{
7437 	    arg_start = ++linep;
7438 	    linep = vim_strchr(linep, '\'');
7439 	    if (linep == NULL)
7440 	    {
7441 		EMSG2(_(e_invarg2), key_start);
7442 		error = TRUE;
7443 		break;
7444 	    }
7445 	}
7446 	else
7447 	{
7448 	    arg_start = linep;
7449 	    linep = skiptowhite(linep);
7450 	}
7451 	if (linep == arg_start)
7452 	{
7453 	    EMSG2(_("E417: missing argument: %s"), key_start);
7454 	    error = TRUE;
7455 	    break;
7456 	}
7457 	vim_free(arg);
7458 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7459 	if (arg == NULL)
7460 	{
7461 	    error = TRUE;
7462 	    break;
7463 	}
7464 	if (*linep == '\'')
7465 	    ++linep;
7466 
7467 	/*
7468 	 * Store the argument.
7469 	 */
7470 	if (  STRCMP(key, "TERM") == 0
7471 		|| STRCMP(key, "CTERM") == 0
7472 		|| STRCMP(key, "GUI") == 0)
7473 	{
7474 	    attr = 0;
7475 	    off = 0;
7476 	    while (arg[off] != NUL)
7477 	    {
7478 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7479 		{
7480 		    len = (int)STRLEN(hl_name_table[i]);
7481 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7482 		    {
7483 			attr |= hl_attr_table[i];
7484 			off += len;
7485 			break;
7486 		    }
7487 		}
7488 		if (i < 0)
7489 		{
7490 		    EMSG2(_("E418: Illegal value: %s"), arg);
7491 		    error = TRUE;
7492 		    break;
7493 		}
7494 		if (arg[off] == ',')		/* another one follows */
7495 		    ++off;
7496 	    }
7497 	    if (error)
7498 		break;
7499 	    if (*key == 'T')
7500 	    {
7501 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7502 		{
7503 		    if (!init)
7504 			HL_TABLE()[idx].sg_set |= SG_TERM;
7505 		    HL_TABLE()[idx].sg_term = attr;
7506 		}
7507 	    }
7508 	    else if (*key == 'C')
7509 	    {
7510 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7511 		{
7512 		    if (!init)
7513 			HL_TABLE()[idx].sg_set |= SG_CTERM;
7514 		    HL_TABLE()[idx].sg_cterm = attr;
7515 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
7516 		}
7517 	    }
7518 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
7519 	    else
7520 	    {
7521 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7522 		{
7523 		    if (!init)
7524 			HL_TABLE()[idx].sg_set |= SG_GUI;
7525 		    HL_TABLE()[idx].sg_gui = attr;
7526 		}
7527 	    }
7528 #endif
7529 	}
7530 	else if (STRCMP(key, "FONT") == 0)
7531 	{
7532 	    /* in non-GUI fonts are simply ignored */
7533 #ifdef FEAT_GUI
7534 	    if (!gui.shell_created)
7535 	    {
7536 		/* GUI not started yet, always accept the name. */
7537 		vim_free(HL_TABLE()[idx].sg_font_name);
7538 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7539 	    }
7540 	    else
7541 	    {
7542 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7543 # ifdef FEAT_XFONTSET
7544 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7545 # endif
7546 		/* First, save the current font/fontset.
7547 		 * Then try to allocate the font/fontset.
7548 		 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7549 		 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7550 		 */
7551 
7552 		HL_TABLE()[idx].sg_font = NOFONT;
7553 # ifdef FEAT_XFONTSET
7554 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
7555 # endif
7556 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
7557 						     is_tooltip_group, FALSE);
7558 
7559 # ifdef FEAT_XFONTSET
7560 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7561 		{
7562 		    /* New fontset was accepted. Free the old one, if there
7563 		     * was one. */
7564 		    gui_mch_free_fontset(temp_sg_fontset);
7565 		    vim_free(HL_TABLE()[idx].sg_font_name);
7566 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7567 		}
7568 		else
7569 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7570 # endif
7571 		if (HL_TABLE()[idx].sg_font != NOFONT)
7572 		{
7573 		    /* New font was accepted. Free the old one, if there was
7574 		     * one. */
7575 		    gui_mch_free_font(temp_sg_font);
7576 		    vim_free(HL_TABLE()[idx].sg_font_name);
7577 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7578 		}
7579 		else
7580 		    HL_TABLE()[idx].sg_font = temp_sg_font;
7581 	    }
7582 #endif
7583 	}
7584 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7585 	{
7586 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7587 	  {
7588 	    if (!init)
7589 		HL_TABLE()[idx].sg_set |= SG_CTERM;
7590 
7591 	    /* When setting the foreground color, and previously the "bold"
7592 	     * flag was set for a light color, reset it now */
7593 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7594 	    {
7595 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7596 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
7597 	    }
7598 
7599 	    if (VIM_ISDIGIT(*arg))
7600 		color = atoi((char *)arg);
7601 	    else if (STRICMP(arg, "fg") == 0)
7602 	    {
7603 		if (cterm_normal_fg_color)
7604 		    color = cterm_normal_fg_color - 1;
7605 		else
7606 		{
7607 		    EMSG(_("E419: FG color unknown"));
7608 		    error = TRUE;
7609 		    break;
7610 		}
7611 	    }
7612 	    else if (STRICMP(arg, "bg") == 0)
7613 	    {
7614 		if (cterm_normal_bg_color > 0)
7615 		    color = cterm_normal_bg_color - 1;
7616 		else
7617 		{
7618 		    EMSG(_("E420: BG color unknown"));
7619 		    error = TRUE;
7620 		    break;
7621 		}
7622 	    }
7623 	    else
7624 	    {
7625 		static char *(color_names[28]) = {
7626 			    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7627 			    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7628 			    "Gray", "Grey",
7629 			    "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7630 			    "Blue", "LightBlue", "Green", "LightGreen",
7631 			    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7632 			    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7633 		static int color_numbers_16[28] = {0, 1, 2, 3,
7634 						 4, 5, 6, 6,
7635 						 7, 7,
7636 						 7, 7, 8, 8,
7637 						 9, 9, 10, 10,
7638 						 11, 11, 12, 12, 13,
7639 						 13, 14, 14, 15, -1};
7640 		/* for xterm with 88 colors... */
7641 		static int color_numbers_88[28] = {0, 4, 2, 6,
7642 						 1, 5, 32, 72,
7643 						 84, 84,
7644 						 7, 7, 82, 82,
7645 						 12, 43, 10, 61,
7646 						 14, 63, 9, 74, 13,
7647 						 75, 11, 78, 15, -1};
7648 		/* for xterm with 256 colors... */
7649 		static int color_numbers_256[28] = {0, 4, 2, 6,
7650 						 1, 5, 130, 130,
7651 						 248, 248,
7652 						 7, 7, 242, 242,
7653 						 12, 81, 10, 121,
7654 						 14, 159, 9, 224, 13,
7655 						 225, 11, 229, 15, -1};
7656 		/* for terminals with less than 16 colors... */
7657 		static int color_numbers_8[28] = {0, 4, 2, 6,
7658 						 1, 5, 3, 3,
7659 						 7, 7,
7660 						 7, 7, 0+8, 0+8,
7661 						 4+8, 4+8, 2+8, 2+8,
7662 						 6+8, 6+8, 1+8, 1+8, 5+8,
7663 						 5+8, 3+8, 3+8, 7+8, -1};
7664 #if defined(__QNXNTO__)
7665 		static int *color_numbers_8_qansi = color_numbers_8;
7666 		/* On qnx, the 8 & 16 color arrays are the same */
7667 		if (STRNCMP(T_NAME, "qansi", 5) == 0)
7668 		    color_numbers_8_qansi = color_numbers_16;
7669 #endif
7670 
7671 		/* reduce calls to STRICMP a bit, it can be slow */
7672 		off = TOUPPER_ASC(*arg);
7673 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7674 		    if (off == color_names[i][0]
7675 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7676 			break;
7677 		if (i < 0)
7678 		{
7679 		    EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7680 		    error = TRUE;
7681 		    break;
7682 		}
7683 
7684 		/* Use the _16 table to check if its a valid color name. */
7685 		color = color_numbers_16[i];
7686 		if (color >= 0)
7687 		{
7688 		    if (t_colors == 8)
7689 		    {
7690 			/* t_Co is 8: use the 8 colors table */
7691 #if defined(__QNXNTO__)
7692 			color = color_numbers_8_qansi[i];
7693 #else
7694 			color = color_numbers_8[i];
7695 #endif
7696 			if (key[5] == 'F')
7697 			{
7698 			    /* set/reset bold attribute to get light foreground
7699 			     * colors (on some terminals, e.g. "linux") */
7700 			    if (color & 8)
7701 			    {
7702 				HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7703 				HL_TABLE()[idx].sg_cterm_bold = TRUE;
7704 			    }
7705 			    else
7706 				HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7707 			}
7708 			color &= 7;	/* truncate to 8 colors */
7709 		    }
7710 		    else if (t_colors == 16 || t_colors == 88
7711 							   || t_colors >= 256)
7712 		    {
7713 			/*
7714 			 * Guess: if the termcap entry ends in 'm', it is
7715 			 * probably an xterm-like terminal.  Use the changed
7716 			 * order for colors.
7717 			 */
7718 			if (*T_CAF != NUL)
7719 			    p = T_CAF;
7720 			else
7721 			    p = T_CSF;
7722 			if (*p != NUL && (t_colors > 256
7723 					      || *(p + STRLEN(p) - 1) == 'm'))
7724 			{
7725 			    if (t_colors == 88)
7726 				color = color_numbers_88[i];
7727 			    else if (t_colors >= 256)
7728 				color = color_numbers_256[i];
7729 			    else
7730 				color = color_numbers_8[i];
7731 			}
7732 		    }
7733 		}
7734 	    }
7735 	    /* Add one to the argument, to avoid zero.  Zero is used for
7736 	     * "NONE", then "color" is -1. */
7737 	    if (key[5] == 'F')
7738 	    {
7739 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
7740 		if (is_normal_group)
7741 		{
7742 		    cterm_normal_fg_color = color + 1;
7743 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7744 #ifdef FEAT_GUI
7745 		    /* Don't do this if the GUI is used. */
7746 		    if (!gui.in_use && !gui.starting)
7747 #endif
7748 		    {
7749 			must_redraw = CLEAR;
7750 			if (termcap_active && color >= 0)
7751 			    term_fg_color(color);
7752 		    }
7753 		}
7754 	    }
7755 	    else
7756 	    {
7757 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
7758 		if (is_normal_group)
7759 		{
7760 		    cterm_normal_bg_color = color + 1;
7761 #ifdef FEAT_GUI
7762 		    /* Don't mess with 'background' if the GUI is used. */
7763 		    if (!gui.in_use && !gui.starting)
7764 #endif
7765 		    {
7766 			must_redraw = CLEAR;
7767 			if (color >= 0)
7768 			{
7769 			    if (termcap_active)
7770 				term_bg_color(color);
7771 			    if (t_colors < 16)
7772 				i = (color == 0 || color == 4);
7773 			    else
7774 				i = (color < 7 || color == 8);
7775 			    /* Set the 'background' option if the value is
7776 			     * wrong. */
7777 			    if (i != (*p_bg == 'd'))
7778 				set_option_value((char_u *)"bg", 0L,
7779 					i ?  (char_u *)"dark"
7780 					  : (char_u *)"light", 0);
7781 			}
7782 		    }
7783 		}
7784 	    }
7785 	  }
7786 	}
7787 	else if (STRCMP(key, "GUIFG") == 0)
7788 	{
7789 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
7790 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7791 	    {
7792 		if (!init)
7793 		    HL_TABLE()[idx].sg_set |= SG_GUI;
7794 
7795 # ifdef FEAT_GUI
7796 		/* In GUI guifg colors are only used when recognized */
7797 		i = color_name2handle(arg);
7798 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7799 		{
7800 		    HL_TABLE()[idx].sg_gui_fg = i;
7801 # endif
7802 		    vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7803 		    if (STRCMP(arg, "NONE"))
7804 			HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7805 		    else
7806 			HL_TABLE()[idx].sg_gui_fg_name = NULL;
7807 # ifdef FEAT_GUI
7808 #  ifdef FEAT_GUI_X11
7809 		    if (is_menu_group)
7810 			gui.menu_fg_pixel = i;
7811 		    if (is_scrollbar_group)
7812 			gui.scroll_fg_pixel = i;
7813 #   ifdef FEAT_BEVAL
7814 		    if (is_tooltip_group)
7815 			gui.tooltip_fg_pixel = i;
7816 #   endif
7817 		    do_colors = TRUE;
7818 #  endif
7819 		}
7820 # endif
7821 	    }
7822 #endif
7823 	}
7824 	else if (STRCMP(key, "GUIBG") == 0)
7825 	{
7826 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
7827 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7828 	    {
7829 		if (!init)
7830 		    HL_TABLE()[idx].sg_set |= SG_GUI;
7831 
7832 # ifdef FEAT_GUI
7833 		/* In GUI guifg colors are only used when recognized */
7834 		i = color_name2handle(arg);
7835 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7836 		{
7837 		    HL_TABLE()[idx].sg_gui_bg = i;
7838 # endif
7839 		    vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7840 		    if (STRCMP(arg, "NONE") != 0)
7841 			HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7842 		    else
7843 			HL_TABLE()[idx].sg_gui_bg_name = NULL;
7844 # ifdef FEAT_GUI
7845 #  ifdef FEAT_GUI_X11
7846 		    if (is_menu_group)
7847 			gui.menu_bg_pixel = i;
7848 		    if (is_scrollbar_group)
7849 			gui.scroll_bg_pixel = i;
7850 #   ifdef FEAT_BEVAL
7851 		    if (is_tooltip_group)
7852 			gui.tooltip_bg_pixel = i;
7853 #   endif
7854 		    do_colors = TRUE;
7855 #  endif
7856 		}
7857 # endif
7858 	    }
7859 #endif
7860 	}
7861 	else if (STRCMP(key, "GUISP") == 0)
7862 	{
7863 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
7864 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7865 	    {
7866 		if (!init)
7867 		    HL_TABLE()[idx].sg_set |= SG_GUI;
7868 
7869 # ifdef FEAT_GUI
7870 		i = color_name2handle(arg);
7871 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7872 		{
7873 		    HL_TABLE()[idx].sg_gui_sp = i;
7874 # endif
7875 		    vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7876 		    if (STRCMP(arg, "NONE") != 0)
7877 			HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7878 		    else
7879 			HL_TABLE()[idx].sg_gui_sp_name = NULL;
7880 # ifdef FEAT_GUI
7881 		}
7882 # endif
7883 	    }
7884 #endif
7885 	}
7886 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7887 	{
7888 	    char_u	buf[100];
7889 	    char_u	*tname;
7890 
7891 	    if (!init)
7892 		HL_TABLE()[idx].sg_set |= SG_TERM;
7893 
7894 	    /*
7895 	     * The "start" and "stop"  arguments can be a literal escape
7896 	     * sequence, or a comma separated list of terminal codes.
7897 	     */
7898 	    if (STRNCMP(arg, "t_", 2) == 0)
7899 	    {
7900 		off = 0;
7901 		buf[0] = 0;
7902 		while (arg[off] != NUL)
7903 		{
7904 		    /* Isolate one termcap name */
7905 		    for (len = 0; arg[off + len] &&
7906 						 arg[off + len] != ','; ++len)
7907 			;
7908 		    tname = vim_strnsave(arg + off, len);
7909 		    if (tname == NULL)		/* out of memory */
7910 		    {
7911 			error = TRUE;
7912 			break;
7913 		    }
7914 		    /* lookup the escape sequence for the item */
7915 		    p = get_term_code(tname);
7916 		    vim_free(tname);
7917 		    if (p == NULL)	    /* ignore non-existing things */
7918 			p = (char_u *)"";
7919 
7920 		    /* Append it to the already found stuff */
7921 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7922 		    {
7923 			EMSG2(_("E422: terminal code too long: %s"), arg);
7924 			error = TRUE;
7925 			break;
7926 		    }
7927 		    STRCAT(buf, p);
7928 
7929 		    /* Advance to the next item */
7930 		    off += len;
7931 		    if (arg[off] == ',')	    /* another one follows */
7932 			++off;
7933 		}
7934 	    }
7935 	    else
7936 	    {
7937 		/*
7938 		 * Copy characters from arg[] to buf[], translating <> codes.
7939 		 */
7940 		for (p = arg, off = 0; off < 100 - 6 && *p; )
7941 		{
7942 		    len = trans_special(&p, buf + off, FALSE);
7943 		    if (len > 0)	    /* recognized special char */
7944 			off += len;
7945 		    else		    /* copy as normal char */
7946 			buf[off++] = *p++;
7947 		}
7948 		buf[off] = NUL;
7949 	    }
7950 	    if (error)
7951 		break;
7952 
7953 	    if (STRCMP(buf, "NONE") == 0)	/* resetting the value */
7954 		p = NULL;
7955 	    else
7956 		p = vim_strsave(buf);
7957 	    if (key[2] == 'A')
7958 	    {
7959 		vim_free(HL_TABLE()[idx].sg_start);
7960 		HL_TABLE()[idx].sg_start = p;
7961 	    }
7962 	    else
7963 	    {
7964 		vim_free(HL_TABLE()[idx].sg_stop);
7965 		HL_TABLE()[idx].sg_stop = p;
7966 	    }
7967 	}
7968 	else
7969 	{
7970 	    EMSG2(_("E423: Illegal argument: %s"), key_start);
7971 	    error = TRUE;
7972 	    break;
7973 	}
7974 
7975 	/*
7976 	 * When highlighting has been given for a group, don't link it.
7977 	 */
7978 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7979 	    HL_TABLE()[idx].sg_link = 0;
7980 
7981 	/*
7982 	 * Continue with next argument.
7983 	 */
7984 	linep = skipwhite(linep);
7985       }
7986 
7987     /*
7988      * If there is an error, and it's a new entry, remove it from the table.
7989      */
7990     if (error && idx == highlight_ga.ga_len)
7991 	syn_unadd_group();
7992     else
7993     {
7994 	if (is_normal_group)
7995 	{
7996 	    HL_TABLE()[idx].sg_term_attr = 0;
7997 	    HL_TABLE()[idx].sg_cterm_attr = 0;
7998 #ifdef FEAT_GUI
7999 	    HL_TABLE()[idx].sg_gui_attr = 0;
8000 	    /*
8001 	     * Need to update all groups, because they might be using "bg"
8002 	     * and/or "fg", which have been changed now.
8003 	     */
8004 	    if (gui.in_use)
8005 		highlight_gui_started();
8006 #endif
8007 	}
8008 #ifdef FEAT_GUI_X11
8009 # ifdef FEAT_MENU
8010 	else if (is_menu_group)
8011 	{
8012 	    if (gui.in_use && do_colors)
8013 		gui_mch_new_menu_colors();
8014 	}
8015 # endif
8016 	else if (is_scrollbar_group)
8017 	{
8018 	    if (gui.in_use && do_colors)
8019 		gui_new_scrollbar_colors();
8020 	}
8021 # ifdef FEAT_BEVAL
8022 	else if (is_tooltip_group)
8023 	{
8024 	    if (gui.in_use && do_colors)
8025 		gui_mch_new_tooltip_colors();
8026 	}
8027 # endif
8028 #endif
8029 	else
8030 	    set_hl_attr(idx);
8031 #ifdef FEAT_EVAL
8032 	HL_TABLE()[idx].sg_scriptID = current_SID;
8033 #endif
8034 	redraw_all_later(NOT_VALID);
8035     }
8036     vim_free(key);
8037     vim_free(arg);
8038 
8039     /* Only call highlight_changed() once, after sourcing a syntax file */
8040     need_highlight_changed = TRUE;
8041 }
8042 
8043 #if defined(EXITFREE) || defined(PROTO)
8044     void
8045 free_highlight()
8046 {
8047     int	    i;
8048 
8049     for (i = 0; i < highlight_ga.ga_len; ++i)
8050     {
8051 	highlight_clear(i);
8052 	vim_free(HL_TABLE()[i].sg_name);
8053 	vim_free(HL_TABLE()[i].sg_name_u);
8054     }
8055     ga_clear(&highlight_ga);
8056 }
8057 #endif
8058 
8059 /*
8060  * Reset the cterm colors to what they were before Vim was started, if
8061  * possible.  Otherwise reset them to zero.
8062  */
8063     void
8064 restore_cterm_colors()
8065 {
8066 #if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
8067     /* Since t_me has been set, this probably means that the user
8068      * wants to use this as default colors.  Need to reset default
8069      * background/foreground colors. */
8070     mch_set_normal_colors();
8071 #else
8072     cterm_normal_fg_color = 0;
8073     cterm_normal_fg_bold = 0;
8074     cterm_normal_bg_color = 0;
8075 #endif
8076 }
8077 
8078 /*
8079  * Return TRUE if highlight group "idx" has any settings.
8080  * When "check_link" is TRUE also check for an existing link.
8081  */
8082     static int
8083 hl_has_settings(idx, check_link)
8084     int		idx;
8085     int		check_link;
8086 {
8087     return (   HL_TABLE()[idx].sg_term_attr != 0
8088 	    || HL_TABLE()[idx].sg_cterm_attr != 0
8089 	    || HL_TABLE()[idx].sg_cterm_fg != 0
8090 	    || HL_TABLE()[idx].sg_cterm_bg != 0
8091 #ifdef FEAT_GUI
8092 	    || HL_TABLE()[idx].sg_gui_attr != 0
8093 	    || HL_TABLE()[idx].sg_gui_fg_name != NULL
8094 	    || HL_TABLE()[idx].sg_gui_bg_name != NULL
8095 	    || HL_TABLE()[idx].sg_gui_sp_name != NULL
8096 	    || HL_TABLE()[idx].sg_font_name != NUL
8097 #endif
8098 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8099 }
8100 
8101 /*
8102  * Clear highlighting for one group.
8103  */
8104     static void
8105 highlight_clear(idx)
8106     int idx;
8107 {
8108     HL_TABLE()[idx].sg_term = 0;
8109     vim_free(HL_TABLE()[idx].sg_start);
8110     HL_TABLE()[idx].sg_start = NULL;
8111     vim_free(HL_TABLE()[idx].sg_stop);
8112     HL_TABLE()[idx].sg_stop = NULL;
8113     HL_TABLE()[idx].sg_term_attr = 0;
8114     HL_TABLE()[idx].sg_cterm = 0;
8115     HL_TABLE()[idx].sg_cterm_bold = FALSE;
8116     HL_TABLE()[idx].sg_cterm_fg = 0;
8117     HL_TABLE()[idx].sg_cterm_bg = 0;
8118     HL_TABLE()[idx].sg_cterm_attr = 0;
8119 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
8120     HL_TABLE()[idx].sg_gui = 0;
8121     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8122     HL_TABLE()[idx].sg_gui_fg_name = NULL;
8123     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8124     HL_TABLE()[idx].sg_gui_bg_name = NULL;
8125     vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8126     HL_TABLE()[idx].sg_gui_sp_name = NULL;
8127 #endif
8128 #ifdef FEAT_GUI
8129     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8130     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8131     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
8132     gui_mch_free_font(HL_TABLE()[idx].sg_font);
8133     HL_TABLE()[idx].sg_font = NOFONT;
8134 # ifdef FEAT_XFONTSET
8135     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8136     HL_TABLE()[idx].sg_fontset = NOFONTSET;
8137 # endif
8138     vim_free(HL_TABLE()[idx].sg_font_name);
8139     HL_TABLE()[idx].sg_font_name = NULL;
8140     HL_TABLE()[idx].sg_gui_attr = 0;
8141 #endif
8142 #ifdef FEAT_EVAL
8143     /* Clear the script ID only when there is no link, since that is not
8144      * cleared. */
8145     if (HL_TABLE()[idx].sg_link == 0)
8146 	HL_TABLE()[idx].sg_scriptID = 0;
8147 #endif
8148 }
8149 
8150 #if defined(FEAT_GUI) || defined(PROTO)
8151 /*
8152  * Set the normal foreground and background colors according to the "Normal"
8153  * highlighting group.  For X11 also set "Menu", "Scrollbar", and
8154  * "Tooltip" colors.
8155  */
8156     void
8157 set_normal_colors()
8158 {
8159     if (set_group_colors((char_u *)"Normal",
8160 			     &gui.norm_pixel, &gui.back_pixel,
8161 			     FALSE, TRUE, FALSE))
8162     {
8163 	gui_mch_new_colors();
8164 	must_redraw = CLEAR;
8165     }
8166 #ifdef FEAT_GUI_X11
8167     if (set_group_colors((char_u *)"Menu",
8168 			 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8169 			 TRUE, FALSE, FALSE))
8170     {
8171 # ifdef FEAT_MENU
8172 	gui_mch_new_menu_colors();
8173 # endif
8174 	must_redraw = CLEAR;
8175     }
8176 # ifdef FEAT_BEVAL
8177     if (set_group_colors((char_u *)"Tooltip",
8178 			 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8179 			 FALSE, FALSE, TRUE))
8180     {
8181 # ifdef FEAT_TOOLBAR
8182 	gui_mch_new_tooltip_colors();
8183 # endif
8184 	must_redraw = CLEAR;
8185     }
8186 #endif
8187     if (set_group_colors((char_u *)"Scrollbar",
8188 		    &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8189 		    FALSE, FALSE, FALSE))
8190     {
8191 	gui_new_scrollbar_colors();
8192 	must_redraw = CLEAR;
8193     }
8194 #endif
8195 }
8196 
8197 /*
8198  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8199  */
8200     static int
8201 set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
8202     char_u	*name;
8203     guicolor_T	*fgp;
8204     guicolor_T	*bgp;
8205     int		do_menu;
8206     int		use_norm;
8207     int		do_tooltip;
8208 {
8209     int		idx;
8210 
8211     idx = syn_name2id(name) - 1;
8212     if (idx >= 0)
8213     {
8214 	gui_do_one_color(idx, do_menu, do_tooltip);
8215 
8216 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8217 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
8218 	else if (use_norm)
8219 	    *fgp = gui.def_norm_pixel;
8220 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8221 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
8222 	else if (use_norm)
8223 	    *bgp = gui.def_back_pixel;
8224 	return TRUE;
8225     }
8226     return FALSE;
8227 }
8228 
8229 /*
8230  * Get the font of the "Normal" group.
8231  * Returns "" when it's not found or not set.
8232  */
8233     char_u *
8234 hl_get_font_name()
8235 {
8236     int		id;
8237     char_u	*s;
8238 
8239     id = syn_name2id((char_u *)"Normal");
8240     if (id > 0)
8241     {
8242 	s = HL_TABLE()[id - 1].sg_font_name;
8243 	if (s != NULL)
8244 	    return s;
8245     }
8246     return (char_u *)"";
8247 }
8248 
8249 /*
8250  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
8251  * actually chosen to be used.
8252  */
8253     void
8254 hl_set_font_name(font_name)
8255     char_u	*font_name;
8256 {
8257     int	    id;
8258 
8259     id = syn_name2id((char_u *)"Normal");
8260     if (id > 0)
8261     {
8262 	vim_free(HL_TABLE()[id - 1].sg_font_name);
8263 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8264     }
8265 }
8266 
8267 /*
8268  * Set background color for "Normal" group.  Called by gui_set_bg_color()
8269  * when the color is known.
8270  */
8271     void
8272 hl_set_bg_color_name(name)
8273     char_u  *name;	    /* must have been allocated */
8274 {
8275     int	    id;
8276 
8277     if (name != NULL)
8278     {
8279 	id = syn_name2id((char_u *)"Normal");
8280 	if (id > 0)
8281 	{
8282 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8283 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
8284 	}
8285     }
8286 }
8287 
8288 /*
8289  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
8290  * when the color is known.
8291  */
8292     void
8293 hl_set_fg_color_name(name)
8294     char_u  *name;	    /* must have been allocated */
8295 {
8296     int	    id;
8297 
8298     if (name != NULL)
8299     {
8300 	id = syn_name2id((char_u *)"Normal");
8301 	if (id > 0)
8302 	{
8303 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8304 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
8305 	}
8306     }
8307 }
8308 
8309 /*
8310  * Return the handle for a color name.
8311  * Returns INVALCOLOR when failed.
8312  */
8313     static guicolor_T
8314 color_name2handle(name)
8315     char_u  *name;
8316 {
8317     if (STRCMP(name, "NONE") == 0)
8318 	return INVALCOLOR;
8319 
8320     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8321 	return gui.norm_pixel;
8322     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8323 	return gui.back_pixel;
8324 
8325     return gui_get_color(name);
8326 }
8327 
8328 /*
8329  * Return the handle for a font name.
8330  * Returns NOFONT when failed.
8331  */
8332     static GuiFont
8333 font_name2handle(name)
8334     char_u  *name;
8335 {
8336     if (STRCMP(name, "NONE") == 0)
8337 	return NOFONT;
8338 
8339     return gui_mch_get_font(name, TRUE);
8340 }
8341 
8342 # ifdef FEAT_XFONTSET
8343 /*
8344  * Return the handle for a fontset name.
8345  * Returns NOFONTSET when failed.
8346  */
8347     static GuiFontset
8348 fontset_name2handle(name, fixed_width)
8349     char_u	*name;
8350     int		fixed_width;
8351 {
8352     if (STRCMP(name, "NONE") == 0)
8353 	return NOFONTSET;
8354 
8355     return gui_mch_get_fontset(name, TRUE, fixed_width);
8356 }
8357 # endif
8358 
8359 /*
8360  * Get the font or fontset for one highlight group.
8361  */
8362     static void
8363 hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font)
8364     int		idx;
8365     char_u	*arg;
8366     int		do_normal;		/* set normal font */
8367     int		do_menu UNUSED;		/* set menu font */
8368     int		do_tooltip UNUSED;	/* set tooltip font */
8369     int		free_font;		/* free current font/fontset */
8370 {
8371 # ifdef FEAT_XFONTSET
8372     /* If 'guifontset' is not empty, first try using the name as a
8373      * fontset.  If that doesn't work, use it as a font name. */
8374     if (*p_guifontset != NUL
8375 #  ifdef FONTSET_ALWAYS
8376 	|| do_menu
8377 #  endif
8378 #  ifdef FEAT_BEVAL_TIP
8379 	/* In Athena & Motif, the Tooltip highlight group is always a fontset */
8380 	|| do_tooltip
8381 #  endif
8382 	    )
8383     {
8384 	if (free_font)
8385 	    gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8386 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8387 #  ifdef FONTSET_ALWAYS
8388 		|| do_menu
8389 #  endif
8390 #  ifdef FEAT_BEVAL_TIP
8391 		|| do_tooltip
8392 #  endif
8393 		);
8394     }
8395     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8396     {
8397 	/* If it worked and it's the Normal group, use it as the normal
8398 	 * fontset.  Same for the Menu group. */
8399 	if (do_normal)
8400 	    gui_init_font(arg, TRUE);
8401 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8402 	if (do_menu)
8403 	{
8404 #    ifdef FONTSET_ALWAYS
8405 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8406 #    else
8407 	    /* YIKES!  This is a bug waiting to crash the program */
8408 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
8409 #    endif
8410 	    gui_mch_new_menu_font();
8411 	}
8412 #    ifdef FEAT_BEVAL
8413 	if (do_tooltip)
8414 	{
8415 	    /* The Athena widget set cannot currently handle switching between
8416 	     * displaying a single font and a fontset.
8417 	     * If the XtNinternational resource is set to True at widget
8418 	     * creation, then a fontset is always used, otherwise an
8419 	     * XFontStruct is used.
8420 	     */
8421 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8422 	    gui_mch_new_tooltip_font();
8423 	}
8424 #    endif
8425 #   endif
8426     }
8427     else
8428 # endif
8429     {
8430 	if (free_font)
8431 	    gui_mch_free_font(HL_TABLE()[idx].sg_font);
8432 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
8433 	/* If it worked and it's the Normal group, use it as the
8434 	 * normal font.  Same for the Menu group. */
8435 	if (HL_TABLE()[idx].sg_font != NOFONT)
8436 	{
8437 	    if (do_normal)
8438 		gui_init_font(arg, FALSE);
8439 #ifndef FONTSET_ALWAYS
8440 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8441 	    if (do_menu)
8442 	    {
8443 		gui.menu_font = HL_TABLE()[idx].sg_font;
8444 		gui_mch_new_menu_font();
8445 	    }
8446 # endif
8447 #endif
8448 	}
8449     }
8450 }
8451 
8452 #endif /* FEAT_GUI */
8453 
8454 /*
8455  * Table with the specifications for an attribute number.
8456  * Note that this table is used by ALL buffers.  This is required because the
8457  * GUI can redraw at any time for any buffer.
8458  */
8459 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
8460 
8461 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8462 
8463 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
8464 
8465 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8466 
8467 #ifdef FEAT_GUI
8468 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
8469 
8470 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8471 #endif
8472 
8473 /*
8474  * Return the attr number for a set of colors and font.
8475  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8476  * if the combination is new.
8477  * Return 0 for error (no more room).
8478  */
8479     static int
8480 get_attr_entry(table, aep)
8481     garray_T	*table;
8482     attrentry_T	*aep;
8483 {
8484     int		i;
8485     attrentry_T	*taep;
8486     static int	recursive = FALSE;
8487 
8488     /*
8489      * Init the table, in case it wasn't done yet.
8490      */
8491     table->ga_itemsize = sizeof(attrentry_T);
8492     table->ga_growsize = 7;
8493 
8494     /*
8495      * Try to find an entry with the same specifications.
8496      */
8497     for (i = 0; i < table->ga_len; ++i)
8498     {
8499 	taep = &(((attrentry_T *)table->ga_data)[i]);
8500 	if (	   aep->ae_attr == taep->ae_attr
8501 		&& (
8502 #ifdef FEAT_GUI
8503 		       (table == &gui_attr_table
8504 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8505 			    && aep->ae_u.gui.bg_color
8506 						    == taep->ae_u.gui.bg_color
8507 			    && aep->ae_u.gui.sp_color
8508 						    == taep->ae_u.gui.sp_color
8509 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
8510 #  ifdef FEAT_XFONTSET
8511 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
8512 #  endif
8513 			    ))
8514 		    ||
8515 #endif
8516 		       (table == &term_attr_table
8517 			&& (aep->ae_u.term.start == NULL)
8518 					    == (taep->ae_u.term.start == NULL)
8519 			&& (aep->ae_u.term.start == NULL
8520 			    || STRCMP(aep->ae_u.term.start,
8521 						  taep->ae_u.term.start) == 0)
8522 			&& (aep->ae_u.term.stop == NULL)
8523 					     == (taep->ae_u.term.stop == NULL)
8524 			&& (aep->ae_u.term.stop == NULL
8525 			    || STRCMP(aep->ae_u.term.stop,
8526 						  taep->ae_u.term.stop) == 0))
8527 		    || (table == &cterm_attr_table
8528 			    && aep->ae_u.cterm.fg_color
8529 						  == taep->ae_u.cterm.fg_color
8530 			    && aep->ae_u.cterm.bg_color
8531 						 == taep->ae_u.cterm.bg_color)
8532 		     ))
8533 
8534 	return i + ATTR_OFF;
8535     }
8536 
8537     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
8538     {
8539 	/*
8540 	 * Running out of attribute entries!  remove all attributes, and
8541 	 * compute new ones for all groups.
8542 	 * When called recursively, we are really out of numbers.
8543 	 */
8544 	if (recursive)
8545 	{
8546 	    EMSG(_("E424: Too many different highlighting attributes in use"));
8547 	    return 0;
8548 	}
8549 	recursive = TRUE;
8550 
8551 	clear_hl_tables();
8552 
8553 	must_redraw = CLEAR;
8554 
8555 	for (i = 0; i < highlight_ga.ga_len; ++i)
8556 	    set_hl_attr(i);
8557 
8558 	recursive = FALSE;
8559     }
8560 
8561     /*
8562      * This is a new combination of colors and font, add an entry.
8563      */
8564     if (ga_grow(table, 1) == FAIL)
8565 	return 0;
8566 
8567     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8568     vim_memset(taep, 0, sizeof(attrentry_T));
8569     taep->ae_attr = aep->ae_attr;
8570 #ifdef FEAT_GUI
8571     if (table == &gui_attr_table)
8572     {
8573 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8574 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8575 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8576 	taep->ae_u.gui.font = aep->ae_u.gui.font;
8577 # ifdef FEAT_XFONTSET
8578 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
8579 # endif
8580     }
8581 #endif
8582     if (table == &term_attr_table)
8583     {
8584 	if (aep->ae_u.term.start == NULL)
8585 	    taep->ae_u.term.start = NULL;
8586 	else
8587 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
8588 	if (aep->ae_u.term.stop == NULL)
8589 	    taep->ae_u.term.stop = NULL;
8590 	else
8591 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
8592     }
8593     else if (table == &cterm_attr_table)
8594     {
8595 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8596 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
8597     }
8598     ++table->ga_len;
8599     return (table->ga_len - 1 + ATTR_OFF);
8600 }
8601 
8602 /*
8603  * Clear all highlight tables.
8604  */
8605     void
8606 clear_hl_tables()
8607 {
8608     int		i;
8609     attrentry_T	*taep;
8610 
8611 #ifdef FEAT_GUI
8612     ga_clear(&gui_attr_table);
8613 #endif
8614     for (i = 0; i < term_attr_table.ga_len; ++i)
8615     {
8616 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8617 	vim_free(taep->ae_u.term.start);
8618 	vim_free(taep->ae_u.term.stop);
8619     }
8620     ga_clear(&term_attr_table);
8621     ga_clear(&cterm_attr_table);
8622 }
8623 
8624 #if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
8625 /*
8626  * Combine special attributes (e.g., for spelling) with other attributes
8627  * (e.g., for syntax highlighting).
8628  * "prim_attr" overrules "char_attr".
8629  * This creates a new group when required.
8630  * Since we expect there to be few spelling mistakes we don't cache the
8631  * result.
8632  * Return the resulting attributes.
8633  */
8634     int
8635 hl_combine_attr(char_attr, prim_attr)
8636     int	    char_attr;
8637     int	    prim_attr;
8638 {
8639     attrentry_T *char_aep = NULL;
8640     attrentry_T *spell_aep;
8641     attrentry_T new_en;
8642 
8643     if (char_attr == 0)
8644 	return prim_attr;
8645     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8646 	return char_attr | prim_attr;
8647 #ifdef FEAT_GUI
8648     if (gui.in_use)
8649     {
8650 	if (char_attr > HL_ALL)
8651 	    char_aep = syn_gui_attr2entry(char_attr);
8652 	if (char_aep != NULL)
8653 	    new_en = *char_aep;
8654 	else
8655 	{
8656 	    vim_memset(&new_en, 0, sizeof(new_en));
8657 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
8658 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
8659 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
8660 	    if (char_attr <= HL_ALL)
8661 		new_en.ae_attr = char_attr;
8662 	}
8663 
8664 	if (prim_attr <= HL_ALL)
8665 	    new_en.ae_attr |= prim_attr;
8666 	else
8667 	{
8668 	    spell_aep = syn_gui_attr2entry(prim_attr);
8669 	    if (spell_aep != NULL)
8670 	    {
8671 		new_en.ae_attr |= spell_aep->ae_attr;
8672 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8673 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8674 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8675 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8676 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8677 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8678 		if (spell_aep->ae_u.gui.font != NOFONT)
8679 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8680 # ifdef FEAT_XFONTSET
8681 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8682 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8683 # endif
8684 	    }
8685 	}
8686 	return get_attr_entry(&gui_attr_table, &new_en);
8687     }
8688 #endif
8689 
8690     if (t_colors > 1)
8691     {
8692 	if (char_attr > HL_ALL)
8693 	    char_aep = syn_cterm_attr2entry(char_attr);
8694 	if (char_aep != NULL)
8695 	    new_en = *char_aep;
8696 	else
8697 	{
8698 	    vim_memset(&new_en, 0, sizeof(new_en));
8699 	    if (char_attr <= HL_ALL)
8700 		new_en.ae_attr = char_attr;
8701 	}
8702 
8703 	if (prim_attr <= HL_ALL)
8704 	    new_en.ae_attr |= prim_attr;
8705 	else
8706 	{
8707 	    spell_aep = syn_cterm_attr2entry(prim_attr);
8708 	    if (spell_aep != NULL)
8709 	    {
8710 		new_en.ae_attr |= spell_aep->ae_attr;
8711 		if (spell_aep->ae_u.cterm.fg_color > 0)
8712 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8713 		if (spell_aep->ae_u.cterm.bg_color > 0)
8714 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8715 	    }
8716 	}
8717 	return get_attr_entry(&cterm_attr_table, &new_en);
8718     }
8719 
8720     if (char_attr > HL_ALL)
8721 	char_aep = syn_term_attr2entry(char_attr);
8722     if (char_aep != NULL)
8723 	new_en = *char_aep;
8724     else
8725     {
8726 	vim_memset(&new_en, 0, sizeof(new_en));
8727 	if (char_attr <= HL_ALL)
8728 	    new_en.ae_attr = char_attr;
8729     }
8730 
8731     if (prim_attr <= HL_ALL)
8732 	new_en.ae_attr |= prim_attr;
8733     else
8734     {
8735 	spell_aep = syn_term_attr2entry(prim_attr);
8736 	if (spell_aep != NULL)
8737 	{
8738 	    new_en.ae_attr |= spell_aep->ae_attr;
8739 	    if (spell_aep->ae_u.term.start != NULL)
8740 	    {
8741 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8742 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8743 	    }
8744 	}
8745     }
8746     return get_attr_entry(&term_attr_table, &new_en);
8747 }
8748 #endif
8749 
8750 #ifdef FEAT_GUI
8751 
8752     attrentry_T *
8753 syn_gui_attr2entry(attr)
8754     int		    attr;
8755 {
8756     attr -= ATTR_OFF;
8757     if (attr >= gui_attr_table.ga_len)	    /* did ":syntax clear" */
8758 	return NULL;
8759     return &(GUI_ATTR_ENTRY(attr));
8760 }
8761 #endif /* FEAT_GUI */
8762 
8763 /*
8764  * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8765  * Only to be used when "attr" > HL_ALL.
8766  */
8767     int
8768 syn_attr2attr(attr)
8769     int	    attr;
8770 {
8771     attrentry_T	*aep;
8772 
8773 #ifdef FEAT_GUI
8774     if (gui.in_use)
8775 	aep = syn_gui_attr2entry(attr);
8776     else
8777 #endif
8778 	if (t_colors > 1)
8779 	aep = syn_cterm_attr2entry(attr);
8780     else
8781 	aep = syn_term_attr2entry(attr);
8782 
8783     if (aep == NULL)	    /* highlighting not set */
8784 	return 0;
8785     return aep->ae_attr;
8786 }
8787 
8788 
8789     attrentry_T *
8790 syn_term_attr2entry(attr)
8791     int		    attr;
8792 {
8793     attr -= ATTR_OFF;
8794     if (attr >= term_attr_table.ga_len)	    /* did ":syntax clear" */
8795 	return NULL;
8796     return &(TERM_ATTR_ENTRY(attr));
8797 }
8798 
8799     attrentry_T *
8800 syn_cterm_attr2entry(attr)
8801     int		    attr;
8802 {
8803     attr -= ATTR_OFF;
8804     if (attr >= cterm_attr_table.ga_len)	/* did ":syntax clear" */
8805 	return NULL;
8806     return &(CTERM_ATTR_ENTRY(attr));
8807 }
8808 
8809 #define LIST_ATTR   1
8810 #define LIST_STRING 2
8811 #define LIST_INT    3
8812 
8813     static void
8814 highlight_list_one(id)
8815     int		id;
8816 {
8817     struct hl_group	*sgp;
8818     int			didh = FALSE;
8819 
8820     sgp = &HL_TABLE()[id - 1];	    /* index is ID minus one */
8821 
8822     didh = highlight_list_arg(id, didh, LIST_ATTR,
8823 				    sgp->sg_term, NULL, "term");
8824     didh = highlight_list_arg(id, didh, LIST_STRING,
8825 				    0, sgp->sg_start, "start");
8826     didh = highlight_list_arg(id, didh, LIST_STRING,
8827 				    0, sgp->sg_stop, "stop");
8828 
8829     didh = highlight_list_arg(id, didh, LIST_ATTR,
8830 				    sgp->sg_cterm, NULL, "cterm");
8831     didh = highlight_list_arg(id, didh, LIST_INT,
8832 				    sgp->sg_cterm_fg, NULL, "ctermfg");
8833     didh = highlight_list_arg(id, didh, LIST_INT,
8834 				    sgp->sg_cterm_bg, NULL, "ctermbg");
8835 
8836 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
8837     didh = highlight_list_arg(id, didh, LIST_ATTR,
8838 				    sgp->sg_gui, NULL, "gui");
8839     didh = highlight_list_arg(id, didh, LIST_STRING,
8840 				    0, sgp->sg_gui_fg_name, "guifg");
8841     didh = highlight_list_arg(id, didh, LIST_STRING,
8842 				    0, sgp->sg_gui_bg_name, "guibg");
8843     didh = highlight_list_arg(id, didh, LIST_STRING,
8844 				    0, sgp->sg_gui_sp_name, "guisp");
8845 #endif
8846 #ifdef FEAT_GUI
8847     didh = highlight_list_arg(id, didh, LIST_STRING,
8848 				    0, sgp->sg_font_name, "font");
8849 #endif
8850 
8851     if (sgp->sg_link && !got_int)
8852     {
8853 	(void)syn_list_header(didh, 9999, id);
8854 	didh = TRUE;
8855 	msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8856 	msg_putchar(' ');
8857 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8858     }
8859 
8860     if (!didh)
8861 	highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
8862 #ifdef FEAT_EVAL
8863     if (p_verbose > 0)
8864 	last_set_msg(sgp->sg_scriptID);
8865 #endif
8866 }
8867 
8868     static int
8869 highlight_list_arg(id, didh, type, iarg, sarg, name)
8870     int		id;
8871     int		didh;
8872     int		type;
8873     int		iarg;
8874     char_u	*sarg;
8875     char	*name;
8876 {
8877     char_u	buf[100];
8878     char_u	*ts;
8879     int		i;
8880 
8881     if (got_int)
8882 	return FALSE;
8883     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8884     {
8885 	ts = buf;
8886 	if (type == LIST_INT)
8887 	    sprintf((char *)buf, "%d", iarg - 1);
8888 	else if (type == LIST_STRING)
8889 	    ts = sarg;
8890 	else /* type == LIST_ATTR */
8891 	{
8892 	    buf[0] = NUL;
8893 	    for (i = 0; hl_attr_table[i] != 0; ++i)
8894 	    {
8895 		if (iarg & hl_attr_table[i])
8896 		{
8897 		    if (buf[0] != NUL)
8898 			vim_strcat(buf, (char_u *)",", 100);
8899 		    vim_strcat(buf, (char_u *)hl_name_table[i], 100);
8900 		    iarg &= ~hl_attr_table[i];	    /* don't want "inverse" */
8901 		}
8902 	    }
8903 	}
8904 
8905 	(void)syn_list_header(didh,
8906 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8907 	didh = TRUE;
8908 	if (!got_int)
8909 	{
8910 	    if (*name != NUL)
8911 	    {
8912 		MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8913 		MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8914 	    }
8915 	    msg_outtrans(ts);
8916 	}
8917     }
8918     return didh;
8919 }
8920 
8921 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8922 /*
8923  * Return "1" if highlight group "id" has attribute "flag".
8924  * Return NULL otherwise.
8925  */
8926     char_u *
8927 highlight_has_attr(id, flag, modec)
8928     int		id;
8929     int		flag;
8930     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
8931 {
8932     int		attr;
8933 
8934     if (id <= 0 || id > highlight_ga.ga_len)
8935 	return NULL;
8936 
8937 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
8938     if (modec == 'g')
8939 	attr = HL_TABLE()[id - 1].sg_gui;
8940     else
8941 #endif
8942 	 if (modec == 'c')
8943 	attr = HL_TABLE()[id - 1].sg_cterm;
8944     else
8945 	attr = HL_TABLE()[id - 1].sg_term;
8946 
8947     if (attr & flag)
8948 	return (char_u *)"1";
8949     return NULL;
8950 }
8951 #endif
8952 
8953 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8954 /*
8955  * Return color name of highlight group "id".
8956  */
8957     char_u *
8958 highlight_color(id, what, modec)
8959     int		id;
8960     char_u	*what;	/* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
8961     int		modec;	/* 'g' for GUI, 'c' for cterm, 't' for term */
8962 {
8963     static char_u	name[20];
8964     int			n;
8965     int			fg = FALSE;
8966     int			sp = FALSE;
8967     int			font = FALSE;
8968 
8969     if (id <= 0 || id > highlight_ga.ga_len)
8970 	return NULL;
8971 
8972     if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
8973 	fg = TRUE;
8974     else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
8975 	     && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
8976 	font = TRUE;
8977     else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
8978 	sp = TRUE;
8979     else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8980 	return NULL;
8981     if (modec == 'g')
8982     {
8983 # ifdef FEAT_GUI
8984 	/* return font name */
8985 	if (font)
8986 	    return HL_TABLE()[id - 1].sg_font_name;
8987 
8988 	/* return #RRGGBB form (only possible when GUI is running) */
8989 	if (gui.in_use && what[2] == '#')
8990 	{
8991 	    guicolor_T		color;
8992 	    long_u		rgb;
8993 	    static char_u	buf[10];
8994 
8995 	    if (fg)
8996 		color = HL_TABLE()[id - 1].sg_gui_fg;
8997 	    else if (sp)
8998 		color = HL_TABLE()[id - 1].sg_gui_sp;
8999 	    else
9000 		color = HL_TABLE()[id - 1].sg_gui_bg;
9001 	    if (color == INVALCOLOR)
9002 		return NULL;
9003 	    rgb = gui_mch_get_rgb(color);
9004 	    sprintf((char *)buf, "#%02x%02x%02x",
9005 				      (unsigned)(rgb >> 16),
9006 				      (unsigned)(rgb >> 8) & 255,
9007 				      (unsigned)rgb & 255);
9008 	    return buf;
9009 	}
9010 #endif
9011 	if (fg)
9012 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
9013 	if (sp)
9014 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
9015 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
9016     }
9017     if (font || sp)
9018 	return NULL;
9019     if (modec == 'c')
9020     {
9021 	if (fg)
9022 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9023 	else
9024 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
9025 	sprintf((char *)name, "%d", n);
9026 	return name;
9027     }
9028     /* term doesn't have color */
9029     return NULL;
9030 }
9031 #endif
9032 
9033 #if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
9034 	|| defined(PROTO)
9035 /*
9036  * Return color name of highlight group "id" as RGB value.
9037  */
9038     long_u
9039 highlight_gui_color_rgb(id, fg)
9040     int		id;
9041     int		fg;	/* TRUE = fg, FALSE = bg */
9042 {
9043     guicolor_T	color;
9044 
9045     if (id <= 0 || id > highlight_ga.ga_len)
9046 	return 0L;
9047 
9048     if (fg)
9049 	color = HL_TABLE()[id - 1].sg_gui_fg;
9050     else
9051 	color = HL_TABLE()[id - 1].sg_gui_bg;
9052 
9053     if (color == INVALCOLOR)
9054 	return 0L;
9055 
9056     return gui_mch_get_rgb(color);
9057 }
9058 #endif
9059 
9060 /*
9061  * Output the syntax list header.
9062  * Return TRUE when started a new line.
9063  */
9064     static int
9065 syn_list_header(did_header, outlen, id)
9066     int	    did_header;		/* did header already */
9067     int	    outlen;		/* length of string that comes */
9068     int	    id;			/* highlight group id */
9069 {
9070     int	    endcol = 19;
9071     int	    newline = TRUE;
9072 
9073     if (!did_header)
9074     {
9075 	msg_putchar('\n');
9076 	if (got_int)
9077 	    return TRUE;
9078 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
9079 	endcol = 15;
9080     }
9081     else if (msg_col + outlen + 1 >= Columns)
9082     {
9083 	msg_putchar('\n');
9084 	if (got_int)
9085 	    return TRUE;
9086     }
9087     else
9088     {
9089 	if (msg_col >= endcol)	/* wrap around is like starting a new line */
9090 	    newline = FALSE;
9091     }
9092 
9093     if (msg_col >= endcol)	/* output at least one space */
9094 	endcol = msg_col + 1;
9095     if (Columns <= endcol)	/* avoid hang for tiny window */
9096 	endcol = Columns - 1;
9097 
9098     msg_advance(endcol);
9099 
9100     /* Show "xxx" with the attributes. */
9101     if (!did_header)
9102     {
9103 	msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9104 	msg_putchar(' ');
9105     }
9106 
9107     return newline;
9108 }
9109 
9110 /*
9111  * Set the attribute numbers for a highlight group.
9112  * Called after one of the attributes has changed.
9113  */
9114     static void
9115 set_hl_attr(idx)
9116     int		idx;	    /* index in array */
9117 {
9118     attrentry_T		at_en;
9119     struct hl_group	*sgp = HL_TABLE() + idx;
9120 
9121     /* The "Normal" group doesn't need an attribute number */
9122     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9123 	return;
9124 
9125 #ifdef FEAT_GUI
9126     /*
9127      * For the GUI mode: If there are other than "normal" highlighting
9128      * attributes, need to allocate an attr number.
9129      */
9130     if (sgp->sg_gui_fg == INVALCOLOR
9131 	    && sgp->sg_gui_bg == INVALCOLOR
9132 	    && sgp->sg_gui_sp == INVALCOLOR
9133 	    && sgp->sg_font == NOFONT
9134 # ifdef FEAT_XFONTSET
9135 	    && sgp->sg_fontset == NOFONTSET
9136 # endif
9137 	    )
9138     {
9139 	sgp->sg_gui_attr = sgp->sg_gui;
9140     }
9141     else
9142     {
9143 	at_en.ae_attr = sgp->sg_gui;
9144 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9145 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
9146 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
9147 	at_en.ae_u.gui.font = sgp->sg_font;
9148 # ifdef FEAT_XFONTSET
9149 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
9150 # endif
9151 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9152     }
9153 #endif
9154     /*
9155      * For the term mode: If there are other than "normal" highlighting
9156      * attributes, need to allocate an attr number.
9157      */
9158     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9159 	sgp->sg_term_attr = sgp->sg_term;
9160     else
9161     {
9162 	at_en.ae_attr = sgp->sg_term;
9163 	at_en.ae_u.term.start = sgp->sg_start;
9164 	at_en.ae_u.term.stop = sgp->sg_stop;
9165 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9166     }
9167 
9168     /*
9169      * For the color term mode: If there are other than "normal"
9170      * highlighting attributes, need to allocate an attr number.
9171      */
9172     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9173 	sgp->sg_cterm_attr = sgp->sg_cterm;
9174     else
9175     {
9176 	at_en.ae_attr = sgp->sg_cterm;
9177 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9178 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9179 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9180     }
9181 }
9182 
9183 /*
9184  * Lookup a highlight group name and return it's ID.
9185  * If it is not found, 0 is returned.
9186  */
9187     int
9188 syn_name2id(name)
9189     char_u	*name;
9190 {
9191     int		i;
9192     char_u	name_u[200];
9193 
9194     /* Avoid using stricmp() too much, it's slow on some systems */
9195     /* Avoid alloc()/free(), these are slow too.  ID names over 200 chars
9196      * don't deserve to be found! */
9197     vim_strncpy(name_u, name, 199);
9198     vim_strup(name_u);
9199     for (i = highlight_ga.ga_len; --i >= 0; )
9200 	if (HL_TABLE()[i].sg_name_u != NULL
9201 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9202 	    break;
9203     return i + 1;
9204 }
9205 
9206 #if defined(FEAT_EVAL) || defined(PROTO)
9207 /*
9208  * Return TRUE if highlight group "name" exists.
9209  */
9210     int
9211 highlight_exists(name)
9212     char_u	*name;
9213 {
9214     return (syn_name2id(name) > 0);
9215 }
9216 
9217 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9218 /*
9219  * Return the name of highlight group "id".
9220  * When not a valid ID return an empty string.
9221  */
9222     char_u *
9223 syn_id2name(id)
9224     int		id;
9225 {
9226     if (id <= 0 || id > highlight_ga.ga_len)
9227 	return (char_u *)"";
9228     return HL_TABLE()[id - 1].sg_name;
9229 }
9230 # endif
9231 #endif
9232 
9233 /*
9234  * Like syn_name2id(), but take a pointer + length argument.
9235  */
9236     int
9237 syn_namen2id(linep, len)
9238     char_u  *linep;
9239     int	    len;
9240 {
9241     char_u  *name;
9242     int	    id = 0;
9243 
9244     name = vim_strnsave(linep, len);
9245     if (name != NULL)
9246     {
9247 	id = syn_name2id(name);
9248 	vim_free(name);
9249     }
9250     return id;
9251 }
9252 
9253 /*
9254  * Find highlight group name in the table and return it's ID.
9255  * The argument is a pointer to the name and the length of the name.
9256  * If it doesn't exist yet, a new entry is created.
9257  * Return 0 for failure.
9258  */
9259     int
9260 syn_check_group(pp, len)
9261     char_u		*pp;
9262     int			len;
9263 {
9264     int	    id;
9265     char_u  *name;
9266 
9267     name = vim_strnsave(pp, len);
9268     if (name == NULL)
9269 	return 0;
9270 
9271     id = syn_name2id(name);
9272     if (id == 0)			/* doesn't exist yet */
9273 	id = syn_add_group(name);
9274     else
9275 	vim_free(name);
9276     return id;
9277 }
9278 
9279 /*
9280  * Add new highlight group and return it's ID.
9281  * "name" must be an allocated string, it will be consumed.
9282  * Return 0 for failure.
9283  */
9284     static int
9285 syn_add_group(name)
9286     char_u	*name;
9287 {
9288     char_u	*p;
9289 
9290     /* Check that the name is ASCII letters, digits and underscore. */
9291     for (p = name; *p != NUL; ++p)
9292     {
9293 	if (!vim_isprintc(*p))
9294 	{
9295 	    EMSG(_("E669: Unprintable character in group name"));
9296 	    vim_free(name);
9297 	    return 0;
9298 	}
9299 	else if (!ASCII_ISALNUM(*p) && *p != '_')
9300 	{
9301 	    /* This is an error, but since there previously was no check only
9302 	     * give a warning. */
9303 	    msg_source(hl_attr(HLF_W));
9304 	    MSG(_("W18: Invalid character in group name"));
9305 	    break;
9306 	}
9307     }
9308 
9309     /*
9310      * First call for this growarray: init growing array.
9311      */
9312     if (highlight_ga.ga_data == NULL)
9313     {
9314 	highlight_ga.ga_itemsize = sizeof(struct hl_group);
9315 	highlight_ga.ga_growsize = 10;
9316     }
9317 
9318     if (highlight_ga.ga_len >= MAX_HL_ID)
9319     {
9320 	EMSG(_("E849: Too many highlight and syntax groups"));
9321 	vim_free(name);
9322 	return 0;
9323     }
9324 
9325     /*
9326      * Make room for at least one other syntax_highlight entry.
9327      */
9328     if (ga_grow(&highlight_ga, 1) == FAIL)
9329     {
9330 	vim_free(name);
9331 	return 0;
9332     }
9333 
9334     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9335     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9336     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9337 #ifdef FEAT_GUI
9338     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9339     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
9340     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
9341 #endif
9342     ++highlight_ga.ga_len;
9343 
9344     return highlight_ga.ga_len;		    /* ID is index plus one */
9345 }
9346 
9347 /*
9348  * When, just after calling syn_add_group(), an error is discovered, this
9349  * function deletes the new name.
9350  */
9351     static void
9352 syn_unadd_group()
9353 {
9354     --highlight_ga.ga_len;
9355     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9356     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9357 }
9358 
9359 /*
9360  * Translate a group ID to highlight attributes.
9361  */
9362     int
9363 syn_id2attr(hl_id)
9364     int			hl_id;
9365 {
9366     int			attr;
9367     struct hl_group	*sgp;
9368 
9369     hl_id = syn_get_final_id(hl_id);
9370     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
9371 
9372 #ifdef FEAT_GUI
9373     /*
9374      * Only use GUI attr when the GUI is being used.
9375      */
9376     if (gui.in_use)
9377 	attr = sgp->sg_gui_attr;
9378     else
9379 #endif
9380 	if (t_colors > 1)
9381 	    attr = sgp->sg_cterm_attr;
9382 	else
9383 	    attr = sgp->sg_term_attr;
9384 
9385     return attr;
9386 }
9387 
9388 #ifdef FEAT_GUI
9389 /*
9390  * Get the GUI colors and attributes for a group ID.
9391  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9392  */
9393     int
9394 syn_id2colors(hl_id, fgp, bgp)
9395     int		hl_id;
9396     guicolor_T	*fgp;
9397     guicolor_T	*bgp;
9398 {
9399     struct hl_group	*sgp;
9400 
9401     hl_id = syn_get_final_id(hl_id);
9402     sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
9403 
9404     *fgp = sgp->sg_gui_fg;
9405     *bgp = sgp->sg_gui_bg;
9406     return sgp->sg_gui;
9407 }
9408 #endif
9409 
9410 /*
9411  * Translate a group ID to the final group ID (following links).
9412  */
9413     int
9414 syn_get_final_id(hl_id)
9415     int			hl_id;
9416 {
9417     int			count;
9418     struct hl_group	*sgp;
9419 
9420     if (hl_id > highlight_ga.ga_len || hl_id < 1)
9421 	return 0;			/* Can be called from eval!! */
9422 
9423     /*
9424      * Follow links until there is no more.
9425      * Look out for loops!  Break after 100 links.
9426      */
9427     for (count = 100; --count >= 0; )
9428     {
9429 	sgp = &HL_TABLE()[hl_id - 1];	    /* index is ID minus one */
9430 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9431 	    break;
9432 	hl_id = sgp->sg_link;
9433     }
9434 
9435     return hl_id;
9436 }
9437 
9438 #ifdef FEAT_GUI
9439 /*
9440  * Call this function just after the GUI has started.
9441  * It finds the font and color handles for the highlighting groups.
9442  */
9443     void
9444 highlight_gui_started()
9445 {
9446     int	    idx;
9447 
9448     /* First get the colors from the "Normal" and "Menu" group, if set */
9449     set_normal_colors();
9450 
9451     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9452 	gui_do_one_color(idx, FALSE, FALSE);
9453 
9454     highlight_changed();
9455 }
9456 
9457     static void
9458 gui_do_one_color(idx, do_menu, do_tooltip)
9459     int		idx;
9460     int		do_menu;	/* TRUE: might set the menu font */
9461     int		do_tooltip;	/* TRUE: might set the tooltip font */
9462 {
9463     int		didit = FALSE;
9464 
9465     if (HL_TABLE()[idx].sg_font_name != NULL)
9466     {
9467 	hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9468 							    do_tooltip, TRUE);
9469 	didit = TRUE;
9470     }
9471     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9472     {
9473 	HL_TABLE()[idx].sg_gui_fg =
9474 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9475 	didit = TRUE;
9476     }
9477     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9478     {
9479 	HL_TABLE()[idx].sg_gui_bg =
9480 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9481 	didit = TRUE;
9482     }
9483     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9484     {
9485 	HL_TABLE()[idx].sg_gui_sp =
9486 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9487 	didit = TRUE;
9488     }
9489     if (didit)	/* need to get a new attr number */
9490 	set_hl_attr(idx);
9491 }
9492 
9493 #endif
9494 
9495 /*
9496  * Translate the 'highlight' option into attributes in highlight_attr[] and
9497  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
9498  * corresponding highlights to use on top of HLF_SNC is computed.
9499  * Called only when the 'highlight' option has been changed and upon first
9500  * screen redraw after any :highlight command.
9501  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
9502  */
9503     int
9504 highlight_changed()
9505 {
9506     int		hlf;
9507     int		i;
9508     char_u	*p;
9509     int		attr;
9510     char_u	*end;
9511     int		id;
9512 #ifdef USER_HIGHLIGHT
9513     char_u      userhl[10];
9514 # ifdef FEAT_STL_OPT
9515     int		id_SNC = -1;
9516     int		id_S = -1;
9517     int		hlcnt;
9518 # endif
9519 #endif
9520     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
9521 
9522     need_highlight_changed = FALSE;
9523 
9524     /*
9525      * Clear all attributes.
9526      */
9527     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9528 	highlight_attr[hlf] = 0;
9529 
9530     /*
9531      * First set all attributes to their default value.
9532      * Then use the attributes from the 'highlight' option.
9533      */
9534     for (i = 0; i < 2; ++i)
9535     {
9536 	if (i)
9537 	    p = p_hl;
9538 	else
9539 	    p = get_highlight_default();
9540 	if (p == NULL)	    /* just in case */
9541 	    continue;
9542 
9543 	while (*p)
9544 	{
9545 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9546 		if (hl_flags[hlf] == *p)
9547 		    break;
9548 	    ++p;
9549 	    if (hlf == (int)HLF_COUNT || *p == NUL)
9550 		return FAIL;
9551 
9552 	    /*
9553 	     * Allow several hl_flags to be combined, like "bu" for
9554 	     * bold-underlined.
9555 	     */
9556 	    attr = 0;
9557 	    for ( ; *p && *p != ','; ++p)	    /* parse upto comma */
9558 	    {
9559 		if (vim_iswhite(*p))		    /* ignore white space */
9560 		    continue;
9561 
9562 		if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
9563 		    return FAIL;
9564 
9565 		switch (*p)
9566 		{
9567 		    case 'b':	attr |= HL_BOLD;
9568 				break;
9569 		    case 'i':	attr |= HL_ITALIC;
9570 				break;
9571 		    case '-':
9572 		    case 'n':			    /* no highlighting */
9573 				break;
9574 		    case 'r':	attr |= HL_INVERSE;
9575 				break;
9576 		    case 's':	attr |= HL_STANDOUT;
9577 				break;
9578 		    case 'u':	attr |= HL_UNDERLINE;
9579 				break;
9580 		    case 'c':	attr |= HL_UNDERCURL;
9581 				break;
9582 		    case ':':	++p;		    /* highlight group name */
9583 				if (attr || *p == NUL)	 /* no combinations */
9584 				    return FAIL;
9585 				end = vim_strchr(p, ',');
9586 				if (end == NULL)
9587 				    end = p + STRLEN(p);
9588 				id = syn_check_group(p, (int)(end - p));
9589 				if (id == 0)
9590 				    return FAIL;
9591 				attr = syn_id2attr(id);
9592 				p = end - 1;
9593 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9594 				if (hlf == (int)HLF_SNC)
9595 				    id_SNC = syn_get_final_id(id);
9596 				else if (hlf == (int)HLF_S)
9597 				    id_S = syn_get_final_id(id);
9598 #endif
9599 				break;
9600 		    default:	return FAIL;
9601 		}
9602 	    }
9603 	    highlight_attr[hlf] = attr;
9604 
9605 	    p = skip_to_option_part(p);	    /* skip comma and spaces */
9606 	}
9607     }
9608 
9609 #ifdef USER_HIGHLIGHT
9610     /* Setup the user highlights
9611      *
9612      * Temporarily  utilize 10 more hl entries.  Have to be in there
9613      * simultaneously in case of table overflows in get_attr_entry()
9614      */
9615 # ifdef FEAT_STL_OPT
9616     if (ga_grow(&highlight_ga, 10) == FAIL)
9617 	return FAIL;
9618     hlcnt = highlight_ga.ga_len;
9619     if (id_S == 0)
9620     {		    /* Make sure id_S is always valid to simplify code below */
9621 	vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
9622 	HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9623 	id_S = hlcnt + 10;
9624     }
9625 # endif
9626     for (i = 0; i < 9; i++)
9627     {
9628 	sprintf((char *)userhl, "User%d", i + 1);
9629 	id = syn_name2id(userhl);
9630 	if (id == 0)
9631 	{
9632 	    highlight_user[i] = 0;
9633 # ifdef FEAT_STL_OPT
9634 	    highlight_stlnc[i] = 0;
9635 # endif
9636 	}
9637 	else
9638 	{
9639 # ifdef FEAT_STL_OPT
9640 	    struct hl_group *hlt = HL_TABLE();
9641 # endif
9642 
9643 	    highlight_user[i] = syn_id2attr(id);
9644 # ifdef FEAT_STL_OPT
9645 	    if (id_SNC == 0)
9646 	    {
9647 		vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9648 		hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9649 		hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
9650 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
9651 		hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9652 #  endif
9653 	    }
9654 	    else
9655 		mch_memmove(&hlt[hlcnt + i],
9656 			    &hlt[id_SNC - 1],
9657 			    sizeof(struct hl_group));
9658 	    hlt[hlcnt + i].sg_link = 0;
9659 
9660 	    /* Apply difference between UserX and HLF_S to HLF_SNC */
9661 	    hlt[hlcnt + i].sg_term ^=
9662 		hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9663 	    if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9664 		hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9665 	    if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9666 		hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9667 	    hlt[hlcnt + i].sg_cterm ^=
9668 		hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9669 	    if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9670 		hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9671 	    if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9672 		hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9673 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
9674 	    hlt[hlcnt + i].sg_gui ^=
9675 		hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9676 #  endif
9677 #  ifdef FEAT_GUI
9678 	    if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9679 		hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9680 	    if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9681 		hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9682 	    if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9683 		hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9684 	    if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9685 		hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9686 #   ifdef FEAT_XFONTSET
9687 	    if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9688 		hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9689 #   endif
9690 #  endif
9691 	    highlight_ga.ga_len = hlcnt + i + 1;
9692 	    set_hl_attr(hlcnt + i);	/* At long last we can apply */
9693 	    highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9694 # endif
9695 	}
9696     }
9697 # ifdef FEAT_STL_OPT
9698     highlight_ga.ga_len = hlcnt;
9699 # endif
9700 
9701 #endif /* USER_HIGHLIGHT */
9702 
9703     return OK;
9704 }
9705 
9706 #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
9707 
9708 static void highlight_list __ARGS((void));
9709 static void highlight_list_two __ARGS((int cnt, int attr));
9710 
9711 /*
9712  * Handle command line completion for :highlight command.
9713  */
9714     void
9715 set_context_in_highlight_cmd(xp, arg)
9716     expand_T	*xp;
9717     char_u	*arg;
9718 {
9719     char_u	*p;
9720 
9721     /* Default: expand group names */
9722     xp->xp_context = EXPAND_HIGHLIGHT;
9723     xp->xp_pattern = arg;
9724     include_link = 2;
9725     include_default = 1;
9726 
9727     /* (part of) subcommand already typed */
9728     if (*arg != NUL)
9729     {
9730 	p = skiptowhite(arg);
9731 	if (*p != NUL)			/* past "default" or group name */
9732 	{
9733 	    include_default = 0;
9734 	    if (STRNCMP("default", arg, p - arg) == 0)
9735 	    {
9736 		arg = skipwhite(p);
9737 		xp->xp_pattern = arg;
9738 		p = skiptowhite(arg);
9739 	    }
9740 	    if (*p != NUL)			/* past group name */
9741 	    {
9742 		include_link = 0;
9743 		if (arg[1] == 'i' && arg[0] == 'N')
9744 		    highlight_list();
9745 		if (STRNCMP("link", arg, p - arg) == 0
9746 			|| STRNCMP("clear", arg, p - arg) == 0)
9747 		{
9748 		    xp->xp_pattern = skipwhite(p);
9749 		    p = skiptowhite(xp->xp_pattern);
9750 		    if (*p != NUL)		/* past first group name */
9751 		    {
9752 			xp->xp_pattern = skipwhite(p);
9753 			p = skiptowhite(xp->xp_pattern);
9754 		    }
9755 		}
9756 		if (*p != NUL)			/* past group name(s) */
9757 		    xp->xp_context = EXPAND_NOTHING;
9758 	    }
9759 	}
9760     }
9761 }
9762 
9763 /*
9764  * List highlighting matches in a nice way.
9765  */
9766     static void
9767 highlight_list()
9768 {
9769     int		i;
9770 
9771     for (i = 10; --i >= 0; )
9772 	highlight_list_two(i, hl_attr(HLF_D));
9773     for (i = 40; --i >= 0; )
9774 	highlight_list_two(99, 0);
9775 }
9776 
9777     static void
9778 highlight_list_two(cnt, attr)
9779     int	    cnt;
9780     int	    attr;
9781 {
9782     msg_puts_attr((char_u *)&("N \bI \b!  \b"[cnt / 11]), attr);
9783     msg_clr_eos();
9784     out_flush();
9785     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9786 }
9787 
9788 #endif /* FEAT_CMDL_COMPL */
9789 
9790 #if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9791     || defined(FEAT_SIGNS) || defined(PROTO)
9792 /*
9793  * Function given to ExpandGeneric() to obtain the list of group names.
9794  * Also used for synIDattr() function.
9795  */
9796     char_u *
9797 get_highlight_name(xp, idx)
9798     expand_T	*xp UNUSED;
9799     int		idx;
9800 {
9801 #ifdef FEAT_CMDL_COMPL
9802     if (idx == highlight_ga.ga_len && include_none != 0)
9803 	return (char_u *)"none";
9804     if (idx == highlight_ga.ga_len + include_none && include_default != 0)
9805 	return (char_u *)"default";
9806     if (idx == highlight_ga.ga_len + include_none + include_default
9807 							 && include_link != 0)
9808 	return (char_u *)"link";
9809     if (idx == highlight_ga.ga_len + include_none + include_default + 1
9810 							 && include_link != 0)
9811 	return (char_u *)"clear";
9812 #endif
9813     if (idx < 0 || idx >= highlight_ga.ga_len)
9814 	return NULL;
9815     return HL_TABLE()[idx].sg_name;
9816 }
9817 #endif
9818 
9819 #if defined(FEAT_GUI) || defined(PROTO)
9820 /*
9821  * Free all the highlight group fonts.
9822  * Used when quitting for systems which need it.
9823  */
9824     void
9825 free_highlight_fonts()
9826 {
9827     int	    idx;
9828 
9829     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9830     {
9831 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
9832 	HL_TABLE()[idx].sg_font = NOFONT;
9833 # ifdef FEAT_XFONTSET
9834 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9835 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
9836 # endif
9837     }
9838 
9839     gui_mch_free_font(gui.norm_font);
9840 # ifdef FEAT_XFONTSET
9841     gui_mch_free_fontset(gui.fontset);
9842 # endif
9843 # ifndef FEAT_GUI_GTK
9844     gui_mch_free_font(gui.bold_font);
9845     gui_mch_free_font(gui.ital_font);
9846     gui_mch_free_font(gui.boldital_font);
9847 # endif
9848 }
9849 #endif
9850 
9851 /**************************************
9852  *  End of Highlighting stuff	      *
9853  **************************************/
9854