xref: /vim-8.2.3635/src/highlight.c (revision bc93cebb)
1 /* vi:set ts=8 sts=4 sw=4 noet:
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  * Highlighting stuff.
12  * Includes highlighting matches.
13  */
14 
15 #include "vim.h"
16 
17 #define SG_TERM		1	// term has been set
18 #define SG_CTERM	2	// cterm has been set
19 #define SG_GUI		4	// gui has been set
20 #define SG_LINK		8	// link has been set
21 
22 /*
23  * The "term", "cterm" and "gui" arguments can be any combination of the
24  * following names, separated by commas (but no spaces!).
25  */
26 static char *(hl_name_table[]) =
27     {"bold", "standout", "underline", "undercurl",
28       "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
29 static int hl_attr_table[] =
30     {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
31 #define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
32 
33 /*
34  * Structure that stores information about a highlight group.
35  * The ID of a highlight group is also called group ID.  It is the index in
36  * the highlight_ga array PLUS ONE.
37  */
38 typedef struct
39 {
40     char_u	*sg_name;	// highlight group name
41     char_u	*sg_name_u;	// uppercase of sg_name
42     int		sg_cleared;	// "hi clear" was used
43 // for normal terminals
44     int		sg_term;	// "term=" highlighting attributes
45     char_u	*sg_start;	// terminal string for start highl
46     char_u	*sg_stop;	// terminal string for stop highl
47     int		sg_term_attr;	// Screen attr for term mode
48 // for color terminals
49     int		sg_cterm;	// "cterm=" highlighting attr
50     int		sg_cterm_bold;	// bold attr was set for light color
51     int		sg_cterm_fg;	// terminal fg color number + 1
52     int		sg_cterm_bg;	// terminal bg color number + 1
53     int		sg_cterm_attr;	// Screen attr for color term mode
54 // for when using the GUI
55 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
56     guicolor_T	sg_gui_fg;	// GUI foreground color handle
57     guicolor_T	sg_gui_bg;	// GUI background color handle
58 #endif
59 #ifdef FEAT_GUI
60     guicolor_T	sg_gui_sp;	// GUI special color handle
61     GuiFont	sg_font;	// GUI font handle
62 #ifdef FEAT_XFONTSET
63     GuiFontset	sg_fontset;	// GUI fontset handle
64 #endif
65     char_u	*sg_font_name;  // GUI font or fontset name
66     int		sg_gui_attr;    // Screen attr for GUI mode
67 #endif
68 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
69 // Store the sp color name for the GUI or synIDattr()
70     int		sg_gui;		// "gui=" highlighting attributes
71     char_u	*sg_gui_fg_name;// GUI foreground color name
72     char_u	*sg_gui_bg_name;// GUI background color name
73     char_u	*sg_gui_sp_name;// GUI special color name
74 #endif
75     int		sg_link;	// link to this highlight group ID
76     int		sg_set;		// combination of SG_* flags
77 #ifdef FEAT_EVAL
78     sctx_T	sg_script_ctx;	// script in which the group was last set
79 #endif
80 } hl_group_T;
81 
82 // highlight groups for 'highlight' option
83 static garray_T highlight_ga;
84 #define HL_TABLE()	((hl_group_T *)((highlight_ga.ga_data)))
85 
86 /*
87  * An attribute number is the index in attr_table plus ATTR_OFF.
88  */
89 #define ATTR_OFF (HL_ALL + 1)
90 
91 static void syn_unadd_group(void);
92 static void set_hl_attr(int idx);
93 static void highlight_list_one(int id);
94 static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
95 static int syn_add_group(char_u *name);
96 static int hl_has_settings(int idx, int check_link);
97 static void highlight_clear(int idx);
98 
99 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
100 static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
101 #endif
102 #ifdef FEAT_GUI
103 static int  set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
104 static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
105 #endif
106 
107 /*
108  * The default highlight groups.  These are compiled-in for fast startup and
109  * they still work when the runtime files can't be found.
110  * When making changes here, also change runtime/colors/default.vim!
111  * The #ifdefs are needed to reduce the amount of static data.  Helps to make
112  * the 16 bit DOS (museum) version compile.
113  */
114 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
115 # define CENT(a, b) b
116 #else
117 # define CENT(a, b) a
118 #endif
119 static char *(highlight_init_both[]) = {
120     CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
121 	 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
122     CENT("IncSearch term=reverse cterm=reverse",
123 	 "IncSearch term=reverse cterm=reverse gui=reverse"),
124     CENT("ModeMsg term=bold cterm=bold",
125 	 "ModeMsg term=bold cterm=bold gui=bold"),
126     CENT("NonText term=bold ctermfg=Blue",
127 	 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
128     CENT("StatusLine term=reverse,bold cterm=reverse,bold",
129 	 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
130     CENT("StatusLineNC term=reverse cterm=reverse",
131 	 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
132     "default link EndOfBuffer NonText",
133     CENT("VertSplit term=reverse cterm=reverse",
134 	 "VertSplit term=reverse cterm=reverse gui=reverse"),
135 #ifdef FEAT_CLIPBOARD
136     CENT("VisualNOS term=underline,bold cterm=underline,bold",
137 	 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
138 #endif
139 #ifdef FEAT_DIFF
140     CENT("DiffText term=reverse cterm=bold ctermbg=Red",
141 	 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
142 #endif
143     CENT("PmenuSbar ctermbg=Grey",
144 	 "PmenuSbar ctermbg=Grey guibg=Grey"),
145     CENT("TabLineSel term=bold cterm=bold",
146 	 "TabLineSel term=bold cterm=bold gui=bold"),
147     CENT("TabLineFill term=reverse cterm=reverse",
148 	 "TabLineFill term=reverse cterm=reverse gui=reverse"),
149 #ifdef FEAT_GUI
150     "Cursor guibg=fg guifg=bg",
151     "lCursor guibg=fg guifg=bg", // should be different, but what?
152 #endif
153     "default link QuickFixLine Search",
154     CENT("Normal cterm=NONE", "Normal gui=NONE"),
155     NULL
156 };
157 
158 // Default colors only used with a light background.
159 static char *(highlight_init_light[]) = {
160     CENT("Directory term=bold ctermfg=DarkBlue",
161 	 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
162     CENT("LineNr term=underline ctermfg=Brown",
163 	 "LineNr term=underline ctermfg=Brown guifg=Brown"),
164     CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
165 	 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
166     CENT("MoreMsg term=bold ctermfg=DarkGreen",
167 	 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
168     CENT("Question term=standout ctermfg=DarkGreen",
169 	 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
170     CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
171 	 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
172 #ifdef FEAT_SPELL
173     CENT("SpellBad term=reverse ctermbg=LightRed",
174 	 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
175     CENT("SpellCap term=reverse ctermbg=LightBlue",
176 	 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
177     CENT("SpellRare term=reverse ctermbg=LightMagenta",
178 	 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
179     CENT("SpellLocal term=underline ctermbg=Cyan",
180 	 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
181 #endif
182     CENT("PmenuThumb ctermbg=Black",
183 	 "PmenuThumb ctermbg=Black guibg=Black"),
184     CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
185 	 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
186     CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
187 	 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
188     CENT("SpecialKey term=bold ctermfg=DarkBlue",
189 	 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
190     CENT("Title term=bold ctermfg=DarkMagenta",
191 	 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
192     CENT("WarningMsg term=standout ctermfg=DarkRed",
193 	 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
194 #ifdef FEAT_WILDMENU
195     CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
196 	 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
197 #endif
198 #ifdef FEAT_FOLDING
199     CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
200 	 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
201     CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
202 	 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
203 #endif
204 #ifdef FEAT_SIGNS
205     CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
206 	 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
207 #endif
208     CENT("Visual term=reverse",
209 	 "Visual term=reverse guibg=LightGrey"),
210 #ifdef FEAT_DIFF
211     CENT("DiffAdd term=bold ctermbg=LightBlue",
212 	 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
213     CENT("DiffChange term=bold ctermbg=LightMagenta",
214 	 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
215     CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
216 	 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
217 #endif
218     CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
219 	 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
220 #ifdef FEAT_SYN_HL
221     CENT("CursorColumn term=reverse ctermbg=LightGrey",
222 	 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
223     CENT("CursorLine term=underline cterm=underline",
224 	 "CursorLine term=underline cterm=underline guibg=Grey90"),
225     CENT("ColorColumn term=reverse ctermbg=LightRed",
226 	 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
227 #endif
228 #ifdef FEAT_CONCEAL
229     CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
230 	 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
231 #endif
232     CENT("MatchParen term=reverse ctermbg=Cyan",
233 	 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
234 #ifdef FEAT_TERMINAL
235     CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
236 	 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
237     CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
238 	 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
239 #endif
240 #ifdef FEAT_MENU
241     CENT("ToolbarLine term=underline ctermbg=LightGrey",
242 	 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
243     CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
244 	 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
245 #endif
246     NULL
247 };
248 
249 // Default colors only used with a dark background.
250 static char *(highlight_init_dark[]) = {
251     CENT("Directory term=bold ctermfg=LightCyan",
252 	 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
253     CENT("LineNr term=underline ctermfg=Yellow",
254 	 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
255     CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
256 	 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
257     CENT("MoreMsg term=bold ctermfg=LightGreen",
258 	 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
259     CENT("Question term=standout ctermfg=LightGreen",
260 	 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
261     CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
262 	 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
263     CENT("SpecialKey term=bold ctermfg=LightBlue",
264 	 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
265 #ifdef FEAT_SPELL
266     CENT("SpellBad term=reverse ctermbg=Red",
267 	 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
268     CENT("SpellCap term=reverse ctermbg=Blue",
269 	 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
270     CENT("SpellRare term=reverse ctermbg=Magenta",
271 	 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
272     CENT("SpellLocal term=underline ctermbg=Cyan",
273 	 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
274 #endif
275     CENT("PmenuThumb ctermbg=White",
276 	 "PmenuThumb ctermbg=White guibg=White"),
277     CENT("Pmenu ctermbg=Magenta ctermfg=Black",
278 	 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
279     CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
280 	 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
281     CENT("Title term=bold ctermfg=LightMagenta",
282 	 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
283     CENT("WarningMsg term=standout ctermfg=LightRed",
284 	 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
285 #ifdef FEAT_WILDMENU
286     CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
287 	 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
288 #endif
289 #ifdef FEAT_FOLDING
290     CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
291 	 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
292     CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
293 	 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
294 #endif
295 #ifdef FEAT_SIGNS
296     CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
297 	 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
298 #endif
299     CENT("Visual term=reverse",
300 	 "Visual term=reverse guibg=DarkGrey"),
301 #ifdef FEAT_DIFF
302     CENT("DiffAdd term=bold ctermbg=DarkBlue",
303 	 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
304     CENT("DiffChange term=bold ctermbg=DarkMagenta",
305 	 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
306     CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
307 	 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
308 #endif
309     CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
310 	 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
311 #ifdef FEAT_SYN_HL
312     CENT("CursorColumn term=reverse ctermbg=DarkGrey",
313 	 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
314     CENT("CursorLine term=underline cterm=underline",
315 	 "CursorLine term=underline cterm=underline guibg=Grey40"),
316     CENT("ColorColumn term=reverse ctermbg=DarkRed",
317 	 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
318 #endif
319     CENT("MatchParen term=reverse ctermbg=DarkCyan",
320 	 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
321 #ifdef FEAT_CONCEAL
322     CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
323 	 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
324 #endif
325 #ifdef FEAT_TERMINAL
326     CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
327 	 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
328     CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
329 	 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
330 #endif
331 #ifdef FEAT_MENU
332     CENT("ToolbarLine term=underline ctermbg=DarkGrey",
333 	 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
334     CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
335 	 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
336 #endif
337     NULL
338 };
339 
340 /*
341  * Returns the number of highlight groups.
342  */
343     int
344 highlight_num_groups(void)
345 {
346     return highlight_ga.ga_len;
347 }
348 
349 /*
350  * Returns the name of a highlight group.
351  */
352     char_u *
353 highlight_group_name(int id)
354 {
355     return HL_TABLE()[id].sg_name;
356 }
357 
358 /*
359  * Returns the ID of the link to a highlight group.
360  */
361     int
362 highlight_link_id(int id)
363 {
364     return HL_TABLE()[id].sg_link;
365 }
366 
367     void
368 init_highlight(
369     int		both,	    // include groups where 'bg' doesn't matter
370     int		reset)	    // clear group first
371 {
372     int		i;
373     char	**pp;
374     static int	had_both = FALSE;
375 #ifdef FEAT_EVAL
376     char_u	*p;
377 
378     /*
379      * Try finding the color scheme file.  Used when a color file was loaded
380      * and 'background' or 't_Co' is changed.
381      */
382     p = get_var_value((char_u *)"g:colors_name");
383     if (p != NULL)
384     {
385 	// The value of g:colors_name could be freed when sourcing the script,
386 	// making "p" invalid, so copy it.
387 	char_u *copy_p = vim_strsave(p);
388 	int    r;
389 
390 	if (copy_p != NULL)
391 	{
392 	    r = load_colors(copy_p);
393 	    vim_free(copy_p);
394 	    if (r == OK)
395 		return;
396 	}
397     }
398 
399 #endif
400 
401     /*
402      * Didn't use a color file, use the compiled-in colors.
403      */
404     if (both)
405     {
406 	had_both = TRUE;
407 	pp = highlight_init_both;
408 	for (i = 0; pp[i] != NULL; ++i)
409 	    do_highlight((char_u *)pp[i], reset, TRUE);
410     }
411     else if (!had_both)
412 	// Don't do anything before the call with both == TRUE from main().
413 	// Not everything has been setup then, and that call will overrule
414 	// everything anyway.
415 	return;
416 
417     if (*p_bg == 'l')
418 	pp = highlight_init_light;
419     else
420 	pp = highlight_init_dark;
421     for (i = 0; pp[i] != NULL; ++i)
422 	do_highlight((char_u *)pp[i], reset, TRUE);
423 
424     // Reverse looks ugly, but grey may not work for 8 colors.  Thus let it
425     // depend on the number of colors available.
426     // With 8 colors brown is equal to yellow, need to use black for Search fg
427     // to avoid Statement highlighted text disappears.
428     // Clear the attributes, needed when changing the t_Co value.
429     if (t_colors > 8)
430 	do_highlight((char_u *)(*p_bg == 'l'
431 		    ? "Visual cterm=NONE ctermbg=LightGrey"
432 		    : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
433     else
434     {
435 	do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
436 								 FALSE, TRUE);
437 	if (*p_bg == 'l')
438 	    do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
439     }
440 
441 #ifdef FEAT_SYN_HL
442     /*
443      * If syntax highlighting is enabled load the highlighting for it.
444      */
445     if (get_var_value((char_u *)"g:syntax_on") != NULL)
446     {
447 	static int	recursive = 0;
448 
449 	if (recursive >= 5)
450 	    emsg(_("E679: recursive loop loading syncolor.vim"));
451 	else
452 	{
453 	    ++recursive;
454 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
455 	    --recursive;
456 	}
457     }
458 #endif
459 }
460 
461 /*
462  * Load color file "name".
463  * Return OK for success, FAIL for failure.
464  */
465     int
466 load_colors(char_u *name)
467 {
468     char_u	*buf;
469     int		retval = FAIL;
470     static int	recursive = FALSE;
471 
472     // When being called recursively, this is probably because setting
473     // 'background' caused the highlighting to be reloaded.  This means it is
474     // working, thus we should return OK.
475     if (recursive)
476 	return OK;
477 
478     recursive = TRUE;
479     buf = alloc(STRLEN(name) + 12);
480     if (buf != NULL)
481     {
482 	apply_autocmds(EVENT_COLORSCHEMEPRE, name,
483 					       curbuf->b_fname, FALSE, curbuf);
484 	sprintf((char *)buf, "colors/%s.vim", name);
485 	retval = source_runtime(buf, DIP_START + DIP_OPT);
486 	vim_free(buf);
487 	apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
488     }
489     recursive = FALSE;
490 
491     return retval;
492 }
493 
494 static char *(color_names[28]) = {
495 	    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
496 	    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
497 	    "Gray", "Grey", "LightGray", "LightGrey",
498 	    "DarkGray", "DarkGrey",
499 	    "Blue", "LightBlue", "Green", "LightGreen",
500 	    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
501 	    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
502 	    // indices:
503 	    // 0, 1, 2, 3,
504 	    // 4, 5, 6, 7,
505 	    // 8, 9, 10, 11,
506 	    // 12, 13,
507 	    // 14, 15, 16, 17,
508 	    // 18, 19, 20, 21, 22,
509 	    // 23, 24, 25, 26, 27
510 static int color_numbers_16[28] = {0, 1, 2, 3,
511 				 4, 5, 6, 6,
512 				 7, 7, 7, 7,
513 				 8, 8,
514 				 9, 9, 10, 10,
515 				 11, 11, 12, 12, 13,
516 				 13, 14, 14, 15, -1};
517 // for xterm with 88 colors...
518 static int color_numbers_88[28] = {0, 4, 2, 6,
519 				 1, 5, 32, 72,
520 				 84, 84, 7, 7,
521 				 82, 82,
522 				 12, 43, 10, 61,
523 				 14, 63, 9, 74, 13,
524 				 75, 11, 78, 15, -1};
525 // for xterm with 256 colors...
526 static int color_numbers_256[28] = {0, 4, 2, 6,
527 				 1, 5, 130, 130,
528 				 248, 248, 7, 7,
529 				 242, 242,
530 				 12, 81, 10, 121,
531 				 14, 159, 9, 224, 13,
532 				 225, 11, 229, 15, -1};
533 // for terminals with less than 16 colors...
534 static int color_numbers_8[28] = {0, 4, 2, 6,
535 				 1, 5, 3, 3,
536 				 7, 7, 7, 7,
537 				 0+8, 0+8,
538 				 4+8, 4+8, 2+8, 2+8,
539 				 6+8, 6+8, 1+8, 1+8, 5+8,
540 				 5+8, 3+8, 3+8, 7+8, -1};
541 
542 /*
543  * Lookup the "cterm" value to be used for color with index "idx" in
544  * color_names[].
545  * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
546  * colors, otherwise it will be unchanged.
547  */
548     int
549 lookup_color(int idx, int foreground, int *boldp)
550 {
551     int		color = color_numbers_16[idx];
552     char_u	*p;
553 
554     // Use the _16 table to check if it's a valid color name.
555     if (color < 0)
556 	return -1;
557 
558     if (t_colors == 8)
559     {
560 	// t_Co is 8: use the 8 colors table
561 #if defined(__QNXNTO__)
562 	// On qnx, the 8 & 16 color arrays are the same
563 	if (STRNCMP(T_NAME, "qansi", 5) == 0)
564 	    color = color_numbers_16[idx];
565 	else
566 #endif
567 	    color = color_numbers_8[idx];
568 	if (foreground)
569 	{
570 	    // set/reset bold attribute to get light foreground
571 	    // colors (on some terminals, e.g. "linux")
572 	    if (color & 8)
573 		*boldp = TRUE;
574 	    else
575 		*boldp = FALSE;
576 	}
577 	color &= 7;	// truncate to 8 colors
578     }
579     else if (t_colors == 16 || t_colors == 88
580 					   || t_colors >= 256)
581     {
582 	/*
583 	 * Guess: if the termcap entry ends in 'm', it is
584 	 * probably an xterm-like terminal.  Use the changed
585 	 * order for colors.
586 	 */
587 	if (*T_CAF != NUL)
588 	    p = T_CAF;
589 	else
590 	    p = T_CSF;
591 	if (*p != NUL && (t_colors > 256
592 			      || *(p + STRLEN(p) - 1) == 'm'))
593 	{
594 	    if (t_colors == 88)
595 		color = color_numbers_88[idx];
596 	    else if (t_colors >= 256)
597 		color = color_numbers_256[idx];
598 	    else
599 		color = color_numbers_8[idx];
600 	}
601 #ifdef FEAT_TERMRESPONSE
602 	if (t_colors >= 256 && color == 15 && is_mac_terminal)
603 	    // Terminal.app has a bug: 15 is light grey. Use white
604 	    // from the color cube instead.
605 	    color = 231;
606 #endif
607     }
608     return color;
609 }
610 
611 /*
612  * Handle the ":highlight .." command.
613  * When using ":hi clear" this is called recursively for each group with
614  * "forceit" and "init" both TRUE.
615  */
616     void
617 do_highlight(
618     char_u	*line,
619     int		forceit,
620     int		init)	    // TRUE when called for initializing
621 {
622     char_u	*name_end;
623     char_u	*p;
624     char_u	*linep;
625     char_u	*key_start;
626     char_u	*arg_start;
627     char_u	*key = NULL, *arg = NULL;
628     long	i;
629     int		off;
630     int		len;
631     int		attr;
632     int		id;
633     int		idx;
634     hl_group_T	item_before;
635     int		did_change = FALSE;
636     int		dodefault = FALSE;
637     int		doclear = FALSE;
638     int		dolink = FALSE;
639     int		error = FALSE;
640     int		color;
641     int		is_normal_group = FALSE;	// "Normal" group
642 #ifdef FEAT_TERMINAL
643     int		is_terminal_group = FALSE;	// "Terminal" group
644 #endif
645 #ifdef FEAT_GUI_X11
646     int		is_menu_group = FALSE;		// "Menu" group
647     int		is_scrollbar_group = FALSE;	// "Scrollbar" group
648     int		is_tooltip_group = FALSE;	// "Tooltip" group
649     int		do_colors = FALSE;		// need to update colors?
650 #else
651 # define is_menu_group 0
652 # define is_tooltip_group 0
653 #endif
654 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
655     int		did_highlight_changed = FALSE;
656 #endif
657 
658     /*
659      * If no argument, list current highlighting.
660      */
661     if (ends_excmd(*line))
662     {
663 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
664 	    // TODO: only call when the group has attributes set
665 	    highlight_list_one((int)i);
666 	return;
667     }
668 
669     /*
670      * Isolate the name.
671      */
672     name_end = skiptowhite(line);
673     linep = skipwhite(name_end);
674 
675     /*
676      * Check for "default" argument.
677      */
678     if (STRNCMP(line, "default", name_end - line) == 0)
679     {
680 	dodefault = TRUE;
681 	line = linep;
682 	name_end = skiptowhite(line);
683 	linep = skipwhite(name_end);
684     }
685 
686     /*
687      * Check for "clear" or "link" argument.
688      */
689     if (STRNCMP(line, "clear", name_end - line) == 0)
690 	doclear = TRUE;
691     if (STRNCMP(line, "link", name_end - line) == 0)
692 	dolink = TRUE;
693 
694     /*
695      * ":highlight {group-name}": list highlighting for one group.
696      */
697     if (!doclear && !dolink && ends_excmd(*linep))
698     {
699 	id = syn_namen2id(line, (int)(name_end - line));
700 	if (id == 0)
701 	    semsg(_("E411: highlight group not found: %s"), line);
702 	else
703 	    highlight_list_one(id);
704 	return;
705     }
706 
707     /*
708      * Handle ":highlight link {from} {to}" command.
709      */
710     if (dolink)
711     {
712 	char_u	    *from_start = linep;
713 	char_u	    *from_end;
714 	char_u	    *to_start;
715 	char_u	    *to_end;
716 	int	    from_id;
717 	int	    to_id;
718 
719 	from_end = skiptowhite(from_start);
720 	to_start = skipwhite(from_end);
721 	to_end	 = skiptowhite(to_start);
722 
723 	if (ends_excmd(*from_start) || ends_excmd(*to_start))
724 	{
725 	    semsg(_("E412: Not enough arguments: \":highlight link %s\""),
726 								  from_start);
727 	    return;
728 	}
729 
730 	if (!ends_excmd(*skipwhite(to_end)))
731 	{
732 	    semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
733 	    return;
734 	}
735 
736 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
737 	if (STRNCMP(to_start, "NONE", 4) == 0)
738 	    to_id = 0;
739 	else
740 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
741 
742 	if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
743 	{
744 	    /*
745 	     * Don't allow a link when there already is some highlighting
746 	     * for the group, unless '!' is used
747 	     */
748 	    if (to_id > 0 && !forceit && !init
749 				   && hl_has_settings(from_id - 1, dodefault))
750 	    {
751 		if (SOURCING_NAME == NULL && !dodefault)
752 		    emsg(_("E414: group has settings, highlight link ignored"));
753 	    }
754 	    else if (HL_TABLE()[from_id - 1].sg_link != to_id
755 #ifdef FEAT_EVAL
756 		    || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
757 							 != current_sctx.sc_sid
758 #endif
759 		    || HL_TABLE()[from_id - 1].sg_cleared)
760 	    {
761 		if (!init)
762 		    HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
763 		HL_TABLE()[from_id - 1].sg_link = to_id;
764 #ifdef FEAT_EVAL
765 		HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
766 		HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += SOURCING_LNUM;
767 #endif
768 		HL_TABLE()[from_id - 1].sg_cleared = FALSE;
769 		redraw_all_later(SOME_VALID);
770 
771 		// Only call highlight_changed() once after multiple changes.
772 		need_highlight_changed = TRUE;
773 	    }
774 	}
775 
776 	return;
777     }
778 
779     if (doclear)
780     {
781 	/*
782 	 * ":highlight clear [group]" command.
783 	 */
784 	line = linep;
785 	if (ends_excmd(*line))
786 	{
787 #ifdef FEAT_GUI
788 	    // First, we do not destroy the old values, but allocate the new
789 	    // ones and update the display. THEN we destroy the old values.
790 	    // If we destroy the old values first, then the old values
791 	    // (such as GuiFont's or GuiFontset's) will still be displayed but
792 	    // invalid because they were free'd.
793 	    if (gui.in_use)
794 	    {
795 # ifdef FEAT_BEVAL_TIP
796 		gui_init_tooltip_font();
797 # endif
798 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
799 		gui_init_menu_font();
800 # endif
801 	    }
802 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
803 	    gui_mch_def_colors();
804 # endif
805 # ifdef FEAT_GUI_X11
806 #  ifdef FEAT_MENU
807 
808 	    // This only needs to be done when there is no Menu highlight
809 	    // group defined by default, which IS currently the case.
810 	    gui_mch_new_menu_colors();
811 #  endif
812 	    if (gui.in_use)
813 	    {
814 		gui_new_scrollbar_colors();
815 #  ifdef FEAT_BEVAL_GUI
816 		gui_mch_new_tooltip_colors();
817 #  endif
818 #  ifdef FEAT_MENU
819 		gui_mch_new_menu_font();
820 #  endif
821 	    }
822 # endif
823 
824 	    // Ok, we're done allocating the new default graphics items.
825 	    // The screen should already be refreshed at this point.
826 	    // It is now Ok to clear out the old data.
827 #endif
828 #ifdef FEAT_EVAL
829 	    do_unlet((char_u *)"colors_name", TRUE);
830 #endif
831 	    restore_cterm_colors();
832 
833 	    /*
834 	     * Clear all default highlight groups and load the defaults.
835 	     */
836 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
837 		highlight_clear(idx);
838 	    init_highlight(TRUE, TRUE);
839 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
840 	    if (USE_24BIT)
841 		highlight_gui_started();
842 	    else
843 #endif
844 		highlight_changed();
845 	    redraw_later_clear();
846 	    return;
847 	}
848 	name_end = skiptowhite(line);
849 	linep = skipwhite(name_end);
850     }
851 
852     /*
853      * Find the group name in the table.  If it does not exist yet, add it.
854      */
855     id = syn_check_group(line, (int)(name_end - line));
856     if (id == 0)			// failed (out of memory)
857 	return;
858     idx = id - 1;			// index is ID minus one
859 
860     // Return if "default" was used and the group already has settings.
861     if (dodefault && hl_has_settings(idx, TRUE))
862 	return;
863 
864     // Make a copy so we can check if any attribute actually changed.
865     item_before = HL_TABLE()[idx];
866 
867     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
868 	is_normal_group = TRUE;
869 #ifdef FEAT_TERMINAL
870     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
871 	is_terminal_group = TRUE;
872 #endif
873 #ifdef FEAT_GUI_X11
874     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
875 	is_menu_group = TRUE;
876     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
877 	is_scrollbar_group = TRUE;
878     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
879 	is_tooltip_group = TRUE;
880 #endif
881 
882     // Clear the highlighting for ":hi clear {group}" and ":hi clear".
883     if (doclear || (forceit && init))
884     {
885 	highlight_clear(idx);
886 	if (!doclear)
887 	    HL_TABLE()[idx].sg_set = 0;
888     }
889 
890     if (!doclear)
891       while (!ends_excmd(*linep))
892       {
893 	key_start = linep;
894 	if (*linep == '=')
895 	{
896 	    semsg(_("E415: unexpected equal sign: %s"), key_start);
897 	    error = TRUE;
898 	    break;
899 	}
900 
901 	/*
902 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
903 	 * "guibg").
904 	 */
905 	while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
906 	    ++linep;
907 	vim_free(key);
908 	key = vim_strnsave_up(key_start, (int)(linep - key_start));
909 	if (key == NULL)
910 	{
911 	    error = TRUE;
912 	    break;
913 	}
914 	linep = skipwhite(linep);
915 
916 	if (STRCMP(key, "NONE") == 0)
917 	{
918 	    if (!init || HL_TABLE()[idx].sg_set == 0)
919 	    {
920 		if (!init)
921 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
922 		highlight_clear(idx);
923 	    }
924 	    continue;
925 	}
926 
927 	/*
928 	 * Check for the equal sign.
929 	 */
930 	if (*linep != '=')
931 	{
932 	    semsg(_("E416: missing equal sign: %s"), key_start);
933 	    error = TRUE;
934 	    break;
935 	}
936 	++linep;
937 
938 	/*
939 	 * Isolate the argument.
940 	 */
941 	linep = skipwhite(linep);
942 	if (*linep == '\'')		// guifg='color name'
943 	{
944 	    arg_start = ++linep;
945 	    linep = vim_strchr(linep, '\'');
946 	    if (linep == NULL)
947 	    {
948 		semsg(_(e_invarg2), key_start);
949 		error = TRUE;
950 		break;
951 	    }
952 	}
953 	else
954 	{
955 	    arg_start = linep;
956 	    linep = skiptowhite(linep);
957 	}
958 	if (linep == arg_start)
959 	{
960 	    semsg(_("E417: missing argument: %s"), key_start);
961 	    error = TRUE;
962 	    break;
963 	}
964 	vim_free(arg);
965 	arg = vim_strnsave(arg_start, (int)(linep - arg_start));
966 	if (arg == NULL)
967 	{
968 	    error = TRUE;
969 	    break;
970 	}
971 	if (*linep == '\'')
972 	    ++linep;
973 
974 	/*
975 	 * Store the argument.
976 	 */
977 	if (  STRCMP(key, "TERM") == 0
978 		|| STRCMP(key, "CTERM") == 0
979 		|| STRCMP(key, "GUI") == 0)
980 	{
981 	    attr = 0;
982 	    off = 0;
983 	    while (arg[off] != NUL)
984 	    {
985 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
986 		{
987 		    len = (int)STRLEN(hl_name_table[i]);
988 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
989 		    {
990 			attr |= hl_attr_table[i];
991 			off += len;
992 			break;
993 		    }
994 		}
995 		if (i < 0)
996 		{
997 		    semsg(_("E418: Illegal value: %s"), arg);
998 		    error = TRUE;
999 		    break;
1000 		}
1001 		if (arg[off] == ',')		// another one follows
1002 		    ++off;
1003 	    }
1004 	    if (error)
1005 		break;
1006 	    if (*key == 'T')
1007 	    {
1008 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
1009 		{
1010 		    if (!init)
1011 			HL_TABLE()[idx].sg_set |= SG_TERM;
1012 		    HL_TABLE()[idx].sg_term = attr;
1013 		}
1014 	    }
1015 	    else if (*key == 'C')
1016 	    {
1017 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1018 		{
1019 		    if (!init)
1020 			HL_TABLE()[idx].sg_set |= SG_CTERM;
1021 		    HL_TABLE()[idx].sg_cterm = attr;
1022 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
1023 		}
1024 	    }
1025 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1026 	    else
1027 	    {
1028 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1029 		{
1030 		    if (!init)
1031 			HL_TABLE()[idx].sg_set |= SG_GUI;
1032 		    HL_TABLE()[idx].sg_gui = attr;
1033 		}
1034 	    }
1035 #endif
1036 	}
1037 	else if (STRCMP(key, "FONT") == 0)
1038 	{
1039 	    // in non-GUI fonts are simply ignored
1040 #ifdef FEAT_GUI
1041 	    if (HL_TABLE()[idx].sg_font_name != NULL
1042 			     && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
1043 	    {
1044 		// Font name didn't change, ignore.
1045 	    }
1046 	    else if (!gui.shell_created)
1047 	    {
1048 		// GUI not started yet, always accept the name.
1049 		vim_free(HL_TABLE()[idx].sg_font_name);
1050 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1051 		did_change = TRUE;
1052 	    }
1053 	    else
1054 	    {
1055 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
1056 # ifdef FEAT_XFONTSET
1057 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
1058 # endif
1059 		// First, save the current font/fontset.
1060 		// Then try to allocate the font/fontset.
1061 		// If the allocation fails, HL_TABLE()[idx].sg_font OR
1062 		// sg_fontset will be set to NOFONT or NOFONTSET respectively.
1063 
1064 		HL_TABLE()[idx].sg_font = NOFONT;
1065 # ifdef FEAT_XFONTSET
1066 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
1067 # endif
1068 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
1069 						     is_tooltip_group, FALSE);
1070 
1071 # ifdef FEAT_XFONTSET
1072 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1073 		{
1074 		    // New fontset was accepted. Free the old one, if there
1075 		    // was one.
1076 		    gui_mch_free_fontset(temp_sg_fontset);
1077 		    vim_free(HL_TABLE()[idx].sg_font_name);
1078 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1079 		    did_change = TRUE;
1080 		}
1081 		else
1082 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
1083 # endif
1084 		if (HL_TABLE()[idx].sg_font != NOFONT)
1085 		{
1086 		    // New font was accepted. Free the old one, if there was
1087 		    // one.
1088 		    gui_mch_free_font(temp_sg_font);
1089 		    vim_free(HL_TABLE()[idx].sg_font_name);
1090 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1091 		    did_change = TRUE;
1092 		}
1093 		else
1094 		    HL_TABLE()[idx].sg_font = temp_sg_font;
1095 	    }
1096 #endif
1097 	}
1098 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
1099 	{
1100 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1101 	  {
1102 	    if (!init)
1103 		HL_TABLE()[idx].sg_set |= SG_CTERM;
1104 
1105 	    // When setting the foreground color, and previously the "bold"
1106 	    // flag was set for a light color, reset it now
1107 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1108 	    {
1109 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1110 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
1111 	    }
1112 
1113 	    if (VIM_ISDIGIT(*arg))
1114 		color = atoi((char *)arg);
1115 	    else if (STRICMP(arg, "fg") == 0)
1116 	    {
1117 		if (cterm_normal_fg_color)
1118 		    color = cterm_normal_fg_color - 1;
1119 		else
1120 		{
1121 		    emsg(_("E419: FG color unknown"));
1122 		    error = TRUE;
1123 		    break;
1124 		}
1125 	    }
1126 	    else if (STRICMP(arg, "bg") == 0)
1127 	    {
1128 		if (cterm_normal_bg_color > 0)
1129 		    color = cterm_normal_bg_color - 1;
1130 		else
1131 		{
1132 		    emsg(_("E420: BG color unknown"));
1133 		    error = TRUE;
1134 		    break;
1135 		}
1136 	    }
1137 	    else
1138 	    {
1139 		int bold = MAYBE;
1140 
1141 		// reduce calls to STRICMP a bit, it can be slow
1142 		off = TOUPPER_ASC(*arg);
1143 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
1144 		    if (off == color_names[i][0]
1145 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1146 			break;
1147 		if (i < 0)
1148 		{
1149 		    semsg(_("E421: Color name or number not recognized: %s"), key_start);
1150 		    error = TRUE;
1151 		    break;
1152 		}
1153 
1154 		color = lookup_color(i, key[5] == 'F', &bold);
1155 
1156 		// set/reset bold attribute to get light foreground
1157 		// colors (on some terminals, e.g. "linux")
1158 		if (bold == TRUE)
1159 		{
1160 		    HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1161 		    HL_TABLE()[idx].sg_cterm_bold = TRUE;
1162 		}
1163 		else if (bold == FALSE)
1164 		    HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1165 	    }
1166 
1167 	    // Add one to the argument, to avoid zero.  Zero is used for
1168 	    // "NONE", then "color" is -1.
1169 	    if (key[5] == 'F')
1170 	    {
1171 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
1172 		if (is_normal_group)
1173 		{
1174 		    cterm_normal_fg_color = color + 1;
1175 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
1176 #ifdef FEAT_GUI
1177 		    // Don't do this if the GUI is used.
1178 		    if (!gui.in_use && !gui.starting)
1179 #endif
1180 		    {
1181 			must_redraw = CLEAR;
1182 			if (termcap_active && color >= 0)
1183 			    term_fg_color(color);
1184 		    }
1185 		}
1186 	    }
1187 	    else
1188 	    {
1189 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
1190 		if (is_normal_group)
1191 		{
1192 		    cterm_normal_bg_color = color + 1;
1193 #ifdef FEAT_GUI
1194 		    // Don't mess with 'background' if the GUI is used.
1195 		    if (!gui.in_use && !gui.starting)
1196 #endif
1197 		    {
1198 			must_redraw = CLEAR;
1199 			if (color >= 0)
1200 			{
1201 			    int dark = -1;
1202 
1203 			    if (termcap_active)
1204 				term_bg_color(color);
1205 			    if (t_colors < 16)
1206 				dark = (color == 0 || color == 4);
1207 			    // Limit the heuristic to the standard 16 colors
1208 			    else if (color < 16)
1209 				dark = (color < 7 || color == 8);
1210 			    // Set the 'background' option if the value is
1211 			    // wrong.
1212 			    if (dark != -1
1213 				    && dark != (*p_bg == 'd')
1214 				    && !option_was_set((char_u *)"bg"))
1215 			    {
1216 				set_option_value((char_u *)"bg", 0L,
1217 				       (char_u *)(dark ? "dark" : "light"), 0);
1218 				reset_option_was_set((char_u *)"bg");
1219 			    }
1220 			}
1221 		    }
1222 		}
1223 	    }
1224 	  }
1225 	}
1226 	else if (STRCMP(key, "GUIFG") == 0)
1227 	{
1228 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1229 	    char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
1230 
1231 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1232 	    {
1233 		if (!init)
1234 		    HL_TABLE()[idx].sg_set |= SG_GUI;
1235 
1236 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1237 		// In GUI guifg colors are only used when recognized
1238 		i = color_name2handle(arg);
1239 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1240 		{
1241 		    HL_TABLE()[idx].sg_gui_fg = i;
1242 # endif
1243 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
1244 		    {
1245 			vim_free(*namep);
1246 			if (STRCMP(arg, "NONE") != 0)
1247 			    *namep = vim_strsave(arg);
1248 			else
1249 			    *namep = NULL;
1250 			did_change = TRUE;
1251 		    }
1252 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1253 #  ifdef FEAT_GUI_X11
1254 		    if (is_menu_group && gui.menu_fg_pixel != i)
1255 		    {
1256 			gui.menu_fg_pixel = i;
1257 			do_colors = TRUE;
1258 		    }
1259 		    if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1260 		    {
1261 			gui.scroll_fg_pixel = i;
1262 			do_colors = TRUE;
1263 		    }
1264 #   ifdef FEAT_BEVAL_GUI
1265 		    if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1266 		    {
1267 			gui.tooltip_fg_pixel = i;
1268 			do_colors = TRUE;
1269 		    }
1270 #   endif
1271 #  endif
1272 		}
1273 # endif
1274 	    }
1275 #endif
1276 	}
1277 	else if (STRCMP(key, "GUIBG") == 0)
1278 	{
1279 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1280 	    char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
1281 
1282 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1283 	    {
1284 		if (!init)
1285 		    HL_TABLE()[idx].sg_set |= SG_GUI;
1286 
1287 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1288 		// In GUI guifg colors are only used when recognized
1289 		i = color_name2handle(arg);
1290 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1291 		{
1292 		    HL_TABLE()[idx].sg_gui_bg = i;
1293 # endif
1294 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
1295 		    {
1296 			vim_free(*namep);
1297 			if (STRCMP(arg, "NONE") != 0)
1298 			    *namep = vim_strsave(arg);
1299 			else
1300 			    *namep = NULL;
1301 			did_change = TRUE;
1302 		    }
1303 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1304 #  ifdef FEAT_GUI_X11
1305 		    if (is_menu_group && gui.menu_bg_pixel != i)
1306 		    {
1307 			gui.menu_bg_pixel = i;
1308 			do_colors = TRUE;
1309 		    }
1310 		    if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1311 		    {
1312 			gui.scroll_bg_pixel = i;
1313 			do_colors = TRUE;
1314 		    }
1315 #   ifdef FEAT_BEVAL_GUI
1316 		    if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1317 		    {
1318 			gui.tooltip_bg_pixel = i;
1319 			do_colors = TRUE;
1320 		    }
1321 #   endif
1322 #  endif
1323 		}
1324 # endif
1325 	    }
1326 #endif
1327 	}
1328 	else if (STRCMP(key, "GUISP") == 0)
1329 	{
1330 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1331 	    char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
1332 
1333 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1334 	    {
1335 		if (!init)
1336 		    HL_TABLE()[idx].sg_set |= SG_GUI;
1337 
1338 # ifdef FEAT_GUI
1339 		i = color_name2handle(arg);
1340 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
1341 		{
1342 		    HL_TABLE()[idx].sg_gui_sp = i;
1343 # endif
1344 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
1345 		    {
1346 			vim_free(*namep);
1347 			if (STRCMP(arg, "NONE") != 0)
1348 			    *namep = vim_strsave(arg);
1349 			else
1350 			    *namep = NULL;
1351 			did_change = TRUE;
1352 		    }
1353 # ifdef FEAT_GUI
1354 		}
1355 # endif
1356 	    }
1357 #endif
1358 	}
1359 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1360 	{
1361 	    char_u	buf[100];
1362 	    char_u	*tname;
1363 
1364 	    if (!init)
1365 		HL_TABLE()[idx].sg_set |= SG_TERM;
1366 
1367 	    /*
1368 	     * The "start" and "stop"  arguments can be a literal escape
1369 	     * sequence, or a comma separated list of terminal codes.
1370 	     */
1371 	    if (STRNCMP(arg, "t_", 2) == 0)
1372 	    {
1373 		off = 0;
1374 		buf[0] = 0;
1375 		while (arg[off] != NUL)
1376 		{
1377 		    // Isolate one termcap name
1378 		    for (len = 0; arg[off + len] &&
1379 						 arg[off + len] != ','; ++len)
1380 			;
1381 		    tname = vim_strnsave(arg + off, len);
1382 		    if (tname == NULL)		// out of memory
1383 		    {
1384 			error = TRUE;
1385 			break;
1386 		    }
1387 		    // lookup the escape sequence for the item
1388 		    p = get_term_code(tname);
1389 		    vim_free(tname);
1390 		    if (p == NULL)	    // ignore non-existing things
1391 			p = (char_u *)"";
1392 
1393 		    // Append it to the already found stuff
1394 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1395 		    {
1396 			semsg(_("E422: terminal code too long: %s"), arg);
1397 			error = TRUE;
1398 			break;
1399 		    }
1400 		    STRCAT(buf, p);
1401 
1402 		    // Advance to the next item
1403 		    off += len;
1404 		    if (arg[off] == ',')	    // another one follows
1405 			++off;
1406 		}
1407 	    }
1408 	    else
1409 	    {
1410 		/*
1411 		 * Copy characters from arg[] to buf[], translating <> codes.
1412 		 */
1413 		for (p = arg, off = 0; off < 100 - 6 && *p; )
1414 		{
1415 		    len = trans_special(&p, buf + off, FALSE, FALSE,
1416 								   TRUE, NULL);
1417 		    if (len > 0)	    // recognized special char
1418 			off += len;
1419 		    else		    // copy as normal char
1420 			buf[off++] = *p++;
1421 		}
1422 		buf[off] = NUL;
1423 	    }
1424 	    if (error)
1425 		break;
1426 
1427 	    if (STRCMP(buf, "NONE") == 0)	// resetting the value
1428 		p = NULL;
1429 	    else
1430 		p = vim_strsave(buf);
1431 	    if (key[2] == 'A')
1432 	    {
1433 		vim_free(HL_TABLE()[idx].sg_start);
1434 		HL_TABLE()[idx].sg_start = p;
1435 	    }
1436 	    else
1437 	    {
1438 		vim_free(HL_TABLE()[idx].sg_stop);
1439 		HL_TABLE()[idx].sg_stop = p;
1440 	    }
1441 	}
1442 	else
1443 	{
1444 	    semsg(_("E423: Illegal argument: %s"), key_start);
1445 	    error = TRUE;
1446 	    break;
1447 	}
1448 	HL_TABLE()[idx].sg_cleared = FALSE;
1449 
1450 	/*
1451 	 * When highlighting has been given for a group, don't link it.
1452 	 */
1453 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1454 	    HL_TABLE()[idx].sg_link = 0;
1455 
1456 	/*
1457 	 * Continue with next argument.
1458 	 */
1459 	linep = skipwhite(linep);
1460       }
1461 
1462     /*
1463      * If there is an error, and it's a new entry, remove it from the table.
1464      */
1465     if (error && idx == highlight_ga.ga_len)
1466 	syn_unadd_group();
1467     else
1468     {
1469 	if (is_normal_group)
1470 	{
1471 	    HL_TABLE()[idx].sg_term_attr = 0;
1472 	    HL_TABLE()[idx].sg_cterm_attr = 0;
1473 #ifdef FEAT_GUI
1474 	    HL_TABLE()[idx].sg_gui_attr = 0;
1475 	    /*
1476 	     * Need to update all groups, because they might be using "bg"
1477 	     * and/or "fg", which have been changed now.
1478 	     */
1479 #endif
1480 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1481 	    if (USE_24BIT)
1482 	    {
1483 		highlight_gui_started();
1484 		did_highlight_changed = TRUE;
1485 		redraw_all_later(NOT_VALID);
1486 	    }
1487 #endif
1488 	}
1489 #ifdef FEAT_TERMINAL
1490 	else if (is_terminal_group)
1491 	    set_terminal_default_colors(
1492 		    HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1493 #endif
1494 #ifdef FEAT_GUI_X11
1495 # ifdef FEAT_MENU
1496 	else if (is_menu_group)
1497 	{
1498 	    if (gui.in_use && do_colors)
1499 		gui_mch_new_menu_colors();
1500 	}
1501 # endif
1502 	else if (is_scrollbar_group)
1503 	{
1504 	    if (gui.in_use && do_colors)
1505 		gui_new_scrollbar_colors();
1506 	    else
1507 		set_hl_attr(idx);
1508 	}
1509 # ifdef FEAT_BEVAL_GUI
1510 	else if (is_tooltip_group)
1511 	{
1512 	    if (gui.in_use && do_colors)
1513 		gui_mch_new_tooltip_colors();
1514 	}
1515 # endif
1516 #endif
1517 	else
1518 	    set_hl_attr(idx);
1519 #ifdef FEAT_EVAL
1520 	HL_TABLE()[idx].sg_script_ctx = current_sctx;
1521 	HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
1522 #endif
1523     }
1524 
1525     vim_free(key);
1526     vim_free(arg);
1527 
1528     // Only call highlight_changed() once, after a sequence of highlight
1529     // commands, and only if an attribute actually changed.
1530     if ((did_change
1531 	   || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1532 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1533 	    && !did_highlight_changed
1534 #endif
1535        )
1536     {
1537 	// Do not trigger a redraw when highlighting is changed while
1538 	// redrawing.  This may happen when evaluating 'statusline' changes the
1539 	// StatusLine group.
1540 	if (!updating_screen)
1541 	    redraw_all_later(NOT_VALID);
1542 	need_highlight_changed = TRUE;
1543     }
1544 }
1545 
1546 #if defined(EXITFREE) || defined(PROTO)
1547     void
1548 free_highlight(void)
1549 {
1550     int	    i;
1551 
1552     for (i = 0; i < highlight_ga.ga_len; ++i)
1553     {
1554 	highlight_clear(i);
1555 	vim_free(HL_TABLE()[i].sg_name);
1556 	vim_free(HL_TABLE()[i].sg_name_u);
1557     }
1558     ga_clear(&highlight_ga);
1559 }
1560 #endif
1561 
1562 /*
1563  * Reset the cterm colors to what they were before Vim was started, if
1564  * possible.  Otherwise reset them to zero.
1565  */
1566     void
1567 restore_cterm_colors(void)
1568 {
1569 #if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1570     // Since t_me has been set, this probably means that the user
1571     // wants to use this as default colors.  Need to reset default
1572     // background/foreground colors.
1573     mch_set_normal_colors();
1574 #else
1575 # ifdef VIMDLL
1576     if (!gui.in_use)
1577     {
1578 	mch_set_normal_colors();
1579 	return;
1580     }
1581 # endif
1582     cterm_normal_fg_color = 0;
1583     cterm_normal_fg_bold = 0;
1584     cterm_normal_bg_color = 0;
1585 # ifdef FEAT_TERMGUICOLORS
1586     cterm_normal_fg_gui_color = INVALCOLOR;
1587     cterm_normal_bg_gui_color = INVALCOLOR;
1588 # endif
1589 #endif
1590 }
1591 
1592 /*
1593  * Return TRUE if highlight group "idx" has any settings.
1594  * When "check_link" is TRUE also check for an existing link.
1595  */
1596     static int
1597 hl_has_settings(int idx, int check_link)
1598 {
1599     return (   HL_TABLE()[idx].sg_term_attr != 0
1600 	    || HL_TABLE()[idx].sg_cterm_attr != 0
1601 	    || HL_TABLE()[idx].sg_cterm_fg != 0
1602 	    || HL_TABLE()[idx].sg_cterm_bg != 0
1603 #ifdef FEAT_GUI
1604 	    || HL_TABLE()[idx].sg_gui_attr != 0
1605 	    || HL_TABLE()[idx].sg_gui_fg_name != NULL
1606 	    || HL_TABLE()[idx].sg_gui_bg_name != NULL
1607 	    || HL_TABLE()[idx].sg_gui_sp_name != NULL
1608 	    || HL_TABLE()[idx].sg_font_name != NULL
1609 #endif
1610 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1611 }
1612 
1613 /*
1614  * Clear highlighting for one group.
1615  */
1616     static void
1617 highlight_clear(int idx)
1618 {
1619     HL_TABLE()[idx].sg_cleared = TRUE;
1620 
1621     HL_TABLE()[idx].sg_term = 0;
1622     VIM_CLEAR(HL_TABLE()[idx].sg_start);
1623     VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1624     HL_TABLE()[idx].sg_term_attr = 0;
1625     HL_TABLE()[idx].sg_cterm = 0;
1626     HL_TABLE()[idx].sg_cterm_bold = FALSE;
1627     HL_TABLE()[idx].sg_cterm_fg = 0;
1628     HL_TABLE()[idx].sg_cterm_bg = 0;
1629     HL_TABLE()[idx].sg_cterm_attr = 0;
1630 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1631     HL_TABLE()[idx].sg_gui = 0;
1632     VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1633     VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1634     VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1635 #endif
1636 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1637     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1638     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
1639 #endif
1640 #ifdef FEAT_GUI
1641     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
1642     gui_mch_free_font(HL_TABLE()[idx].sg_font);
1643     HL_TABLE()[idx].sg_font = NOFONT;
1644 # ifdef FEAT_XFONTSET
1645     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1646     HL_TABLE()[idx].sg_fontset = NOFONTSET;
1647 # endif
1648     VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1649     HL_TABLE()[idx].sg_gui_attr = 0;
1650 #endif
1651 #ifdef FEAT_EVAL
1652     // Clear the script ID only when there is no link, since that is not
1653     // cleared.
1654     if (HL_TABLE()[idx].sg_link == 0)
1655     {
1656 	HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
1657 	HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
1658     }
1659 #endif
1660 }
1661 
1662 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1663 /*
1664  * Set the normal foreground and background colors according to the "Normal"
1665  * highlighting group.  For X11 also set "Menu", "Scrollbar", and
1666  * "Tooltip" colors.
1667  */
1668     void
1669 set_normal_colors(void)
1670 {
1671 # ifdef FEAT_GUI
1672 #  ifdef FEAT_TERMGUICOLORS
1673     if (gui.in_use)
1674 #  endif
1675     {
1676 	if (set_group_colors((char_u *)"Normal",
1677 				 &gui.norm_pixel, &gui.back_pixel,
1678 				 FALSE, TRUE, FALSE))
1679 	{
1680 	    gui_mch_new_colors();
1681 	    must_redraw = CLEAR;
1682 	}
1683 #  ifdef FEAT_GUI_X11
1684 	if (set_group_colors((char_u *)"Menu",
1685 			     &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1686 			     TRUE, FALSE, FALSE))
1687 	{
1688 #   ifdef FEAT_MENU
1689 	    gui_mch_new_menu_colors();
1690 #   endif
1691 	    must_redraw = CLEAR;
1692 	}
1693 #   ifdef FEAT_BEVAL_GUI
1694 	if (set_group_colors((char_u *)"Tooltip",
1695 			     &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1696 			     FALSE, FALSE, TRUE))
1697 	{
1698 #    ifdef FEAT_TOOLBAR
1699 	    gui_mch_new_tooltip_colors();
1700 #    endif
1701 	    must_redraw = CLEAR;
1702 	}
1703 #   endif
1704 	if (set_group_colors((char_u *)"Scrollbar",
1705 			&gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1706 			FALSE, FALSE, FALSE))
1707 	{
1708 	    gui_new_scrollbar_colors();
1709 	    must_redraw = CLEAR;
1710 	}
1711 #  endif
1712     }
1713 # endif
1714 # ifdef FEAT_TERMGUICOLORS
1715 #  ifdef FEAT_GUI
1716     else
1717 #  endif
1718     {
1719 	int		idx;
1720 
1721 	idx = syn_name2id((char_u *)"Normal") - 1;
1722 	if (idx >= 0)
1723 	{
1724 	    gui_do_one_color(idx, FALSE, FALSE);
1725 
1726 	    // If the normal fg or bg color changed a complete redraw is
1727 	    // required.
1728 	    if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1729 		    || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1730 	    {
1731 		// if the GUI color is INVALCOLOR then we use the default cterm
1732 		// color
1733 		cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1734 		cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1735 		must_redraw = CLEAR;
1736 	    }
1737 	}
1738     }
1739 # endif
1740 }
1741 #endif
1742 
1743 #if defined(FEAT_GUI) || defined(PROTO)
1744 /*
1745  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1746  */
1747     static int
1748 set_group_colors(
1749     char_u	*name,
1750     guicolor_T	*fgp,
1751     guicolor_T	*bgp,
1752     int		do_menu,
1753     int		use_norm,
1754     int		do_tooltip)
1755 {
1756     int		idx;
1757 
1758     idx = syn_name2id(name) - 1;
1759     if (idx >= 0)
1760     {
1761 	gui_do_one_color(idx, do_menu, do_tooltip);
1762 
1763 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1764 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
1765 	else if (use_norm)
1766 	    *fgp = gui.def_norm_pixel;
1767 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1768 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
1769 	else if (use_norm)
1770 	    *bgp = gui.def_back_pixel;
1771 	return TRUE;
1772     }
1773     return FALSE;
1774 }
1775 
1776 /*
1777  * Get the font of the "Normal" group.
1778  * Returns "" when it's not found or not set.
1779  */
1780     char_u *
1781 hl_get_font_name(void)
1782 {
1783     int		id;
1784     char_u	*s;
1785 
1786     id = syn_name2id((char_u *)"Normal");
1787     if (id > 0)
1788     {
1789 	s = HL_TABLE()[id - 1].sg_font_name;
1790 	if (s != NULL)
1791 	    return s;
1792     }
1793     return (char_u *)"";
1794 }
1795 
1796 /*
1797  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
1798  * actually chosen to be used.
1799  */
1800     void
1801 hl_set_font_name(char_u *font_name)
1802 {
1803     int	    id;
1804 
1805     id = syn_name2id((char_u *)"Normal");
1806     if (id > 0)
1807     {
1808 	vim_free(HL_TABLE()[id - 1].sg_font_name);
1809 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1810     }
1811 }
1812 
1813 /*
1814  * Set background color for "Normal" group.  Called by gui_set_bg_color()
1815  * when the color is known.
1816  */
1817     void
1818 hl_set_bg_color_name(
1819     char_u  *name)	    // must have been allocated
1820 {
1821     int	    id;
1822 
1823     if (name != NULL)
1824     {
1825 	id = syn_name2id((char_u *)"Normal");
1826 	if (id > 0)
1827 	{
1828 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1829 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
1830 	}
1831     }
1832 }
1833 
1834 /*
1835  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
1836  * when the color is known.
1837  */
1838     void
1839 hl_set_fg_color_name(
1840     char_u  *name)	    // must have been allocated
1841 {
1842     int	    id;
1843 
1844     if (name != NULL)
1845     {
1846 	id = syn_name2id((char_u *)"Normal");
1847 	if (id > 0)
1848 	{
1849 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1850 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
1851 	}
1852     }
1853 }
1854 
1855 /*
1856  * Return the handle for a font name.
1857  * Returns NOFONT when failed.
1858  */
1859     static GuiFont
1860 font_name2handle(char_u *name)
1861 {
1862     if (STRCMP(name, "NONE") == 0)
1863 	return NOFONT;
1864 
1865     return gui_mch_get_font(name, TRUE);
1866 }
1867 
1868 # ifdef FEAT_XFONTSET
1869 /*
1870  * Return the handle for a fontset name.
1871  * Returns NOFONTSET when failed.
1872  */
1873     static GuiFontset
1874 fontset_name2handle(char_u *name, int fixed_width)
1875 {
1876     if (STRCMP(name, "NONE") == 0)
1877 	return NOFONTSET;
1878 
1879     return gui_mch_get_fontset(name, TRUE, fixed_width);
1880 }
1881 # endif
1882 
1883 /*
1884  * Get the font or fontset for one highlight group.
1885  */
1886     static void
1887 hl_do_font(
1888     int		idx,
1889     char_u	*arg,
1890     int		do_normal,		// set normal font
1891     int		do_menu UNUSED,		// set menu font
1892     int		do_tooltip UNUSED,	// set tooltip font
1893     int		free_font)		// free current font/fontset
1894 {
1895 # ifdef FEAT_XFONTSET
1896     // If 'guifontset' is not empty, first try using the name as a
1897     // fontset.  If that doesn't work, use it as a font name.
1898     if (*p_guifontset != NUL
1899 #  ifdef FONTSET_ALWAYS
1900 	|| do_menu
1901 #  endif
1902 #  ifdef FEAT_BEVAL_TIP
1903 	// In Athena & Motif, the Tooltip highlight group is always a fontset
1904 	|| do_tooltip
1905 #  endif
1906 	    )
1907     {
1908 	if (free_font)
1909 	    gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1910 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1911 #  ifdef FONTSET_ALWAYS
1912 		|| do_menu
1913 #  endif
1914 #  ifdef FEAT_BEVAL_TIP
1915 		|| do_tooltip
1916 #  endif
1917 		);
1918     }
1919     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1920     {
1921 	// If it worked and it's the Normal group, use it as the normal
1922 	// fontset.  Same for the Menu group.
1923 	if (do_normal)
1924 	    gui_init_font(arg, TRUE);
1925 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1926 	if (do_menu)
1927 	{
1928 #    ifdef FONTSET_ALWAYS
1929 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1930 #    else
1931 	    // YIKES!  This is a bug waiting to crash the program
1932 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
1933 #    endif
1934 	    gui_mch_new_menu_font();
1935 	}
1936 #    ifdef FEAT_BEVAL_GUI
1937 	if (do_tooltip)
1938 	{
1939 	    // The Athena widget set cannot currently handle switching between
1940 	    // displaying a single font and a fontset.
1941 	    // If the XtNinternational resource is set to True at widget
1942 	    // creation, then a fontset is always used, otherwise an
1943 	    // XFontStruct is used.
1944 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1945 	    gui_mch_new_tooltip_font();
1946 	}
1947 #    endif
1948 #   endif
1949     }
1950     else
1951 # endif
1952     {
1953 	if (free_font)
1954 	    gui_mch_free_font(HL_TABLE()[idx].sg_font);
1955 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
1956 	// If it worked and it's the Normal group, use it as the
1957 	// normal font.  Same for the Menu group.
1958 	if (HL_TABLE()[idx].sg_font != NOFONT)
1959 	{
1960 	    if (do_normal)
1961 		gui_init_font(arg, FALSE);
1962 #ifndef FONTSET_ALWAYS
1963 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1964 	    if (do_menu)
1965 	    {
1966 		gui.menu_font = HL_TABLE()[idx].sg_font;
1967 		gui_mch_new_menu_font();
1968 	    }
1969 # endif
1970 #endif
1971 	}
1972     }
1973 }
1974 
1975 #endif // FEAT_GUI
1976 
1977 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1978 /*
1979  * Return the handle for a color name.
1980  * Returns INVALCOLOR when failed.
1981  */
1982     guicolor_T
1983 color_name2handle(char_u *name)
1984 {
1985     if (STRCMP(name, "NONE") == 0)
1986 	return INVALCOLOR;
1987 
1988     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
1989     {
1990 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
1991 	if (gui.in_use)
1992 #endif
1993 #ifdef FEAT_GUI
1994 	    return gui.norm_pixel;
1995 #endif
1996 #ifdef FEAT_TERMGUICOLORS
1997 	if (cterm_normal_fg_gui_color != INVALCOLOR)
1998 	    return cterm_normal_fg_gui_color;
1999 	// Guess that the foreground is black or white.
2000 	return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2001 #endif
2002     }
2003     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2004     {
2005 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2006 	if (gui.in_use)
2007 #endif
2008 #ifdef FEAT_GUI
2009 	    return gui.back_pixel;
2010 #endif
2011 #ifdef FEAT_TERMGUICOLORS
2012 	if (cterm_normal_bg_gui_color != INVALCOLOR)
2013 	    return cterm_normal_bg_gui_color;
2014 	// Guess that the background is white or black.
2015 	return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2016 #endif
2017     }
2018 
2019     return GUI_GET_COLOR(name);
2020 }
2021 #endif
2022 
2023 /*
2024  * Table with the specifications for an attribute number.
2025  * Note that this table is used by ALL buffers.  This is required because the
2026  * GUI can redraw at any time for any buffer.
2027  */
2028 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
2029 
2030 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2031 
2032 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
2033 
2034 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2035 
2036 #ifdef FEAT_GUI
2037 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
2038 
2039 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2040 #endif
2041 
2042 /*
2043  * Return the attr number for a set of colors and font.
2044  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2045  * if the combination is new.
2046  * Return 0 for error (no more room).
2047  */
2048     static int
2049 get_attr_entry(garray_T *table, attrentry_T *aep)
2050 {
2051     int		i;
2052     attrentry_T	*taep;
2053     static int	recursive = FALSE;
2054 
2055     /*
2056      * Init the table, in case it wasn't done yet.
2057      */
2058     table->ga_itemsize = sizeof(attrentry_T);
2059     table->ga_growsize = 7;
2060 
2061     /*
2062      * Try to find an entry with the same specifications.
2063      */
2064     for (i = 0; i < table->ga_len; ++i)
2065     {
2066 	taep = &(((attrentry_T *)table->ga_data)[i]);
2067 	if (	   aep->ae_attr == taep->ae_attr
2068 		&& (
2069 #ifdef FEAT_GUI
2070 		       (table == &gui_attr_table
2071 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2072 			    && aep->ae_u.gui.bg_color
2073 						    == taep->ae_u.gui.bg_color
2074 			    && aep->ae_u.gui.sp_color
2075 						    == taep->ae_u.gui.sp_color
2076 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
2077 #  ifdef FEAT_XFONTSET
2078 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2079 #  endif
2080 			    ))
2081 		    ||
2082 #endif
2083 		       (table == &term_attr_table
2084 			&& (aep->ae_u.term.start == NULL)
2085 					    == (taep->ae_u.term.start == NULL)
2086 			&& (aep->ae_u.term.start == NULL
2087 			    || STRCMP(aep->ae_u.term.start,
2088 						  taep->ae_u.term.start) == 0)
2089 			&& (aep->ae_u.term.stop == NULL)
2090 					     == (taep->ae_u.term.stop == NULL)
2091 			&& (aep->ae_u.term.stop == NULL
2092 			    || STRCMP(aep->ae_u.term.stop,
2093 						  taep->ae_u.term.stop) == 0))
2094 		    || (table == &cterm_attr_table
2095 			    && aep->ae_u.cterm.fg_color
2096 						  == taep->ae_u.cterm.fg_color
2097 			    && aep->ae_u.cterm.bg_color
2098 						  == taep->ae_u.cterm.bg_color
2099 #ifdef FEAT_TERMGUICOLORS
2100 			    && aep->ae_u.cterm.fg_rgb
2101 						    == taep->ae_u.cterm.fg_rgb
2102 			    && aep->ae_u.cterm.bg_rgb
2103 						    == taep->ae_u.cterm.bg_rgb
2104 #endif
2105 		       )))
2106 
2107 	return i + ATTR_OFF;
2108     }
2109 
2110     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2111     {
2112 	/*
2113 	 * Running out of attribute entries!  remove all attributes, and
2114 	 * compute new ones for all groups.
2115 	 * When called recursively, we are really out of numbers.
2116 	 */
2117 	if (recursive)
2118 	{
2119 	    emsg(_("E424: Too many different highlighting attributes in use"));
2120 	    return 0;
2121 	}
2122 	recursive = TRUE;
2123 
2124 	clear_hl_tables();
2125 
2126 	must_redraw = CLEAR;
2127 
2128 	for (i = 0; i < highlight_ga.ga_len; ++i)
2129 	    set_hl_attr(i);
2130 
2131 	recursive = FALSE;
2132     }
2133 
2134     /*
2135      * This is a new combination of colors and font, add an entry.
2136      */
2137     if (ga_grow(table, 1) == FAIL)
2138 	return 0;
2139 
2140     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
2141     vim_memset(taep, 0, sizeof(attrentry_T));
2142     taep->ae_attr = aep->ae_attr;
2143 #ifdef FEAT_GUI
2144     if (table == &gui_attr_table)
2145     {
2146 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2147 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2148 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2149 	taep->ae_u.gui.font = aep->ae_u.gui.font;
2150 # ifdef FEAT_XFONTSET
2151 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2152 # endif
2153     }
2154 #endif
2155     if (table == &term_attr_table)
2156     {
2157 	if (aep->ae_u.term.start == NULL)
2158 	    taep->ae_u.term.start = NULL;
2159 	else
2160 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2161 	if (aep->ae_u.term.stop == NULL)
2162 	    taep->ae_u.term.stop = NULL;
2163 	else
2164 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2165     }
2166     else if (table == &cterm_attr_table)
2167     {
2168 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2169 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
2170 #ifdef FEAT_TERMGUICOLORS
2171 	taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2172 	taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
2173 #endif
2174     }
2175     ++table->ga_len;
2176     return (table->ga_len - 1 + ATTR_OFF);
2177 }
2178 
2179 #if defined(FEAT_TERMINAL) || defined(PROTO)
2180 /*
2181  * Get an attribute index for a cterm entry.
2182  * Uses an existing entry when possible or adds one when needed.
2183  */
2184     int
2185 get_cterm_attr_idx(int attr, int fg, int bg)
2186 {
2187     attrentry_T		at_en;
2188 
2189     vim_memset(&at_en, 0, sizeof(attrentry_T));
2190 #ifdef FEAT_TERMGUICOLORS
2191     at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2192     at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2193 #endif
2194     at_en.ae_attr = attr;
2195     at_en.ae_u.cterm.fg_color = fg;
2196     at_en.ae_u.cterm.bg_color = bg;
2197     return get_attr_entry(&cterm_attr_table, &at_en);
2198 }
2199 #endif
2200 
2201 #if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2202 /*
2203  * Get an attribute index for a 'termguicolors' entry.
2204  * Uses an existing entry when possible or adds one when needed.
2205  */
2206     int
2207 get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2208 {
2209     attrentry_T		at_en;
2210 
2211     vim_memset(&at_en, 0, sizeof(attrentry_T));
2212     at_en.ae_attr = attr;
2213     if (fg == INVALCOLOR && bg == INVALCOLOR)
2214     {
2215 	// If both GUI colors are not set fall back to the cterm colors.  Helps
2216 	// if the GUI only has an attribute, such as undercurl.
2217 	at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2218 	at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2219     }
2220     else
2221     {
2222 	at_en.ae_u.cterm.fg_rgb = fg;
2223 	at_en.ae_u.cterm.bg_rgb = bg;
2224     }
2225     return get_attr_entry(&cterm_attr_table, &at_en);
2226 }
2227 #endif
2228 
2229 #if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2230 /*
2231  * Get an attribute index for a cterm entry.
2232  * Uses an existing entry when possible or adds one when needed.
2233  */
2234     int
2235 get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2236 {
2237     attrentry_T		at_en;
2238 
2239     vim_memset(&at_en, 0, sizeof(attrentry_T));
2240     at_en.ae_attr = attr;
2241     at_en.ae_u.gui.fg_color = fg;
2242     at_en.ae_u.gui.bg_color = bg;
2243     return get_attr_entry(&gui_attr_table, &at_en);
2244 }
2245 #endif
2246 
2247 /*
2248  * Clear all highlight tables.
2249  */
2250     void
2251 clear_hl_tables(void)
2252 {
2253     int		i;
2254     attrentry_T	*taep;
2255 
2256 #ifdef FEAT_GUI
2257     ga_clear(&gui_attr_table);
2258 #endif
2259     for (i = 0; i < term_attr_table.ga_len; ++i)
2260     {
2261 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2262 	vim_free(taep->ae_u.term.start);
2263 	vim_free(taep->ae_u.term.stop);
2264     }
2265     ga_clear(&term_attr_table);
2266     ga_clear(&cterm_attr_table);
2267 }
2268 
2269 /*
2270  * Combine special attributes (e.g., for spelling) with other attributes
2271  * (e.g., for syntax highlighting).
2272  * "prim_attr" overrules "char_attr".
2273  * This creates a new group when required.
2274  * Since we expect there to be few spelling mistakes we don't cache the
2275  * result.
2276  * Return the resulting attributes.
2277  */
2278     int
2279 hl_combine_attr(int char_attr, int prim_attr)
2280 {
2281     attrentry_T *char_aep = NULL;
2282     attrentry_T *spell_aep;
2283     attrentry_T new_en;
2284 
2285     if (char_attr == 0)
2286 	return prim_attr;
2287     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2288 	return ATTR_COMBINE(char_attr, prim_attr);
2289 #ifdef FEAT_GUI
2290     if (gui.in_use)
2291     {
2292 	if (char_attr > HL_ALL)
2293 	    char_aep = syn_gui_attr2entry(char_attr);
2294 	if (char_aep != NULL)
2295 	    new_en = *char_aep;
2296 	else
2297 	{
2298 	    vim_memset(&new_en, 0, sizeof(new_en));
2299 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
2300 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
2301 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
2302 	    if (char_attr <= HL_ALL)
2303 		new_en.ae_attr = char_attr;
2304 	}
2305 
2306 	if (prim_attr <= HL_ALL)
2307 	    new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2308 	else
2309 	{
2310 	    spell_aep = syn_gui_attr2entry(prim_attr);
2311 	    if (spell_aep != NULL)
2312 	    {
2313 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2314 							   spell_aep->ae_attr);
2315 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2316 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2317 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2318 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2319 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2320 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2321 		if (spell_aep->ae_u.gui.font != NOFONT)
2322 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2323 # ifdef FEAT_XFONTSET
2324 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2325 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2326 # endif
2327 	    }
2328 	}
2329 	return get_attr_entry(&gui_attr_table, &new_en);
2330     }
2331 #endif
2332 
2333     if (IS_CTERM)
2334     {
2335 	if (char_attr > HL_ALL)
2336 	    char_aep = syn_cterm_attr2entry(char_attr);
2337 	if (char_aep != NULL)
2338 	    new_en = *char_aep;
2339 	else
2340 	{
2341 	    vim_memset(&new_en, 0, sizeof(new_en));
2342 #ifdef FEAT_TERMGUICOLORS
2343 	    new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2344 	    new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2345 #endif
2346 	    if (char_attr <= HL_ALL)
2347 		new_en.ae_attr = char_attr;
2348 	}
2349 
2350 	if (prim_attr <= HL_ALL)
2351 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2352 	else
2353 	{
2354 	    spell_aep = syn_cterm_attr2entry(prim_attr);
2355 	    if (spell_aep != NULL)
2356 	    {
2357 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2358 							   spell_aep->ae_attr);
2359 		if (spell_aep->ae_u.cterm.fg_color > 0)
2360 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2361 		if (spell_aep->ae_u.cterm.bg_color > 0)
2362 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
2363 #ifdef FEAT_TERMGUICOLORS
2364 		// If both fg and bg are not set fall back to cterm colors.
2365 		// Helps for SpellBad which uses undercurl in the GUI.
2366 		if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2367 			&& COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2368 		{
2369 		    if (spell_aep->ae_u.cterm.fg_color > 0)
2370 			new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2371 		    if (spell_aep->ae_u.cterm.bg_color > 0)
2372 			new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2373 		}
2374 		else
2375 		{
2376 		    if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2377 			new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2378 		    if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2379 			new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2380 		}
2381 #endif
2382 	    }
2383 	}
2384 	return get_attr_entry(&cterm_attr_table, &new_en);
2385     }
2386 
2387     if (char_attr > HL_ALL)
2388 	char_aep = syn_term_attr2entry(char_attr);
2389     if (char_aep != NULL)
2390 	new_en = *char_aep;
2391     else
2392     {
2393 	vim_memset(&new_en, 0, sizeof(new_en));
2394 	if (char_attr <= HL_ALL)
2395 	    new_en.ae_attr = char_attr;
2396     }
2397 
2398     if (prim_attr <= HL_ALL)
2399 	new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2400     else
2401     {
2402 	spell_aep = syn_term_attr2entry(prim_attr);
2403 	if (spell_aep != NULL)
2404 	{
2405 	    new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2406 	    if (spell_aep->ae_u.term.start != NULL)
2407 	    {
2408 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2409 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2410 	    }
2411 	}
2412     }
2413     return get_attr_entry(&term_attr_table, &new_en);
2414 }
2415 
2416 #ifdef FEAT_GUI
2417     attrentry_T *
2418 syn_gui_attr2entry(int attr)
2419 {
2420     attr -= ATTR_OFF;
2421     if (attr >= gui_attr_table.ga_len)	    // did ":syntax clear"
2422 	return NULL;
2423     return &(GUI_ATTR_ENTRY(attr));
2424 }
2425 #endif
2426 
2427 /*
2428  * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2429  * Only to be used when "attr" > HL_ALL.
2430  */
2431     int
2432 syn_attr2attr(int attr)
2433 {
2434     attrentry_T	*aep;
2435 
2436 #ifdef FEAT_GUI
2437     if (gui.in_use)
2438 	aep = syn_gui_attr2entry(attr);
2439     else
2440 #endif
2441 	if (IS_CTERM)
2442 	    aep = syn_cterm_attr2entry(attr);
2443 	else
2444 	    aep = syn_term_attr2entry(attr);
2445 
2446     if (aep == NULL)	    // highlighting not set
2447 	return 0;
2448     return aep->ae_attr;
2449 }
2450 
2451 
2452     attrentry_T *
2453 syn_term_attr2entry(int attr)
2454 {
2455     attr -= ATTR_OFF;
2456     if (attr >= term_attr_table.ga_len)	    // did ":syntax clear"
2457 	return NULL;
2458     return &(TERM_ATTR_ENTRY(attr));
2459 }
2460 
2461     attrentry_T *
2462 syn_cterm_attr2entry(int attr)
2463 {
2464     attr -= ATTR_OFF;
2465     if (attr >= cterm_attr_table.ga_len)	// did ":syntax clear"
2466 	return NULL;
2467     return &(CTERM_ATTR_ENTRY(attr));
2468 }
2469 
2470 #define LIST_ATTR   1
2471 #define LIST_STRING 2
2472 #define LIST_INT    3
2473 
2474     static void
2475 highlight_list_one(int id)
2476 {
2477     hl_group_T	    *sgp;
2478     int		    didh = FALSE;
2479 
2480     sgp = &HL_TABLE()[id - 1];	    // index is ID minus one
2481 
2482     if (message_filtered(sgp->sg_name))
2483 	return;
2484 
2485     didh = highlight_list_arg(id, didh, LIST_ATTR,
2486 				    sgp->sg_term, NULL, "term");
2487     didh = highlight_list_arg(id, didh, LIST_STRING,
2488 				    0, sgp->sg_start, "start");
2489     didh = highlight_list_arg(id, didh, LIST_STRING,
2490 				    0, sgp->sg_stop, "stop");
2491 
2492     didh = highlight_list_arg(id, didh, LIST_ATTR,
2493 				    sgp->sg_cterm, NULL, "cterm");
2494     didh = highlight_list_arg(id, didh, LIST_INT,
2495 				    sgp->sg_cterm_fg, NULL, "ctermfg");
2496     didh = highlight_list_arg(id, didh, LIST_INT,
2497 				    sgp->sg_cterm_bg, NULL, "ctermbg");
2498 
2499 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
2500     didh = highlight_list_arg(id, didh, LIST_ATTR,
2501 				    sgp->sg_gui, NULL, "gui");
2502     didh = highlight_list_arg(id, didh, LIST_STRING,
2503 				    0, sgp->sg_gui_fg_name, "guifg");
2504     didh = highlight_list_arg(id, didh, LIST_STRING,
2505 				    0, sgp->sg_gui_bg_name, "guibg");
2506     didh = highlight_list_arg(id, didh, LIST_STRING,
2507 				    0, sgp->sg_gui_sp_name, "guisp");
2508 #endif
2509 #ifdef FEAT_GUI
2510     didh = highlight_list_arg(id, didh, LIST_STRING,
2511 				    0, sgp->sg_font_name, "font");
2512 #endif
2513 
2514     if (sgp->sg_link && !got_int)
2515     {
2516 	(void)syn_list_header(didh, 9999, id);
2517 	didh = TRUE;
2518 	msg_puts_attr("links to", HL_ATTR(HLF_D));
2519 	msg_putchar(' ');
2520 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2521     }
2522 
2523     if (!didh)
2524 	highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2525 #ifdef FEAT_EVAL
2526     if (p_verbose > 0)
2527 	last_set_msg(sgp->sg_script_ctx);
2528 #endif
2529 }
2530 
2531     static int
2532 highlight_list_arg(
2533     int		id,
2534     int		didh,
2535     int		type,
2536     int		iarg,
2537     char_u	*sarg,
2538     char	*name)
2539 {
2540     char_u	buf[100];
2541     char_u	*ts;
2542     int		i;
2543 
2544     if (got_int)
2545 	return FALSE;
2546     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2547     {
2548 	ts = buf;
2549 	if (type == LIST_INT)
2550 	    sprintf((char *)buf, "%d", iarg - 1);
2551 	else if (type == LIST_STRING)
2552 	    ts = sarg;
2553 	else // type == LIST_ATTR
2554 	{
2555 	    buf[0] = NUL;
2556 	    for (i = 0; hl_attr_table[i] != 0; ++i)
2557 	    {
2558 		if (iarg & hl_attr_table[i])
2559 		{
2560 		    if (buf[0] != NUL)
2561 			vim_strcat(buf, (char_u *)",", 100);
2562 		    vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2563 		    iarg &= ~hl_attr_table[i];	    // don't want "inverse"
2564 		}
2565 	    }
2566 	}
2567 
2568 	(void)syn_list_header(didh,
2569 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2570 	didh = TRUE;
2571 	if (!got_int)
2572 	{
2573 	    if (*name != NUL)
2574 	    {
2575 		msg_puts_attr(name, HL_ATTR(HLF_D));
2576 		msg_puts_attr("=", HL_ATTR(HLF_D));
2577 	    }
2578 	    msg_outtrans(ts);
2579 	}
2580     }
2581     return didh;
2582 }
2583 
2584 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2585 /*
2586  * Return "1" if highlight group "id" has attribute "flag".
2587  * Return NULL otherwise.
2588  */
2589     char_u *
2590 highlight_has_attr(
2591     int		id,
2592     int		flag,
2593     int		modec)	// 'g' for GUI, 'c' for cterm, 't' for term
2594 {
2595     int		attr;
2596 
2597     if (id <= 0 || id > highlight_ga.ga_len)
2598 	return NULL;
2599 
2600 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
2601     if (modec == 'g')
2602 	attr = HL_TABLE()[id - 1].sg_gui;
2603     else
2604 #endif
2605 	 if (modec == 'c')
2606 	attr = HL_TABLE()[id - 1].sg_cterm;
2607     else
2608 	attr = HL_TABLE()[id - 1].sg_term;
2609 
2610     if (attr & flag)
2611 	return (char_u *)"1";
2612     return NULL;
2613 }
2614 #endif
2615 
2616 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2617 /*
2618  * Return color name of highlight group "id".
2619  */
2620     char_u *
2621 highlight_color(
2622     int		id,
2623     char_u	*what,	// "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#"
2624     int		modec)	// 'g' for GUI, 'c' for cterm, 't' for term
2625 {
2626     static char_u	name[20];
2627     int			n;
2628     int			fg = FALSE;
2629     int			sp = FALSE;
2630     int			font = FALSE;
2631 
2632     if (id <= 0 || id > highlight_ga.ga_len)
2633 	return NULL;
2634 
2635     if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2636 	fg = TRUE;
2637     else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2638 	     && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2639 	font = TRUE;
2640     else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2641 	sp = TRUE;
2642     else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2643 	return NULL;
2644     if (modec == 'g')
2645     {
2646 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2647 #  ifdef FEAT_GUI
2648 	// return font name
2649 	if (font)
2650 	    return HL_TABLE()[id - 1].sg_font_name;
2651 #  endif
2652 
2653 	// return #RRGGBB form (only possible when GUI is running)
2654 	if ((USE_24BIT) && what[2] == '#')
2655 	{
2656 	    guicolor_T		color;
2657 	    long_u		rgb;
2658 	    static char_u	buf[10];
2659 
2660 	    if (fg)
2661 		color = HL_TABLE()[id - 1].sg_gui_fg;
2662 	    else if (sp)
2663 #  ifdef FEAT_GUI
2664 		color = HL_TABLE()[id - 1].sg_gui_sp;
2665 #  else
2666 		color = INVALCOLOR;
2667 #  endif
2668 	    else
2669 		color = HL_TABLE()[id - 1].sg_gui_bg;
2670 	    if (color == INVALCOLOR)
2671 		return NULL;
2672 	    rgb = (long_u)GUI_MCH_GET_RGB(color);
2673 	    sprintf((char *)buf, "#%02x%02x%02x",
2674 				      (unsigned)(rgb >> 16),
2675 				      (unsigned)(rgb >> 8) & 255,
2676 				      (unsigned)rgb & 255);
2677 	    return buf;
2678 	}
2679 # endif
2680 	if (fg)
2681 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
2682 	if (sp)
2683 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
2684 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
2685     }
2686     if (font || sp)
2687 	return NULL;
2688     if (modec == 'c')
2689     {
2690 	if (fg)
2691 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2692 	else
2693 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2694 	if (n < 0)
2695 	    return NULL;
2696 	sprintf((char *)name, "%d", n);
2697 	return name;
2698     }
2699     // term doesn't have color
2700     return NULL;
2701 }
2702 #endif
2703 
2704 #if (defined(FEAT_SYN_HL) \
2705 	    && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2706 	&& defined(FEAT_PRINTER)) || defined(PROTO)
2707 /*
2708  * Return color name of highlight group "id" as RGB value.
2709  */
2710     long_u
2711 highlight_gui_color_rgb(
2712     int		id,
2713     int		fg)	// TRUE = fg, FALSE = bg
2714 {
2715     guicolor_T	color;
2716 
2717     if (id <= 0 || id > highlight_ga.ga_len)
2718 	return 0L;
2719 
2720     if (fg)
2721 	color = HL_TABLE()[id - 1].sg_gui_fg;
2722     else
2723 	color = HL_TABLE()[id - 1].sg_gui_bg;
2724 
2725     if (color == INVALCOLOR)
2726 	return 0L;
2727 
2728     return GUI_MCH_GET_RGB(color);
2729 }
2730 #endif
2731 
2732 /*
2733  * Output the syntax list header.
2734  * Return TRUE when started a new line.
2735  */
2736     int
2737 syn_list_header(
2738     int	    did_header,		// did header already
2739     int	    outlen,		// length of string that comes
2740     int	    id)			// highlight group id
2741 {
2742     int	    endcol = 19;
2743     int	    newline = TRUE;
2744     int	    name_col = 0;
2745 
2746     if (!did_header)
2747     {
2748 	msg_putchar('\n');
2749 	if (got_int)
2750 	    return TRUE;
2751 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
2752 	name_col = msg_col;
2753 	endcol = 15;
2754     }
2755     else if (msg_col + outlen + 1 >= Columns)
2756     {
2757 	msg_putchar('\n');
2758 	if (got_int)
2759 	    return TRUE;
2760     }
2761     else
2762     {
2763 	if (msg_col >= endcol)	// wrap around is like starting a new line
2764 	    newline = FALSE;
2765     }
2766 
2767     if (msg_col >= endcol)	// output at least one space
2768 	endcol = msg_col + 1;
2769     if (Columns <= endcol)	// avoid hang for tiny window
2770 	endcol = Columns - 1;
2771 
2772     msg_advance(endcol);
2773 
2774     // Show "xxx" with the attributes.
2775     if (!did_header)
2776     {
2777 	if (endcol == Columns - 1 && endcol <= name_col)
2778 	    msg_putchar(' ');
2779 	msg_puts_attr("xxx", syn_id2attr(id));
2780 	msg_putchar(' ');
2781     }
2782 
2783     return newline;
2784 }
2785 
2786 /*
2787  * Set the attribute numbers for a highlight group.
2788  * Called after one of the attributes has changed.
2789  */
2790     static void
2791 set_hl_attr(
2792     int		idx)	    // index in array
2793 {
2794     attrentry_T	    at_en;
2795     hl_group_T	    *sgp = HL_TABLE() + idx;
2796 
2797     // The "Normal" group doesn't need an attribute number
2798     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2799 	return;
2800 
2801 #ifdef FEAT_GUI
2802     /*
2803      * For the GUI mode: If there are other than "normal" highlighting
2804      * attributes, need to allocate an attr number.
2805      */
2806     if (sgp->sg_gui_fg == INVALCOLOR
2807 	    && sgp->sg_gui_bg == INVALCOLOR
2808 	    && sgp->sg_gui_sp == INVALCOLOR
2809 	    && sgp->sg_font == NOFONT
2810 # ifdef FEAT_XFONTSET
2811 	    && sgp->sg_fontset == NOFONTSET
2812 # endif
2813 	    )
2814     {
2815 	sgp->sg_gui_attr = sgp->sg_gui;
2816     }
2817     else
2818     {
2819 	at_en.ae_attr = sgp->sg_gui;
2820 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2821 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2822 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2823 	at_en.ae_u.gui.font = sgp->sg_font;
2824 # ifdef FEAT_XFONTSET
2825 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
2826 # endif
2827 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2828     }
2829 #endif
2830     /*
2831      * For the term mode: If there are other than "normal" highlighting
2832      * attributes, need to allocate an attr number.
2833      */
2834     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2835 	sgp->sg_term_attr = sgp->sg_term;
2836     else
2837     {
2838 	at_en.ae_attr = sgp->sg_term;
2839 	at_en.ae_u.term.start = sgp->sg_start;
2840 	at_en.ae_u.term.stop = sgp->sg_stop;
2841 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2842     }
2843 
2844     /*
2845      * For the color term mode: If there are other than "normal"
2846      * highlighting attributes, need to allocate an attr number.
2847      */
2848     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
2849 # ifdef FEAT_TERMGUICOLORS
2850 	    && sgp->sg_gui_fg == INVALCOLOR
2851 	    && sgp->sg_gui_bg == INVALCOLOR
2852 # endif
2853 	    )
2854 	sgp->sg_cterm_attr = sgp->sg_cterm;
2855     else
2856     {
2857 	at_en.ae_attr = sgp->sg_cterm;
2858 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2859 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
2860 # ifdef FEAT_TERMGUICOLORS
2861 #  ifdef MSWIN
2862 #   ifdef VIMDLL
2863 	// Only when not using the GUI.
2864 	if (!gui.in_use && !gui.starting)
2865 #   endif
2866 	{
2867 	    int id;
2868 	    guicolor_T fg, bg;
2869 
2870 	    id = syn_name2id((char_u *)"Normal");
2871 	    if (id > 0)
2872 	    {
2873 		syn_id2colors(id, &fg, &bg);
2874 		if (sgp->sg_gui_fg == INVALCOLOR)
2875 		    sgp->sg_gui_fg = fg;
2876 		if (sgp->sg_gui_bg == INVALCOLOR)
2877 		    sgp->sg_gui_bg = bg;
2878 	    }
2879 
2880 	}
2881 #  endif
2882 	at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2883 	at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
2884 	if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2885 		&& at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2886 	{
2887 	    // If both fg and bg are invalid fall back to the cterm colors.
2888 	    // Helps when the GUI only uses an attribute, e.g. undercurl.
2889 	    at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2890 	    at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2891 	}
2892 # endif
2893 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2894     }
2895 }
2896 
2897 /*
2898  * Lookup a highlight group name and return its ID.
2899  * If it is not found, 0 is returned.
2900  */
2901     int
2902 syn_name2id(char_u *name)
2903 {
2904     int		i;
2905     char_u	name_u[200];
2906 
2907     // Avoid using stricmp() too much, it's slow on some systems
2908     // Avoid alloc()/free(), these are slow too.  ID names over 200 chars
2909     // don't deserve to be found!
2910     vim_strncpy(name_u, name, 199);
2911     vim_strup(name_u);
2912     for (i = highlight_ga.ga_len; --i >= 0; )
2913 	if (HL_TABLE()[i].sg_name_u != NULL
2914 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2915 	    break;
2916     return i + 1;
2917 }
2918 
2919 /*
2920  * Lookup a highlight group name and return its attributes.
2921  * Return zero if not found.
2922  */
2923     int
2924 syn_name2attr(char_u *name)
2925 {
2926     int id = syn_name2id(name);
2927 
2928     if (id != 0)
2929 	return syn_id2attr(id);
2930     return 0;
2931 }
2932 
2933 #if defined(FEAT_EVAL) || defined(PROTO)
2934 /*
2935  * Return TRUE if highlight group "name" exists.
2936  */
2937     int
2938 highlight_exists(char_u *name)
2939 {
2940     return (syn_name2id(name) > 0);
2941 }
2942 
2943 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2944 /*
2945  * Return the name of highlight group "id".
2946  * When not a valid ID return an empty string.
2947  */
2948     char_u *
2949 syn_id2name(int id)
2950 {
2951     if (id <= 0 || id > highlight_ga.ga_len)
2952 	return (char_u *)"";
2953     return HL_TABLE()[id - 1].sg_name;
2954 }
2955 # endif
2956 #endif
2957 
2958 /*
2959  * Like syn_name2id(), but take a pointer + length argument.
2960  */
2961     int
2962 syn_namen2id(char_u *linep, int len)
2963 {
2964     char_u  *name;
2965     int	    id = 0;
2966 
2967     name = vim_strnsave(linep, len);
2968     if (name != NULL)
2969     {
2970 	id = syn_name2id(name);
2971 	vim_free(name);
2972     }
2973     return id;
2974 }
2975 
2976 /*
2977  * Find highlight group name in the table and return its ID.
2978  * The argument is a pointer to the name and the length of the name.
2979  * If it doesn't exist yet, a new entry is created.
2980  * Return 0 for failure.
2981  */
2982     int
2983 syn_check_group(char_u *pp, int len)
2984 {
2985     int	    id;
2986     char_u  *name;
2987 
2988     name = vim_strnsave(pp, len);
2989     if (name == NULL)
2990 	return 0;
2991 
2992     id = syn_name2id(name);
2993     if (id == 0)			// doesn't exist yet
2994 	id = syn_add_group(name);
2995     else
2996 	vim_free(name);
2997     return id;
2998 }
2999 
3000 /*
3001  * Add new highlight group and return its ID.
3002  * "name" must be an allocated string, it will be consumed.
3003  * Return 0 for failure.
3004  */
3005     static int
3006 syn_add_group(char_u *name)
3007 {
3008     char_u	*p;
3009     char_u	*name_up;
3010 
3011     // Check that the name is ASCII letters, digits and underscore.
3012     for (p = name; *p != NUL; ++p)
3013     {
3014 	if (!vim_isprintc(*p))
3015 	{
3016 	    emsg(_("E669: Unprintable character in group name"));
3017 	    vim_free(name);
3018 	    return 0;
3019 	}
3020 	else if (!ASCII_ISALNUM(*p) && *p != '_')
3021 	{
3022 	    // This is an error, but since there previously was no check only
3023 	    // give a warning.
3024 	    msg_source(HL_ATTR(HLF_W));
3025 	    msg(_("W18: Invalid character in group name"));
3026 	    break;
3027 	}
3028     }
3029 
3030     /*
3031      * First call for this growarray: init growing array.
3032      */
3033     if (highlight_ga.ga_data == NULL)
3034     {
3035 	highlight_ga.ga_itemsize = sizeof(hl_group_T);
3036 	highlight_ga.ga_growsize = 10;
3037     }
3038 
3039     if (highlight_ga.ga_len >= MAX_HL_ID)
3040     {
3041 	emsg(_("E849: Too many highlight and syntax groups"));
3042 	vim_free(name);
3043 	return 0;
3044     }
3045 
3046     /*
3047      * Make room for at least one other syntax_highlight entry.
3048      */
3049     if (ga_grow(&highlight_ga, 1) == FAIL)
3050     {
3051 	vim_free(name);
3052 	return 0;
3053     }
3054 
3055     name_up = vim_strsave_up(name);
3056     if (name_up == NULL)
3057     {
3058 	vim_free(name);
3059 	return 0;
3060     }
3061 
3062     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(hl_group_T));
3063     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
3064     HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
3065 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3066     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3067     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
3068 # ifdef FEAT_GUI
3069     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
3070 # endif
3071 #endif
3072     ++highlight_ga.ga_len;
3073 
3074     return highlight_ga.ga_len;		    // ID is index plus one
3075 }
3076 
3077 /*
3078  * When, just after calling syn_add_group(), an error is discovered, this
3079  * function deletes the new name.
3080  */
3081     static void
3082 syn_unadd_group(void)
3083 {
3084     --highlight_ga.ga_len;
3085     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3086     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3087 }
3088 
3089 /*
3090  * Translate a group ID to highlight attributes.
3091  */
3092     int
3093 syn_id2attr(int hl_id)
3094 {
3095     int		attr;
3096     hl_group_T	*sgp;
3097 
3098     hl_id = syn_get_final_id(hl_id);
3099     sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3100 
3101 #ifdef FEAT_GUI
3102     /*
3103      * Only use GUI attr when the GUI is being used.
3104      */
3105     if (gui.in_use)
3106 	attr = sgp->sg_gui_attr;
3107     else
3108 #endif
3109 	if (IS_CTERM)
3110 	    attr = sgp->sg_cterm_attr;
3111 	else
3112 	    attr = sgp->sg_term_attr;
3113 
3114     return attr;
3115 }
3116 
3117 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3118 /*
3119  * Get the GUI colors and attributes for a group ID.
3120  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3121  */
3122     int
3123 syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3124 {
3125     hl_group_T	*sgp;
3126 
3127     hl_id = syn_get_final_id(hl_id);
3128     sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3129 
3130     *fgp = sgp->sg_gui_fg;
3131     *bgp = sgp->sg_gui_bg;
3132     return sgp->sg_gui;
3133 }
3134 #endif
3135 
3136 #if (defined(MSWIN) \
3137 	    && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3138 	    && defined(FEAT_TERMGUICOLORS)) \
3139 	|| defined(FEAT_TERMINAL) || defined(PROTO)
3140     void
3141 syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3142 {
3143     hl_group_T	*sgp;
3144 
3145     hl_id = syn_get_final_id(hl_id);
3146     sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3147     *fgp = sgp->sg_cterm_fg - 1;
3148     *bgp = sgp->sg_cterm_bg - 1;
3149 }
3150 #endif
3151 
3152 /*
3153  * Translate a group ID to the final group ID (following links).
3154  */
3155     int
3156 syn_get_final_id(int hl_id)
3157 {
3158     int		count;
3159     hl_group_T	*sgp;
3160 
3161     if (hl_id > highlight_ga.ga_len || hl_id < 1)
3162 	return 0;			// Can be called from eval!!
3163 
3164     /*
3165      * Follow links until there is no more.
3166      * Look out for loops!  Break after 100 links.
3167      */
3168     for (count = 100; --count >= 0; )
3169     {
3170 	sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3171 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3172 	    break;
3173 	hl_id = sgp->sg_link;
3174     }
3175 
3176     return hl_id;
3177 }
3178 
3179 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3180 /*
3181  * Call this function just after the GUI has started.
3182  * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3183  * It finds the font and color handles for the highlighting groups.
3184  */
3185     void
3186 highlight_gui_started(void)
3187 {
3188     int	    idx;
3189 
3190     // First get the colors from the "Normal" and "Menu" group, if set
3191     if (USE_24BIT)
3192 	set_normal_colors();
3193 
3194     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3195 	gui_do_one_color(idx, FALSE, FALSE);
3196 
3197     highlight_changed();
3198 }
3199 
3200     static void
3201 gui_do_one_color(
3202     int		idx,
3203     int		do_menu UNUSED,	   // TRUE: might set the menu font
3204     int		do_tooltip UNUSED) // TRUE: might set the tooltip font
3205 {
3206     int		didit = FALSE;
3207 
3208 # ifdef FEAT_GUI
3209 #  ifdef FEAT_TERMGUICOLORS
3210     if (gui.in_use)
3211 #  endif
3212 	if (HL_TABLE()[idx].sg_font_name != NULL)
3213 	{
3214 	    hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3215 							    do_tooltip, TRUE);
3216 	    didit = TRUE;
3217 	}
3218 # endif
3219     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3220     {
3221 	HL_TABLE()[idx].sg_gui_fg =
3222 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3223 	didit = TRUE;
3224     }
3225     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3226     {
3227 	HL_TABLE()[idx].sg_gui_bg =
3228 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3229 	didit = TRUE;
3230     }
3231 # ifdef FEAT_GUI
3232     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3233     {
3234 	HL_TABLE()[idx].sg_gui_sp =
3235 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3236 	didit = TRUE;
3237     }
3238 # endif
3239     if (didit)	// need to get a new attr number
3240 	set_hl_attr(idx);
3241 }
3242 #endif
3243 
3244 #if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3245 /*
3246  * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3247  */
3248     static void
3249 combine_stl_hlt(
3250 	int id,
3251 	int id_S,
3252 	int id_alt,
3253 	int hlcnt,
3254 	int i,
3255 	int hlf,
3256 	int *table)
3257 {
3258     hl_group_T *hlt = HL_TABLE();
3259 
3260     if (id_alt == 0)
3261     {
3262 	vim_memset(&hlt[hlcnt + i], 0, sizeof(hl_group_T));
3263 	hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3264 	hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3265 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
3266 	hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3267 #  endif
3268     }
3269     else
3270 	mch_memmove(&hlt[hlcnt + i],
3271 		    &hlt[id_alt - 1],
3272 		    sizeof(hl_group_T));
3273     hlt[hlcnt + i].sg_link = 0;
3274 
3275     hlt[hlcnt + i].sg_term ^=
3276 	hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3277     if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3278 	hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3279     if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3280 	hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3281     hlt[hlcnt + i].sg_cterm ^=
3282 	hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3283     if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3284 	hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3285     if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3286 	hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3287 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
3288     hlt[hlcnt + i].sg_gui ^=
3289 	hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3290 #  endif
3291 #  ifdef FEAT_GUI
3292     if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3293 	hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3294     if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3295 	hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3296     if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3297 	hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3298     if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3299 	hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3300 #   ifdef FEAT_XFONTSET
3301     if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3302 	hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3303 #   endif
3304 #  endif
3305     highlight_ga.ga_len = hlcnt + i + 1;
3306     set_hl_attr(hlcnt + i);	// At long last we can apply
3307     table[i] = syn_id2attr(hlcnt + i + 1);
3308 }
3309 #endif
3310 
3311 /*
3312  * Translate the 'highlight' option into attributes in highlight_attr[] and
3313  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
3314  * corresponding highlights to use on top of HLF_SNC is computed.
3315  * Called only when the 'highlight' option has been changed and upon first
3316  * screen redraw after any :highlight command.
3317  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
3318  */
3319     int
3320 highlight_changed(void)
3321 {
3322     int		hlf;
3323     int		i;
3324     char_u	*p;
3325     int		attr;
3326     char_u	*end;
3327     int		id;
3328 #ifdef USER_HIGHLIGHT
3329     char_u      userhl[30];  // use 30 to avoid compiler warning
3330 # ifdef FEAT_STL_OPT
3331     int		id_S = -1;
3332     int		id_SNC = 0;
3333 #  ifdef FEAT_TERMINAL
3334     int		id_ST = 0;
3335     int		id_STNC = 0;
3336 #  endif
3337     int		hlcnt;
3338 # endif
3339 #endif
3340     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
3341 
3342     need_highlight_changed = FALSE;
3343 
3344     /*
3345      * Clear all attributes.
3346      */
3347     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3348 	highlight_attr[hlf] = 0;
3349 
3350     /*
3351      * First set all attributes to their default value.
3352      * Then use the attributes from the 'highlight' option.
3353      */
3354     for (i = 0; i < 2; ++i)
3355     {
3356 	if (i)
3357 	    p = p_hl;
3358 	else
3359 	    p = get_highlight_default();
3360 	if (p == NULL)	    // just in case
3361 	    continue;
3362 
3363 	while (*p)
3364 	{
3365 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3366 		if (hl_flags[hlf] == *p)
3367 		    break;
3368 	    ++p;
3369 	    if (hlf == (int)HLF_COUNT || *p == NUL)
3370 		return FAIL;
3371 
3372 	    /*
3373 	     * Allow several hl_flags to be combined, like "bu" for
3374 	     * bold-underlined.
3375 	     */
3376 	    attr = 0;
3377 	    for ( ; *p && *p != ','; ++p)	    // parse up to comma
3378 	    {
3379 		if (VIM_ISWHITE(*p))		    // ignore white space
3380 		    continue;
3381 
3382 		if (attr > HL_ALL)  // Combination with ':' is not allowed.
3383 		    return FAIL;
3384 
3385 		switch (*p)
3386 		{
3387 		    case 'b':	attr |= HL_BOLD;
3388 				break;
3389 		    case 'i':	attr |= HL_ITALIC;
3390 				break;
3391 		    case '-':
3392 		    case 'n':			    // no highlighting
3393 				break;
3394 		    case 'r':	attr |= HL_INVERSE;
3395 				break;
3396 		    case 's':	attr |= HL_STANDOUT;
3397 				break;
3398 		    case 'u':	attr |= HL_UNDERLINE;
3399 				break;
3400 		    case 'c':	attr |= HL_UNDERCURL;
3401 				break;
3402 		    case 't':	attr |= HL_STRIKETHROUGH;
3403 				break;
3404 		    case ':':	++p;		    // highlight group name
3405 				if (attr || *p == NUL)	 // no combinations
3406 				    return FAIL;
3407 				end = vim_strchr(p, ',');
3408 				if (end == NULL)
3409 				    end = p + STRLEN(p);
3410 				id = syn_check_group(p, (int)(end - p));
3411 				if (id == 0)
3412 				    return FAIL;
3413 				attr = syn_id2attr(id);
3414 				p = end - 1;
3415 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3416 				if (hlf == (int)HLF_SNC)
3417 				    id_SNC = syn_get_final_id(id);
3418 # ifdef FEAT_TERMINAL
3419 				else if (hlf == (int)HLF_ST)
3420 				    id_ST = syn_get_final_id(id);
3421 				else if (hlf == (int)HLF_STNC)
3422 				    id_STNC = syn_get_final_id(id);
3423 # endif
3424 				else if (hlf == (int)HLF_S)
3425 				    id_S = syn_get_final_id(id);
3426 #endif
3427 				break;
3428 		    default:	return FAIL;
3429 		}
3430 	    }
3431 	    highlight_attr[hlf] = attr;
3432 
3433 	    p = skip_to_option_part(p);	    // skip comma and spaces
3434 	}
3435     }
3436 
3437 #ifdef USER_HIGHLIGHT
3438     /*
3439      * Setup the user highlights
3440      *
3441      * Temporarily utilize 28 more hl entries:
3442      * 9 for User1-User9 combined with StatusLineNC
3443      * 9 for User1-User9 combined with StatusLineTerm
3444      * 9 for User1-User9 combined with StatusLineTermNC
3445      * 1 for StatusLine default
3446      * Have to be in there simultaneously in case of table overflows in
3447      * get_attr_entry()
3448      */
3449 # ifdef FEAT_STL_OPT
3450     if (ga_grow(&highlight_ga, 28) == FAIL)
3451 	return FAIL;
3452     hlcnt = highlight_ga.ga_len;
3453     if (id_S == -1)
3454     {
3455 	// Make sure id_S is always valid to simplify code below. Use the last
3456 	// entry.
3457 	vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(hl_group_T));
3458 	HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3459 	id_S = hlcnt + 19;
3460     }
3461 # endif
3462     for (i = 0; i < 9; i++)
3463     {
3464 	sprintf((char *)userhl, "User%d", i + 1);
3465 	id = syn_name2id(userhl);
3466 	if (id == 0)
3467 	{
3468 	    highlight_user[i] = 0;
3469 # ifdef FEAT_STL_OPT
3470 	    highlight_stlnc[i] = 0;
3471 #  ifdef FEAT_TERMINAL
3472 	    highlight_stlterm[i] = 0;
3473 	    highlight_stltermnc[i] = 0;
3474 #  endif
3475 # endif
3476 	}
3477 	else
3478 	{
3479 	    highlight_user[i] = syn_id2attr(id);
3480 # ifdef FEAT_STL_OPT
3481 	    combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3482 						     HLF_SNC, highlight_stlnc);
3483 #  ifdef FEAT_TERMINAL
3484 	    combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3485 						    HLF_ST, highlight_stlterm);
3486 	    combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3487 						HLF_STNC, highlight_stltermnc);
3488 #  endif
3489 # endif
3490 	}
3491     }
3492 # ifdef FEAT_STL_OPT
3493     highlight_ga.ga_len = hlcnt;
3494 # endif
3495 
3496 #endif // USER_HIGHLIGHT
3497 
3498     return OK;
3499 }
3500 
3501 static void highlight_list(void);
3502 static void highlight_list_two(int cnt, int attr);
3503 
3504 /*
3505  * Handle command line completion for :highlight command.
3506  */
3507     void
3508 set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3509 {
3510     char_u	*p;
3511 
3512     // Default: expand group names
3513     xp->xp_context = EXPAND_HIGHLIGHT;
3514     xp->xp_pattern = arg;
3515     include_link = 2;
3516     include_default = 1;
3517 
3518     // (part of) subcommand already typed
3519     if (*arg != NUL)
3520     {
3521 	p = skiptowhite(arg);
3522 	if (*p != NUL)			// past "default" or group name
3523 	{
3524 	    include_default = 0;
3525 	    if (STRNCMP("default", arg, p - arg) == 0)
3526 	    {
3527 		arg = skipwhite(p);
3528 		xp->xp_pattern = arg;
3529 		p = skiptowhite(arg);
3530 	    }
3531 	    if (*p != NUL)			// past group name
3532 	    {
3533 		include_link = 0;
3534 		if (arg[1] == 'i' && arg[0] == 'N')
3535 		    highlight_list();
3536 		if (STRNCMP("link", arg, p - arg) == 0
3537 			|| STRNCMP("clear", arg, p - arg) == 0)
3538 		{
3539 		    xp->xp_pattern = skipwhite(p);
3540 		    p = skiptowhite(xp->xp_pattern);
3541 		    if (*p != NUL)		// past first group name
3542 		    {
3543 			xp->xp_pattern = skipwhite(p);
3544 			p = skiptowhite(xp->xp_pattern);
3545 		    }
3546 		}
3547 		if (*p != NUL)			// past group name(s)
3548 		    xp->xp_context = EXPAND_NOTHING;
3549 	    }
3550 	}
3551     }
3552 }
3553 
3554 /*
3555  * List highlighting matches in a nice way.
3556  */
3557     static void
3558 highlight_list(void)
3559 {
3560     int		i;
3561 
3562     for (i = 10; --i >= 0; )
3563 	highlight_list_two(i, HL_ATTR(HLF_D));
3564     for (i = 40; --i >= 0; )
3565 	highlight_list_two(99, 0);
3566 }
3567 
3568     static void
3569 highlight_list_two(int cnt, int attr)
3570 {
3571     msg_puts_attr(&("N \bI \b!  \b"[cnt / 11]), attr);
3572     msg_clr_eos();
3573     out_flush();
3574     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3575 }
3576 
3577 /*
3578  * Function given to ExpandGeneric() to obtain the list of group names.
3579  */
3580     char_u *
3581 get_highlight_name(expand_T *xp UNUSED, int idx)
3582 {
3583     return get_highlight_name_ext(xp, idx, TRUE);
3584 }
3585 
3586 /*
3587  * Obtain a highlight group name.
3588  * When "skip_cleared" is TRUE don't return a cleared entry.
3589  */
3590     char_u *
3591 get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3592 {
3593     if (idx < 0)
3594 	return NULL;
3595 
3596     // Items are never removed from the table, skip the ones that were
3597     // cleared.
3598     if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3599 	return (char_u *)"";
3600 
3601     if (idx == highlight_ga.ga_len && include_none != 0)
3602 	return (char_u *)"none";
3603     if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3604 	return (char_u *)"default";
3605     if (idx == highlight_ga.ga_len + include_none + include_default
3606 							 && include_link != 0)
3607 	return (char_u *)"link";
3608     if (idx == highlight_ga.ga_len + include_none + include_default + 1
3609 							 && include_link != 0)
3610 	return (char_u *)"clear";
3611     if (idx >= highlight_ga.ga_len)
3612 	return NULL;
3613     return HL_TABLE()[idx].sg_name;
3614 }
3615 
3616 #if defined(FEAT_GUI) || defined(PROTO)
3617 /*
3618  * Free all the highlight group fonts.
3619  * Used when quitting for systems which need it.
3620  */
3621     void
3622 free_highlight_fonts(void)
3623 {
3624     int	    idx;
3625 
3626     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3627     {
3628 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
3629 	HL_TABLE()[idx].sg_font = NOFONT;
3630 # ifdef FEAT_XFONTSET
3631 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3632 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
3633 # endif
3634     }
3635 
3636     gui_mch_free_font(gui.norm_font);
3637 # ifdef FEAT_XFONTSET
3638     gui_mch_free_fontset(gui.fontset);
3639 # endif
3640 # ifndef FEAT_GUI_GTK
3641     gui_mch_free_font(gui.bold_font);
3642     gui_mch_free_font(gui.ital_font);
3643     gui_mch_free_font(gui.boldital_font);
3644 # endif
3645 }
3646 #endif
3647 
3648 
3649 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3650 
3651 # define SEARCH_HL_PRIORITY 0
3652 
3653 /*
3654  * Add match to the match list of window 'wp'.  The pattern 'pat' will be
3655  * highlighted with the group 'grp' with priority 'prio'.
3656  * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3657  * If no particular ID is desired, -1 must be specified for 'id'.
3658  * Return ID of added match, -1 on failure.
3659  */
3660     static int
3661 match_add(
3662     win_T	*wp,
3663     char_u	*grp,
3664     char_u	*pat,
3665     int		prio,
3666     int		id,
3667     list_T	*pos_list,
3668     char_u      *conceal_char UNUSED) // pointer to conceal replacement char
3669 {
3670     matchitem_T	*cur;
3671     matchitem_T	*prev;
3672     matchitem_T	*m;
3673     int		hlg_id;
3674     regprog_T	*regprog = NULL;
3675     int		rtype = SOME_VALID;
3676 
3677     if (*grp == NUL || (pat != NULL && *pat == NUL))
3678 	return -1;
3679     if (id < -1 || id == 0)
3680     {
3681 	semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
3682 									   id);
3683 	return -1;
3684     }
3685     if (id != -1)
3686     {
3687 	cur = wp->w_match_head;
3688 	while (cur != NULL)
3689 	{
3690 	    if (cur->id == id)
3691 	    {
3692 		semsg(_("E801: ID already taken: %d"), id);
3693 		return -1;
3694 	    }
3695 	    cur = cur->next;
3696 	}
3697     }
3698     if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3699     {
3700 	semsg(_(e_nogroup), grp);
3701 	return -1;
3702     }
3703     if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3704     {
3705 	semsg(_(e_invarg2), pat);
3706 	return -1;
3707     }
3708 
3709     // Find available match ID.
3710     while (id == -1)
3711     {
3712 	cur = wp->w_match_head;
3713 	while (cur != NULL && cur->id != wp->w_next_match_id)
3714 	    cur = cur->next;
3715 	if (cur == NULL)
3716 	    id = wp->w_next_match_id;
3717 	wp->w_next_match_id++;
3718     }
3719 
3720     // Build new match.
3721     m = ALLOC_CLEAR_ONE(matchitem_T);
3722     m->id = id;
3723     m->priority = prio;
3724     m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3725     m->hlg_id = hlg_id;
3726     m->match.regprog = regprog;
3727     m->match.rmm_ic = FALSE;
3728     m->match.rmm_maxcol = 0;
3729 # if defined(FEAT_CONCEAL)
3730     m->conceal_char = 0;
3731     if (conceal_char != NULL)
3732 	m->conceal_char = (*mb_ptr2char)(conceal_char);
3733 # endif
3734 
3735     // Set up position matches
3736     if (pos_list != NULL)
3737     {
3738 	linenr_T	toplnum = 0;
3739 	linenr_T	botlnum = 0;
3740 	listitem_T	*li;
3741 	int		i;
3742 
3743 	range_list_materialize(pos_list);
3744 	for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3745 							i++, li = li->li_next)
3746 	{
3747 	    linenr_T	lnum = 0;
3748 	    colnr_T	col = 0;
3749 	    int		len = 1;
3750 	    list_T	*subl;
3751 	    listitem_T	*subli;
3752 	    int		error = FALSE;
3753 
3754 	    if (li->li_tv.v_type == VAR_LIST)
3755 	    {
3756 		subl = li->li_tv.vval.v_list;
3757 		if (subl == NULL)
3758 		    goto fail;
3759 		subli = subl->lv_first;
3760 		if (subli == NULL)
3761 		    goto fail;
3762 		lnum = tv_get_number_chk(&subli->li_tv, &error);
3763 		if (error == TRUE)
3764 		    goto fail;
3765 		if (lnum == 0)
3766 		{
3767 		    --i;
3768 		    continue;
3769 		}
3770 		m->pos.pos[i].lnum = lnum;
3771 		subli = subli->li_next;
3772 		if (subli != NULL)
3773 		{
3774 		    col = tv_get_number_chk(&subli->li_tv, &error);
3775 		    if (error == TRUE)
3776 			goto fail;
3777 		    subli = subli->li_next;
3778 		    if (subli != NULL)
3779 		    {
3780 			len = tv_get_number_chk(&subli->li_tv, &error);
3781 			if (error == TRUE)
3782 			    goto fail;
3783 		    }
3784 		}
3785 		m->pos.pos[i].col = col;
3786 		m->pos.pos[i].len = len;
3787 	    }
3788 	    else if (li->li_tv.v_type == VAR_NUMBER)
3789 	    {
3790 		if (li->li_tv.vval.v_number == 0)
3791 		{
3792 		    --i;
3793 		    continue;
3794 		}
3795 		m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3796 		m->pos.pos[i].col = 0;
3797 		m->pos.pos[i].len = 0;
3798 	    }
3799 	    else
3800 	    {
3801 		emsg(_("E290: List or number required"));
3802 		goto fail;
3803 	    }
3804 	    if (toplnum == 0 || lnum < toplnum)
3805 		toplnum = lnum;
3806 	    if (botlnum == 0 || lnum >= botlnum)
3807 		botlnum = lnum + 1;
3808 	}
3809 
3810 	// Calculate top and bottom lines for redrawing area
3811 	if (toplnum != 0)
3812 	{
3813 	    if (wp->w_buffer->b_mod_set)
3814 	    {
3815 		if (wp->w_buffer->b_mod_top > toplnum)
3816 		    wp->w_buffer->b_mod_top = toplnum;
3817 		if (wp->w_buffer->b_mod_bot < botlnum)
3818 		    wp->w_buffer->b_mod_bot = botlnum;
3819 	    }
3820 	    else
3821 	    {
3822 		wp->w_buffer->b_mod_set = TRUE;
3823 		wp->w_buffer->b_mod_top = toplnum;
3824 		wp->w_buffer->b_mod_bot = botlnum;
3825 		wp->w_buffer->b_mod_xlines = 0;
3826 	    }
3827 	    m->pos.toplnum = toplnum;
3828 	    m->pos.botlnum = botlnum;
3829 	    rtype = VALID;
3830 	}
3831     }
3832 
3833     // Insert new match.  The match list is in ascending order with regard to
3834     // the match priorities.
3835     cur = wp->w_match_head;
3836     prev = cur;
3837     while (cur != NULL && prio >= cur->priority)
3838     {
3839 	prev = cur;
3840 	cur = cur->next;
3841     }
3842     if (cur == prev)
3843 	wp->w_match_head = m;
3844     else
3845 	prev->next = m;
3846     m->next = cur;
3847 
3848     redraw_win_later(wp, rtype);
3849     return id;
3850 
3851 fail:
3852     vim_free(m);
3853     return -1;
3854 }
3855 
3856 /*
3857  * Delete match with ID 'id' in the match list of window 'wp'.
3858  * Print error messages if 'perr' is TRUE.
3859  */
3860     static int
3861 match_delete(win_T *wp, int id, int perr)
3862 {
3863     matchitem_T	*cur = wp->w_match_head;
3864     matchitem_T	*prev = cur;
3865     int		rtype = SOME_VALID;
3866 
3867     if (id < 1)
3868     {
3869 	if (perr == TRUE)
3870 	    semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3871 									  id);
3872 	return -1;
3873     }
3874     while (cur != NULL && cur->id != id)
3875     {
3876 	prev = cur;
3877 	cur = cur->next;
3878     }
3879     if (cur == NULL)
3880     {
3881 	if (perr == TRUE)
3882 	    semsg(_("E803: ID not found: %d"), id);
3883 	return -1;
3884     }
3885     if (cur == prev)
3886 	wp->w_match_head = cur->next;
3887     else
3888 	prev->next = cur->next;
3889     vim_regfree(cur->match.regprog);
3890     vim_free(cur->pattern);
3891     if (cur->pos.toplnum != 0)
3892     {
3893 	if (wp->w_buffer->b_mod_set)
3894 	{
3895 	    if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3896 		wp->w_buffer->b_mod_top = cur->pos.toplnum;
3897 	    if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3898 		wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3899 	}
3900 	else
3901 	{
3902 	    wp->w_buffer->b_mod_set = TRUE;
3903 	    wp->w_buffer->b_mod_top = cur->pos.toplnum;
3904 	    wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3905 	    wp->w_buffer->b_mod_xlines = 0;
3906 	}
3907 	rtype = VALID;
3908     }
3909     vim_free(cur);
3910     redraw_win_later(wp, rtype);
3911     return 0;
3912 }
3913 
3914 /*
3915  * Delete all matches in the match list of window 'wp'.
3916  */
3917     void
3918 clear_matches(win_T *wp)
3919 {
3920     matchitem_T *m;
3921 
3922     while (wp->w_match_head != NULL)
3923     {
3924 	m = wp->w_match_head->next;
3925 	vim_regfree(wp->w_match_head->match.regprog);
3926 	vim_free(wp->w_match_head->pattern);
3927 	vim_free(wp->w_match_head);
3928 	wp->w_match_head = m;
3929     }
3930     redraw_win_later(wp, SOME_VALID);
3931 }
3932 
3933 /*
3934  * Get match from ID 'id' in window 'wp'.
3935  * Return NULL if match not found.
3936  */
3937     static matchitem_T *
3938 get_match(win_T *wp, int id)
3939 {
3940     matchitem_T *cur = wp->w_match_head;
3941 
3942     while (cur != NULL && cur->id != id)
3943 	cur = cur->next;
3944     return cur;
3945 }
3946 
3947 /*
3948  * Init for calling prepare_search_hl().
3949  */
3950     void
3951 init_search_hl(win_T *wp, match_T *search_hl)
3952 {
3953     matchitem_T *cur;
3954 
3955     // Setup for match and 'hlsearch' highlighting.  Disable any previous
3956     // match
3957     cur = wp->w_match_head;
3958     while (cur != NULL)
3959     {
3960 	cur->hl.rm = cur->match;
3961 	if (cur->hlg_id == 0)
3962 	    cur->hl.attr = 0;
3963 	else
3964 	    cur->hl.attr = syn_id2attr(cur->hlg_id);
3965 	cur->hl.buf = wp->w_buffer;
3966 	cur->hl.lnum = 0;
3967 	cur->hl.first_lnum = 0;
3968 # ifdef FEAT_RELTIME
3969 	// Set the time limit to 'redrawtime'.
3970 	profile_setlimit(p_rdt, &(cur->hl.tm));
3971 # endif
3972 	cur = cur->next;
3973     }
3974     search_hl->buf = wp->w_buffer;
3975     search_hl->lnum = 0;
3976     search_hl->first_lnum = 0;
3977     // time limit is set at the toplevel, for all windows
3978 }
3979 
3980 /*
3981  * If there is a match fill "shl" and return one.
3982  * Return zero otherwise.
3983  */
3984     static int
3985 next_search_hl_pos(
3986     match_T	    *shl,	// points to a match
3987     linenr_T	    lnum,
3988     posmatch_T	    *posmatch,	// match positions
3989     colnr_T	    mincol)	// minimal column for a match
3990 {
3991     int	    i;
3992     int	    found = -1;
3993 
3994     for (i = posmatch->cur; i < MAXPOSMATCH; i++)
3995     {
3996 	llpos_T	*pos = &posmatch->pos[i];
3997 
3998 	if (pos->lnum == 0)
3999 	    break;
4000 	if (pos->len == 0 && pos->col < mincol)
4001 	    continue;
4002 	if (pos->lnum == lnum)
4003 	{
4004 	    if (found >= 0)
4005 	    {
4006 		// if this match comes before the one at "found" then swap
4007 		// them
4008 		if (pos->col < posmatch->pos[found].col)
4009 		{
4010 		    llpos_T	tmp = *pos;
4011 
4012 		    *pos = posmatch->pos[found];
4013 		    posmatch->pos[found] = tmp;
4014 		}
4015 	    }
4016 	    else
4017 		found = i;
4018 	}
4019     }
4020     posmatch->cur = 0;
4021     if (found >= 0)
4022     {
4023 	colnr_T	start = posmatch->pos[found].col == 0
4024 					    ? 0 : posmatch->pos[found].col - 1;
4025 	colnr_T	end = posmatch->pos[found].col == 0
4026 				   ? MAXCOL : start + posmatch->pos[found].len;
4027 
4028 	shl->lnum = lnum;
4029 	shl->rm.startpos[0].lnum = 0;
4030 	shl->rm.startpos[0].col = start;
4031 	shl->rm.endpos[0].lnum = 0;
4032 	shl->rm.endpos[0].col = end;
4033 	shl->is_addpos = TRUE;
4034 	posmatch->cur = found + 1;
4035 	return 1;
4036     }
4037     return 0;
4038 }
4039 
4040 /*
4041  * Search for a next 'hlsearch' or match.
4042  * Uses shl->buf.
4043  * Sets shl->lnum and shl->rm contents.
4044  * Note: Assumes a previous match is always before "lnum", unless
4045  * shl->lnum is zero.
4046  * Careful: Any pointers for buffer lines will become invalid.
4047  */
4048     static void
4049 next_search_hl(
4050     win_T	    *win,
4051     match_T	    *search_hl,
4052     match_T	    *shl,	// points to search_hl or a match
4053     linenr_T	    lnum,
4054     colnr_T	    mincol,	// minimal column for a match
4055     matchitem_T	    *cur)	// to retrieve match positions if any
4056 {
4057     linenr_T	l;
4058     colnr_T	matchcol;
4059     long	nmatched;
4060     int		called_emsg_before = called_emsg;
4061 
4062     // for :{range}s/pat only highlight inside the range
4063     if (lnum < search_first_line || lnum > search_last_line)
4064     {
4065 	shl->lnum = 0;
4066 	return;
4067     }
4068 
4069     if (shl->lnum != 0)
4070     {
4071 	// Check for three situations:
4072 	// 1. If the "lnum" is below a previous match, start a new search.
4073 	// 2. If the previous match includes "mincol", use it.
4074 	// 3. Continue after the previous match.
4075 	l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4076 	if (lnum > l)
4077 	    shl->lnum = 0;
4078 	else if (lnum < l || shl->rm.endpos[0].col > mincol)
4079 	    return;
4080     }
4081 
4082     /*
4083      * Repeat searching for a match until one is found that includes "mincol"
4084      * or none is found in this line.
4085      */
4086     for (;;)
4087     {
4088 # ifdef FEAT_RELTIME
4089 	// Stop searching after passing the time limit.
4090 	if (profile_passed_limit(&(shl->tm)))
4091 	{
4092 	    shl->lnum = 0;		// no match found in time
4093 	    break;
4094 	}
4095 # endif
4096 	// Three situations:
4097 	// 1. No useful previous match: search from start of line.
4098 	// 2. Not Vi compatible or empty match: continue at next character.
4099 	//    Break the loop if this is beyond the end of the line.
4100 	// 3. Vi compatible searching: continue at end of previous match.
4101 	if (shl->lnum == 0)
4102 	    matchcol = 0;
4103 	else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4104 		|| (shl->rm.endpos[0].lnum == 0
4105 		    && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4106 	{
4107 	    char_u	*ml;
4108 
4109 	    matchcol = shl->rm.startpos[0].col;
4110 	    ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4111 	    if (*ml == NUL)
4112 	    {
4113 		++matchcol;
4114 		shl->lnum = 0;
4115 		break;
4116 	    }
4117 	    if (has_mbyte)
4118 		matchcol += mb_ptr2len(ml);
4119 	    else
4120 		++matchcol;
4121 	}
4122 	else
4123 	    matchcol = shl->rm.endpos[0].col;
4124 
4125 	shl->lnum = lnum;
4126 	if (shl->rm.regprog != NULL)
4127 	{
4128 	    // Remember whether shl->rm is using a copy of the regprog in
4129 	    // cur->match.
4130 	    int regprog_is_copy = (shl != search_hl && cur != NULL
4131 				&& shl == &cur->hl
4132 				&& cur->match.regprog == cur->hl.rm.regprog);
4133 	    int timed_out = FALSE;
4134 
4135 	    nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4136 		    matchcol,
4137 #ifdef FEAT_RELTIME
4138 		    &(shl->tm), &timed_out
4139 #else
4140 		    NULL, NULL
4141 #endif
4142 		    );
4143 	    // Copy the regprog, in case it got freed and recompiled.
4144 	    if (regprog_is_copy)
4145 		cur->match.regprog = cur->hl.rm.regprog;
4146 
4147 	    if (called_emsg > called_emsg_before || got_int || timed_out)
4148 	    {
4149 		// Error while handling regexp: stop using this regexp.
4150 		if (shl == search_hl)
4151 		{
4152 		    // don't free regprog in the match list, it's a copy
4153 		    vim_regfree(shl->rm.regprog);
4154 		    set_no_hlsearch(TRUE);
4155 		}
4156 		shl->rm.regprog = NULL;
4157 		shl->lnum = 0;
4158 		got_int = FALSE;  // avoid the "Type :quit to exit Vim" message
4159 		break;
4160 	    }
4161 	}
4162 	else if (cur != NULL)
4163 	    nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4164 	else
4165 	    nmatched = 0;
4166 	if (nmatched == 0)
4167 	{
4168 	    shl->lnum = 0;		// no match found
4169 	    break;
4170 	}
4171 	if (shl->rm.startpos[0].lnum > 0
4172 		|| shl->rm.startpos[0].col >= mincol
4173 		|| nmatched > 1
4174 		|| shl->rm.endpos[0].col > mincol)
4175 	{
4176 	    shl->lnum += shl->rm.startpos[0].lnum;
4177 	    break;			// useful match found
4178 	}
4179     }
4180 }
4181 
4182 /*
4183  * Advance to the match in window "wp" line "lnum" or past it.
4184  */
4185     void
4186 prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4187 {
4188     matchitem_T *cur;		// points to the match list
4189     match_T	*shl;		// points to search_hl or a match
4190     int		shl_flag;	// flag to indicate whether search_hl
4191 				// has been processed or not
4192     int		pos_inprogress;	// marks that position match search is
4193 				// in progress
4194     int		n;
4195 
4196     // When using a multi-line pattern, start searching at the top
4197     // of the window or just after a closed fold.
4198     // Do this both for search_hl and the match list.
4199     cur = wp->w_match_head;
4200     shl_flag = WIN_IS_POPUP(wp);  // skip search_hl in a popup window
4201     while (cur != NULL || shl_flag == FALSE)
4202     {
4203 	if (shl_flag == FALSE)
4204 	{
4205 	    shl = search_hl;
4206 	    shl_flag = TRUE;
4207 	}
4208 	else
4209 	    shl = &cur->hl;
4210 	if (shl->rm.regprog != NULL
4211 		&& shl->lnum == 0
4212 		&& re_multiline(shl->rm.regprog))
4213 	{
4214 	    if (shl->first_lnum == 0)
4215 	    {
4216 # ifdef FEAT_FOLDING
4217 		for (shl->first_lnum = lnum;
4218 			   shl->first_lnum > wp->w_topline; --shl->first_lnum)
4219 		    if (hasFoldingWin(wp, shl->first_lnum - 1,
4220 						      NULL, NULL, TRUE, NULL))
4221 			break;
4222 # else
4223 		shl->first_lnum = wp->w_topline;
4224 # endif
4225 	    }
4226 	    if (cur != NULL)
4227 		cur->pos.cur = 0;
4228 	    pos_inprogress = TRUE;
4229 	    n = 0;
4230 	    while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4231 					  || (cur != NULL && pos_inprogress)))
4232 	    {
4233 		next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4234 					       shl == search_hl ? NULL : cur);
4235 		pos_inprogress = cur == NULL || cur->pos.cur == 0
4236 							      ? FALSE : TRUE;
4237 		if (shl->lnum != 0)
4238 		{
4239 		    shl->first_lnum = shl->lnum
4240 				    + shl->rm.endpos[0].lnum
4241 				    - shl->rm.startpos[0].lnum;
4242 		    n = shl->rm.endpos[0].col;
4243 		}
4244 		else
4245 		{
4246 		    ++shl->first_lnum;
4247 		    n = 0;
4248 		}
4249 	    }
4250 	}
4251 	if (shl != search_hl && cur != NULL)
4252 	    cur = cur->next;
4253     }
4254 }
4255 
4256 /*
4257  * Prepare for 'hlsearch' and match highlighting in one window line.
4258  * Return TRUE if there is such highlighting and set "search_attr" to the
4259  * current highlight attribute.
4260  */
4261     int
4262 prepare_search_hl_line(
4263 	win_T	    *wp,
4264 	linenr_T    lnum,
4265 	colnr_T	    mincol,
4266 	char_u	    **line,
4267 	match_T	    *search_hl,
4268 	int	    *search_attr)
4269 {
4270     matchitem_T *cur;			// points to the match list
4271     match_T	*shl;			// points to search_hl or a match
4272     int		shl_flag;		// flag to indicate whether search_hl
4273 					// has been processed or not
4274     int		area_highlighting = FALSE;
4275 
4276     /*
4277      * Handle highlighting the last used search pattern and matches.
4278      * Do this for both search_hl and the match list.
4279      * Do not use search_hl in a popup window.
4280      */
4281     cur = wp->w_match_head;
4282     shl_flag = WIN_IS_POPUP(wp);
4283     while (cur != NULL || shl_flag == FALSE)
4284     {
4285 	if (shl_flag == FALSE)
4286 	{
4287 	    shl = search_hl;
4288 	    shl_flag = TRUE;
4289 	}
4290 	else
4291 	    shl = &cur->hl;
4292 	shl->startcol = MAXCOL;
4293 	shl->endcol = MAXCOL;
4294 	shl->attr_cur = 0;
4295 	shl->is_addpos = FALSE;
4296 	if (cur != NULL)
4297 	    cur->pos.cur = 0;
4298 	next_search_hl(wp, search_hl, shl, lnum, mincol,
4299 						shl == search_hl ? NULL : cur);
4300 
4301 	// Need to get the line again, a multi-line regexp may have made it
4302 	// invalid.
4303 	*line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4304 
4305 	if (shl->lnum != 0 && shl->lnum <= lnum)
4306 	{
4307 	    if (shl->lnum == lnum)
4308 		shl->startcol = shl->rm.startpos[0].col;
4309 	    else
4310 		shl->startcol = 0;
4311 	    if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4312 						- shl->rm.startpos[0].lnum)
4313 		shl->endcol = shl->rm.endpos[0].col;
4314 	    else
4315 		shl->endcol = MAXCOL;
4316 	    // Highlight one character for an empty match.
4317 	    if (shl->startcol == shl->endcol)
4318 	    {
4319 		if (has_mbyte && (*line)[shl->endcol] != NUL)
4320 		    shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4321 		else
4322 		    ++shl->endcol;
4323 	    }
4324 	    if ((long)shl->startcol < mincol)  // match at leftcol
4325 	    {
4326 		shl->attr_cur = shl->attr;
4327 		*search_attr = shl->attr;
4328 	    }
4329 	    area_highlighting = TRUE;
4330 	}
4331 	if (shl != search_hl && cur != NULL)
4332 	    cur = cur->next;
4333     }
4334     return area_highlighting;
4335 }
4336 
4337 /*
4338  * For a position in a line: Check for start/end of 'hlsearch' and other
4339  * matches.
4340  * After end, check for start/end of next match.
4341  * When another match, have to check for start again.
4342  * Watch out for matching an empty string!
4343  * Return the updated search_attr.
4344  */
4345     int
4346 update_search_hl(
4347 	win_T	    *wp,
4348 	linenr_T    lnum,
4349 	colnr_T	    col,
4350 	char_u	    **line,
4351 	match_T	    *search_hl,
4352 	int	    *has_match_conc UNUSED,
4353 	int	    *match_conc UNUSED,
4354 	int	    did_line_attr,
4355 	int	    lcs_eol_one)
4356 {
4357     matchitem_T *cur;		    // points to the match list
4358     match_T	*shl;		    // points to search_hl or a match
4359     int		shl_flag;	    // flag to indicate whether search_hl
4360 				    // has been processed or not
4361     int		pos_inprogress;	    // marks that position match search is in
4362 				    // progress
4363     int		search_attr = 0;
4364 
4365 
4366     // Do this for 'search_hl' and the match list (ordered by priority).
4367     cur = wp->w_match_head;
4368     shl_flag = WIN_IS_POPUP(wp);
4369     while (cur != NULL || shl_flag == FALSE)
4370     {
4371 	if (shl_flag == FALSE
4372 		&& ((cur != NULL
4373 			&& cur->priority > SEARCH_HL_PRIORITY)
4374 		    || cur == NULL))
4375 	{
4376 	    shl = search_hl;
4377 	    shl_flag = TRUE;
4378 	}
4379 	else
4380 	    shl = &cur->hl;
4381 	if (cur != NULL)
4382 	    cur->pos.cur = 0;
4383 	pos_inprogress = TRUE;
4384 	while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4385 	{
4386 	    if (shl->startcol != MAXCOL
4387 		    && col >= shl->startcol
4388 		    && col < shl->endcol)
4389 	    {
4390 		int next_col = col + mb_ptr2len(*line + col);
4391 
4392 		if (shl->endcol < next_col)
4393 		    shl->endcol = next_col;
4394 		shl->attr_cur = shl->attr;
4395 # ifdef FEAT_CONCEAL
4396 		// Match with the "Conceal" group results in hiding
4397 		// the match.
4398 		if (cur != NULL
4399 			&& shl != search_hl
4400 			&& syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4401 		{
4402 		    *has_match_conc = col == shl->startcol ? 2 : 1;
4403 		    *match_conc = cur->conceal_char;
4404 		}
4405 		else
4406 		    *has_match_conc = *match_conc = 0;
4407 # endif
4408 	    }
4409 	    else if (col == shl->endcol)
4410 	    {
4411 		shl->attr_cur = 0;
4412 		next_search_hl(wp, search_hl, shl, lnum, col,
4413 					       shl == search_hl ? NULL : cur);
4414 		pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4415 
4416 		// Need to get the line again, a multi-line regexp may have
4417 		// made it invalid.
4418 		*line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4419 
4420 		if (shl->lnum == lnum)
4421 		{
4422 		    shl->startcol = shl->rm.startpos[0].col;
4423 		    if (shl->rm.endpos[0].lnum == 0)
4424 			shl->endcol = shl->rm.endpos[0].col;
4425 		    else
4426 			shl->endcol = MAXCOL;
4427 
4428 		    if (shl->startcol == shl->endcol)
4429 		    {
4430 			// highlight empty match, try again after
4431 			// it
4432 			if (has_mbyte)
4433 			    shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4434 			else
4435 			    ++shl->endcol;
4436 		    }
4437 
4438 		    // Loop to check if the match starts at the
4439 		    // current position
4440 		    continue;
4441 		}
4442 	    }
4443 	    break;
4444 	}
4445 	if (shl != search_hl && cur != NULL)
4446 	    cur = cur->next;
4447     }
4448 
4449     // Use attributes from match with highest priority among 'search_hl' and
4450     // the match list.
4451     cur = wp->w_match_head;
4452     shl_flag = WIN_IS_POPUP(wp);
4453     while (cur != NULL || shl_flag == FALSE)
4454     {
4455 	if (shl_flag == FALSE
4456 		&& ((cur != NULL
4457 			&& cur->priority > SEARCH_HL_PRIORITY)
4458 		    || cur == NULL))
4459 	{
4460 	    shl = search_hl;
4461 	    shl_flag = TRUE;
4462 	}
4463 	else
4464 	    shl = &cur->hl;
4465 	if (shl->attr_cur != 0)
4466 	    search_attr = shl->attr_cur;
4467 	if (shl != search_hl && cur != NULL)
4468 	    cur = cur->next;
4469     }
4470     // Only highlight one character after the last column.
4471     if (*(*line + col) == NUL && (did_line_attr >= 1
4472 				       || (wp->w_p_list && lcs_eol_one == -1)))
4473 	search_attr = 0;
4474     return search_attr;
4475 }
4476 
4477     int
4478 get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4479 {
4480     long	prevcol = curcol;
4481     int		prevcol_hl_flag = FALSE;
4482     matchitem_T *cur;			// points to the match list
4483 
4484     // we're not really at that column when skipping some text
4485     if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4486 	++prevcol;
4487 
4488     if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4489 	prevcol_hl_flag = TRUE;
4490     else
4491     {
4492 	cur = wp->w_match_head;
4493 	while (cur != NULL)
4494 	{
4495 	    if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4496 	    {
4497 		prevcol_hl_flag = TRUE;
4498 		break;
4499 	    }
4500 	    cur = cur->next;
4501 	}
4502     }
4503     return prevcol_hl_flag;
4504 }
4505 
4506 /*
4507  * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4508  * or match highlighting.
4509  */
4510     void
4511 get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4512 {
4513     matchitem_T *cur;			// points to the match list
4514     match_T	*shl;			// points to search_hl or a match
4515     int		shl_flag;		// flag to indicate whether search_hl
4516 					// has been processed or not
4517 
4518     cur = wp->w_match_head;
4519     shl_flag = WIN_IS_POPUP(wp);
4520     while (cur != NULL || shl_flag == FALSE)
4521     {
4522 	if (shl_flag == FALSE
4523 		&& ((cur != NULL
4524 			&& cur->priority > SEARCH_HL_PRIORITY)
4525 		    || cur == NULL))
4526 	{
4527 	    shl = search_hl;
4528 	    shl_flag = TRUE;
4529 	}
4530 	else
4531 	    shl = &cur->hl;
4532 	if (col - 1 == (long)shl->startcol
4533 		&& (shl == search_hl || !shl->is_addpos))
4534 	    *char_attr = shl->attr;
4535 	if (shl != search_hl && cur != NULL)
4536 	    cur = cur->next;
4537     }
4538 }
4539 
4540 #endif // FEAT_SEARCH_EXTRA
4541 
4542 #if defined(FEAT_EVAL) || defined(PROTO)
4543 # ifdef FEAT_SEARCH_EXTRA
4544     static int
4545 matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4546 {
4547     dictitem_T *di;
4548 
4549     if (tv->v_type != VAR_DICT)
4550     {
4551 	emsg(_(e_dictreq));
4552 	return FAIL;
4553     }
4554 
4555     if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4556 	*conceal_char = dict_get_string(tv->vval.v_dict,
4557 						   (char_u *)"conceal", FALSE);
4558 
4559     if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4560     {
4561 	*win = find_win_by_nr_or_id(&di->di_tv);
4562 	if (*win == NULL)
4563 	{
4564 	    emsg(_(e_invalwindow));
4565 	    return FAIL;
4566 	}
4567     }
4568 
4569     return OK;
4570 }
4571 #endif
4572 
4573 /*
4574  * "clearmatches()" function
4575  */
4576     void
4577 f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4578 {
4579 #ifdef FEAT_SEARCH_EXTRA
4580     win_T   *win = get_optional_window(argvars, 0);
4581 
4582     if (win != NULL)
4583 	clear_matches(win);
4584 #endif
4585 }
4586 
4587 /*
4588  * "getmatches()" function
4589  */
4590     void
4591 f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4592 {
4593 # ifdef FEAT_SEARCH_EXTRA
4594     dict_T	*dict;
4595     matchitem_T	*cur;
4596     int		i;
4597     win_T	*win = get_optional_window(argvars, 0);
4598 
4599     if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4600 	return;
4601 
4602     cur = win->w_match_head;
4603     while (cur != NULL)
4604     {
4605 	dict = dict_alloc();
4606 	if (dict == NULL)
4607 	    return;
4608 	if (cur->match.regprog == NULL)
4609 	{
4610 	    // match added with matchaddpos()
4611 	    for (i = 0; i < MAXPOSMATCH; ++i)
4612 	    {
4613 		llpos_T	*llpos;
4614 		char	buf[30];  // use 30 to avoid compiler warning
4615 		list_T	*l;
4616 
4617 		llpos = &cur->pos.pos[i];
4618 		if (llpos->lnum == 0)
4619 		    break;
4620 		l = list_alloc();
4621 		if (l == NULL)
4622 		    break;
4623 		list_append_number(l, (varnumber_T)llpos->lnum);
4624 		if (llpos->col > 0)
4625 		{
4626 		    list_append_number(l, (varnumber_T)llpos->col);
4627 		    list_append_number(l, (varnumber_T)llpos->len);
4628 		}
4629 		sprintf(buf, "pos%d", i + 1);
4630 		dict_add_list(dict, buf, l);
4631 	    }
4632 	}
4633 	else
4634 	{
4635 	    dict_add_string(dict, "pattern", cur->pattern);
4636 	}
4637 	dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4638 	dict_add_number(dict, "priority", (long)cur->priority);
4639 	dict_add_number(dict, "id", (long)cur->id);
4640 #  if defined(FEAT_CONCEAL)
4641 	if (cur->conceal_char)
4642 	{
4643 	    char_u buf[MB_MAXBYTES + 1];
4644 
4645 	    buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4646 	    dict_add_string(dict, "conceal", (char_u *)&buf);
4647 	}
4648 #  endif
4649 	list_append_dict(rettv->vval.v_list, dict);
4650 	cur = cur->next;
4651     }
4652 # endif
4653 }
4654 
4655 /*
4656  * "setmatches()" function
4657  */
4658     void
4659 f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4660 {
4661 #ifdef FEAT_SEARCH_EXTRA
4662     list_T	*l;
4663     listitem_T	*li;
4664     dict_T	*d;
4665     list_T	*s = NULL;
4666     win_T	*win = get_optional_window(argvars, 1);
4667 
4668     rettv->vval.v_number = -1;
4669     if (argvars[0].v_type != VAR_LIST)
4670     {
4671 	emsg(_(e_listreq));
4672 	return;
4673     }
4674     if (win == NULL)
4675 	return;
4676 
4677     if ((l = argvars[0].vval.v_list) != NULL)
4678     {
4679 	// To some extent make sure that we are dealing with a list from
4680 	// "getmatches()".
4681 	li = l->lv_first;
4682 	while (li != NULL)
4683 	{
4684 	    if (li->li_tv.v_type != VAR_DICT
4685 		    || (d = li->li_tv.vval.v_dict) == NULL)
4686 	    {
4687 		emsg(_(e_invarg));
4688 		return;
4689 	    }
4690 	    if (!(dict_find(d, (char_u *)"group", -1) != NULL
4691 			&& (dict_find(d, (char_u *)"pattern", -1) != NULL
4692 			    || dict_find(d, (char_u *)"pos1", -1) != NULL)
4693 			&& dict_find(d, (char_u *)"priority", -1) != NULL
4694 			&& dict_find(d, (char_u *)"id", -1) != NULL))
4695 	    {
4696 		emsg(_(e_invarg));
4697 		return;
4698 	    }
4699 	    li = li->li_next;
4700 	}
4701 
4702 	clear_matches(win);
4703 	li = l->lv_first;
4704 	while (li != NULL)
4705 	{
4706 	    int		i = 0;
4707 	    char	buf[30];  // use 30 to avoid compiler warning
4708 	    dictitem_T  *di;
4709 	    char_u	*group;
4710 	    int		priority;
4711 	    int		id;
4712 	    char_u	*conceal;
4713 
4714 	    d = li->li_tv.vval.v_dict;
4715 	    if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4716 	    {
4717 		if (s == NULL)
4718 		{
4719 		    s = list_alloc();
4720 		    if (s == NULL)
4721 			return;
4722 		}
4723 
4724 		// match from matchaddpos()
4725 		for (i = 1; i < 9; i++)
4726 		{
4727 		    sprintf((char *)buf, (char *)"pos%d", i);
4728 		    if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4729 		    {
4730 			if (di->di_tv.v_type != VAR_LIST)
4731 			    return;
4732 
4733 			list_append_tv(s, &di->di_tv);
4734 			s->lv_refcount++;
4735 		    }
4736 		    else
4737 			break;
4738 		}
4739 	    }
4740 
4741 	    group = dict_get_string(d, (char_u *)"group", TRUE);
4742 	    priority = (int)dict_get_number(d, (char_u *)"priority");
4743 	    id = (int)dict_get_number(d, (char_u *)"id");
4744 	    conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4745 			      ? dict_get_string(d, (char_u *)"conceal", TRUE)
4746 			      : NULL;
4747 	    if (i == 0)
4748 	    {
4749 		match_add(win, group,
4750 		    dict_get_string(d, (char_u *)"pattern", FALSE),
4751 		    priority, id, NULL, conceal);
4752 	    }
4753 	    else
4754 	    {
4755 		match_add(win, group, NULL, priority, id, s, conceal);
4756 		list_unref(s);
4757 		s = NULL;
4758 	    }
4759 	    vim_free(group);
4760 	    vim_free(conceal);
4761 
4762 	    li = li->li_next;
4763 	}
4764 	rettv->vval.v_number = 0;
4765     }
4766 #endif
4767 }
4768 
4769 /*
4770  * "matchadd()" function
4771  */
4772     void
4773 f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4774 {
4775 # ifdef FEAT_SEARCH_EXTRA
4776     char_u	buf[NUMBUFLEN];
4777     char_u	*grp = tv_get_string_buf_chk(&argvars[0], buf);	// group
4778     char_u	*pat = tv_get_string_buf_chk(&argvars[1], buf);	// pattern
4779     int		prio = 10;	// default priority
4780     int		id = -1;
4781     int		error = FALSE;
4782     char_u	*conceal_char = NULL;
4783     win_T	*win = curwin;
4784 
4785     rettv->vval.v_number = -1;
4786 
4787     if (grp == NULL || pat == NULL)
4788 	return;
4789     if (argvars[2].v_type != VAR_UNKNOWN)
4790     {
4791 	prio = (int)tv_get_number_chk(&argvars[2], &error);
4792 	if (argvars[3].v_type != VAR_UNKNOWN)
4793 	{
4794 	    id = (int)tv_get_number_chk(&argvars[3], &error);
4795 	    if (argvars[4].v_type != VAR_UNKNOWN
4796 		&& matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4797 		return;
4798 	}
4799     }
4800     if (error == TRUE)
4801 	return;
4802     if (id >= 1 && id <= 3)
4803     {
4804 	semsg(_("E798: ID is reserved for \":match\": %d"), id);
4805 	return;
4806     }
4807 
4808     rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4809 								conceal_char);
4810 # endif
4811 }
4812 
4813 /*
4814  * "matchaddpos()" function
4815  */
4816     void
4817 f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4818 {
4819 # ifdef FEAT_SEARCH_EXTRA
4820     char_u	buf[NUMBUFLEN];
4821     char_u	*group;
4822     int		prio = 10;
4823     int		id = -1;
4824     int		error = FALSE;
4825     list_T	*l;
4826     char_u	*conceal_char = NULL;
4827     win_T	*win = curwin;
4828 
4829     rettv->vval.v_number = -1;
4830 
4831     group = tv_get_string_buf_chk(&argvars[0], buf);
4832     if (group == NULL)
4833 	return;
4834 
4835     if (argvars[1].v_type != VAR_LIST)
4836     {
4837 	semsg(_(e_listarg), "matchaddpos()");
4838 	return;
4839     }
4840     l = argvars[1].vval.v_list;
4841     if (l == NULL)
4842 	return;
4843 
4844     if (argvars[2].v_type != VAR_UNKNOWN)
4845     {
4846 	prio = (int)tv_get_number_chk(&argvars[2], &error);
4847 	if (argvars[3].v_type != VAR_UNKNOWN)
4848 	{
4849 	    id = (int)tv_get_number_chk(&argvars[3], &error);
4850 
4851 	    if (argvars[4].v_type != VAR_UNKNOWN
4852 		&& matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4853 		return;
4854 	}
4855     }
4856     if (error == TRUE)
4857 	return;
4858 
4859     // id == 3 is ok because matchaddpos() is supposed to substitute :3match
4860     if (id == 1 || id == 2)
4861     {
4862 	semsg(_("E798: ID is reserved for \":match\": %d"), id);
4863 	return;
4864     }
4865 
4866     rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4867 								conceal_char);
4868 # endif
4869 }
4870 
4871 /*
4872  * "matcharg()" function
4873  */
4874     void
4875 f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4876 {
4877     if (rettv_list_alloc(rettv) == OK)
4878     {
4879 # ifdef FEAT_SEARCH_EXTRA
4880 	int	    id = (int)tv_get_number(&argvars[0]);
4881 	matchitem_T *m;
4882 
4883 	if (id >= 1 && id <= 3)
4884 	{
4885 	    if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4886 	    {
4887 		list_append_string(rettv->vval.v_list,
4888 						syn_id2name(m->hlg_id), -1);
4889 		list_append_string(rettv->vval.v_list, m->pattern, -1);
4890 	    }
4891 	    else
4892 	    {
4893 		list_append_string(rettv->vval.v_list, NULL, -1);
4894 		list_append_string(rettv->vval.v_list, NULL, -1);
4895 	    }
4896 	}
4897 # endif
4898     }
4899 }
4900 
4901 /*
4902  * "matchdelete()" function
4903  */
4904     void
4905 f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4906 {
4907 # ifdef FEAT_SEARCH_EXTRA
4908     win_T   *win = get_optional_window(argvars, 1);
4909 
4910     if (win == NULL)
4911 	rettv->vval.v_number = -1;
4912     else
4913 	rettv->vval.v_number = match_delete(win,
4914 				       (int)tv_get_number(&argvars[0]), TRUE);
4915 # endif
4916 }
4917 #endif
4918 
4919 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4920 /*
4921  * ":[N]match {group} {pattern}"
4922  * Sets nextcmd to the start of the next command, if any.  Also called when
4923  * skipping commands to find the next command.
4924  */
4925     void
4926 ex_match(exarg_T *eap)
4927 {
4928     char_u	*p;
4929     char_u	*g = NULL;
4930     char_u	*end;
4931     int		c;
4932     int		id;
4933 
4934     if (eap->line2 <= 3)
4935 	id = eap->line2;
4936     else
4937     {
4938 	emsg(_(e_invcmd));
4939 	return;
4940     }
4941 
4942     // First clear any old pattern.
4943     if (!eap->skip)
4944 	match_delete(curwin, id, FALSE);
4945 
4946     if (ends_excmd(*eap->arg))
4947 	end = eap->arg;
4948     else if ((STRNICMP(eap->arg, "none", 4) == 0
4949 		&& (VIM_ISWHITE(eap->arg[4]) || ends_excmd(eap->arg[4]))))
4950 	end = eap->arg + 4;
4951     else
4952     {
4953 	p = skiptowhite(eap->arg);
4954 	if (!eap->skip)
4955 	    g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4956 	p = skipwhite(p);
4957 	if (*p == NUL)
4958 	{
4959 	    // There must be two arguments.
4960 	    vim_free(g);
4961 	    semsg(_(e_invarg2), eap->arg);
4962 	    return;
4963 	}
4964 	end = skip_regexp(p + 1, *p, TRUE, NULL);
4965 	if (!eap->skip)
4966 	{
4967 	    if (*end != NUL && !ends_excmd(*skipwhite(end + 1)))
4968 	    {
4969 		vim_free(g);
4970 		eap->errmsg = e_trailing;
4971 		return;
4972 	    }
4973 	    if (*end != *p)
4974 	    {
4975 		vim_free(g);
4976 		semsg(_(e_invarg2), p);
4977 		return;
4978 	    }
4979 
4980 	    c = *end;
4981 	    *end = NUL;
4982 	    match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4983 	    vim_free(g);
4984 	    *end = c;
4985 	}
4986     }
4987     eap->nextcmd = find_nextcmd(end);
4988 }
4989 #endif
4990