Lua: set and get config options

Still needs more work, especially the error handling.
This commit is contained in:
Pasi Kallinen
2021-02-06 18:57:41 +02:00
parent c4d9ceda9d
commit d817564a6a
5 changed files with 276 additions and 126 deletions

View File

@@ -70,6 +70,16 @@ Example:
local loc = nh.getmap(x,y);
nh.pline("Map location at (" .. x .. "," .. y .. ) is " .. (loc.lit ? "lit" : "unlit") );
=== get_config
Get current value of a boolean or a compound configuration option.
Example:
local wt = nh.get_config("windowtype");
=== gettrap
Get trap info at x,y
@@ -158,6 +168,15 @@ Example:
local selected = nh.menu("prompt", default, pickX, { {key:"a", text:"option a"}, {key:"b", text:"option b"} } );
=== parse_config
Parse string as if it was read from a config file.
Example:
nh.parse_config("OPTIONS=color");
=== pline
Show the text in the message area.

View File

@@ -775,6 +775,7 @@ extern void nh_compress(const char *);
extern void nh_uncompress(const char *);
extern boolean lock_file(const char *, int, int);
extern void unlock_file(const char *);
extern boolean parse_config_line(char *);
#ifdef USER_SOUNDS
extern boolean can_read_file(const char *);
#endif
@@ -784,6 +785,7 @@ extern int config_error_done(void);
extern boolean read_config_file(const char *, int);
extern void check_recordfile(const char *);
extern void read_wizkit(void);
extern boolean parse_conf_str(const char *str, boolean (*proc)(char *));
extern int read_sym_file(int);
extern int parse_sym_line(char *, int);
extern void paniclog(const char *, const char *);
@@ -1782,6 +1784,7 @@ extern void initoptions(void);
extern void initoptions_init(void);
extern void initoptions_finish(void);
extern boolean parseoptions(char *, boolean, boolean);
extern char *get_option_value(const char *);
extern int doset(void);
extern int dotogglepickup(void);
extern void option_help(void);

View File

