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