xref: /vim-8.2.3635/src/highlight.c (revision a0122dcd)
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  */
13 
14 #include "vim.h"
15 
16 #define SG_TERM		1	// term has been set
17 #define SG_CTERM	2	// cterm has been set
18 #define SG_GUI		4	// gui has been set
19 #define SG_LINK		8	// link has been set
20 
21 /*
22  * The "term", "cterm" and "gui" arguments can be any combination of the
23  * following names, separated by commas (but no spaces!).
24  */
25 static char *(hl_name_table[]) =
26     {"bold", "standout", "underline", "undercurl",
27       "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
28 static int hl_attr_table[] =
29     {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
30 #define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
31 
32 /*
33  * Structure that stores information about a highlight group.
34  * The ID of a highlight group is also called group ID.  It is the index in
35  * the highlight_ga array PLUS ONE.
36  */
37 typedef struct
38 {
39     char_u	*sg_name;	// highlight group name
40     char_u	*sg_name_u;	// uppercase of sg_name
41     int		sg_cleared;	// "hi clear" was used
42 // for normal terminals
43     int		sg_term;	// "term=" highlighting attributes
44     char_u	*sg_start;	// terminal string for start highl
45     char_u	*sg_stop;	// terminal string for stop highl
46     int		sg_term_attr;	// Screen attr for term mode
47 // for color terminals
48     int		sg_cterm;	// "cterm=" highlighting attr
49     int		sg_cterm_bold;	// bold attr was set for light color
50     int		sg_cterm_fg;	// terminal fg color number + 1
51     int		sg_cterm_bg;	// terminal bg color number + 1
52     int		sg_cterm_ul;	// terminal ul 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     guicolor_T	sg_gui_sp;	// GUI special color handle
59 #endif
60 #ifdef FEAT_GUI
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_deflink;	// default link; restored in highlight_clear()
77     int		sg_set;		// combination of SG_* flags
78 #ifdef FEAT_EVAL
79     sctx_T	sg_deflink_sctx;  // script where the default link was set
80     sctx_T	sg_script_ctx;	// script in which the group was last set
81 #endif
82 } hl_group_T;
83 
84 // highlight groups for 'highlight' option
85 static garray_T highlight_ga;
86 #define HL_TABLE()	((hl_group_T *)((highlight_ga.ga_data)))
87 
88 /*
89  * An attribute number is the index in attr_table plus ATTR_OFF.
90  */
91 #define ATTR_OFF (HL_ALL + 1)
92 
93 static void syn_unadd_group(void);
94 static void set_hl_attr(int idx);
95 static void highlight_list_one(int id);
96 static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
97 static int syn_add_group(char_u *name);
98 static int hl_has_settings(int idx, int check_link);
99 static void highlight_clear(int idx);
100 
101 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
102 static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
103 #endif
104 #ifdef FEAT_GUI
105 static int  set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
106 static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
107 #endif
108 
109 /*
110  * The default highlight groups.  These are compiled-in for fast startup and
111  * they still work when the runtime files can't be found.
112  * When making changes here, also change runtime/colors/default.vim!
113  * The #ifdefs are needed to reduce the amount of static data.  Helps to make
114  * the 16 bit DOS (museum) version compile.
115  */
116 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
117 # define CENT(a, b) b
118 #else
119 # define CENT(a, b) a
120 #endif
121 static char *(highlight_init_both[]) = {
122     CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
123 	 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
124     CENT("IncSearch term=reverse cterm=reverse",
125 	 "IncSearch term=reverse cterm=reverse gui=reverse"),
126     CENT("ModeMsg term=bold cterm=bold",
127 	 "ModeMsg term=bold cterm=bold gui=bold"),
128     CENT("NonText term=bold ctermfg=Blue",
129 	 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
130     CENT("StatusLine term=reverse,bold cterm=reverse,bold",
131 	 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
132     CENT("StatusLineNC term=reverse cterm=reverse",
133 	 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
134     "default link EndOfBuffer NonText",
135     CENT("VertSplit term=reverse cterm=reverse",
136 	 "VertSplit term=reverse cterm=reverse gui=reverse"),
137 #ifdef FEAT_CLIPBOARD
138     CENT("VisualNOS term=underline,bold cterm=underline,bold",
139 	 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
140 #endif
141 #ifdef FEAT_DIFF
142     CENT("DiffText term=reverse cterm=bold ctermbg=Red",
143 	 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
144 #endif
145     CENT("PmenuSbar ctermbg=Grey",
146 	 "PmenuSbar ctermbg=Grey guibg=Grey"),
147     CENT("TabLineSel term=bold cterm=bold",
148 	 "TabLineSel term=bold cterm=bold gui=bold"),
149     CENT("TabLineFill term=reverse cterm=reverse",
150 	 "TabLineFill term=reverse cterm=reverse gui=reverse"),
151 #ifdef FEAT_GUI
152     "Cursor guibg=fg guifg=bg",
153     "lCursor guibg=fg guifg=bg", // should be different, but what?
154 #endif
155     "default link QuickFixLine Search",
156     CENT("Normal cterm=NONE", "Normal gui=NONE"),
157     NULL
158 };
159 
160 // Default colors only used with a light background.
161 static char *(highlight_init_light[]) = {
162     CENT("Directory term=bold ctermfg=DarkBlue",
163 	 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
164     CENT("LineNr term=underline ctermfg=Brown",
165 	 "LineNr term=underline ctermfg=Brown guifg=Brown"),
166     CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
167 	 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
168     CENT("MoreMsg term=bold ctermfg=DarkGreen",
169 	 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
170     CENT("Question term=standout ctermfg=DarkGreen",
171 	 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
172     CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
173 	 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
174 #ifdef FEAT_SPELL
175     CENT("SpellBad term=reverse ctermbg=LightRed",
176 	 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
177     CENT("SpellCap term=reverse ctermbg=LightBlue",
178 	 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
179     CENT("SpellRare term=reverse ctermbg=LightMagenta",
180 	 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
181     CENT("SpellLocal term=underline ctermbg=Cyan",
182 	 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
183 #endif
184     CENT("PmenuThumb ctermbg=Black",
185 	 "PmenuThumb ctermbg=Black guibg=Black"),
186     CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
187 	 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
188     CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
189 	 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
190     CENT("SpecialKey term=bold ctermfg=DarkBlue",
191 	 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
192     CENT("Title term=bold ctermfg=DarkMagenta",
193 	 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
194     CENT("WarningMsg term=standout ctermfg=DarkRed",
195 	 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
196 #ifdef FEAT_WILDMENU
197     CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
198 	 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
199 #endif
200 #ifdef FEAT_FOLDING
201     CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
202 	 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
203     CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
204 	 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
205 #endif
206 #ifdef FEAT_SIGNS
207     CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
208 	 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
209 #endif
210     CENT("Visual term=reverse",
211 	 "Visual term=reverse guibg=LightGrey"),
212 #ifdef FEAT_DIFF
213     CENT("DiffAdd term=bold ctermbg=LightBlue",
214 	 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
215     CENT("DiffChange term=bold ctermbg=LightMagenta",
216 	 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
217     CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
218 	 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
219 #endif
220     CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
221 	 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
222 #ifdef FEAT_SYN_HL
223     CENT("CursorColumn term=reverse ctermbg=LightGrey",
224 	 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
225     CENT("CursorLine term=underline cterm=underline",
226 	 "CursorLine term=underline cterm=underline guibg=Grey90"),
227     CENT("ColorColumn term=reverse ctermbg=LightRed",
228 	 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
229 #endif
230 #ifdef FEAT_CONCEAL
231     CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
232 	 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
233 #endif
234     CENT("MatchParen term=reverse ctermbg=Cyan",
235 	 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
236 #ifdef FEAT_TERMINAL
237     CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
238 	 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
239     CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
240 	 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
241 #endif
242 #ifdef FEAT_MENU
243     CENT("ToolbarLine term=underline ctermbg=LightGrey",
244 	 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
245     CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
246 	 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
247 #endif
248     NULL
249 };
250 
251 // Default colors only used with a dark background.
252 static char *(highlight_init_dark[]) = {
253     CENT("Directory term=bold ctermfg=LightCyan",
254 	 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
255     CENT("LineNr term=underline ctermfg=Yellow",
256 	 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
257     CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
258 	 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
259     CENT("MoreMsg term=bold ctermfg=LightGreen",
260 	 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
261     CENT("Question term=standout ctermfg=LightGreen",
262 	 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
263     CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
264 	 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
265     CENT("SpecialKey term=bold ctermfg=LightBlue",
266 	 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
267 #ifdef FEAT_SPELL
268     CENT("SpellBad term=reverse ctermbg=Red",
269 	 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
270     CENT("SpellCap term=reverse ctermbg=Blue",
271 	 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
272     CENT("SpellRare term=reverse ctermbg=Magenta",
273 	 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
274     CENT("SpellLocal term=underline ctermbg=Cyan",
275 	 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
276 #endif
277     CENT("PmenuThumb ctermbg=White",
278 	 "PmenuThumb ctermbg=White guibg=White"),
279     CENT("Pmenu ctermbg=Magenta ctermfg=Black",
280 	 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
281     CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
282 	 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
283     CENT("Title term=bold ctermfg=LightMagenta",
284 	 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
285     CENT("WarningMsg term=standout ctermfg=LightRed",
286 	 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
287 #ifdef FEAT_WILDMENU
288     CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
289 	 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
290 #endif
291 #ifdef FEAT_FOLDING
292     CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
293 	 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
294     CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
295 	 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
296 #endif
297 #ifdef FEAT_SIGNS
298     CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
299 	 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
300 #endif
301     CENT("Visual term=reverse",
302 	 "Visual term=reverse guibg=DarkGrey"),
303 #ifdef FEAT_DIFF
304     CENT("DiffAdd term=bold ctermbg=DarkBlue",
305 	 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
306     CENT("DiffChange term=bold ctermbg=DarkMagenta",
307 	 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
308     CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
309 	 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
310 #endif
311     CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
312 	 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
313 #ifdef FEAT_SYN_HL
314     CENT("CursorColumn term=reverse ctermbg=DarkGrey",
315 	 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
316     CENT("CursorLine term=underline cterm=underline",
317 	 "CursorLine term=underline cterm=underline guibg=Grey40"),
318     CENT("ColorColumn term=reverse ctermbg=DarkRed",
319 	 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
320 #endif
321     CENT("MatchParen term=reverse ctermbg=DarkCyan",
322 	 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
323 #ifdef FEAT_CONCEAL
324     CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
325 	 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
326 #endif
327 #ifdef FEAT_TERMINAL
328     CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
329 	 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
330     CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
331 	 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
332 #endif
333 #ifdef FEAT_MENU
334     CENT("ToolbarLine term=underline ctermbg=DarkGrey",
335 	 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
336     CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
337 	 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
338 #endif
339     NULL
340 };
341 
342 /*
343  * Returns the number of highlight groups.
344  */
345     int
346 highlight_num_groups(void)
347 {
348     return highlight_ga.ga_len;
349 }
350 
351 /*
352  * Returns the name of a highlight group.
353  */
354     char_u *
355 highlight_group_name(int id)
356 {
357     return HL_TABLE()[id].sg_name;
358 }
359 
360 /*
361  * Returns the ID of the link to a highlight group.
362  */
363     int
364 highlight_link_id(int id)
365 {
366     return HL_TABLE()[id].sg_link;
367 }
368 
369     void
370 init_highlight(
371     int		both,	    // include groups where 'bg' doesn't matter
372     int		reset)	    // clear group first
373 {
374     int		i;
375     char	**pp;
376     static int	had_both = FALSE;
377 #ifdef FEAT_EVAL
378     char_u	*p;
379 
380     /*
381      * Try finding the color scheme file.  Used when a color file was loaded
382      * and 'background' or 't_Co' is changed.
383      */
384     p = get_var_value((char_u *)"g:colors_name");
385     if (p != NULL)
386     {
387 	// The value of g:colors_name could be freed when sourcing the script,
388 	// making "p" invalid, so copy it.
389 	char_u *copy_p = vim_strsave(p);
390 	int    r;
391 
392 	if (copy_p != NULL)
393 	{
394 	    r = load_colors(copy_p);
395 	    vim_free(copy_p);
396 	    if (r == OK)
397 		return;
398 	}
399     }
400 
401 #endif
402 
403     /*
404      * Didn't use a color file, use the compiled-in colors.
405      */
406     if (both)
407     {
408 	had_both = TRUE;
409 	pp = highlight_init_both;
410 	for (i = 0; pp[i] != NULL; ++i)
411 	    do_highlight((char_u *)pp[i], reset, TRUE);
412     }
413     else if (!had_both)
414 	// Don't do anything before the call with both == TRUE from main().
415 	// Not everything has been setup then, and that call will overrule
416 	// everything anyway.
417 	return;
418 
419     if (*p_bg == 'l')
420 	pp = highlight_init_light;
421     else
422 	pp = highlight_init_dark;
423     for (i = 0; pp[i] != NULL; ++i)
424 	do_highlight((char_u *)pp[i], reset, TRUE);
425 
426     // Reverse looks ugly, but grey may not work for 8 colors.  Thus let it
427     // depend on the number of colors available.
428     // With 8 colors brown is equal to yellow, need to use black for Search fg
429     // to avoid Statement highlighted text disappears.
430     // Clear the attributes, needed when changing the t_Co value.
431     if (t_colors > 8)
432 	do_highlight((char_u *)(*p_bg == 'l'
433 		    ? "Visual cterm=NONE ctermbg=LightGrey"
434 		    : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
435     else
436     {
437 	do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
438 								 FALSE, TRUE);
439 	if (*p_bg == 'l')
440 	    do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
441     }
442 
443 #ifdef FEAT_SYN_HL
444     /*
445      * If syntax highlighting is enabled load the highlighting for it.
446      */
447     if (get_var_value((char_u *)"g:syntax_on") != NULL)
448     {
449 	static int	recursive = 0;
450 
451 	if (recursive >= 5)
452 	    emsg(_("E679: recursive loop loading syncolor.vim"));
453 	else
454 	{
455 	    ++recursive;
456 	    (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
457 	    --recursive;
458 	}
459     }
460 #endif
461 }
462 
463 /*
464  * Load color file "name".
465  * Return OK for success, FAIL for failure.
466  */
467     int
468 load_colors(char_u *name)
469 {
470     char_u	*buf;
471     int		retval = FAIL;
472     static int	recursive = FALSE;
473 
474     // When being called recursively, this is probably because setting
475     // 'background' caused the highlighting to be reloaded.  This means it is
476     // working, thus we should return OK.
477     if (recursive)
478 	return OK;
479 
480     recursive = TRUE;
481     buf = alloc(STRLEN(name) + 12);
482     if (buf != NULL)
483     {
484 	apply_autocmds(EVENT_COLORSCHEMEPRE, name,
485 					       curbuf->b_fname, FALSE, curbuf);
486 	sprintf((char *)buf, "colors/%s.vim", name);
487 	retval = source_runtime(buf, DIP_START + DIP_OPT);
488 	vim_free(buf);
489 	apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
490     }
491     recursive = FALSE;
492 
493     return retval;
494 }
495 
496 static char *(color_names[28]) = {
497 	    "Black", "DarkBlue", "DarkGreen", "DarkCyan",
498 	    "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
499 	    "Gray", "Grey", "LightGray", "LightGrey",
500 	    "DarkGray", "DarkGrey",
501 	    "Blue", "LightBlue", "Green", "LightGreen",
502 	    "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
503 	    "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
504 	    // indices:
505 	    // 0, 1, 2, 3,
506 	    // 4, 5, 6, 7,
507 	    // 8, 9, 10, 11,
508 	    // 12, 13,
509 	    // 14, 15, 16, 17,
510 	    // 18, 19, 20, 21, 22,
511 	    // 23, 24, 25, 26, 27
512 static int color_numbers_16[28] = {0, 1, 2, 3,
513 				 4, 5, 6, 6,
514 				 7, 7, 7, 7,
515 				 8, 8,
516 				 9, 9, 10, 10,
517 				 11, 11, 12, 12, 13,
518 				 13, 14, 14, 15, -1};
519 // for xterm with 88 colors...
520 static int color_numbers_88[28] = {0, 4, 2, 6,
521 				 1, 5, 32, 72,
522 				 84, 84, 7, 7,
523 				 82, 82,
524 				 12, 43, 10, 61,
525 				 14, 63, 9, 74, 13,
526 				 75, 11, 78, 15, -1};
527 // for xterm with 256 colors...
528 static int color_numbers_256[28] = {0, 4, 2, 6,
529 				 1, 5, 130, 3,
530 				 248, 248, 7, 7,
531 				 242, 242,
532 				 12, 81, 10, 121,
533 				 14, 159, 9, 224, 13,
534 				 225, 11, 229, 15, -1};
535 // for terminals with less than 16 colors...
536 static int color_numbers_8[28] = {0, 4, 2, 6,
537 				 1, 5, 3, 3,
538 				 7, 7, 7, 7,
539 				 0+8, 0+8,
540 				 4+8, 4+8, 2+8, 2+8,
541 				 6+8, 6+8, 1+8, 1+8, 5+8,
542 				 5+8, 3+8, 3+8, 7+8, -1};
543 
544 /*
545  * Lookup the "cterm" value to be used for color with index "idx" in
546  * color_names[].
547  * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
548  * colors, otherwise it will be unchanged.
549  */
550     int
551 lookup_color(int idx, int foreground, int *boldp)
552 {
553     int		color = color_numbers_16[idx];
554     char_u	*p;
555 
556     // Use the _16 table to check if it's a valid color name.
557     if (color < 0)
558 	return -1;
559 
560     if (t_colors == 8)
561     {
562 	// t_Co is 8: use the 8 colors table
563 #if defined(__QNXNTO__)
564 	// On qnx, the 8 & 16 color arrays are the same
565 	if (STRNCMP(T_NAME, "qansi", 5) == 0)
566 	    color = color_numbers_16[idx];
567 	else
568 #endif
569 	    color = color_numbers_8[idx];
570 	if (foreground)
571 	{
572 	    // set/reset bold attribute to get light foreground
573 	    // colors (on some terminals, e.g. "linux")
574 	    if (color & 8)
575 		*boldp = TRUE;
576 	    else
577 		*boldp = FALSE;
578 	}
579 	color &= 7;	// truncate to 8 colors
580     }
581     else if (t_colors == 16 || t_colors == 88
582 					   || t_colors >= 256)
583     {
584 	/*
585 	 * Guess: if the termcap entry ends in 'm', it is
586 	 * probably an xterm-like terminal.  Use the changed
587 	 * order for colors.
588 	 */
589 	if (*T_CAF != NUL)
590 	    p = T_CAF;
591 	else
592 	    p = T_CSF;
593 	if (*p != NUL && (t_colors > 256
594 			      || *(p + STRLEN(p) - 1) == 'm'))
595 	{
596 	    if (t_colors == 88)
597 		color = color_numbers_88[idx];
598 	    else if (t_colors >= 256)
599 		color = color_numbers_256[idx];
600 	    else
601 		color = color_numbers_8[idx];
602 	}
603 #ifdef FEAT_TERMRESPONSE
604 	if (t_colors >= 256 && color == 15 && is_mac_terminal)
605 	    // Terminal.app has a bug: 15 is light grey. Use white
606 	    // from the color cube instead.
607 	    color = 231;
608 #endif
609     }
610     return color;
611 }
612 
613 /*
614  * Handle the ":highlight .." command.
615  * When using ":hi clear" this is called recursively for each group with
616  * "forceit" and "init" both TRUE.
617  */
618     void
619 do_highlight(
620     char_u	*line,
621     int		forceit,
622     int		init)	    // TRUE when called for initializing
623 {
624     char_u	*name_end;
625     char_u	*p;
626     char_u	*linep;
627     char_u	*key_start;
628     char_u	*arg_start;
629     char_u	*key = NULL, *arg = NULL;
630     long	i;
631     int		off;
632     int		len;
633     int		attr;
634     int		id;
635     int		idx;
636     hl_group_T	item_before;
637     int		did_change = FALSE;
638     int		dodefault = FALSE;
639     int		doclear = FALSE;
640     int		dolink = FALSE;
641     int		error = FALSE;
642     int		color;
643     int		is_normal_group = FALSE;	// "Normal" group
644 #ifdef FEAT_TERMINAL
645     int		is_terminal_group = FALSE;	// "Terminal" group
646 #endif
647 #ifdef FEAT_GUI_X11
648     int		is_menu_group = FALSE;		// "Menu" group
649     int		is_scrollbar_group = FALSE;	// "Scrollbar" group
650     int		is_tooltip_group = FALSE;	// "Tooltip" group
651     int		do_colors = FALSE;		// need to update colors?
652 #else
653 # define is_menu_group 0
654 # define is_tooltip_group 0
655 #endif
656 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
657     int		did_highlight_changed = FALSE;
658 #endif
659 
660     /*
661      * If no argument, list current highlighting.
662      */
663     if (!init && ends_excmd2(line - 1, line))
664     {
665 	for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
666 	    // TODO: only call when the group has attributes set
667 	    highlight_list_one((int)i);
668 	return;
669     }
670 
671     /*
672      * Isolate the name.
673      */
674     name_end = skiptowhite(line);
675     linep = skipwhite(name_end);
676 
677     /*
678      * Check for "default" argument.
679      */
680     if (STRNCMP(line, "default", name_end - line) == 0)
681     {
682 	dodefault = TRUE;
683 	line = linep;
684 	name_end = skiptowhite(line);
685 	linep = skipwhite(name_end);
686     }
687 
688     /*
689      * Check for "clear" or "link" argument.
690      */
691     if (STRNCMP(line, "clear", name_end - line) == 0)
692 	doclear = TRUE;
693     if (STRNCMP(line, "link", name_end - line) == 0)
694 	dolink = TRUE;
695 
696     /*
697      * ":highlight {group-name}": list highlighting for one group.
698      */
699     if (!doclear && !dolink && ends_excmd2(line, linep))
700     {
701 	id = syn_namen2id(line, (int)(name_end - line));
702 	if (id == 0)
703 	    semsg(_("E411: highlight group not found: %s"), line);
704 	else
705 	    highlight_list_one(id);
706 	return;
707     }
708 
709     /*
710      * Handle ":highlight link {from} {to}" command.
711      */
712     if (dolink)
713     {
714 	char_u	    *from_start = linep;
715 	char_u	    *from_end;
716 	char_u	    *to_start;
717 	char_u	    *to_end;
718 	int	    from_id;
719 	int	    to_id;
720 	hl_group_T  *hlgroup = NULL;
721 
722 	from_end = skiptowhite(from_start);
723 	to_start = skipwhite(from_end);
724 	to_end	 = skiptowhite(to_start);
725 
726 	if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
727 	{
728 	    semsg(_("E412: Not enough arguments: \":highlight link %s\""),
729 								  from_start);
730 	    return;
731 	}
732 
733 	if (!ends_excmd2(line, skipwhite(to_end)))
734 	{
735 	    semsg(_("E413: Too many arguments: \":highlight link %s\""),
736 								   from_start);
737 	    return;
738 	}
739 
740 	from_id = syn_check_group(from_start, (int)(from_end - from_start));
741 	if (STRNCMP(to_start, "NONE", 4) == 0)
742 	    to_id = 0;
743 	else
744 	    to_id = syn_check_group(to_start, (int)(to_end - to_start));
745 
746 	if (from_id > 0)
747 	{
748 	    hlgroup = &HL_TABLE()[from_id - 1];
749 	    if (dodefault && (forceit || hlgroup->sg_deflink == 0))
750 	    {
751 		hlgroup->sg_deflink = to_id;
752 #ifdef FEAT_EVAL
753 		hlgroup->sg_deflink_sctx = current_sctx;
754 		hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
755 #endif
756 	    }
757 	}
758 
759 	if (from_id > 0 && (!init || hlgroup->sg_set == 0))
760 	{
761 	    /*
762 	     * Don't allow a link when there already is some highlighting
763 	     * for the group, unless '!' is used
764 	     */
765 	    if (to_id > 0 && !forceit && !init
766 				   && hl_has_settings(from_id - 1, dodefault))
767 	    {
768 		if (SOURCING_NAME == NULL && !dodefault)
769 		    emsg(_("E414: group has settings, highlight link ignored"));
770 	    }
771 	    else if (hlgroup->sg_link != to_id
772 #ifdef FEAT_EVAL
773 		    || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
774 #endif
775 		    || hlgroup->sg_cleared)
776 	    {
777 		if (!init)
778 		    hlgroup->sg_set |= SG_LINK;
779 		hlgroup->sg_link = to_id;
780 #ifdef FEAT_EVAL
781 		hlgroup->sg_script_ctx = current_sctx;
782 		hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
783 #endif
784 		hlgroup->sg_cleared = FALSE;
785 		redraw_all_later(SOME_VALID);
786 
787 		// Only call highlight_changed() once after multiple changes.
788 		need_highlight_changed = TRUE;
789 	    }
790 	}
791 
792 	return;
793     }
794 
795     if (doclear)
796     {
797 	/*
798 	 * ":highlight clear [group]" command.
799 	 */
800 	if (ends_excmd2(line, linep))
801 	{
802 #ifdef FEAT_GUI
803 	    // First, we do not destroy the old values, but allocate the new
804 	    // ones and update the display. THEN we destroy the old values.
805 	    // If we destroy the old values first, then the old values
806 	    // (such as GuiFont's or GuiFontset's) will still be displayed but
807 	    // invalid because they were free'd.
808 	    if (gui.in_use)
809 	    {
810 # ifdef FEAT_BEVAL_TIP
811 		gui_init_tooltip_font();
812 # endif
813 # if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
814 		gui_init_menu_font();
815 # endif
816 	    }
817 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
818 	    gui_mch_def_colors();
819 # endif
820 # ifdef FEAT_GUI_X11
821 #  ifdef FEAT_MENU
822 
823 	    // This only needs to be done when there is no Menu highlight
824 	    // group defined by default, which IS currently the case.
825 	    gui_mch_new_menu_colors();
826 #  endif
827 	    if (gui.in_use)
828 	    {
829 		gui_new_scrollbar_colors();
830 #  ifdef FEAT_BEVAL_GUI
831 		gui_mch_new_tooltip_colors();
832 #  endif
833 #  ifdef FEAT_MENU
834 		gui_mch_new_menu_font();
835 #  endif
836 	    }
837 # endif
838 
839 	    // Ok, we're done allocating the new default graphics items.
840 	    // The screen should already be refreshed at this point.
841 	    // It is now Ok to clear out the old data.
842 #endif
843 #ifdef FEAT_EVAL
844 	    do_unlet((char_u *)"g:colors_name", TRUE);
845 #endif
846 	    restore_cterm_colors();
847 
848 	    /*
849 	     * Clear all default highlight groups and load the defaults.
850 	     */
851 	    for (idx = 0; idx < highlight_ga.ga_len; ++idx)
852 		highlight_clear(idx);
853 	    init_highlight(TRUE, TRUE);
854 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
855 	    if (USE_24BIT)
856 		highlight_gui_started();
857 	    else
858 #endif
859 		highlight_changed();
860 	    redraw_later_clear();
861 	    return;
862 	}
863 	line = linep;
864 	name_end = skiptowhite(line);
865 	linep = skipwhite(name_end);
866     }
867 
868     /*
869      * Find the group name in the table.  If it does not exist yet, add it.
870      */
871     id = syn_check_group(line, (int)(name_end - line));
872     if (id == 0)			// failed (out of memory)
873 	return;
874     idx = id - 1;			// index is ID minus one
875 
876     // Return if "default" was used and the group already has settings.
877     if (dodefault && hl_has_settings(idx, TRUE))
878 	return;
879 
880     // Make a copy so we can check if any attribute actually changed.
881     item_before = HL_TABLE()[idx];
882 
883     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
884 	is_normal_group = TRUE;
885 #ifdef FEAT_TERMINAL
886     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
887 	is_terminal_group = TRUE;
888 #endif
889 #ifdef FEAT_GUI_X11
890     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
891 	is_menu_group = TRUE;
892     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
893 	is_scrollbar_group = TRUE;
894     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
895 	is_tooltip_group = TRUE;
896 #endif
897 
898     // Clear the highlighting for ":hi clear {group}" and ":hi clear".
899     if (doclear || (forceit && init))
900     {
901 	highlight_clear(idx);
902 	if (!doclear)
903 	    HL_TABLE()[idx].sg_set = 0;
904     }
905 
906     if (!doclear)
907       while (!ends_excmd2(line, linep))
908       {
909 	key_start = linep;
910 	if (*linep == '=')
911 	{
912 	    semsg(_("E415: unexpected equal sign: %s"), key_start);
913 	    error = TRUE;
914 	    break;
915 	}
916 
917 	/*
918 	 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
919 	 * "guibg").
920 	 */
921 	while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
922 	    ++linep;
923 	vim_free(key);
924 	key = vim_strnsave_up(key_start, linep - key_start);
925 	if (key == NULL)
926 	{
927 	    error = TRUE;
928 	    break;
929 	}
930 	linep = skipwhite(linep);
931 
932 	if (STRCMP(key, "NONE") == 0)
933 	{
934 	    if (!init || HL_TABLE()[idx].sg_set == 0)
935 	    {
936 		if (!init)
937 		    HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
938 		highlight_clear(idx);
939 	    }
940 	    continue;
941 	}
942 
943 	/*
944 	 * Check for the equal sign.
945 	 */
946 	if (*linep != '=')
947 	{
948 	    semsg(_("E416: missing equal sign: %s"), key_start);
949 	    error = TRUE;
950 	    break;
951 	}
952 	++linep;
953 
954 	/*
955 	 * Isolate the argument.
956 	 */
957 	linep = skipwhite(linep);
958 	if (*linep == '\'')		// guifg='color name'
959 	{
960 	    arg_start = ++linep;
961 	    linep = vim_strchr(linep, '\'');
962 	    if (linep == NULL)
963 	    {
964 		semsg(_(e_invarg2), key_start);
965 		error = TRUE;
966 		break;
967 	    }
968 	}
969 	else
970 	{
971 	    arg_start = linep;
972 	    linep = skiptowhite(linep);
973 	}
974 	if (linep == arg_start)
975 	{
976 	    semsg(_("E417: missing argument: %s"), key_start);
977 	    error = TRUE;
978 	    break;
979 	}
980 	vim_free(arg);
981 	arg = vim_strnsave(arg_start, linep - arg_start);
982 	if (arg == NULL)
983 	{
984 	    error = TRUE;
985 	    break;
986 	}
987 	if (*linep == '\'')
988 	    ++linep;
989 
990 	/*
991 	 * Store the argument.
992 	 */
993 	if (  STRCMP(key, "TERM") == 0
994 		|| STRCMP(key, "CTERM") == 0
995 		|| STRCMP(key, "GUI") == 0)
996 	{
997 	    attr = 0;
998 	    off = 0;
999 	    while (arg[off] != NUL)
1000 	    {
1001 		for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
1002 		{
1003 		    len = (int)STRLEN(hl_name_table[i]);
1004 		    if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
1005 		    {
1006 			attr |= hl_attr_table[i];
1007 			off += len;
1008 			break;
1009 		    }
1010 		}
1011 		if (i < 0)
1012 		{
1013 		    semsg(_("E418: Illegal value: %s"), arg);
1014 		    error = TRUE;
1015 		    break;
1016 		}
1017 		if (arg[off] == ',')		// another one follows
1018 		    ++off;
1019 	    }
1020 	    if (error)
1021 		break;
1022 	    if (*key == 'T')
1023 	    {
1024 		if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
1025 		{
1026 		    if (!init)
1027 			HL_TABLE()[idx].sg_set |= SG_TERM;
1028 		    HL_TABLE()[idx].sg_term = attr;
1029 		}
1030 	    }
1031 	    else if (*key == 'C')
1032 	    {
1033 		if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1034 		{
1035 		    if (!init)
1036 			HL_TABLE()[idx].sg_set |= SG_CTERM;
1037 		    HL_TABLE()[idx].sg_cterm = attr;
1038 		    HL_TABLE()[idx].sg_cterm_bold = FALSE;
1039 		}
1040 	    }
1041 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1042 	    else
1043 	    {
1044 		if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1045 		{
1046 		    if (!init)
1047 			HL_TABLE()[idx].sg_set |= SG_GUI;
1048 		    HL_TABLE()[idx].sg_gui = attr;
1049 		}
1050 	    }
1051 #endif
1052 	}
1053 	else if (STRCMP(key, "FONT") == 0)
1054 	{
1055 	    // in non-GUI fonts are simply ignored
1056 #ifdef FEAT_GUI
1057 	    if (HL_TABLE()[idx].sg_font_name != NULL
1058 			     && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
1059 	    {
1060 		// Font name didn't change, ignore.
1061 	    }
1062 	    else if (!gui.shell_created)
1063 	    {
1064 		// GUI not started yet, always accept the name.
1065 		vim_free(HL_TABLE()[idx].sg_font_name);
1066 		HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1067 		did_change = TRUE;
1068 	    }
1069 	    else
1070 	    {
1071 		GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
1072 # ifdef FEAT_XFONTSET
1073 		GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
1074 # endif
1075 		// First, save the current font/fontset.
1076 		// Then try to allocate the font/fontset.
1077 		// If the allocation fails, HL_TABLE()[idx].sg_font OR
1078 		// sg_fontset will be set to NOFONT or NOFONTSET respectively.
1079 
1080 		HL_TABLE()[idx].sg_font = NOFONT;
1081 # ifdef FEAT_XFONTSET
1082 		HL_TABLE()[idx].sg_fontset = NOFONTSET;
1083 # endif
1084 		hl_do_font(idx, arg, is_normal_group, is_menu_group,
1085 						     is_tooltip_group, FALSE);
1086 
1087 # ifdef FEAT_XFONTSET
1088 		if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1089 		{
1090 		    // New fontset was accepted. Free the old one, if there
1091 		    // was one.
1092 		    gui_mch_free_fontset(temp_sg_fontset);
1093 		    vim_free(HL_TABLE()[idx].sg_font_name);
1094 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1095 		    did_change = TRUE;
1096 		}
1097 		else
1098 		    HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
1099 # endif
1100 		if (HL_TABLE()[idx].sg_font != NOFONT)
1101 		{
1102 		    // New font was accepted. Free the old one, if there was
1103 		    // one.
1104 		    gui_mch_free_font(temp_sg_font);
1105 		    vim_free(HL_TABLE()[idx].sg_font_name);
1106 		    HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1107 		    did_change = TRUE;
1108 		}
1109 		else
1110 		    HL_TABLE()[idx].sg_font = temp_sg_font;
1111 	    }
1112 #endif
1113 	}
1114 	else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0
1115 						|| STRCMP(key, "CTERMUL") == 0)
1116 	{
1117 	  if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1118 	  {
1119 	    if (!init)
1120 		HL_TABLE()[idx].sg_set |= SG_CTERM;
1121 
1122 	    // When setting the foreground color, and previously the "bold"
1123 	    // flag was set for a light color, reset it now
1124 	    if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1125 	    {
1126 		HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1127 		HL_TABLE()[idx].sg_cterm_bold = FALSE;
1128 	    }
1129 
1130 	    if (VIM_ISDIGIT(*arg))
1131 		color = atoi((char *)arg);
1132 	    else if (STRICMP(arg, "fg") == 0)
1133 	    {
1134 		if (cterm_normal_fg_color)
1135 		    color = cterm_normal_fg_color - 1;
1136 		else
1137 		{
1138 		    emsg(_("E419: FG color unknown"));
1139 		    error = TRUE;
1140 		    break;
1141 		}
1142 	    }
1143 	    else if (STRICMP(arg, "bg") == 0)
1144 	    {
1145 		if (cterm_normal_bg_color > 0)
1146 		    color = cterm_normal_bg_color - 1;
1147 		else
1148 		{
1149 		    emsg(_("E420: BG color unknown"));
1150 		    error = TRUE;
1151 		    break;
1152 		}
1153 	    }
1154 	    else if (STRICMP(arg, "ul") == 0)
1155 	    {
1156 		if (cterm_normal_ul_color > 0)
1157 		    color = cterm_normal_ul_color - 1;
1158 		else
1159 		{
1160 		    emsg(_("E453: UL color unknown"));
1161 		    error = TRUE;
1162 		    break;
1163 		}
1164 	    }
1165 	    else
1166 	    {
1167 		int bold = MAYBE;
1168 
1169 		// reduce calls to STRICMP a bit, it can be slow
1170 		off = TOUPPER_ASC(*arg);
1171 		for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
1172 		    if (off == color_names[i][0]
1173 				 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1174 			break;
1175 		if (i < 0)
1176 		{
1177 		    semsg(_("E421: Color name or number not recognized: %s"), key_start);
1178 		    error = TRUE;
1179 		    break;
1180 		}
1181 
1182 		color = lookup_color(i, key[5] == 'F', &bold);
1183 
1184 		// set/reset bold attribute to get light foreground
1185 		// colors (on some terminals, e.g. "linux")
1186 		if (bold == TRUE)
1187 		{
1188 		    HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1189 		    HL_TABLE()[idx].sg_cterm_bold = TRUE;
1190 		}
1191 		else if (bold == FALSE)
1192 		    HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1193 	    }
1194 
1195 	    // Add one to the argument, to avoid zero.  Zero is used for
1196 	    // "NONE", then "color" is -1.
1197 	    if (key[5] == 'F')
1198 	    {
1199 		HL_TABLE()[idx].sg_cterm_fg = color + 1;
1200 		if (is_normal_group)
1201 		{
1202 		    cterm_normal_fg_color = color + 1;
1203 		    cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
1204 #ifdef FEAT_GUI
1205 		    // Don't do this if the GUI is used.
1206 		    if (!gui.in_use && !gui.starting)
1207 #endif
1208 		    {
1209 			must_redraw = CLEAR;
1210 			if (termcap_active && color >= 0)
1211 			    term_fg_color(color);
1212 		    }
1213 		}
1214 	    }
1215 	    else if (key[5] == 'B')
1216 	    {
1217 		HL_TABLE()[idx].sg_cterm_bg = color + 1;
1218 		if (is_normal_group)
1219 		{
1220 		    cterm_normal_bg_color = color + 1;
1221 #ifdef FEAT_GUI
1222 		    // Don't mess with 'background' if the GUI is used.
1223 		    if (!gui.in_use && !gui.starting)
1224 #endif
1225 		    {
1226 			must_redraw = CLEAR;
1227 			if (color >= 0)
1228 			{
1229 			    int dark = -1;
1230 
1231 			    if (termcap_active)
1232 				term_bg_color(color);
1233 			    if (t_colors < 16)
1234 				dark = (color == 0 || color == 4);
1235 			    // Limit the heuristic to the standard 16 colors
1236 			    else if (color < 16)
1237 				dark = (color < 7 || color == 8);
1238 			    // Set the 'background' option if the value is
1239 			    // wrong.
1240 			    if (dark != -1
1241 				    && dark != (*p_bg == 'd')
1242 				    && !option_was_set((char_u *)"bg"))
1243 			    {
1244 				set_option_value((char_u *)"bg", 0L,
1245 				       (char_u *)(dark ? "dark" : "light"), 0);
1246 				reset_option_was_set((char_u *)"bg");
1247 			    }
1248 			}
1249 		    }
1250 		}
1251 	    }
1252 	    else // ctermul
1253 	    {
1254 		HL_TABLE()[idx].sg_cterm_ul = color + 1;
1255 		if (is_normal_group)
1256 		{
1257 		    cterm_normal_ul_color = color + 1;
1258 #ifdef FEAT_GUI
1259 		    // Don't do this if the GUI is used.
1260 		    if (!gui.in_use && !gui.starting)
1261 #endif
1262 		    {
1263 			must_redraw = CLEAR;
1264 			if (termcap_active && color >= 0)
1265 			    term_ul_color(color);
1266 		    }
1267 		}
1268 	    }
1269 	  }
1270 	}
1271 	else if (STRCMP(key, "GUIFG") == 0)
1272 	{
1273 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1274 	    char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
1275 
1276 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1277 	    {
1278 		if (!init)
1279 		    HL_TABLE()[idx].sg_set |= SG_GUI;
1280 
1281 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1282 		// In GUI guifg colors are only used when recognized
1283 		i = color_name2handle(arg);
1284 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1285 		{
1286 		    HL_TABLE()[idx].sg_gui_fg = i;
1287 # endif
1288 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
1289 		    {
1290 			vim_free(*namep);
1291 			if (STRCMP(arg, "NONE") != 0)
1292 			    *namep = vim_strsave(arg);
1293 			else
1294 			    *namep = NULL;
1295 			did_change = TRUE;
1296 		    }
1297 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1298 #  ifdef FEAT_GUI_X11
1299 		    if (is_menu_group && gui.menu_fg_pixel != i)
1300 		    {
1301 			gui.menu_fg_pixel = i;
1302 			do_colors = TRUE;
1303 		    }
1304 		    if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1305 		    {
1306 			gui.scroll_fg_pixel = i;
1307 			do_colors = TRUE;
1308 		    }
1309 #   ifdef FEAT_BEVAL_GUI
1310 		    if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1311 		    {
1312 			gui.tooltip_fg_pixel = i;
1313 			do_colors = TRUE;
1314 		    }
1315 #   endif
1316 #  endif
1317 		}
1318 # endif
1319 	    }
1320 #endif
1321 	}
1322 	else if (STRCMP(key, "GUIBG") == 0)
1323 	{
1324 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1325 	    char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
1326 
1327 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1328 	    {
1329 		if (!init)
1330 		    HL_TABLE()[idx].sg_set |= SG_GUI;
1331 
1332 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1333 		// In GUI guifg colors are only used when recognized
1334 		i = color_name2handle(arg);
1335 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1336 		{
1337 		    HL_TABLE()[idx].sg_gui_bg = i;
1338 # endif
1339 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
1340 		    {
1341 			vim_free(*namep);
1342 			if (STRCMP(arg, "NONE") != 0)
1343 			    *namep = vim_strsave(arg);
1344 			else
1345 			    *namep = NULL;
1346 			did_change = TRUE;
1347 		    }
1348 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1349 #  ifdef FEAT_GUI_X11
1350 		    if (is_menu_group && gui.menu_bg_pixel != i)
1351 		    {
1352 			gui.menu_bg_pixel = i;
1353 			do_colors = TRUE;
1354 		    }
1355 		    if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1356 		    {
1357 			gui.scroll_bg_pixel = i;
1358 			do_colors = TRUE;
1359 		    }
1360 #   ifdef FEAT_BEVAL_GUI
1361 		    if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1362 		    {
1363 			gui.tooltip_bg_pixel = i;
1364 			do_colors = TRUE;
1365 		    }
1366 #   endif
1367 #  endif
1368 		}
1369 # endif
1370 	    }
1371 #endif
1372 	}
1373 	else if (STRCMP(key, "GUISP") == 0)
1374 	{
1375 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1376 	    char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
1377 
1378 	    if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1379 	    {
1380 		if (!init)
1381 		    HL_TABLE()[idx].sg_set |= SG_GUI;
1382 
1383 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1384 		// In GUI guisp colors are only used when recognized
1385 		i = color_name2handle(arg);
1386 		if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1387 		{
1388 		    HL_TABLE()[idx].sg_gui_sp = i;
1389 # endif
1390 		    if (*namep == NULL || STRCMP(*namep, arg) != 0)
1391 		    {
1392 			vim_free(*namep);
1393 			if (STRCMP(arg, "NONE") != 0)
1394 			    *namep = vim_strsave(arg);
1395 			else
1396 			    *namep = NULL;
1397 			did_change = TRUE;
1398 		    }
1399 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1400 		}
1401 # endif
1402 	    }
1403 #endif
1404 	}
1405 	else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1406 	{
1407 	    char_u	buf[100];
1408 	    char_u	*tname;
1409 
1410 	    if (!init)
1411 		HL_TABLE()[idx].sg_set |= SG_TERM;
1412 
1413 	    /*
1414 	     * The "start" and "stop"  arguments can be a literal escape
1415 	     * sequence, or a comma separated list of terminal codes.
1416 	     */
1417 	    if (STRNCMP(arg, "t_", 2) == 0)
1418 	    {
1419 		off = 0;
1420 		buf[0] = 0;
1421 		while (arg[off] != NUL)
1422 		{
1423 		    // Isolate one termcap name
1424 		    for (len = 0; arg[off + len] &&
1425 						 arg[off + len] != ','; ++len)
1426 			;
1427 		    tname = vim_strnsave(arg + off, len);
1428 		    if (tname == NULL)		// out of memory
1429 		    {
1430 			error = TRUE;
1431 			break;
1432 		    }
1433 		    // lookup the escape sequence for the item
1434 		    p = get_term_code(tname);
1435 		    vim_free(tname);
1436 		    if (p == NULL)	    // ignore non-existing things
1437 			p = (char_u *)"";
1438 
1439 		    // Append it to the already found stuff
1440 		    if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1441 		    {
1442 			semsg(_("E422: terminal code too long: %s"), arg);
1443 			error = TRUE;
1444 			break;
1445 		    }
1446 		    STRCAT(buf, p);
1447 
1448 		    // Advance to the next item
1449 		    off += len;
1450 		    if (arg[off] == ',')	    // another one follows
1451 			++off;
1452 		}
1453 	    }
1454 	    else
1455 	    {
1456 		/*
1457 		 * Copy characters from arg[] to buf[], translating <> codes.
1458 		 */
1459 		for (p = arg, off = 0; off < 100 - 6 && *p; )
1460 		{
1461 		    len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
1462 		    if (len > 0)	    // recognized special char
1463 			off += len;
1464 		    else		    // copy as normal char
1465 			buf[off++] = *p++;
1466 		}
1467 		buf[off] = NUL;
1468 	    }
1469 	    if (error)
1470 		break;
1471 
1472 	    if (STRCMP(buf, "NONE") == 0)	// resetting the value
1473 		p = NULL;
1474 	    else
1475 		p = vim_strsave(buf);
1476 	    if (key[2] == 'A')
1477 	    {
1478 		vim_free(HL_TABLE()[idx].sg_start);
1479 		HL_TABLE()[idx].sg_start = p;
1480 	    }
1481 	    else
1482 	    {
1483 		vim_free(HL_TABLE()[idx].sg_stop);
1484 		HL_TABLE()[idx].sg_stop = p;
1485 	    }
1486 	}
1487 	else
1488 	{
1489 	    semsg(_("E423: Illegal argument: %s"), key_start);
1490 	    error = TRUE;
1491 	    break;
1492 	}
1493 	HL_TABLE()[idx].sg_cleared = FALSE;
1494 
1495 	/*
1496 	 * When highlighting has been given for a group, don't link it.
1497 	 */
1498 	if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1499 	    HL_TABLE()[idx].sg_link = 0;
1500 
1501 	/*
1502 	 * Continue with next argument.
1503 	 */
1504 	linep = skipwhite(linep);
1505       }
1506 
1507     /*
1508      * If there is an error, and it's a new entry, remove it from the table.
1509      */
1510     if (error && idx == highlight_ga.ga_len)
1511 	syn_unadd_group();
1512     else
1513     {
1514 	if (is_normal_group)
1515 	{
1516 	    HL_TABLE()[idx].sg_term_attr = 0;
1517 	    HL_TABLE()[idx].sg_cterm_attr = 0;
1518 #ifdef FEAT_GUI
1519 	    HL_TABLE()[idx].sg_gui_attr = 0;
1520 	    /*
1521 	     * Need to update all groups, because they might be using "bg"
1522 	     * and/or "fg", which have been changed now.
1523 	     */
1524 #endif
1525 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1526 	    if (USE_24BIT)
1527 	    {
1528 		highlight_gui_started();
1529 		did_highlight_changed = TRUE;
1530 		redraw_all_later(NOT_VALID);
1531 	    }
1532 #endif
1533 #ifdef FEAT_VTP
1534 	    control_console_color_rgb();
1535 #endif
1536 	}
1537 #ifdef FEAT_TERMINAL
1538 	else if (is_terminal_group)
1539 	    set_terminal_default_colors(
1540 		    HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1541 #endif
1542 #ifdef FEAT_GUI_X11
1543 # ifdef FEAT_MENU
1544 	else if (is_menu_group)
1545 	{
1546 	    if (gui.in_use && do_colors)
1547 		gui_mch_new_menu_colors();
1548 	}
1549 # endif
1550 	else if (is_scrollbar_group)
1551 	{
1552 	    if (gui.in_use && do_colors)
1553 		gui_new_scrollbar_colors();
1554 	    else
1555 		set_hl_attr(idx);
1556 	}
1557 # ifdef FEAT_BEVAL_GUI
1558 	else if (is_tooltip_group)
1559 	{
1560 	    if (gui.in_use && do_colors)
1561 		gui_mch_new_tooltip_colors();
1562 	}
1563 # endif
1564 #endif
1565 	else
1566 	    set_hl_attr(idx);
1567 #ifdef FEAT_EVAL
1568 	HL_TABLE()[idx].sg_script_ctx = current_sctx;
1569 	HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
1570 #endif
1571     }
1572 
1573     vim_free(key);
1574     vim_free(arg);
1575 
1576     // Only call highlight_changed() once, after a sequence of highlight
1577     // commands, and only if an attribute actually changed.
1578     if ((did_change
1579 	   || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1580 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1581 	    && !did_highlight_changed
1582 #endif
1583        )
1584     {
1585 	// Do not trigger a redraw when highlighting is changed while
1586 	// redrawing.  This may happen when evaluating 'statusline' changes the
1587 	// StatusLine group.
1588 	if (!updating_screen)
1589 	    redraw_all_later(NOT_VALID);
1590 	need_highlight_changed = TRUE;
1591     }
1592 }
1593 
1594 #if defined(EXITFREE) || defined(PROTO)
1595     void
1596 free_highlight(void)
1597 {
1598     int	    i;
1599 
1600     for (i = 0; i < highlight_ga.ga_len; ++i)
1601     {
1602 	highlight_clear(i);
1603 	vim_free(HL_TABLE()[i].sg_name);
1604 	vim_free(HL_TABLE()[i].sg_name_u);
1605     }
1606     ga_clear(&highlight_ga);
1607 }
1608 #endif
1609 
1610 /*
1611  * Reset the cterm colors to what they were before Vim was started, if
1612  * possible.  Otherwise reset them to zero.
1613  */
1614     void
1615 restore_cterm_colors(void)
1616 {
1617 #if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1618     // Since t_me has been set, this probably means that the user
1619     // wants to use this as default colors.  Need to reset default
1620     // background/foreground colors.
1621     mch_set_normal_colors();
1622 #else
1623 # ifdef VIMDLL
1624     if (!gui.in_use)
1625     {
1626 	mch_set_normal_colors();
1627 	return;
1628     }
1629 # endif
1630     cterm_normal_fg_color = 0;
1631     cterm_normal_fg_bold = 0;
1632     cterm_normal_bg_color = 0;
1633 # ifdef FEAT_TERMGUICOLORS
1634     cterm_normal_fg_gui_color = INVALCOLOR;
1635     cterm_normal_bg_gui_color = INVALCOLOR;
1636     cterm_normal_ul_gui_color = INVALCOLOR;
1637 # endif
1638 #endif
1639 }
1640 
1641 /*
1642  * Return TRUE if highlight group "idx" has any settings.
1643  * When "check_link" is TRUE also check for an existing link.
1644  */
1645     static int
1646 hl_has_settings(int idx, int check_link)
1647 {
1648     return HL_TABLE()[idx].sg_cleared == 0
1649 	 && (  HL_TABLE()[idx].sg_term_attr != 0
1650 	    || HL_TABLE()[idx].sg_cterm_attr != 0
1651 	    || HL_TABLE()[idx].sg_cterm_fg != 0
1652 	    || HL_TABLE()[idx].sg_cterm_bg != 0
1653 #ifdef FEAT_GUI
1654 	    || HL_TABLE()[idx].sg_gui_attr != 0
1655 	    || HL_TABLE()[idx].sg_gui_fg_name != NULL
1656 	    || HL_TABLE()[idx].sg_gui_bg_name != NULL
1657 	    || HL_TABLE()[idx].sg_gui_sp_name != NULL
1658 	    || HL_TABLE()[idx].sg_font_name != NULL
1659 #endif
1660 	    || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1661 }
1662 
1663 /*
1664  * Clear highlighting for one group.
1665  */
1666     static void
1667 highlight_clear(int idx)
1668 {
1669     HL_TABLE()[idx].sg_cleared = TRUE;
1670 
1671     HL_TABLE()[idx].sg_term = 0;
1672     VIM_CLEAR(HL_TABLE()[idx].sg_start);
1673     VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1674     HL_TABLE()[idx].sg_term_attr = 0;
1675     HL_TABLE()[idx].sg_cterm = 0;
1676     HL_TABLE()[idx].sg_cterm_bold = FALSE;
1677     HL_TABLE()[idx].sg_cterm_fg = 0;
1678     HL_TABLE()[idx].sg_cterm_bg = 0;
1679     HL_TABLE()[idx].sg_cterm_attr = 0;
1680 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
1681     HL_TABLE()[idx].sg_gui = 0;
1682     VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1683     VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1684     VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1685 #endif
1686 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1687     HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1688     HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
1689     HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
1690 #endif
1691 #ifdef FEAT_GUI
1692     gui_mch_free_font(HL_TABLE()[idx].sg_font);
1693     HL_TABLE()[idx].sg_font = NOFONT;
1694 # ifdef FEAT_XFONTSET
1695     gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1696     HL_TABLE()[idx].sg_fontset = NOFONTSET;
1697 # endif
1698     VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1699     HL_TABLE()[idx].sg_gui_attr = 0;
1700 #endif
1701     // Restore default link and context if they exist. Otherwise clears.
1702     HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
1703 #ifdef FEAT_EVAL
1704     // Since we set the default link, set the location to where the default
1705     // link was set.
1706     HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
1707 #endif
1708 }
1709 
1710 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1711 /*
1712  * Set the normal foreground and background colors according to the "Normal"
1713  * highlighting group.  For X11 also set "Menu", "Scrollbar", and
1714  * "Tooltip" colors.
1715  */
1716     void
1717 set_normal_colors(void)
1718 {
1719 # ifdef FEAT_GUI
1720 #  ifdef FEAT_TERMGUICOLORS
1721     if (gui.in_use)
1722 #  endif
1723     {
1724 	if (set_group_colors((char_u *)"Normal",
1725 				 &gui.norm_pixel, &gui.back_pixel,
1726 				 FALSE, TRUE, FALSE))
1727 	{
1728 	    gui_mch_new_colors();
1729 	    must_redraw = CLEAR;
1730 	}
1731 #  ifdef FEAT_GUI_X11
1732 	if (set_group_colors((char_u *)"Menu",
1733 			     &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1734 			     TRUE, FALSE, FALSE))
1735 	{
1736 #   ifdef FEAT_MENU
1737 	    gui_mch_new_menu_colors();
1738 #   endif
1739 	    must_redraw = CLEAR;
1740 	}
1741 #   ifdef FEAT_BEVAL_GUI
1742 	if (set_group_colors((char_u *)"Tooltip",
1743 			     &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1744 			     FALSE, FALSE, TRUE))
1745 	{
1746 #    ifdef FEAT_TOOLBAR
1747 	    gui_mch_new_tooltip_colors();
1748 #    endif
1749 	    must_redraw = CLEAR;
1750 	}
1751 #   endif
1752 	if (set_group_colors((char_u *)"Scrollbar",
1753 			&gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1754 			FALSE, FALSE, FALSE))
1755 	{
1756 	    gui_new_scrollbar_colors();
1757 	    must_redraw = CLEAR;
1758 	}
1759 #  endif
1760     }
1761 # endif
1762 # ifdef FEAT_TERMGUICOLORS
1763 #  ifdef FEAT_GUI
1764     else
1765 #  endif
1766     {
1767 	int		idx;
1768 
1769 	idx = syn_name2id((char_u *)"Normal") - 1;
1770 	if (idx >= 0)
1771 	{
1772 	    gui_do_one_color(idx, FALSE, FALSE);
1773 
1774 	    // If the normal fg or bg color changed a complete redraw is
1775 	    // required.
1776 	    if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1777 		    || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1778 	    {
1779 		// if the GUI color is INVALCOLOR then we use the default cterm
1780 		// color
1781 		cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1782 		cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1783 		must_redraw = CLEAR;
1784 	    }
1785 	}
1786     }
1787 # endif
1788 }
1789 #endif
1790 
1791 #if defined(FEAT_GUI) || defined(PROTO)
1792 /*
1793  * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1794  */
1795     static int
1796 set_group_colors(
1797     char_u	*name,
1798     guicolor_T	*fgp,
1799     guicolor_T	*bgp,
1800     int		do_menu,
1801     int		use_norm,
1802     int		do_tooltip)
1803 {
1804     int		idx;
1805 
1806     idx = syn_name2id(name) - 1;
1807     if (idx >= 0)
1808     {
1809 	gui_do_one_color(idx, do_menu, do_tooltip);
1810 
1811 	if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1812 	    *fgp = HL_TABLE()[idx].sg_gui_fg;
1813 	else if (use_norm)
1814 	    *fgp = gui.def_norm_pixel;
1815 	if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1816 	    *bgp = HL_TABLE()[idx].sg_gui_bg;
1817 	else if (use_norm)
1818 	    *bgp = gui.def_back_pixel;
1819 	return TRUE;
1820     }
1821     return FALSE;
1822 }
1823 
1824 /*
1825  * Get the font of the "Normal" group.
1826  * Returns "" when it's not found or not set.
1827  */
1828     char_u *
1829 hl_get_font_name(void)
1830 {
1831     int		id;
1832     char_u	*s;
1833 
1834     id = syn_name2id((char_u *)"Normal");
1835     if (id > 0)
1836     {
1837 	s = HL_TABLE()[id - 1].sg_font_name;
1838 	if (s != NULL)
1839 	    return s;
1840     }
1841     return (char_u *)"";
1842 }
1843 
1844 /*
1845  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
1846  * actually chosen to be used.
1847  */
1848     void
1849 hl_set_font_name(char_u *font_name)
1850 {
1851     int	    id;
1852 
1853     id = syn_name2id((char_u *)"Normal");
1854     if (id > 0)
1855     {
1856 	vim_free(HL_TABLE()[id - 1].sg_font_name);
1857 	HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1858     }
1859 }
1860 
1861 /*
1862  * Set background color for "Normal" group.  Called by gui_set_bg_color()
1863  * when the color is known.
1864  */
1865     void
1866 hl_set_bg_color_name(
1867     char_u  *name)	    // must have been allocated
1868 {
1869     int	    id;
1870 
1871     if (name != NULL)
1872     {
1873 	id = syn_name2id((char_u *)"Normal");
1874 	if (id > 0)
1875 	{
1876 	    vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1877 	    HL_TABLE()[id - 1].sg_gui_bg_name = name;
1878 	}
1879     }
1880 }
1881 
1882 /*
1883  * Set foreground color for "Normal" group.  Called by gui_set_fg_color()
1884  * when the color is known.
1885  */
1886     void
1887 hl_set_fg_color_name(
1888     char_u  *name)	    // must have been allocated
1889 {
1890     int	    id;
1891 
1892     if (name != NULL)
1893     {
1894 	id = syn_name2id((char_u *)"Normal");
1895 	if (id > 0)
1896 	{
1897 	    vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1898 	    HL_TABLE()[id - 1].sg_gui_fg_name = name;
1899 	}
1900     }
1901 }
1902 
1903 /*
1904  * Return the handle for a font name.
1905  * Returns NOFONT when failed.
1906  */
1907     static GuiFont
1908 font_name2handle(char_u *name)
1909 {
1910     if (STRCMP(name, "NONE") == 0)
1911 	return NOFONT;
1912 
1913     return gui_mch_get_font(name, TRUE);
1914 }
1915 
1916 # ifdef FEAT_XFONTSET
1917 /*
1918  * Return the handle for a fontset name.
1919  * Returns NOFONTSET when failed.
1920  */
1921     static GuiFontset
1922 fontset_name2handle(char_u *name, int fixed_width)
1923 {
1924     if (STRCMP(name, "NONE") == 0)
1925 	return NOFONTSET;
1926 
1927     return gui_mch_get_fontset(name, TRUE, fixed_width);
1928 }
1929 # endif
1930 
1931 /*
1932  * Get the font or fontset for one highlight group.
1933  */
1934     static void
1935 hl_do_font(
1936     int		idx,
1937     char_u	*arg,
1938     int		do_normal,		// set normal font
1939     int		do_menu UNUSED,		// set menu font
1940     int		do_tooltip UNUSED,	// set tooltip font
1941     int		free_font)		// free current font/fontset
1942 {
1943 # ifdef FEAT_XFONTSET
1944     // If 'guifontset' is not empty, first try using the name as a
1945     // fontset.  If that doesn't work, use it as a font name.
1946     if (*p_guifontset != NUL
1947 #  ifdef FONTSET_ALWAYS
1948 	|| do_menu
1949 #  endif
1950 #  ifdef FEAT_BEVAL_TIP
1951 	// In Athena & Motif, the Tooltip highlight group is always a fontset
1952 	|| do_tooltip
1953 #  endif
1954 	    )
1955     {
1956 	if (free_font)
1957 	    gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1958 	HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1959 #  ifdef FONTSET_ALWAYS
1960 		|| do_menu
1961 #  endif
1962 #  ifdef FEAT_BEVAL_TIP
1963 		|| do_tooltip
1964 #  endif
1965 		);
1966     }
1967     if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1968     {
1969 	// If it worked and it's the Normal group, use it as the normal
1970 	// fontset.  Same for the Menu group.
1971 	if (do_normal)
1972 	    gui_init_font(arg, TRUE);
1973 #   if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1974 	if (do_menu)
1975 	{
1976 #    ifdef FONTSET_ALWAYS
1977 	    gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1978 #    else
1979 	    // YIKES!  This is a bug waiting to crash the program
1980 	    gui.menu_font = HL_TABLE()[idx].sg_fontset;
1981 #    endif
1982 	    gui_mch_new_menu_font();
1983 	}
1984 #    ifdef FEAT_BEVAL_GUI
1985 	if (do_tooltip)
1986 	{
1987 	    // The Athena widget set cannot currently handle switching between
1988 	    // displaying a single font and a fontset.
1989 	    // If the XtNinternational resource is set to True at widget
1990 	    // creation, then a fontset is always used, otherwise an
1991 	    // XFontStruct is used.
1992 	    gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1993 	    gui_mch_new_tooltip_font();
1994 	}
1995 #    endif
1996 #   endif
1997     }
1998     else
1999 # endif
2000     {
2001 	if (free_font)
2002 	    gui_mch_free_font(HL_TABLE()[idx].sg_font);
2003 	HL_TABLE()[idx].sg_font = font_name2handle(arg);
2004 	// If it worked and it's the Normal group, use it as the
2005 	// normal font.  Same for the Menu group.
2006 	if (HL_TABLE()[idx].sg_font != NOFONT)
2007 	{
2008 	    if (do_normal)
2009 		gui_init_font(arg, FALSE);
2010 #ifndef FONTSET_ALWAYS
2011 # if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2012 	    if (do_menu)
2013 	    {
2014 		gui.menu_font = HL_TABLE()[idx].sg_font;
2015 		gui_mch_new_menu_font();
2016 	    }
2017 # endif
2018 #endif
2019 	}
2020     }
2021 }
2022 
2023 #endif // FEAT_GUI
2024 
2025 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2026 /*
2027  * Return the handle for a color name.
2028  * Returns INVALCOLOR when failed.
2029  */
2030     guicolor_T
2031 color_name2handle(char_u *name)
2032 {
2033     if (STRCMP(name, "NONE") == 0)
2034 	return INVALCOLOR;
2035 
2036     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2037     {
2038 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2039 	if (gui.in_use)
2040 #endif
2041 #ifdef FEAT_GUI
2042 	    return gui.norm_pixel;
2043 #endif
2044 #ifdef FEAT_TERMGUICOLORS
2045 	if (cterm_normal_fg_gui_color != INVALCOLOR)
2046 	    return cterm_normal_fg_gui_color;
2047 	// Guess that the foreground is black or white.
2048 	return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2049 #endif
2050     }
2051     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2052     {
2053 #if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2054 	if (gui.in_use)
2055 #endif
2056 #ifdef FEAT_GUI
2057 	    return gui.back_pixel;
2058 #endif
2059 #ifdef FEAT_TERMGUICOLORS
2060 	if (cterm_normal_bg_gui_color != INVALCOLOR)
2061 	    return cterm_normal_bg_gui_color;
2062 	// Guess that the background is white or black.
2063 	return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2064 #endif
2065     }
2066 
2067     return GUI_GET_COLOR(name);
2068 }
2069 #endif
2070 
2071 /*
2072  * Table with the specifications for an attribute number.
2073  * Note that this table is used by ALL buffers.  This is required because the
2074  * GUI can redraw at any time for any buffer.
2075  */
2076 static garray_T	term_attr_table = {0, 0, 0, 0, NULL};
2077 
2078 #define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2079 
2080 static garray_T	cterm_attr_table = {0, 0, 0, 0, NULL};
2081 
2082 #define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2083 
2084 #ifdef FEAT_GUI
2085 static garray_T	gui_attr_table = {0, 0, 0, 0, NULL};
2086 
2087 #define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2088 #endif
2089 
2090 /*
2091  * Return the attr number for a set of colors and font.
2092  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2093  * if the combination is new.
2094  * Return 0 for error (no more room).
2095  */
2096     static int
2097 get_attr_entry(garray_T *table, attrentry_T *aep)
2098 {
2099     int		i;
2100     attrentry_T	*taep;
2101     static int	recursive = FALSE;
2102 
2103     /*
2104      * Init the table, in case it wasn't done yet.
2105      */
2106     table->ga_itemsize = sizeof(attrentry_T);
2107     table->ga_growsize = 7;
2108 
2109     /*
2110      * Try to find an entry with the same specifications.
2111      */
2112     for (i = 0; i < table->ga_len; ++i)
2113     {
2114 	taep = &(((attrentry_T *)table->ga_data)[i]);
2115 	if (	   aep->ae_attr == taep->ae_attr
2116 		&& (
2117 #ifdef FEAT_GUI
2118 		       (table == &gui_attr_table
2119 			&& (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2120 			    && aep->ae_u.gui.bg_color
2121 						    == taep->ae_u.gui.bg_color
2122 			    && aep->ae_u.gui.sp_color
2123 						    == taep->ae_u.gui.sp_color
2124 			    && aep->ae_u.gui.font == taep->ae_u.gui.font
2125 #  ifdef FEAT_XFONTSET
2126 			    && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2127 #  endif
2128 			    ))
2129 		    ||
2130 #endif
2131 		       (table == &term_attr_table
2132 			&& (aep->ae_u.term.start == NULL)
2133 					    == (taep->ae_u.term.start == NULL)
2134 			&& (aep->ae_u.term.start == NULL
2135 			    || STRCMP(aep->ae_u.term.start,
2136 						  taep->ae_u.term.start) == 0)
2137 			&& (aep->ae_u.term.stop == NULL)
2138 					     == (taep->ae_u.term.stop == NULL)
2139 			&& (aep->ae_u.term.stop == NULL
2140 			    || STRCMP(aep->ae_u.term.stop,
2141 						  taep->ae_u.term.stop) == 0))
2142 		    || (table == &cterm_attr_table
2143 			    && aep->ae_u.cterm.fg_color
2144 						  == taep->ae_u.cterm.fg_color
2145 			    && aep->ae_u.cterm.bg_color
2146 						  == taep->ae_u.cterm.bg_color
2147 			    && aep->ae_u.cterm.ul_color
2148 						  == taep->ae_u.cterm.ul_color
2149 #ifdef FEAT_TERMGUICOLORS
2150 			    && aep->ae_u.cterm.fg_rgb
2151 						    == taep->ae_u.cterm.fg_rgb
2152 			    && aep->ae_u.cterm.bg_rgb
2153 						    == taep->ae_u.cterm.bg_rgb
2154 			    && aep->ae_u.cterm.ul_rgb
2155 						    == taep->ae_u.cterm.ul_rgb
2156 #endif
2157 		       )))
2158 
2159 	return i + ATTR_OFF;
2160     }
2161 
2162     if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2163     {
2164 	/*
2165 	 * Running out of attribute entries!  remove all attributes, and
2166 	 * compute new ones for all groups.
2167 	 * When called recursively, we are really out of numbers.
2168 	 */
2169 	if (recursive)
2170 	{
2171 	    emsg(_("E424: Too many different highlighting attributes in use"));
2172 	    return 0;
2173 	}
2174 	recursive = TRUE;
2175 
2176 	clear_hl_tables();
2177 
2178 	must_redraw = CLEAR;
2179 
2180 	for (i = 0; i < highlight_ga.ga_len; ++i)
2181 	    set_hl_attr(i);
2182 
2183 	recursive = FALSE;
2184     }
2185 
2186     /*
2187      * This is a new combination of colors and font, add an entry.
2188      */
2189     if (ga_grow(table, 1) == FAIL)
2190 	return 0;
2191 
2192     taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
2193     CLEAR_POINTER(taep);
2194     taep->ae_attr = aep->ae_attr;
2195 #ifdef FEAT_GUI
2196     if (table == &gui_attr_table)
2197     {
2198 	taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2199 	taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2200 	taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2201 	taep->ae_u.gui.font = aep->ae_u.gui.font;
2202 # ifdef FEAT_XFONTSET
2203 	taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2204 # endif
2205     }
2206 #endif
2207     if (table == &term_attr_table)
2208     {
2209 	if (aep->ae_u.term.start == NULL)
2210 	    taep->ae_u.term.start = NULL;
2211 	else
2212 	    taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2213 	if (aep->ae_u.term.stop == NULL)
2214 	    taep->ae_u.term.stop = NULL;
2215 	else
2216 	    taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2217     }
2218     else if (table == &cterm_attr_table)
2219     {
2220 	taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2221 	taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
2222 	taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
2223 #ifdef FEAT_TERMGUICOLORS
2224 	taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2225 	taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
2226 	taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
2227 #endif
2228     }
2229     ++table->ga_len;
2230     return (table->ga_len - 1 + ATTR_OFF);
2231 }
2232 
2233 #if defined(FEAT_TERMINAL) || defined(PROTO)
2234 /*
2235  * Get an attribute index for a cterm entry.
2236  * Uses an existing entry when possible or adds one when needed.
2237  */
2238     int
2239 get_cterm_attr_idx(int attr, int fg, int bg)
2240 {
2241     attrentry_T		at_en;
2242 
2243     CLEAR_FIELD(at_en);
2244 #ifdef FEAT_TERMGUICOLORS
2245     at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2246     at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2247     at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
2248 #endif
2249     at_en.ae_attr = attr;
2250     at_en.ae_u.cterm.fg_color = fg;
2251     at_en.ae_u.cterm.bg_color = bg;
2252     at_en.ae_u.cterm.ul_color = 0;
2253     return get_attr_entry(&cterm_attr_table, &at_en);
2254 }
2255 #endif
2256 
2257 #if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2258 /*
2259  * Get an attribute index for a 'termguicolors' entry.
2260  * Uses an existing entry when possible or adds one when needed.
2261  */
2262     int
2263 get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2264 {
2265     attrentry_T		at_en;
2266 
2267     CLEAR_FIELD(at_en);
2268     at_en.ae_attr = attr;
2269     if (fg == INVALCOLOR && bg == INVALCOLOR)
2270     {
2271 	// If both GUI colors are not set fall back to the cterm colors.  Helps
2272 	// if the GUI only has an attribute, such as undercurl.
2273 	at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2274 	at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2275     }
2276     else
2277     {
2278 	at_en.ae_u.cterm.fg_rgb = fg;
2279 	at_en.ae_u.cterm.bg_rgb = bg;
2280     }
2281     at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
2282     return get_attr_entry(&cterm_attr_table, &at_en);
2283 }
2284 #endif
2285 
2286 #if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2287 /*
2288  * Get an attribute index for a cterm entry.
2289  * Uses an existing entry when possible or adds one when needed.
2290  */
2291     int
2292 get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2293 {
2294     attrentry_T		at_en;
2295 
2296     CLEAR_FIELD(at_en);
2297     at_en.ae_attr = attr;
2298     at_en.ae_u.gui.fg_color = fg;
2299     at_en.ae_u.gui.bg_color = bg;
2300     return get_attr_entry(&gui_attr_table, &at_en);
2301 }
2302 #endif
2303 
2304 /*
2305  * Clear all highlight tables.
2306  */
2307     void
2308 clear_hl_tables(void)
2309 {
2310     int		i;
2311     attrentry_T	*taep;
2312 
2313 #ifdef FEAT_GUI
2314     ga_clear(&gui_attr_table);
2315 #endif
2316     for (i = 0; i < term_attr_table.ga_len; ++i)
2317     {
2318 	taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2319 	vim_free(taep->ae_u.term.start);
2320 	vim_free(taep->ae_u.term.stop);
2321     }
2322     ga_clear(&term_attr_table);
2323     ga_clear(&cterm_attr_table);
2324 }
2325 
2326 /*
2327  * Combine special attributes (e.g., for spelling) with other attributes
2328  * (e.g., for syntax highlighting).
2329  * "prim_attr" overrules "char_attr".
2330  * This creates a new group when required.
2331  * Since we expect there to be few spelling mistakes we don't cache the
2332  * result.
2333  * Return the resulting attributes.
2334  */
2335     int
2336 hl_combine_attr(int char_attr, int prim_attr)
2337 {
2338     attrentry_T *char_aep = NULL;
2339     attrentry_T *spell_aep;
2340     attrentry_T new_en;
2341 
2342     if (char_attr == 0)
2343 	return prim_attr;
2344     if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2345 	return ATTR_COMBINE(char_attr, prim_attr);
2346 #ifdef FEAT_GUI
2347     if (gui.in_use)
2348     {
2349 	if (char_attr > HL_ALL)
2350 	    char_aep = syn_gui_attr2entry(char_attr);
2351 	if (char_aep != NULL)
2352 	    new_en = *char_aep;
2353 	else
2354 	{
2355 	    CLEAR_FIELD(new_en);
2356 	    new_en.ae_u.gui.fg_color = INVALCOLOR;
2357 	    new_en.ae_u.gui.bg_color = INVALCOLOR;
2358 	    new_en.ae_u.gui.sp_color = INVALCOLOR;
2359 	    if (char_attr <= HL_ALL)
2360 		new_en.ae_attr = char_attr;
2361 	}
2362 
2363 	if (prim_attr <= HL_ALL)
2364 	    new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2365 	else
2366 	{
2367 	    spell_aep = syn_gui_attr2entry(prim_attr);
2368 	    if (spell_aep != NULL)
2369 	    {
2370 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2371 							   spell_aep->ae_attr);
2372 		if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2373 		    new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2374 		if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2375 		    new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2376 		if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2377 		    new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2378 		if (spell_aep->ae_u.gui.font != NOFONT)
2379 		    new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2380 # ifdef FEAT_XFONTSET
2381 		if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2382 		    new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2383 # endif
2384 	    }
2385 	}
2386 	return get_attr_entry(&gui_attr_table, &new_en);
2387     }
2388 #endif
2389 
2390     if (IS_CTERM)
2391     {
2392 	if (char_attr > HL_ALL)
2393 	    char_aep = syn_cterm_attr2entry(char_attr);
2394 	if (char_aep != NULL)
2395 	    new_en = *char_aep;
2396 	else
2397 	{
2398 	    CLEAR_FIELD(new_en);
2399 #ifdef FEAT_TERMGUICOLORS
2400 	    new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2401 	    new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2402 	    new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
2403 #endif
2404 	    if (char_attr <= HL_ALL)
2405 		new_en.ae_attr = char_attr;
2406 	}
2407 
2408 	if (prim_attr <= HL_ALL)
2409 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2410 	else
2411 	{
2412 	    spell_aep = syn_cterm_attr2entry(prim_attr);
2413 	    if (spell_aep != NULL)
2414 	    {
2415 		new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2416 							   spell_aep->ae_attr);
2417 		if (spell_aep->ae_u.cterm.fg_color > 0)
2418 		    new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2419 		if (spell_aep->ae_u.cterm.bg_color > 0)
2420 		    new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
2421 		if (spell_aep->ae_u.cterm.ul_color > 0)
2422 		    new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
2423 #ifdef FEAT_TERMGUICOLORS
2424 		// If both fg and bg are not set fall back to cterm colors.
2425 		// Helps for SpellBad which uses undercurl in the GUI.
2426 		if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2427 			&& COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2428 		{
2429 		    if (spell_aep->ae_u.cterm.fg_color > 0)
2430 			new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2431 		    if (spell_aep->ae_u.cterm.bg_color > 0)
2432 			new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2433 		}
2434 		else
2435 		{
2436 		    if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2437 			new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2438 		    if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2439 			new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2440 		}
2441 		if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2442 		    new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
2443 #endif
2444 	    }
2445 	}
2446 	return get_attr_entry(&cterm_attr_table, &new_en);
2447     }
2448 
2449     if (char_attr > HL_ALL)
2450 	char_aep = syn_term_attr2entry(char_attr);
2451     if (char_aep != NULL)
2452 	new_en = *char_aep;
2453     else
2454     {
2455 	CLEAR_FIELD(new_en);
2456 	if (char_attr <= HL_ALL)
2457 	    new_en.ae_attr = char_attr;
2458     }
2459 
2460     if (prim_attr <= HL_ALL)
2461 	new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2462     else
2463     {
2464 	spell_aep = syn_term_attr2entry(prim_attr);
2465 	if (spell_aep != NULL)
2466 	{
2467 	    new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2468 	    if (spell_aep->ae_u.term.start != NULL)
2469 	    {
2470 		new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2471 		new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2472 	    }
2473 	}
2474     }
2475     return get_attr_entry(&term_attr_table, &new_en);
2476 }
2477 
2478 #ifdef FEAT_GUI
2479     attrentry_T *
2480 syn_gui_attr2entry(int attr)
2481 {
2482     attr -= ATTR_OFF;
2483     if (attr >= gui_attr_table.ga_len)	    // did ":syntax clear"
2484 	return NULL;
2485     return &(GUI_ATTR_ENTRY(attr));
2486 }
2487 #endif
2488 
2489 /*
2490  * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2491  * Only to be used when "attr" > HL_ALL.
2492  */
2493     int
2494 syn_attr2attr(int attr)
2495 {
2496     attrentry_T	*aep;
2497 
2498 #ifdef FEAT_GUI
2499     if (gui.in_use)
2500 	aep = syn_gui_attr2entry(attr);
2501     else
2502 #endif
2503 	if (IS_CTERM)
2504 	    aep = syn_cterm_attr2entry(attr);
2505 	else
2506 	    aep = syn_term_attr2entry(attr);
2507 
2508     if (aep == NULL)	    // highlighting not set
2509 	return 0;
2510     return aep->ae_attr;
2511 }
2512 
2513 
2514     attrentry_T *
2515 syn_term_attr2entry(int attr)
2516 {
2517     attr -= ATTR_OFF;
2518     if (attr >= term_attr_table.ga_len)	    // did ":syntax clear"
2519 	return NULL;
2520     return &(TERM_ATTR_ENTRY(attr));
2521 }
2522 
2523     attrentry_T *
2524 syn_cterm_attr2entry(int attr)
2525 {
2526     attr -= ATTR_OFF;
2527     if (attr >= cterm_attr_table.ga_len)	// did ":syntax clear"
2528 	return NULL;
2529     return &(CTERM_ATTR_ENTRY(attr));
2530 }
2531 
2532 #define LIST_ATTR   1
2533 #define LIST_STRING 2
2534 #define LIST_INT    3
2535 
2536     static void
2537 highlight_list_one(int id)
2538 {
2539     hl_group_T	    *sgp;
2540     int		    didh = FALSE;
2541 
2542     sgp = &HL_TABLE()[id - 1];	    // index is ID minus one
2543 
2544     if (message_filtered(sgp->sg_name))
2545 	return;
2546 
2547     didh = highlight_list_arg(id, didh, LIST_ATTR,
2548 				    sgp->sg_term, NULL, "term");
2549     didh = highlight_list_arg(id, didh, LIST_STRING,
2550 				    0, sgp->sg_start, "start");
2551     didh = highlight_list_arg(id, didh, LIST_STRING,
2552 				    0, sgp->sg_stop, "stop");
2553 
2554     didh = highlight_list_arg(id, didh, LIST_ATTR,
2555 				    sgp->sg_cterm, NULL, "cterm");
2556     didh = highlight_list_arg(id, didh, LIST_INT,
2557 				    sgp->sg_cterm_fg, NULL, "ctermfg");
2558     didh = highlight_list_arg(id, didh, LIST_INT,
2559 				    sgp->sg_cterm_bg, NULL, "ctermbg");
2560     didh = highlight_list_arg(id, didh, LIST_INT,
2561 				    sgp->sg_cterm_ul, NULL, "ctermul");
2562 
2563 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
2564     didh = highlight_list_arg(id, didh, LIST_ATTR,
2565 				    sgp->sg_gui, NULL, "gui");
2566     didh = highlight_list_arg(id, didh, LIST_STRING,
2567 				    0, sgp->sg_gui_fg_name, "guifg");
2568     didh = highlight_list_arg(id, didh, LIST_STRING,
2569 				    0, sgp->sg_gui_bg_name, "guibg");
2570     didh = highlight_list_arg(id, didh, LIST_STRING,
2571 				    0, sgp->sg_gui_sp_name, "guisp");
2572 #endif
2573 #ifdef FEAT_GUI
2574     didh = highlight_list_arg(id, didh, LIST_STRING,
2575 				    0, sgp->sg_font_name, "font");
2576 #endif
2577 
2578     if (sgp->sg_link && !got_int)
2579     {
2580 	(void)syn_list_header(didh, 9999, id);
2581 	didh = TRUE;
2582 	msg_puts_attr("links to", HL_ATTR(HLF_D));
2583 	msg_putchar(' ');
2584 	msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2585     }
2586 
2587     if (!didh)
2588 	highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2589 #ifdef FEAT_EVAL
2590     if (p_verbose > 0)
2591 	last_set_msg(sgp->sg_script_ctx);
2592 #endif
2593 }
2594 
2595     static int
2596 highlight_list_arg(
2597     int		id,
2598     int		didh,
2599     int		type,
2600     int		iarg,
2601     char_u	*sarg,
2602     char	*name)
2603 {
2604     char_u	buf[100];
2605     char_u	*ts;
2606     int		i;
2607 
2608     if (got_int)
2609 	return FALSE;
2610     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2611     {
2612 	ts = buf;
2613 	if (type == LIST_INT)
2614 	    sprintf((char *)buf, "%d", iarg - 1);
2615 	else if (type == LIST_STRING)
2616 	    ts = sarg;
2617 	else // type == LIST_ATTR
2618 	{
2619 	    buf[0] = NUL;
2620 	    for (i = 0; hl_attr_table[i] != 0; ++i)
2621 	    {
2622 		if (iarg & hl_attr_table[i])
2623 		{
2624 		    if (buf[0] != NUL)
2625 			vim_strcat(buf, (char_u *)",", 100);
2626 		    vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2627 		    iarg &= ~hl_attr_table[i];	    // don't want "inverse"
2628 		}
2629 	    }
2630 	}
2631 
2632 	(void)syn_list_header(didh,
2633 			       (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2634 	didh = TRUE;
2635 	if (!got_int)
2636 	{
2637 	    if (*name != NUL)
2638 	    {
2639 		msg_puts_attr(name, HL_ATTR(HLF_D));
2640 		msg_puts_attr("=", HL_ATTR(HLF_D));
2641 	    }
2642 	    msg_outtrans(ts);
2643 	}
2644     }
2645     return didh;
2646 }
2647 
2648 #if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2649 /*
2650  * Return "1" if highlight group "id" has attribute "flag".
2651  * Return NULL otherwise.
2652  */
2653     char_u *
2654 highlight_has_attr(
2655     int		id,
2656     int		flag,
2657     int		modec)	// 'g' for GUI, 'c' for cterm, 't' for term
2658 {
2659     int		attr;
2660 
2661     if (id <= 0 || id > highlight_ga.ga_len)
2662 	return NULL;
2663 
2664 #if defined(FEAT_GUI) || defined(FEAT_EVAL)
2665     if (modec == 'g')
2666 	attr = HL_TABLE()[id - 1].sg_gui;
2667     else
2668 #endif
2669 	 if (modec == 'c')
2670 	attr = HL_TABLE()[id - 1].sg_cterm;
2671     else
2672 	attr = HL_TABLE()[id - 1].sg_term;
2673 
2674     if (attr & flag)
2675 	return (char_u *)"1";
2676     return NULL;
2677 }
2678 #endif
2679 
2680 #if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2681 /*
2682  * Return color name of highlight group "id".
2683  */
2684     char_u *
2685 highlight_color(
2686     int		id,
2687     char_u	*what,	// "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
2688     int		modec)	// 'g' for GUI, 'c' for cterm, 't' for term
2689 {
2690     static char_u	name[20];
2691     int			n;
2692     int			fg = FALSE;
2693     int			sp = FALSE;
2694     int			ul = FALSE;
2695     int			font = FALSE;
2696 
2697     if (id <= 0 || id > highlight_ga.ga_len)
2698 	return NULL;
2699 
2700     if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2701 	fg = TRUE;
2702     else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2703 	     && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2704 	font = TRUE;
2705     else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2706 	sp = TRUE;
2707     else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
2708 	ul = TRUE;
2709     else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2710 	return NULL;
2711     if (modec == 'g')
2712     {
2713 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2714 #  ifdef FEAT_GUI
2715 	// return font name
2716 	if (font)
2717 	    return HL_TABLE()[id - 1].sg_font_name;
2718 #  endif
2719 
2720 	// return #RRGGBB form (only possible when GUI is running)
2721 	if ((USE_24BIT) && what[2] == '#')
2722 	{
2723 	    guicolor_T		color;
2724 	    long_u		rgb;
2725 	    static char_u	buf[10];
2726 
2727 	    if (fg)
2728 		color = HL_TABLE()[id - 1].sg_gui_fg;
2729 	    else if (sp)
2730 		color = HL_TABLE()[id - 1].sg_gui_sp;
2731 	    else
2732 		color = HL_TABLE()[id - 1].sg_gui_bg;
2733 	    if (color == INVALCOLOR)
2734 		return NULL;
2735 	    rgb = (long_u)GUI_MCH_GET_RGB(color);
2736 	    sprintf((char *)buf, "#%02x%02x%02x",
2737 				      (unsigned)(rgb >> 16),
2738 				      (unsigned)(rgb >> 8) & 255,
2739 				      (unsigned)rgb & 255);
2740 	    return buf;
2741 	}
2742 # endif
2743 	if (fg)
2744 	    return (HL_TABLE()[id - 1].sg_gui_fg_name);
2745 	if (sp)
2746 	    return (HL_TABLE()[id - 1].sg_gui_sp_name);
2747 	return (HL_TABLE()[id - 1].sg_gui_bg_name);
2748     }
2749     if (font || sp)
2750 	return NULL;
2751     if (modec == 'c')
2752     {
2753 	if (fg)
2754 	    n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2755 	else if (ul)
2756 	    n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
2757 	else
2758 	    n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2759 	if (n < 0)
2760 	    return NULL;
2761 	sprintf((char *)name, "%d", n);
2762 	return name;
2763     }
2764     // term doesn't have color
2765     return NULL;
2766 }
2767 #endif
2768 
2769 #if (defined(FEAT_SYN_HL) \
2770 	    && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2771 	&& defined(FEAT_PRINTER)) || defined(PROTO)
2772 /*
2773  * Return color name of highlight group "id" as RGB value.
2774  */
2775     long_u
2776 highlight_gui_color_rgb(
2777     int		id,
2778     int		fg)	// TRUE = fg, FALSE = bg
2779 {
2780     guicolor_T	color;
2781 
2782     if (id <= 0 || id > highlight_ga.ga_len)
2783 	return 0L;
2784 
2785     if (fg)
2786 	color = HL_TABLE()[id - 1].sg_gui_fg;
2787     else
2788 	color = HL_TABLE()[id - 1].sg_gui_bg;
2789 
2790     if (color == INVALCOLOR)
2791 	return 0L;
2792 
2793     return GUI_MCH_GET_RGB(color);
2794 }
2795 #endif
2796 
2797 /*
2798  * Output the syntax list header.
2799  * Return TRUE when started a new line.
2800  */
2801     int
2802 syn_list_header(
2803     int	    did_header,		// did header already
2804     int	    outlen,		// length of string that comes
2805     int	    id)			// highlight group id
2806 {
2807     int	    endcol = 19;
2808     int	    newline = TRUE;
2809     int	    name_col = 0;
2810 
2811     if (!did_header)
2812     {
2813 	msg_putchar('\n');
2814 	if (got_int)
2815 	    return TRUE;
2816 	msg_outtrans(HL_TABLE()[id - 1].sg_name);
2817 	name_col = msg_col;
2818 	endcol = 15;
2819     }
2820     else if (msg_col + outlen + 1 >= Columns)
2821     {
2822 	msg_putchar('\n');
2823 	if (got_int)
2824 	    return TRUE;
2825     }
2826     else
2827     {
2828 	if (msg_col >= endcol)	// wrap around is like starting a new line
2829 	    newline = FALSE;
2830     }
2831 
2832     if (msg_col >= endcol)	// output at least one space
2833 	endcol = msg_col + 1;
2834     if (Columns <= endcol)	// avoid hang for tiny window
2835 	endcol = Columns - 1;
2836 
2837     msg_advance(endcol);
2838 
2839     // Show "xxx" with the attributes.
2840     if (!did_header)
2841     {
2842 	if (endcol == Columns - 1 && endcol <= name_col)
2843 	    msg_putchar(' ');
2844 	msg_puts_attr("xxx", syn_id2attr(id));
2845 	msg_putchar(' ');
2846     }
2847 
2848     return newline;
2849 }
2850 
2851 /*
2852  * Set the attribute numbers for a highlight group.
2853  * Called after one of the attributes has changed.
2854  */
2855     static void
2856 set_hl_attr(
2857     int		idx)	    // index in array
2858 {
2859     attrentry_T	    at_en;
2860     hl_group_T	    *sgp = HL_TABLE() + idx;
2861 
2862     // The "Normal" group doesn't need an attribute number
2863     if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2864 	return;
2865 
2866 #ifdef FEAT_GUI
2867     /*
2868      * For the GUI mode: If there are other than "normal" highlighting
2869      * attributes, need to allocate an attr number.
2870      */
2871     if (sgp->sg_gui_fg == INVALCOLOR
2872 	    && sgp->sg_gui_bg == INVALCOLOR
2873 	    && sgp->sg_gui_sp == INVALCOLOR
2874 	    && sgp->sg_font == NOFONT
2875 # ifdef FEAT_XFONTSET
2876 	    && sgp->sg_fontset == NOFONTSET
2877 # endif
2878 	    )
2879     {
2880 	sgp->sg_gui_attr = sgp->sg_gui;
2881     }
2882     else
2883     {
2884 	at_en.ae_attr = sgp->sg_gui;
2885 	at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2886 	at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2887 	at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2888 	at_en.ae_u.gui.font = sgp->sg_font;
2889 # ifdef FEAT_XFONTSET
2890 	at_en.ae_u.gui.fontset = sgp->sg_fontset;
2891 # endif
2892 	sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2893     }
2894 #endif
2895     /*
2896      * For the term mode: If there are other than "normal" highlighting
2897      * attributes, need to allocate an attr number.
2898      */
2899     if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2900 	sgp->sg_term_attr = sgp->sg_term;
2901     else
2902     {
2903 	at_en.ae_attr = sgp->sg_term;
2904 	at_en.ae_u.term.start = sgp->sg_start;
2905 	at_en.ae_u.term.stop = sgp->sg_stop;
2906 	sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2907     }
2908 
2909     /*
2910      * For the color term mode: If there are other than "normal"
2911      * highlighting attributes, need to allocate an attr number.
2912      */
2913     if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
2914 # ifdef FEAT_TERMGUICOLORS
2915 	    && sgp->sg_gui_fg == INVALCOLOR
2916 	    && sgp->sg_gui_bg == INVALCOLOR
2917 	    && sgp->sg_gui_sp == INVALCOLOR
2918 # endif
2919 	    )
2920 	sgp->sg_cterm_attr = sgp->sg_cterm;
2921     else
2922     {
2923 	at_en.ae_attr = sgp->sg_cterm;
2924 	at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2925 	at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
2926 	at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
2927 # ifdef FEAT_TERMGUICOLORS
2928 #  ifdef MSWIN
2929 #   ifdef VIMDLL
2930 	// Only when not using the GUI.
2931 	if (!gui.in_use && !gui.starting)
2932 #   endif
2933 	{
2934 	    int id;
2935 	    guicolor_T fg, bg;
2936 
2937 	    id = syn_name2id((char_u *)"Normal");
2938 	    if (id > 0)
2939 	    {
2940 		syn_id2colors(id, &fg, &bg);
2941 		if (sgp->sg_gui_fg == INVALCOLOR)
2942 		    sgp->sg_gui_fg = fg;
2943 		if (sgp->sg_gui_bg == INVALCOLOR)
2944 		    sgp->sg_gui_bg = bg;
2945 	    }
2946 
2947 	}
2948 #  endif
2949 	at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2950 	at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
2951 	// Only use the underline/undercurl color when used, it may clear the
2952 	// background color if not supported.
2953 	if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
2954 	    at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
2955 	else
2956 	    at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
2957 	if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2958 		&& at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2959 	{
2960 	    // If both fg and bg are invalid fall back to the cterm colors.
2961 	    // Helps when the GUI only uses an attribute, e.g. undercurl.
2962 	    at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2963 	    at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2964 	}
2965 # endif
2966 	sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2967     }
2968 }
2969 
2970 /*
2971  * Lookup a highlight group name and return its ID.
2972  * If it is not found, 0 is returned.
2973  */
2974     int
2975 syn_name2id(char_u *name)
2976 {
2977     int		i;
2978     char_u	name_u[200];
2979 
2980     // Avoid using stricmp() too much, it's slow on some systems
2981     // Avoid alloc()/free(), these are slow too.  ID names over 200 chars
2982     // don't deserve to be found!
2983     vim_strncpy(name_u, name, 199);
2984     vim_strup(name_u);
2985     for (i = highlight_ga.ga_len; --i >= 0; )
2986 	if (HL_TABLE()[i].sg_name_u != NULL
2987 		&& STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2988 	    break;
2989     return i + 1;
2990 }
2991 
2992 /*
2993  * Lookup a highlight group name and return its attributes.
2994  * Return zero if not found.
2995  */
2996     int
2997 syn_name2attr(char_u *name)
2998 {
2999     int id = syn_name2id(name);
3000 
3001     if (id != 0)
3002 	return syn_id2attr(id);
3003     return 0;
3004 }
3005 
3006 #if defined(FEAT_EVAL) || defined(PROTO)
3007 /*
3008  * Return TRUE if highlight group "name" exists.
3009  */
3010     int
3011 highlight_exists(char_u *name)
3012 {
3013     return (syn_name2id(name) > 0);
3014 }
3015 
3016 # if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3017 /*
3018  * Return the name of highlight group "id".
3019  * When not a valid ID return an empty string.
3020  */
3021     char_u *
3022 syn_id2name(int id)
3023 {
3024     if (id <= 0 || id > highlight_ga.ga_len)
3025 	return (char_u *)"";
3026     return HL_TABLE()[id - 1].sg_name;
3027 }
3028 # endif
3029 #endif
3030 
3031 /*
3032  * Like syn_name2id(), but take a pointer + length argument.
3033  */
3034     int
3035 syn_namen2id(char_u *linep, int len)
3036 {
3037     char_u  *name;
3038     int	    id = 0;
3039 
3040     name = vim_strnsave(linep, len);
3041     if (name != NULL)
3042     {
3043 	id = syn_name2id(name);
3044 	vim_free(name);
3045     }
3046     return id;
3047 }
3048 
3049 /*
3050  * Find highlight group name in the table and return its ID.
3051  * The argument is a pointer to the name and the length of the name.
3052  * If it doesn't exist yet, a new entry is created.
3053  * Return 0 for failure.
3054  */
3055     int
3056 syn_check_group(char_u *pp, int len)
3057 {
3058     int	    id;
3059     char_u  *name;
3060 
3061     name = vim_strnsave(pp, len);
3062     if (name == NULL)
3063 	return 0;
3064 
3065     id = syn_name2id(name);
3066     if (id == 0)			// doesn't exist yet
3067 	id = syn_add_group(name);
3068     else
3069 	vim_free(name);
3070     return id;
3071 }
3072 
3073 /*
3074  * Add new highlight group and return its ID.
3075  * "name" must be an allocated string, it will be consumed.
3076  * Return 0 for failure.
3077  */
3078     static int
3079 syn_add_group(char_u *name)
3080 {
3081     char_u	*p;
3082     char_u	*name_up;
3083 
3084     // Check that the name is ASCII letters, digits and underscore.
3085     for (p = name; *p != NUL; ++p)
3086     {
3087 	if (!vim_isprintc(*p))
3088 	{
3089 	    emsg(_("E669: Unprintable character in group name"));
3090 	    vim_free(name);
3091 	    return 0;
3092 	}
3093 	else if (!ASCII_ISALNUM(*p) && *p != '_')
3094 	{
3095 	    // This is an error, but since there previously was no check only
3096 	    // give a warning.
3097 	    msg_source(HL_ATTR(HLF_W));
3098 	    msg(_("W18: Invalid character in group name"));
3099 	    break;
3100 	}
3101     }
3102 
3103     /*
3104      * First call for this growarray: init growing array.
3105      */
3106     if (highlight_ga.ga_data == NULL)
3107     {
3108 	highlight_ga.ga_itemsize = sizeof(hl_group_T);
3109 	highlight_ga.ga_growsize = 10;
3110     }
3111 
3112     if (highlight_ga.ga_len >= MAX_HL_ID)
3113     {
3114 	emsg(_("E849: Too many highlight and syntax groups"));
3115 	vim_free(name);
3116 	return 0;
3117     }
3118 
3119     /*
3120      * Make room for at least one other syntax_highlight entry.
3121      */
3122     if (ga_grow(&highlight_ga, 1) == FAIL)
3123     {
3124 	vim_free(name);
3125 	return 0;
3126     }
3127 
3128     name_up = vim_strsave_up(name);
3129     if (name_up == NULL)
3130     {
3131 	vim_free(name);
3132 	return 0;
3133     }
3134 
3135     CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
3136     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
3137     HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
3138 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3139     HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3140     HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
3141     HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
3142 #endif
3143     ++highlight_ga.ga_len;
3144 
3145     return highlight_ga.ga_len;		    // ID is index plus one
3146 }
3147 
3148 /*
3149  * When, just after calling syn_add_group(), an error is discovered, this
3150  * function deletes the new name.
3151  */
3152     static void
3153 syn_unadd_group(void)
3154 {
3155     --highlight_ga.ga_len;
3156     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3157     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3158 }
3159 
3160 /*
3161  * Translate a group ID to highlight attributes.
3162  */
3163     int
3164 syn_id2attr(int hl_id)
3165 {
3166     int		attr;
3167     hl_group_T	*sgp;
3168 
3169     hl_id = syn_get_final_id(hl_id);
3170     sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3171 
3172 #ifdef FEAT_GUI
3173     /*
3174      * Only use GUI attr when the GUI is being used.
3175      */
3176     if (gui.in_use)
3177 	attr = sgp->sg_gui_attr;
3178     else
3179 #endif
3180 	if (IS_CTERM)
3181 	    attr = sgp->sg_cterm_attr;
3182 	else
3183 	    attr = sgp->sg_term_attr;
3184 
3185     return attr;
3186 }
3187 
3188 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3189 /*
3190  * Get the GUI colors and attributes for a group ID.
3191  * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3192  */
3193     int
3194 syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3195 {
3196     hl_group_T	*sgp;
3197 
3198     hl_id = syn_get_final_id(hl_id);
3199     sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3200 
3201     *fgp = sgp->sg_gui_fg;
3202     *bgp = sgp->sg_gui_bg;
3203     return sgp->sg_gui;
3204 }
3205 #endif
3206 
3207 #if (defined(MSWIN) \
3208 	    && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3209 	    && defined(FEAT_TERMGUICOLORS)) \
3210 	|| defined(FEAT_TERMINAL) || defined(PROTO)
3211     void
3212 syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3213 {
3214     hl_group_T	*sgp;
3215 
3216     hl_id = syn_get_final_id(hl_id);
3217     sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3218     *fgp = sgp->sg_cterm_fg - 1;
3219     *bgp = sgp->sg_cterm_bg - 1;
3220 }
3221 #endif
3222 
3223 /*
3224  * Translate a group ID to the final group ID (following links).
3225  */
3226     int
3227 syn_get_final_id(int hl_id)
3228 {
3229     int		count;
3230     hl_group_T	*sgp;
3231 
3232     if (hl_id > highlight_ga.ga_len || hl_id < 1)
3233 	return 0;			// Can be called from eval!!
3234 
3235     /*
3236      * Follow links until there is no more.
3237      * Look out for loops!  Break after 100 links.
3238      */
3239     for (count = 100; --count >= 0; )
3240     {
3241 	sgp = &HL_TABLE()[hl_id - 1];	    // index is ID minus one
3242 	if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3243 	    break;
3244 	hl_id = sgp->sg_link;
3245     }
3246 
3247     return hl_id;
3248 }
3249 
3250 #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3251 /*
3252  * Call this function just after the GUI has started.
3253  * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3254  * It finds the font and color handles for the highlighting groups.
3255  */
3256     void
3257 highlight_gui_started(void)
3258 {
3259     int	    idx;
3260 
3261     // First get the colors from the "Normal" and "Menu" group, if set
3262     if (USE_24BIT)
3263 	set_normal_colors();
3264 
3265     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3266 	gui_do_one_color(idx, FALSE, FALSE);
3267 
3268     highlight_changed();
3269 }
3270 
3271     static void
3272 gui_do_one_color(
3273     int		idx,
3274     int		do_menu UNUSED,	   // TRUE: might set the menu font
3275     int		do_tooltip UNUSED) // TRUE: might set the tooltip font
3276 {
3277     int		didit = FALSE;
3278 
3279 # ifdef FEAT_GUI
3280 #  ifdef FEAT_TERMGUICOLORS
3281     if (gui.in_use)
3282 #  endif
3283 	if (HL_TABLE()[idx].sg_font_name != NULL)
3284 	{
3285 	    hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3286 							    do_tooltip, TRUE);
3287 	    didit = TRUE;
3288 	}
3289 # endif
3290     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3291     {
3292 	HL_TABLE()[idx].sg_gui_fg =
3293 			    color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3294 	didit = TRUE;
3295     }
3296     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3297     {
3298 	HL_TABLE()[idx].sg_gui_bg =
3299 			    color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3300 	didit = TRUE;
3301     }
3302     if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3303     {
3304 	HL_TABLE()[idx].sg_gui_sp =
3305 			    color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3306 	didit = TRUE;
3307     }
3308     if (didit)	// need to get a new attr number
3309 	set_hl_attr(idx);
3310 }
3311 #endif
3312 
3313 #if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3314 /*
3315  * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3316  */
3317     static void
3318 combine_stl_hlt(
3319 	int id,
3320 	int id_S,
3321 	int id_alt,
3322 	int hlcnt,
3323 	int i,
3324 	int hlf,
3325 	int *table)
3326 {
3327     hl_group_T *hlt = HL_TABLE();
3328 
3329     if (id_alt == 0)
3330     {
3331 	CLEAR_POINTER(&hlt[hlcnt + i]);
3332 	hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3333 	hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3334 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
3335 	hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3336 #  endif
3337     }
3338     else
3339 	mch_memmove(&hlt[hlcnt + i],
3340 		    &hlt[id_alt - 1],
3341 		    sizeof(hl_group_T));
3342     hlt[hlcnt + i].sg_link = 0;
3343 
3344     hlt[hlcnt + i].sg_term ^=
3345 	hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3346     if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3347 	hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3348     if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3349 	hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3350     hlt[hlcnt + i].sg_cterm ^=
3351 	hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3352     if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3353 	hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3354     if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3355 	hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3356 #  if defined(FEAT_GUI) || defined(FEAT_EVAL)
3357     hlt[hlcnt + i].sg_gui ^=
3358 	hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3359 #  endif
3360 #  ifdef FEAT_GUI
3361     if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3362 	hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3363     if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3364 	hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3365     if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3366 	hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3367     if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3368 	hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3369 #   ifdef FEAT_XFONTSET
3370     if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3371 	hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3372 #   endif
3373 #  endif
3374     highlight_ga.ga_len = hlcnt + i + 1;
3375     set_hl_attr(hlcnt + i);	// At long last we can apply
3376     table[i] = syn_id2attr(hlcnt + i + 1);
3377 }
3378 #endif
3379 
3380 /*
3381  * Translate the 'highlight' option into attributes in highlight_attr[] and
3382  * set up the user highlights User1..9.  If FEAT_STL_OPT is in use, a set of
3383  * corresponding highlights to use on top of HLF_SNC is computed.
3384  * Called only when the 'highlight' option has been changed and upon first
3385  * screen redraw after any :highlight command.
3386  * Return FAIL when an invalid flag is found in 'highlight'.  OK otherwise.
3387  */
3388     int
3389 highlight_changed(void)
3390 {
3391     int		hlf;
3392     int		i;
3393     char_u	*p;
3394     int		attr;
3395     char_u	*end;
3396     int		id;
3397 #ifdef USER_HIGHLIGHT
3398     char_u      userhl[30];  // use 30 to avoid compiler warning
3399 # ifdef FEAT_STL_OPT
3400     int		id_S = -1;
3401     int		id_SNC = 0;
3402 #  ifdef FEAT_TERMINAL
3403     int		id_ST = 0;
3404     int		id_STNC = 0;
3405 #  endif
3406     int		hlcnt;
3407 # endif
3408 #endif
3409     static int	hl_flags[HLF_COUNT] = HL_FLAGS;
3410 
3411     need_highlight_changed = FALSE;
3412 
3413     /*
3414      * Clear all attributes.
3415      */
3416     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3417 	highlight_attr[hlf] = 0;
3418 
3419     /*
3420      * First set all attributes to their default value.
3421      * Then use the attributes from the 'highlight' option.
3422      */
3423     for (i = 0; i < 2; ++i)
3424     {
3425 	if (i)
3426 	    p = p_hl;
3427 	else
3428 	    p = get_highlight_default();
3429 	if (p == NULL)	    // just in case
3430 	    continue;
3431 
3432 	while (*p)
3433 	{
3434 	    for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3435 		if (hl_flags[hlf] == *p)
3436 		    break;
3437 	    ++p;
3438 	    if (hlf == (int)HLF_COUNT || *p == NUL)
3439 		return FAIL;
3440 
3441 	    /*
3442 	     * Allow several hl_flags to be combined, like "bu" for
3443 	     * bold-underlined.
3444 	     */
3445 	    attr = 0;
3446 	    for ( ; *p && *p != ','; ++p)	    // parse up to comma
3447 	    {
3448 		if (VIM_ISWHITE(*p))		    // ignore white space
3449 		    continue;
3450 
3451 		if (attr > HL_ALL)  // Combination with ':' is not allowed.
3452 		    return FAIL;
3453 
3454 		switch (*p)
3455 		{
3456 		    case 'b':	attr |= HL_BOLD;
3457 				break;
3458 		    case 'i':	attr |= HL_ITALIC;
3459 				break;
3460 		    case '-':
3461 		    case 'n':			    // no highlighting
3462 				break;
3463 		    case 'r':	attr |= HL_INVERSE;
3464 				break;
3465 		    case 's':	attr |= HL_STANDOUT;
3466 				break;
3467 		    case 'u':	attr |= HL_UNDERLINE;
3468 				break;
3469 		    case 'c':	attr |= HL_UNDERCURL;
3470 				break;
3471 		    case 't':	attr |= HL_STRIKETHROUGH;
3472 				break;
3473 		    case ':':	++p;		    // highlight group name
3474 				if (attr || *p == NUL)	 // no combinations
3475 				    return FAIL;
3476 				end = vim_strchr(p, ',');
3477 				if (end == NULL)
3478 				    end = p + STRLEN(p);
3479 				id = syn_check_group(p, (int)(end - p));
3480 				if (id == 0)
3481 				    return FAIL;
3482 				attr = syn_id2attr(id);
3483 				p = end - 1;
3484 #if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3485 				if (hlf == (int)HLF_SNC)
3486 				    id_SNC = syn_get_final_id(id);
3487 # ifdef FEAT_TERMINAL
3488 				else if (hlf == (int)HLF_ST)
3489 				    id_ST = syn_get_final_id(id);
3490 				else if (hlf == (int)HLF_STNC)
3491 				    id_STNC = syn_get_final_id(id);
3492 # endif
3493 				else if (hlf == (int)HLF_S)
3494 				    id_S = syn_get_final_id(id);
3495 #endif
3496 				break;
3497 		    default:	return FAIL;
3498 		}
3499 	    }
3500 	    highlight_attr[hlf] = attr;
3501 
3502 	    p = skip_to_option_part(p);	    // skip comma and spaces
3503 	}
3504     }
3505 
3506 #ifdef USER_HIGHLIGHT
3507     /*
3508      * Setup the user highlights
3509      *
3510      * Temporarily utilize 28 more hl entries:
3511      * 9 for User1-User9 combined with StatusLineNC
3512      * 9 for User1-User9 combined with StatusLineTerm
3513      * 9 for User1-User9 combined with StatusLineTermNC
3514      * 1 for StatusLine default
3515      * Have to be in there simultaneously in case of table overflows in
3516      * get_attr_entry()
3517      */
3518 # ifdef FEAT_STL_OPT
3519     if (ga_grow(&highlight_ga, 28) == FAIL)
3520 	return FAIL;
3521     hlcnt = highlight_ga.ga_len;
3522     if (id_S == -1)
3523     {
3524 	// Make sure id_S is always valid to simplify code below. Use the last
3525 	// entry.
3526 	CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
3527 	HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3528 	id_S = hlcnt + 19;
3529     }
3530 # endif
3531     for (i = 0; i < 9; i++)
3532     {
3533 	sprintf((char *)userhl, "User%d", i + 1);
3534 	id = syn_name2id(userhl);
3535 	if (id == 0)
3536 	{
3537 	    highlight_user[i] = 0;
3538 # ifdef FEAT_STL_OPT
3539 	    highlight_stlnc[i] = 0;
3540 #  ifdef FEAT_TERMINAL
3541 	    highlight_stlterm[i] = 0;
3542 	    highlight_stltermnc[i] = 0;
3543 #  endif
3544 # endif
3545 	}
3546 	else
3547 	{
3548 	    highlight_user[i] = syn_id2attr(id);
3549 # ifdef FEAT_STL_OPT
3550 	    combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3551 						     HLF_SNC, highlight_stlnc);
3552 #  ifdef FEAT_TERMINAL
3553 	    combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3554 						    HLF_ST, highlight_stlterm);
3555 	    combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3556 						HLF_STNC, highlight_stltermnc);
3557 #  endif
3558 # endif
3559 	}
3560     }
3561 # ifdef FEAT_STL_OPT
3562     highlight_ga.ga_len = hlcnt;
3563 # endif
3564 
3565 #endif // USER_HIGHLIGHT
3566 
3567     return OK;
3568 }
3569 
3570 static void highlight_list(void);
3571 static void highlight_list_two(int cnt, int attr);
3572 
3573 /*
3574  * Handle command line completion for :highlight command.
3575  */
3576     void
3577 set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3578 {
3579     char_u	*p;
3580 
3581     // Default: expand group names
3582     xp->xp_context = EXPAND_HIGHLIGHT;
3583     xp->xp_pattern = arg;
3584     include_link = 2;
3585     include_default = 1;
3586 
3587     // (part of) subcommand already typed
3588     if (*arg != NUL)
3589     {
3590 	p = skiptowhite(arg);
3591 	if (*p != NUL)			// past "default" or group name
3592 	{
3593 	    include_default = 0;
3594 	    if (STRNCMP("default", arg, p - arg) == 0)
3595 	    {
3596 		arg = skipwhite(p);
3597 		xp->xp_pattern = arg;
3598 		p = skiptowhite(arg);
3599 	    }
3600 	    if (*p != NUL)			// past group name
3601 	    {
3602 		include_link = 0;
3603 		if (arg[1] == 'i' && arg[0] == 'N')
3604 		    highlight_list();
3605 		if (STRNCMP("link", arg, p - arg) == 0
3606 			|| STRNCMP("clear", arg, p - arg) == 0)
3607 		{
3608 		    xp->xp_pattern = skipwhite(p);
3609 		    p = skiptowhite(xp->xp_pattern);
3610 		    if (*p != NUL)		// past first group name
3611 		    {
3612 			xp->xp_pattern = skipwhite(p);
3613 			p = skiptowhite(xp->xp_pattern);
3614 		    }
3615 		}
3616 		if (*p != NUL)			// past group name(s)
3617 		    xp->xp_context = EXPAND_NOTHING;
3618 	    }
3619 	}
3620     }
3621 }
3622 
3623 /*
3624  * List highlighting matches in a nice way.
3625  */
3626     static void
3627 highlight_list(void)
3628 {
3629     int		i;
3630 
3631     for (i = 10; --i >= 0; )
3632 	highlight_list_two(i, HL_ATTR(HLF_D));
3633     for (i = 40; --i >= 0; )
3634 	highlight_list_two(99, 0);
3635 }
3636 
3637     static void
3638 highlight_list_two(int cnt, int attr)
3639 {
3640     msg_puts_attr(&("N \bI \b!  \b"[cnt / 11]), attr);
3641     msg_clr_eos();
3642     out_flush();
3643     ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3644 }
3645 
3646 /*
3647  * Function given to ExpandGeneric() to obtain the list of group names.
3648  */
3649     char_u *
3650 get_highlight_name(expand_T *xp UNUSED, int idx)
3651 {
3652     return get_highlight_name_ext(xp, idx, TRUE);
3653 }
3654 
3655 /*
3656  * Obtain a highlight group name.
3657  * When "skip_cleared" is TRUE don't return a cleared entry.
3658  */
3659     char_u *
3660 get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3661 {
3662     if (idx < 0)
3663 	return NULL;
3664 
3665     // Items are never removed from the table, skip the ones that were
3666     // cleared.
3667     if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3668 	return (char_u *)"";
3669 
3670     if (idx == highlight_ga.ga_len && include_none != 0)
3671 	return (char_u *)"none";
3672     if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3673 	return (char_u *)"default";
3674     if (idx == highlight_ga.ga_len + include_none + include_default
3675 							 && include_link != 0)
3676 	return (char_u *)"link";
3677     if (idx == highlight_ga.ga_len + include_none + include_default + 1
3678 							 && include_link != 0)
3679 	return (char_u *)"clear";
3680     if (idx >= highlight_ga.ga_len)
3681 	return NULL;
3682     return HL_TABLE()[idx].sg_name;
3683 }
3684 
3685 #if defined(FEAT_GUI) || defined(PROTO)
3686 /*
3687  * Free all the highlight group fonts.
3688  * Used when quitting for systems which need it.
3689  */
3690     void
3691 free_highlight_fonts(void)
3692 {
3693     int	    idx;
3694 
3695     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3696     {
3697 	gui_mch_free_font(HL_TABLE()[idx].sg_font);
3698 	HL_TABLE()[idx].sg_font = NOFONT;
3699 # ifdef FEAT_XFONTSET
3700 	gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3701 	HL_TABLE()[idx].sg_fontset = NOFONTSET;
3702 # endif
3703     }
3704 
3705     gui_mch_free_font(gui.norm_font);
3706 # ifdef FEAT_XFONTSET
3707     gui_mch_free_fontset(gui.fontset);
3708 # endif
3709 # ifndef FEAT_GUI_GTK
3710     gui_mch_free_font(gui.bold_font);
3711     gui_mch_free_font(gui.ital_font);
3712     gui_mch_free_font(gui.boldital_font);
3713 # endif
3714 }
3715 #endif
3716