/* ** OSSP cfg - Configuration Parsing ** Copyright (c) 1999-2002 Ralf S. Engelschall ** Copyright (c) 1999-2002 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/) ** ** 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 #include #include #include #include #include "cfg_main.h" #include "cfg_node.h" #include "cfg_fmt.h" cfg_rc_t cfg_node_create(cfg_t *cfg, cfg_node_t **node) { cfg_node_t *n; cfg_rc_t rc; if (node == NULL) return CFG_ERR_ARG; if ((rc = cfg_grid_alloc(cfg->grid_nodes, (void **)&n)) != CFG_OK) return rc; n->parent = NULL; n->lbroth = NULL; n->rbroth = NULL; n->child1 = NULL; n->srcname = NULL; n->srcpos = 0; n->type = CFG_NODE_TYPE_ARG; n->token = NULL; cfg_data_init(&n->data); *node = n; return CFG_OK; } cfg_rc_t cfg_node_destroy(cfg_t *cfg, cfg_node_t *node) { if (node == NULL) return CFG_ERR_ARG; if (node->token != NULL) free(node->token); if (node->srcname != NULL) free(node->srcname); cfg_grid_free(cfg->grid_nodes, node); return CFG_OK; } 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; if (node == NULL || node2 == NULL) return CFG_ERR_ARG; if ((rc = cfg_grid_alloc(cfg->grid_nodes, (void **)&n)) != CFG_OK) return rc; n->parent = node->parent; n->lbroth = node->lbroth; n->rbroth = node->rbroth; n->child1 = node->child1; n->srcname = (node->srcname != NULL ? strdup(node->srcname) : NULL); n->srcpos = node->srcpos; n->type = node->type; n->token = (node->token != NULL ? strdup(node->token) : NULL); cfg_data_copy(&node->data, &n->data); *node2 = n; return CFG_OK; } cfg_rc_t cfg_node_set(cfg_t *cfg, cfg_node_t *node, cfg_node_attr_t attr, ...) { va_list ap; if (node == NULL) return CFG_ERR_ARG; va_start(ap, attr); switch (attr) { 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; } 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; } case CFG_NODE_ATTR_CHILDS: { return CFG_ERR_USE; } case CFG_NODE_ATTR_NODES: { return CFG_ERR_USE; } case CFG_NODE_ATTR_DEPTH: { return CFG_ERR_USE; } case CFG_NODE_ATTR_SRCNAME: { if (node->srcname != NULL) free(node->srcname); node->srcname = (char *)va_arg(ap, char *); if (node->srcname != NULL) node->srcname = strdup(node->srcname); break; } case CFG_NODE_ATTR_SRCPOS: { node->srcpos = (int)va_arg(ap, int); break; } case CFG_NODE_ATTR_TYPE: { node->type = (cfg_node_type_t)va_arg(ap, cfg_node_type_t); break; } case CFG_NODE_ATTR_TOKEN: { if (node->token != NULL) free(node->token); node->token = (char *)va_arg(ap, char *); if (node->token != NULL) node->token = strdup(node->token); break; } case CFG_NODE_ATTR_DATA: { return CFG_ERR_USE; } default: return CFG_ERR_ARG; } va_end(ap); return CFG_OK; } 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; } cfg_rc_t cfg_node_get(cfg_t *cfg, cfg_node_t *node, cfg_node_attr_t attr, ...) { va_list ap; if (node == NULL) return CFG_ERR_ARG; va_start(ap, attr); switch (attr) { 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_SRCNAME: { char **name = (char **)va_arg(ap, char **); if (name == NULL) return CFG_ERR_ARG; *name = node->srcname; break; } case CFG_NODE_ATTR_SRCPOS: { int *pos = (int *)va_arg(ap, int *); if (pos == NULL) return CFG_ERR_ARG; *pos = node->srcpos; 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; 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; } default: return CFG_ERR_ARG; } va_end(ap); return CFG_OK; } cfg_rc_t cfg_node_root(cfg_t *cfg, cfg_node_t **node) { if (cfg == NULL || node == NULL) return CFG_ERR_ARG; *node = cfg->root; return CFG_OK; } 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 *); 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; fprintf(stderr, "step3: (1) cpSel=\"%s\", nSel=%d, nFilMin=%ld, nFilMax=%ld, nFound=%ld\n", cpSel, nSel, nFilMin, nFilMax, *nFound); if (spec[0] == '\0') { /* end of selection, node found */ *nFound++; fprintf(stderr, "step3: found node=0x%lx!!\n", (unsigned long)node); 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 { rc = cfg_node_select_step1(cfg, node, result_vec, result_len, spec); } return rc; } 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; fprintf(stderr, "step2: (1) cpSel=\"%s\", nSel=%d, nFilMin=%ld, nFilMax=%ld, nFound=%ld\n", cpSel, nSel, nFilMin, nFilMax, *nFound); if (strncmp(cpSel, ".", nSel) == 0) { /* current node (no-op) */ return cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound); } else if (strncmp(cpSel, "..", nSel) == 0) { /* parent node */ if ((node = node->parent) == NULL) return CFG_OK; if (node->type == CFG_NODE_TYPE_DIR) if ((node = node->parent) == NULL) return CFG_OK; return cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound); } else if (strncmp(cpSel, "....", nSel) == 0) { /* anchestor nodes */ while ((node = node->parent) != NULL) { if (node->type == CFG_NODE_TYPE_DIR) if ((node = node->parent) == NULL) break; if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK) return rc; } return CFG_OK; } else if (strncmp(cpSel, "-", nSel) == 0) { /* previous sibling node */ if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_LBROTH, &node)) != CFG_OK) return CFG_OK; return cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound); } else if (strncmp(cpSel, "--", nSel) == 0) { /* preceeding sibling nodes */ while ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_LBROTH, &node)) == CFG_OK) if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK) return rc; return CFG_OK; } else if (strncmp(cpSel, "+", nSel) == 0) { /* next sibling node */ if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node)) != CFG_OK) return CFG_OK; return cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound); } else if (strncmp(cpSel, "++", nSel) == 0) { /* following sibling nodes */ while ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node)) == CFG_OK) if ((rc = cfg_node_select_step3(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK) return rc; return CFG_OK; } else if (nSel == 0) { /* descendant nodes */ if ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_CHILD1, &node)) == CFG_OK) while ((rc = cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node)) == CFG_OK) if ((rc = cfg_node_select_step2(cfg, node, result_vec, result_len, spec, cpSel, nSel, nFilMin, nFilMax, nFound)) != CFG_OK) return rc; return CFG_OK; } else { /* child node */ fprintf(stderr, "step2: child node 0x%lx\n", (unsigned long)node); if ((node = node->child1) == NULL) return CFG_OK; if (node->type == CFG_NODE_TYPE_DIR) if ((node = node->child1) == NULL) return CFG_OK; fprintf(stderr, "step2: child node 0x%lx\n", (unsigned long)node); do { if (node->token != NULL) { size_t l; token = node->token; l = strlen(token); fprintf(stderr, "step2: child node: \"%s\"\n", token); if ( (l == 1 && l == nSel && 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 ((node = node->rbroth) != NULL); return CFG_OK; } return rc; } 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; fprintf(stderr, "step1(spec=\"%s\")\n", spec); /* 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; fprintf(stderr, " step1: (1) cpSel=\"%s\", nSel=%d, nFilMin=%ld, nFilMax=%ld\n", cpSel, nSel, nFilMin, nFilMax); /* perform pre-selection if filter range is relative to last element */ if (nFilMin < 0 || nFilMax < 0) { if (nFilMin == -1) nFilMin = LONG_MAX; if (nFilMax == -1) nFilMax = LONG_MAX; if (nFilMin < 0 || nFilMax < 0) { nFound = 0; if ((rc = cfg_node_select_step2(cfg, node, NULL, NULL, spec, cpSel, nSel, 1, LONG_MAX, &nFound)) != CFG_OK) return rc; if (nFilMin < 0) { nFilMin = nFound + nFilMin; if (nFilMin < 1) nFilMin = 1; } if (nFilMax < 0) { nFilMax = nFound + nFilMax; if (nFilMax < 1) nFilMax = 1; } } } fprintf(stderr, " step1: (2) cpSel=\"%s\", nSel=%d, nFilMin=%ld, nFilMax=%ld\n", cpSel, nSel, nFilMin, nFilMax); /* 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; return CFG_OK; } 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; 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; /* special case for absolute (start from root-node) selection */ cp = spec; if (cp[0] == '/') { node = cfg->root; cp++; } if (node == NULL) node = cfg->root; /* 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; } return CFG_OK; } 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; } 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; if (cfg == NULL) return CFG_ERR_ARG; if (node != NULL) { if ((rc = cb_fct_cmp(cfg, node, cb_ctx_cmp)) == CFG_OK) cb_fct_cb(cfg, node, cb_ctx_cb); if (rc != CFG_ERR_NDE) return rc; if (node->child1 != NULL) if ((rc = cfg_node_apply(cfg, node->child1, cb_fct_cmp, cb_ctx_cmp, cb_fct_cb, cb_ctx_cb)) != CFG_OK) return rc; if (node->rbroth != NULL) if ((rc = cfg_node_apply(cfg, node->rbroth, cb_fct_cmp, cb_ctx_cmp, cb_fct_cb, cb_ctx_cb)) != CFG_OK) return rc; } return CFG_OK; } cfg_rc_t cfg_node_cmp( cfg_t *cfg, cfg_node_t *node, void *token) { 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; } 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; 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; } cfg_rc_t cfg_node_unlink(cfg_t *cfg, cfg_node_t *node) { cfg_node_t *n; 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; }