config: drop dependency on libsbuf

Use an std::stringstream instead.  get_word() and get_quoted_word() both
return a buffer that's presumed to not need release, so solve this by
returning a new special configword type that holds a string or eof/eol
state.  This cleans up caller checking for EOF/EOL to make it more
explicit what they're doing, at least in the EOL cases which previously
checked for NULL.

Sponsored by:	Klara, Inc.
Sponsored by:	NetApp, Inc.
Differential Revision:	https://reviews.freebsd.org/D38276
This commit is contained in:
Kyle Evans 2023-02-08 22:56:10 -06:00
parent ee84487120
commit 83d7ed8af3
5 changed files with 152 additions and 107 deletions

View File

@ -5,8 +5,8 @@ SRCDIR:=${.PARSEDIR:tA}
PROG_CXX= config PROG_CXX= config
MAN= config.5 config.8 MAN= config.5 config.8
SRCS= config.y main.c lang.l mkmakefile.cc mkheaders.c \ SRCS= config.y main.cc lang.l mkmakefile.cc mkheaders.c \
mkoptions.c y.tab.h kernconf.c mkoptions.cc y.tab.h kernconf.c
FILE2C?=file2c FILE2C?=file2c
@ -18,8 +18,6 @@ CFLAGS+= -I. -I${SRCDIR}
NO_WMISSING_VARIABLE_DECLARATIONS= NO_WMISSING_VARIABLE_DECLARATIONS=
LIBADD= sbuf
CLEANFILES+= kernconf.c CLEANFILES+= kernconf.c
mkmakefile.o: configvers.h mkmakefile.o: configvers.h

View File

