diff --git a/include/color.h b/include/color.h index fe7ba3fbc..50befddd8 100644 --- a/include/color.h +++ b/include/color.h @@ -54,4 +54,21 @@ #define DRAGON_SILVER CLR_BRIGHT_CYAN #define HI_ZAP CLR_BRIGHT_BLUE +enum nhcolortype { no_color, nh_color, rgb_color }; + +struct nethack_color { + enum nhcolortype colortyp; + int tableindex; + int rgbindex; + const char *name; + const char *hexval; + long r, g, b; +}; + +typedef struct color_and_attr { + int color, attr; +} color_attr; + #endif /* COLOR_H */ + +/*color.h*/ diff --git a/include/extern.h b/include/extern.h index b871d8640..ea66f1d79 100644 --- a/include/extern.h +++ b/include/extern.h @@ -281,6 +281,26 @@ extern void all_options_statushilites(strbuf_t *); extern boolean status_hilite_menu(void); #endif /* STATUS_HILITES */ +/* ### coloratt.c ### */ + +extern char *color_attr_to_str(color_attr *); +extern boolean color_attr_parse_str(color_attr *, char *); +extern int32 colortable_to_int32(struct nethack_color *); +extern int query_color(const char *, int) NO_NNARGS; +extern int query_attr(const char *, int) NO_NNARGS; +extern boolean query_color_attr(color_attr *, const char *) NONNULLARG1; +extern const char *attr2attrname(int); +extern void basic_menu_colors(boolean); +extern boolean add_menu_coloring_parsed(const char *, int, int); +extern const char *clr2colorname(int); +extern int match_str2clr(char *) NONNULLARG1; +extern int match_str2attr(const char *, boolean) NONNULLARG1; +extern boolean add_menu_coloring(char *) NONNULLARG1; +extern void free_one_menu_coloring(int); +extern void free_menu_coloring(void); +extern int count_menucolors(void); +extern int32 check_enhanced_colors(const char *) NONNULLARG1; + /* ### cmd.c ### */ extern void set_move_cmd(int, int); @@ -2238,14 +2258,6 @@ extern int add_autopickup_exception(const char *) NONNULLARG1; extern void free_autopickup_exceptions(void); extern void set_playmode(void); extern int sym_val(const char *) NONNULLARG1; -extern int query_color(const char *, int) NO_NNARGS; -extern int query_attr(const char *, int) NO_NNARGS; -extern boolean query_color_attr(color_attr *, const char *) NONNULLARG1; -extern const char *clr2colorname(int); -extern int match_str2clr(char *) NONNULLARG1; -extern int match_str2attr(const char *, boolean) NONNULLARG1; -extern boolean add_menu_coloring(char *) NONNULLARG1; -extern void free_menu_coloring(void); extern boolean msgtype_parse_add(char *) NONNULLARG1; extern int msgtype_type(const char *, boolean) NONNULLARG1; extern void hide_unhide_msgtypes(boolean, int); diff --git a/include/global.h b/include/global.h index 763d10af0..0fae6d2ac 100644 --- a/include/global.h +++ b/include/global.h @@ -445,9 +445,7 @@ extern struct nomakedefs_s nomakedefs; #define MAX_MSG_HISTORY 128 /* max # of lines in msg_history */ #endif -typedef struct color_and_attr { - int color, attr; -} color_attr; +#include "color.h" /* * Version 3.7.x has aspirations of portable file formats. We diff --git a/include/hack.h b/include/hack.h index 556d1371a..47ccf5080 100644 --- a/include/hack.h +++ b/include/hack.h @@ -11,7 +11,6 @@ #endif #include "lint.h" -#include "color.h" #include "align.h" #include "dungeon.h" #include "objclass.h" diff --git a/src/coloratt.c b/src/coloratt.c new file mode 100644 index 000000000..6512c478d --- /dev/null +++ b/src/coloratt.c @@ -0,0 +1,709 @@ +/* NetHack 3.7 color.c $NHDT-Date: 1682205020 2023/04/22 23:10:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ */ +/* Copyright (c) Pasi Kallinen, 2024 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include + +struct color_names { + const char *name; + int color; +}; + +static const struct color_names colornames[] = { + { "black", CLR_BLACK }, + { "red", CLR_RED }, + { "green", CLR_GREEN }, + { "brown", CLR_BROWN }, + { "blue", CLR_BLUE }, + { "magenta", CLR_MAGENTA }, + { "cyan", CLR_CYAN }, + { "gray", CLR_GRAY }, + { "orange", CLR_ORANGE }, + { "light green", CLR_BRIGHT_GREEN }, + { "yellow", CLR_YELLOW }, + { "light blue", CLR_BRIGHT_BLUE }, + { "light magenta", CLR_BRIGHT_MAGENTA }, + { "light cyan", CLR_BRIGHT_CYAN }, + { "white", CLR_WHITE }, + { "no color", NO_COLOR }, + { (const char *) 0, CLR_BLACK }, /* everything after this is an alias */ + { "transparent", NO_COLOR }, + { "purple", CLR_MAGENTA }, + { "light purple", CLR_BRIGHT_MAGENTA }, + { "bright purple", CLR_BRIGHT_MAGENTA }, + { "grey", CLR_GRAY }, + { "bright red", CLR_ORANGE }, + { "bright green", CLR_BRIGHT_GREEN }, + { "bright blue", CLR_BRIGHT_BLUE }, + { "bright magenta", CLR_BRIGHT_MAGENTA }, + { "bright cyan", CLR_BRIGHT_CYAN } +}; + +struct attr_names { + const char *name; + int attr; +}; + +static const struct attr_names attrnames[] = { + { "none", ATR_NONE }, + { "bold", ATR_BOLD }, + { "dim", ATR_DIM }, + { "italic", ATR_ITALIC }, + { "underline", ATR_ULINE }, + { "blink", ATR_BLINK }, + { "inverse", ATR_INVERSE }, + { (const char *) 0, ATR_NONE }, /* everything after this is an alias */ + { "normal", ATR_NONE }, + { "uline", ATR_ULINE }, + { "reverse", ATR_INVERSE }, +}; + +/* { colortyp, tableindex, rgbindx, name, hexval, r, g, b }, */ + +static struct nethack_color colortable[] = { + { nh_color, 0, 0, "black", "#000000", 0, 0, 0 }, + { nh_color, 1, 0, "red", "#FF0000", 255, 0, 0 }, + { nh_color, 2, 0, "green", "#228B22", 34, 139, 34 }, + { nh_color, 3, 0, "brown", "#A52A2A", 165, 42, 42 }, + { nh_color, 4, 0, "blue", "#0000FF", 0, 0, 255 }, + { nh_color, 5, 0, "magenta", "#FF00FF", 255, 0, 255 }, + { nh_color, 6, 0, "cyan", "#00FFFF", 0, 255, 255 }, + { nh_color, 7, 0, "gray", "#808080", 128, 128, 128 }, + { no_color, 8, 0, "nocolor", "#000000", 0, 0, 0 }, + { nh_color, 9, 0, "orange", "#FFA500", 255, 165, 0 }, + { nh_color, 10, 0, "bright-green", "#008000", 0, 128, 0 }, + { nh_color, 11, 0, "yellow", "#FFFF00", 255, 255, 0 }, + { nh_color, 12, 0, "bright-blue", "#ADD8E6", 173, 216, 230 }, + { nh_color, 13, 0, "bright-magenta", "#9370DB", 147, 112, 219 }, + { nh_color, 14, 0, "light-cyan", "#E0FFFF", 224, 255, 255 }, + { nh_color, 15, 0, "white", "#FFFFFF", 255, 255, 255 }, + { rgb_color, 16, 0, "maroon", "#800000", 128, 0, 0 }, + { rgb_color, 17, 1, "dark-red", "#8B0000", 139, 0, 0 }, + { rgb_color, 18, 2, "brown", "#A52A2A", 165, 42, 42 }, + { rgb_color, 19, 3, "firebrick", "#B22222", 178, 34, 34 }, + { rgb_color, 20, 4, "crimson", "#DC143C", 220, 20, 60 }, + { rgb_color, 21, 5, "red", "#FF0000", 255, 0, 0 }, + { rgb_color, 22, 6, "tomato", "#FF6347", 255, 99, 71 }, + { rgb_color, 23, 7, "coral", "#FF7F50", 255, 127, 80 }, + { rgb_color, 24, 8, "indian-red", "#CD5C5C", 205, 92, 92 }, + { rgb_color, 25, 9, "light-coral", "#F08080", 240, 128, 128 }, + { rgb_color, 26, 10, "dark-salmon", "#E9967A", 233, 150, 122 }, + { rgb_color, 27, 11, "salmon", "#FA8072", 250, 128, 114 }, + { rgb_color, 28, 12, "light-salmon", "#FFA07A", 255, 160, 122 }, + { rgb_color, 29, 13, "orange-red", "#FF4500", 255, 69, 0 }, + { rgb_color, 30, 14, "dark-orange", "#FF8C00", 255, 140, 0 }, + { rgb_color, 31, 15, "orange", "#FFA500", 255, 165, 0 }, + { rgb_color, 32, 16, "gold", "#FFD700", 255, 215, 0 }, + { rgb_color, 33, 17, "dark-golden-rod", "#B8860B", 184, 134, 11 }, + { rgb_color, 34, 18, "golden-rod", "#DAA520", 218, 165, 32 }, + { rgb_color, 35, 19, "pale-golden-rod", "#EEE8AA", 238, 232, 170 }, + { rgb_color, 36, 20, "dark-khaki", "#BDB76B", 189, 183, 107 }, + { rgb_color, 37, 21, "khaki", "#F0E68C", 240, 230, 140 }, + { rgb_color, 38, 22, "olive", "#808000", 128, 128, 0 }, + { rgb_color, 39, 23, "yellow", "#FFFF00", 255, 255, 0 }, + { rgb_color, 40, 24, "yellow-green", "#9ACD32", 154, 205, 50 }, + { rgb_color, 41, 25, "dark-olive-green", "#556B2F", 85, 107, 47 }, + { rgb_color, 42, 26, "olive-drab", "#6B8E23", 107, 142, 35 }, + { rgb_color, 43, 27, "lawn-green", "#7CFC00", 124, 252, 0 }, + { rgb_color, 44, 28, "chart-reuse", "#7FFF00", 127, 255, 0 }, + { rgb_color, 45, 29, "green-yellow", "#ADFF2F", 173, 255, 47 }, + { rgb_color, 46, 30, "dark-green", "#006400", 0, 100, 0 }, + { rgb_color, 47, 31, "green", "#008000", 0, 128, 0 }, + { rgb_color, 48, 32, "forest-green", "#228B22", 34, 139, 34 }, + { rgb_color, 49, 33, "lime", "#00FF00", 0, 255, 0 }, + { rgb_color, 50, 34, "lime-green", "#32CD32", 50, 205, 50 }, + { rgb_color, 51, 35, "light-green", "#90EE90", 144, 238, 144 }, + { rgb_color, 52, 36, "pale-green", "#98FB98", 152, 251, 152 }, + { rgb_color, 53, 37, "dark-sea-green", "#8FBC8F", 143, 188, 143 }, + { rgb_color, 54, 38, "medium-spring-green", "#00FA9A", 0, 250, 154 }, + { rgb_color, 55, 39, "spring-green", "#00FF7F", 0, 255, 127 }, + { rgb_color, 56, 40, "sea-green", "#2E8B57", 46, 139, 87 }, + { rgb_color, 57, 41, "medium-aqua-marine", "#66CDAA", 102, 205, 170 }, + { rgb_color, 58, 42, "medium-sea-green", "#3CB371", 60, 179, 113 }, + { rgb_color, 59, 43, "light-sea-green", "#20B2AA", 32, 178, 170 }, + { rgb_color, 60, 44, "dark-slate-gray", "#2F4F4F", 47, 79, 79 }, + { rgb_color, 61, 45, "teal", "#008080", 0, 128, 128 }, + { rgb_color, 62, 46, "dark-cyan", "#008B8B", 0, 139, 139 }, + { rgb_color, 63, 47, "aqua", "#00FFFF", 0, 255, 255 }, + { rgb_color, 64, 48, "cyan", "#00FFFF", 0, 255, 255 }, + { rgb_color, 65, 49, "light-cyan", "#E0FFFF", 224, 255, 255 }, + { rgb_color, 66, 50, "dark-turquoise", "#00CED1", 0, 206, 209 }, + { rgb_color, 67, 51, "turquoise", "#40E0D0", 64, 224, 208 }, + { rgb_color, 68, 52, "medium-turquoise", "#48D1CC", 72, 209, 204 }, + { rgb_color, 69, 53, "pale-turquoise", "#AFEEEE", 175, 238, 238 }, + { rgb_color, 70, 54, "aqua-marine", "#7FFFD4", 127, 255, 212 }, + { rgb_color, 71, 55, "powder-blue", "#B0E0E6", 176, 224, 230 }, + { rgb_color, 72, 56, "cadet-blue", "#5F9EA0", 95, 158, 160 }, + { rgb_color, 73, 57, "steel-blue", "#4682B4", 70, 130, 180 }, + { rgb_color, 74, 58, "corn-flower-blue", "#6495ED", 100, 149, 237 }, + { rgb_color, 75, 59, "deep-sky-blue", "#00BFFF", 0, 191, 255 }, + { rgb_color, 76, 60, "dodger-blue", "#1E90FF", 30, 144, 255 }, + { rgb_color, 77, 61, "light-blue", "#ADD8E6", 173, 216, 230 }, + { rgb_color, 78, 62, "sky-blue", "#87CEEB", 135, 206, 235 }, + { rgb_color, 79, 63, "light-sky-blue", "#87CEFA", 135, 206, 250 }, + { rgb_color, 80, 64, "midnight-blue", "#191970", 25, 25, 112 }, + { rgb_color, 81, 65, "navy", "#000080", 0, 0, 128 }, + { rgb_color, 82, 66, "dark-blue", "#00008B", 0, 0, 139 }, + { rgb_color, 83, 67, "medium-blue", "#0000CD", 0, 0, 205 }, + { rgb_color, 84, 68, "blue", "#0000FF", 0, 0, 255 }, + { rgb_color, 85, 69, "royal-blue", "#4169E1", 65, 105, 225 }, + { rgb_color, 86, 70, "blue-violet", "#8A2BE2", 138, 43, 226 }, + { rgb_color, 87, 71, "indigo", "#4B0082", 75, 0, 130 }, + { rgb_color, 88, 72, "dark-slate-blue", "#483D8B", 72, 61, 139 }, + { rgb_color, 89, 73, "slate-blue", "#6A5ACD", 106, 90, 205 }, + { rgb_color, 90, 74, "medium-slate-blue", "#7B68EE", 123, 104, 238 }, + { rgb_color, 91, 75, "medium-purple", "#9370DB", 147, 112, 219 }, + { rgb_color, 92, 76, "dark-magenta", "#8B008B", 139, 0, 139 }, + { rgb_color, 93, 77, "dark-violet", "#9400D3", 148, 0, 211 }, + { rgb_color, 94, 78, "dark-orchid", "#9932CC", 153, 50, 204 }, + { rgb_color, 95, 79, "medium-orchid", "#BA55D3", 186, 85, 211 }, + { rgb_color, 96, 80, "purple", "#800080", 128, 0, 128 }, + { rgb_color, 97, 81, "thistle", "#D8BFD8", 216, 191, 216 }, + { rgb_color, 98, 82, "plum", "#DDA0DD", 221, 160, 221 }, + { rgb_color, 99, 83, "violet", "#EE82EE", 238, 130, 238 }, + { rgb_color, 100, 84, "magenta", "#FF00FF", 255, 0, 255 }, + { rgb_color, 101, 85, "orchid", "#DA70D6", 218, 112, 214 }, + { rgb_color, 102, 86, "medium-violet-red", "#C71585", 199, 21, 133 }, + { rgb_color, 103, 87, "pale-violet-red", "#DB7093", 219, 112, 147 }, + { rgb_color, 104, 88, "deep-pink", "#FF1493", 255, 20, 147 }, + { rgb_color, 105, 89, "hot-pink", "#FF69B4", 255, 105, 180 }, + { rgb_color, 106, 90, "light-pink", "#FFB6C1", 255, 182, 193 }, + { rgb_color, 107, 91, "pink", "#FFC0CB", 255, 192, 203 }, + { rgb_color, 108, 92, "antique-white", "#FAEBD7", 250, 235, 215 }, + { rgb_color, 109, 93, "beige", "#F5F5DC", 245, 245, 220 }, + { rgb_color, 110, 94, "bisque", "#FFE4C4", 255, 228, 196 }, + { rgb_color, 111, 95, "blanched-almond", "#FFEBCD", 255, 235, 205 }, + { rgb_color, 112, 96, "wheat", "#F5DEB3", 245, 222, 179 }, + { rgb_color, 113, 97, "corn-silk", "#FFF8DC", 255, 248, 220 }, + { rgb_color, 114, 98, "lemon-chiffon", "#FFFACD", 255, 250, 205 }, + { rgb_color, 115, 99, "light-golden-rod-yellow", "#FAFAD2", 250, 250, 210 }, + { rgb_color, 116, 100, "light-yellow", "#FFFFE0", 255, 255, 224 }, + { rgb_color, 117, 101, "saddle-brown", "#8B4513", 139, 69, 19 }, + { rgb_color, 118, 102, "sienna", "#A0522D", 160, 82, 45 }, + { rgb_color, 119, 103, "chocolate", "#D2691E", 210, 105, 30 }, + { rgb_color, 120, 104, "peru", "#CD853F", 205, 133, 63 }, + { rgb_color, 121, 105, "sandy-brown", "#F4A460", 244, 164, 96 }, + { rgb_color, 122, 106, "burly-wood", "#DEB887", 222, 184, 135 }, + { rgb_color, 123, 107, "tan", "#D2B48C", 210, 180, 140 }, + { rgb_color, 124, 108, "rosy-brown", "#BC8F8F", 188, 143, 143 }, + { rgb_color, 125, 109, "moccasin", "#FFE4B5", 255, 228, 181 }, + { rgb_color, 126, 110, "navajo-white", "#FFDEAD", 255, 222, 173 }, + { rgb_color, 127, 111, "peach-puff", "#FFDAB9", 255, 218, 185 }, + { rgb_color, 128, 112, "misty-rose", "#FFE4E1", 255, 228, 225 }, + { rgb_color, 129, 113, "lavender-blush", "#FFF0F5", 255, 240, 245 }, + { rgb_color, 130, 114, "linen", "#FAF0E6", 250, 240, 230 }, + { rgb_color, 131, 115, "old-lace", "#FDF5E6", 253, 245, 230 }, + { rgb_color, 132, 116, "papaya-whip", "#FFEFD5", 255, 239, 213 }, + { rgb_color, 133, 117, "sea-shell", "#FFF5EE", 255, 245, 238 }, + { rgb_color, 134, 118, "mint-cream", "#F5FFFA", 245, 255, 250 }, + { rgb_color, 135, 119, "slate-gray", "#708090", 112, 128, 144 }, + { rgb_color, 136, 120, "light-slate-gray", "#778899", 119, 136, 153 }, + { rgb_color, 137, 121, "light-steel-blue", "#B0C4DE", 176, 196, 222 }, + { rgb_color, 138, 122, "lavender", "#E6E6FA", 230, 230, 250 }, + { rgb_color, 139, 123, "floral-white", "#FFFAF0", 255, 250, 240 }, + { rgb_color, 140, 124, "alice-blue", "#F0F8FF", 240, 248, 255 }, + { rgb_color, 141, 125, "ghost-white", "#F8F8FF", 248, 248, 255 }, + { rgb_color, 142, 126, "honeydew", "#F0FFF0", 240, 255, 240 }, + { rgb_color, 143, 127, "ivory", "#FFFFF0", 255, 255, 240 }, + { rgb_color, 144, 128, "azure", "#F0FFFF", 240, 255, 255 }, + { rgb_color, 145, 129, "snow", "#FFFAFA", 255, 250, 250 }, + { rgb_color, 146, 130, "black", "#000000", 0, 0, 0 }, + { rgb_color, 147, 131, "dim-gray", "#696969", 105, 105, 105 }, + { rgb_color, 148, 132, "gray", "#808080", 128, 128, 128 }, + { rgb_color, 149, 133, "dark-gray", "#A9A9A9", 169, 169, 169 }, + { rgb_color, 150, 134, "silver", "#C0C0C0", 192, 192, 192 }, + { rgb_color, 151, 135, "light-gray", "#D3D3D3", 211, 211, 211 }, + { rgb_color, 152, 136, "gainsboro", "#DCDCDC", 220, 220, 220 }, + { rgb_color, 153, 137, "white-smoke", "#F5F5F5", 245, 245, 245 }, + { rgb_color, 154, 138, "white", "#FFFFFF", 255, 255, 255 }, +}; + +int32 +colortable_to_int32(struct nethack_color *cte) +{ + int32 clr = (cte->r << 16) | (cte->g << 8) | cte->b; + return clr; +} + +char * +color_attr_to_str(color_attr *ca) +{ + static char buf[BUFSZ]; + + Sprintf(buf, "%s&%s", + clr2colorname(ca->color), + attr2attrname(ca->attr)); + return buf; +} + +/* parse string like "color&attr" into color_attr */ +boolean +color_attr_parse_str(color_attr *ca, char *str) +{ + char buf[BUFSZ]; + char *amp = NULL; + int tmp, c = NO_COLOR, a = ATR_NONE; + + (void) strncpy(buf, str, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; + + if ((amp = strchr(buf, '&')) != 0) + *amp = '\0'; + + if (amp) { + amp++; + c = match_str2clr(buf); + a = match_str2attr(amp, TRUE); + /* FIXME: match_str2clr & match_str2attr give config_error_add(), + so this is useless */ + if (c >= CLR_MAX && a == -1) { + /* try other way around */ + c = match_str2clr(amp); + a = match_str2attr(buf, TRUE); + } + if (c >= CLR_MAX || a == -1) + return FALSE; + } else { + /* one param only */ + tmp = match_str2attr(buf, FALSE); + if (tmp == -1) { + tmp = match_str2clr(buf); + if (tmp >= CLR_MAX) + return FALSE; + c = tmp; + } else { + a = tmp; + } + } + ca->attr = a; + ca->color = c; + return TRUE; +} + +boolean +query_color_attr(color_attr *ca, const char *prompt) +{ + int c, a; + + c = query_color(prompt, ca->color); + if (c == -1) + return FALSE; + a = query_attr(prompt, ca->attr); + if (a == -1) + return FALSE; + ca->color = c; + ca->attr = a; + return TRUE; +} + +const char * +attr2attrname(int attr) +{ + int i; + + for (i = 0; i < SIZE(attrnames); i++) + if (attrnames[i].attr == attr) + return attrnames[i].name; + return (char *) 0; +} + +/* + * Color support functions and data for "color" + * + * Used by: optfn_() + * + */ + +const char * +clr2colorname(int clr) +{ + int i; + + for (i = 0; i < SIZE(colornames); i++) + if (colornames[i].name && colornames[i].color == clr) + return colornames[i].name; + return (char *) 0; +} + +int +match_str2clr(char *str) +{ + int i, c = CLR_MAX; + + /* allow "lightblue", "light blue", and "light-blue" to match "light blue" + (also junk like "_l i-gh_t---b l u e" but we won't worry about that); + also copes with trailing space; caller has removed any leading space */ + for (i = 0; i < SIZE(colornames); i++) + if (colornames[i].name + && fuzzymatch(str, colornames[i].name, " -_", TRUE)) { + c = colornames[i].color; + break; + } + if (i == SIZE(colornames) && digit(*str)) + c = atoi(str); + + if (c < 0 || c >= CLR_MAX) { + config_error_add("Unknown color '%.60s'", str); + c = CLR_MAX; /* "none of the above" */ + } + return c; +} + +int +match_str2attr(const char *str, boolean complain) +{ + int i, a = -1; + + for (i = 0; i < SIZE(attrnames); i++) + if (attrnames[i].name + && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) { + a = attrnames[i].attr; + break; + } + + if (a == -1 && complain) + config_error_add("Unknown text attribute '%.50s'", str); + + return a; +} + +/* ask about highlighting attribute; for menu headers and menu + coloring patterns, only one attribute at a time is allowed; + for status highlighting, multiple attributes are allowed [overkill; + life would be much simpler if that were restricted to one also...] */ +int +query_attr(const char *prompt, int dflt_attr) +{ + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *) 0; + boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6)); + int clr = NO_COLOR; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + for (i = 0; i < SIZE(attrnames); i++) { + if (!attrnames[i].name) + break; + any.a_int = i + 1; + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + attrnames[i].attr, clr, attrnames[i].name, + (attrnames[i].attr == dflt_attr) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + } + end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute"); + pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (pick_cnt > 0) { + int j, k = 0; + + if (allow_many) { + /* PICK_ANY, with one preselected entry (ATR_NONE) which + should be excluded if any other choices were picked */ + for (i = 0; i < pick_cnt; ++i) { + j = picks[i].item.a_int - 1; + if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) { + switch (attrnames[j].attr) { + case ATR_NONE: + k = HL_NONE; + break; + case ATR_BOLD: + k |= HL_BOLD; + break; + case ATR_DIM: + k |= HL_DIM; + break; + case ATR_ITALIC: + k |= HL_ITALIC; + break; + case ATR_ULINE: + k |= HL_ULINE; + break; + case ATR_BLINK: + k |= HL_BLINK; + break; + case ATR_INVERSE: + k |= HL_INVERSE; + break; + } + } + } + } else { + /* PICK_ONE, but might get 0 or 2 due to preselected entry */ + j = picks[0].item.a_int - 1; + /* pick_cnt==2: explicitly picked something other than the + preselected entry */ + if (pick_cnt == 2 && attrnames[j].attr == dflt_attr) + j = picks[1].item.a_int - 1; + k = attrnames[j].attr; + } + free((genericptr_t) picks); + return k; + } else if (pick_cnt == 0 && !allow_many) { + /* PICK_ONE, preselected entry explicitly chosen */ + return dflt_attr; + } + /* either ESC to explicitly cancel (pick_cnt==-1) or + PICK_ANY with preselected entry toggled off and nothing chosen */ + return -1; +} + +int +query_color(const char *prompt, int dflt_color) +{ + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *) 0; + + /* replace user patterns with color name ones and force 'menucolors' On */ + basic_menu_colors(TRUE); + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + for (i = 0; i < SIZE(colornames); i++) { + if (!colornames[i].name) + break; + any.a_int = i + 1; + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, NO_COLOR, colornames[i].name, + (colornames[i].color == dflt_color) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + } + end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color"); + pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + + /* remove temporary color name patterns and restore user-specified ones; + reset 'menucolors' option to its previous value */ + basic_menu_colors(FALSE); + + if (pick_cnt > 0) { + i = colornames[picks[0].item.a_int - 1].color; + /* pick_cnt==2: explicitly picked something other than the + preselected entry */ + if (pick_cnt == 2 && i == NO_COLOR) + i = colornames[picks[1].item.a_int - 1].color; + free((genericptr_t) picks); + return i; + } else if (pick_cnt == 0) { + /* pick_cnt==0: explicitly picking preselected entry toggled it off */ + return dflt_color; + } + return -1; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +extern const char regex_id[]; /* from sys/share/regex.{c,cpp} */ + +/* True: temporarily replace menu color entries with a fake set of menu + colors, { "light blue"=light_blue, "blue"=blue, "red"=red, &c }, that + illustrates most colors for use when the pick-a-color menu is rendered; + suppresses black and white because one of those will likely be invisible + due to matching the background; False: restore user-specified colorings */ +void +basic_menu_colors(boolean load_colors) +{ + if (load_colors) { + /* replace normal menu colors with a set specifically for colors */ + gs.save_menucolors = iflags.use_menu_color; + gs.save_colorings = gm.menu_colorings; + + iflags.use_menu_color = TRUE; + if (gc.color_colorings) { + /* use the alternate colorings which were set up previously */ + gm.menu_colorings = gc.color_colorings; + } else { + /* create the alternate colorings once */ + char cnm[QBUFSZ]; + int i, c; + boolean pmatchregex = !strcmpi(regex_id, "pmatchregex"); + const char *patternfmt = pmatchregex ? "*%s" : "%s"; + + /* menu_colorings pointer has been saved; clear it in order + to add the alternate entries as if from scratch */ + gm.menu_colorings = (struct menucoloring *) 0; + + /* this orders the patterns last-in/first-out; that means + that the "light " variations come before the basic + "" ones, which is exactly what we want */ + for (i = 0; i < SIZE(colornames); ++i) { + if (!colornames[i].name) /* first alias entry has no name */ + break; + c = colornames[i].color; + if (c == CLR_BLACK || c == CLR_WHITE || c == NO_COLOR) + continue; /* skip these */ + Sprintf(cnm, patternfmt, colornames[i].name); + add_menu_coloring_parsed(cnm, c, ATR_NONE); + } + + /* right now, menu_colorings contains the alternate color list; + remember that list for future pick-a-color instances and + also keep it as is for this instance */ + gc.color_colorings = gm.menu_colorings; + } + } else { + /* restore normal user-specified menu colors */ + iflags.use_menu_color = gs.save_menucolors; + gm.menu_colorings = gs.save_colorings; + } +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +boolean +add_menu_coloring_parsed(const char *str, int c, int a) +{ + static const char re_error[] = "Menucolor regex error"; + struct menucoloring *tmp; + + if (!str) + return FALSE; + tmp = (struct menucoloring *) alloc(sizeof *tmp); + tmp->match = regex_init(); + /* test_regex_pattern() has already validated this regexp but parsing + it again could conceivably run out of memory */ + if (!regex_compile(str, tmp->match)) { + char errbuf[BUFSZ]; + char *re_error_desc = regex_error_desc(tmp->match, errbuf); + + /* free first in case reason for regcomp failure was out-of-memory */ + regex_free(tmp->match); + free((genericptr_t) tmp); + config_error_add("%s: %s", re_error, re_error_desc); + return FALSE; + } + tmp->next = gm.menu_colorings; + tmp->origstr = dupstr(str); + tmp->color = c; + tmp->attr = a; + gm.menu_colorings = tmp; + iflags.use_menu_color = TRUE; + return TRUE; +} + +/* parse '"regex_string"=color&attr' and add it to menucoloring */ +boolean +add_menu_coloring(char *tmpstr) /* never Null but could be empty */ +{ + int c = NO_COLOR, a = ATR_NONE; + char *tmps, *cs, *amp; + char str[BUFSZ]; + + (void) strncpy(str, tmpstr, sizeof str - 1); + str[sizeof str - 1] = '\0'; + + if ((cs = strchr(str, '=')) == 0) { + config_error_add("Malformed MENUCOLOR"); + return FALSE; + } + + tmps = cs + 1; /* advance past '=' */ + mungspaces(tmps); + if ((amp = strchr(tmps, '&')) != 0) + *amp = '\0'; + + c = match_str2clr(tmps); + if (c >= CLR_MAX) + return FALSE; + + if (amp) { + tmps = amp + 1; /* advance past '&' */ + a = match_str2attr(tmps, TRUE); + if (a == -1) + return FALSE; + } + + /* the regexp portion here has not been condensed by mungspaces() */ + *cs = '\0'; + tmps = str; + if (*tmps == '"' || *tmps == '\'') { + cs--; + while (isspace((uchar) *cs)) + cs--; + if (*cs == *tmps) { + *cs = '\0'; + tmps++; + } + } + return add_menu_coloring_parsed(tmps, c, a); +} + +/* release all menu color patterns */ +void +free_menu_coloring(void) +{ + /* either menu_colorings or color_colorings or both might need to + be freed or already be Null; do-loop will iterate at most twice */ + do { + struct menucoloring *tmp, *tmp2; + + for (tmp = gm.menu_colorings; tmp; tmp = tmp2) { + tmp2 = tmp->next; + regex_free(tmp->match); + free((genericptr_t) tmp->origstr); + free((genericptr_t) tmp); + } + gm.menu_colorings = gc.color_colorings; + gc.color_colorings = (struct menucoloring *) 0; + } while (gm.menu_colorings); +} + +/* release a specific menu color pattern; not used for color_colorings */ +void +free_one_menu_coloring(int idx) /* 0 .. */ +{ + struct menucoloring *tmp = gm.menu_colorings; + struct menucoloring *prev = NULL; + + while (tmp) { + if (idx == 0) { + struct menucoloring *next = tmp->next; + + regex_free(tmp->match); + free((genericptr_t) tmp->origstr); + free((genericptr_t) tmp); + if (prev) + prev->next = next; + else + gm.menu_colorings = next; + return; + } + idx--; + prev = tmp; + tmp = tmp->next; + } +} + +int +count_menucolors(void) +{ + struct menucoloring *tmp; + int count = 0; + + for (tmp = gm.menu_colorings; tmp; tmp = tmp->next) + count++; + return count; +} + +/* returns -1 on no-match. + * buf is NONNULLARG1 + */ +int32 +check_enhanced_colors(const char *buf) +{ + int c; + + for (c = 0; c < SIZE(colortable); ++c) { + if (!strcmpi(buf, colortable[c].name)) + return colortable_to_int32(&colortable[c]); + } + return -1; +} + + diff --git a/src/options.c b/src/options.c index 7c935a4c9..d397cc63b 100644 --- a/src/options.c +++ b/src/options.c @@ -26,9 +26,6 @@ NEARDATA struct instance_flags iflags; /* provide linkage */ #define PREV_MSGS 0 #endif -static char *color_attr_to_str(color_attr *); -static boolean color_attr_parse_str(color_attr *, char *); - /* * NOTE: If you add (or delete) an option, please review the following: * doc/options.txt @@ -340,17 +337,12 @@ static int petname_optfn(int, int, boolean, char *, char *); static int shared_menu_optfn(int, int, boolean, char *, char *); static int spcfn_misc_menu_cmd(int, int, boolean, char *, char *); -static const char *attr2attrname(int); -static void basic_menu_colors(boolean); static const char * msgtype2name(int); static int query_msgtype(void); static boolean msgtype_add(int, char *); static void free_one_msgtype(int); static int msgtype_count(void); static boolean test_regex_pattern(const char *, const char *); -static boolean add_menu_coloring_parsed(const char *, int, int); -static void free_one_menu_coloring(int); -static int count_menucolors(void); static boolean parse_role_opt(int, boolean, const char *, char *, char **); static char *get_cnf_role_opt(int); static unsigned int longest_option_name(int, int); @@ -2095,61 +2087,6 @@ optfn_menu_shift_right(int optidx, int req, boolean negated, /* end of shared key assignments for menu commands */ -static char * -color_attr_to_str(color_attr *ca) -{ - static char buf[BUFSZ]; - - Sprintf(buf, "%s&%s", - clr2colorname(ca->color), - attr2attrname(ca->attr)); - return buf; -} - -/* parse string like "color&attr" into color_attr */ -static boolean -color_attr_parse_str(color_attr *ca, char *str) -{ - char buf[BUFSZ]; - char *amp = NULL; - int tmp, c = NO_COLOR, a = ATR_NONE; - - (void) strncpy(buf, str, sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; - - if ((amp = strchr(buf, '&')) != 0) - *amp = '\0'; - - if (amp) { - amp++; - c = match_str2clr(buf); - a = match_str2attr(amp, TRUE); - /* FIXME: match_str2clr & match_str2attr give config_error_add(), - so this is useless */ - if (c >= CLR_MAX && a == -1) { - /* try other way around */ - c = match_str2clr(amp); - a = match_str2attr(buf, TRUE); - } - if (c >= CLR_MAX || a == -1) - return FALSE; - } else { - /* one param only */ - tmp = match_str2attr(buf, FALSE); - if (tmp == -1) { - tmp = match_str2clr(buf); - if (tmp >= CLR_MAX) - return FALSE; - c = tmp; - } else { - a = tmp; - } - } - ca->attr = a; - ca->color = c; - return TRUE; -} - static int optfn_menu_headings( int optidx, @@ -7419,335 +7356,6 @@ parsebindings(char *bindings) return ret; } -/* - * Color support functions and data for "color" - * - * Used by: optfn_() - * - */ - -struct color_names { - const char *name; - int color; -}; - -static const struct color_names colornames[] = { - { "black", CLR_BLACK }, - { "red", CLR_RED }, - { "green", CLR_GREEN }, - { "brown", CLR_BROWN }, - { "blue", CLR_BLUE }, - { "magenta", CLR_MAGENTA }, - { "cyan", CLR_CYAN }, - { "gray", CLR_GRAY }, - { "orange", CLR_ORANGE }, - { "light green", CLR_BRIGHT_GREEN }, - { "yellow", CLR_YELLOW }, - { "light blue", CLR_BRIGHT_BLUE }, - { "light magenta", CLR_BRIGHT_MAGENTA }, - { "light cyan", CLR_BRIGHT_CYAN }, - { "white", CLR_WHITE }, - { "no color", NO_COLOR }, - { (const char *) 0, CLR_BLACK }, /* everything after this is an alias */ - { "transparent", NO_COLOR }, - { "purple", CLR_MAGENTA }, - { "light purple", CLR_BRIGHT_MAGENTA }, - { "bright purple", CLR_BRIGHT_MAGENTA }, - { "grey", CLR_GRAY }, - { "bright red", CLR_ORANGE }, - { "bright green", CLR_BRIGHT_GREEN }, - { "bright blue", CLR_BRIGHT_BLUE }, - { "bright magenta", CLR_BRIGHT_MAGENTA }, - { "bright cyan", CLR_BRIGHT_CYAN } -}; - -struct attr_names { - const char *name; - int attr; -}; - -static const struct attr_names attrnames[] = { - { "none", ATR_NONE }, - { "bold", ATR_BOLD }, - { "dim", ATR_DIM }, - { "italic", ATR_ITALIC }, - { "underline", ATR_ULINE }, - { "blink", ATR_BLINK }, - { "inverse", ATR_INVERSE }, - { (const char *) 0, ATR_NONE }, /* everything after this is an alias */ - { "normal", ATR_NONE }, - { "uline", ATR_ULINE }, - { "reverse", ATR_INVERSE }, -}; - -const char * -clr2colorname(int clr) -{ - int i; - - for (i = 0; i < SIZE(colornames); i++) - if (colornames[i].name && colornames[i].color == clr) - return colornames[i].name; - return (char *) 0; -} - -int -match_str2clr(char *str) -{ - int i, c = CLR_MAX; - - /* allow "lightblue", "light blue", and "light-blue" to match "light blue" - (also junk like "_l i-gh_t---b l u e" but we won't worry about that); - also copes with trailing space; caller has removed any leading space */ - for (i = 0; i < SIZE(colornames); i++) - if (colornames[i].name - && fuzzymatch(str, colornames[i].name, " -_", TRUE)) { - c = colornames[i].color; - break; - } - if (i == SIZE(colornames) && digit(*str)) - c = atoi(str); - - if (c < 0 || c >= CLR_MAX) { - config_error_add("Unknown color '%.60s'", str); - c = CLR_MAX; /* "none of the above" */ - } - return c; -} - -static const char * -attr2attrname(int attr) -{ - int i; - - for (i = 0; i < SIZE(attrnames); i++) - if (attrnames[i].attr == attr) - return attrnames[i].name; - return (char *) 0; -} - -int -match_str2attr(const char *str, boolean complain) -{ - int i, a = -1; - - for (i = 0; i < SIZE(attrnames); i++) - if (attrnames[i].name - && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) { - a = attrnames[i].attr; - break; - } - - if (a == -1 && complain) - config_error_add("Unknown text attribute '%.50s'", str); - - return a; -} - -extern const char regex_id[]; /* from sys/share/regex.{c,cpp} */ - -DISABLE_WARNING_FORMAT_NONLITERAL - -/* True: temporarily replace menu color entries with a fake set of menu - colors, { "light blue"=light_blue, "blue"=blue, "red"=red, &c }, that - illustrates most colors for use when the pick-a-color menu is rendered; - suppresses black and white because one of those will likely be invisible - due to matching the background; False: restore user-specified colorings */ -static void -basic_menu_colors(boolean load_colors) -{ - if (load_colors) { - /* replace normal menu colors with a set specifically for colors */ - gs.save_menucolors = iflags.use_menu_color; - gs.save_colorings = gm.menu_colorings; - - iflags.use_menu_color = TRUE; - if (gc.color_colorings) { - /* use the alternate colorings which were set up previously */ - gm.menu_colorings = gc.color_colorings; - } else { - /* create the alternate colorings once */ - char cnm[QBUFSZ]; - int i, c; - boolean pmatchregex = !strcmpi(regex_id, "pmatchregex"); - const char *patternfmt = pmatchregex ? "*%s" : "%s"; - - /* menu_colorings pointer has been saved; clear it in order - to add the alternate entries as if from scratch */ - gm.menu_colorings = (struct menucoloring *) 0; - - /* this orders the patterns last-in/first-out; that means - that the "light " variations come before the basic - "" ones, which is exactly what we want */ - for (i = 0; i < SIZE(colornames); ++i) { - if (!colornames[i].name) /* first alias entry has no name */ - break; - c = colornames[i].color; - if (c == CLR_BLACK || c == CLR_WHITE || c == NO_COLOR) - continue; /* skip these */ - Sprintf(cnm, patternfmt, colornames[i].name); - add_menu_coloring_parsed(cnm, c, ATR_NONE); - } - - /* right now, menu_colorings contains the alternate color list; - remember that list for future pick-a-color instances and - also keep it as is for this instance */ - gc.color_colorings = gm.menu_colorings; - } - } else { - /* restore normal user-specified menu colors */ - iflags.use_menu_color = gs.save_menucolors; - gm.menu_colorings = gs.save_colorings; - } -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -int -query_color(const char *prompt, int dflt_color) -{ - winid tmpwin; - anything any; - int i, pick_cnt; - menu_item *picks = (menu_item *) 0; - - /* replace user patterns with color name ones and force 'menucolors' On */ - basic_menu_colors(TRUE); - - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - for (i = 0; i < SIZE(colornames); i++) { - if (!colornames[i].name) - break; - any.a_int = i + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, NO_COLOR, colornames[i].name, - (colornames[i].color == dflt_color) ? MENU_ITEMFLAGS_SELECTED - : MENU_ITEMFLAGS_NONE); - } - end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color"); - pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); - destroy_nhwindow(tmpwin); - - /* remove temporary color name patterns and restore user-specified ones; - reset 'menucolors' option to its previous value */ - basic_menu_colors(FALSE); - - if (pick_cnt > 0) { - i = colornames[picks[0].item.a_int - 1].color; - /* pick_cnt==2: explicitly picked something other than the - preselected entry */ - if (pick_cnt == 2 && i == NO_COLOR) - i = colornames[picks[1].item.a_int - 1].color; - free((genericptr_t) picks); - return i; - } else if (pick_cnt == 0) { - /* pick_cnt==0: explicitly picking preselected entry toggled it off */ - return dflt_color; - } - return -1; -} - -/* ask about highlighting attribute; for menu headers and menu - coloring patterns, only one attribute at a time is allowed; - for status highlighting, multiple attributes are allowed [overkill; - life would be much simpler if that were restricted to one also...] */ -int -query_attr(const char *prompt, int dflt_attr) -{ - winid tmpwin; - anything any; - int i, pick_cnt; - menu_item *picks = (menu_item *) 0; - boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6)); - int clr = NO_COLOR; - - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - for (i = 0; i < SIZE(attrnames); i++) { - if (!attrnames[i].name) - break; - any.a_int = i + 1; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - attrnames[i].attr, clr, attrnames[i].name, - (attrnames[i].attr == dflt_attr) ? MENU_ITEMFLAGS_SELECTED - : MENU_ITEMFLAGS_NONE); - } - end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute"); - pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks); - destroy_nhwindow(tmpwin); - if (pick_cnt > 0) { - int j, k = 0; - - if (allow_many) { - /* PICK_ANY, with one preselected entry (ATR_NONE) which - should be excluded if any other choices were picked */ - for (i = 0; i < pick_cnt; ++i) { - j = picks[i].item.a_int - 1; - if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) { - switch (attrnames[j].attr) { - case ATR_NONE: - k = HL_NONE; - break; - case ATR_BOLD: - k |= HL_BOLD; - break; - case ATR_DIM: - k |= HL_DIM; - break; - case ATR_ITALIC: - k |= HL_ITALIC; - break; - case ATR_ULINE: - k |= HL_ULINE; - break; - case ATR_BLINK: - k |= HL_BLINK; - break; - case ATR_INVERSE: - k |= HL_INVERSE; - break; - } - } - } - } else { - /* PICK_ONE, but might get 0 or 2 due to preselected entry */ - j = picks[0].item.a_int - 1; - /* pick_cnt==2: explicitly picked something other than the - preselected entry */ - if (pick_cnt == 2 && attrnames[j].attr == dflt_attr) - j = picks[1].item.a_int - 1; - k = attrnames[j].attr; - } - free((genericptr_t) picks); - return k; - } else if (pick_cnt == 0 && !allow_many) { - /* PICK_ONE, preselected entry explicitly chosen */ - return dflt_attr; - } - /* either ESC to explicitly cancel (pick_cnt==-1) or - PICK_ANY with preselected entry toggled off and nothing chosen */ - return -1; -} - -boolean -query_color_attr(color_attr *ca, const char *prompt) -{ - int c, a; - - c = query_color(prompt, ca->color); - if (c == -1) - return FALSE; - a = query_attr(prompt, ca->attr); - if (a == -1) - return FALSE; - ca->color = c; - ca->attr = a; - return TRUE; -} - static const struct { const char *name; xint8 msgtyp; @@ -7974,141 +7582,6 @@ test_regex_pattern(const char *str, const char *errmsg) return retval; } -static boolean -add_menu_coloring_parsed(const char *str, int c, int a) -{ - static const char re_error[] = "Menucolor regex error"; - struct menucoloring *tmp; - - if (!str) - return FALSE; - tmp = (struct menucoloring *) alloc(sizeof *tmp); - tmp->match = regex_init(); - /* test_regex_pattern() has already validated this regexp but parsing - it again could conceivably run out of memory */ - if (!regex_compile(str, tmp->match)) { - char errbuf[BUFSZ]; - char *re_error_desc = regex_error_desc(tmp->match, errbuf); - - /* free first in case reason for regcomp failure was out-of-memory */ - regex_free(tmp->match); - free((genericptr_t) tmp); - config_error_add("%s: %s", re_error, re_error_desc); - return FALSE; - } - tmp->next = gm.menu_colorings; - tmp->origstr = dupstr(str); - tmp->color = c; - tmp->attr = a; - gm.menu_colorings = tmp; - iflags.use_menu_color = TRUE; - return TRUE; -} - -/* parse '"regex_string"=color&attr' and add it to menucoloring */ -boolean -add_menu_coloring(char *tmpstr) /* never Null but could be empty */ -{ - int c = NO_COLOR, a = ATR_NONE; - char *tmps, *cs, *amp; - char str[BUFSZ]; - - (void) strncpy(str, tmpstr, sizeof str - 1); - str[sizeof str - 1] = '\0'; - - if ((cs = strchr(str, '=')) == 0) { - config_error_add("Malformed MENUCOLOR"); - return FALSE; - } - - tmps = cs + 1; /* advance past '=' */ - mungspaces(tmps); - if ((amp = strchr(tmps, '&')) != 0) - *amp = '\0'; - - c = match_str2clr(tmps); - if (c >= CLR_MAX) - return FALSE; - - if (amp) { - tmps = amp + 1; /* advance past '&' */ - a = match_str2attr(tmps, TRUE); - if (a == -1) - return FALSE; - } - - /* the regexp portion here has not been condensed by mungspaces() */ - *cs = '\0'; - tmps = str; - if (*tmps == '"' || *tmps == '\'') { - cs--; - while (isspace((uchar) *cs)) - cs--; - if (*cs == *tmps) { - *cs = '\0'; - tmps++; - } - } - return add_menu_coloring_parsed(tmps, c, a); -} - -/* release all menu color patterns */ -void -free_menu_coloring(void) -{ - /* either menu_colorings or color_colorings or both might need to - be freed or already be Null; do-loop will iterate at most twice */ - do { - struct menucoloring *tmp, *tmp2; - - for (tmp = gm.menu_colorings; tmp; tmp = tmp2) { - tmp2 = tmp->next; - regex_free(tmp->match); - free((genericptr_t) tmp->origstr); - free((genericptr_t) tmp); - } - gm.menu_colorings = gc.color_colorings; - gc.color_colorings = (struct menucoloring *) 0; - } while (gm.menu_colorings); -} - -/* release a specific menu color pattern; not used for color_colorings */ -static void -free_one_menu_coloring(int idx) /* 0 .. */ -{ - struct menucoloring *tmp = gm.menu_colorings; - struct menucoloring *prev = NULL; - - while (tmp) { - if (idx == 0) { - struct menucoloring *next = tmp->next; - - regex_free(tmp->match); - free((genericptr_t) tmp->origstr); - free((genericptr_t) tmp); - if (prev) - prev->next = next; - else - gm.menu_colorings = next; - return; - } - idx--; - prev = tmp; - tmp = tmp->next; - } -} - -static int -count_menucolors(void) -{ - struct menucoloring *tmp; - int count = 0; - - for (tmp = gm.menu_colorings; tmp; tmp = tmp->next) - count++; - return count; -} - /* parse 'role' or 'race' or 'gender' or 'alignment' */ static boolean parse_role_opt( diff --git a/src/utf8map.c b/src/utf8map.c index 4b229fbfd..9a896128e 100644 --- a/src/utf8map.c +++ b/src/utf8map.c @@ -51,6 +51,7 @@ static int32_t rgbstr_to_int32(const char *rgbstr); boolean closest_color(uint32_t lcolor, uint32_t *closecolor, int *clridx); static const long nonzero_black = 0x1000000; static int color_distance(uint32_t, uint32_t); +static boolean onlyhexdigits(const char *buf); static void to_custom_symset_entry_callback(int glyph, struct find_struct *findwhat) @@ -142,6 +143,20 @@ glyphrep_to_custom_map_entries(const char *op, int *glyphptr) return 0; } +static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; + +static boolean +onlyhexdigits(const char *buf) +{ + const char *dp = buf; + + for (dp = buf; *dp; ++dp) { + if (!(strchr(hex, *dp) || *dp == '-')) + return FALSE; + } + return TRUE; +} + static int32_t rgbstr_to_int32(const char *rgbstr) { @@ -151,41 +166,51 @@ rgbstr_to_int32(const char *rgbstr) char buf[BUFSZ]; boolean dash = FALSE; - r = g = b = 0; - c_g = c_b = (char *) 0; - Snprintf(buf, sizeof buf, "%s", rgbstr); - c_r = cp = buf; - while (*cp) { - if (digit(*cp) || *cp == '-') { - if (*cp == '-') { - *cp = '\0'; - milestone++; - dash = TRUE; + + Snprintf(buf, sizeof buf, "%s", + rgbstr ? rgbstr : ""); + + if (*buf && onlyhexdigits(buf)) { + r = g = b = 0; + c_g = c_b = (char *) 0; + c_r = cp = buf; + while (*cp) { + if (digit(*cp) || *cp == '-') { + if (*cp == '-') { + *cp = '\0'; + milestone++; + dash = TRUE; + } + cp++; + if (dash) { + if (milestone < 2) + c_g = cp; + else + c_b = cp; + dash = FALSE; + } + } else { + return -1L; } - cp++; - if (dash) { - if (milestone < 2) - c_g = cp; - else - c_b = cp; - dash = FALSE; - } - } else { - return -1L; + } + /* sanity checks */ + if (c_r && c_g && c_b + && (strlen(c_r) > 0 && strlen(c_r) < 4) + && (strlen(c_g) > 0 && strlen(c_g) < 4) + && (strlen(c_b) > 0 && strlen(c_b) < 4)) { + r = atoi(c_r); + g = atoi(c_g); + b = atoi(c_b); + rgb = (r << 16) | (g << 8) | (b << 0); + return rgb; + } + } else if (*buf) { + /* perhaps an enhanced color name was used instead of rgb value? */ + if ((rgb = check_enhanced_colors(buf)) != -1) { + return rgb; } } - /* sanity checks */ - if (c_r && c_g && c_b - && (strlen(c_r) > 0 && strlen(c_r) < 4) - && (strlen(c_g) > 0 && strlen(c_g) < 4) - && (strlen(c_b) > 0 && strlen(c_b) < 4)) { - r = atoi(c_r); - g = atoi(c_g); - b = atoi(c_b); - rgb = (r << 16) | (g << 8) | (b << 0); - return rgb; - } - return -1L; + return -1; } static char * @@ -209,7 +234,6 @@ unicode_val(const char *cp) { const char *dp; int cval = 0, dcount; - static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; if (cp && *cp) { cval = dcount = 0; diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index ae9cf2a4c..f682444f8 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -493,7 +493,8 @@ HACK_H = ../src/hack.h-t # all .c that are part of the main NetHack program and are not operating- or # windowing-system specific. Do not include date.c in this list. HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ - botl.c cmd.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c \ + botl.c coloratt.c cmd.c dbridge.c decl.c detect.c dig.c display.c \ + dlb.c do.c \ do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c drawing.c \ dungeon.c eat.c end.c engrave.c exper.c explode.c extralev.c \ files.c fountain.c hack.c hacklib.c \ @@ -568,6 +569,7 @@ HOSTOBJ = $(FIRSTOBJ) alloc.o drawing.o HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)apply.o $(TARGETPFX)artifact.o $(TARGETPFX)attrib.o \ $(TARGETPFX)ball.o $(TARGETPFX)bones.o $(TARGETPFX)botl.o \ + $(TARGETPFX)coloratt.o \ $(TARGETPFX)cmd.o $(TARGETPFX)dbridge.o $(TARGETPFX)decl.o \ $(TARGETPFX)detect.o $(TARGETPFX)dig.o $(TARGETPFX)display.o \ $(TARGETPFX)dlb.o $(TARGETPFX)do.o $(TARGETPFX)do_name.o \