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