ossp-pkg/cfg/cfg_node.c
/*
** OSSP cfg - Configuration Parsing
** Copyright (c) 2002-2006 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2002-2006 The OSSP Project (http://www.ossp.org/)
**
** This file is part of OSSP cfg, a configuration parsing
** library which can be found at http://www.ossp.org/pkg/lib/cfg/.
**
** 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.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** cfg_node.c: configuration nodes
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <limits.h>
#include "cfg_main.h"
#include "cfg_node.h"
#include "cfg_fmt.h"
#include "cfg_global.h"
/* create a configuration node */
cfg_rc_t
cfg_node_create(
cfg_t *cfg,
cfg_node_t **node)
{
cfg_node_t *n;
cfg_rc_t rc;
/* argument sanity checking */
if (node == NULL)
return CFG_ERR_ARG;
/* allocate memory */
if ((rc = cfg_grid_alloc(cfg->grid_nodes, (void **)(void *)&n)) != CFG_OK)
return rc;
/* initialize node attributes */
n->owner = 0;
n->parent = NULL;
n->rbroth = NULL;
n->child1 = NULL;
n->type = CFG_NODE_TYPE_ARG;
n->token = NULL;
cfg_data_init(&n->data);
n->srcname = NULL;
n->srcpos = 0;
*node = n;
return CFG_OK;
}
/* destroy a configuration node */
cfg_rc_t
cfg_node_destroy(
cfg_t *cfg,
cfg_node_t *node)
{
/* argument sanity checking */
if (node == NULL)
return CFG_ERR_ARG;
/* destroy memory */
if ((node->owner & CFG_NODE_ATTR_TOKEN) && node->token != NULL)
free(node->token);
if ((node->owner & CFG_NODE_ATTR_SRCNAME) && node->srcname != NULL)
free(node->srcname);
cfg_data_kill(&node->data);
cfg_grid_free(cfg->grid_nodes, node);
return CFG_OK;
}
/* clone a node */
cfg_rc_t
cfg_node_clone(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_t **node2)
{
cfg_node_t *n;
cfg_rc_t rc;
/* argument sanity checking */
if (node == NULL || node2 == NULL)
return CFG_ERR_ARG;
/* allocate memory for new node */
if ((rc = cfg_grid_alloc(cfg->grid_nodes, (void **)(void *)&n)) != CFG_OK)
return rc;
/* clone node attributes */
n->owner = node->owner;
n->parent = node->parent;
n->rbroth = node->rbroth;
n->child1 = node->child1;
n->type = node->type;
n->srcpos = node->srcpos;
if (node->owner & CFG_NODE_ATTR_TOKEN)
n->token = (node->token != NULL ? strdup(node->token) : NULL);
else
n->token = node->token;
if (node->owner & CFG_NODE_ATTR_SRCNAME)
n->srcname = (node->srcname != NULL ? strdup(node->srcname) : NULL);
else
n->srcname = node->srcname;
cfg_data_copy(&node->data, &n->data);
/* store result */
*node2 = n;
return CFG_OK;
}
/* set a node attribute */
cfg_rc_t
cfg_node_set(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_attr_t attr,
...)
{
va_list ap;
/* argument sanity checking */
if (node == NULL)
return CFG_ERR_ARG;
/* dispatch into individual attribute handling */
va_start(ap, attr);
switch (attr & ~(CFG_ATTR_LOAN|CFG_ATTR_GIFT|CFG_ATTR_COPY)) {
case CFG_NODE_ATTR_PARENT: {
node->parent = (cfg_node_t *)va_arg(ap, cfg_node_t *);
break;
}
case CFG_NODE_ATTR_LBROTH: {
return CFG_ERR_USE; /* cannot be set, only get */
}
case CFG_NODE_ATTR_RBROTH: {
node->rbroth = (cfg_node_t *)va_arg(ap, cfg_node_t *);
break;
}
case CFG_NODE_ATTR_CHILD1: {
node->child1 = (cfg_node_t *)va_arg(ap, cfg_node_t *);
break;
}
case CFG_NODE_ATTR_CHILDL: {
return CFG_ERR_USE; /* cannot be set, only get */
}
case CFG_NODE_ATTR_CHILDS: {
return CFG_ERR_USE; /* cannot be set, only get */
}
case CFG_NODE_ATTR_NODES: {
return CFG_ERR_USE; /* cannot be set, only get */
}
case CFG_NODE_ATTR_DEPTH: {
return CFG_ERR_USE; /* cannot be set, only get */
}
case CFG_NODE_ATTR_TYPE: {
node->type = (cfg_node_type_t)va_arg(ap, cfg_node_type_t);
break;
}
case CFG_NODE_ATTR_TOKEN: {
char *value = (char *)va_arg(ap, char *);
if (attr & CFG_ATTR_COPY)
value = strdup(value);
if (node->owner & CFG_NODE_ATTR_TOKEN && node->token != NULL)
free(node->token);
node->token = value;
if (attr & (CFG_ATTR_COPY|CFG_ATTR_GIFT))
node->owner |= CFG_NODE_ATTR_TOKEN;
break;
}
case CFG_NODE_ATTR_DATA: {
return CFG_ERR_USE;
}
case CFG_NODE_ATTR_SRCNAME: {
char *value = (char *)va_arg(ap, char *);
if (attr & CFG_ATTR_COPY)
value = strdup(value);
if (node->owner & CFG_NODE_ATTR_SRCNAME && node->srcname != NULL)
free(node->srcname);
node->srcname = value;
if (attr & (CFG_ATTR_COPY|CFG_ATTR_GIFT))
node->owner |= CFG_NODE_ATTR_SRCNAME;
break;
}
case CFG_NODE_ATTR_SRCPOS: {
node->srcpos = (int)va_arg(ap, int);
break;
}
default:
return CFG_ERR_ARG;
}
va_end(ap);
return CFG_OK;
}
/* INTERNAL: calculate number of nodes */
static int
cfg_node_get_nodes(
cfg_node_t *node)
{
int n;
n = 0;
if (node != NULL) {
n++;
if ((node = node->child1) != NULL) {
n += cfg_node_get_nodes(node);
if ((node = node->rbroth) != NULL)
n += cfg_node_get_nodes(node);
}
}
return n;
}
/* get a node attribute */
cfg_rc_t
cfg_node_get(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_attr_t attr,
...)
{
va_list ap;
/* argument sanity checking */
if (node == NULL)
return CFG_ERR_ARG;
/* dispatch into individual attribute handling */
va_start(ap, attr);
switch (attr & ~(CFG_ATTR_LOAN|CFG_ATTR_GIFT|CFG_ATTR_COPY)) {
case CFG_NODE_ATTR_PARENT: {
cfg_node_t **n = (cfg_node_t **)va_arg(ap, void *);
if (n == NULL)
return CFG_ERR_ARG;
*n = node->parent;
break;
}
case CFG_NODE_ATTR_LBROTH: {
cfg_node_t **np = (cfg_node_t **)va_arg(ap, void *);
cfg_node_t *n;
if (np == NULL)
return CFG_ERR_ARG;
*np = NULL;
if ((n = node->parent) != NULL) {
if ((n = n->child1) != NULL) {
while (n->rbroth != node && n->rbroth != NULL)
n = n->rbroth;
if (n->rbroth == node)
*np = n;
}
}
break;
}
case CFG_NODE_ATTR_RBROTH: {
cfg_node_t **n = (cfg_node_t **)va_arg(ap, void *);
if (n == NULL)
return CFG_ERR_ARG;
*n = node->rbroth;
break;
}
case CFG_NODE_ATTR_CHILD1: {
cfg_node_t **n = (cfg_node_t **)va_arg(ap, void *);
if (n == NULL)
return CFG_ERR_ARG;
*n = node->child1;
break;
}
case CFG_NODE_ATTR_CHILDL: {
cfg_node_t **n = (cfg_node_t **)va_arg(ap, void *);
if (n == NULL)
return CFG_ERR_ARG;
if ((*n = node->child1) != NULL)
while ((*n)->rbroth != NULL)
*n = (*n)->rbroth;
break;
}
case CFG_NODE_ATTR_CHILDS: {
int *c = (int *)va_arg(ap, int *);
cfg_node_t *n;
if (c == NULL)
return CFG_ERR_ARG;
*c = 0;
if ((n = node->child1) != NULL) {
(*c)++;
while (n->rbroth != NULL) {
n = n->rbroth;
(*c)++;
}
}
break;
}
case CFG_NODE_ATTR_NODES: {
int *k = (int *)va_arg(ap, int *);
if (k == NULL)
return CFG_ERR_ARG;
*k = cfg_node_get_nodes(node);
break;
}
case CFG_NODE_ATTR_DEPTH: {
int *k = (int *)va_arg(ap, int *);
cfg_node_t *n;
if (k == NULL)
return CFG_ERR_ARG;
*k = 0;
n = node;
while ((n = n->parent) != NULL)
(*k)++;
break;
}
case CFG_NODE_ATTR_TYPE: {
cfg_node_type_t *type = (cfg_node_type_t *)va_arg(ap, void *);
if (type == NULL)
return CFG_ERR_ARG;
*type = node->type;
break;
}
case CFG_NODE_ATTR_TOKEN: {
char **token = (char **)va_arg(ap, char **);
if (token == NULL)
return CFG_ERR_ARG;
*token = node->token;
if (attr & CFG_ATTR_COPY)
*token = strdup(*token);
if (attr & CFG_ATTR_GIFT)
node->token = NULL;
break;
}
case CFG_NODE_ATTR_DATA: {
cfg_data_t **data = (cfg_data_t **)va_arg(ap, void *);
if (data == NULL)
return CFG_ERR_ARG;
*data = &(node->data);
break;
}
case CFG_NODE_ATTR_SRCNAME: {
char **name = (char **)va_arg(ap, char **);
if (name == NULL)
return CFG_ERR_ARG;
*name = node->srcname;
if (attr & CFG_ATTR_COPY)
*name = strdup(*name);
if (attr & CFG_ATTR_GIFT)
node->srcname = NULL;
break;
}
case CFG_NODE_ATTR_SRCPOS: {
int *pos = (int *)va_arg(ap, int *);
if (pos == NULL)
return CFG_ERR_ARG;
*pos = node->srcpos;
break;
}
default:
return CFG_ERR_ARG;
}
va_end(ap);
return CFG_OK;
}
/* get and/or set root node */
cfg_rc_t
cfg_node_root(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_t **node_old)
{
/* argument sanity checking */
if (cfg == NULL || (node == NULL && node_old == NULL))
return CFG_ERR_ARG;
/* optionally get old root node pointer */
if (node_old != NULL)
*node_old = cfg->root;
/* optionally set new root node pointer */
if (node != NULL)
cfg->root = node;
return CFG_OK;
}
/* forward declarations for internal stepping functions */
static cfg_rc_t
cfg_node_select_step1(
cfg_t *, cfg_node_t *, cfg_node_t ***, long *, const char *);
static cfg_rc_t
cfg_node_select_step2(
cfg_t *, cfg_node_t *, cfg_node_t ***, long *, const char *, const char *, size_t, long, long, long *);
/* INTERNAL: selection stepping function (part 3, matching node processing) */
static cfg_rc_t
cfg_node_select_step3(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_t ***result_vec, long *result_len,
const char *spec,
const char *cpSel, size_t nSel,
long nFilMin, long nFilMax,
long *nFound)
{
cfg_rc_t rc;
#if 0
fprintf(stderr, " step3: (1) cpSel=\"%s\", nSel=%d, spec=\"%s\", nFilMin=%ld, nFilMax=%ld, nFound=%ld\n",
cpSel, nSel, spec, nFilMin, nFilMax, *nFound);
#endif
if (spec[0] == '\0') {
/* end of selection, node found */
(*nFound)++;
#if 0
fprintf(stderr, " step3: (2) found node!!\n");
fprintf(stderr, " step3: current node=0x%lx type=%s token=\"%s\"\n",
(unsigned long)node,
(node != NULL ? (node->type == CFG_NODE_TYPE_SEQ ? "SEQ" : (node->type == CFG_NODE_TYPE_DIR ? "DIR" : "ARG")) : "<NULL>/.."),
(node != NULL ? (node->token != NULL ? node->token : "<NULL>") : "<NULL>/.."));
#endif
if (nFilMin <= *nFound && *nFound <= nFilMax) {
if (result_len != NULL) {
(*result_len)++;
if (result_vec != NULL) {
if ((*result_vec = (cfg_node_t **)realloc(*result_vec, sizeof(cfg_node_t *)*((*result_len)+1))) == NULL)
return CFG_ERR_SYS;
(*result_vec)[(*result_len)-1] = node;
(*result_vec)[(*result_len)] = NULL;
}
}
}
rc = CFG_OK;
}
else {
/* start over with next step */
rc = cfg_node_select_step1(cfg, node, result_vec, result_len, spec);
}
return rc;
}
/* INTERNAL: selection stepping function (part 2, node matching) */
static cfg_rc_t
cfg_node_select_step2(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_t ***result_vec, long *result_len,
const char *spec,
const char *cpSel, size_t nSel,
long nFilMin, long nFilMax,
long *nFound)
{
cfg_rc_t rc;
char *token;
cfg_node_t *node2;
cfg_node_type_t type;
#if 0
fprintf(stderr, " step2: (1) cpSel=\"%.*s\", nSel=%d, spec=\"%s\", nFilMin=%ld, nFilMax=%ld, nFound=%ld\n",
nSel, cpSel, nSel, spec, nFilMin, nFilMax, *nFound);
fprintf(stderr, " step2: current node=0x%lx type=%s token=\"%s\"\n",
(unsigned long)node,
(node != NULL ? (node->type == CFG_NODE_TYPE_SEQ ? "SEQ" : (node->type == CFG_NODE_TYPE_DIR ? "DIR" : "ARG")) : "<NULL>/.."),
(node != NULL ? (node->token != NULL ? node->token : "<NULL>") : "<NULL>/.."));
#endif
rc = CFG_OK;
if (nSel == 1 && strncmp(cpSel, ".", nSel) == 0) {
/* current node (no-op) */
#if 0
fprintf(stderr, " step2: search current node, starting at node 0x%lx\n", (unsigned long)node);
#endif
rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound);
}
else if (nSel == 2 && strncmp(cpSel, "..", nSel) == 0) {
/* parent node */
#if 0
fprintf(stderr, " step2: search parent node, starting at node 0x%lx\n", (unsigned long)node);
#endif
if (cfg_node_get(cfg, node, CFG_NODE_ATTR_PARENT, &node) == CFG_OK && node != NULL)
rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound);
}
else if (nSel == 4 && strncmp(cpSel, "....", nSel) == 0) {
/* anchestor nodes */
#if 0
fprintf(stderr, " step2: search ancestor nodes, starting at node 0x%lx\n", (unsigned long)node);
#endif
while (cfg_node_get(cfg, node, CFG_NODE_ATTR_PARENT, &node) == CFG_OK && node != NULL)
if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK)
break;
}
else if (nSel == 1 && strncmp(cpSel, "-", nSel) == 0) {
/* previous sibling node */
#if 0
fprintf(stderr, " step2: search previous sibling node, starting at node 0x%lx\n", (unsigned long)node);
#endif
if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_LBROTH, &node)) == CFG_OK && node != NULL)
rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound);
}
else if (nSel == 2 && strncmp(cpSel, "--", nSel) == 0) {
/* preceeding sibling nodes */
#if 0
fprintf(stderr, " step2: search preceeding sibling nodes, starting at node 0x%lx\n", (unsigned long)node);
#endif
while (cfg_node_get(cfg, node, CFG_NODE_ATTR_LBROTH, &node) == CFG_OK && node != NULL)
if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK)
break;
}
else if (nSel == 1 && strncmp(cpSel, "+", nSel) == 0) {
/* next sibling node */
#if 0
fprintf(stderr, " step2: search next sibling node, starting at node 0x%lx\n", (unsigned long)node);
#endif
if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node)) == CFG_OK && node != NULL)
rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound);
}
else if (nSel == 2 && strncmp(cpSel, "++", nSel) == 0) {
/* following sibling nodes */
#if 0
fprintf(stderr, " step2: search following sibling nodes, starting at node 0x%lx\n", (unsigned long)node);
#endif
while (cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node) == CFG_OK && node != NULL)
if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK)
break;
}
else if (nSel == 0 /* "//" */) {
/* descendant nodes */
#if 0
fprintf(stderr, " step2: search descendant nodes, starting at node 0x%lx\n", (unsigned long)node);
#endif
#if 0 /* FIXME */
if ((rc = cfg_node_apply(cfg, node, NULL, NULL, cfg_node_select_step2_descendant, &descendant_ctx)
cfg_rc_t cfg_node_select_step2_descendant(cfg_t *cfg, cfg_node_t *node, void *_ctx));
#endif
if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_CHILD1, &node)) == CFG_OK && node != NULL) {
do {
if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK)
return rc;
if ((rc = cfg_node_select_step2(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK)
return rc;
} while ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node)) == CFG_OK && node != NULL);
}
return CFG_OK;
}
else {
/* child node */
#if 0
fprintf(stderr, " step2: search child nodes, starting at node 0x%lx\n", (unsigned long)node);
#endif
if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_CHILD1, &node)) == CFG_OK && node != NULL) {
do {
if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_TOKEN, &token)) != CFG_OK)
continue;
if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_TYPE, &type)) == CFG_OK && type == CFG_NODE_TYPE_DIR) {
if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_CHILD1, &node2)) != CFG_OK || node2 == NULL)
continue;
if ((rc = cfg_node_get(cfg, node2, CFG_NODE_ATTR_TOKEN, &token)) != CFG_OK)
continue;
}
if (token != NULL) {
size_t l = strlen(token);
#if 0
fprintf(stderr, " step2: compare child node: token \"%s\"\n", token);
#endif
if ( (l == 1 && nSel == 1 && token[0] == '*')
|| (l == nSel && strncmp(token, cpSel, nSel) == 0)) {
if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK)
return rc;
}
}
} while ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node)) == CFG_OK && node != NULL);
}
return CFG_OK;
}
return rc;
}
/* INTERNAL: selection stepping function (part 1, selection parsing) */
static cfg_rc_t
cfg_node_select_step1(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_t ***result_vec, long *result_len,
const char *spec)
{
const char *cpSel; size_t nSel;
long nFilMin, nFilMax;
long n;
const char *cp;
char *cp2;
cfg_rc_t rc;
long nFound;
#if 0
fprintf(stderr, " step1: (0) spec=\"%s\"\n", spec);
#endif
/* stop processing if spec is empty */
if (spec[0] == '\0')
return CFG_OK;
/* determine selection step information */
cpSel = spec;
nSel = strcspn(cpSel, "[/");
cp = cpSel+nSel;
nFilMin = 1;
nFilMax = LONG_MAX;
if (*cp == '[') {
cp++;
n = strtol(cp, &cp2, 10);
if (cp2 > cp && n != 0)
nFilMin = n;
cp = cp2;
if (*cp == ',') {
cp++;
n = strtol(cp, &cp2, 10);
if (cp2 > cp && n != 0)
nFilMax = n;
cp = cp2;
}
else
nFilMax = nFilMin;
if (*cp != ']') {
cfg_error_info(cfg, CFG_ERR_ARG, "invalid selection specification filter range");
return CFG_ERR_ARG;
}
cp++;
}
if (*cp == '/')
cp++;
spec = cp;
#if 0
fprintf(stderr, " step1: (1) cpSel=\"%s\", nSel=%d, spec=\"%s\", nFilMin=%ld, nFilMax=%ld\n",
cpSel, nSel, spec, nFilMin, nFilMax);
#endif
/* perform pre-selection if filter range is relative to last element */
if (nFilMin < 0 || nFilMax < 0) {
nFound = 0;
#if 0
fprintf(stderr, " step1: pre-selection start\n");
#endif
if ((rc = cfg_node_select_step2(cfg, node, NULL, NULL, spec, cpSel, nSel, 1, LONG_MAX, &nFound)) != CFG_OK)
return rc;
#if 0
fprintf(stderr, " step1: (1b) cpSel=\"%s\", nSel=%d, spec=\"%s\", nFilMin=%ld, nFilMax=%ld nFound=%ld\n",
cpSel, nSel, spec, nFilMin, nFilMax, nFound);
fprintf(stderr, " step1: pre-selection end\n");
#endif
if (nFound == 0)
return CFG_OK;
if (nFilMin < 0) {
nFilMin = nFound + (nFilMin+1);
if (nFilMin < 1)
nFilMin = 1;
}
if (nFilMax < 0) {
nFilMax = nFound + (nFilMax+1);
if (nFilMax < 1)
nFilMax = 1;
}
}
#if 0
fprintf(stderr, " step1: (2) cpSel=\"%s\", nSel=%d, spec=\"%s\", nFilMin=%ld, nFilMax=%ld\n",
cpSel, nSel, spec, nFilMin, nFilMax);
#endif
/* perform real selection */
nFound = 0;
if ((rc = cfg_node_select_step2(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, &nFound)) != CFG_OK)
return rc;
#if 0
fprintf(stderr, " step1: (3) cpSel=\"%s\", nSel=%d, spec=\"%s\", nFilMin=%ld, nFilMax=%ld nFound=%ld\n",
cpSel, nSel, spec, nFilMin, nFilMax, nFound);
#endif
return CFG_OK;
}
/* select a node */
cfg_rc_t
cfg_node_select(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_t ***result_vec,
const char *fmt,
...)
{
cfg_rc_t rc;
va_list ap;
char *spec;
char *cp;
long result_len;
/* argument sanity checking */
if (cfg == NULL || result_vec == NULL || fmt == NULL)
return CFG_ERR_ARG;
/* on-the-fly create or just take over specification string */
va_start(ap, fmt);
spec = cfg_fmt_vasprintf(fmt, ap);
va_end(ap);
if (spec == NULL)
return CFG_ERR_FMT;
#if 0
fprintf(stderr, "select: (1) spec=\"%s\"\n", spec);
#endif
/* special cases for absolute (start from root-node) selection */
cp = spec;
if (cp[0] == '/') {
/* <any,"/x"> is same as <root,"x"> */
if (node != NULL)
return CFG_ERR_USE;
node = cfg->root;
cp++;
}
else if (node == NULL) {
/* <NULL, "..."> is same as <root, "..."> */
node = cfg->root;
}
#if 0
fprintf(stderr, "select: (2) spec=\"%s\"\n", cp);
#endif
/* initialize result node array */
result_len = 0;
if ((*result_vec = (cfg_node_t **)malloc(sizeof(cfg_node_t *)*(result_len+1))) == NULL)
return CFG_ERR_SYS;
(*result_vec)[result_len] = NULL;
/* perform the selection stepping */
if ((rc = cfg_node_select_step1(cfg, node, result_vec, &result_len, cp)) != CFG_OK) {
free(*result_vec);
return rc;
}
#if 0
fprintf(stderr, "select: (3) result_len=%ld\n", result_len);
#endif
return CFG_OK;
}
/* return next matching node (iteration) */
cfg_rc_t
cfg_node_find(
cfg_t *cfg,
cfg_node_t *node,
cfg_rc_t (*cb_fct_cmp)(cfg_t *, cfg_node_t *, void *),
void *cb_ctx_cmp,
cfg_node_t **cont)
{
/* FIXME */
return CFG_ERR_INT;
}
/* apply for each matching node (recursion) */
cfg_rc_t
cfg_node_apply(
cfg_t *cfg,
cfg_node_t *node,
cfg_rc_t (*cb_fct_cmp)(cfg_t *, cfg_node_t *, void *),
void *cb_ctx_cmp,
cfg_rc_t (*cb_fct_cb)(cfg_t *, cfg_node_t *, void *),
void *cb_ctx_cb)
{
cfg_rc_t rc;
cfg_node_t *child1;
cfg_node_t *rbroth;
/* argument sanity checking */
if (cfg == NULL)
return CFG_ERR_ARG;
/* short-circuit processing */
if (node == NULL)
return CFG_OK;
/* remember traversing informations from child and brother
(important in case the callbacks destroy it) */
child1 = node->child1;
rbroth = node->rbroth;
/* check whether the node matches */
if (cb_fct_cmp != NULL)
rc = cb_fct_cmp(cfg, node, cb_ctx_cmp);
else
rc = CFG_OK;
/* if node matched, apply optional callback on node */
if (rc == CFG_OK && cb_fct_cb != NULL)
if ((rc = cb_fct_cb(cfg, node, cb_ctx_cb)) != CFG_OK)
return rc;
/* recursively apply on child and brother nodes */
if (child1 != NULL)
if ((rc = cfg_node_apply(cfg, child1,
cb_fct_cmp, cb_ctx_cmp,
cb_fct_cb, cb_ctx_cb)) != CFG_OK)
return rc;
if (rbroth != NULL)
if ((rc = cfg_node_apply(cfg, rbroth,
cb_fct_cmp, cb_ctx_cmp,
cb_fct_cb, cb_ctx_cb)) != CFG_OK)
return rc;
return CFG_OK;
}
/* compare two nodes */
cfg_rc_t
cfg_node_cmp(
cfg_t *cfg,
cfg_node_t *node,
void *token)
{
/* argument sanity checking */
if (cfg == NULL || node == NULL || token == NULL)
return CFG_ERR_NDE;
if (node->token == NULL && token == NULL)
return CFG_OK;
if (node->token == NULL || token == NULL)
return CFG_ERR_NDE;
if (strcmp(node->token, (char *)token) == 0)
return CFG_OK;
return CFG_ERR_NDE;
}
/* link to nodes together */
cfg_rc_t
cfg_node_link(
cfg_t *cfg,
cfg_node_t *node,
cfg_node_attr_t attr,
cfg_node_t *node2)
{
cfg_node_t *n;
/* argument sanity checking */
if (node == NULL || node2 == NULL)
return CFG_ERR_ARG;
if (attr == CFG_NODE_ATTR_RBROTH) {
/* make node a rbroth */
n = node2;
n->parent = node->parent;
while (n->rbroth != NULL) {
n->parent = node->parent;
n = n->rbroth;
}
n->rbroth = node->rbroth;
node->rbroth = node2;
}
else if (attr == CFG_NODE_ATTR_CHILD1) {
/* make node a child1 */
n = node2;
n->parent = node;
while (n->rbroth != NULL) {
n->parent = node;
n = n->rbroth;
}
n->rbroth = node->child1;
node->child1 = node2;
}
/* FIXME more linkage possibilities */
else
return CFG_ERR_ARG;
return CFG_OK;
}
/* unlink a node from others */
cfg_rc_t
cfg_node_unlink(
cfg_t *cfg,
cfg_node_t *node)
{
cfg_node_t *n;
/* argument sanity checking */
if (node == NULL)
return CFG_ERR_ARG;
if (node->parent == NULL)
return CFG_OK;
if (node->parent->child1 == node) {
/* node was a child1 */
node->parent->child1 = node->rbroth;
}
else {
/* node was a rbroth */
n = node->parent->child1;
while (n->rbroth != node)
n = n->rbroth;
n->rbroth = node->rbroth;
}
return CFG_OK;
}