@ -35,13 +35,66 @@
/* /*
* Config. * Config.
*/ */
#include <sys/cdefs.h> /* __BEGIN_DECLS/__END_DECLS */
#include <sys/types.h> #include <sys/types.h>
#include <sys/queue.h> #include <sys/queue.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifdef __cplusplus
#include <string>
class configword {
private:
std::string cw_word;
bool cw_eof;
bool cw_eol;
public:
configword() : cw_word(""), cw_eof(false), cw_eol(false) {}
configword(std::string &&word) : cw_word(word), cw_eof(false), cw_eol(false) {}
bool eof() const {
return (cw_eof);
}
bool eol() const {
return (cw_eol);
}
configword &eof(bool eof) {
cw_eof = eof;
return (*this);
}
configword &eol(bool eol) {
cw_eol = eol;
return (*this);
}
char operator[](int idx) {
return (cw_word[idx]);
}
operator const char*() const {
return (cw_word.c_str());
}
const std::string &operator*() const {
return (cw_word);
}
const std::string *operator->() const {
return (&cw_word);
}
};
/*
* Is it ugly to limit these to C++ files? Yes.
*/
configword get_word(FILE *);
configword get_quoted_word(FILE *);
#endif
__BEGIN_DECLS __BEGIN_DECLS
struct cfgfile { struct cfgfile {
@ -186,8 +239,6 @@ extern char kernconfstr[];
extern int do_trace; extern int do_trace;
extern int incignore; extern int incignore;
char *get_word(FILE *);
char *get_quoted_word(FILE *);
char *path(const char *); char *path(const char *);
char *raisestr(char *); char *raisestr(char *);
void remember(const char *); void remember(const char *);

View File

@ -45,19 +45,21 @@ static const char rcsid[] =
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/sbuf.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/param.h> #include <sys/param.h>
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h>
#include <err.h> #include <err.h>
#include <iostream>
#include <sstream>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sysexits.h> #include <sysexits.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h>
#include "y.tab.h" #include "y.tab.h"
#include "config.h" #include "config.h"
#include "configvers.h" #include "configvers.h"
@ -111,7 +113,7 @@ struct hdr_list {
struct hdr_list *h_next; struct hdr_list *h_next;
} *htab; } *htab;
static struct sbuf *line_buf = NULL; static std::stringstream line_buf;
/* /*
* Config builds a set of files for building a UNIX * Config builds a set of files for building a UNIX
@ -313,24 +315,21 @@ usage(void)
static void static void
init_line_buf(void) init_line_buf(void)
{ {
if (line_buf == NULL) {
line_buf = sbuf_new(NULL, NULL, 80, SBUF_AUTOEXTEND); line_buf.str("");
if (line_buf == NULL) {
errx(EXIT_FAILURE, "failed to allocate line buffer");
}
} else {
sbuf_clear(line_buf);
}
} }
static char * static std::string
get_line_buf(void) get_line_buf(void)
{ {
if (sbuf_finish(line_buf) != 0) {
line_buf.flush();
if (!line_buf.good()) {
errx(EXIT_FAILURE, "failed to generate line buffer, " errx(EXIT_FAILURE, "failed to generate line buffer, "
"partial line = %s", sbuf_data(line_buf)); "partial line = %s", line_buf.str().c_str());
} }
return sbuf_data(line_buf);
return line_buf.str();
} }
/* /*
@ -339,7 +338,7 @@ get_line_buf(void)
* NULL on end of line * NULL on end of line
* pointer to the word otherwise * pointer to the word otherwise
*/ */
char * configword
get_word(FILE *fp) get_word(FILE *fp)
{ {
int ch; int ch;
@ -351,7 +350,7 @@ get_word(FILE *fp)
if (ch != ' ' && ch != '\t') if (ch != ' ' && ch != '\t')
break; break;
if (ch == EOF) if (ch == EOF)
return ((char *)EOF); return (configword().eof(true));
if (ch == '\\'){ if (ch == '\\'){
escaped_nl = 1; escaped_nl = 1;
goto begin; goto begin;
@ -362,22 +361,22 @@ get_word(FILE *fp)
goto begin; goto begin;
} }
else else
return (NULL); return (configword().eol(true));
} }
sbuf_putc(line_buf, ch); line_buf << (char)ch;
/* Negation operator is a word by itself. */ /* Negation operator is a word by itself. */
if (ch == '!') { if (ch == '!') {
return get_line_buf(); return (configword(get_line_buf()));
} }
while ((ch = getc(fp)) != EOF) { while ((ch = getc(fp)) != EOF) {
if (isspace(ch)) if (isspace(ch))
break; break;
sbuf_putc(line_buf, ch); line_buf << (char)ch;
} }
if (ch == EOF) if (ch == EOF)
return ((char *)EOF); return (configword().eof(true));
(void) ungetc(ch, fp); (void) ungetc(ch, fp);
return (get_line_buf()); return (configword(get_line_buf()));
} }
/* /*
@ -385,7 +384,7 @@ get_word(FILE *fp)
* like get_word but will accept something in double or single quotes * like get_word but will accept something in double or single quotes
* (to allow embedded spaces). * (to allow embedded spaces).
*/ */
char * configword
get_quoted_word(FILE *fp) get_quoted_word(FILE *fp)
{ {
int ch; int ch;
@ -397,7 +396,7 @@ get_quoted_word(FILE *fp)
if (ch != ' ' && ch != '\t') if (ch != ' ' && ch != '\t')
break; break;
if (ch == EOF) if (ch == EOF)
return ((char *)EOF); return (configword().eof(true));
if (ch == '\\'){ if (ch == '\\'){
escaped_nl = 1; escaped_nl = 1;
goto begin; goto begin;
@ -408,7 +407,7 @@ get_quoted_word(FILE *fp)
goto begin; goto begin;
} }
else else
return (NULL); return (configword().eol(true));
} }
if (ch == '"' || ch == '\'') { if (ch == '"' || ch == '\'') {
int quote = ch; int quote = ch;
@ -419,7 +418,7 @@ get_quoted_word(FILE *fp)
break; break;
if (ch == '\n' && !escaped_nl) { if (ch == '\n' && !escaped_nl) {
printf("config: missing quote reading `%s'\n", printf("config: missing quote reading `%s'\n",
get_line_buf()); get_line_buf().c_str());
exit(2); exit(2);
} }
if (ch == '\\' && !escaped_nl) { if (ch == '\\' && !escaped_nl) {
@ -427,23 +426,23 @@ get_quoted_word(FILE *fp)
continue; continue;
} }
if (ch != quote && escaped_nl) if (ch != quote && escaped_nl)
sbuf_putc(line_buf, '\\'); line_buf << "\\";
sbuf_putc(line_buf, ch); line_buf << (char)ch;
escaped_nl = 0; escaped_nl = 0;
} }
} else { } else {
sbuf_putc(line_buf, ch); line_buf << (char)ch;
while ((ch = getc(fp)) != EOF) { while ((ch = getc(fp)) != EOF) {
if (isspace(ch)) if (isspace(ch))
break; break;
sbuf_putc(line_buf, ch); line_buf << (char)ch;
} }
if (ch != EOF) if (ch != EOF)
(void) ungetc(ch, fp); (void) ungetc(ch, fp);
} }
if (ch == EOF) if (ch == EOF)
return ((char *)EOF); return (configword().eof(true));
return (get_line_buf()); return (configword(get_line_buf()));
} }
/* /*
@ -466,7 +465,7 @@ path(const char *file)
* will be able to obtain and build conifguration file with one command. * will be able to obtain and build conifguration file with one command.
*/ */
static void static void
configfile_dynamic(struct sbuf *sb) configfile_dynamic(std::ostringstream &cfg)
{ {
struct cputype *cput; struct cputype *cput;
struct device *d; struct device *d;
@ -476,38 +475,35 @@ configfile_dynamic(struct sbuf *sb)
asprintf(&lend, "\\n\\\n"); asprintf(&lend, "\\n\\\n");
assert(lend != NULL); assert(lend != NULL);
sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend); cfg << "options\t" << OPT_AUTOGEN << lend;
sbuf_printf(sb, "ident\t%s%s", ident, lend); cfg << "ident\t" << ident << lend;
sbuf_printf(sb, "machine\t%s%s", machinename, lend); cfg << "machine\t" << machinename << lend;
SLIST_FOREACH(cput, &cputype, cpu_next) SLIST_FOREACH(cput, &cputype, cpu_next)
sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend); cfg << "cpu\t" << cput->cpu_name << lend;
SLIST_FOREACH(ol, &mkopt, op_next) SLIST_FOREACH(ol, &mkopt, op_next)
sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name, cfg << "makeoptions\t" << ol->op_name << '=' <<
ol->op_value, lend); ol->op_value << lend;
SLIST_FOREACH(ol, &opt, op_next) { SLIST_FOREACH(ol, &opt, op_next) {
if (strncmp(ol->op_name, "DEV_", 4) == 0) if (strncmp(ol->op_name, "DEV_", 4) == 0)
continue; continue;
sbuf_printf(sb, "options\t%s", ol->op_name); cfg << "options\t" << ol->op_name;
if (ol->op_value != NULL) { if (ol->op_value != NULL) {
sbuf_putc(sb, '='); cfg << '=';
for (i = 0; i < strlen(ol->op_value); i++) { for (i = 0; i < strlen(ol->op_value); i++) {
if (ol->op_value[i] == '"') if (ol->op_value[i] == '"')
sbuf_printf(sb, "\\%c", cfg << '\\' << ol->op_value[i];
ol->op_value[i]);
else else
sbuf_printf(sb, "%c", cfg << ol->op_value[i];
ol->op_value[i]);
} }
sbuf_printf(sb, "%s", lend);
} else {
sbuf_printf(sb, "%s", lend);
} }
cfg << lend;
} }
/* /*
* Mark this file as containing everything we need. * Mark this file as containing everything we need.
*/ */
STAILQ_FOREACH(d, &dtab, d_next) STAILQ_FOREACH(d, &dtab, d_next)
sbuf_printf(sb, "device\t%s%s", d->d_name, lend); cfg << "device\t" << d->d_name << lend;
free(lend); free(lend);
} }
@ -515,7 +511,7 @@ configfile_dynamic(struct sbuf *sb)
* Generate file from the configuration files. * Generate file from the configuration files.
*/ */
static void static void
configfile_filebased(struct sbuf *sb) configfile_filebased(std::ostringstream &cfg)
{ {
FILE *cff; FILE *cff;
struct cfgfile *cf; struct cfgfile *cf;
@ -534,11 +530,11 @@ configfile_filebased(struct sbuf *sb)
} }
while ((i = getc(cff)) != EOF) { while ((i = getc(cff)) != EOF) {
if (i == '\n') if (i == '\n')
sbuf_printf(sb, "\\n\\\n"); cfg << "\\n\\\n";
else if (i == '"' || i == '\'') else if (i == '"' || i == '\'')
sbuf_printf(sb, "\\%c", i); cfg << '\\' << i;
else else
sbuf_putc(sb, i); cfg << i;
} }
fclose(cff); fclose(cff);
} }
@ -548,7 +544,7 @@ static void
configfile(void) configfile(void)
{ {
FILE *fo; FILE *fo;
struct sbuf *sb; std::ostringstream cfg;
char *p; char *p;
/* Add main configuration file to the list of files to be included */ /* Add main configuration file to the list of files to be included */
@ -557,16 +553,14 @@ configfile(void)
fo = fopen(p, "w"); fo = fopen(p, "w");
if (!fo) if (!fo)
err(2, "%s", p); err(2, "%s", p);
sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND);
assert(sb != NULL);
sbuf_clear(sb);
if (filebased) { if (filebased) {
/* Is needed, can be used for backward compatibility. */ /* Is needed, can be used for backward compatibility. */
configfile_filebased(sb); configfile_filebased(cfg);
} else { } else {
configfile_dynamic(sb); configfile_dynamic(cfg);
} }
sbuf_finish(sb);
cfg.flush();
/* /*
* We print first part of the template, replace our tag with * We print first part of the template, replace our tag with
* configuration files content and later continue writing our * configuration files content and later continue writing our
@ -577,10 +571,9 @@ configfile(void)
errx(EXIT_FAILURE, "Something went terribly wrong!"); errx(EXIT_FAILURE, "Something went terribly wrong!");
*p = '\0'; *p = '\0';
fprintf(fo, "%s", kernconfstr); fprintf(fo, "%s", kernconfstr);
fprintf(fo, "%s", sbuf_data(sb)); fprintf(fo, "%s", cfg.str().c_str());
p += strlen(KERNCONFTAG); p += strlen(KERNCONFTAG);
fprintf(fo, "%s", p); fprintf(fo, "%s", p);
sbuf_delete(sb);
fclose(fo); fclose(fo);
moveifchanged(path("config.c.new"), path("config.c")); moveifchanged(path("config.c.new"), path("config.c"));
cfgfile_removeall(); cfgfile_removeall();

View File

@ -380,7 +380,8 @@ read_file(char *fname)
struct file_list *tp; struct file_list *tp;
struct device *dp; struct device *dp;
struct opt *op; struct opt *op;
char *wd, *rfile, *compilewith, *depends, *clean, *warning; configword wd;
char *rfile, *compilewith, *depends, *clean, *warning;
const char *objprefix; const char *objprefix;
int compile, match, nreqs, std, filetype, negate, int compile, match, nreqs, std, filetype, negate,
imp_rule, no_ctfconvert, no_obj, before_depend, nowerror; imp_rule, no_ctfconvert, no_obj, before_depend, nowerror;
@ -400,33 +401,34 @@ next:
* [ nowerror ] [ local ] * [ nowerror ] [ local ]
*/ */
wd = get_word(fp); wd = get_word(fp);
if (wd == (char *)EOF) { if (wd.eof()) {
(void) fclose(fp); (void) fclose(fp);
return; return;
} }
if (wd == NULL) if (wd.eol())
goto next; goto next;
if (wd[0] == '#') if (wd[0] == '#')
{ {
while (((wd = get_word(fp)) != (char *)EOF) && wd) while (!(wd = get_word(fp)).eof() && !wd.eol())
; ;
goto next; goto next;
} }
if (eq(wd, "include")) { if (eq(wd, "include")) {
wd = get_quoted_word(fp); wd = get_quoted_word(fp);
if (wd == (char *)EOF || wd == NULL) if (wd.eof() || wd.eol())
errout("%s: missing include filename.\n", fname); errout("%s: missing include filename.\n", fname);
(void) snprintf(ifname, sizeof(ifname), "../../%s", wd); (void) snprintf(ifname, sizeof(ifname), "../../%s",
wd->c_str());
read_file(ifname); read_file(ifname);
while (((wd = get_word(fp)) != (char *)EOF) && wd) while (!(wd = get_word(fp)).eof() && !wd.eol())
; ;
goto next; goto next;
} }
rfile = ns(wd); rfile = ns(wd);
wd = get_word(fp); wd = get_word(fp);
if (wd == (char *)EOF) if (wd.eof())
return; return;
if (wd == NULL) if (wd.eol())
errout("%s: No type for %s.\n", fname, rfile); errout("%s: No type for %s.\n", fname, rfile);
tp = fl_lookup(rfile); tp = fl_lookup(rfile);
compile = 0; compile = 0;
@ -449,9 +451,9 @@ next:
std = 1; std = 1;
else if (!eq(wd, "optional")) else if (!eq(wd, "optional"))
errout("%s: \"%s\" %s must be optional or standard\n", errout("%s: \"%s\" %s must be optional or standard\n",
fname, wd, rfile); fname, wd->c_str(), rfile);
for (wd = get_word(fp); wd; wd = get_word(fp)) { for (wd = get_word(fp); !wd.eol(); wd = get_word(fp)) {
if (wd == (char *)EOF) if (wd.eof())
return; return;
if (eq(wd, "!")) { if (eq(wd, "!")) {
negate = 1; negate = 1;
@ -489,7 +491,7 @@ next:
} }
if (eq(wd, "dependency")) { if (eq(wd, "dependency")) {
wd = get_quoted_word(fp); wd = get_quoted_word(fp);
if (wd == (char *)EOF || wd == NULL) if (wd.eof() || wd.eol())
errout("%s: %s missing dependency string.\n", errout("%s: %s missing dependency string.\n",
fname, rfile); fname, rfile);
depends = ns(wd); depends = ns(wd);
@ -497,7 +499,7 @@ next:
} }
if (eq(wd, "clean")) { if (eq(wd, "clean")) {
wd = get_quoted_word(fp); wd = get_quoted_word(fp);
if (wd == (char *)EOF || wd == NULL) if (wd.eof() || wd.eol())
errout("%s: %s missing clean file list.\n", errout("%s: %s missing clean file list.\n",
fname, rfile); fname, rfile);
clean = ns(wd); clean = ns(wd);
@ -505,7 +507,7 @@ next:
} }
if (eq(wd, "compile-with")) { if (eq(wd, "compile-with")) {
wd = get_quoted_word(fp); wd = get_quoted_word(fp);
if (wd == (char *)EOF || wd == NULL) if (wd.eof() || wd.eol())
errout("%s: %s missing compile command string.\n", errout("%s: %s missing compile command string.\n",
fname, rfile); fname, rfile);
compilewith = ns(wd); compilewith = ns(wd);
@ -513,7 +515,7 @@ next:
} }
if (eq(wd, "warning")) { if (eq(wd, "warning")) {
wd = get_quoted_word(fp); wd = get_quoted_word(fp);
if (wd == (char *)EOF || wd == NULL) if (wd.eof() || wd.eol())
errout("%s: %s missing warning text string.\n", errout("%s: %s missing warning text string.\n",
fname, rfile); fname, rfile);
warning = ns(wd); warning = ns(wd);
@ -521,7 +523,7 @@ next:
} }
if (eq(wd, "obj-prefix")) { if (eq(wd, "obj-prefix")) {
wd = get_quoted_word(fp); wd = get_quoted_word(fp);
if (wd == (char *)EOF || wd == NULL) if (wd.eof() || wd.eol())
errout("%s: %s missing object prefix string.\n", errout("%s: %s missing object prefix string.\n",
fname, rfile); fname, rfile);
objprefix = ns(wd); objprefix = ns(wd);
@ -542,7 +544,7 @@ next:
nreqs++; nreqs++;
if (std) if (std)
errout("standard entry %s has optional inclusion specifier %s!\n", errout("standard entry %s has optional inclusion specifier %s!\n",
rfile, wd); rfile, wd->c_str());
STAILQ_FOREACH(dp, &dtab, d_next) STAILQ_FOREACH(dp, &dtab, d_next)
if (eq(dp->d_name, wd)) { if (eq(dp->d_name, wd)) {
if (negate) if (negate)

View File

@ -141,7 +141,7 @@ options(void)
static void static void
do_option(char *name) do_option(char *name)
{ {
char *file, *inw; char *file;
const char *basefile; const char *basefile;
struct opt_list *ol; struct opt_list *ol;
struct opt *op; struct opt *op;
@ -198,18 +198,17 @@ do_option(char *name)
seen = 0; seen = 0;
tidy = 0; tidy = 0;
for (;;) { for (;;) {
char *cp; configword cp, inw;
char *invalue; char *invalue;
/* get the #define */ /* get the #define */
if ((inw = get_word(inf)) == NULL || inw == (char *)EOF) if ((inw = get_word(inf)).eol() || inw.eof())
break; break;
/* get the option name */ /* get the option name */
if ((inw = get_word(inf)) == NULL || inw == (char *)EOF) if ((inw = get_word(inf)).eol() || inw.eof())
break; break;
inw = ns(inw);
/* get the option value */ /* get the option value */
if ((cp = get_word(inf)) == NULL || cp == (char *)EOF) if ((cp = get_word(inf)).eol() || cp.eof())
break; break;
/* option value */ /* option value */
invalue = ns(cp); /* malloced */ invalue = ns(cp); /* malloced */
@ -224,25 +223,25 @@ do_option(char *name)
if (!eq(inw, name) && !ol) { if (!eq(inw, name) && !ol) {
fprintf(stderr, fprintf(stderr,
"WARNING: unknown option `%s' removed from %s\n", "WARNING: unknown option `%s' removed from %s\n",
inw, file); inw->c_str(), file);
tidy++; tidy++;
} else if (ol != NULL && !eq(basefile, ol->o_file)) { } else if (ol != NULL && !eq(basefile, ol->o_file)) {
fprintf(stderr, fprintf(stderr,
"WARNING: option `%s' moved from %s to %s\n", "WARNING: option `%s' moved from %s to %s\n",
inw, basefile, ol->o_file); inw->c_str(), basefile, ol->o_file);
tidy++; tidy++;
} else { } else {
op = (struct opt *) calloc(1, sizeof *op); op = (struct opt *) calloc(1, sizeof *op);
if (op == NULL) if (op == NULL)
err(EXIT_FAILURE, "calloc"); err(EXIT_FAILURE, "calloc");
op->op_name = inw; op->op_name = ns(inw);
op->op_value = invalue; op->op_value = invalue;
SLIST_INSERT_HEAD(&op_head, op, op_next); SLIST_INSERT_HEAD(&op_head, op, op_next);
} }
/* EOL? */ /* EOL? */
cp = get_word(inf); cp = get_word(inf);
if (cp == (char *)EOF) if (cp.eof())
break; break;
} }
(void)fclose(inf); (void)fclose(inf);
@ -364,25 +363,26 @@ static int
read_option_file(const char *fname, int flags) read_option_file(const char *fname, int flags)
{ {
FILE *fp; FILE *fp;
char *wd, *optname, *val; configword wd;
char *optname, *val;
char genopt[MAXPATHLEN]; char genopt[MAXPATHLEN];
fp = fopen(fname, "r"); fp = fopen(fname, "r");
if (fp == NULL) if (fp == NULL)
return (0); return (0);
while ((wd = get_word(fp)) != (char *)EOF) { while (!(wd = get_word(fp)).eof()) {
if (wd == NULL) if (wd.eol())
continue; continue;
if (wd[0] == '#') { if (wd[0] == '#') {
while (((wd = get_word(fp)) != (char *)EOF) && wd) while (!(wd = get_word(fp)).eof() && !wd.eol())
continue; continue;
continue; continue;
} }
optname = ns(wd); optname = ns(wd);
val = get_word(fp); wd = get_word(fp);
if (val == (char *)EOF) if (wd.eof())
return (1); return (1);
if (val == NULL) { if (wd.eol()) {
if (flags) { if (flags) {
fprintf(stderr, "%s: compat file requires two" fprintf(stderr, "%s: compat file requires two"
" words per line at %s\n", fname, optname); " words per line at %s\n", fname, optname);
@ -391,10 +391,11 @@ read_option_file(const char *fname, int flags)
char *s = ns(optname); char *s = ns(optname);
(void)snprintf(genopt, sizeof(genopt), "opt_%s.h", (void)snprintf(genopt, sizeof(genopt), "opt_%s.h",
lower(s)); lower(s));
val = genopt; val = ns(genopt);
free(s); free(s);
} else {
val = ns(wd);
} }
val = ns(val);
if (flags == 0) if (flags == 0)
insert_option(fname, optname, val); insert_option(fname, optname, val);
else else