#include <ctype.h>
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
#include "config.h"
#include "y.tab.h"
static struct users {
int u_default;
int u_min;
int u_max;
} users = { 8, 2, 512 };
static char *lower(char *);
static void read_options(void);
static void do_option(char *);
static char *tooption(char *);
void
options(void)
{
char buf[40];
struct cputype *cp;
struct opt_list *ol;
struct opt *op;
SLIST_FOREACH(cp, &cputype, cpu_next) {
op = (struct opt *)calloc(1, sizeof(*op));
if (op == NULL)
err(EXIT_FAILURE, "calloc");
op->op_name = ns(cp->cpu_name);
SLIST_INSERT_HEAD(&opt, op, op_next);
}
if (maxusers == 0) {
if (verbose)
fprintf(stderr,
"maxusers not specified; will auto-size\n");
} else if (maxusers < users.u_min) {
fprintf(stderr, "minimum of %d maxusers assumed\n",
users.u_min);
maxusers = users.u_min;
} else if (maxusers > users.u_max)
fprintf(stderr, "warning: maxusers > %d (%d)\n",
users.u_max, maxusers);
op = (struct opt *)calloc(1, sizeof(*op));
if (op == NULL)
err(EXIT_FAILURE, "calloc");
op->op_name = ns("MAXUSERS");
snprintf(buf, sizeof(buf), "%d", maxusers);
op->op_value = ns(buf);
SLIST_INSERT_HEAD(&opt, op, op_next);
read_options();
SLIST_FOREACH(ol, &otab, o_next) {
if (strcasecmp(ol->o_name, machinearch) != 0)
continue;
op = (struct opt *)calloc(1, sizeof(*op));
if (op == NULL)
err(EXIT_FAILURE, "calloc");
op->op_name = ns(ol->o_name);
SLIST_INSERT_HEAD(&opt, op, op_next);
break;
}
SLIST_FOREACH(op, &opt, op_next) {
SLIST_FOREACH(ol, &otab, o_next) {
if (eq(op->op_name, ol->o_name) &&
(ol->o_flags & OL_ALIAS)) {
fprintf(stderr, "Mapping option %s to %s.\n",
op->op_name, ol->o_file);
op->op_name = ol->o_file;
break;
}
}
}
SLIST_FOREACH(ol, &otab, o_next)
do_option(ol->o_name);
SLIST_FOREACH(op, &opt, op_next) {
if (!op->op_ownfile && strncmp(op->op_name, "DEV_", 4)) {
fprintf(stderr, "%s: unknown option \"%s\"\n",
PREFIX, op->op_name);
exit(1);
}
}
}
static void
do_option(char *name)
{
char *file;
const char *basefile;
struct opt_list *ol;
struct opt *op;
struct opt_head op_head;
FILE *inf, *outf;
char *value;
char *oldvalue;
int seen;
int tidy;
file = tooption(name);
value = NULL;
SLIST_FOREACH(op, &opt, op_next) {
if (eq(name, op->op_name)) {
oldvalue = value;
value = op->op_value;
if (value == NULL)
value = ns("1");
if (oldvalue != NULL && !eq(value, oldvalue))
fprintf(stderr,
"%s: option \"%s\" redefined from %s to %s\n",
PREFIX, op->op_name, oldvalue,
value);
op->op_ownfile++;
}
}
remember(file);
inf = fopen(file, "r");
if (inf == NULL) {
outf = fopen(file, "w");
if (outf == NULL)
err(1, "%s", file);
if (value) {
fprintf(outf, "#define %s %s\n", name, value);
}
(void)fclose(outf);
return;
}
basefile = "";
SLIST_FOREACH(ol, &otab, o_next)
if (eq(name, ol->o_name)) {
basefile = ol->o_file;
break;
}
oldvalue = NULL;
SLIST_INIT(&op_head);
seen = 0;
tidy = 0;
for (;;) {
configword cp, inw;
char *invalue;
if ((inw = get_word(inf)).eol() || inw.eof())
break;
if ((inw = get_word(inf)).eol() || inw.eof())
break;
if ((cp = get_word(inf)).eol() || cp.eof())
break;
invalue = ns(cp);
if (eq(inw, name)) {
oldvalue = invalue;
invalue = value;
seen++;
}
SLIST_FOREACH(ol, &otab, o_next)
if (eq(inw, ol->o_name))
break;
if (!eq(inw, name) && !ol) {
fprintf(stderr,
"WARNING: unknown option `%s' removed from %s\n",
inw->c_str(), file);
tidy++;
} else if (ol != NULL && !eq(basefile, ol->o_file)) {
fprintf(stderr,
"WARNING: option `%s' moved from %s to %s\n",
inw->c_str(), basefile, ol->o_file);
tidy++;
} else {
op = (struct opt *) calloc(1, sizeof *op);
if (op == NULL)
err(EXIT_FAILURE, "calloc");
op->op_name = ns(inw);
op->op_value = invalue;
SLIST_INSERT_HEAD(&op_head, op, op_next);
}
cp = get_word(inf);
if (cp.eof())
break;
}
(void)fclose(inf);
if (!tidy && ((value == NULL && oldvalue == NULL) ||
(value && oldvalue && eq(value, oldvalue)))) {
while (!SLIST_EMPTY(&op_head)) {
op = SLIST_FIRST(&op_head);
SLIST_REMOVE_HEAD(&op_head, op_next);
free(op->op_name);
free(op->op_value);
free(op);
}
return;
}
if (value && !seen) {
op = (struct opt *) calloc(1, sizeof *op);
if (op == NULL)
err(EXIT_FAILURE, "calloc");
op->op_name = ns(name);
op->op_value = ns(value);
SLIST_INSERT_HEAD(&op_head, op, op_next);
}
outf = fopen(file, "w");
if (outf == NULL)
err(1, "%s", file);
while (!SLIST_EMPTY(&op_head)) {
op = SLIST_FIRST(&op_head);
if (op->op_value) {
fprintf(outf, "#define %s %s\n",
op->op_name, op->op_value);
}
SLIST_REMOVE_HEAD(&op_head, op_next);
free(op->op_name);
free(op->op_value);
free(op);
}
(void)fclose(outf);
}
static char *
tooption(char *name)
{
static char hbuf[MAXPATHLEN];
char nbuf[MAXPATHLEN];
struct opt_list *po;
char *fpath;
(void)strlcpy(nbuf, "options.h", sizeof(nbuf));
SLIST_FOREACH(po, &otab, o_next) {
if (eq(po->o_name, name)) {
strlcpy(nbuf, po->o_file, sizeof(nbuf));
break;
}
}
fpath = path(nbuf);
(void)strlcpy(hbuf, fpath, sizeof(hbuf));
free(fpath);
return (hbuf);
}
static void
check_duplicate(const char *fname, const char *chkopt)
{
struct opt_list *po;
SLIST_FOREACH(po, &otab, o_next) {
if (eq(po->o_name, chkopt)) {
fprintf(stderr, "%s: Duplicate option %s.\n",
fname, chkopt);
exit(1);
}
}
}
static void
insert_option(const char *fname, char *optname, char *val)
{
struct opt_list *po;
check_duplicate(fname, optname);
po = (struct opt_list *) calloc(1, sizeof *po);
if (po == NULL)
err(EXIT_FAILURE, "calloc");
po->o_name = optname;
po->o_file = val;
po->o_flags = 0;
SLIST_INSERT_HEAD(&otab, po, o_next);
}
static void
update_option(const char *optname, char *val, int flags)
{
struct opt_list *po;
SLIST_FOREACH(po, &otab, o_next) {
if (eq(po->o_name, optname)) {
free(po->o_file);
po->o_file = val;
po->o_flags = flags;
return;
}
}
return;
}
static int
read_option_file(const char *fname, int flags)
{
FILE *fp;
configword wd;
char *optname, *val;
char genopt[MAXPATHLEN];
fp = fopen(fname, "r");
if (fp == NULL) {
if (verbose) {
getcwd(genopt, sizeof(genopt));
fprintf(stderr, "Unable to open options file: %s\n",
fname);
fprintf(stderr, "CWD: %s\n", genopt);
}
return (0);
}
while (!(wd = get_word(fp)).eof()) {
if (wd.eol())
continue;
if (wd[0] == '#') {
while (!(wd = get_word(fp)).eof() && !wd.eol())
continue;
continue;
}
optname = ns(wd);
wd = get_word(fp);
if (wd.eof()) {
free(optname);
break;
}
if (wd.eol()) {
if (flags) {
fprintf(stderr, "%s: compat file requires two"
" words per line at %s\n", fname, optname);
exit(1);
}
char *s = ns(optname);
(void)snprintf(genopt, sizeof(genopt), "opt_%s.h",
lower(s));
val = ns(genopt);
free(s);
} else {
val = ns(wd);
}
if (flags == 0) {
insert_option(fname, optname, val);
} else {
update_option(optname, val, flags);
free(optname);
optname = NULL;
}
}
(void)fclose(fp);
return (1);
}
static void
read_options(void)
{
char fname[MAXPATHLEN];
struct files_name *nl, *tnl;
SLIST_INIT(&otab);
read_option_file("../../conf/options", 0);
(void)snprintf(fname, sizeof fname, "../../conf/options.%s",
machinename);
if (!read_option_file(fname, 0)) {
(void)snprintf(fname, sizeof fname, "options.%s", machinename);
read_option_file(fname, 0);
}
for (nl = STAILQ_FIRST(&optfntab); nl != NULL; nl = tnl) {
read_option_file(nl->f_name, 0);
tnl = STAILQ_NEXT(nl, f_next);
free(nl->f_name);
free(nl);
}
read_option_file("../../conf/options-compat", OL_ALIAS);
}
static char *
lower(char *str)
{
char *cp = str;
while (*str) {
if (isupper(*str))
*str = tolower(*str);
str++;
}
return (cp);
}