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