mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-22 01:49:35 +00:00
1259 lines
22 KiB
C
1259 lines
22 KiB
C
/*
|
|
* Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Internet Systems Consortium, Inc.
|
|
* PO Box 360
|
|
* Newmarket, NH 03857 USA
|
|
* <info@isc.org>
|
|
* http://www.isc.org/
|
|
*/
|
|
|
|
#include "data.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
struct string *
|
|
allocString(void)
|
|
{
|
|
struct string *result;
|
|
|
|
result = (struct string *)malloc(sizeof(struct string));
|
|
assert(result != NULL);
|
|
memset(result, 0, sizeof(struct string));
|
|
|
|
return result;
|
|
}
|
|
|
|
struct string *
|
|
makeString(int l, const char *s)
|
|
{
|
|
struct string *result;
|
|
|
|
result = allocString();
|
|
if (l < 0)
|
|
result->length = strlen(s);
|
|
else
|
|
result->length = (size_t)l;
|
|
if (result->length > 0) {
|
|
result->content = (char *)malloc(result->length + 1);
|
|
assert(result->content != NULL);
|
|
memcpy(result->content, s, result->length);
|
|
result->content[result->length] = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
struct string *
|
|
makeStringExt(int l, const char *s, char fmt)
|
|
{
|
|
switch (fmt) {
|
|
case 'Z':
|
|
/* zero-length */
|
|
return allocString();
|
|
|
|
case 'l': {
|
|
/* 32-bit signed integer */
|
|
int32_t x;
|
|
char buf[40];
|
|
|
|
assert(s != NULL);
|
|
assert(l > 3);
|
|
|
|
memcpy(&x, s, 4);
|
|
x = (int32_t)ntohl((uint32_t)x);
|
|
snprintf(buf, sizeof(buf), "%lld", (long long)x);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 'L': {
|
|
/* 32-bit unsigned integer */
|
|
uint32_t x;
|
|
char buf[40];
|
|
|
|
assert(s != NULL);
|
|
assert(l > 3);
|
|
|
|
memcpy(&x, s, 4);
|
|
x = ntohl(x);
|
|
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)x);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 's': {
|
|
/* 16-bit signed integer */
|
|
int16_t x;
|
|
char buf[20];
|
|
|
|
assert(s != NULL);
|
|
assert(l > 1);
|
|
|
|
memcpy(&x, s, 2);
|
|
x = (int16_t)ntohs((uint16_t)x);
|
|
snprintf(buf, sizeof(buf), "%hd", x);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 'S': {
|
|
/* 16-bit unsigned integer */
|
|
uint16_t x;
|
|
char buf[20];
|
|
|
|
assert(s != NULL);
|
|
assert(l > 1);
|
|
|
|
memcpy(&x, s, 2);
|
|
x = ntohs(x);
|
|
snprintf(buf, sizeof(buf), "%hu", x);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 'b': {
|
|
/* 8-bit signed integer */
|
|
int8_t x;
|
|
char buf[10];
|
|
|
|
assert(s != NULL);
|
|
assert(l > 0);
|
|
|
|
memcpy(&x, s, 1);
|
|
snprintf(buf, sizeof(buf), "%hhd", x);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 'B': {
|
|
/* 8-bit unsigned integer */
|
|
uint8_t x;
|
|
char buf[10];
|
|
|
|
assert(s != NULL);
|
|
assert(l > 0);
|
|
|
|
memcpy(&x, s, 1);
|
|
snprintf(buf, sizeof(buf), "%hhu", x);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 'f': {
|
|
/* flag (true or false) */
|
|
uint8_t f;
|
|
|
|
assert(s != NULL);
|
|
assert(l > 0);
|
|
|
|
f = *s;
|
|
return makeString(-1, f ? "true" : "false");
|
|
}
|
|
|
|
case 'X': {
|
|
/* binary data */
|
|
struct string *result;
|
|
size_t i;
|
|
char buf[4];
|
|
|
|
assert((l == 0) || (s != NULL));
|
|
|
|
result = allocString();
|
|
for (i = 0; i < l; i++) {
|
|
snprintf(buf, sizeof(buf), "%02hhx", (uint8_t)s[i]);
|
|
appendString(result, buf);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
case 'H': {
|
|
/* binary data with colons */
|
|
struct string *result;
|
|
size_t i;
|
|
isc_boolean_t first = ISC_TRUE;
|
|
char buf[4];
|
|
|
|
assert((l == 0) || (s != NULL));
|
|
|
|
result = allocString();
|
|
for (i = 0; i < l; i++) {
|
|
if (!first)
|
|
appendString(result, ":");
|
|
first = ISC_FALSE;
|
|
snprintf(buf, sizeof(buf), "%02hhx", (uint8_t)s[i]);
|
|
appendString(result, buf);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
case 'I': {
|
|
/* IPv4 address to text */
|
|
char buf[40 /* INET_ADDRSTRLEN == 26 */];
|
|
|
|
assert(l > 3);
|
|
assert(inet_ntop(AF_INET, s, buf, sizeof(buf)) != NULL);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 'i': {
|
|
/* IPv4 address to hexa */
|
|
uint8_t a[4];
|
|
char buf[10];
|
|
|
|
assert(inet_pton(AF_INET, s, a) == 1);
|
|
snprintf(buf, sizeof(buf), "%02hhx%02hhx%02hhx%02hhx",
|
|
a[0], a[1], a[2], a[3]);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case '6': {
|
|
/* IPv6 address */
|
|
char buf[80 /* INET6_ADDRSTRLEN == 46 */];
|
|
|
|
assert(l > 15);
|
|
assert(inet_ntop(AF_INET6, s, buf, sizeof(buf)) != NULL);
|
|
return makeString(-1, buf);
|
|
}
|
|
|
|
case 'd': {
|
|
/* FQDN to DNS wire format */
|
|
struct string *result;
|
|
const char *p;
|
|
const char *dot;
|
|
char ll;
|
|
|
|
assert(s[l] == '0');
|
|
|
|
result = allocString();
|
|
p = s;
|
|
while ((dot = strchr(p, '.')) != NULL) {
|
|
int len;
|
|
|
|
len = dot - p - 1;
|
|
if ((len & 0xc0) != 0)
|
|
return NULL;
|
|
if (dot - s >= l)
|
|
return NULL;
|
|
ll = len & 0x3f;
|
|
concatString(result, makeString(1, &ll));
|
|
concatString(result, makeString(len, p));
|
|
p = dot + 1;
|
|
if (p - s == l)
|
|
break;
|
|
}
|
|
if (dot == NULL) {
|
|
ll = 0;
|
|
concatString(result, makeString(1, &ll));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
struct string *
|
|
makeStringArray(int l, const char *s, char fmt)
|
|
{
|
|
struct string *result;
|
|
size_t step;
|
|
isc_boolean_t first = ISC_TRUE;
|
|
|
|
switch (fmt) {
|
|
case '6':
|
|
step = 16;
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
case 'I':
|
|
step = 4;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
step = 2;
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
case 'f':
|
|
step = 1;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
assert((l % step) == 0);
|
|
|
|
result = allocString();
|
|
while (l > 0) {
|
|
if (!first)
|
|
appendString(result, ",");
|
|
first = ISC_FALSE;
|
|
concatString(result, makeStringExt(l, s, fmt));
|
|
s += step;
|
|
l -= step;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void
|
|
appendString(struct string *s, const char *a)
|
|
{
|
|
size_t n;
|
|
|
|
assert(s != NULL);
|
|
|
|
if (a == NULL)
|
|
return;
|
|
n = strlen(a);
|
|
if (n == 0)
|
|
return;
|
|
s->content = (char *)realloc(s->content, s->length + n + 1);
|
|
assert(s->content != NULL);
|
|
memcpy(s->content + s->length, a, n);
|
|
s->length += n;
|
|
s->content[s->length] = 0;
|
|
}
|
|
|
|
void
|
|
concatString(struct string *s, const struct string *a)
|
|
{
|
|
assert(s != NULL);
|
|
assert(a != NULL);
|
|
|
|
s->content = (char *)realloc(s->content, s->length + a->length + 1);
|
|
assert(s->content != NULL);
|
|
memcpy(s->content + s->length, a->content, a->length);
|
|
s->length += a->length;
|
|
s->content[s->length] = 0;
|
|
}
|
|
|
|
isc_boolean_t
|
|
eqString(const struct string *s, const struct string *o)
|
|
{
|
|
assert(s != NULL);
|
|
assert(o != NULL);
|
|
|
|
if (s->length != o->length)
|
|
return ISC_FALSE;
|
|
if (s->length == 0)
|
|
return ISC_TRUE;
|
|
return ISC_TF(memcmp(s->content, o->content, s->length) == 0);
|
|
}
|
|
|
|
struct string *
|
|
quote(struct string *s)
|
|
{
|
|
struct string *result;
|
|
|
|
result = makeString(-1, "'");
|
|
concatString(result, s);
|
|
appendString(result, "'");
|
|
return result;
|
|
}
|
|
|
|
struct comment *
|
|
createComment(const char *line)
|
|
{
|
|
struct comment *comment;
|
|
|
|
assert(line != NULL);
|
|
|
|
comment = (struct comment *)malloc(sizeof(struct comment));
|
|
assert(comment != NULL);
|
|
memset(comment, 0, sizeof(struct comment));
|
|
|
|
comment->line = strdup(line);
|
|
|
|
return comment;
|
|
}
|
|
|
|
int64_t
|
|
intValue(const struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
assert(e->type == ELEMENT_INTEGER);
|
|
return e->value.int_value;
|
|
}
|
|
|
|
double
|
|
doubleValue(const struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
assert(e->type == ELEMENT_REAL);
|
|
return e->value.double_value;
|
|
}
|
|
|
|
isc_boolean_t
|
|
boolValue(const struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
assert(e->type == ELEMENT_BOOLEAN);
|
|
/* could check if 0 or 1 */
|
|
return e->value.bool_value;
|
|
}
|
|
|
|
struct string *
|
|
stringValue(struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
assert(e->type == ELEMENT_STRING);
|
|
return &e->value.string_value;
|
|
}
|
|
|
|
struct list *
|
|
listValue(struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
assert(e->type == ELEMENT_LIST);
|
|
return &e->value.list_value;
|
|
}
|
|
|
|
struct map *
|
|
mapValue(struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
assert(e->type == ELEMENT_MAP);
|
|
return &e->value.map_value;
|
|
}
|
|
|
|
struct element *
|
|
create(void)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = (struct element *)malloc(sizeof(struct element));
|
|
assert(elem != NULL);
|
|
memset(elem, 0, sizeof(struct element));
|
|
TAILQ_INIT(&elem->comments);
|
|
|
|
return elem;
|
|
}
|
|
|
|
struct element *
|
|
createInt(int64_t i)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = create();
|
|
elem->type = ELEMENT_INTEGER;
|
|
elem->value.int_value = i;
|
|
|
|
return elem;
|
|
}
|
|
|
|
struct element *
|
|
createDouble(double d)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = create();
|
|
elem->type = ELEMENT_REAL;
|
|
elem->value.double_value = d;
|
|
|
|
return elem;
|
|
}
|
|
|
|
struct element *
|
|
createBool(isc_boolean_t b)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = create();
|
|
elem->type = ELEMENT_BOOLEAN;
|
|
elem->value.bool_value = b;
|
|
|
|
return elem;
|
|
}
|
|
|
|
struct element *
|
|
createNull(void)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = create();
|
|
elem->type = ELEMENT_NULL;
|
|
|
|
return elem;
|
|
}
|
|
|
|
struct element *
|
|
createString(const struct string *s)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = create();
|
|
elem->type = ELEMENT_STRING;
|
|
elem->value.string_value = *s;
|
|
|
|
return elem;
|
|
}
|
|
|
|
struct element *
|
|
createList(void)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = create();
|
|
elem->type = ELEMENT_LIST;
|
|
TAILQ_INIT(&elem->value.list_value);
|
|
|
|
return elem;
|
|
}
|
|
|
|
struct element *
|
|
createMap(void)
|
|
{
|
|
struct element *elem;
|
|
|
|
elem = create();
|
|
elem->type = ELEMENT_MAP;
|
|
TAILQ_INIT(&elem->value.map_value);
|
|
|
|
return elem;
|
|
}
|
|
|
|
static void
|
|
reset(struct element *e)
|
|
{
|
|
e->type = 0;
|
|
e->kind = 0;
|
|
assert(e->key == NULL);
|
|
memset(&e->value, 0, sizeof(e->value));
|
|
}
|
|
|
|
void
|
|
resetInt(struct element *e, int64_t i)
|
|
{
|
|
assert(e != NULL);
|
|
|
|
reset(e);
|
|
e->type = ELEMENT_INTEGER;
|
|
e->value.int_value = i;
|
|
}
|
|
|
|
void
|
|
resetDouble(struct element *e, double d)
|
|
{
|
|
assert(e != NULL);
|
|
|
|
reset(e);
|
|
e->type = ELEMENT_REAL;
|
|
e->value.double_value = d;
|
|
}
|
|
|
|
void
|
|
resetBool(struct element *e, isc_boolean_t b)
|
|
{
|
|
assert(e != NULL);
|
|
|
|
reset(e);
|
|
e->type = ELEMENT_BOOLEAN;
|
|
e->value.bool_value = b;
|
|
}
|
|
|
|
void resetNull(struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
|
|
reset(e);
|
|
e->type = ELEMENT_NULL;
|
|
}
|
|
|
|
void
|
|
resetString(struct element *e, const struct string *s)
|
|
{
|
|
assert(e != NULL);
|
|
|
|
reset(e);
|
|
e->type = ELEMENT_STRING;
|
|
e->value.string_value = *s;
|
|
}
|
|
|
|
void
|
|
resetList(struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
|
|
reset(e);
|
|
e->type = ELEMENT_LIST;
|
|
TAILQ_INIT(&e->value.list_value);
|
|
}
|
|
|
|
void
|
|
resetMap(struct element *e)
|
|
{
|
|
assert(e != NULL);
|
|
|
|
reset(e);
|
|
e->type = ELEMENT_MAP;
|
|
TAILQ_INIT(&e->value.map_value);
|
|
}
|
|
|
|
void
|
|
resetBy(struct element *e, struct element *o)
|
|
{
|
|
assert(e != NULL);
|
|
assert(o != NULL);
|
|
|
|
reset(e);
|
|
e->type = o->type;
|
|
e->kind = o->kind;
|
|
e->skip = o->skip;
|
|
e->key = o->key;
|
|
o->key = NULL;
|
|
TAILQ_CONCAT(&e->comments, &o->comments);
|
|
|
|
switch (e->type) {
|
|
case ELEMENT_INTEGER:
|
|
e->value.int_value = o->value.int_value;
|
|
break;
|
|
case ELEMENT_REAL:
|
|
e->value.double_value = o->value.double_value;
|
|
break;
|
|
case ELEMENT_BOOLEAN:
|
|
e->value.bool_value = o->value.bool_value;
|
|
break;
|
|
case ELEMENT_STRING:
|
|
e->value.string_value = o->value.string_value;
|
|
break;
|
|
case ELEMENT_LIST:
|
|
TAILQ_INIT(&e->value.list_value);
|
|
TAILQ_CONCAT(&e->value.list_value, &o->value.list_value);
|
|
break;
|
|
case ELEMENT_MAP:
|
|
TAILQ_INIT(&e->value.map_value);
|
|
TAILQ_CONCAT(&e->value.map_value, &o->value.map_value);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
reset(o);
|
|
}
|
|
|
|
struct element *
|
|
listGet(struct element *l, int i)
|
|
{
|
|
struct element *elem;
|
|
|
|
assert(l != NULL);
|
|
assert(l->type == ELEMENT_LIST);
|
|
assert(i >= 0);
|
|
|
|
elem = TAILQ_FIRST(&l->value.list_value);
|
|
assert(elem != NULL);
|
|
assert(elem->key == NULL);
|
|
|
|
unsigned j;
|
|
for (j = i; j > 0; --j) {
|
|
elem = TAILQ_NEXT(elem);
|
|
assert(elem != NULL);
|
|
assert(elem->key == NULL);
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
void
|
|
listSet(struct element *l, struct element *e, int i)
|
|
{
|
|
assert(l != NULL);
|
|
assert(l->type == ELEMENT_LIST);
|
|
assert(e != NULL);
|
|
assert(i >= 0);
|
|
|
|
if (i == 0) {
|
|
TAILQ_INSERT_HEAD(&l->value.list_value, e);
|
|
} else {
|
|
struct element *prev;
|
|
|
|
prev = TAILQ_FIRST(&l->value.list_value);
|
|
assert(prev != NULL);
|
|
assert(prev->key == NULL);
|
|
|
|
unsigned j;
|
|
for (j = i; j > 1; --j) {
|
|
prev = TAILQ_NEXT(prev);
|
|
assert(prev != NULL);
|
|
assert(prev->key == NULL);
|
|
}
|
|
|
|
TAILQ_INSERT_AFTER(&l->value.list_value, prev, e);
|
|
}
|
|
}
|
|
|
|
void
|
|
listPush(struct element *l, struct element *e)
|
|
{
|
|
assert(l != NULL);
|
|
assert(l->type == ELEMENT_LIST);
|
|
assert(e != NULL);
|
|
|
|
TAILQ_INSERT_TAIL(&l->value.list_value, e);
|
|
}
|
|
|
|
void
|
|
listRemove(struct element *l, int i)
|
|
{
|
|
struct element *elem;
|
|
|
|
assert(l != NULL);
|
|
assert(l->type == ELEMENT_LIST);
|
|
assert(i >= 0);
|
|
|
|
elem = TAILQ_FIRST(&l->value.list_value);
|
|
assert(elem != NULL);
|
|
assert(elem->key == NULL);
|
|
|
|
unsigned j;
|
|
for (j = i; j > 0; --j) {
|
|
elem = TAILQ_NEXT(elem);
|
|
assert(elem != NULL);
|
|
assert(elem->key == NULL);
|
|
}
|
|
|
|
TAILQ_REMOVE(&l->value.list_value, elem);
|
|
}
|
|
|
|
size_t
|
|
listSize(const struct element *l)
|
|
{
|
|
struct element *elem;
|
|
size_t cnt;
|
|
|
|
assert(l != NULL);
|
|
assert(l->type == ELEMENT_LIST);
|
|
|
|
cnt = 0;
|
|
TAILQ_FOREACH(elem, &l->value.list_value) {
|
|
assert(elem->key == NULL);
|
|
cnt++;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
void
|
|
concat(struct element *l, struct element *o)
|
|
{
|
|
assert(l != NULL);
|
|
assert(l->type == ELEMENT_LIST);
|
|
assert(o != NULL);
|
|
assert(o->type == ELEMENT_LIST);
|
|
|
|
TAILQ_CONCAT(&l->value.list_value, &o->value.list_value);
|
|
}
|
|
|
|
struct element *
|
|
mapGet(struct element *m, const char *k)
|
|
{
|
|
struct element *elem;
|
|
|
|
assert(m != NULL);
|
|
assert(m->type == ELEMENT_MAP);
|
|
assert(k != NULL);
|
|
|
|
TAILQ_FOREACH(elem, &m->value.map_value) {
|
|
assert(elem->key != NULL);
|
|
if (strcmp(elem->key, k) == 0)
|
|
break;
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
void
|
|
mapSet(struct element *m, struct element *e, const char *k)
|
|
{
|
|
assert(m != NULL);
|
|
assert(m->type == ELEMENT_MAP);
|
|
assert(e != NULL);
|
|
assert(k != NULL);
|
|
#if 0
|
|
assert(mapGet(m, k) == NULL);
|
|
#endif
|
|
e->key = strdup(k);
|
|
assert(e->key != NULL);
|
|
TAILQ_INSERT_TAIL(&m->value.map_value, e);
|
|
}
|
|
|
|
void
|
|
mapRemove(struct element *m, const char *k)
|
|
{
|
|
struct element *elem;
|
|
|
|
assert(m != NULL);
|
|
assert(m->type == ELEMENT_MAP);
|
|
assert(k != NULL);
|
|
|
|
TAILQ_FOREACH(elem, &m->value.map_value) {
|
|
assert(elem->key != NULL);
|
|
if (strcmp(elem->key, k) == 0)
|
|
break;
|
|
}
|
|
|
|
assert(elem != NULL);
|
|
TAILQ_REMOVE(&m->value.map_value, elem);
|
|
}
|
|
|
|
isc_boolean_t
|
|
mapContains(const struct element *m, const char *k)
|
|
{
|
|
struct element *elem;
|
|
|
|
assert(m != NULL);
|
|
assert(m->type == ELEMENT_MAP);
|
|
assert(k != NULL);
|
|
|
|
TAILQ_FOREACH(elem, &m->value.map_value) {
|
|
assert(elem->key != NULL);
|
|
if (strcmp(elem->key, k) == 0)
|
|
break;
|
|
}
|
|
|
|
return ISC_TF(elem != NULL);
|
|
}
|
|
|
|
size_t
|
|
mapSize(const struct element *m)
|
|
{
|
|
struct element *elem;
|
|
size_t cnt;
|
|
|
|
assert(m != NULL);
|
|
assert(m->type == ELEMENT_MAP);
|
|
|
|
cnt = 0;
|
|
TAILQ_FOREACH(elem, &m->value.map_value) {
|
|
assert(elem->key != NULL);
|
|
cnt++;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
void
|
|
merge(struct element *m, struct element *o)
|
|
{
|
|
struct element *elem;
|
|
struct element *ne;
|
|
|
|
assert(m != NULL);
|
|
assert(m->type == ELEMENT_MAP);
|
|
assert(o != NULL);
|
|
assert(o->type == ELEMENT_MAP);
|
|
|
|
TAILQ_FOREACH_SAFE(elem, &o->value.map_value, ne) {
|
|
assert(elem->key != NULL);
|
|
TAILQ_REMOVE(&o->value.map_value, elem);
|
|
if (!mapContains(m, elem->key)) {
|
|
TAILQ_INSERT_TAIL(&m->value.map_value, elem);
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *
|
|
type2name(int t)
|
|
{
|
|
switch (t) {
|
|
case ELEMENT_NONE:
|
|
return "not initialized?";
|
|
case ELEMENT_INTEGER:
|
|
return "integer";
|
|
case ELEMENT_REAL:
|
|
return "real";
|
|
case ELEMENT_BOOLEAN:
|
|
return "boolean";
|
|
case ELEMENT_NULL:
|
|
return "(unused) null";
|
|
case ELEMENT_STRING:
|
|
return "string";
|
|
case ELEMENT_LIST:
|
|
return "list";
|
|
case ELEMENT_MAP:
|
|
return "map";
|
|
default:
|
|
#if 0
|
|
assert(0);
|
|
#endif
|
|
return "unknown?";
|
|
}
|
|
}
|
|
|
|
int
|
|
name2type(const char *n)
|
|
{
|
|
assert(n != NULL);
|
|
if (strcmp(n, "integer") == 0)
|
|
return ELEMENT_INTEGER;
|
|
if (strcmp(n, "real") == 0)
|
|
return ELEMENT_REAL;
|
|
if (strcmp(n, "boolean") == 0)
|
|
return ELEMENT_BOOLEAN;
|
|
if (strcmp(n, "null") == 0)
|
|
return ELEMENT_NULL;
|
|
if (strcmp(n, "string") == 0)
|
|
return ELEMENT_STRING;
|
|
if (strcmp(n, "list") == 0)
|
|
return ELEMENT_LIST;
|
|
if (strcmp(n, "map") == 0)
|
|
return ELEMENT_MAP;
|
|
#if 0
|
|
assert(0);
|
|
#endif
|
|
return ELEMENT_NONE;
|
|
}
|
|
|
|
void
|
|
print(FILE *fp, const struct element *e, isc_boolean_t skip, unsigned indent)
|
|
{
|
|
assert(fp != NULL);
|
|
assert(e != NULL);
|
|
|
|
switch (e->type) {
|
|
case ELEMENT_LIST:
|
|
printList(fp, &e->value.list_value, skip, indent);
|
|
return;
|
|
case ELEMENT_MAP:
|
|
printMap(fp, &e->value.map_value, skip, indent);
|
|
return;
|
|
case ELEMENT_STRING:
|
|
printString(fp, &e->value.string_value);
|
|
return;
|
|
case ELEMENT_INTEGER:
|
|
fprintf(fp, "%lld", (long long)e->value.int_value);
|
|
return;
|
|
case ELEMENT_REAL:
|
|
fprintf(fp, "%f", e->value.double_value);
|
|
return;
|
|
case ELEMENT_BOOLEAN:
|
|
if (e->value.bool_value)
|
|
fprintf(fp, "true");
|
|
else
|
|
fprintf(fp, "false");
|
|
return;
|
|
case ELEMENT_NULL:
|
|
fprintf(fp, "null");
|
|
return;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
addIndent(FILE *fp, int skip, unsigned indent)
|
|
{
|
|
unsigned sp;
|
|
|
|
if (skip) {
|
|
fprintf(fp, "//");
|
|
if (indent > 2)
|
|
for (sp = 0; sp < indent - 2; ++sp)
|
|
fprintf(fp, " ");
|
|
} else
|
|
for (sp = 0; sp < indent; ++sp)
|
|
fprintf(fp, " ");
|
|
}
|
|
|
|
void
|
|
printList(FILE *fp, const struct list *l, isc_boolean_t skip, unsigned indent)
|
|
{
|
|
struct element *elem;
|
|
struct comment *comment;
|
|
isc_boolean_t first;
|
|
|
|
assert(fp != NULL);
|
|
assert(l != NULL);
|
|
|
|
if (TAILQ_EMPTY(l)) {
|
|
fprintf(fp, "[ ]");
|
|
return;
|
|
}
|
|
|
|
fprintf(fp, "[\n");
|
|
first = ISC_TRUE;
|
|
TAILQ_FOREACH(elem, l) {
|
|
isc_boolean_t skip_elem = skip;
|
|
|
|
assert(elem->key == NULL);
|
|
if (!skip) {
|
|
skip_elem = elem->skip;
|
|
if (skip_to_end(elem)) {
|
|
if (!first)
|
|
fprintf(fp, "\n");
|
|
first = ISC_TRUE;
|
|
}
|
|
}
|
|
if (!first)
|
|
fprintf(fp, ",\n");
|
|
first = ISC_FALSE;
|
|
TAILQ_FOREACH(comment, &elem->comments) {
|
|
addIndent(fp, skip_elem, indent + 2);
|
|
fprintf(fp, "%s\n", comment->line);
|
|
}
|
|
addIndent(fp, skip_elem, indent + 2);
|
|
print(fp, elem, skip_elem, indent + 2);
|
|
}
|
|
fprintf(fp, "\n");
|
|
addIndent(fp, skip, indent);
|
|
fprintf(fp, "]");
|
|
}
|
|
|
|
void
|
|
printMap(FILE *fp, const struct map *m, isc_boolean_t skip, unsigned indent)
|
|
{
|
|
struct element *elem;
|
|
struct comment *comment;
|
|
isc_boolean_t first;
|
|
|
|
assert(fp != NULL);
|
|
assert(m != NULL);
|
|
|
|
if (TAILQ_EMPTY(m)) {
|
|
fprintf(fp, "{ }");
|
|
return;
|
|
}
|
|
|
|
fprintf(fp, "{\n");
|
|
first = ISC_TRUE;
|
|
TAILQ_FOREACH(elem, m) {
|
|
isc_boolean_t skip_elem = skip;
|
|
|
|
assert(elem->key != NULL);
|
|
if (!skip) {
|
|
skip_elem = elem->skip;
|
|
if (skip_to_end(elem)) {
|
|
if (!first)
|
|
fprintf(fp, "\n");
|
|
first = ISC_TRUE;
|
|
}
|
|
}
|
|
if (!first)
|
|
fprintf(fp, ",\n");
|
|
first = ISC_FALSE;
|
|
TAILQ_FOREACH(comment, &elem->comments) {
|
|
addIndent(fp, skip_elem, indent + 2);
|
|
fprintf(fp, "%s\n", comment->line);
|
|
}
|
|
addIndent(fp, skip_elem, indent + 2);
|
|
fprintf(fp, "\"%s\": ", elem->key);
|
|
print(fp, elem, skip_elem, indent + 2);
|
|
}
|
|
fprintf(fp, "\n");
|
|
addIndent(fp, skip, indent);
|
|
fprintf(fp, "}");
|
|
}
|
|
|
|
void
|
|
printString(FILE *fp, const struct string *s)
|
|
{
|
|
size_t i;
|
|
|
|
assert(fp != NULL);
|
|
assert(s != NULL);
|
|
|
|
fprintf(fp, "\"");
|
|
for (i = 0; i < s->length; i++) {
|
|
char c = *(s->content + i);
|
|
|
|
switch (c) {
|
|
case '"':
|
|
fprintf(fp, "\\\"");
|
|
break;
|
|
case '\\':
|
|
fprintf(fp, "\\\\");
|
|
break;
|
|
case '\b':
|
|
fprintf(fp, "\\b");
|
|
break;
|
|
case '\f':
|
|
fprintf(fp, "\\f");
|
|
break;
|
|
case '\n':
|
|
fprintf(fp, "\\n");
|
|
break;
|
|
case '\r':
|
|
fprintf(fp, "\\r");
|
|
break;
|
|
case '\t':
|
|
fprintf(fp, "\\t");
|
|
break;
|
|
default:
|
|
if ((c >= 0) && (c < 0x20)) {
|
|
fprintf(fp, "\\u%04x", (unsigned)c & 0xff);
|
|
} else {
|
|
fprintf(fp, "%c", c);
|
|
}
|
|
}
|
|
}
|
|
fprintf(fp, "\"");
|
|
}
|
|
|
|
isc_boolean_t
|
|
skip_to_end(const struct element *e)
|
|
{
|
|
do {
|
|
if (!e->skip)
|
|
return ISC_FALSE;
|
|
e = TAILQ_NEXT(e);
|
|
} while (e != NULL);
|
|
return ISC_TRUE;
|
|
}
|
|
|
|
struct element *
|
|
copy(struct element *e)
|
|
{
|
|
struct element *result;
|
|
struct comment *comment;
|
|
|
|
assert(e != NULL);
|
|
|
|
switch (e->type) {
|
|
case ELEMENT_INTEGER:
|
|
result = createInt(intValue(e));
|
|
break;
|
|
case ELEMENT_REAL:
|
|
result = createDouble(doubleValue(e));
|
|
break;
|
|
case ELEMENT_BOOLEAN:
|
|
result = createBool(boolValue(e));
|
|
break;
|
|
case ELEMENT_NULL:
|
|
result = createNull();
|
|
break;
|
|
case ELEMENT_STRING:
|
|
result = createString(stringValue(e));
|
|
break;
|
|
case ELEMENT_LIST:
|
|
result = copyList(e);
|
|
break;
|
|
case ELEMENT_MAP:
|
|
result = copyMap(e);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
result->kind = e->kind;
|
|
result->skip = e->skip;
|
|
/* don't copy key */
|
|
/* copy comments */
|
|
TAILQ_FOREACH(comment, &e->comments) {
|
|
/* do not reuse comment variable! */
|
|
struct comment *tmp;
|
|
|
|
tmp = createComment(comment->line);
|
|
TAILQ_INSERT_TAIL(&result->comments, tmp);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct element *
|
|
copyList(struct element *l)
|
|
{
|
|
struct element *result;
|
|
size_t i;
|
|
|
|
result = createList();
|
|
for (i = 0; i < listSize(l); i++)
|
|
listPush(result, copy(listGet(l, i)));
|
|
return result;
|
|
}
|
|
|
|
struct element *
|
|
copyMap(struct element *m)
|
|
{
|
|
struct element *result;
|
|
struct element *item;
|
|
|
|
result = createMap();
|
|
TAILQ_FOREACH(item, &m->value.map_value)
|
|
mapSet(result, copy(item), item->key);
|
|
return result;
|
|
}
|
|
|
|
struct handle *
|
|
mapPop(struct element *m)
|
|
{
|
|
struct element *item;
|
|
struct handle *h;
|
|
|
|
assert(m != NULL);
|
|
assert(m->type == ELEMENT_MAP);
|
|
|
|
h = (struct handle *)malloc(sizeof(struct handle));
|
|
assert(h != NULL);
|
|
memset(h, 0, sizeof(struct handle));
|
|
TAILQ_INIT(&h->values);
|
|
|
|
item = TAILQ_FIRST(&m->value.map_value);
|
|
assert(item != NULL);
|
|
assert(item->key != NULL);
|
|
h->key = strdup(item->key);
|
|
assert(h->key != NULL);
|
|
h->value = item;
|
|
|
|
TAILQ_REMOVE(&m->value.map_value, item);
|
|
|
|
return h;
|
|
}
|
|
|
|
void
|
|
derive(struct handle *src, struct handle *dst)
|
|
{
|
|
struct element *list;
|
|
struct element *item;
|
|
size_t i;
|
|
|
|
if (dst == NULL)
|
|
return;
|
|
list = dst->value;
|
|
assert(list != NULL);
|
|
assert(list->type == ELEMENT_LIST);
|
|
for (i = 0; i < listSize(list); i++) {
|
|
item = listGet(list, i);
|
|
assert(item != NULL);
|
|
assert(item->type == ELEMENT_MAP);
|
|
if (mapContains(item, src->key))
|
|
continue;
|
|
mapSet(item, copy(src->value), src->key);
|
|
}
|
|
}
|
|
|
|
struct string *
|
|
hexaValue(struct element *s)
|
|
{
|
|
struct string *h;
|
|
|
|
assert(s != NULL);
|
|
assert(s->type == ELEMENT_STRING);
|
|
|
|
h = stringValue(s);
|
|
assert(h->length >= 2);
|
|
|
|
/* string leading 0x */
|
|
return makeString(h->length - 2, h->content + 2);
|
|
}
|
|
|
|
struct element *
|
|
createHexa(struct string *h)
|
|
{
|
|
struct string *s;
|
|
|
|
assert(h != NULL);
|
|
|
|
s = makeString(-1, "0x");
|
|
concatString(s, h);
|
|
return createString(s);
|
|
}
|