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