mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-19 05:07:56 +00:00
Re-organise new config code some more
Split into several source .c files, leave throw-away code in config_test.c.
This commit is contained in:
parent
32debf57ef
commit
2853d346f1
427
conf_om.c
Normal file
427
conf_om.c
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
/*
|
||||||
|
Serval DNA configuration
|
||||||
|
Copyright (C) 2012 Serval Project Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
#include "strbuf_helpers.h"
|
||||||
|
|
||||||
|
#define _DEBUGF(F,...) fprintf(stderr, "DEBUG: " F "\n", ##__VA_ARGS__)
|
||||||
|
#define _WARNF(F,...) fprintf(stderr, "WARN: " F "\n", ##__VA_ARGS__)
|
||||||
|
#define _WHYF(F,...) fprintf(stderr, "ERROR: " F "\n", ##__VA_ARGS__)
|
||||||
|
#define _WHYF_perror(F,...) fprintf(stderr, "ERROR: " F ": %s [errno=%d]\n", ##__VA_ARGS__, strerror(errno), errno)
|
||||||
|
#define DEBUGF(F,...) _DEBUGF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define WARNF(F,...) _WARNF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define WHYF(F,...) _WHYF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define WHYF_perror(F,...) _WHYF_perror("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
void *emalloc(size_t len)
|
||||||
|
{
|
||||||
|
char *new = malloc(len + 1);
|
||||||
|
if (!new) {
|
||||||
|
WHYF_perror("malloc(%lu)", (long)len);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strn_emalloc(const char *str, size_t len)
|
||||||
|
{
|
||||||
|
char *new = emalloc(len + 1);
|
||||||
|
if (new) {
|
||||||
|
strncpy(new, str, len);
|
||||||
|
new[len] = '\0';
|
||||||
|
}
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *str_emalloc(const char *str)
|
||||||
|
{
|
||||||
|
return strn_emalloc(str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *cf_find_keyend(const char *const key, const char *const fullkeyend)
|
||||||
|
{
|
||||||
|
const char *s = key;
|
||||||
|
if (s < fullkeyend && (isalpha(*s) || *s == '_'))
|
||||||
|
++s;
|
||||||
|
while (s < fullkeyend && (isalnum(*s) || *s == '_'))
|
||||||
|
++s;
|
||||||
|
if (s == key || (s < fullkeyend && *s != '.'))
|
||||||
|
return NULL;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cf_om_make_child(struct cf_om_node **const parentp, const char *const fullkey, const char *const key, const char *const keyend)
|
||||||
|
{
|
||||||
|
size_t keylen = keyend - key;
|
||||||
|
//DEBUGF("%s key=%s", __FUNCTION__, alloca_toprint(-1, key, keylen));
|
||||||
|
int i = 0;
|
||||||
|
struct cf_om_node *child;
|
||||||
|
if ((*parentp)->nodc) {
|
||||||
|
// Binary search for matching child.
|
||||||
|
int m = 0;
|
||||||
|
int n = (*parentp)->nodc - 1;
|
||||||
|
int c;
|
||||||
|
do {
|
||||||
|
i = (m + n) / 2;
|
||||||
|
child = (*parentp)->nodv[i];
|
||||||
|
c = strncmp(key, child->key, keylen);
|
||||||
|
if (c == 0 && child->key[keylen])
|
||||||
|
c = -1;
|
||||||
|
//DEBUGF(" m=%d n=%d i=%d child->key=%s c=%d", m, n, i, alloca_str_toprint(child->key), c);
|
||||||
|
if (c == 0) {
|
||||||
|
//DEBUGF(" found i=%d", i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
if (c > 0)
|
||||||
|
m = ++i;
|
||||||
|
else
|
||||||
|
n = i - 1;
|
||||||
|
} while (m <= n);
|
||||||
|
}
|
||||||
|
// At this point, i is the index where a new child should be inserted.
|
||||||
|
assert(i >= 0);
|
||||||
|
assert(i <= (*parentp)->nodc);
|
||||||
|
child = emalloc(sizeof *child);
|
||||||
|
if (child == NULL)
|
||||||
|
return -1;
|
||||||
|
memset(child, 0, sizeof *child);
|
||||||
|
++(*parentp)->nodc;
|
||||||
|
if ((*parentp)->nodc > NELS((*parentp)->nodv))
|
||||||
|
*parentp = realloc(*parentp, sizeof(**parentp) + sizeof((*parentp)->nodv[0]) * ((*parentp)->nodc - NELS((*parentp)->nodv)));
|
||||||
|
int j;
|
||||||
|
for (j = (*parentp)->nodc - 1; j > i; --j)
|
||||||
|
(*parentp)->nodv[j] = (*parentp)->nodv[j-1];
|
||||||
|
(*parentp)->nodv[i] = child;
|
||||||
|
if (!(child->fullkey = strn_emalloc(fullkey, keyend - fullkey))) {
|
||||||
|
free(child);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
child->key = child->fullkey + (key - fullkey);
|
||||||
|
//DEBUGF(" insert i=%d", i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_get_child(const struct cf_om_node *parent, const char *key)
|
||||||
|
{
|
||||||
|
// TODO: use binary search, since child nodes are already sorted by key
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < parent->nodc; ++i)
|
||||||
|
if (strcmp(parent->nodv[i]->key, key) == 0)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_free_node(struct cf_om_node *node)
|
||||||
|
{
|
||||||
|
while (node->nodc)
|
||||||
|
cf_free_node(node->nodv[--node->nodc]);
|
||||||
|
if (node->fullkey) {
|
||||||
|
free((char *)node->fullkey);
|
||||||
|
node->fullkey = node->key = NULL;
|
||||||
|
}
|
||||||
|
if (node->text) {
|
||||||
|
free((char *)node->text);
|
||||||
|
node->text = NULL;
|
||||||
|
}
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cf_om_node *cf_parse_to_om(const char *source, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct cf_om_node *root = emalloc(sizeof(struct cf_om_node));
|
||||||
|
if (root == NULL)
|
||||||
|
return NULL;
|
||||||
|
memset(root, 0, sizeof *root);
|
||||||
|
const char *end = buf + len;
|
||||||
|
const char *line = buf;
|
||||||
|
const char *nextline;
|
||||||
|
unsigned lineno = 1;
|
||||||
|
for (lineno = 1; line < end; line = nextline, ++lineno) {
|
||||||
|
const char *lend = line;
|
||||||
|
while (lend < end && *lend != '\n')
|
||||||
|
++lend;
|
||||||
|
nextline = lend + 1;
|
||||||
|
if (lend > line && lend[-1] == '\r')
|
||||||
|
--lend;
|
||||||
|
//DEBUGF("lineno=%u %s", lineno, alloca_toprint(-1, line, lend - line));
|
||||||
|
if (line[0] == '#')
|
||||||
|
continue; // skip comment lines
|
||||||
|
const char *p;
|
||||||
|
for (p = line; p < lend && isspace(*p); ++p)
|
||||||
|
;
|
||||||
|
if (p == lend)
|
||||||
|
continue; // skip empty and blank lines
|
||||||
|
for (p = line; p < lend && *p != '='; ++p)
|
||||||
|
;
|
||||||
|
if (p == line || p == lend) {
|
||||||
|
WARNF("%s:%u: malformed configuration line -- ignored", source, lineno);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct cf_om_node **nodep = &root;
|
||||||
|
const char *fullkey = line;
|
||||||
|
const char *fullkeyend = p;
|
||||||
|
const char *key = fullkey;
|
||||||
|
const char *keyend = NULL;
|
||||||
|
int nodi = -1;
|
||||||
|
while (key <= fullkeyend && (keyend = cf_find_keyend(key, fullkeyend)) && (nodi = cf_om_make_child(nodep, fullkey, key, keyend)) != -1) {
|
||||||
|
key = keyend + 1;
|
||||||
|
nodep = &(*nodep)->nodv[nodi];
|
||||||
|
}
|
||||||
|
if (keyend == NULL) {
|
||||||
|
WARNF("%s:%u: malformed configuration option %s -- ignored",
|
||||||
|
source, lineno, alloca_toprint(-1, fullkey, fullkeyend - fullkey)
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nodi == -1)
|
||||||
|
goto error; // out of memory
|
||||||
|
struct cf_om_node *node = *nodep;
|
||||||
|
if (node->text) {
|
||||||
|
WARNF("%s:%u: duplicate configuration option %s -- ignored (original is at %s:%u)",
|
||||||
|
source, lineno, alloca_toprint(-1, fullkey, fullkeyend - fullkey),
|
||||||
|
node->source, node->line_number
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++p;
|
||||||
|
if (!(node->text = strn_emalloc(p, lend - p)))
|
||||||
|
break; // out of memory
|
||||||
|
node->source = source;
|
||||||
|
node->line_number = lineno;
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
error:
|
||||||
|
cf_free_node(root);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_dump_node(const struct cf_om_node *node, int indent)
|
||||||
|
{
|
||||||
|
if (node == NULL)
|
||||||
|
DEBUGF("%*sNULL", indent * 3, "");
|
||||||
|
else {
|
||||||
|
DEBUGF("%*s%s:%u fullkey=%s key=%s text=%s", indent * 3, "",
|
||||||
|
node->source ? node->source : "NULL",
|
||||||
|
node->line_number,
|
||||||
|
alloca_str_toprint(node->fullkey),
|
||||||
|
alloca_str_toprint(node->key),
|
||||||
|
alloca_str_toprint(node->text)
|
||||||
|
);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < node->nodc; ++i)
|
||||||
|
cf_dump_node(node->nodv[i], indent + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_nodev(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
strbuf b = strbuf_alloca(1024);
|
||||||
|
if (node) {
|
||||||
|
if (node->source && node->line_number)
|
||||||
|
strbuf_sprintf(b, "%s:%u: ", node->source, node->line_number);
|
||||||
|
strbuf_puts(b, "configuration option \"");
|
||||||
|
strbuf_puts(b, node->fullkey);
|
||||||
|
if (key && key[0]) {
|
||||||
|
strbuf_putc(b, '.');
|
||||||
|
strbuf_puts(b, key);
|
||||||
|
}
|
||||||
|
strbuf_puts(b, "\" ");
|
||||||
|
}
|
||||||
|
strbuf_vsprintf(b, fmt, ap);
|
||||||
|
_WARNF("%s:%u %s", file, line, strbuf_str(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_childrenv(const char *file, unsigned line, const struct cf_om_node *parent, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < parent->nodc; ++i) {
|
||||||
|
cf_warn_nodev(file, line, parent->nodv[i], NULL, fmt, ap);
|
||||||
|
cf_warn_childrenv(file, line, parent->nodv[i], fmt, ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_node(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
cf_warn_nodev(file, line, node, key, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_children(const char *file, unsigned line, const struct cf_om_node *node, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
cf_warn_childrenv(file, line, node, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_duplicate_node(const struct cf_om_node *parent, const char *key)
|
||||||
|
{
|
||||||
|
cf_warn_node(__FILE__, __LINE__, parent, key, "is duplicate");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_missing_node(const struct cf_om_node *parent, const char *key)
|
||||||
|
{
|
||||||
|
cf_warn_node(__FILE__, __LINE__, parent, key, "is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_spurious_children(const struct cf_om_node *parent)
|
||||||
|
{
|
||||||
|
cf_warn_children(__FILE__, __LINE__, parent, "spurious");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_unsupported_node(const struct cf_om_node *node)
|
||||||
|
{
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_unsupported_children(const struct cf_om_node *parent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < parent->nodc; ++i) {
|
||||||
|
if (parent->nodv[i]->text)
|
||||||
|
cf_warn_unsupported_node(parent->nodv[i]);
|
||||||
|
cf_warn_unsupported_children(parent->nodv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf strbuf_cf_flags(strbuf sb, int flags)
|
||||||
|
{
|
||||||
|
if (flags == CFERROR)
|
||||||
|
return strbuf_puts(sb, "CFERROR");
|
||||||
|
size_t n = strbuf_len(sb);
|
||||||
|
static struct { int flag; const char *name; } flagdefs[] = {
|
||||||
|
{ CFEMPTY, "CFEMPTY" },
|
||||||
|
{ CFSTRINGOVERFLOW, "CFSTRINGOVERFLOW" },
|
||||||
|
{ CFARRAYOVERFLOW, "CFARRAYOVERFLOW" },
|
||||||
|
{ CFINCOMPLETE, "CFINCOMPLETE" },
|
||||||
|
{ CFINVALID, "CFINVALID" },
|
||||||
|
{ CFUNSUPPORTED, "CFUNSUPPORTED" },
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < NELS(flagdefs); ++i) {
|
||||||
|
if (flags & flagdefs[i].flag) {
|
||||||
|
if (strbuf_len(sb) != n)
|
||||||
|
strbuf_putc(sb, ' ');
|
||||||
|
strbuf_puts(sb, flagdefs[i].name);
|
||||||
|
flags &= ~flagdefs[i].flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < NELS(flagdefs); ++i) {
|
||||||
|
if (flags & CFSUB(flagdefs[i].flag)) {
|
||||||
|
if (strbuf_len(sb) != n)
|
||||||
|
strbuf_putc(sb, ' ');
|
||||||
|
strbuf_puts(sb, "CFSUB(");
|
||||||
|
strbuf_puts(sb, flagdefs[i].name);
|
||||||
|
strbuf_putc(sb, ')');
|
||||||
|
flags &= ~CFSUB(flagdefs[i].flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags) {
|
||||||
|
if (strbuf_len(sb) != n)
|
||||||
|
strbuf_putc(sb, ' ');
|
||||||
|
strbuf_sprintf(sb, "%#x", flags);
|
||||||
|
}
|
||||||
|
if (strbuf_len(sb) == n)
|
||||||
|
strbuf_puts(sb, "CFOK");
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf strbuf_cf_flag_reason(strbuf sb, int flags)
|
||||||
|
{
|
||||||
|
if (flags == CFERROR)
|
||||||
|
return strbuf_puts(sb, "unrecoverable error");
|
||||||
|
size_t n = strbuf_len(sb);
|
||||||
|
static struct { int flag; const char *reason; } flagdefs[] = {
|
||||||
|
{ CFEMPTY, "empty" },
|
||||||
|
{ CFSTRINGOVERFLOW, "string overflow" },
|
||||||
|
{ CFARRAYOVERFLOW, "array overflow" },
|
||||||
|
{ CFINCOMPLETE, "incomplete" },
|
||||||
|
{ CFINVALID, "invalid" },
|
||||||
|
{ CFUNSUPPORTED, "not supported" },
|
||||||
|
{ CFSUB(CFEMPTY), "contains empty element" },
|
||||||
|
{ CFSUB(CFSTRINGOVERFLOW), "contains string overflow" },
|
||||||
|
{ CFSUB(CFARRAYOVERFLOW), "contains array overflow" },
|
||||||
|
{ CFSUB(CFINCOMPLETE), "contains incomplete element" },
|
||||||
|
{ CFSUB(CFINVALID), "contains invalid element" },
|
||||||
|
{ CFSUB(CFUNSUPPORTED), "contains unsupported element" },
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < NELS(flagdefs); ++i) {
|
||||||
|
if (flags & flagdefs[i].flag) {
|
||||||
|
if (strbuf_len(sb) != n)
|
||||||
|
strbuf_puts(sb, ", ");
|
||||||
|
strbuf_puts(sb, flagdefs[i].reason);
|
||||||
|
flags &= ~flagdefs[i].flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strbuf_len(sb) == n)
|
||||||
|
strbuf_puts(sb, "no reason");
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_node_value(const struct cf_om_node *node, int reason)
|
||||||
|
{
|
||||||
|
strbuf b = strbuf_alloca(180);
|
||||||
|
strbuf_cf_flag_reason(b, reason);
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "value %s %s", alloca_str_toprint(node->text), strbuf_str(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_no_array(const struct cf_om_node *node, int reason)
|
||||||
|
{
|
||||||
|
strbuf b = strbuf_alloca(180);
|
||||||
|
strbuf_cf_flag_reason(b, reason);
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "array discarded -- %s", strbuf_str(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_array_key(const struct cf_om_node *node, int reason)
|
||||||
|
{
|
||||||
|
strbuf b = strbuf_alloca(180);
|
||||||
|
strbuf_cf_flag_reason(b, reason);
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "array label %s -- %s", alloca_str_toprint(node->key), strbuf_str(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_array_value(const struct cf_om_node *node, int reason)
|
||||||
|
{
|
||||||
|
strbuf b = strbuf_alloca(180);
|
||||||
|
strbuf_cf_flag_reason(b, reason);
|
||||||
|
if (node->text)
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "array value %s -- %s", alloca_str_toprint(node->text), strbuf_str(b));
|
||||||
|
else
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "array element -- %s", strbuf_str(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cf_warn_list_overflow(const struct cf_om_node *node)
|
||||||
|
{
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "list overflow");
|
||||||
|
cf_warn_children(__FILE__, __LINE__, node, "list overflow");
|
||||||
|
}
|
271
conf_parse.c
Normal file
271
conf_parse.c
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
Serval DNA configuration
|
||||||
|
Copyright (C) 2012 Serval Project Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
// Generate config set-default function definitions, cf_dfl_config_NAME().
|
||||||
|
#define STRUCT(__name, __validator...) \
|
||||||
|
int cf_dfl_config_##__name(struct config_##__name *s) {
|
||||||
|
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
|
||||||
|
s->__element = (__default);
|
||||||
|
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
|
||||||
|
s->__element = (__default);
|
||||||
|
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
|
||||||
|
strncpy(s->__element, (__default), (__size))[(__size)] = '\0';
|
||||||
|
#define SUB_STRUCT(__name, __element, __flags) \
|
||||||
|
cf_dfl_config_##__name(&s->__element);
|
||||||
|
#define NODE_STRUCT(__name, __element, __parser, __flags) \
|
||||||
|
cf_dfl_config_##__name(&s->__element);
|
||||||
|
#define END_STRUCT \
|
||||||
|
return CFOK; \
|
||||||
|
}
|
||||||
|
#define ARRAY(__name, __validator...) \
|
||||||
|
int cf_dfl_config_##__name(struct config_##__name *a) { \
|
||||||
|
a->ac = 0; \
|
||||||
|
return CFOK; \
|
||||||
|
}
|
||||||
|
#define KEY_ATOM(__type, __eltparser, __cmpfunc...)
|
||||||
|
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...)
|
||||||
|
#define VALUE_ATOM(__type, __eltparser)
|
||||||
|
#define VALUE_STRING(__strsize, __eltparser)
|
||||||
|
#define VALUE_NODE(__type, __eltparser)
|
||||||
|
#define VALUE_SUB_STRUCT(__structname)
|
||||||
|
#define VALUE_NODE_STRUCT(__structname, __eltparser)
|
||||||
|
#define END_ARRAY(__size)
|
||||||
|
#include "config_schema.h"
|
||||||
|
#undef STRUCT
|
||||||
|
#undef NODE
|
||||||
|
#undef ATOM
|
||||||
|
#undef STRING
|
||||||
|
#undef SUB_STRUCT
|
||||||
|
#undef NODE_STRUCT
|
||||||
|
#undef END_STRUCT
|
||||||
|
#undef ARRAY
|
||||||
|
#undef KEY_ATOM
|
||||||
|
#undef KEY_STRING
|
||||||
|
#undef VALUE_ATOM
|
||||||
|
#undef VALUE_STRING
|
||||||
|
#undef VALUE_NODE
|
||||||
|
#undef VALUE_SUB_STRUCT
|
||||||
|
#undef VALUE_NODE_STRUCT
|
||||||
|
#undef END_ARRAY
|
||||||
|
|
||||||
|
// Generate array element comparison functions.
|
||||||
|
#define STRUCT(__name, __validator...)
|
||||||
|
#define NODE(__type, __element, __default, __parser, __flags, __comment)
|
||||||
|
#define ATOM(__type, __element, __default, __parser, __flags, __comment)
|
||||||
|
#define STRING(__size, __element, __default, __parser, __flags, __comment)
|
||||||
|
#define SUB_STRUCT(__name, __element, __flags)
|
||||||
|
#define NODE_STRUCT(__name, __element, __parser, __flags)
|
||||||
|
#define END_STRUCT
|
||||||
|
#define ARRAY(__name, __validator...) \
|
||||||
|
static int __cmp_config_##__name(const struct config_##__name##__element *a, const struct config_##__name##__element *b) { \
|
||||||
|
__compare_func__config_##__name##__t *cmp = (NULL
|
||||||
|
#define KEY_ATOM(__type, __eltparser, __cmpfunc...) \
|
||||||
|
,##__cmpfunc); \
|
||||||
|
return cmp ? (*cmp)(&a->key, &b->key) : 0;
|
||||||
|
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \
|
||||||
|
,##__cmpfunc); \
|
||||||
|
return cmp ? (*cmp)(a->key, b->key) : 0;
|
||||||
|
#define VALUE_ATOM(__type, __eltparser)
|
||||||
|
#define VALUE_STRING(__strsize, __eltparser)
|
||||||
|
#define VALUE_NODE(__type, __eltparser)
|
||||||
|
#define VALUE_SUB_STRUCT(__structname)
|
||||||
|
#define VALUE_NODE_STRUCT(__structname, __eltparser)
|
||||||
|
#define END_ARRAY(__size) \
|
||||||
|
}
|
||||||
|
#include "config_schema.h"
|
||||||
|
#undef STRUCT
|
||||||
|
#undef NODE
|
||||||
|
#undef ATOM
|
||||||
|
#undef STRING
|
||||||
|
#undef SUB_STRUCT
|
||||||
|
#undef NODE_STRUCT
|
||||||
|
#undef END_STRUCT
|
||||||
|
#undef ARRAY
|
||||||
|
#undef KEY_ATOM
|
||||||
|
#undef KEY_STRING
|
||||||
|
#undef VALUE_ATOM
|
||||||
|
#undef VALUE_STRING
|
||||||
|
#undef VALUE_NODE
|
||||||
|
#undef VALUE_SUB_STRUCT
|
||||||
|
#undef VALUE_NODE_STRUCT
|
||||||
|
#undef END_ARRAY
|
||||||
|
|
||||||
|
// Schema item flags.
|
||||||
|
#define __MANDATORY (1<<0)
|
||||||
|
#define __TEXT (1<<1)
|
||||||
|
#define __CHILDREN (1<<2)
|
||||||
|
|
||||||
|
// Schema flag symbols, to be used in the '__flags' macro arguments.
|
||||||
|
#define MANDATORY |__MANDATORY
|
||||||
|
#define USES_TEXT |__TEXT
|
||||||
|
#define USES_CHILDREN |__CHILDREN
|
||||||
|
|
||||||
|
// Generate parsing functions, cf_opt_config_SECTION()
|
||||||
|
#define STRUCT(__name, __validator...) \
|
||||||
|
int cf_opt_config_##__name(struct config_##__name *strct, const struct cf_om_node *node) { \
|
||||||
|
int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = (NULL, ##__validator); \
|
||||||
|
int result = CFEMPTY; \
|
||||||
|
char used[node->nodc]; \
|
||||||
|
memset(used, 0, node->nodc * sizeof used[0]);
|
||||||
|
#define __ITEM(__element, __flags, __parseexpr) \
|
||||||
|
{ \
|
||||||
|
int i = cf_get_child(node, #__element); \
|
||||||
|
const struct cf_om_node *child = (i != -1) ? node->nodv[i] : NULL; \
|
||||||
|
int ret = CFEMPTY; \
|
||||||
|
if (child) { \
|
||||||
|
used[i] |= (__flags); \
|
||||||
|
ret = (__parseexpr); \
|
||||||
|
if (ret == CFERROR) \
|
||||||
|
return CFERROR; \
|
||||||
|
} \
|
||||||
|
result |= ret & CF__SUBFLAGS; \
|
||||||
|
ret &= CF__FLAGS; \
|
||||||
|
if (!(ret & CFEMPTY)) \
|
||||||
|
result &= ~CFEMPTY; \
|
||||||
|
else if ((__flags) & __MANDATORY) { \
|
||||||
|
cf_warn_missing_node(node, #__element); \
|
||||||
|
result |= CFINCOMPLETE; \
|
||||||
|
} \
|
||||||
|
if (ret & ~CFEMPTY) { \
|
||||||
|
assert(child != NULL); \
|
||||||
|
if (child->text) \
|
||||||
|
cf_warn_node_value(child, ret); \
|
||||||
|
result |= CFSUB(ret); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
|
||||||
|
__ITEM(__element, 0 __flags, __parser(&strct->__element, child))
|
||||||
|
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
|
||||||
|
__ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(&strct->__element, child->text) : CFEMPTY)
|
||||||
|
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
|
||||||
|
__ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(strct->__element, (__size) + 1, child->text) : CFEMPTY)
|
||||||
|
#define SUB_STRUCT(__name, __element, __flags) \
|
||||||
|
__ITEM(__element, (0 __flags)|__CHILDREN, cf_opt_config_##__name(&strct->__element, child))
|
||||||
|
#define NODE_STRUCT(__name, __element, __parser, __flags) \
|
||||||
|
__ITEM(__element, (0 __flags)|__TEXT|__CHILDREN, __parser(&strct->__element, child))
|
||||||
|
#define END_STRUCT \
|
||||||
|
{ \
|
||||||
|
int i; \
|
||||||
|
for (i = 0; i < node->nodc; ++i) { \
|
||||||
|
if (node->nodv[i]->text && !(used[i] & __TEXT)) { \
|
||||||
|
cf_warn_unsupported_node(node->nodv[i]); \
|
||||||
|
result |= CFSUB(CFUNSUPPORTED); \
|
||||||
|
} \
|
||||||
|
if (node->nodv[i]->nodc && !(used[i] & __CHILDREN)) { \
|
||||||
|
cf_warn_unsupported_children(node->nodv[i]); \
|
||||||
|
result |= CFSUB(CFUNSUPPORTED); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (validator) \
|
||||||
|
result = (*validator)(node, strct, result); \
|
||||||
|
return result; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARRAY(__name, __validator...) \
|
||||||
|
int cf_opt_config_##__name(struct config_##__name *array, const struct cf_om_node *node) { \
|
||||||
|
__compare_func__config_##__name##__t *cmp = NULL; \
|
||||||
|
int (*eltcmp)(const struct config_##__name##__element *, const struct config_##__name##__element *) = __cmp_config_##__name; \
|
||||||
|
int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = (NULL, ##__validator); \
|
||||||
|
int result = CFOK; \
|
||||||
|
int i, n; \
|
||||||
|
for (n = 0, i = 0; i < node->nodc && n < NELS(array->av); ++i) { \
|
||||||
|
const struct cf_om_node *child = node->nodv[i]; \
|
||||||
|
int ret = CFEMPTY;
|
||||||
|
#define __ARRAY_KEY(__parseexpr) \
|
||||||
|
ret = (__parseexpr); \
|
||||||
|
if (ret == CFERROR) \
|
||||||
|
return CFERROR; \
|
||||||
|
result |= ret & CF__SUBFLAGS; \
|
||||||
|
ret &= CF__FLAGS; \
|
||||||
|
result |= CFSUB(ret); \
|
||||||
|
if (ret != CFOK) \
|
||||||
|
cf_warn_array_key(child, ret);
|
||||||
|
#define __ARRAY_VALUE(__parseexpr) \
|
||||||
|
if (ret == CFOK) { \
|
||||||
|
ret = (__parseexpr); \
|
||||||
|
if (ret == CFERROR) \
|
||||||
|
return CFERROR; \
|
||||||
|
result |= ret & CF__SUBFLAGS; \
|
||||||
|
ret &= CF__FLAGS; \
|
||||||
|
result |= CFSUB(ret); \
|
||||||
|
if (ret == CFOK) \
|
||||||
|
++n; \
|
||||||
|
else \
|
||||||
|
cf_warn_array_value(child, ret); \
|
||||||
|
}
|
||||||
|
#define END_ARRAY(__size) \
|
||||||
|
} \
|
||||||
|
if (i < node->nodc) { \
|
||||||
|
assert(n == NELS(array->av)); \
|
||||||
|
result |= CFARRAYOVERFLOW; \
|
||||||
|
for (; i < node->nodc; ++i) \
|
||||||
|
cf_warn_list_overflow(node->nodv[i]); \
|
||||||
|
} \
|
||||||
|
array->ac = n; \
|
||||||
|
if (cmp) \
|
||||||
|
qsort(array->av, array->ac, sizeof array->av[0], (int (*)(const void *, const void *)) eltcmp); \
|
||||||
|
if (validator) \
|
||||||
|
result = (*validator)(node, array, result); \
|
||||||
|
if (result & ~CFEMPTY) { \
|
||||||
|
cf_warn_no_array(node, result); \
|
||||||
|
array->ac = 0; \
|
||||||
|
} \
|
||||||
|
if (array->ac == 0) \
|
||||||
|
result |= CFEMPTY; \
|
||||||
|
return result; \
|
||||||
|
}
|
||||||
|
#define KEY_ATOM(__type, __eltparser, __cmpfunc...) \
|
||||||
|
__ARRAY_KEY(__eltparser(&array->av[n].key, child->key)) \
|
||||||
|
cmp = (NULL, ##__cmpfunc);
|
||||||
|
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \
|
||||||
|
__ARRAY_KEY(__eltparser(array->av[n].key, sizeof array->av[n].key, child->key)) \
|
||||||
|
cmp = (NULL, ##__cmpfunc);
|
||||||
|
#define VALUE_ATOM(__type, __eltparser) \
|
||||||
|
__ARRAY_VALUE(child->text ? __eltparser(&array->av[n].value, child->text) : CFEMPTY)
|
||||||
|
#define VALUE_STRING(__strsize, __eltparser) \
|
||||||
|
__ARRAY_VALUE(child->text ? __eltparser(array->av[n].value, sizeof array->av[n].value, child->text) : CFEMPTY)
|
||||||
|
#define VALUE_NODE(__type, __eltparser) \
|
||||||
|
__ARRAY_VALUE(__eltparser(&array->av[n].value, child))
|
||||||
|
#define VALUE_SUB_STRUCT(__structname) \
|
||||||
|
__ARRAY_VALUE(cf_opt_config_##__structname(&array->av[n].value, child))
|
||||||
|
#define VALUE_NODE_STRUCT(__structname, __eltparser) \
|
||||||
|
__ARRAY_VALUE(__eltparser(&array->av[n].value, child))
|
||||||
|
|
||||||
|
#include "config_schema.h"
|
||||||
|
|
||||||
|
#undef STRUCT
|
||||||
|
#undef NODE
|
||||||
|
#undef ATOM
|
||||||
|
#undef STRING
|
||||||
|
#undef SUB_STRUCT
|
||||||
|
#undef NODE_STRUCT
|
||||||
|
#undef END_STRUCT
|
||||||
|
#undef ARRAY
|
||||||
|
#undef KEY_ATOM
|
||||||
|
#undef KEY_STRING
|
||||||
|
#undef VALUE_ATOM
|
||||||
|
#undef VALUE_STRING
|
||||||
|
#undef VALUE_NODE
|
||||||
|
#undef VALUE_SUB_STRUCT
|
||||||
|
#undef VALUE_NODE_STRUCT
|
||||||
|
#undef END_ARRAY
|
70
config.h
70
config.h
@ -180,10 +180,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
|
|
||||||
|
#define NELS(a) (sizeof (a) / sizeof *(a))
|
||||||
|
|
||||||
typedef unsigned long debugflags_t;
|
typedef unsigned long debugflags_t;
|
||||||
|
debugflags_t debugFlagMask(const char *flagname);
|
||||||
|
|
||||||
#define RHIZOME_BUNDLE_KEY_BYTES 32
|
#define RHIZOME_BUNDLE_KEY_BYTES 32
|
||||||
|
|
||||||
#define PORT_DNA 4110
|
#define PORT_DNA 4110
|
||||||
@ -218,25 +224,11 @@ struct cf_om_node {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct cf_om_node *cf_parse_to_om(const char *source, const char *buf, size_t len);
|
struct cf_om_node *cf_parse_to_om(const char *source, const char *buf, size_t len);
|
||||||
|
int cf_get_child(const struct cf_om_node *parent, const char *key);
|
||||||
void cf_free_node(struct cf_om_node *node);
|
void cf_free_node(struct cf_om_node *node);
|
||||||
void cf_dump_node(const struct cf_om_node *node, int indent);
|
void cf_dump_node(const struct cf_om_node *node, int indent);
|
||||||
|
|
||||||
void cf_warn_nodev(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, va_list ap);
|
/* Return bit flags for config schema default cf_dfl_xxx() and parsing cf_opt_xxx() functions. */
|
||||||
void cf_warn_childrenv(const char *file, unsigned line, const struct cf_om_node *parent, const char *fmt, va_list ap);
|
|
||||||
void cf_warn_node(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, ...);
|
|
||||||
void cf_warn_children(const char *file, unsigned line, const struct cf_om_node *node, const char *fmt, ...);
|
|
||||||
void cf_warn_duplicate_node(const struct cf_om_node *parent, const char *key);
|
|
||||||
void cf_warn_missing_node(const struct cf_om_node *parent, const char *key);
|
|
||||||
void cf_warn_node_value(const struct cf_om_node *node, int reason);
|
|
||||||
void cf_warn_no_array(const struct cf_om_node *node, int reason);
|
|
||||||
void cf_warn_unsupported_node(const struct cf_om_node *node);
|
|
||||||
void cf_warn_unsupported_children(const struct cf_om_node *parent);
|
|
||||||
void cf_warn_list_overflow(const struct cf_om_node *node);
|
|
||||||
void cf_warn_spurious_children(const struct cf_om_node *parent);
|
|
||||||
void cf_warn_array_label(const struct cf_om_node *node, int reason);
|
|
||||||
void cf_warn_array_value(const struct cf_om_node *node, int reason);
|
|
||||||
|
|
||||||
/* Return bit flags for schema default cf_dfl_ and parsing cf_opt_ functions. */
|
|
||||||
|
|
||||||
#define CFERROR (~0) // all set
|
#define CFERROR (~0) // all set
|
||||||
#define CFOK 0
|
#define CFOK 0
|
||||||
@ -252,6 +244,24 @@ void cf_warn_array_value(const struct cf_om_node *node, int reason);
|
|||||||
#define CF__FLAGS (~0 & ~CF__SUBFLAGS)
|
#define CF__FLAGS (~0 & ~CF__SUBFLAGS)
|
||||||
|
|
||||||
strbuf strbuf_cf_flags(strbuf, int);
|
strbuf strbuf_cf_flags(strbuf, int);
|
||||||
|
strbuf strbuf_cf_flag_reason(strbuf sb, int flags);
|
||||||
|
|
||||||
|
/* Diagnostic functions for use in config schema parsing functions, cf_opt_xxx(). */
|
||||||
|
|
||||||
|
void cf_warn_nodev(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, va_list ap);
|
||||||
|
void cf_warn_childrenv(const char *file, unsigned line, const struct cf_om_node *parent, const char *fmt, va_list ap);
|
||||||
|
void cf_warn_node(const char *file, unsigned line, const struct cf_om_node *node, const char *key, const char *fmt, ...);
|
||||||
|
void cf_warn_children(const char *file, unsigned line, const struct cf_om_node *node, const char *fmt, ...);
|
||||||
|
void cf_warn_duplicate_node(const struct cf_om_node *parent, const char *key);
|
||||||
|
void cf_warn_missing_node(const struct cf_om_node *parent, const char *key);
|
||||||
|
void cf_warn_node_value(const struct cf_om_node *node, int reason);
|
||||||
|
void cf_warn_no_array(const struct cf_om_node *node, int reason);
|
||||||
|
void cf_warn_unsupported_node(const struct cf_om_node *node);
|
||||||
|
void cf_warn_unsupported_children(const struct cf_om_node *parent);
|
||||||
|
void cf_warn_list_overflow(const struct cf_om_node *node);
|
||||||
|
void cf_warn_spurious_children(const struct cf_om_node *parent);
|
||||||
|
void cf_warn_array_key(const struct cf_om_node *node, int reason);
|
||||||
|
void cf_warn_array_value(const struct cf_om_node *node, int reason);
|
||||||
|
|
||||||
// Generate config struct definitions, struct config_NAME.
|
// Generate config struct definitions, struct config_NAME.
|
||||||
#define STRUCT(__name, __validator...) \
|
#define STRUCT(__name, __validator...) \
|
||||||
@ -307,27 +317,17 @@ strbuf strbuf_cf_flags(strbuf, int);
|
|||||||
#undef VALUE_NODE_STRUCT
|
#undef VALUE_NODE_STRUCT
|
||||||
#undef END_ARRAY
|
#undef END_ARRAY
|
||||||
|
|
||||||
// Generate config set-default functions, cf_dfl_config_NAME().
|
// Generate config set-default function prototypes, cf_dfl_config_NAME().
|
||||||
#define STRUCT(__name, __validator...) \
|
#define STRUCT(__name, __validator...) \
|
||||||
int cf_dfl_config_##__name(struct config_##__name *s) {
|
int cf_dfl_config_##__name(struct config_##__name *s);
|
||||||
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
|
#define NODE(__type, __element, __default, __parser, __flags, __comment)
|
||||||
s->__element = (__default);
|
#define ATOM(__type, __element, __default, __parser, __flags, __comment)
|
||||||
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
|
#define STRING(__size, __element, __default, __parser, __flags, __comment)
|
||||||
s->__element = (__default);
|
#define SUB_STRUCT(__name, __element, __flags)
|
||||||
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
|
#define NODE_STRUCT(__name, __element, __parser, __flags)
|
||||||
strncpy(s->__element, (__default), (__size))[(__size)] = '\0';
|
#define END_STRUCT
|
||||||
#define SUB_STRUCT(__name, __element, __flags) \
|
|
||||||
cf_dfl_config_##__name(&s->__element);
|
|
||||||
#define NODE_STRUCT(__name, __element, __parser, __flags) \
|
|
||||||
cf_dfl_config_##__name(&s->__element);
|
|
||||||
#define END_STRUCT \
|
|
||||||
return CFOK; \
|
|
||||||
}
|
|
||||||
#define ARRAY(__name, __validator...) \
|
#define ARRAY(__name, __validator...) \
|
||||||
int cf_dfl_config_##__name(struct config_##__name *a) { \
|
int cf_dfl_config_##__name(struct config_##__name *a);
|
||||||
a->ac = 0; \
|
|
||||||
return CFOK; \
|
|
||||||
}
|
|
||||||
#define KEY_ATOM(__type, __eltparser, __cmpfunc...)
|
#define KEY_ATOM(__type, __eltparser, __cmpfunc...)
|
||||||
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...)
|
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...)
|
||||||
#define VALUE_ATOM(__type, __eltparser)
|
#define VALUE_ATOM(__type, __eltparser)
|
||||||
|
530
config_schema.c
Normal file
530
config_schema.c
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
/*
|
||||||
|
Serval DNA configuration
|
||||||
|
Copyright (C) 2012 Serval Project Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
#include "strbuf_helpers.h"
|
||||||
|
|
||||||
|
#define _DEBUGF(F,...) fprintf(stderr, "DEBUG: " F "\n", ##__VA_ARGS__)
|
||||||
|
#define _WARNF(F,...) fprintf(stderr, "WARN: " F "\n", ##__VA_ARGS__)
|
||||||
|
#define _WHYF(F,...) fprintf(stderr, "ERROR: " F "\n", ##__VA_ARGS__)
|
||||||
|
#define _WHYF_perror(F,...) fprintf(stderr, "ERROR: " F ": %s [errno=%d]\n", ##__VA_ARGS__, strerror(errno), errno)
|
||||||
|
#define DEBUGF(F,...) _DEBUGF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define WARNF(F,...) _WARNF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define WHYF(F,...) _WHYF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#define WHYF_perror(F,...) _WHYF_perror("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
int cf_opt_boolean(int *booleanp, const char *text)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(text, "true") || !strcasecmp(text, "yes") || !strcasecmp(text, "on") || !strcasecmp(text, "1")) {
|
||||||
|
*booleanp = 1;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(text, "false") || !strcasecmp(text, "no") || !strcasecmp(text, "off") || !strcasecmp(text, "0")) {
|
||||||
|
*booleanp = 0;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
return CFINVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_absolute_path(char *str, size_t len, const char *text)
|
||||||
|
{
|
||||||
|
if (text[0] != '/')
|
||||||
|
return CFINVALID;
|
||||||
|
if (strlen(text) >= len)
|
||||||
|
return CFSTRINGOVERFLOW;
|
||||||
|
strncpy(str, text, len);
|
||||||
|
assert(str[len - 1] == '\0');
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_debugflags(debugflags_t *flagsp, const struct cf_om_node *node)
|
||||||
|
{
|
||||||
|
//DEBUGF("%s", __FUNCTION__);
|
||||||
|
//cf_dump_node(node, 1);
|
||||||
|
debugflags_t setmask = 0;
|
||||||
|
debugflags_t clearmask = 0;
|
||||||
|
int setall = 0;
|
||||||
|
int clearall = 0;
|
||||||
|
int result = CFEMPTY;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < node->nodc; ++i) {
|
||||||
|
const struct cf_om_node *child = node->nodv[i];
|
||||||
|
cf_warn_unsupported_children(child);
|
||||||
|
debugflags_t mask = debugFlagMask(child->key);
|
||||||
|
int flag = -1;
|
||||||
|
if (!mask)
|
||||||
|
cf_warn_unsupported_node(child);
|
||||||
|
else if (child->text) {
|
||||||
|
int ret = cf_opt_boolean(&flag, child->text);
|
||||||
|
switch (ret) {
|
||||||
|
case CFERROR: return CFERROR;
|
||||||
|
case CFOK:
|
||||||
|
result &= ~CFEMPTY;
|
||||||
|
if (mask == ~0) {
|
||||||
|
if (flag)
|
||||||
|
setall = 1;
|
||||||
|
else
|
||||||
|
clearall = 1;
|
||||||
|
} else {
|
||||||
|
if (flag)
|
||||||
|
setmask |= mask;
|
||||||
|
else
|
||||||
|
clearmask |= mask;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cf_warn_node_value(child, ret);
|
||||||
|
result |= ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (setall)
|
||||||
|
*flagsp = ~0;
|
||||||
|
else if (clearall)
|
||||||
|
*flagsp = 0;
|
||||||
|
*flagsp &= ~clearmask;
|
||||||
|
*flagsp |= setmask;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_protocol(char *str, size_t len, const char *text)
|
||||||
|
{
|
||||||
|
if (!str_is_uri_scheme(text))
|
||||||
|
return CFINVALID;
|
||||||
|
if (strlen(text) >= len)
|
||||||
|
return CFSTRINGOVERFLOW;
|
||||||
|
strncpy(str, text, len);
|
||||||
|
assert(str[len - 1] == '\0');
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_rhizome_peer(struct config_rhizomepeer *rpeer, const struct cf_om_node *node)
|
||||||
|
{
|
||||||
|
if (!node->text)
|
||||||
|
return cf_opt_config_rhizomepeer(rpeer, node);
|
||||||
|
cf_warn_spurious_children(node);
|
||||||
|
const char *protocol;
|
||||||
|
size_t protolen;
|
||||||
|
const char *auth;
|
||||||
|
if (str_is_uri(node->text)) {
|
||||||
|
const char *hier;
|
||||||
|
if (!( str_uri_scheme(node->text, &protocol, &protolen)
|
||||||
|
&& str_uri_hierarchical(node->text, &hier, NULL)
|
||||||
|
&& str_uri_hierarchical_authority(hier, &auth, NULL))
|
||||||
|
)
|
||||||
|
return CFINVALID;
|
||||||
|
} else {
|
||||||
|
auth = node->text;
|
||||||
|
protocol = "http";
|
||||||
|
protolen = strlen(protocol);
|
||||||
|
}
|
||||||
|
const char *host;
|
||||||
|
size_t hostlen;
|
||||||
|
unsigned short port = 4110;
|
||||||
|
if (!str_uri_authority_hostname(auth, &host, &hostlen))
|
||||||
|
return CFINVALID;
|
||||||
|
str_uri_authority_port(auth, &port);
|
||||||
|
if (protolen >= sizeof rpeer->protocol)
|
||||||
|
return CFSTRINGOVERFLOW;
|
||||||
|
if (hostlen >= sizeof rpeer->host)
|
||||||
|
return CFSTRINGOVERFLOW;
|
||||||
|
strncpy(rpeer->protocol, protocol, protolen)[protolen] = '\0';
|
||||||
|
strncpy(rpeer->host, host, hostlen)[hostlen] = '\0';
|
||||||
|
rpeer->port = port;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_str(char *str, size_t len, const char *text)
|
||||||
|
{
|
||||||
|
if (strlen(text) >= len)
|
||||||
|
return CFSTRINGOVERFLOW;
|
||||||
|
strncpy(str, text, len);
|
||||||
|
assert(str[len - 1] == '\0');
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_str_nonempty(char *str, size_t len, const char *text)
|
||||||
|
{
|
||||||
|
if (!text[0])
|
||||||
|
return CFINVALID;
|
||||||
|
return cf_opt_str(str, len, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_int(int *intp, const char *text)
|
||||||
|
{
|
||||||
|
const char *end = text;
|
||||||
|
long value = strtol(text, (char**)&end, 10);
|
||||||
|
if (end == text || *end)
|
||||||
|
return CFINVALID;
|
||||||
|
*intp = value;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_uint32_nonzero(uint32_t *intp, const char *text)
|
||||||
|
{
|
||||||
|
const char *end = text;
|
||||||
|
long value = strtoul(text, (char**)&end, 10);
|
||||||
|
if (end == text || *end || value < 1 || value > 0xffffffffL)
|
||||||
|
return CFINVALID;
|
||||||
|
*intp = value;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_uint64_scaled(uint64_t *intp, const char *text)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
const char *end;
|
||||||
|
if (!str_to_uint64_scaled(text, 10, &result, &end) || *end)
|
||||||
|
return CFINVALID;
|
||||||
|
*intp = result;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_ushort_nonzero(unsigned short *ushortp, const char *text)
|
||||||
|
{
|
||||||
|
uint32_t ui;
|
||||||
|
if (cf_opt_uint32_nonzero(&ui, text) != CFOK || ui > 0xffff)
|
||||||
|
return CFINVALID;
|
||||||
|
*ushortp = ui;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmp_short(const short *a, const short *b)
|
||||||
|
{
|
||||||
|
return *a < *b ? -1 : *a > *b ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmp_ushort(const unsigned short *a, const unsigned short *b)
|
||||||
|
{
|
||||||
|
return *a < *b ? -1 : *a > *b ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmp_sid(const sid_t *a, const sid_t *b)
|
||||||
|
{
|
||||||
|
return memcmp(a->binary, b->binary, sizeof a->binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vld_argv(const struct cf_om_node *parent, struct config_argv *array, int result)
|
||||||
|
{
|
||||||
|
unsigned short last_key = 0;
|
||||||
|
int i;
|
||||||
|
if (array->ac) {
|
||||||
|
unsigned short last_key = array->av[0].key;
|
||||||
|
for (i = 1; i < array->ac; ++i) {
|
||||||
|
unsigned short key = array->av[i].key;
|
||||||
|
if (last_key > key) {
|
||||||
|
cf_warn_node(__FILE__, __LINE__, parent, NULL, "array is not sorted");
|
||||||
|
return CFERROR;
|
||||||
|
}
|
||||||
|
last_key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < array->ac; ++i) {
|
||||||
|
unsigned short key = array->av[i].key;
|
||||||
|
assert(key >= 1);
|
||||||
|
assert(key >= last_key);
|
||||||
|
if (last_key == key) {
|
||||||
|
char labelkey[12];
|
||||||
|
sprintf(labelkey, "%u", last_key);
|
||||||
|
cf_warn_duplicate_node(parent, labelkey);
|
||||||
|
result |= CFINVALID;
|
||||||
|
}
|
||||||
|
while (++last_key < key && last_key <= sizeof(array->av)) {
|
||||||
|
char labelkey[12];
|
||||||
|
sprintf(labelkey, "%u", last_key);
|
||||||
|
cf_warn_missing_node(parent, labelkey);
|
||||||
|
result |= CFINCOMPLETE;
|
||||||
|
}
|
||||||
|
last_key = key;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_in_addr(struct in_addr *addrp, const char *text)
|
||||||
|
{
|
||||||
|
struct in_addr addr;
|
||||||
|
if (!inet_aton(text, &addr))
|
||||||
|
return CFINVALID;
|
||||||
|
*addrp = addr;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_port(unsigned short *portp, const char *text)
|
||||||
|
{
|
||||||
|
unsigned short port = 0;
|
||||||
|
const char *p;
|
||||||
|
for (p = text; isdigit(*p); ++p) {
|
||||||
|
unsigned oport = port;
|
||||||
|
port = port * 10 + *p - '0';
|
||||||
|
if (port / 10 != oport)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*p || port == 0)
|
||||||
|
return CFINVALID;
|
||||||
|
*portp = port;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_sid(sid_t *sidp, const char *text)
|
||||||
|
{
|
||||||
|
sid_t sid;
|
||||||
|
if (!str_is_subscriber_id(text))
|
||||||
|
return CFINVALID;
|
||||||
|
size_t n = fromhex(sidp->binary, text, SID_SIZE);
|
||||||
|
assert(n == SID_SIZE);
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_rhizome_bk(rhizome_bk_t *bkp, const char *text)
|
||||||
|
{
|
||||||
|
rhizome_bk_t sid;
|
||||||
|
if (!rhizome_str_is_bundle_key(text))
|
||||||
|
return CFINVALID;
|
||||||
|
size_t n = fromhex(bkp->binary, text, RHIZOME_BUNDLE_KEY_BYTES);
|
||||||
|
assert(n == RHIZOME_BUNDLE_KEY_BYTES);
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_interface_type(short *typep, const char *text)
|
||||||
|
{
|
||||||
|
if (strcasecmp(text, "ethernet") == 0) {
|
||||||
|
*typep = OVERLAY_INTERFACE_ETHERNET;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
if (strcasecmp(text, "wifi") == 0) {
|
||||||
|
*typep = OVERLAY_INTERFACE_WIFI;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
if (strcasecmp(text, "catear") == 0) {
|
||||||
|
*typep = OVERLAY_INTERFACE_PACKETRADIO;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
if (strcasecmp(text, "other") == 0) {
|
||||||
|
*typep = OVERLAY_INTERFACE_UNKNOWN;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
return CFINVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cf_opt_pattern_list(struct pattern_list *listp, const char *text)
|
||||||
|
{
|
||||||
|
struct pattern_list list;
|
||||||
|
memset(&list, 0, sizeof list);
|
||||||
|
const char *word = NULL;
|
||||||
|
const char *p;
|
||||||
|
for (p = text; ; ++p) {
|
||||||
|
if (!*p || isspace(*p) || *p == ',') {
|
||||||
|
if (word) {
|
||||||
|
size_t len = p - word;
|
||||||
|
if (list.patc >= NELS(list.patv) || len >= sizeof(list.patv[list.patc]))
|
||||||
|
return CFARRAYOVERFLOW;
|
||||||
|
strncpy(list.patv[list.patc++], word, len)[len] = '\0';
|
||||||
|
word = NULL;
|
||||||
|
}
|
||||||
|
if (!*p)
|
||||||
|
break;
|
||||||
|
} else if (!word)
|
||||||
|
word = p;
|
||||||
|
}
|
||||||
|
assert(word == NULL);
|
||||||
|
*listp = list;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Config parse function. Implements the original form of the 'interfaces' config option. Parses a
|
||||||
|
* single text string of the form:
|
||||||
|
*
|
||||||
|
* ( "+" | "-" ) [ interfacename ] [ "=" type ] [ ":" port [ ":" speed ] ]
|
||||||
|
*
|
||||||
|
* where:
|
||||||
|
*
|
||||||
|
* "+" means include the interface
|
||||||
|
* "-" means exclude the interface
|
||||||
|
*
|
||||||
|
* The original implementation applied include/exclude matching in the order that the list was
|
||||||
|
* given, but the new implementation applies all exclusions before apply inclusions. This should
|
||||||
|
* not be a problem, as there were no known uses that depended on testing an inclusion before an
|
||||||
|
* exclusion.
|
||||||
|
*
|
||||||
|
* An empty 'interfacename' matches all interfaces. So a "+" by itself includes all interfaces,
|
||||||
|
* and a '-' by itself excludes all interfaces. These two rules are applied after all other
|
||||||
|
* interface inclusions/exclusions are tested, otherwise "-" would overrule all other interfaces.
|
||||||
|
*
|
||||||
|
* The optional 'type' tells DNA how to handle the interface in terms of bandwidth:distance
|
||||||
|
* relationship for calculating tick times etc.
|
||||||
|
*
|
||||||
|
* The optional 'port' is the port number to bind all interfaces, instead of the default.
|
||||||
|
*
|
||||||
|
* The optional 'speed' is the nominal bits/second bandwidth of the interface, instead of the
|
||||||
|
* default. It is expressed as a positive integer with an optional scaling suffix, eg, "150k",
|
||||||
|
* "1K", "900M".
|
||||||
|
*
|
||||||
|
* @author Andrew Bettison <andrew@servalproject.com>
|
||||||
|
*/
|
||||||
|
int cf_opt_network_interface(struct config_network_interface *nifp, const char *text)
|
||||||
|
{
|
||||||
|
//DEBUGF("%s text=%s", __FUNCTION__, alloca_str_toprint(text));
|
||||||
|
struct config_network_interface nif;
|
||||||
|
cf_dfl_config_network_interface(&nif);
|
||||||
|
if (text[0] != '+' && text[0] != '-')
|
||||||
|
return CFINVALID; // "Sign must be + or -"
|
||||||
|
nif.exclude = (text[0] == '-');
|
||||||
|
const char *const endtext = text + strlen(text);
|
||||||
|
const char *name = text + 1;
|
||||||
|
const char *p = strpbrk(name, "=:");
|
||||||
|
if (!p)
|
||||||
|
p = endtext;
|
||||||
|
size_t len = p - name;
|
||||||
|
int star = (len == 0 || (name[0] != '>' && name[len - 1] != '*')) ? 1 : 0;
|
||||||
|
if (len + star >= sizeof(nif.match.patv[0]))
|
||||||
|
return CFSTRINGOVERFLOW;
|
||||||
|
strncpy(nif.match.patv[0], name, len)[len + star] = '\0';
|
||||||
|
if (star)
|
||||||
|
nif.match.patv[0][len] = '*';
|
||||||
|
nif.match.patc = 1;
|
||||||
|
if (*p == '=') {
|
||||||
|
const char *const type = p + 1;
|
||||||
|
p = strchr(type, ':');
|
||||||
|
if (!p)
|
||||||
|
p = endtext;
|
||||||
|
len = p - type;
|
||||||
|
if (len) {
|
||||||
|
char buf[len + 1];
|
||||||
|
strncpy(buf, type, len)[len] = '\0';
|
||||||
|
int result = cf_opt_interface_type(&nif.type, buf);
|
||||||
|
switch (result) {
|
||||||
|
case CFERROR: return CFERROR;
|
||||||
|
case CFOK: break;
|
||||||
|
default: return result; // "Invalid interface type"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*p == ':') {
|
||||||
|
const char *const port = p + 1;
|
||||||
|
p = strchr(port, ':');
|
||||||
|
if (!p)
|
||||||
|
p = endtext;
|
||||||
|
len = p - port;
|
||||||
|
if (len) {
|
||||||
|
char buf[len + 1];
|
||||||
|
strncpy(buf, port, len)[len] = '\0';
|
||||||
|
int result = cf_opt_port(&nif.port, buf);
|
||||||
|
switch (result) {
|
||||||
|
case CFERROR: return CFERROR;
|
||||||
|
case CFOK: break;
|
||||||
|
default: return result; // "Invalid interface port number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*p == ':') {
|
||||||
|
const char *const speed = p + 1;
|
||||||
|
p = endtext;
|
||||||
|
len = p - speed;
|
||||||
|
if (len) {
|
||||||
|
char buf[len + 1];
|
||||||
|
strncpy(buf, speed, len)[len] = '\0';
|
||||||
|
int result = cf_opt_uint64_scaled(&nif.speed, buf);
|
||||||
|
switch (result) {
|
||||||
|
case CFERROR: return CFERROR;
|
||||||
|
case CFOK: break;
|
||||||
|
default: return result; // "Invalid interface speed"
|
||||||
|
}
|
||||||
|
if (nif.speed < 1)
|
||||||
|
return CFINVALID; // "Interfaces must be capable of at least 1 bit per second"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*p)
|
||||||
|
return CFINVALID; // "Extra junk at end of interface specification"
|
||||||
|
*nifp = nif;
|
||||||
|
return CFOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Config parse function. Implements the original form of the 'interfaces' config option. Parses a
|
||||||
|
* comma-separated list of interface rules (see cf_opt_network_interface() for the format of each
|
||||||
|
* rule), then parses the regular config array-of-struct style interface option settings so that
|
||||||
|
* both forms are supported.
|
||||||
|
*
|
||||||
|
* @author Andrew Bettison <andrew@servalproject.com>
|
||||||
|
*/
|
||||||
|
int cf_opt_interface_list(struct config_interface_list *listp, const struct cf_om_node *node)
|
||||||
|
{
|
||||||
|
int result = cf_opt_config_interface_list(listp, node);
|
||||||
|
if (result == CFERROR)
|
||||||
|
return CFERROR;
|
||||||
|
if (node->text) {
|
||||||
|
const char *p;
|
||||||
|
const char *arg = NULL;
|
||||||
|
unsigned n = listp->ac;
|
||||||
|
for (p = node->text; n < NELS(listp->av); ++p) {
|
||||||
|
if (*p == '\0' || *p == ',' || isspace(*p)) {
|
||||||
|
if (arg) {
|
||||||
|
int len = p - arg;
|
||||||
|
if (len > 80) {
|
||||||
|
result |= CFSTRINGOVERFLOW;
|
||||||
|
goto bye;
|
||||||
|
}
|
||||||
|
char buf[len + 1];
|
||||||
|
strncpy(buf, arg, len)[len] = '\0';
|
||||||
|
int ret = cf_opt_network_interface(&listp->av[n].value, buf);
|
||||||
|
switch (ret) {
|
||||||
|
case CFERROR: return CFERROR;
|
||||||
|
case CFOK:
|
||||||
|
len = snprintf(listp->av[n].key, sizeof listp->av[n].key - 1, "%u", n);
|
||||||
|
listp->av[n].key[len] = '\0';
|
||||||
|
++n;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cf_warn_node(__FILE__, __LINE__, node, NULL, "invalid interface rule %s", alloca_str_toprint(buf)); \
|
||||||
|
result |= CFSUB(ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
arg = NULL;
|
||||||
|
}
|
||||||
|
if (!*p)
|
||||||
|
break;
|
||||||
|
} else if (!arg)
|
||||||
|
arg = p;
|
||||||
|
}
|
||||||
|
if (*p) {
|
||||||
|
result |= CFARRAYOVERFLOW;
|
||||||
|
goto bye;
|
||||||
|
}
|
||||||
|
assert(n <= NELS(listp->av));
|
||||||
|
listp->ac = n;
|
||||||
|
}
|
||||||
|
bye:
|
||||||
|
if (listp->ac == 0)
|
||||||
|
result |= CFEMPTY;
|
||||||
|
else
|
||||||
|
result &= ~CFEMPTY;
|
||||||
|
return result;
|
||||||
|
}
|
1155
config_test.c
1155
config_test.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user