patch 8.2.3562: cannot add color names

Problem:    Cannot add color names.
Solution:   Add the v:colornames dictionary. (Drew Vogel, closes #8761)
diff --git a/src/Makefile b/src/Makefile
index 32411d0..e5953af 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1136,9 +1136,6 @@
 ### Name of the bugreport file target.
 SYS_BUGR_FILE	= $(DESTDIR)$(SCRIPTLOC)/bugreport.vim
 
-### Name of the rgb.txt file target.
-SYS_RGB_FILE   = $(DESTDIR)$(SCRIPTLOC)/rgb.txt
-
 ### Name of the file type detection file target.
 SYS_FILETYPE_FILE = $(DESTDIR)$(SCRIPTLOC)/filetype.vim
 
@@ -2449,9 +2446,6 @@
 	chmod $(VIMSCRIPTMOD) $(EVIM_FILE)
 	$(INSTALL_DATA) $(SCRIPTSOURCE)/mswin.vim $(MSWIN_FILE)
 	chmod $(VIMSCRIPTMOD) $(MSWIN_FILE)
-# install the rgb.txt file
-	$(INSTALL_DATA) $(SCRIPTSOURCE)/rgb.txt $(SYS_RGB_FILE)
-	chmod $(VIMSCRIPTMOD) $(SYS_RGB_FILE)
 # install the bugreport file
 	$(INSTALL_DATA) $(SCRIPTSOURCE)/bugreport.vim $(SYS_BUGR_FILE)
 	chmod $(VIMSCRIPTMOD) $(SYS_BUGR_FILE)
@@ -2481,7 +2475,7 @@
 	cd $(PRINTSOURCE); $(INSTALL_DATA) *.ps $(DEST_PRINT)
 	cd $(DEST_PRINT); chmod $(FILEMOD) *.ps
 # install the colorscheme files
-	cd $(COLSOURCE); $(INSTALL_DATA_R) *.vim tools README.txt $(DEST_COL)
+	cd $(COLSOURCE); $(INSTALL_DATA_R) *.vim lists tools README.txt $(DEST_COL)
 	cd $(DEST_COL); chmod $(DIRMOD) tools
 	cd $(DEST_COL); chmod $(HELPMOD) *.vim README.txt tools/*.vim
 # install the syntax files
@@ -2894,7 +2888,6 @@
 	-rm -f $(DEST_MAN_RU)/xxd.1 $(DEST_MAN_RU_U)/xxd.1
 	-rm -f $(DEST_HELP)/*.txt $(DEST_HELP)/tags $(DEST_HELP)/*.pl
 	-rm -f $(DEST_HELP)/*.??x $(DEST_HELP)/tags-??
-	-rm -f $(SYS_RGB_FILE)
 	-rm -f $(SYS_MENU_FILE) $(SYS_SYNMENU_FILE) $(SYS_DELMENU_FILE)
 	-rm -f $(SYS_BUGR_FILE) $(VIM_DEFAULTS_FILE) $(EVIM_FILE) $(MSWIN_FILE)
 	-rm -f $(DEST_SCRIPT)/gvimrc_example.vim $(DEST_SCRIPT)/vimrc_example.vim
@@ -3666,12 +3659,10 @@
 # This rule:
 #		- add resources to already installed vim binary to avoid
 #		  stripping them during install;
-#		- copy rgb.txt to runtime directory;
 #		- update system MIME database with info about vim application.
 #
 install_haiku_extra: $(DEST_BIN)/$(VIMTARGET) objects/os_haiku.rsrc
 	xres -o $(DEST_BIN)/$(VIMTARGET) objects/os_haiku.rsrc
-	$(INSTALL_DATA) $(SCRIPTSOURCE)/rgb.txt $(DEST_RT)
 	mimeset	$(DEST_BIN)/$(VIMTARGET)
 
 # List of g*-links that should be replaced with shell script equivalents.
diff --git a/src/errors.h b/src/errors.h
index 7778b39..537072d 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -147,6 +147,10 @@
 EXTERN char e_no_digraphs_version[]
 	INIT(= N_("E196: No digraphs in this version"));
 #endif
+#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
+EXTERN char e_cannot_allocate_color_str[]
+	INIT(= N_("E254: Cannot allocate color %s"));
+#endif
 EXTERN char e_ambiguous_use_of_user_defined_command[]
 	INIT(= N_("E464: Ambiguous use of user-defined command"));
 EXTERN char e_invalid_command[]
@@ -678,3 +682,5 @@
 	INIT(= N_("E1242: No white space allowed before separator: %s"));
 EXTERN char e_ascii_code_not_in_range[]
 	INIT(= N_("E1243: ASCII code not in 32-127 range"));
+EXTERN char e_bad_color_string_str[]
+	INIT(= N_("E1244: Bad color string: %s"));
diff --git a/src/evalvars.c b/src/evalvars.c
index fbf2d68..08736dd 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -149,6 +149,7 @@
     {VV_NAME("argv",		 VAR_LIST), VV_RO},
     {VV_NAME("collate",		 VAR_STRING), VV_RO},
     {VV_NAME("exiting",		 VAR_SPECIAL), VV_RO},
+    {VV_NAME("colornames",       VAR_DICT), VV_RO},
 };
 
 // shorthand
@@ -248,6 +249,8 @@
 
     set_vim_var_nr(VV_ECHOSPACE,    sc_col - 1);
 
+    set_vim_var_dict(VV_COLORNAMES, dict_alloc());
+
     // Default for v:register is not 0 but '"'.  This is adjusted once the
     // clipboard has been setup by calling reset_reg_var().
     set_reg_var(0);
diff --git a/src/globals.h b/src/globals.h
index 948c35f..5c54130 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1774,9 +1774,6 @@
 EXTERN char e_lock_unlock[]	INIT(= N_("E940: Cannot lock or unlock variable %s"));
 #endif
 
-#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
-EXTERN char e_alloc_color[]	INIT(= N_("E254: Cannot allocate color %s"));
-#endif
 EXTERN char e_chan_or_job_req[]	INIT(= N_("E706: Channel or Job required"));
 EXTERN char e_jobreq[]		INIT(= N_("E693: Job required"));
 
diff --git a/src/gui.c b/src/gui.c
index 1edf659..68754b3 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -4785,7 +4785,7 @@
 	    && gui.in_use
 #endif
 	    )
-	semsg(_(e_alloc_color), name);
+	semsg(_(e_cannot_allocate_color_str), name);
     return t;
 }
 
diff --git a/src/gui_haiku.cc b/src/gui_haiku.cc
index 644c2db..6d973c5 100644
--- a/src/gui_haiku.cc
+++ b/src/gui_haiku.cc
@@ -4054,164 +4054,7 @@
 gui_mch_get_color(
 	char_u	*name)
 {
-    typedef struct GuiColourTable
-    {
-	const char    *name;
-	guicolor_T     colour;
-    } GuiColourTable;
-
-#define NSTATIC_COLOURS	    50 // 32
-#define NDYNAMIC_COLOURS    33
-#define NCOLOURS	(NSTATIC_COLOURS + NDYNAMIC_COLOURS)
-
-    static GuiColourTable table[NCOLOURS] =
-    {
-	{"Black",	RGB(0x00, 0x00, 0x00)},
-	{"DarkGray",	    RGB(0x80, 0x80, 0x80)},
-	{"DarkGrey",	    RGB(0x80, 0x80, 0x80)},
-	{"Gray",	RGB(0xC0, 0xC0, 0xC0)},
-	{"Grey",	RGB(0xC0, 0xC0, 0xC0)},
-	{"LightGray",	    RGB(0xD3, 0xD3, 0xD3)},
-	{"LightGrey",	    RGB(0xD3, 0xD3, 0xD3)},
-	{"Gray10",	RGB(0x1A, 0x1A, 0x1A)},
-	{"Grey10",	RGB(0x1A, 0x1A, 0x1A)},
-	{"Gray20",	RGB(0x33, 0x33, 0x33)},
-	{"Grey20",	RGB(0x33, 0x33, 0x33)},
-	{"Gray30",	RGB(0x4D, 0x4D, 0x4D)},
-	{"Grey30",	RGB(0x4D, 0x4D, 0x4D)},
-	{"Gray40",	RGB(0x66, 0x66, 0x66)},
-	{"Grey40",	RGB(0x66, 0x66, 0x66)},
-	{"Gray50",	RGB(0x7F, 0x7F, 0x7F)},
-	{"Grey50",	RGB(0x7F, 0x7F, 0x7F)},
-	{"Gray60",	RGB(0x99, 0x99, 0x99)},
-	{"Grey60",	RGB(0x99, 0x99, 0x99)},
-	{"Gray70",	RGB(0xB3, 0xB3, 0xB3)},
-	{"Grey70",	RGB(0xB3, 0xB3, 0xB3)},
-	{"Gray80",	RGB(0xCC, 0xCC, 0xCC)},
-	{"Grey80",	RGB(0xCC, 0xCC, 0xCC)},
-	{"Gray90",	RGB(0xE5, 0xE5, 0xE5)},
-	{"Grey90",	RGB(0xE5, 0xE5, 0xE5)},
-	{"White",	RGB(0xFF, 0xFF, 0xFF)},
-	{"DarkRed",	RGB(0x80, 0x00, 0x00)},
-	{"Red",		RGB(0xFF, 0x00, 0x00)},
-	{"LightRed",	    RGB(0xFF, 0xA0, 0xA0)},
-	{"DarkBlue",	    RGB(0x00, 0x00, 0x80)},
-	{"Blue",	RGB(0x00, 0x00, 0xFF)},
-	{"LightBlue",	    RGB(0xA0, 0xA0, 0xFF)},
-	{"DarkGreen",	    RGB(0x00, 0x80, 0x00)},
-	{"Green",	RGB(0x00, 0xFF, 0x00)},
-	{"LightGreen",	    RGB(0xA0, 0xFF, 0xA0)},
-	{"DarkCyan",	    RGB(0x00, 0x80, 0x80)},
-	{"Cyan",	RGB(0x00, 0xFF, 0xFF)},
-	{"LightCyan",	    RGB(0xA0, 0xFF, 0xFF)},
-	{"DarkMagenta",	    RGB(0x80, 0x00, 0x80)},
-	{"Magenta",	RGB(0xFF, 0x00, 0xFF)},
-	{"LightMagenta",    RGB(0xFF, 0xA0, 0xFF)},
-	{"Brown",	RGB(0x80, 0x40, 0x40)},
-	{"Yellow",	RGB(0xFF, 0xFF, 0x00)},
-	{"LightYellow",	    RGB(0xFF, 0xFF, 0xA0)},
-	{"DarkYellow",	    RGB(0xBB, 0xBB, 0x00)},
-	{"SeaGreen",	    RGB(0x2E, 0x8B, 0x57)},
-	{"Orange",	RGB(0xFF, 0xA5, 0x00)},
-	{"Purple",	RGB(0xA0, 0x20, 0xF0)},
-	{"SlateBlue",	    RGB(0x6A, 0x5A, 0xCD)},
-	{"Violet",	RGB(0xEE, 0x82, 0xEE)},
-	//  NOTE: some entries are zero-allocated for NDDYNAMIC_COLORS
-	//	 in this table!
-    };
-
-    static int endColour = NSTATIC_COLOURS;
-    static int newColour = NSTATIC_COLOURS;
-
-    int		r, g, b;
-    int		i;
-
-    if (name[0] == '#' && STRLEN(name) == 7)
-    {
-	// Name is in "#rrggbb" format
-	r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
-	g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
-	b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
-	if (r < 0 || g < 0 || b < 0)
-	    return INVALCOLOR;
-	return RGB(r, g, b);
-    }
-    else
-    {
-	// Check if the name is one of the colours we know
-	for (i = 0; i < endColour; i++)
-	    if (STRICMP(name, table[i].name) == 0)
-		return table[i].colour;
-    }
-
-    /*
-     * Last attempt. Look in the file "$VIMRUNTIME/rgb.txt".
-     */
-    {
-#define LINE_LEN 100
-	FILE	*fd;
-	char	line[LINE_LEN];
-	char_u	*fname;
-
-	fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
-	if (fname == NULL)
-	    return INVALCOLOR;
-
-	fd = fopen((char *)fname, "rt");
-	vim_free(fname);
-	if (fd == NULL)
-	    return INVALCOLOR;
-
-	while (!feof(fd))
-	{
-	    int	    len;
-	    int	    pos;
-	    char    *colour;
-
-	    fgets(line, LINE_LEN, fd);
-	    len = strlen(line);
-
-	    if (len <= 1 || line[len-1] != '\n')
-		continue;
-
-	    line[len-1] = '\0';
-
-	    i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
-	    if (i != 3)
-		continue;
-
-	    colour = line + pos;
-
-	    if (STRICMP(colour, name) == 0)
-	    {
-		fclose(fd);
-		/*
-		 * Now remember this colour in the table.
-		 * A LRU scheme might be better but this is simpler.
-		 * Or could use a growing array.
-		 */
-		guicolor_T gcolour = RGB(r,g,b);
-
-		// NOTE: see note above in table allocation! We are working here with
-		//	dynamically allocated names, not constant ones!
-		vim_free((char*)table[newColour].name);
-		table[newColour].name = (char *)vim_strsave((char_u *)colour);
-		table[newColour].colour = gcolour;
-
-		newColour++;
-		if (newColour >= NCOLOURS)
-		    newColour = NSTATIC_COLOURS;
-		if (endColour < NCOLOURS)
-		    endColour = newColour;
-
-		return gcolour;
-	    }
-	}
-
-	fclose(fd);
-    }
-
-    return INVALCOLOR;
+    return gui_get_color_cmn(name);
 }
 
 /*
diff --git a/src/highlight.c b/src/highlight.c
index 76d9a7e..5dde08b 100644
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -475,6 +475,9 @@
     buf = alloc(STRLEN(name) + 12);
     if (buf != NULL)
     {
+#ifdef FEAT_EVAL
+	load_default_colors_lists();
+#endif
 	apply_autocmds(EVENT_COLORSCHEMEPRE, name,
 					       curbuf->b_fname, FALSE, curbuf);
 	sprintf((char *)buf, "colors/%s.vim", name);
@@ -1190,7 +1193,7 @@
 	    HL_TABLE()[idx].sg_set |= SG_GUI;
 
 # if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
-	// In GUI guifg colors are only used when recognized
+	// In GUI guibg colors are only used when recognized
 	i = color_name2handle(arg);
 	if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
 	{
@@ -2231,6 +2234,234 @@
 
     return GUI_GET_COLOR(name);
 }
+
+// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
+// values as used by the MS-Windows GDI api.  It should be used only for
+// MS-Windows GDI builds.
+# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
+#  undef RGB
+# endif
+# ifndef RGB
+#  define RGB(r, g, b)	((r<<16) | (g<<8) | (b))
+# endif
+
+# ifdef VIMDLL
+    static guicolor_T
+gui_adjust_rgb(guicolor_T c)
+{
+    if (gui.in_use)
+	return c;
+    else
+	return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
+}
+# else
+#  define gui_adjust_rgb(c) (c)
+# endif
+
+    static int
+hex_digit(int c)
+{
+    if (isdigit(c))
+	return c - '0';
+    c = TOLOWER_ASC(c);
+    if (c >= 'a' && c <= 'f')
+	return c - 'a' + 10;
+    return 0x1ffffff;
+}
+
+    guicolor_T
+decode_hex_color(char_u *hex)
+{
+    guicolor_T color;
+
+    if (hex[0] != '#' || STRLEN(hex) != 7)
+	return INVALCOLOR;
+
+    // Name is in "#rrggbb" format
+    color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
+		((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
+		((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
+    if (color > 0xffffff)
+	return INVALCOLOR;
+    return gui_adjust_rgb(color);
+}
+
+#if defined(FEAT_EVAL)
+// Returns the color currently mapped to the given name or INVALCOLOR if no
+// such name exists in the color table. The convention is to use lowercase for
+// all keys in the v:colornames dictionary. The value can be either a string in
+// the form #rrggbb or a number, either of which is converted to a guicolor_T.
+    guicolor_T
+colorname2rgb(char_u *name)
+{
+    dict_T      *colornames_table = get_vim_var_dict(VV_COLORNAMES);
+    char_u      *lc_name;
+    dictitem_T  *colentry;
+    char_u      *colstr;
+    varnumber_T colnum;
+
+    lc_name = strlow_save(name);
+    if (lc_name == NULL)
+	return INVALCOLOR;
+
+    colentry = dict_find(colornames_table, lc_name, -1);
+    vim_free(lc_name);
+    if (colentry == NULL)
+	return INVALCOLOR;
+
+    if (colentry->di_tv.v_type == VAR_STRING)
+    {
+	colstr = tv_get_string_strict(&colentry->di_tv);
+	if ((STRLEN(colstr) == 7) && (*colstr == '#'))
+	{
+	    return decode_hex_color(colstr);
+	}
+	else
+	{
+	    semsg(_(e_bad_color_string_str), colstr);
+	    return INVALCOLOR;
+	}
+    }
+
+    if (colentry->di_tv.v_type == VAR_NUMBER)
+    {
+	colnum = tv_get_number(&colentry->di_tv);
+	return (guicolor_T)colnum;
+    }
+
+    return INVALCOLOR;
+}
+
+// Maps the given name to the given color value, overwriting any current
+// mapping. If allocation fails the named color will no longer exist in the
+// table and the user will receive an error message.
+    void
+save_colorname_hexstr(int r, int g, int b, char_u *name)
+{
+    int        result;
+    dict_T     *colornames_table;
+    dictitem_T *existing;
+    char_u     hexstr[8];
+
+    if (vim_snprintf((char *)hexstr, sizeof(hexstr),
+						 "#%02x%02x%02x", r, g, b) < 0)
+    {
+	semsg(_(e_cannot_allocate_color_str), name);
+	return;
+    }
+
+    colornames_table = get_vim_var_dict(VV_COLORNAMES);
+    // The colornames_table dict is safe to use here because it is allocated at
+    // startup in evalvars.c
+    existing = dict_find(colornames_table, name, -1);
+    if (existing != NULL)
+    {
+	dictitem_remove(colornames_table, existing);
+	existing = NULL; // dictitem_remove freed the item
+    }
+
+    result = dict_add_string(colornames_table, (char *)name, hexstr);
+    if (result == FAIL)
+	semsg(_(e_cannot_allocate_color_str), name);
+}
+
+/*
+ * Load a default color list. Intended to support legacy color names but allows
+ * the user to override the color values. Only loaded once.
+ */
+    void
+load_default_colors_lists()
+{
+    // Lacking a default color list isn't the end of the world but it is likely
+    // an inconvenience so users should know when it is missing.
+    if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
+	msg("failed to load colors/lists/default.vim");
+}
+#endif
+
+    guicolor_T
+gui_get_color_cmn(char_u *name)
+{
+    int         i;
+    guicolor_T  color;
+
+    struct rgbcolor_table_S {
+	char_u	    *color_name;
+	guicolor_T  color;
+    };
+
+    // Only non X11 colors (not present in rgb.txt) and colors in
+    // color_names[], useful when $VIMRUNTIME is not found,.
+    static struct rgbcolor_table_S rgb_table[] = {
+	    {(char_u *)"black",		RGB(0x00, 0x00, 0x00)},
+	    {(char_u *)"blue",		RGB(0x00, 0x00, 0xFF)},
+	    {(char_u *)"brown",		RGB(0xA5, 0x2A, 0x2A)},
+	    {(char_u *)"cyan",		RGB(0x00, 0xFF, 0xFF)},
+	    {(char_u *)"darkblue",	RGB(0x00, 0x00, 0x8B)},
+	    {(char_u *)"darkcyan",	RGB(0x00, 0x8B, 0x8B)},
+	    {(char_u *)"darkgray",	RGB(0xA9, 0xA9, 0xA9)},
+	    {(char_u *)"darkgreen",	RGB(0x00, 0x64, 0x00)},
+	    {(char_u *)"darkgrey",	RGB(0xA9, 0xA9, 0xA9)},
+	    {(char_u *)"darkmagenta",	RGB(0x8B, 0x00, 0x8B)},
+	    {(char_u *)"darkred",	RGB(0x8B, 0x00, 0x00)},
+	    {(char_u *)"darkyellow",	RGB(0x8B, 0x8B, 0x00)}, // No X11
+	    {(char_u *)"gray",		RGB(0xBE, 0xBE, 0xBE)},
+	    {(char_u *)"green",		RGB(0x00, 0xFF, 0x00)},
+	    {(char_u *)"grey",		RGB(0xBE, 0xBE, 0xBE)},
+	    {(char_u *)"grey40",	RGB(0x66, 0x66, 0x66)},
+	    {(char_u *)"grey50",	RGB(0x7F, 0x7F, 0x7F)},
+	    {(char_u *)"grey90",	RGB(0xE5, 0xE5, 0xE5)},
+	    {(char_u *)"lightblue",	RGB(0xAD, 0xD8, 0xE6)},
+	    {(char_u *)"lightcyan",	RGB(0xE0, 0xFF, 0xFF)},
+	    {(char_u *)"lightgray",	RGB(0xD3, 0xD3, 0xD3)},
+	    {(char_u *)"lightgreen",	RGB(0x90, 0xEE, 0x90)},
+	    {(char_u *)"lightgrey",	RGB(0xD3, 0xD3, 0xD3)},
+	    {(char_u *)"lightmagenta",	RGB(0xFF, 0x8B, 0xFF)}, // No X11
+	    {(char_u *)"lightred",	RGB(0xFF, 0x8B, 0x8B)}, // No X11
+	    {(char_u *)"lightyellow",	RGB(0xFF, 0xFF, 0xE0)},
+	    {(char_u *)"magenta",	RGB(0xFF, 0x00, 0xFF)},
+	    {(char_u *)"red",		RGB(0xFF, 0x00, 0x00)},
+	    {(char_u *)"seagreen",	RGB(0x2E, 0x8B, 0x57)},
+	    {(char_u *)"white",		RGB(0xFF, 0xFF, 0xFF)},
+	    {(char_u *)"yellow",	RGB(0xFF, 0xFF, 0x00)},
+    };
+
+    color = decode_hex_color(name);
+    if (color != INVALCOLOR)
+	return color;
+
+    // Check if the name is one of the colors we know
+    for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
+	if (STRICMP(name, rgb_table[i].color_name) == 0)
+	    return gui_adjust_rgb(rgb_table[i].color);
+
+#if defined(FEAT_EVAL)
+    /*
+     * Not a traditional color. Load additional color aliases and then consult the alias table.
+     */
+
+    color = colorname2rgb(name);
+    if (color == INVALCOLOR)
+    {
+	load_default_colors_lists();
+	color = colorname2rgb(name);
+    }
+
+    return color;
+#else
+    return INVALCOLOR;
+#endif
+}
+
+    guicolor_T
+gui_get_rgb_color_cmn(int r, int g, int b)
+{
+    guicolor_T  color = RGB(r, g, b);
+
+    if (color > 0xffffff)
+	return INVALCOLOR;
+    return gui_adjust_rgb(color);
+}
 #endif
 
 /*
diff --git a/src/job.c b/src/job.c
index afa9972..fcb482c 100644
--- a/src/job.c
+++ b/src/job.c
@@ -559,7 +559,7 @@
 		    {
 			if (called_emsg_before == called_emsg)
 			    // may not get the error if the GUI didn't start
-			    semsg(_(e_alloc_color), color_name);
+			    semsg(_(e_cannot_allocate_color_str), color_name);
 			return FAIL;
 		    }
 
diff --git a/src/proto/highlight.pro b/src/proto/highlight.pro
index ca44981..4c6b2f7 100644
--- a/src/proto/highlight.pro
+++ b/src/proto/highlight.pro
@@ -14,6 +14,12 @@
 void hl_set_bg_color_name(char_u *name);
 void hl_set_fg_color_name(char_u *name);
 guicolor_T color_name2handle(char_u *name);
+guicolor_T decode_hex_color(char_u *hex);
+guicolor_T colorname2rgb(char_u *name);
+void save_colorname_hexstr(int r, int g, int b, char_u *name);
+void load_default_colors_lists(void);
+guicolor_T gui_get_color_cmn(char_u *name);
+guicolor_T gui_get_rgb_color_cmn(int r, int g, int b);
 int get_cterm_attr_idx(int attr, int fg, int bg);
 int get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg);
 int get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg);
diff --git a/src/proto/term.pro b/src/proto/term.pro
index efb2555..572a41d 100644
--- a/src/proto/term.pro
+++ b/src/proto/term.pro
@@ -83,8 +83,6 @@
 int show_one_termcode(char_u *name, char_u *code, int printit);
 void update_tcap(int attr);
 void swap_tcap(void);
-guicolor_T gui_get_color_cmn(char_u *name);
-guicolor_T gui_get_rgb_color_cmn(int r, int g, int b);
 void cterm_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx);
 void term_replace_bs_del_keycode(char_u *ta_buf, int ta_len, int len);
 /* vim: set ft=c : */