@@ -3064,146 +3064,214 @@ read_wizkit(void)
return;
}
/* parse_conf_file
*
* Read from file fp, handling comments, empty lines, config sections,
struct _cnf_parser_state {
char *inbuf;
size_t inbufsz;
int rv;
char *ep;
char *buf;
boolean skip, morelines;
boolean cont;
boolean pbreak;
};
/* Initialize config parser data */
static void
cnf_parser_init(struct _cnf_parser_state *parser)
{
parser->rv = TRUE; /* assume successful parse */
parser->ep = parser->buf = (char *) 0;
parser->skip = FALSE;
parser->morelines = FALSE;
parser->inbufsz = 4 * BUFSZ;
parser->inbuf = (char *) alloc(parser->inbufsz);
parser->cont = FALSE;
parser->pbreak = FALSE;
memset(parser->inbuf, 0, parser->inbufsz);
}
/*
* Parse config buffer, handling comments, empty lines, config sections,
* CHOOSE, and line continuation, calling proc for every valid line.
*
* Continued lines are merged together with one space in between.
*/
static void
parse_conf_buf(struct _cnf_parser_state *p, boolean (*proc)(char *))
{
p->cont = FALSE;
p->pbreak = FALSE;
p->ep = index(p->inbuf, '\n');
if (p->skip) { /* in case previous line was too long */
if (p->ep)
p->skip = FALSE; /* found newline; next line is normal */
} else {
if (!p->ep) { /* newline missing */
if (strlen(p->inbuf) < (p->inbufsz - 2)) {
/* likely the last line of file is just
missing a newline; process it anyway */
p->ep = eos(p->inbuf);
} else {
config_error_add("Line too long, skipping");
p->skip = TRUE; /* discard next fgets */
}
} else {
*p->ep = '\0'; /* remove newline */
}
if (p->ep) {
char *tmpbuf = (char *) 0;
int len;
boolean ignoreline = FALSE;
boolean oldline = FALSE;
/* line continuation (trailing '\') */
p->morelines = (--p->ep >= p->inbuf && *p->ep == '\\');
if (p->morelines)
*p->ep = '\0';
/* trim off spaces at end of line */
while (p->ep >= p->inbuf
&& (*p->ep == ' ' || *p->ep == '\t' || *p->ep == '\r'))
*p->ep-- = '\0';
if (!config_error_nextline(p->inbuf)) {
p->rv = FALSE;
if (p->buf)
free(p->buf), p->buf = (char *) 0;
p->pbreak = TRUE;
return;
}
p->ep = p->inbuf;
while (*p->ep == ' ' || *p->ep == '\t')
++p->ep;
/* ignore empty lines and full-line comment lines */
if (!*p->ep || *p->ep == '#')
ignoreline = TRUE;
if (p->buf)
oldline = TRUE;
/* merge now read line with previous ones, if necessary */
if (!ignoreline) {
len = (int) strlen(p->ep) + 1; /* +1: final '\0' */
if (p->buf)
len += (int) strlen(p->buf) + 1; /* +1: space */
tmpbuf = (char *) alloc(len);
*tmpbuf = '\0';
if (p->buf) {
Strcat(strcpy(tmpbuf, p->buf), " ");
free(p->buf);
}
p->buf = strcat(tmpbuf, p->ep);
if (strlen(p->buf) >= p->inbufsz)
p->buf[p->inbufsz - 1] = '\0';
}
if (p->morelines || (ignoreline && !oldline))
return;
if (handle_config_section(p->buf)) {
free(p->buf);
p->buf = (char *) 0;
return;
}
/* from here onwards, we'll handle buf only */
if (match_varname(p->buf, "CHOOSE", 6)) {
char *section;
char *bufp = find_optparam(p->buf);
if (!bufp) {
config_error_add("Format is CHOOSE=section1,section2,...");
p->rv = FALSE;
free(p->buf);
p->buf = (char *) 0;
return;
}
bufp++;
if (g.config_section_chosen)
free(g.config_section_chosen),
g.config_section_chosen = 0;
section = choose_random_part(bufp, ',');
if (section) {
g.config_section_chosen = dupstr(section);
} else {
config_error_add("No config section to choose");
p->rv = FALSE;
}
free(p->buf);
p->buf = (char *) 0;
return;
}
if (!(*proc)(p->buf))
p->rv = FALSE;
free(p->buf);
p->buf = (char *) 0;
}
}
}
boolean
parse_conf_str(const char *str, boolean (*proc)(char *))
{
size_t len;
struct _cnf_parser_state parser;
cnf_parser_init(&parser);
free_config_sections();
config_error_init(FALSE, "parse_conf_str", FALSE);
while (str && *str) {
len = 0;
while (*str && len < (parser.inbufsz-1)) {
parser.inbuf[len] = *str;
len++;
str++;
if (parser.inbuf[len-1] == '\n')
break;
}
parser.inbuf[len] = '\0';
parse_conf_buf(&parser, proc);
if (parser.pbreak)
break;
}
if (parser.buf)
free(parser.buf);
free_config_sections();
config_error_done();
return parser.rv;
}
/* parse_conf_file
*
* Read from file fp, calling parse_conf_buf for each line.
*/
static boolean
parse_conf_file(FILE *fp, boolean (*proc)(char *))
{
char inbuf[4 * BUFSZ];
boolean rv = TRUE; /* assume successful parse */
char *ep;
boolean skip = FALSE, morelines = FALSE;
char *buf = (char *) 0;
size_t inbufsz = sizeof inbuf;
struct _cnf_parser_state parser;
cnf_parser_init(&parser);
free_config_sections();
while (fgets(inbuf, (int) inbufsz, fp)) {
ep = index(inbuf, '\n');
if (skip) { /* in case previous line was too long */
if (ep)
skip = FALSE; /* found newline; next line is normal */
} else {
if (!ep) { /* newline missing */
if (strlen(inbuf) < (inbufsz - 2)) {
/* likely the last line of file is just
missing a newline; process it anyway */
ep = eos(inbuf);
} else {
config_error_add("Line too long, skipping");
skip = TRUE; /* discard next fgets */
}
} else {
*ep = '\0'; /* remove newline */
}
if (ep) {
char *tmpbuf = (char *) 0;
int len;
boolean ignoreline = FALSE;
boolean oldline = FALSE;
/* line continuation (trailing '\') */
morelines = (--ep >= inbuf && *ep == '\\');
if (morelines)
*ep = '\0';
/* trim off spaces at end of line */
while (ep >= inbuf
&& (*ep == ' ' || *ep == '\t' || *ep == '\r'))
*ep-- = '\0';
if (!config_error_nextline(inbuf)) {
rv = FALSE;
if (buf)
free(buf), buf = (char *) 0;
break;
}
ep = inbuf;
while (*ep == ' ' || *ep == '\t')
++ep;
/* ignore empty lines and full-line comment lines */
if (!*ep || *ep == '#')
ignoreline = TRUE;
if (buf)
oldline = TRUE;
/* merge now read line with previous ones, if necessary */
if (!ignoreline) {
len = (int) strlen(ep) + 1; /* +1: final '\0' */
if (buf)
len += (int) strlen(buf) + 1; /* +1: space */
tmpbuf = (char *) alloc(len);
*tmpbuf = '\0';
if (buf) {
Strcat(strcpy(tmpbuf, buf), " ");
free(buf);
}
buf = strcat(tmpbuf, ep);
if (strlen(buf) >= sizeof inbuf)
buf[sizeof inbuf - 1] = '\0';
}
if (morelines || (ignoreline && !oldline))
continue;
if (handle_config_section(buf)) {
free(buf);
buf = (char *) 0;
continue;
}
/* from here onwards, we'll handle buf only */
if (match_varname(buf, "CHOOSE", 6)) {
char *section;
char *bufp = find_optparam(buf);
if (!bufp) {
config_error_add(
"Format is CHOOSE=section1,section2,...");
rv = FALSE;
free(buf);
buf = (char *) 0;
continue;
}
bufp++;
if (g.config_section_chosen)
free(g.config_section_chosen),
g.config_section_chosen = 0;
section = choose_random_part(bufp, ',');
if (section) {
g.config_section_chosen = dupstr(section);
} else {
config_error_add("No config section to choose");
rv = FALSE;
}
free(buf);
buf = (char *) 0;
continue;
}
if (!(*proc)(buf))
rv = FALSE;
free(buf);
buf = (char *) 0;
}
}
while (fgets(parser.inbuf, parser.inbufsz, fp)) {
parse_conf_buf(&parser, proc);
if (parser.pbreak)
break;
}
if (buf)
free(buf);
if (parser.buf)
free(parser.buf);
free_config_sections();
return rv;
return parser.rv;
}
extern const char *known_handling[]; /* drawing.c */

View File

@@ -25,6 +25,7 @@ static int nhl_setmap(lua_State *);
#endif
static int nhl_pline(lua_State *);
static int nhl_verbalize(lua_State *);
static int nhl_parse_config(lua_State *);
static int nhl_menu(lua_State *);
static int nhl_getlin(lua_State *);
static int nhl_makeplural(lua_State *);
@@ -412,6 +413,35 @@ nhl_verbalize(lua_State *L)
return 0;
}
/* parse_config("OPTIONS=!color") */
static int
nhl_parse_config(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 1)
parse_conf_str(luaL_checkstring(L, 1), parse_config_line);
else
nhl_error(L, "Wrong args");
return 0;
}
/* local windowtype = get_config("windowtype"); */
static int
nhl_get_config(lua_State *L)
{
int argc = lua_gettop(L);
if (argc == 1) {
lua_pushstring(L, get_option_value(luaL_checkstring(L, 1)));
return 1;
} else
nhl_error(L, "Wrong args");
return 0;
}
/*
str = getlin("What do you want to call this dungeon level?");
*/
@@ -798,6 +828,8 @@ static const struct luaL_Reg nhl_functions[] = {
{"rn2", nhl_rn2},
{"random", nhl_random},
{"level_difficulty", nhl_level_difficulty},
{"parse_config", nhl_parse_config},
{"get_config", nhl_get_config},
{NULL, NULL}
};

View File

@@ -7102,6 +7102,34 @@ optfn_o_status_hilites(int optidx UNUSED, int req, boolean negated UNUSED,
}
#endif /*STATUS_HILITES*/
/* Get string value of configuration option.
* Currently handles only boolean and compound options.
*/
char *
get_option_value(const char *optname)
{
static char retbuf[BUFSZ];
boolean *bool_p;
int i;
for (i = 0; allopt[i].name != 0; i++)
if (!strcmp(optname, allopt[i].name)) {
if (allopt[i].opttyp == BoolOpt && (bool_p = allopt[i].addr) != 0) {
Sprintf(retbuf, "%s", *bool_p ? "true" : "false");
return retbuf;
} else if (allopt[i].opttyp == CompOpt && allopt[i].optfn) {
int reslt = optn_err;
reslt = (*allopt[i].optfn)(allopt[i].idx, get_val,
FALSE, retbuf, empty_optstr);
if (reslt == optn_ok && retbuf[0])
return retbuf;
return (char *) 0;
}
}
return (char *) 0;
}
/* the 'O' command */
int
doset(void) /* changing options via menu by Per Liboriussen */