shorten options.c a little by moving some color stuff out of it

creates new coloratt.c file

Also, this attempts to fulfill a wish-list item by paxed, to
allow naming colors in symbols file by name as an alternative
to using r-g-b values.  The basic color names as well as html
color names are supported.
This commit is contained in:
nhmall
2024-03-03 14:11:17 -05:00
parent fb353fff6c
commit dbea5d8684
8 changed files with 807 additions and 573 deletions

View File

@@ -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/<various>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 <foo>" variations come before the basic
"<foo>" 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(