From: Pasi Kallinen Date: Sat, 6 Feb 2021 16:57:41 +0000 (+0200) Subject: Lua: set and get config options X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d817564a6a2426e0dc3ed0958a5c85d2f0b27be3;p=nethack Lua: set and get config options Still needs more work, especially the error handling. --- diff --git a/doc/lua.adoc b/doc/lua.adoc index b19186003..fe650d45a 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -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. diff --git a/include/extern.h b/include/extern.h index 290eedf7e..a6302da2c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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); diff --git a/src/files.c b/src/files.c index 771f6d74f..d094ca6d4 100644 --- a/src/files.c +++ b/src/files.c @@ -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 boolean -parse_conf_file(FILE *fp, boolean (*proc)(char *)) +static void +parse_conf_buf(struct _cnf_parser_state *p, 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; - - 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 */ - } + 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 { - *ep = '\0'; /* remove newline */ + 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; } - 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'; + 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 (morelines || (ignoreline && !oldline)) - continue; + if (p->morelines || (ignoreline && !oldline)) + return; - if (handle_config_section(buf)) { - free(buf); - buf = (char *) 0; - continue; - } + if (handle_config_section(p->buf)) { + free(p->buf); + p->buf = (char *) 0; + return; + } - /* from here onwards, we'll handle buf only */ + /* from here onwards, we'll handle buf only */ - if (match_varname(buf, "CHOOSE", 6)) { - char *section; - char *bufp = find_optparam(buf); + 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,..."); - 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 (!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)(buf)) - rv = FALSE; + if (!(*proc)(p->buf)) + p->rv = FALSE; - free(buf); - buf = (char *) 0; - } + free(p->buf); + p->buf = (char *) 0; } } +} - if (buf) - free(buf); +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(); - return rv; + 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 *)) +{ + struct _cnf_parser_state parser; + + cnf_parser_init(&parser); + + free_config_sections(); + + while (fgets(parser.inbuf, parser.inbufsz, fp)) { + parse_conf_buf(&parser, proc); + if (parser.pbreak) + break; + } + + if (parser.buf) + free(parser.buf); + + free_config_sections(); + return parser.rv; } extern const char *known_handling[]; /* drawing.c */ diff --git a/src/nhlua.c b/src/nhlua.c index 99165b1a5..1e25fc7f4 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -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} }; diff --git a/src/options.c b/src/options.c index bd27dd166..3698929b7 100644 --- a/src/options.c +++ b/src/options.c @@ -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 */