diff --git a/src/term.c b/src/term.c
index b61667d..cb38b93 100644
--- a/src/term.c
+++ b/src/term.c
@@ -1353,7 +1353,7 @@
     t = termgui_mch_get_color(name);
 
     if (t == INVALCOLOR)
-	semsg(_(e_alloc_color), name);
+	semsg(_(e_cannot_allocate_color_str), name);
     return t;
 }
 
@@ -6630,203 +6630,6 @@
 
 #endif
 
-#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
-    static int
-hex_digit(int c)
-{
-    if (isdigit(c))
-	return c - '0';
-    c = TOLOWER_ASC(c);
-    if (c >= 'a' && c <= 'f')
-	return c - 'a' + 10;
-    return 0x1ffffff;
-}
-
-# ifdef VIMDLL
-    static guicolor_T
-gui_adjust_rgb(guicolor_T c)
-{
-    if (gui.in_use)
-	return c;
-    else
-	return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
-}
-# else
-#  define gui_adjust_rgb(c) (c)
-# endif
-
-    guicolor_T
-gui_get_color_cmn(char_u *name)
-{
-    // On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
-    // values as used by the MS-Windows GDI api.  It should be used only for
-    // MS-Windows GDI builds.
-# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
-#  undef RGB
-# endif
-# ifndef RGB
-#  define RGB(r, g, b)	((r<<16) | (g<<8) | (b))
-# endif
-# define LINE_LEN 100
-    FILE	*fd;
-    char	line[LINE_LEN];
-    char_u	*fname;
-    int		r, g, b, i;
-    guicolor_T  color;
-
-    struct rgbcolor_table_S {
-	char_u	    *color_name;
-	guicolor_T  color;
-    };
-
-    // Only non X11 colors (not present in rgb.txt) and colors in
-    // color_names[], useful when $VIMRUNTIME is not found,.
-    static struct rgbcolor_table_S rgb_table[] = {
-	    {(char_u *)"black",		RGB(0x00, 0x00, 0x00)},
-	    {(char_u *)"blue",		RGB(0x00, 0x00, 0xFF)},
-	    {(char_u *)"brown",		RGB(0xA5, 0x2A, 0x2A)},
-	    {(char_u *)"cyan",		RGB(0x00, 0xFF, 0xFF)},
-	    {(char_u *)"darkblue",	RGB(0x00, 0x00, 0x8B)},
-	    {(char_u *)"darkcyan",	RGB(0x00, 0x8B, 0x8B)},
-	    {(char_u *)"darkgray",	RGB(0xA9, 0xA9, 0xA9)},
-	    {(char_u *)"darkgreen",	RGB(0x00, 0x64, 0x00)},
-	    {(char_u *)"darkgrey",	RGB(0xA9, 0xA9, 0xA9)},
-	    {(char_u *)"darkmagenta",	RGB(0x8B, 0x00, 0x8B)},
-	    {(char_u *)"darkred",	RGB(0x8B, 0x00, 0x00)},
-	    {(char_u *)"darkyellow",	RGB(0x8B, 0x8B, 0x00)}, // No X11
-	    {(char_u *)"gray",		RGB(0xBE, 0xBE, 0xBE)},
-	    {(char_u *)"green",		RGB(0x00, 0xFF, 0x00)},
-	    {(char_u *)"grey",		RGB(0xBE, 0xBE, 0xBE)},
-	    {(char_u *)"grey40",	RGB(0x66, 0x66, 0x66)},
-	    {(char_u *)"grey50",	RGB(0x7F, 0x7F, 0x7F)},
-	    {(char_u *)"grey90",	RGB(0xE5, 0xE5, 0xE5)},
-	    {(char_u *)"lightblue",	RGB(0xAD, 0xD8, 0xE6)},
-	    {(char_u *)"lightcyan",	RGB(0xE0, 0xFF, 0xFF)},
-	    {(char_u *)"lightgray",	RGB(0xD3, 0xD3, 0xD3)},
-	    {(char_u *)"lightgreen",	RGB(0x90, 0xEE, 0x90)},
-	    {(char_u *)"lightgrey",	RGB(0xD3, 0xD3, 0xD3)},
-	    {(char_u *)"lightmagenta",	RGB(0xFF, 0x8B, 0xFF)}, // No X11
-	    {(char_u *)"lightred",	RGB(0xFF, 0x8B, 0x8B)}, // No X11
-	    {(char_u *)"lightyellow",	RGB(0xFF, 0xFF, 0xE0)},
-	    {(char_u *)"magenta",	RGB(0xFF, 0x00, 0xFF)},
-	    {(char_u *)"red",		RGB(0xFF, 0x00, 0x00)},
-	    {(char_u *)"seagreen",	RGB(0x2E, 0x8B, 0x57)},
-	    {(char_u *)"white",		RGB(0xFF, 0xFF, 0xFF)},
-	    {(char_u *)"yellow",	RGB(0xFF, 0xFF, 0x00)},
-    };
-
-    static struct rgbcolor_table_S *colornames_table;
-    static int size = 0;
-
-    if (name[0] == '#' && STRLEN(name) == 7)
-    {
-	// Name is in "#rrggbb" format
-	color = RGB(((hex_digit(name[1]) << 4) + hex_digit(name[2])),
-		    ((hex_digit(name[3]) << 4) + hex_digit(name[4])),
-		    ((hex_digit(name[5]) << 4) + hex_digit(name[6])));
-	if (color > 0xffffff)
-	    return INVALCOLOR;
-	return gui_adjust_rgb(color);
-    }
-
-    // Check if the name is one of the colors we know
-    for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
-	if (STRICMP(name, rgb_table[i].color_name) == 0)
-	    return gui_adjust_rgb(rgb_table[i].color);
-
-    /*
-     * Last attempt. Look in the file "$VIMRUNTIME/rgb.txt".
-     */
-    if (size == 0)
-    {
-	int counting;
-
-	// colornames_table not yet initialized
-	fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
-	if (fname == NULL)
-	    return INVALCOLOR;
-
-	fd = fopen((char *)fname, "rt");
-	vim_free(fname);
-	if (fd == NULL)
-	{
-	    if (p_verbose > 1)
-		verb_msg(_("Cannot open $VIMRUNTIME/rgb.txt"));
-	    size = -1;  // don't try again
-	    return INVALCOLOR;
-	}
-
-	for (counting = 1; counting >= 0; --counting)
-	{
-	    if (!counting)
-	    {
-		colornames_table = ALLOC_MULT(struct rgbcolor_table_S, size);
-		if (colornames_table == NULL)
-		{
-		    fclose(fd);
-		    return INVALCOLOR;
-		}
-		rewind(fd);
-	    }
-	    size = 0;
-
-	    while (!feof(fd))
-	    {
-		size_t	len;
-		int	pos;
-
-		vim_ignoredp = fgets(line, LINE_LEN, fd);
-		len = strlen(line);
-
-		if (len <= 1 || line[len - 1] != '\n')
-		    continue;
-
-		line[len - 1] = '\0';
-
-		i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
-		if (i != 3)
-		    continue;
-
-		if (!counting)
-		{
-		    char_u *s = vim_strsave((char_u *)line + pos);
-
-		    if (s == NULL)
-		    {
-			fclose(fd);
-			return INVALCOLOR;
-		    }
-		    colornames_table[size].color_name = s;
-		    colornames_table[size].color = (guicolor_T)RGB(r, g, b);
-		}
-		size++;
-
-		// The distributed rgb.txt has less than 1000 entries. Limit to
-		// 10000, just in case the file was messed up.
-		if (size == 10000)
-		    break;
-	    }
-	}
-	fclose(fd);
-    }
-
-    for (i = 0; i < size; i++)
-	if (STRICMP(name, colornames_table[i].color_name) == 0)
-	    return gui_adjust_rgb(colornames_table[i].color);
-
-    return INVALCOLOR;
-}
-
-    guicolor_T
-gui_get_rgb_color_cmn(int r, int g, int b)
-{
-    guicolor_T  color = RGB(r, g, b);
-
-    if (color > 0xffffff)
-	return INVALCOLOR;
-    return gui_adjust_rgb(color);
-}
-#endif
 
 #if (defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))) || defined(FEAT_TERMINAL) \
 	|| defined(PROTO)
diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim
index a4c7a72..d3734b3 100644
--- a/src/testdir/test_highlight.vim
+++ b/src/testdir/test_highlight.vim
@@ -934,4 +934,40 @@
   hi clear
 endfunc
 
+func Test_colornames_assignment_and_lookup()
+  " Ensure highlight command can find custom color.
+  let v:colornames['a redish white'] = '#ffeedd'
+  highlight Normal guifg='a redish white'
+  highlight clear
+endfunc
+
+func Test_colornames_default_list()
+  " Ensure default lists are loaded automatically and can be used for all gui fields.
+  highlight Normal guifg='rebecca purple' guibg='rebecca purple' guisp='rebecca purple'
+  highlight clear
+endfunc
+
+func Test_colornames_overwrite_default()
+  " Ensure entries in v:colornames can be overwritten.
+  " Load default color scheme to trigger default color list loading.
+  colorscheme default
+  let old_rebecca_purple = v:colornames['rebecca purple']
+  highlight Normal guifg='rebecca purple' guibg='rebecca purple'
+  let v:colornames['rebecca purple'] = '#550099'
+  highlight Normal guifg='rebecca purple' guibg='rebecca purple'
+  let v:colornames['rebecca purple'] = old_rebecca_purple
+  highlight clear
+endfunc
+
+func Test_colornames_assignment_and_unassignment()
+  " Ensure we cannot overwrite the v:colornames dict.
+  call assert_fails("let v:colornames = {}", 'E46:')
+
+  " Ensure we can delete entries from the v:colornames dict.
+  let v:colornames['x1'] = '#111111'
+  call assert_equal(v:colornames['x1'], '#111111')
+  unlet v:colornames['x1']
+  call assert_fails("echo v:colornames['x1']")
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 7dc55ac..3bba972 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3562,
+/**/
     3561,
 /**/
     3560,
diff --git a/src/vim.h b/src/vim.h
index 0ce0e96..31d4bde 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2044,7 +2044,8 @@
 #define VV_ARGV		96
 #define VV_COLLATE      97
 #define VV_EXITING	98
-#define VV_LEN		99	// number of v: vars
+#define VV_COLORNAMES   99
+#define VV_LEN		100	// number of v: vars
 
 // used for v_number in VAR_BOOL and VAR_SPECIAL
 #define VVAL_FALSE	0L	// VAR_BOOL