/*
* Copyright (c) 2001, 2002, 2003, 2004, 2005 Netli, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS 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 AUTHOR OR 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.
*
* $Id: ncnf_ql.c,v 1.1 2005/05/26 12:08:19 vlm Exp $
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "headers.h"
#include "asn_SET_OF.h"
#include "ncnf.h"
#include "ncnf_int.h"
#include "ncnf_ql.h"
#define QERROR(fmt, args...) do { \
if(nq) ncnf_delete_query(nq); \
if(errbuf && errlen) { \
ssize_t _ret; \
assert(*errlen > 0); \
_ret = snprintf(errbuf, *errlen, \
"%s(): " fmt, __func__, ##args); \
assert(_ret >= 0); \
*errlen = _ret; \
} \
return NULL; \
} while(0)
/*
* Required attribute with the value specification.
*/
typedef struct ncnf_attrreq_s {
char *Name;
char *Value;
sed_t *value_expression;/* If not given, use literal value */
} ncnf_attrreq_t;
static void ncnf_attrreq_free(ncnf_attrreq_t *ar) {
if(ar) {
if(ar->Name) free(ar->Name);
if(ar->Value) free(ar->Value);
if(ar->value_expression) sed_free(ar->value_expression);
}
}
struct ncnf_query_s {
ncnf_attrreq_t object_filter; /* Filter on object name */
A_SET_OF(ncnf_attrreq_t) required_attributes;
A_SET_OF(sed_t) _select;/* _select "/type-name/i" [{ ... }]; */
enum {
NQSC_DEFAULT, /* Semantically same as NQSC_NONE */
NQSC_NONE, /* No child objects are selected */
NQSC_SINGLE, /* Select the single level below the current */
NQSC_ALL, /* Select all underlying levels */
} _select_children;
A_SET_OF(struct ncnf_query_s) level_deeper;
};
ncnf_query_t *
ncnf_compile_query(ncnf_obj *qroot, char *errbuf, size_t *errlen) {
ncnf_query_t *nq = NULL;
ncnf_obj *iter, *attr, *obj;
if(!qroot) QERROR("missing query specification, qroot=%p", qroot);
nq = calloc(1, sizeof(*nq));
if(nq) {
nq->required_attributes.free = ncnf_attrreq_free;
nq->_select.free = sed_free;
nq->level_deeper.free = ncnf_delete_query;
} else {
QERROR("%s", strerror(errno));
}
if(ncnf_obj_type(qroot)) {
char *type = ncnf_obj_type(qroot);
char *value = ncnf_obj_name(qroot);
if(strcmp(value, "*") == 0) value = "/.*/";
nq->object_filter.Name = strdup(type);
nq->object_filter.Value = strdup(value);
if(!nq->object_filter.Name
|| !nq->object_filter.Value)
QERROR("%s", strerror(errno));
if(*value == '/') {
sed_t *se = sed_compile(value);
if(!se) QERROR("Cannot compile \"%s\" "
"at line %d: %s",
value, ncnf_obj_line(qroot),
strerror(errno));
nq->object_filter.value_expression = se;
}
} else {
/* This is the root, don't bother setting filters */
}
/*
* Gather all attribute value requirements.
*/
iter = ncnf_get_obj(qroot, NULL, NULL, NCNF_CHAIN_ATTRIBUTES);
while((attr = ncnf_iter_next(iter))) {
char *type = ncnf_obj_type(attr);
char *value = ncnf_obj_name(attr);
if(*type == '_') {
if(strcmp(type, "_select") == 0) {
sed_t *se;
if(*value != '/') {
errno = EINVAL;
QERROR("%s \"%s\" "
"regular expression expected "
"at line %d",
type, value, ncnf_obj_line(attr));
}
se = sed_compile(value);
if(!se) QERROR("Cannot compile \"%s\" "
"at line %d: %s",
value, ncnf_obj_line(attr),
strerror(errno));
if(ASN_SET_ADD(&nq->_select, se)) {
sed_free(se);
QERROR("%s", strerror(errno));
}
continue;
} else if(strcmp(type, "_select-children") == 0) {
if(nq->_select_children) {
errno = EINVAL;
QERROR("_select-children is getting "
"redefined at line %d",
ncnf_obj_line(attr));
}
if(!strcmp(value, "none")) {
nq->_select_children = NQSC_NONE;
} else if(!strcmp(value, "one")) {
nq->_select_children = NQSC_SINGLE;
} else if(!strcmp(value, "all")) {
nq->_select_children = NQSC_ALL;
} else {
errno = EINVAL;
QERROR("`%s \"%s\"` at line %d: "
"{none|one|all} expected",
type, value, ncnf_obj_line(attr));
}
continue;
}
errno = EINVAL;
QERROR("Unrecognized `%s \"%s\"` at line %d",
type, value, ncnf_obj_line(attr));
} else {
ncnf_attrreq_t *ar;
if(strcmp(value, "*") == 0) value = "/.*/";
ar = calloc(1, sizeof(*ar));
if(ASN_SET_ADD(&nq->required_attributes, ar)) {
if(ar) free(ar);
QERROR("%s", strerror(errno));
}
ar->Name = strdup(type);
ar->Value = strdup(value);
if(!ar->Name || !ar->Value)
QERROR("%s", strerror(errno));
if(*value == '/') {
sed_t *se = sed_compile(value);
if(!se) QERROR("Cannot compile \"%s\" "
"at line %d: %s",
value, ncnf_obj_line(attr),
strerror(errno));
ar->value_expression = se;
} else {
ar->value_expression = NULL;
}
}
}
/*
* Dig one level deeper.
*/
iter = ncnf_get_obj(qroot, NULL, NULL, NCNF_CHAIN_OBJECTS);
while((obj = ncnf_iter_next(iter))) {
ncnf_query_t *newnq = ncnf_compile_query(obj, errbuf, errlen);
if(ASN_SET_ADD(&nq->level_deeper, newnq)) {
if(newnq) ncnf_delete_query(newnq);
ncnf_delete_query(nq);
return NULL; /* Reporting has already been done */
}
}
return nq;
}
void
ncnf_delete_query(ncnf_query_t *nq) {
if(nq) {
if(nq->object_filter.Name) free(nq->object_filter.Name);
if(nq->object_filter.Value) free(nq->object_filter.Value);
if(nq->object_filter.value_expression)
sed_free(nq->object_filter.value_expression);
asn_set_empty(&nq->required_attributes);
asn_set_empty(&nq->_select);
asn_set_empty(&nq->level_deeper);
}
}
static int set_mark_func(ncnf_obj *obj, void *markv)
{ obj->mark = (int)markv; return 0; }
static void Mark(ncnf_obj *obj, int recurseDown) {
if(!obj) return;
if(!obj->mark) {
obj->mark = 1;
/* Mark the path to the root in the tree */
Mark(ncnf_obj_parent(obj), 0);
/* Mark the reference's path to the root */
if(ncnf_obj_real(obj) != obj)
Mark(ncnf_obj_real(obj), 0);
}
if(recurseDown && obj->mark != 2) {
ncnf_obj *o;
ncnf_obj *iter;
if(ncnf_obj_real(obj) != obj)
return;
obj->mark = 2;
iter = ncnf_get_obj(obj, NULL, NULL,
NCNF_CHAIN_ATTRIBUTES);
while((o = ncnf_iter_next(iter))) o->mark = 1;
iter = ncnf_get_obj(obj, NULL, NULL,
NCNF_ITER_OBJECTS);
assert(iter || errno == ESRCH);
while((o = ncnf_iter_next(iter))) Mark(o, recurseDown);
ncnf_destroy(iter);
}
}
void
ncnf_clear_query(ncnf_obj *qroot) {
if(!qroot) {
ncnf_walk_tree(qroot, set_mark_func, 0);
}
}
#undef DEBUG
#define DEBUG(fmt, args...) do { \
if(!debug) break; \
fprintf(stderr, fmt "\n", ##args); \
} while(0)
int
ncnf_exec_query(ncnf_obj *qroot, ncnf_query_t *nq, int debug) {
ncnf_obj *iter, *obj;
int i;
if(!qroot || !nq) {
errno = EINVAL;
return -1;
}
DEBUG("Entering %s \"%s\"",
ncnf_obj_type(qroot), ncnf_obj_name(qroot));
/*
* Check the object filter on entry.
*/
if(nq->object_filter.Name) {
char *type = ncnf_obj_type(qroot);
char *value = ncnf_obj_name(qroot);
DEBUG("Filtering against %s %s",
nq->object_filter.Name, nq->object_filter.Value);
if(strcmp(nq->object_filter.Name, type))
/* Name filter does not match */
return 0;
if(nq->object_filter.value_expression) {
if(!sed_exec(nq->object_filter.value_expression, value))
/* Expression is not satisfied */
return 0;
} else {
if(strcmp(nq->object_filter.Value, value))
/* Value filter does not match */
return 0;
}
} else {
/* This is supposed to be a root object. */
}
DEBUG("Enter confirmed");
/*
* Check that no required_attributes result in a mismatch.
*/
for(i = 0; i < nq->required_attributes.count; i++) {
ncnf_attrreq_t *ar = nq->required_attributes.array[i];
DEBUG("Against %s \"%s\"", ar->Name, ar->Value);
if(ar->value_expression) {
ncnf_obj *attr;
iter = ncnf_get_obj(qroot, NULL, NULL,
NCNF_CHAIN_ATTRIBUTES);
while((attr = ncnf_iter_next(iter))) {
char *value = ncnf_obj_name(attr);
if(sed_exec(ar->value_expression, value))
break;
}
if(!attr) {
/* This attribute shall be present */
return 0;
}
} else if(*ar->Value) {
ncnf_obj *attr = ncnf_get_obj(qroot,
ar->Name, ar->Value,
NCNF_CHAIN_ATTRIBUTES);
if(!attr) {
/* This attribute shall be present */
return 0;
}
} else {
ncnf_obj *attr = ncnf_get_obj(qroot,
ar->Name, NULL, NCNF_FIRST_ATTRIBUTE);
if(attr) {
/* This attribute should not be present */
return 0;
}
}
}
/*
* Mark the attributes described by _select.
* NCNF entities will be selected separately.
*/
iter = ncnf_get_obj(qroot, NULL, NULL, NCNF_ITER_ATTRIBUTES);
while((obj = ncnf_iter_next(iter))) {
switch(nq->_select_children) {
case NQSC_ALL:
case NQSC_SINGLE:
Mark(obj, 0);
continue;
default:
break;
}
for(i = 0; i < nq->_select.count; i++)
if(sed_exec(nq->_select.array[i], ncnf_obj_type(obj)))
Mark(obj, 0);
}
/*
* Process the rest of the nesting levels.
*/
iter = ncnf_get_obj(qroot, NULL, NULL, NCNF_CHAIN_OBJECTS);
while((obj = ncnf_iter_next(iter))) {
/*
* Execute the _select statements.
*/
switch(nq->_select_children) {
case NQSC_ALL:
case NQSC_SINGLE:
if(ncnf_obj_real(obj) == obj) {
ncnf_obj *attr;
ncnf_obj *chain;
chain = ncnf_get_obj(obj, NULL, NULL,
NCNF_CHAIN_ATTRIBUTES);
DEBUG("Marking %s \"%s\"",
ncnf_obj_type(obj), ncnf_obj_name(obj));
/* Mark this single level, or all levels */
Mark(obj, nq->_select_children == NQSC_ALL);
/* Select this level's attributes */
while((attr = ncnf_iter_next(chain)))
Mark(attr, 0);
} else {
Mark(obj, 0);
}
break;
default:
DEBUG("Marking selected in %s \"%s\" against %s \"%s\"",
ncnf_obj_type(obj),
ncnf_obj_name(obj),
nq->object_filter.Name,
nq->object_filter.Value);
for(i = 0; i < nq->_select.count; i++)
if(sed_exec(nq->_select.array[i], ncnf_obj_type(obj)))
Mark(obj, 0);
}
/*
* Process with deeper object levels.
*/
for(i = 0; i < nq->level_deeper.count; i++)
if(ncnf_exec_query(obj, nq->level_deeper.array[i],
debug))
return -1;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1