/*
 * 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_coll.c,v 1.1 2005/05/26 12:08:19 vlm Exp $
 */
/*
 * Implementation of object Collection structure.
 */
#include "headers.h"
#include "ncnf_int.h"

/*
 * Adjust storage size of the collection.
 */
int
_ncnf_coll_adjust_size(void *mr, collection_t *coll, int new_count) {

	if(new_count > coll->entries) {

		/*
		 * Allocate and clear a few more entries.
		 */
		if(new_count > coll->size) {
			void *p;
			size_t new_size = mr
				? (coll->size + ((new_count + 3) & ~3))
				: new_count;

			p = realloc(coll->entry,
				new_size * sizeof(collection_entry));
			if(p == NULL)
				return -1;
			coll->entry = p;
			coll->size = new_size;
		}

		memset(&coll->entry[coll->entries],
			0,
			(new_count - coll->entries)
				* sizeof(collection_entry));

	} else {

		/*
		 * Destroy all inserted objects.
		 */

		for(; coll->entries > new_count;) {
			int num = --coll->entries;
			struct ncnf_obj_s *obj;

			obj = coll->entry[num].object;
			coll->entry[num].object = NULL;

			_ncnf_obj_destroy(obj);
		}

		if(new_count == 0) {
			if(coll->entry) {
				free(coll->entry);
				coll->entry = NULL;
				coll->size = 0;
			}
		}
	}

	return 0;
}


/*
 * Insert new object into collection.
 */
int
_ncnf_coll_insert(void *mr, collection_t *coll, struct ncnf_obj_s *obj, enum merge_flags merge_flags) {

	/* Check for duplicates */
	if(merge_flags & MERGE_DUPCHECK) {
		if(_ncnf_coll_get(coll,
			CG_TYPE_NOCASE | CG_NAME_NOCASE,
			(obj->obj_class == NOBJ_ATTRIBUTE
			  || obj->obj_class == NOBJ_LAZY_NOTIF)
			? obj->type
			: NULL,
				obj->value, NULL)) {
			errno = EEXIST;
			return -1;
		}
	}

	if(merge_flags & MERGE_PTRCHECK) {
		int ent;
		for(ent = 0; ent < coll->entries; ent++) {
			if(coll->entry[ent].object == obj) {
				errno = EEXIST;
				return -1;
			}
		}
	}

	if(_ncnf_coll_adjust_size(mr, coll, coll->entries + 1))
		/* ENOMEM */
		return -1;

	coll->entry[coll->entries++].object = obj;

	return 0;
}


/*
 * Add contents of the second collection to the first collection.
 */
int
_ncnf_coll_join(void *mr, collection_t *to, collection_t *from, struct ncnf_obj_s *parent, enum merge_flags merge_flags) {
	int to_idx, from_idx;

	/* Check for duplicates */
	if(merge_flags & MERGE_DUPCHECK) {
	    for(from_idx = 0; from_idx < from->entries; from_idx++) {
		struct ncnf_obj_s *obj = from->entry[from_idx].object;
		if(_ncnf_coll_get(to,
			CG_TYPE_NOCASE | CG_NAME_NOCASE,
			(obj->obj_class == NOBJ_ATTRIBUTE
			  || obj->obj_class == NOBJ_LAZY_NOTIF)
			? obj->type
			: NULL,
				obj->value, NULL)) {
			errno = EEXIST;
			return -1;
		}
	    }
	}

	if(_ncnf_coll_adjust_size(mr, to, to->entries + from->entries))
		/* ENOMEM */
		return -1;

	for(to_idx = to->entries, from_idx = 0;
		from_idx < from->entries;
			to_idx++, from_idx++) {

		to->entry[to_idx] = from->entry[from_idx];
		if(parent)
			to->entry[to_idx].object->parent = parent;
	}

	to->entries += from->entries;

	if(merge_flags & MERGE_EMPTYSRC)
		/* Empty the source collection */
		_ncnf_coll_clear(mr, from, 0);

	return 0;
}

/*
 * Clear specified collection.
 */
void
_ncnf_coll_clear(void *mr, collection_t *coll, int destroy) {

	/* [Martin 03/03/05]: bug 1917. memory leak.
	 * we must do this before _ncnf_coll_adjust_size
	 */
	if(destroy == 0)
		coll->entries = 0;

	_ncnf_coll_adjust_size(mr, coll, 0);

	coll->entries = 0;

}


/*
 * Search in given collection for object specified by type or value or both.
 */
