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