/*
 * 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_walk.c,v 1.2 2005/05/26 20:17:32 vlm Exp $
 */
#include "headers.h"
#include "ncnf_int.h"

struct ncnf_obj_s *
_ncnf_real_object(struct ncnf_obj_s *obj) {
	if(obj->obj_class == NOBJ_REFERENCE) {
		assert(obj->m_direct_reference);
		return obj->m_direct_reference;
	}
	return obj;
}

struct ncnf_obj_s *
_ncnf_get_obj(struct ncnf_obj_s *obj,
	const char *opt_type, const char *opt_name,
		enum ncnf_get_style style, enum _ncnf_get_flags flags) {

	struct ncnf_obj_s *iterator = NULL;
	struct ncnf_obj_s *found;
	enum cget_flags cget_flags;
	collection_t *coll = NULL;

retry:
	switch(obj->obj_class) {
	case NOBJ_ROOT:
	case NOBJ_COMPLEX:
		break;
	case NOBJ_REFERENCE:
		obj = _ncnf_real_object(obj);
		goto retry;
	case NOBJ_INVALID:
		assert(obj->obj_class != NOBJ_INVALID);
	default:
		errno = EINVAL;
		return NULL;
	}

	/* _ncnf_coll_get() flags */
	cget_flags = (flags & _NGF_IGNORE_REFS) ? CG_IGNORE_REFERENCES : 0;

	/*
	 * Initialize iterator, if necessary.
	 */
	switch(style) {
	case NCNF_FIRST_OBJECT:
		coll = &obj->m_collection[COLLECTION_OBJECTS];
		break;

	case NCNF_FIRST_ATTRIBUTE:
		coll = &obj->m_collection[COLLECTION_ATTRIBUTES];
		break;

	case NCNF_ITER_OBJECTS:
		coll = &obj->m_collection[COLLECTION_OBJECTS];
		goto get_iterator;

	case NCNF_ITER_ATTRIBUTES:
		coll = &obj->m_collection[COLLECTION_ATTRIBUTES];

	  get_iterator:
		/*
		 * Prepare iterator body for adding found items later.
		 */
		iterator = _ncnf_obj_new(0, NOBJ_ITERATOR, NULL, NULL, 0);
		if(iterator == NULL)
			return NULL;
		break;

	case NCNF_CHAIN_OBJECTS:

		coll = &obj->m_collection[COLLECTION_OBJECTS];
		cget_flags |= CG_RETURN_CHAIN;
		break;

	case NCNF_CHAIN_ATTRIBUTES:

		coll = &obj->m_collection[COLLECTION_ATTRIBUTES];
		cget_flags |= CG_RETURN_CHAIN;
		break;

	default:
		errno = EINVAL;
		return NULL;
	}

	/* Search in straight descendents */
	found = _ncnf_coll_get(coll, cget_flags,
		opt_type, opt_name, iterator);
	if(found)
		return found;


	/*
	 * If no objects placed into iterator,
	 * destroy iterator.
	 */
	if(iterator)
		_ncnf_obj_destroy(iterator);

	/*
	 * Search one level higher, if requested.
	 */
	if((flags & _NGF_RECURSIVE) && obj->parent) {
		found = _ncnf_get_obj(obj->parent, opt_type,
			opt_name, style, flags);
		if(found)
			return found;
	}

	errno = ESRCH;
	return NULL;
}


char *
_ncnf_get_attr(struct ncnf_obj_s *obj, const char *type) {
	struct ncnf_obj_s *found;

	if(obj->obj_class == NOBJ_ATTRIBUTE)
		return obj->value;

	found = _ncnf_get_obj(obj, type, NULL,
		NCNF_FIRST_ATTRIBUTE, _NGF_NOFLAGS);
	if(found)
		return found->value;

	return NULL;
}



int
_ncnf_walk_tree(struct ncnf_obj_s *obj, int (*callback)(struct ncnf_obj_s *obj, void *key), void *key) {
	enum collections_e c;
	int r;

	switch(obj->obj_class) {
	case NOBJ_ROOT:
	case NOBJ_COMPLEX:

		r = callback(obj, key);
		if(r) return r;

		for(c = 0; c < MAX_COLLECTIONS; c++) {
		  collection_t *coll = &obj->m_collection[c];
		  int i;

		  for(i = 0; i < coll->entries; i++) {
		    r = _ncnf_walk_tree(coll->entry[i].object,
				callback, key);
		    if(r) return r;
		  }
		}
		break;
	case NOBJ_ATTRIBUTE:
	case NOBJ_REFERENCE:
		r = callback(obj, key);
		if(r) return r;

		break;
	case NOBJ_INSERTION:
	case NOBJ_ITERATOR:
	case NOBJ_LAZY_NOTIF:

		/* Do nothing */
		break;
	case NOBJ_INVALID:
		assert(obj->obj_class != NOBJ_INVALID);
	}

	return 0;
}


void
_ncnf_iter_rewind(struct ncnf_obj_s *iter) {
	if(iter->obj_class == NOBJ_ITERATOR) {
		iter->m_iterator_position = 0;
	} else {
		iter->chain_cur = NULL;
	}
}

struct ncnf_obj_s *
_ncnf_iter_next(struct ncnf_obj_s *iter) {
	struct ncnf_obj_s *obj;
	static void *nothing_is_here = "";

	if(iter->obj_class == NOBJ_ITERATOR) {
		/*
		 * This is the iterator - the specially allocated
		 * entity having the pointers to the selected objects.
		 */

		if(iter->m_iterator_position <
			iter->m_iterator_collection.entries) {

			obj = iter->m_iterator_collection
				.entry[iter->m_iterator_position++].object;

		} else {
			obj = NULL;
		}

	} else {
		/*
		 * This is the first object of the chain.
		 * The first object contains the pointer to the
		 * next object, and so on. The pointer is shifted
		 * forward and the previous value is returned.
		 */

		if(iter->chain_cur != nothing_is_here) {

			if(iter->chain_cur) {
				obj = iter->chain_cur;
			} else {
				/* Rewind to the start of the chain */
				obj = iter;
			}

			/*
			 * Shift the pointer.
			 */
			iter->chain_cur = obj->chain_next;
			if(iter->chain_cur == NULL
			  || iter->chain_cur == obj) {
				iter->chain_cur = nothing_is_here;
			}

		} else {
			obj = NULL;
		}

	}

	if(obj == NULL)
		errno = ESRCH;

	return obj;
}





syntax highlighted by Code2HTML, v. 0.9.1