struct ncnf_obj_s *
_ncnf_coll_get(collection_t *coll, enum cget_flags flags,
	const char *opt_type, const char *opt_name,
		void *iterator) {
	struct ncnf_obj_s *found = NULL;
	struct ncnf_obj_s *found_last = NULL;
	int (*type_compare)(const char *, const char *);
	int (*name_compare)(const char *, const char *);
	int ignore_class;
	int opt_type_len;
	int opt_name_len;
	int entries;
	int i;

	type_compare = (flags & CG_TYPE_NOCASE) ? strcasecmp : strcmp;
	name_compare = (flags & CG_NAME_NOCASE) ? strcasecmp : strcmp;

	ignore_class = (flags & CG_IGNORE_REFERENCES)
		? NOBJ_REFERENCE
		: -1;

	opt_type_len = opt_type ? strlen(opt_type) : 0;
	opt_name_len = opt_name ? strlen(opt_name) : 0;

	for(i = 0, entries = coll->entries; i < entries; i++) {
		struct ncnf_obj_s *cur = coll->entry[i].object;

		/*
		 * Filters.
		 */

		if(opt_type)
			if(bstr_len(cur->type) != opt_type_len
			|| type_compare(cur->type, opt_type))
				continue;
		if(opt_name)
			if(bstr_len(cur->value) != opt_name_len
			|| name_compare(cur->value, opt_name))
				continue;

		if(ignore_class == coll->entry[i].object->obj_class)
			continue;

		if(coll->entry[i].ignore_in_search)
			continue;

		/*
		 * Found something.
		 */

		if((flags & CG_MARK_UNSEARCHABLE))
			coll->entry[i].ignore_in_search = 1;

		if(iterator) {
			if(flags & CG_RETURN_POSITION) {
				*(int *)iterator = i;
				return cur;
			} else {
				struct ncnf_obj_s *iterator_obj
					= (struct ncnf_obj_s *)iterator;
				if(_ncnf_coll_insert(0,
					&iterator_obj->m_iterator_collection,
						cur, MERGE_NOFLAGS)) {
					return NULL;
				}
				found = iterator;
			}
		} else {
			if((flags & CG_RETURN_CHAIN)) {
				if(found) {
					found_last->chain_next = cur;
				} else {
					found = cur;
				}
				found_last = cur;
				found_last->chain_next = NULL;
				found_last->chain_cur = NULL;
			} else {
			    	return cur;
			}
		}
	}

	if(found == NULL)
		errno = ESRCH;

	return found;
}


/*
 * Remove all objects with mark equal to match_mark
 * from the collection.
 */
void
_ncnf_coll_remove_marked(collection_t *coll, int match_mark) {
	int shift = 0;
	int k;

	assert(coll && match_mark);

	for(k = 0; k < coll->entries; k++) {
		ncnf_obj *obj;

		if(shift)
			coll->entry[k] = coll->entry[k + shift];

		obj = coll->entry[k].object;

		if(obj->mark == match_mark) {
			/* Squeeze a little tighter */
			shift++;
			coll->entries--;
			coll->entry[k].object = NULL;
			_ncnf_obj_destroy(obj);
			k--;
		}
	}

}


int ncnf_coll_get_nentry(collection_t *coll)
{
	if(coll) {
		return coll->entries;
	} else {
		errno = EINVAL;
		return 0;
	}
}

struct ncnf_obj_s* ncnf_coll_get_obj_at(collection_t *coll, int idx)
{
	if(coll && idx < coll->entries && idx >= 0) {
		return coll->entry[idx].object;
	} else {
		errno = EINVAL;
		return NULL;
	}
}

#ifdef	MODULE_TEST

int
main(int ac, char **av) {
	static collection_t coll;
	struct ncnf_obj_s *obj;
	int ret;

	/*
	 * Test 1
	 */

	/* Mark */
	obj = _ncnf_obj_new(0, NOBJ_ATTRIBUTE, NULL, NULL, 0);
	assert(obj);
	obj->mark = 1;
	ret = _ncnf_coll_insert(0, &coll, obj, MERGE_DUPCHECK);
	assert(ret == 0);

	_ncnf_coll_remove_marked(&coll, 1);

	assert(coll.entries == 0);

	/*
	 * Test 2
	 */

	/* Mark */
	obj = _ncnf_obj_new(0, NOBJ_ATTRIBUTE,
		str2bstr("a", -1), str2bstr("marked", -1), 0);
	assert(obj);
	obj->mark = 1;
	ret = _ncnf_coll_insert(0, &coll, obj, MERGE_DUPCHECK);
	assert(ret == 0);

	/* Zero */
	obj = _ncnf_obj_new(0, NOBJ_ATTRIBUTE,
		str2bstr("b", -1), str2bstr("unmarked", -1), 0);
	assert(obj);
	obj->mark = 0;
	ret = _ncnf_coll_insert(0, &coll, obj, MERGE_DUPCHECK);
	assert(ret == 0);

	/* Mark */
	obj = _ncnf_obj_new(0, NOBJ_ATTRIBUTE,
		str2bstr("c", -1), str2bstr("marked", -1), 0);
	assert(obj);
	obj->mark = 1;
	ret = _ncnf_coll_insert(0, &coll, obj, MERGE_DUPCHECK);
	assert(ret == 0);

	_ncnf_coll_remove_marked(&coll, 1);

	assert(coll.entries == 1);
	assert(coll.entry[0].object->mark != 1);
	assert(!strcmp(coll.entry[0].object->type, "b"));

	/*
	 * Test 3
	 */

	/* Zero */
	obj = _ncnf_obj_new(0, NOBJ_ATTRIBUTE,
		str2bstr("c", -1), str2bstr("unmarked", -1), 0);
	assert(obj);
	obj->mark = 0;
	ret = _ncnf_coll_insert(0, &coll, obj, MERGE_DUPCHECK);
	assert(ret == 0);

	assert(coll.entries == 2);

	/* Mark */
	obj = _ncnf_obj_new(0, NOBJ_ATTRIBUTE,
		str2bstr("d", -1), str2bstr("marked", -1), 0);
	assert(obj);
	obj->mark = 1;
	ret = _ncnf_coll_insert(0, &coll, obj, MERGE_DUPCHECK);
	assert(ret == 0);

	/* Zero */
	obj = _ncnf_obj_new(0, NOBJ_ATTRIBUTE,
		str2bstr("g", -1), str2bstr("unmarked", -1), 0);
	assert(obj);
	obj->mark = 0;
	ret = _ncnf_coll_insert(0, &coll, obj, MERGE_DUPCHECK);
	assert(ret == 0);

	_ncnf_coll_remove_marked(&coll, 1);

	assert(coll.entries == 3);

	_ncnf_coll_adjust_size(0, &coll, 0);

	assert(coll.entries == 0);

	return 0;
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1