diff options
Diffstat (limited to 'common/collection/collection.c')
-rw-r--r-- | common/collection/collection.c | 3056 |
1 files changed, 0 insertions, 3056 deletions
diff --git a/common/collection/collection.c b/common/collection/collection.c deleted file mode 100644 index 87ac32124..000000000 --- a/common/collection/collection.c +++ /dev/null @@ -1,3056 +0,0 @@ -/* - COLLECTION LIBRARY - - Implementation of the collection library interface. - - Copyright (C) Dmitri Pal <dpal@redhat.com> 2009 - - Collection Library is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Collection Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with Collection Library. If not, see <http://www.gnu.org/licenses/>. -*/ - -#define _GNU_SOURCE -#include <string.h> -#include <stdlib.h> -#include <errno.h> -#include <ctype.h> -#include <time.h> -#include "config.h" -#include "trace.h" - -/* The collection should use the real structures */ -#include "collection_priv.h" -#include "collection.h" - - -/* Internal constants defined to denote actions that can be performed by find handler */ -#define COLLECTION_ACTION_FIND 1 -#define COLLECTION_ACTION_DEL 2 -#define COLLECTION_ACTION_UPDATE 3 -#define COLLECTION_ACTION_GET 4 - - -/* Special internal error code to indicate that collection search was interrupted */ -#define EINTR_INTERNAL 10000 - - -/* Potential subject for management with libtools */ -#define DATE_FORMAT "%c" - -#define TIME_ARRAY_SIZE 100 - -/* Magic numbers for hashing */ -#if SIZEOF_LONG == 8 - #define FNV1a_prime 1099511628211ul - #define FNV1a_base 14695981039346656037ul -#elif SIZEOF_LONG_LONG == 8 - #define FNV1a_prime 1099511628211ull - #define FNV1a_base 14695981039346656037ull -#else - #error "Platform cannot support 64-bit constant integers" -#endif - -/* Struct used for passing parameter for update operation */ -struct update_property { - int type; - void *data; - int length; - int found; -}; - -/* This struct is used to construct path - * to an item in the collection (tree) - */ -struct path_data { - char *name; - int length; - struct path_data *previous_path; -}; - -/* Structure to keep data needed to - * copy collection - * while traversing it - */ -struct col_copy { - int mode; - struct path_data *current_path; - char *given_name; - int given_len; - col_copy_cb copy_cb; - void *ext_data; -}; - -/******************** FUNCTION DECLARATIONS ****************************/ - -/* Have to declare those due to function cross referencing */ -static int col_find_item_and_do(struct collection_item *ci, - const char *property_to_find, - int type, - int mode_flags, - col_item_fn item_handler, - void *custom_data, - int action); - -/* Traverse callback for find & delete function */ -static int col_act_traverse_handler(struct collection_item *head, - struct collection_item *previous, - struct collection_item *current, - void *passed_traverse_data, - col_item_fn user_item_handler, - void *custom_data, - int *stop); - -/* Traverse handler to find parent of the item */ -static int col_parent_traverse_handler(struct collection_item *head, - struct collection_item *previous, - struct collection_item *current, - void *traverse_data, - col_item_fn user_item_handler, - void *custom_data, - int *stop); - -/* Traverse callback signature */ -typedef int (*internal_item_fn)(struct collection_item *head, - struct collection_item *previous, - struct collection_item *current, - void *traverse_data, - col_item_fn user_item_handler, - void *custom_data, - int *stop); -/* Function to walk_items */ -static int col_walk_items(struct collection_item *ci, - int mode_flags, - internal_item_fn traverse_handler, - void *traverse_data, - col_item_fn user_item_handler, - void *custom_data, - unsigned *depth); - -/* Function to get sub collection */ -static int col_get_subcollection(const char *property, - int property_len, - int type, - void *data, - int length, - void *found, - int *dummy); - -/* Function to destroy collection */ -void col_destroy_collection(struct collection_item *ci); - -/******************** SUPPLEMENTARY FUNCTIONS ****************************/ -/* BASIC OPERATIONS */ - -/* Function that checks if property can be added */ -static int col_validate_property(const char *property) -{ - TRACE_FLOW_STRING("col_validate_property", "Entry point."); - /* Only alpha numeric characters are allowed in names of the properties */ - int invalid = 0; - const char *check; - - check = property; - while (*check != '\0') { - /* It turned out that limiting collection charcters is bad */ - if ((*check < ' ') || (*check == '!')) { - invalid = 1; - break; - } - check++; - } - TRACE_FLOW_NUMBER("col_validate_property. Returning ", invalid); - return invalid; -} - -/* Function that cleans the item */ -void col_delete_item(struct collection_item *item) -{ - struct collection_item *other_collection; - - TRACE_FLOW_STRING("col_delete_item","Entry point."); - - if (item == NULL) { - TRACE_FLOW_STRING("col_delete_item","Nothing to delete!"); - return; - } - - /* Handle external or embedded collection */ - if(item->type == COL_TYPE_COLLECTIONREF) { - /* Our data is a pointer to a whole external collection so dereference - * it or delete */ - other_collection = *((struct collection_item **)(item->data)); - col_destroy_collection(other_collection); - } - - TRACE_INFO_STRING("Deleting property:", item->property); - TRACE_INFO_NUMBER("Type:", item->type); - - if (item->property != NULL) free(item->property); - if (item->data != NULL) free(item->data); - - free(item); - - TRACE_FLOW_STRING("col_delete_item","Exit."); -} - -/* A generic function to allocate a property item */ -int col_allocate_item(struct collection_item **ci, const char *property, - const void *item_data, int length, int type) -{ - struct collection_item *item = NULL; - - TRACE_FLOW_STRING("col_allocate_item", "Entry point."); - TRACE_INFO_NUMBER("Will be using type:", type); - - /* Check the length */ - if (length >= COL_MAX_DATA) { - TRACE_ERROR_STRING("col_allocate_item", "Data to long."); - return EMSGSIZE; - } - - if (col_validate_property(property)) { - TRACE_ERROR_STRING("Invalid chracters in the property name", property); - return EINVAL; - } - - /* Allocate memory for the structure */ - item = (struct collection_item *)malloc(sizeof(struct collection_item)); - if (item == NULL) { - TRACE_ERROR_STRING("col_allocate_item", "Malloc failed."); - return ENOMEM; - } - - /* After we initialize members we can use delete_item() in case of error */ - item->next = NULL; - item->property = NULL; - item->data = NULL; - TRACE_INFO_NUMBER("About to set type to:", type); - item->type = type; - - /* Copy property */ - item->property = strdup(property); - if (item->property == NULL) { - TRACE_ERROR_STRING("col_allocate_item", "Failed to dup property."); - col_delete_item(item); - return ENOMEM; - } - - item->phash = col_make_hash(property, 0, &(item->property_len)); - TRACE_INFO_NUMBER("Item hash", item->phash); - TRACE_INFO_NUMBER("Item property length", item->property_len); - TRACE_INFO_NUMBER("Item property strlen", strlen(item->property)); - - /* Deal with data */ - item->data = malloc(length); - if (item->data == NULL) { - TRACE_ERROR_STRING("col_allocate_item", "Failed to dup data."); - col_delete_item(item); - return ENOMEM; - } - - memcpy(item->data, item_data, length); - item->length = length; - - /* Make sure that data is NULL terminated in case of string */ - if (type == COL_TYPE_STRING) ((char *)(item->data))[length-1] = '\0'; - - *ci = item; - - TRACE_INFO_STRING("Item property", item->property); - TRACE_INFO_NUMBER("Item property type", item->type); - TRACE_INFO_NUMBER("Item data length", item->length); - TRACE_FLOW_STRING("col_allocate_item", "Success exit."); - return EOK; -} - -/* Structure used to find things in collection */ -struct property_search { - const char *property; - uint64_t hash; - struct collection_item *parent; - int index; - int count; - int found; - int use_type; - int type; -}; - -/* Find the parent of the item with given name */ -static int col_find_property(struct collection_item *collection, - const char *refprop, - int idx, - int use_type, - int type, - struct collection_item **parent) -{ - struct property_search ps; - int i = 0; - unsigned depth = 0; - - TRACE_FLOW_STRING("col_find_property", "Entry."); - - *parent = NULL; - - ps.property = refprop; - ps.hash = FNV1a_base; - ps.parent = NULL; - ps.index = idx; - ps.count = 0; - ps.found = 0; - ps.use_type = use_type; - ps.type = type; - - /* Create hash of the string to search */ - while(refprop[i] != 0) { - ps.hash = ps.hash ^ toupper(refprop[i]); - ps.hash *= FNV1a_prime; - i++; - } - - /* We do not care about error here */ - (void)col_walk_items(collection, COL_TRAVERSE_ONELEVEL, - col_parent_traverse_handler, - (void *)parent, NULL, (void *)&ps, - &depth); - - if (*parent) { - /* Item is found in the collection */ - TRACE_FLOW_STRING("col_find_property", "Exit - item found"); - return 1; - } - - /* Item is not found */ - TRACE_FLOW_STRING("col_find_property", "Exit - item NOT found"); - return EOK; -} - - - -/* Insert item into the current collection */ -int col_insert_item_into_current(struct collection_item *collection, - struct collection_item *item, - int disposition, - const char *refprop, - int idx, - unsigned flags) -{ - struct collection_header *header = NULL; - struct collection_item *parent = NULL; - struct collection_item *current = NULL; - int refindex = 0; - - TRACE_FLOW_STRING("col_insert_item_into_current", "Entry point"); - - /* Do best effort on the item */ - if ((!item) || (item->next)) { - TRACE_ERROR_STRING("Passed in item is invalid", ""); - return EINVAL; - } - - if (collection == NULL) { - TRACE_INFO_STRING("col_insert_item_into_current", - "Collection accepting is NULL"); - if (item->type == COL_TYPE_COLLECTION) { - /* This is a special case of self creation */ - TRACE_INFO_STRING("col_insert_item_into_current", - "Adding header item to new collection."); - collection = item; - } - else { - TRACE_ERROR_STRING("Passed in item is invalid", ""); - return EINVAL; - } - } - else { - /* We can add items only to collections */ - if (collection->type != COL_TYPE_COLLECTION) { - TRACE_ERROR_STRING("Attempt to add item to non collection.",""); - TRACE_ERROR_STRING("Collection name:", collection->property); - TRACE_ERROR_NUMBER("Collection type:", collection->type); - return EINVAL; - } - } - - /* After processing flags we can process disposition */ - - header = (struct collection_header *)collection->data; - - /* Check flags first */ - switch(flags) { - case COL_INSERT_NOCHECK: /* No check - good just fall through */ - TRACE_INFO_STRING("Insert without check", ""); - break; - case COL_INSERT_DUPOVER: /* Find item and overwrite - ignore disposition */ - if (col_find_property(collection, item->property, 0, 0, 0, &parent)) { - current = parent->next; - item->next = current->next; - parent->next = item; - if (header->last == current) header->last = item; - col_delete_item(current); - /* Deleted one added another - count stays the same! */ - TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit"); - return EOK; - } - /* Not found so we fall thorough and add as requested */ - break; - - case COL_INSERT_DUPOVERT: /* Find item by name and type and overwrite - ignore disposition */ - if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) { - current = parent->next; - item->next = current->next; - parent->next = item; - if (header->last == current) header->last = item; - col_delete_item(current); - /* Deleted one added another - count stays the same! */ - TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit"); - return EOK; - } - /* Not found so we fall thorough and add as requested */ - break; - - case COL_INSERT_DUPERROR: if (col_find_property(collection, item->property, 0, 0, 0, &parent)) { - /* Return error */ - TRACE_ERROR_NUMBER("Duplicate property", EEXIST); - return EEXIST; - } - break; - - case COL_INSERT_DUPERRORT: if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) { - /* Return error */ - TRACE_ERROR_NUMBER("Duplicate property of the same type", EEXIST); - return EEXIST; - } - break; - - case COL_INSERT_DUPMOVE: /* Find item and delete */ - if (col_find_property(collection, item->property, 0, 0, 0, &parent)) { - current = parent->next; - parent->next = current->next; - if (header->last == current) header->last = parent; - col_delete_item(current); - header->count--; - } - /* Now add item according to the disposition */ - break; - - case COL_INSERT_DUPMOVET: /* Find item and delete */ - TRACE_INFO_STRING("Property:", item->property); - TRACE_INFO_NUMBER("Type:", item->type); - if (col_find_property(collection, item->property, 0, 1, item->type, &parent)) { - TRACE_INFO_NUMBER("Current:", (unsigned)(parent->next)); - current = parent->next; - parent->next = current->next; - if (header->last == current) header->last = parent; - col_delete_item(current); - header->count--; - } - /* Now add item according to the disposition */ - break; - - default: /* The new ones should be added here */ - TRACE_ERROR_NUMBER("Flag is not implemented", ENOSYS); - return ENOSYS; - } - - - switch (disposition) { - case COL_DSP_END: /* Link new item to the last item in the list if there any */ - if (header->count != 0) header->last->next = item; - /* Make sure we save a new last element */ - header->last = item; - header->count++; - break; - - case COL_DSP_FRONT: /* Same as above if there is header only */ - if (header->count == 1) { - header->last->next = item; - header->last = item; - } - else { - item->next = collection->next; - collection->next = item; - } - header->count++; - break; - - case COL_DSP_BEFORE: /* Check argument */ - if (!refprop) { - TRACE_ERROR_STRING("In this case property is required", ""); - return EINVAL; - } - - /* We need to find property */ - if (col_find_property(collection, refprop, 0, 0, 0, &parent)) { - item->next = parent->next; - parent->next = item; - header->count++; - } - else { - TRACE_ERROR_STRING("Property not found", refprop); - return ENOENT; - } - break; - - case COL_DSP_AFTER: /* Check argument */ - if (!refprop) { - TRACE_ERROR_STRING("In this case property is required", ""); - return EINVAL; - } - - /* We need to find property */ - if (col_find_property(collection, refprop, 0, 0, 0, &parent)) { - parent = parent->next; - if (parent->next) { - /* It is not the last item */ - item->next = parent->next; - parent->next = item; - } - else { - /* It is the last item */ - header->last->next = item; - header->last = item; - } - header->count++; - } - else { - TRACE_ERROR_STRING("Property not found", refprop); - return ENOENT; - } - break; - - case COL_DSP_INDEX: if(idx == 0) { - /* Same is first */ - if (header->count == 1) { - header->last->next = item; - header->last = item; - } - else { - item->next = collection->next; - collection->next = item; - } - } - else if(idx >= header->count - 1) { - /* In this case add to the end */ - header->last->next = item; - /* Make sure we save a new last element */ - header->last = item; - } - else { - /* In the middle */ - parent = collection; - /* Move to the right position counting */ - while (idx > 0) { - idx--; - parent = parent->next; - } - item->next = parent->next; - parent->next = item; - } - header->count++; - break; - - case COL_DSP_FIRSTDUP: - case COL_DSP_LASTDUP: - case COL_DSP_NDUP: - - if (disposition == COL_DSP_FIRSTDUP) refindex = 0; - else if (disposition == COL_DSP_LASTDUP) refindex = -1; - else refindex = idx; - - /* We need to find property based on index */ - if (col_find_property(collection, item->property, refindex, 0, 0, &parent)) { - item->next = parent->next; - parent->next = item; - header->count++; - if(header->last == parent) header->last = item; - } - else { - TRACE_ERROR_STRING("Property not found", refprop); - return ENOENT; - } - break; - - default: - TRACE_ERROR_STRING("Disposition is not implemented", ""); - return ENOSYS; - - } - - - TRACE_INFO_STRING("Collection:", collection->property); - TRACE_INFO_STRING("Just added item is:", item->property); - TRACE_INFO_NUMBER("Item type.", item->type); - TRACE_INFO_NUMBER("Number of items in collection now is.", header->count); - - TRACE_FLOW_STRING("col_insert_item_into_current", "Exit"); - return EOK; -} - -/* Extract item from the current collection */ -int col_extract_item_from_current(struct collection_item *collection, - int disposition, - const char *refprop, - int idx, - int type, - struct collection_item **ret_ref) -{ - struct collection_header *header = NULL; - struct collection_item *parent = NULL; - struct collection_item *current = NULL; - struct collection_item *found = NULL; - int refindex = 0; - int use_type = 0; - - TRACE_FLOW_STRING("col_extract_item_from_current", "Entry point"); - - /* Check that collection is not empty */ - if ((collection == NULL) || (collection->type != COL_TYPE_COLLECTION)) { - TRACE_ERROR_STRING("Collection can't be NULL", ""); - return EINVAL; - } - - header = (struct collection_header *)collection->data; - - /* Before moving forward we need to check if there is anything to extract */ - if (header->count <= 1) { - TRACE_ERROR_STRING("Collection is empty.", "Nothing to extract."); - return ENOENT; - } - - if (type != 0) use_type = 1; - - switch (disposition) { - case COL_DSP_END: /* Extract last item in the list. */ - parent = collection; - current = collection->next; - while (current->next != NULL) { - parent = current; - current = current->next; - } - *ret_ref = parent->next; - parent->next = NULL; - /* Special case - one data element */ - if (header->count == 2) header->last = collection; - else header->last = parent; - break; - - case COL_DSP_FRONT: /* Extract first item in the list */ - *ret_ref = collection->next; - collection->next = (*ret_ref)->next; - /* Special case - one data element */ - if (header->count == 2) header->last = collection; - break; - - case COL_DSP_BEFORE: /* Check argument */ - if (!refprop) { - TRACE_ERROR_STRING("In this case property is required", ""); - return EINVAL; - } - - /* We have to do it in two steps */ - /* First find the property that is mentioned */ - if (col_find_property(collection, refprop, 0, use_type, type, &found)) { - /* We found the requested property */ - if (found->next == collection->next) { - /* The referenced property is the first in the list */ - TRACE_ERROR_STRING("Nothing to extract. Lists starts with property", refprop); - return ENOENT; - } - /* Get to the parent of the item that is before the one that is found */ - parent = collection; - current = collection->next; - while (current != found) { - parent = current; - current = current->next; - } - *ret_ref = current; - parent->next = current->next; - - } - else { - TRACE_ERROR_STRING("Property not found", refprop); - return ENOENT; - } - break; - - case COL_DSP_AFTER: /* Check argument */ - if (!refprop) { - TRACE_ERROR_STRING("In this case property is required", ""); - return EINVAL; - } - - /* We need to find property */ - if (col_find_property(collection, refprop, 0, use_type, type, &parent)) { - current = parent->next; - if (current->next) { - *ret_ref = current->next; - current->next = (*ret_ref)->next; - /* If we removed the last element adjust header */ - if(current->next == NULL) header->last = current; - } - else { - TRACE_ERROR_STRING("Property is last in the list", refprop); - return ENOENT; - } - } - else { - TRACE_ERROR_STRING("Property not found", refprop); - return ENOENT; - } - break; - - case COL_DSP_INDEX: if (idx == 0) { - *ret_ref = collection->next; - collection->next = (*ret_ref)->next; - /* Special case - one data element */ - if (header->count == 2) header->last = collection; - } - /* Index 0 stands for the first data element. - * Count includes header element. - */ - else if (idx >= (header->count - 1)) { - TRACE_ERROR_STRING("Index is out of boundaries", refprop); - return ENOENT; - } - else { - /* Loop till the element with right index */ - refindex = 0; - parent = collection; - current = collection->next; - while (refindex < idx) { - parent = current; - current = current->next; - refindex++; - } - *ret_ref = parent->next; - parent->next = (*ret_ref)->next; - /* If we removed the last element adjust header */ - if (parent->next == NULL) header->last = parent; - } - break; - - case COL_DSP_FIRSTDUP: - case COL_DSP_LASTDUP: - case COL_DSP_NDUP: - - if (disposition == COL_DSP_FIRSTDUP) refindex = 0; - else if (disposition == COL_DSP_LASTDUP) refindex = -2; - else refindex = idx; - - /* We need to find property based on index */ - if (col_find_property(collection, refprop, refindex, use_type, type, &parent)) { - *ret_ref = parent->next; - parent->next = (*ret_ref)->next; - /* If we removed the last element adjust header */ - if(parent->next == NULL) header->last = parent; - } - else { - TRACE_ERROR_STRING("Property not found", refprop); - return ENOENT; - } - break; - - default: - TRACE_ERROR_STRING("Disposition is not implemented", ""); - return ENOSYS; - - } - - - /* Clear item and reduce count */ - (*ret_ref)->next = NULL; - header->count--; - - TRACE_INFO_STRING("Collection:", (*ret_ref)->property); - TRACE_INFO_NUMBER("Item type.", (*ret_ref)->type); - TRACE_INFO_NUMBER("Number of items in collection now is.", header->count); - - TRACE_FLOW_STRING("col_extract_item_from_current", "Exit"); - return EOK; -} - -/* Extract item from the collection */ -int col_extract_item(struct collection_item *collection, - const char *subcollection, - int disposition, - const char *refprop, - int idx, - int type, - struct collection_item **ret_ref) -{ - struct collection_item *col = NULL; - int error = EOK; - - TRACE_FLOW_STRING("col_extract_item", "Entry point"); - - /* Check that collection is not empty */ - if ((collection == NULL) || (collection->type != COL_TYPE_COLLECTION)) { - TRACE_ERROR_STRING("Collection can't be NULL", ""); - return EINVAL; - } - - /* Get subcollection if needed */ - if (subcollection == NULL) { - col = collection; - } - else { - TRACE_INFO_STRING("Subcollection id not null, searching", subcollection); - error = col_find_item_and_do(collection, subcollection, - COL_TYPE_COLLECTIONREF, - COL_TRAVERSE_DEFAULT, - col_get_subcollection, (void *)(&col), - COLLECTION_ACTION_FIND); - if (error) { - TRACE_ERROR_NUMBER("Search for subcollection returned error:", error); - return error; - } - - if (col == NULL) { - TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", ""); - return ENOENT; - } - - } - - /* Extract from the current collection */ - error = col_extract_item_from_current(col, - disposition, - refprop, - idx, - type, - ret_ref); - if (error) { - TRACE_ERROR_NUMBER("Failed to extract item from the current collection", error); - return error; - } - - TRACE_FLOW_STRING("col_extract_item", "Exit"); - return EOK; -} - - -/* Remove item (property) from collection.*/ -int col_remove_item(struct collection_item *ci, - const char *subcollection, - int disposition, - const char *refprop, - int idx, - int type) -{ - int error = EOK; - struct collection_item *ret_ref = NULL; - - TRACE_FLOW_STRING("col_remove_item", "Exit"); - - /* Extract from the current collection */ - error = col_extract_item(ci, - subcollection, - disposition, - refprop, - idx, - type, - &ret_ref); - if (error) { - TRACE_ERROR_NUMBER("Failed to extract item from the collection", error); - return error; - } - - col_delete_item(ret_ref); - - TRACE_FLOW_STRING("col_remove_item", "Exit"); - return EOK; -} - -/* Remove item (property) from current collection. - * Just a simple wrapper. - */ -int col_remove_item_from_current(struct collection_item *ci, - int disposition, - const char *refprop, - int idx, - int type) -{ - int error = EOK; - - TRACE_FLOW_STRING("col_remove_item_from_current", "Exit"); - - /* Remove item from current collection */ - error = col_remove_item(ci, - NULL, - disposition, - refprop, - idx, - type); - - TRACE_FLOW_NUMBER("col_remove_item_from_current. Exit. Returning", error); - return error; -} - - -/* Insert the item into the collection or subcollection */ -int col_insert_item(struct collection_item *collection, - const char *subcollection, - struct collection_item *item, - int disposition, - const char *refprop, - int idx, - unsigned flags) -{ - int error; - struct collection_item *acceptor = NULL; - - TRACE_FLOW_STRING("col_insert_item", "Entry point."); - - /* Do best effort on the item */ - if ((!item) || (item->next)) { - TRACE_ERROR_STRING("Passed in item is invalid", ""); - return EINVAL; - } - - /* Check that collection is not empty */ - if ((collection == NULL) && (item->type != COL_TYPE_COLLECTION)) { - TRACE_ERROR_STRING("Collection can't be NULL", ""); - return EINVAL; - } - - /* Add item to collection */ - if (subcollection == NULL) { - acceptor = collection; - } - else { - TRACE_INFO_STRING("Subcollection id not null, searching", subcollection); - error = col_find_item_and_do(collection, subcollection, - COL_TYPE_COLLECTIONREF, - COL_TRAVERSE_DEFAULT, - col_get_subcollection, (void *)(&acceptor), - COLLECTION_ACTION_FIND); - if (error) { - TRACE_ERROR_NUMBER("Search for subcollection returned error:", error); - return error; - } - - if (acceptor == NULL) { - TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", ""); - return ENOENT; - } - - } - - /* Instert item to the current collection */ - error = col_insert_item_into_current(acceptor, - item, - disposition, - refprop, - idx, - flags); - - if (error) { - TRACE_ERROR_NUMBER("Failed to insert item into current collection", error); - return error; - } - - TRACE_FLOW_STRING("insert_item", "Exit"); - return EOK; -} - - -/* Insert property with reference. - * This is internal function so we do not check parameters. - * See external wrapper below. - */ -static int col_insert_property_with_ref_int(struct collection_item *collection, - const char *subcollection, - int disposition, - const char *refprop, - int idx, - unsigned flags, - const char *property, - int type, - const void *data, - int length, - struct collection_item **ret_ref) -{ - struct collection_item *item = NULL; - int error; - - TRACE_FLOW_STRING("col_insert_property_with_ref_int", "Entry point."); - - /* Create a new property out of the given parameters */ - error = col_allocate_item(&item, property, data, length, type); - if (error) { - TRACE_ERROR_NUMBER("Failed to allocate item", error); - return error; - } - - /* Send the property to the insert_item function */ - error = col_insert_item(collection, - subcollection, - item, - disposition, - refprop, - idx, - flags); - if (error) { - TRACE_ERROR_NUMBER("Failed to insert item", error); - col_delete_item(item); - return error; - } - - if (ret_ref) *ret_ref = item; - - TRACE_FLOW_STRING("col_insert_property_with_ref_int", "Exit"); - return EOK; -} - -/* Special function used to copy item from one - * collection to another using caller's callback. - */ -static int col_copy_item_with_cb(struct collection_item *collection, - const char *property, - int type, - const void *data, - int length, - col_copy_cb copy_cb, - void *ext_data) -{ - struct collection_item *item = NULL; - int skip = 0; - int error = EOK; - - TRACE_FLOW_STRING("col_copy_item_with_cb", "Entry point."); - - /* Create a new property out of the given parameters */ - error = col_allocate_item(&item, property, data, length, type); - if (error) { - TRACE_ERROR_NUMBER("Failed to allocate item", error); - return error; - } - - /* Call callback if any */ - if (copy_cb) { - TRACE_INFO_STRING("Calling callback for item:", item->property); - error = copy_cb(item, ext_data, &skip); - if (error) { - TRACE_ERROR_NUMBER("Callback failed", error); - col_delete_item(item); - return error; - } - } - - /* Are we told to skip this item? */ - if (skip) col_delete_item(item); - else { - /* Insted property into the collection */ - error = col_insert_item(collection, - NULL, - item, - COL_DSP_END, - NULL, - 0, - 0); - if (error) { - TRACE_ERROR_NUMBER("Failed to insert item", error); - col_delete_item(item); - return error; - } - } - - TRACE_FLOW_STRING("col_copy_item_with_cb", "Exit"); - return EOK; -} - - -/* This is public function so we need to check the validity - * of the arguments. - */ -int col_insert_property_with_ref(struct collection_item *collection, - const char *subcollection, - int disposition, - const char *refprop, - int idx, - unsigned flags, - const char *property, - int type, - const void *data, - int length, - struct collection_item **ret_ref) -{ - int error; - - TRACE_FLOW_STRING("col_insert_property_with_ref", "Entry point."); - - /* Check that collection is not empty */ - if (collection == NULL) { - TRACE_ERROR_STRING("Collection cant be NULL", ""); - return EINVAL; - } - - error = col_insert_property_with_ref_int(collection, - subcollection, - disposition, - refprop, - idx, - flags, - property, - type, - data, - length, - ret_ref); - - TRACE_FLOW_NUMBER("col_insert_property_with_ref_int Returning:", error); - return error; -} -/* TRAVERSE HANDLERS */ - -/* Special handler to just set a flag if the item is found */ -static int col_is_in_item_handler(const char *property, - int property_len, - int type, - void *data, - int length, - void *found, - int *dummy) -{ - TRACE_FLOW_STRING("col_is_in_item_handler", "Entry."); - TRACE_INFO_STRING("Property:", property); - TRACE_INFO_NUMBER("Property length:", property_len); - TRACE_INFO_NUMBER("Type:", type); - TRACE_INFO_NUMBER("Length:", length); - - *((int *)(found)) = COL_MATCH; - - TRACE_FLOW_STRING("col_is_in_item_handler", "Success Exit."); - - return EOK; -} - -/* Special handler to retrieve the sub collection */ -static int col_get_subcollection(const char *property, - int property_len, - int type, - void *data, - int length, - void *found, - int *dummy) -{ - TRACE_FLOW_STRING("col_get_subcollection", "Entry."); - TRACE_INFO_STRING("Property:", property); - TRACE_INFO_NUMBER("Property length:", property_len); - TRACE_INFO_NUMBER("Type:", type); - TRACE_INFO_NUMBER("Length:", length); - - *((struct collection_item **)(found)) = *((struct collection_item **)(data)); - - TRACE_FLOW_STRING("col_get_subcollection","Success Exit."); - - return EOK; - -} - - - -/* CLEANUP */ - -/* Cleans the collection tree including current item. */ -/* The passed in variable should not be used after the call - * as memory is freed!!! */ -static void col_delete_collection(struct collection_item *ci) -{ - TRACE_FLOW_STRING("col_delete_collection", "Entry."); - - if (ci == NULL) { - TRACE_FLOW_STRING("col_delete_collection", "Nothing to do Exit."); - return; - } - - TRACE_INFO_STRING("Real work to do", ""); - TRACE_INFO_STRING("Property", ci->property); - TRACE_INFO_NUMBER("Next item", ci->next); - - col_delete_collection(ci->next); - - /* Delete this item */ - col_delete_item(ci); - TRACE_FLOW_STRING("col_delete_collection", "Exit."); -} - - -/* NAME MANAGEMENT - used by search */ - -/* Internal data structures used for search */ - - -struct find_name { - const char *name_to_find; - int name_len_to_find; - uint64_t hash; - int type_to_match; - char *given_name; - int given_len; - struct path_data *current_path; - int action; -}; - -/* Create a new name */ -static int col_create_path_data(struct path_data **name_path, - const char *name, int length, - const char *property, int property_len, - char sep) -{ - int error = EOK; - struct path_data *new_name_path; - - TRACE_FLOW_STRING("col_create_path_data", "Entry."); - - TRACE_INFO_STRING("Constructing path from name:", name); - TRACE_INFO_STRING("Constructing path from property:", property); - - /* Allocate structure */ - new_name_path = (struct path_data *)malloc(sizeof(struct path_data)); - if (new_name_path == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory for new path struct.", ENOMEM); - return ENOMEM; - } - new_name_path->name = malloc(length + property_len + 2); - if (new_name_path->name == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory for new path name.", ENOMEM); - free(new_name_path); - return ENOMEM; - } - - /* Construct the new name */ - new_name_path->length = 0; - - if(length > 0) { - memcpy(new_name_path->name, name, length); - new_name_path->length = length; - new_name_path->name[new_name_path->length] = sep; - new_name_path->length++; - new_name_path->name[new_name_path->length] = '\0'; - TRACE_INFO_STRING("Name so far:", new_name_path->name); - TRACE_INFO_NUMBER("Len so far:", new_name_path->length); - } - memcpy(&new_name_path->name[new_name_path->length], property, property_len); - new_name_path->length += property_len; - new_name_path->name[new_name_path->length] = '\0'; - - /* Link to the chain */ - new_name_path->previous_path = *name_path; - *name_path = new_name_path; - - TRACE_INFO_STRING("Constructed path", new_name_path->name); - - - TRACE_FLOW_NUMBER("col_create_path_data. Returning:", error); - return error; -} - -/* Matching item name and type */ -static int col_match_item(struct collection_item *current, - struct find_name *traverse_data) -{ - - const char *find_str; - const char *start; - const char *data_str; - - TRACE_FLOW_STRING("col_match_item", "Entry"); - - if (traverse_data->type_to_match & current->type) { - - /* Check if there is any value to match */ - if ((traverse_data->name_to_find == NULL) || - (*(traverse_data->name_to_find) == '\0')) { - TRACE_INFO_STRING("col_match_item", - "Returning MATCH because there is no search criteria!"); - return COL_MATCH; - } - - /* Check the hashes - if they do not match return */ - if (traverse_data->hash != current->phash) { - TRACE_INFO_STRING("col_match_item","Returning NO match!"); - return COL_NOMATCH; - } - - /* We will do the actual string comparison only if the hashes matched */ - - /* Start comparing the two strings from the end */ - find_str = traverse_data->name_to_find + traverse_data->name_len_to_find; - start = current->property; - data_str = start + current->property_len; - - TRACE_INFO_STRING("Searching for:", traverse_data->name_to_find); - TRACE_INFO_STRING("Item name:", current->property); - TRACE_INFO_STRING("Current path:", traverse_data->current_path->name); - TRACE_INFO_NUMBER("Searching:", toupper(*find_str)); - TRACE_INFO_NUMBER("Have:", toupper(*data_str)); - - /* We start pointing to 0 so the loop will be executed at least once */ - while (toupper(*data_str) == toupper(*find_str)) { - - TRACE_INFO_STRING("Loop iteration:",""); - - if (data_str == start) { - if (find_str > traverse_data->name_to_find) { - if (*(find_str-1) == '!') { - /* We matched the property but the search string is - * longer so we need to continue matching */ - TRACE_INFO_STRING("col_match_item", - "Need to continue matching"); - start = traverse_data->current_path->name; - data_str = &start[traverse_data->current_path->length - 1]; - find_str -= 2; - continue; - } - else { - TRACE_INFO_STRING("col_match_item","Returning NO match!"); - return COL_NOMATCH; - } - } - else { - TRACE_INFO_STRING("col_match_item","Returning MATCH!"); - return COL_MATCH; - } - } - else if ((find_str == traverse_data->name_to_find) && - (*(data_str-1) == '!')) return COL_MATCH; - - data_str--; - find_str--; - TRACE_INFO_NUMBER("Searching:", toupper(*find_str)); - TRACE_INFO_NUMBER("Have:", toupper(*data_str)); - - } - } - - TRACE_FLOW_STRING("col_match_item","Returning NO match!"); - return COL_NOMATCH; - -} - -/* Function to delete the data that contains search path */ -static void col_delete_path_data(struct path_data *path) -{ - TRACE_FLOW_STRING("col_delete_path_data","Entry."); - - if (path != NULL) { - TRACE_INFO_STRING("col_delete_path_data", "Item to delete exits."); - if (path->previous_path != NULL) { - TRACE_INFO_STRING("col_delete_path_data", - "But previous item to delete exits to. Nesting."); - col_delete_path_data(path->previous_path); - } - if (path->name != NULL) { - TRACE_INFO_STRING("col_delete_path_data Deleting path:", path->name); - free(path->name); - } - TRACE_INFO_STRING("col_delete_path_data", "Deleting path element"); - free(path); - } - TRACE_FLOW_STRING("col_delete_path_data", "Exit"); -} - - -/* MAIN TRAVERSAL FUNCTION */ - -/* Internal function to walk collection */ -/* For each item walked it will call traverse handler. - Traverse handler accepts: current item, - user provided item handler and user provided custom data. */ -/* See below different traverse handlers for different cases */ -static int col_walk_items(struct collection_item *ci, - int mode_flags, - internal_item_fn traverse_handler, - void *traverse_data, - col_item_fn user_item_handler, - void *custom_data, - unsigned *depth) -{ - struct collection_item *current; - struct collection_item *parent = NULL; - struct collection_item *sub; - int stop = 0; - int error = EOK; - - TRACE_FLOW_STRING("col_walk_items", "Entry."); - TRACE_INFO_NUMBER("Mode flags:", mode_flags); - - /* Increase depth */ - /* NOTE: The depth is increased at the entry to the function. - * and decreased right before the exit so it is safe to decrease it. - */ - (*depth)++; - - current = ci; - - while (current) { - - TRACE_INFO_STRING("Processing item:", current->property); - TRACE_INFO_NUMBER("Item type:", current->type); - - if (current->type == COL_TYPE_COLLECTIONREF) { - - TRACE_INFO_STRING("Subcollection:", current->property); - - if ((mode_flags & COL_TRAVERSE_IGNORE) == 0) { - - TRACE_INFO_STRING("Subcollection is not ignored.", ""); - /* We are not ignoring sub collections */ - - if ((mode_flags & COL_TRAVERSE_FLAT) == 0) { - - TRACE_INFO_STRING("Subcollection is not flattened.", ""); - /* We are not flattening sub collections. - * The flattening means that we are not going - * to return reference and headers for sub collections. - * We will also not do special end collection - * invocation for sub collections. - */ - error = traverse_handler(ci, parent, current, traverse_data, - user_item_handler, custom_data, &stop); - if (stop != 0) { - TRACE_INFO_STRING("Traverse handler returned STOP.", ""); - error = EINTR_INTERNAL; - } - /* Check what error we got */ - if (error == EINTR_INTERNAL) { - TRACE_FLOW_NUMBER("Internal error - means we are stopping.", error); - (*depth)--; - return error; - } - else if (error) { - TRACE_ERROR_NUMBER("Traverse handler returned error.", error); - (*depth)--; - return error; - } - } - - if ((mode_flags & COL_TRAVERSE_ONELEVEL) == 0) { - TRACE_INFO_STRING("Before diving into sub collection",""); - sub = *((struct collection_item **)(current->data)); - TRACE_INFO_STRING("Sub collection name", sub->property); - TRACE_INFO_NUMBER("Header type", sub->type); - /* We need to go into sub collections */ - error = col_walk_items(sub, mode_flags, - traverse_handler, traverse_data, - user_item_handler, custom_data, - depth); - TRACE_INFO_STRING("Returned from sub collection processing", ""); - TRACE_INFO_STRING("Done processing item:", current->property); - TRACE_INFO_NUMBER("Done processing item type:", current->type); - - } - } - } - else { - /* Check if it is a header and we are not on the root level. - * If we are flattening collection we need to skip headers - * for sub collections. - */ - - /* Call handler if: - * a) It is not collection header - * OR - * b) It is header we are flattening but we are on top level - * OR - * c) It is header and we are not flattening. - */ - if ((current->type != COL_TYPE_COLLECTION) || - (((mode_flags & COL_TRAVERSE_FLAT) != 0) && (*depth == 1)) || - ((mode_flags & COL_TRAVERSE_FLAT) == 0)) { - /* Call handler then move on */ - error = traverse_handler(ci, parent, current, - traverse_data, user_item_handler, - custom_data, &stop); - - } - } - /* If we are stopped - return EINTR_INTERNAL */ - if (stop != 0) { - TRACE_INFO_STRING("Traverse handler returned STOP.", ""); - error = EINTR_INTERNAL; - } - /* Check what error we got */ - if (error == EINTR_INTERNAL) { - TRACE_FLOW_NUMBER("Internal error - means we are stopping.", error); - (*depth)--; - return error; - } - else if (error) { - TRACE_ERROR_NUMBER("Traverse handler returned error.", error); - (*depth)--; - return error; - } - - parent = current; - current = current->next; - - } - - TRACE_INFO_STRING("Out of loop", ""); - - /* Check if we need to have a special - * call at the end of the collection. - */ - if ((mode_flags & COL_TRAVERSE_END) != 0) { - - /* Do this dummy invocation only: - * a) If we are flattening and on the root level - * b) We are not flattening - */ - if ((((mode_flags & COL_TRAVERSE_FLAT) != 0) && (*depth == 1)) || - ((mode_flags & COL_TRAVERSE_FLAT) == 0)) { - - TRACE_INFO_STRING("About to do the special end collection invocation of handler", ""); - error = traverse_handler(ci, parent, current, - traverse_data, user_item_handler, - custom_data, &stop); - } - } - - TRACE_FLOW_NUMBER("col_walk_items. Returns: ", error); - (*depth)--; - return error; -} - - -/* ACTION */ - -/* Find an item by property name and perform an action on it. */ -/* No pattern matching supported in the first implementation. */ -/* To refer to child properties use notatation like this: */ -/* parent!child!subchild!subsubchild etc. */ -static int col_find_item_and_do(struct collection_item *ci, - const char *property_to_find, - int type, - int mode_flags, - col_item_fn item_handler, - void *custom_data, - int action) -{ - - int error = EOK; - struct find_name *traverse_data = NULL; - unsigned depth = 0; - int count = 0; - const char *last_part; - char *sep; - - TRACE_FLOW_STRING("col_find_item_and_do", "Entry."); - - /* Item handler is always required */ - if ((item_handler == NULL) && - (action == COLLECTION_ACTION_FIND)) { - TRACE_ERROR_NUMBER("No item handler - returning error!", EINVAL); - return EINVAL; - } - - /* Collection is requered */ - if (ci == NULL) { - TRACE_ERROR_NUMBER("No collection to search!", EINVAL); - return EINVAL; - } - - /* Make sure that there is anything to search */ - type &= COL_TYPE_ANY; - if (((property_to_find == NULL) && (type == 0)) || - ((*property_to_find == '\0') && (type == 0))) { - TRACE_ERROR_NUMBER("No item search criteria specified - returning error!", ENOENT); - return ENOENT; - } - /* Prepare data for traversal */ - traverse_data = (struct find_name *)malloc(sizeof(struct find_name)); - if (traverse_data == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate traverse data memory - returning error!", ENOMEM); - return ENOMEM; - } - - TRACE_INFO_STRING("col_find_item_and_do", "Filling in traverse data."); - - traverse_data->name_to_find = property_to_find; - - if (property_to_find != NULL) { - - traverse_data->name_len_to_find = strlen(property_to_find); - - /* Check if the search string ends with "!" - this is illegal */ - if (traverse_data->name_to_find[traverse_data->name_len_to_find - 1] == '!') { - TRACE_ERROR_NUMBER("Search string is invalid.", EINVAL); - free(traverse_data); - return EINVAL; - } - - /* Find last ! if any */ - sep = strrchr(traverse_data->name_to_find, '!'); - if (sep != NULL) { - sep++; - last_part = sep; - } - else last_part = traverse_data->name_to_find; - - TRACE_INFO_STRING("Last item", last_part); - - /* Create hash of the last part */ - traverse_data->hash = FNV1a_base; - - /* Create hash of the string to search */ - while(last_part[count] != 0) { - traverse_data->hash = traverse_data->hash ^ toupper(last_part[count]); - traverse_data->hash *= FNV1a_prime; - count++; - } - } - else { - /* We a looking for a first element of a given type */ - TRACE_INFO_STRING("No search string", ""); - traverse_data->name_len_to_find = 0; - } - - - traverse_data->type_to_match = type; - traverse_data->given_name = NULL; - traverse_data->given_len = 0; - traverse_data->current_path = NULL; - traverse_data->action = action; - - mode_flags |= COL_TRAVERSE_END; - - TRACE_INFO_STRING("col_find_item_and_do", "About to walk the tree."); - TRACE_INFO_NUMBER("Traverse flags", mode_flags); - - error = col_walk_items(ci, mode_flags, col_act_traverse_handler, - (void *)traverse_data, item_handler, custom_data, - &depth); - - if (traverse_data->current_path != NULL) { - TRACE_INFO_STRING("find_item_and_do", - "Path was not cleared - deleting"); - col_delete_path_data(traverse_data->current_path); - } - - free(traverse_data); - - if (error && (error != EINTR_INTERNAL)) { - TRACE_ERROR_NUMBER("Walk items returned error. Returning: ", error); - return error; - } - else { - TRACE_FLOW_STRING("Walk items returned SUCCESS.", ""); - return EOK; - } -} - -/* Function to replace data in the item */ -static int col_update_current_item(struct collection_item *current, - struct update_property *update_data) -{ - TRACE_FLOW_STRING("col_update_current_item", "Entry"); - - /* If type is different or same but it is string or binary we need to - * replace the storage */ - if ((current->type != update_data->type) || - ((current->type == update_data->type) && - ((current->type == COL_TYPE_STRING) || - (current->type == COL_TYPE_BINARY)))) { - TRACE_INFO_STRING("Replacing item data buffer", ""); - free(current->data); - current->data = malloc(update_data->length); - if (current->data == NULL) { - TRACE_ERROR_STRING("Failed to allocate memory", ""); - current->length = 0; - return ENOMEM; - } - current->length = update_data->length; - } - - TRACE_INFO_STRING("Overwriting item data", ""); - memcpy(current->data, update_data->data, current->length); - current->type = update_data->type; - - if (current->type == COL_TYPE_STRING) - ((char *)(current->data))[current->length-1] = '\0'; - - TRACE_FLOW_STRING("update_current_item", "Exit"); - return EOK; -} - -/* TRAVERSE CALLBACKS */ - -/* Traverse handler for simple traverse function */ -/* Handler must be able to deal with NULL current item */ -static int col_simple_traverse_handler(struct collection_item *head, - struct collection_item *previous, - struct collection_item *current, - void *traverse_data, - col_item_fn user_item_handler, - void *custom_data, - int *stop) -{ - int error = EOK; - struct collection_item end_item; - char zero = '\0'; - - TRACE_FLOW_STRING("col_simple_traverse_handler", "Entry."); - - if (current == NULL) { - memset((void *)&end_item, 0, sizeof(struct collection_item)); - end_item.type = COL_TYPE_END; - end_item.property = &zero; - current = &end_item; - } - - error = user_item_handler(current->property, - current->property_len, - current->type, - current->data, - current->length, - custom_data, - stop); - - TRACE_FLOW_NUMBER("col_simple_traverse_handler. Returning:", error); - return error; -} - -/* Traverse handler for to find parent */ -static int col_parent_traverse_handler(struct collection_item *head, - struct collection_item *previous, - struct collection_item *current, - void *traverse_data, - col_item_fn user_item_handler, - void *custom_data, - int *stop) -{ - struct property_search *to_find; - int done = 0; - int match = 0; - - TRACE_FLOW_STRING("col_parent_traverse_handler", "Entry."); - - to_find = (struct property_search *)custom_data; - - TRACE_INFO_NUMBER("Looking for HASH:", (unsigned)(to_find->hash)); - TRACE_INFO_NUMBER("Current HASH:", (unsigned)(current->phash)); - - /* Check hashes first */ - if(to_find->hash == current->phash) { - - /* Check type if we are asked to use type */ - if ((to_find->use_type) && (!(to_find->type & current->type))) { - TRACE_FLOW_STRING("parent_traverse_handler. Returning:","Exit. Hash is Ok, type is not"); - return EOK; - } - - /* Validate property. Make sure we include terminating 0 in the comparison */ - if (strncasecmp(current->property, to_find->property, current->property_len + 1) == 0) { - - match = 1; - to_find->found = 1; - - /* Do the right thing based on index */ - /* If index is 0 we are looking for the first value in the list of duplicate properties */ - if (to_find->index == 0) done = 1; - /* If index is non zero we are looking for N-th instance of the dup property */ - else if (to_find->index > 0) { - if (to_find->count == to_find->index) done = 1; - else { - /* Record found instance and move on */ - to_find->parent = previous; - (to_find->count)++; - } - } - /* If we are looking for last instance just record it */ - else to_find->parent = previous; - } - } - - if (done) { - *stop = 1; - *((struct collection_item **)traverse_data) = previous; - } - else { - /* As soon as we found first non matching one but there was a match - * return the parent of the last found item. - */ - if (((!match) || (current->next == NULL)) && (to_find->index != 0) && (to_find->found)) { - *stop = 1; - if (to_find->index == -2) - *((struct collection_item **)traverse_data) = to_find->parent; - else - *((struct collection_item **)traverse_data) = to_find->parent->next; - } - } - - - TRACE_FLOW_STRING("col_parent_traverse_handler. Returning:","Exit"); - return EOK; -} - - -/* Traverse callback for find & delete function */ -static int col_act_traverse_handler(struct collection_item *head, - struct collection_item *previous, - struct collection_item *current, - void *passed_traverse_data, - col_item_fn user_item_handler, - void *custom_data, - int *stop) -{ - int error = EOK; - struct find_name *traverse_data = NULL; - char *name; - int length; - struct path_data *temp; - struct collection_header *header; - char *property; - int property_len; - struct update_property *update_data; - - TRACE_FLOW_STRING("col_act_traverse_handler", "Entry."); - - traverse_data = (struct find_name *)passed_traverse_data; - - /* We can be called when current points to NULL */ - if (current == NULL) { - TRACE_INFO_STRING("col_act_traverse_handler", - "Special call at the end of the collection."); - temp = traverse_data->current_path; - traverse_data->current_path = temp->previous_path; - temp->previous_path = NULL; - col_delete_path_data(temp); - traverse_data->given_name = NULL; - traverse_data->given_len = 0; - TRACE_FLOW_NUMBER("Handling end of collection - removed path. Returning:", error); - return error; - } - - /* Create new path at the beginning of a new sub collection */ - if (current->type == COL_TYPE_COLLECTION) { - - TRACE_INFO_STRING("col_act_traverse_handler", - "Processing collection handle."); - - /* Create new path */ - if (traverse_data->current_path != NULL) { - TRACE_INFO_STRING("Already have part of the path", ""); - name = traverse_data->current_path->name; - length = traverse_data->current_path->length; - TRACE_INFO_STRING("Path:", name); - TRACE_INFO_NUMBER("Path len:", length); - } - else { - name = NULL; - length = 0; - } - - if (traverse_data->given_name != NULL) { - property = traverse_data->given_name; - property_len = traverse_data->given_len; - } - else { - property = current->property; - property_len = current->property_len; - } - - TRACE_INFO_STRING("col_act_traverse_handler", "About to create path data."); - - error = col_create_path_data(&(traverse_data->current_path), - name, length, - property, property_len, '!'); - - TRACE_INFO_NUMBER("col_create_path_data returned:", error); - return error; - } - - /* Handle the collection pointers */ - if (current->type == COL_TYPE_COLLECTIONREF) { - traverse_data->given_name = current->property; - traverse_data->given_len = current->property_len; - TRACE_INFO_STRING("Saved given name:", traverse_data->given_name); - } - - TRACE_INFO_STRING("Processing item with property:", current->property); - - /* Do here what we do with items */ - if (col_match_item(current, traverse_data)) { - TRACE_INFO_STRING("Matched item:", current->property); - switch (traverse_data->action) { - case COLLECTION_ACTION_FIND: - TRACE_INFO_STRING("It is a find action - calling handler.", ""); - if (user_item_handler != NULL) { - /* Call user handler */ - error = user_item_handler(current->property, - current->property_len, - current->type, - current->data, - current->length, - custom_data, - stop); - - TRACE_INFO_NUMBER("Handler returned:", error); - TRACE_INFO_NUMBER("Handler set STOP to:", *stop); - - } - break; - - case COLLECTION_ACTION_GET: - TRACE_INFO_STRING("It is a get action.", ""); - if (custom_data != NULL) - *((struct collection_item **)(custom_data)) = current; - break; - - case COLLECTION_ACTION_DEL: - TRACE_INFO_STRING("It is a delete action.", ""); - /* Make sure we tell the caller we found a match */ - if (custom_data != NULL) - *(int *)custom_data = COL_MATCH; - - /* Adjust header of the collection */ - header = (struct collection_header *)head->data; - header->count--; - if (current->next == NULL) - header->last = previous; - - /* Unlink and delete iteam */ - /* Previous can't be NULL here becuase we never delete - * header elements */ - previous->next = current->next; - col_delete_item(current); - TRACE_INFO_STRING("Did the delete of the item.", ""); - break; - - case COLLECTION_ACTION_UPDATE: - TRACE_INFO_STRING("It is an update action.", ""); - if((current->type == COL_TYPE_COLLECTION) || - (current->type == COL_TYPE_COLLECTIONREF)) { - TRACE_ERROR_STRING("Can't update collections it is an error for now", ""); - return EINVAL; - } - - /* Make sure we tell the caller we found a match */ - if (custom_data != NULL) { - update_data = (struct update_property *)custom_data; - update_data->found = COL_MATCH; - error = col_update_current_item(current, update_data); - } - else { - TRACE_ERROR_STRING("Error - update data is required", ""); - return EINVAL; - } - - TRACE_INFO_STRING("Did the delete of the item.", ""); - break; - default: - break; - } - /* Force interrupt if we found */ - *stop = 1; - } - - TRACE_FLOW_NUMBER("col_act_traverse_handler returning", error); - return error; -} - - -/* Traverse handler for copy function */ -static int col_copy_traverse_handler(struct collection_item *head, - struct collection_item *previous, - struct collection_item *current, - void *passed_traverse_data, - col_item_fn user_item_handler, - void *custom_data, - int *stop) -{ - int error = EOK; - struct collection_item *parent; - struct collection_item *other = NULL; - struct col_copy *traverse_data; - struct path_data *temp; - char *name; - int length; - char *property = NULL; - int property_len; - struct collection_header *header; - char *offset; - - TRACE_FLOW_STRING("col_copy_traverse_handler", "Entry."); - - parent = (struct collection_item *)custom_data; - traverse_data = (struct col_copy *)passed_traverse_data; - - /* We can be called when current points to NULL */ - /* This will happen only in the FLATDOT case */ - if (current == NULL) { - TRACE_INFO_STRING("col_copy_traverse_handler", - "Special call at the end of the collection."); - temp = traverse_data->current_path; - traverse_data->current_path = temp->previous_path; - temp->previous_path = NULL; - col_delete_path_data(temp); - traverse_data->given_name = NULL; - traverse_data->given_len = 0; - TRACE_FLOW_NUMBER("Handling end of collection - removed path. Returning:", error); - return error; - } - - /* Create new path at the beginning of a new sub collection */ - if (current->type == COL_TYPE_COLLECTION) { - - TRACE_INFO_STRING("col_copy_traverse_handler", - "Processing collection handle."); - if (traverse_data->mode == COL_COPY_FLATDOT) { - /* Create new path */ - if (traverse_data->current_path != NULL) { - TRACE_INFO_STRING("Already have part of the path", ""); - name = traverse_data->current_path->name; - length = traverse_data->current_path->length; - TRACE_INFO_STRING("Path:", name); - TRACE_INFO_NUMBER("Path len:", length); - if (traverse_data->given_name != NULL) { - property = traverse_data->given_name; - property_len = traverse_data->given_len; - } - else { - property = current->property; - property_len = current->property_len; - } - } - else { - /* Do not create prefix for top collection - * if there is no given name. - */ - name = NULL; - length = 0; - if (traverse_data->given_name != NULL) { - property = traverse_data->given_name; - property_len = traverse_data->given_len; - } - else { - property = NULL; - property_len = 0; - } - } - - TRACE_INFO_STRING("col_copy_traverse_handler", "About to create path data."); - - error = col_create_path_data(&(traverse_data->current_path), - name, length, - property, property_len, '.'); - - TRACE_FLOW_NUMBER("col_copy_traverse_handler processed header:", error); - return error; - } - else { - TRACE_FLOW_NUMBER("col_copy_traverse_handler skipping the header:", error); - return error; - } - } - - - /* Check if this is a special case of sub collection */ - if (current->type == COL_TYPE_COLLECTIONREF) { - - TRACE_INFO_STRING("Found a subcollection we need to copy. Name:", - current->property); - - /* Based on the mode we need to do different things */ - switch (traverse_data->mode) { - case COL_COPY_NORMAL: - - error = col_copy_collection(&other, - *((struct collection_item **)(current->data)), - current->property, - COL_COPY_NORMAL); - if (error) { - TRACE_ERROR_NUMBER("Copy subcollection returned error:", error); - return error; - } - - /* Add new item to a collection - * all references are now sub collections */ - error = col_insert_property_with_ref_int(parent, - NULL, - COL_DSP_END, - NULL, - 0, - 0, - current->property, - COL_TYPE_COLLECTIONREF, - (void *)(&other), - sizeof(struct collection_item **), - NULL); - - TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in NORMAL mode:", error); - return error; - - case COL_COPY_KEEPREF: - - /* Just increase reference count of the referenced collection */ - other = *((struct collection_item **)(current->data)); - header = (struct collection_header *)(other->data); - header->reference_count++; - - /* Add new item to a collection - * all references are now sub collections */ - error = col_insert_property_with_ref_int(parent, - NULL, - COL_DSP_END, - NULL, - 0, - 0, - current->property, - COL_TYPE_COLLECTIONREF, - (void *)(&other), - sizeof(struct collection_item **), - NULL); - TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in KEEPREF mode:", error); - return error; - - case COL_COPY_TOP: - /* Told to ignore sub collections */ - TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in TOP mode:", error); - return error; - - case COL_COPY_FLATDOT: - - traverse_data->given_name = current->property; - traverse_data->given_len = current->property_len; - TRACE_INFO_STRING("Saved given name:", traverse_data->given_name); - TRACE_FLOW_NUMBER("col_copy_traverse_handler returning in FLATDOT mode:", error); - return error; - - /* NOTE: The mode COL_COPY_FLAT is not in the list of cases becuase - * in this flat mode we traverse collection using COL_TRAVERSE_FLAT flag - * thus we should not be called on referenced collections at all - * by the col_walk_items() function. - */ - - default: - TRACE_ERROR_NUMBER("col_copy_traverse_handler bad mode error:", EINVAL); - return EINVAL; - } - } - else { - - if (traverse_data->mode == COL_COPY_FLATDOT) { - /* Since this code can't use asprintf have to do it hard way */ - property = malloc(traverse_data->current_path->length + - current->property_len + 2); - if (property == NULL) { - TRACE_ERROR_NUMBER("Failed to allocate memory for a new name:", error); - return error; - } - /* Add first part and dot only if we have prefix */ - offset = property; - if (traverse_data->current_path->length) { - memcpy(offset, traverse_data->current_path->name, - traverse_data->current_path->length); - offset[traverse_data->current_path->length] = '.'; - offset += traverse_data->current_path->length + 1; - } - memcpy(offset, current->property, current->property_len); - offset[current->property_len] = '\0'; - } - else property = current->property; - - TRACE_INFO_STRING("Using property:", property); - - error = col_copy_item_with_cb(parent, - property, - current->type, - current->data, - current->length, - traverse_data->copy_cb, - traverse_data->ext_data); - - /* Free property if we allocated it */ - if (traverse_data->mode == COL_COPY_FLATDOT) free(property); - - if (error) { - TRACE_ERROR_NUMBER("Failed to copy property:", error); - return error; - } - } - - TRACE_FLOW_NUMBER("col_copy_traverse_handler returning", error); - return error; -} - - - - -/********************* MAIN INTERFACE FUNCTIONS *****************************/ - - -/* CREATE */ - -/* Function that creates an named collection of a given class*/ -int col_create_collection(struct collection_item **ci, const char *name, - unsigned cclass) -{ - struct collection_item *handle = NULL; - struct collection_header header; - int error = EOK; - - TRACE_FLOW_STRING("col_create_collection", "Entry."); - - /* Prepare header */ - header.last = NULL; - header.reference_count = 1; - header.count = 0; - header.cclass = cclass; - - /* Create a collection type property */ - error = col_insert_property_with_ref_int(NULL, - NULL, - COL_DSP_END, - NULL, - 0, - 0, - name, - COL_TYPE_COLLECTION, - &header, - sizeof(header), - &handle); - - - if (error) return error; - - *ci = handle; - - TRACE_FLOW_STRING("col_create_collection", "Success Exit."); - return EOK; -} - - -/* DESTROY */ - -/* Function that destroys a collection */ -void col_destroy_collection(struct collection_item *ci) -{ - struct collection_header *header; - - TRACE_FLOW_STRING("col_destroy_collection", "Entry."); - - /* Do not try to delete NULL */ - if (ci == NULL) return; - - /* You can delete only whole collection not a part of it */ - if (ci->type != COL_TYPE_COLLECTION) { - TRACE_ERROR_STRING("Attempt to delete a non collection - BAD!", ""); - TRACE_ERROR_NUMBER("Actual type is:", ci->type); - return; - } - - TRACE_INFO_STRING("Name:", ci->property); - - /* Collection can be referenced by other collection */ - header = (struct collection_header *)(ci->data); - TRACE_INFO_NUMBER("Reference count:", header->reference_count); - if (header->reference_count > 1) { - TRACE_INFO_STRING("Dereferencing a referenced collection.", ""); - header->reference_count--; - TRACE_INFO_NUMBER("Number after dereferencing.", - header->reference_count); - } - else { - col_delete_collection(ci); - } - - TRACE_FLOW_STRING("col_destroy_collection", "Exit."); -} - - -/* COPY */ - -/* Wrapper around a more advanced function */ -int col_copy_collection(struct collection_item **collection_copy, - struct collection_item *collection_to_copy, - const char *name_to_use, - int copy_mode) -{ - int error = EOK; - TRACE_FLOW_STRING("col_copy_collection", "Entry."); - - error = col_copy_collection_with_cb(collection_copy, - collection_to_copy, - name_to_use, - copy_mode, - NULL, - NULL); - - TRACE_FLOW_NUMBER("col_copy_collection. Exit. Returning", error); - return error; -} - -/* Create a deep copy of the current collection. */ -/* Referenced collections of the donor are copied as sub collections. */ -int col_copy_collection_with_cb(struct collection_item **collection_copy, - struct collection_item *collection_to_copy, - const char *name_to_use, - int copy_mode, - col_copy_cb copy_cb, - void *ext_data) -{ - int error = EOK; - struct collection_item *new_collection = NULL; - const char *name; - struct collection_header *header; - unsigned depth = 0; - struct col_copy traverse_data; - int flags; - - TRACE_FLOW_STRING("col_copy_collection_with_cb", "Entry."); - - /* Collection is required */ - if (collection_to_copy == NULL) { - TRACE_ERROR_NUMBER("No collection to search!", EINVAL); - return EINVAL; - } - - /* Storage is required too */ - if (collection_copy == NULL) { - TRACE_ERROR_NUMBER("No memory provided to receive collection copy!", EINVAL); - return EINVAL; - } - - /* NOTE: Refine this check if adding a new copy mode */ - if ((copy_mode < 0) || (copy_mode > COL_COPY_TOP)) { - TRACE_ERROR_NUMBER("Invalid copy mode:", copy_mode); - return EINVAL; - } - - /* Determine what name to use */ - if (name_to_use != NULL) - name = name_to_use; - else - name = collection_to_copy->property; - - header = (struct collection_header *)collection_to_copy->data; - - /* Create a new collection */ - error = col_create_collection(&new_collection, name, header->cclass); - if (error) { - TRACE_ERROR_NUMBER("col_create_collection failed returning", error); - return error; - } - - traverse_data.mode = copy_mode; - traverse_data.current_path = NULL; - traverse_data.given_name = NULL; - traverse_data.given_len = 0; - traverse_data.copy_cb = copy_cb; - traverse_data.ext_data = ext_data; - - if (copy_mode == COL_COPY_FLATDOT) flags = COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END; - else if (copy_mode == COL_COPY_FLAT) flags = COL_TRAVERSE_FLAT; - else flags = COL_TRAVERSE_ONELEVEL; - - error = col_walk_items(collection_to_copy, flags, - col_copy_traverse_handler, (void *)(&traverse_data), - NULL, new_collection, &depth); - - if (!error) *collection_copy = new_collection; - else col_destroy_collection(new_collection); - - TRACE_FLOW_NUMBER("col_copy_collection_with_cb returning", error); - return error; - -} - - -/* EXTRACTION */ - -/* Extract collection */ -int col_get_collection_reference(struct collection_item *ci, - struct collection_item **acceptor, - const char *collection_to_find) -{ - struct collection_header *header; - struct collection_item *subcollection = NULL; - int error = EOK; - - TRACE_FLOW_STRING("col_get_collection_reference", "Entry."); - - if ((ci == NULL) || - (ci->type != COL_TYPE_COLLECTION) || - (acceptor == NULL)) { - TRACE_ERROR_NUMBER("Invalid parameter - returning error",EINVAL); - return EINVAL; - } - - if (collection_to_find) { - /* Find a sub collection */ - TRACE_INFO_STRING("We are given subcollection name - search it:", - collection_to_find); - error = col_find_item_and_do(ci, collection_to_find, - COL_TYPE_COLLECTIONREF, - COL_TRAVERSE_DEFAULT, - col_get_subcollection, - (void *)(&subcollection), - COLLECTION_ACTION_FIND); - if (error) { - TRACE_ERROR_NUMBER("Search failed returning error", error); - return error; - } - - if (subcollection == NULL) { - TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", ""); - return ENOENT; - } - } - else { - /* Create reference to the same collection */ - TRACE_INFO_STRING("Creating reference to the top level collection.", ""); - subcollection = ci; - } - - header = (struct collection_header *)subcollection->data; - TRACE_INFO_NUMBER("Count:", header->count); - TRACE_INFO_NUMBER("Ref count:", header->reference_count); - header->reference_count++; - TRACE_INFO_NUMBER("Ref count after increment:", header->reference_count); - *acceptor = subcollection; - - TRACE_FLOW_STRING("col_get_collection_reference", "Success Exit."); - return EOK; -} - -/* Get collection - if current item is a reference get a real collection from it. */ -int col_get_reference_from_item(struct collection_item *ci, - struct collection_item **acceptor) -{ - struct collection_header *header; - struct collection_item *subcollection = NULL; - - TRACE_FLOW_STRING("get_reference_from_item", "Entry."); - - if ((ci == NULL) || - (ci->type != COL_TYPE_COLLECTIONREF) || - (acceptor == NULL)) { - TRACE_ERROR_NUMBER("Invalid parameter - returning error",EINVAL); - return EINVAL; - } - - subcollection = *((struct collection_item **)ci->data); - - header = (struct collection_header *)subcollection->data; - TRACE_INFO_NUMBER("Count:", header->count); - TRACE_INFO_NUMBER("Ref count:", header->reference_count); - header->reference_count++; - TRACE_INFO_NUMBER("Ref count after increment:", header->reference_count); - *acceptor = subcollection; - - TRACE_FLOW_STRING("col_get_reference_from_item", "Success Exit."); - return EOK; -} - -/* ADDITION */ - -/* Add collection to collection */ -int col_add_collection_to_collection(struct collection_item *ci, - const char *sub_collection_name, - const char *as_property, - struct collection_item *collection_to_add, - int mode) -{ - struct collection_item *acceptor = NULL; - const char *name_to_use; - struct collection_header *header; - struct collection_item *collection_copy; - int error = EOK; - struct col_copy traverse_data; - unsigned depth = 0; - - - TRACE_FLOW_STRING("col_add_collection_to_collection", "Entry."); - - if ((ci == NULL) || - (ci->type != COL_TYPE_COLLECTION) || - (collection_to_add == NULL) || - (collection_to_add->type != COL_TYPE_COLLECTION)) { - /* Need to debug here */ - TRACE_ERROR_NUMBER("Missing parameter - returning error", EINVAL); - return EINVAL; - } - - if (sub_collection_name != NULL) { - /* Find a sub collection */ - TRACE_INFO_STRING("We are given subcollection name - search it:", - sub_collection_name); - error = col_find_item_and_do(ci, sub_collection_name, - COL_TYPE_COLLECTIONREF, - COL_TRAVERSE_DEFAULT, - col_get_subcollection, - (void *)(&acceptor), - COLLECTION_ACTION_FIND); - if (error) { - TRACE_ERROR_NUMBER("Search failed returning error", error); - return error; - } - - if (acceptor == NULL) { - TRACE_ERROR_STRING("Search for subcollection returned NULL pointer", ""); - return ENOENT; - } - - } - else { - acceptor = ci; - } - - if (as_property != NULL) - name_to_use = as_property; - else - name_to_use = collection_to_add->property; - - - TRACE_INFO_STRING("Going to use name:", name_to_use); - - - switch (mode) { - case COL_ADD_MODE_REFERENCE: - TRACE_INFO_STRING("We are adding a reference.", ""); - TRACE_INFO_NUMBER("Type of the header element:", - collection_to_add->type); - TRACE_INFO_STRING("Header name we are adding.", - collection_to_add->property); - /* Create a pointer to external collection */ - /* For future thread safety: Transaction start -> */ - error = col_insert_property_with_ref_int(acceptor, - NULL, - COL_DSP_END, - NULL, - 0, - 0, - name_to_use, - COL_TYPE_COLLECTIONREF, - (void *)(&collection_to_add), - sizeof(struct collection_item **), - NULL); - - TRACE_INFO_NUMBER("Type of the header element after adding property:", - collection_to_add->type); - TRACE_INFO_STRING("Header name we just added.", - collection_to_add->property); - if (error) { - TRACE_ERROR_NUMBER("Adding property failed with error:", error); - return error; - } - header = (struct collection_header *)collection_to_add->data; - TRACE_INFO_NUMBER("Count:", header->count); - TRACE_INFO_NUMBER("Ref count:", header->reference_count); - header->reference_count++; - TRACE_INFO_NUMBER("Ref count after increment:", - header->reference_count); - /* -> Transaction end */ - break; - - case COL_ADD_MODE_EMBED: - TRACE_INFO_STRING("We are embedding the collection.", ""); - /* First check if the passed in collection is referenced more than once */ - TRACE_INFO_NUMBER("Type of the header element we are adding:", - collection_to_add->type); - TRACE_INFO_STRING("Header name we are adding.", - collection_to_add->property); - TRACE_INFO_NUMBER("Type of the header element we are adding to:", - acceptor->type); - TRACE_INFO_STRING("Header name we are adding to.", - acceptor->property); - - error = col_insert_property_with_ref_int(acceptor, - NULL, - COL_DSP_END, - NULL, - 0, - 0, - name_to_use, - COL_TYPE_COLLECTIONREF, - (void *)(&collection_to_add), - sizeof(struct collection_item **), - NULL); - - - TRACE_INFO_NUMBER("Adding property returned:", error); - break; - - case COL_ADD_MODE_CLONE: - TRACE_INFO_STRING("We are cloning the collection.", ""); - TRACE_INFO_STRING("Name we will use.", name_to_use); - - /* For future thread safety: Transaction start -> */ - error = col_copy_collection(&collection_copy, - collection_to_add, name_to_use, - COL_COPY_NORMAL); - if (error) return error; - - TRACE_INFO_STRING("We have a collection copy.", collection_copy->property); - TRACE_INFO_NUMBER("Collection type.", collection_copy->type); - TRACE_INFO_STRING("Acceptor collection.", acceptor->property); - TRACE_INFO_NUMBER("Acceptor collection type.", acceptor->type); - - error = col_insert_property_with_ref_int(acceptor, - NULL, - COL_DSP_END, - NULL, - 0, - 0, - name_to_use, - COL_TYPE_COLLECTIONREF, - (void *)(&collection_copy), - sizeof(struct collection_item **), - NULL); - - /* -> Transaction end */ - TRACE_INFO_NUMBER("Adding property returned:", error); - break; - - case COL_ADD_MODE_FLAT: - TRACE_INFO_STRING("We are flattening the collection.", ""); - - traverse_data.mode = COL_COPY_FLAT; - traverse_data.current_path = NULL; - traverse_data.copy_cb = NULL; - traverse_data.ext_data = NULL; - - if ((as_property) && (*as_property)) { - /* The normal assignement generates a warning - * becuase I am assigning const to a non const. - * I can't make the structure member to be const - * since it changes but it changes - * to point to different stings at different time - * This is just an initial sting it will use. - * The logic does not change the content of the string. - * To overcome the issue I use memcpy(); - */ - memcpy(&(traverse_data.given_name), - &(as_property), sizeof(char *)); - traverse_data.given_len = strlen(as_property); - } - else { - traverse_data.given_name = NULL; - traverse_data.given_len = 0; - } - - error = col_walk_items(collection_to_add, COL_TRAVERSE_FLAT, - col_copy_traverse_handler, (void *)(&traverse_data), - NULL, acceptor, &depth); - - TRACE_INFO_NUMBER("Copy collection flat returned:", error); - break; - - case COL_ADD_MODE_FLATDOT: - TRACE_INFO_STRING("We are flattening the collection with dots.", ""); - - traverse_data.mode = COL_COPY_FLATDOT; - traverse_data.current_path = NULL; - traverse_data.copy_cb = NULL; - traverse_data.ext_data = NULL; - - if ((as_property) && (*as_property)) { - /* The normal assignement generates a warning - * becuase I am assigning const to a non const. - * I can't make the structure member to be const - * since it changes but it changes - * to point to different stings at different time - * This is just an initial sting it will use. - * The logic does not change the content of the string. - * To overcome the issue I use memcpy(); - */ - memcpy(&(traverse_data.given_name), - &(as_property), sizeof(char *)); - traverse_data.given_len = strlen(as_property); - } - else { - traverse_data.given_name = NULL; - traverse_data.given_len = 0; - } - - error = col_walk_items(collection_to_add, COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END, - col_copy_traverse_handler, (void *)(&traverse_data), - NULL, acceptor, &depth); - - TRACE_INFO_NUMBER("Copy collection flatdot returned:", error); - break; - - default: - error = EINVAL; - } - - TRACE_FLOW_NUMBER("col_add_collection_to_collection returning:", error); - return error; -} - -/* TRAVERSING */ - -/* Function to traverse the entire collection including optionally - * sub collections */ -int col_traverse_collection(struct collection_item *ci, - int mode_flags, - col_item_fn item_handler, - void *custom_data) -{ - - int error = EOK; - unsigned depth = 0; - - TRACE_FLOW_STRING("col_traverse_collection", "Entry."); - - if (ci == NULL) { - TRACE_ERROR_NUMBER("No collection to traverse!", EINVAL); - return EINVAL; - } - - error = col_walk_items(ci, mode_flags, col_simple_traverse_handler, - NULL, item_handler, custom_data, &depth); - - if ((error != 0) && (error != EINTR_INTERNAL)) { - TRACE_ERROR_NUMBER("Error walking tree", error); - return error; - } - - TRACE_FLOW_STRING("col_traverse_collection", "Success exit."); - return EOK; -} - -/* CHECK */ - -/* Convenience function to check if specific property is in the collection */ -int col_is_item_in_collection(struct collection_item *ci, - const char *property_to_find, - int type, - int mode_flags, - int *found) -{ - int error; - - TRACE_FLOW_STRING("col_is_item_in_collection","Entry."); - - *found = COL_NOMATCH; - error = col_find_item_and_do(ci, property_to_find, - type, mode_flags, - col_is_in_item_handler, - (void *)found, - COLLECTION_ACTION_FIND); - - TRACE_FLOW_NUMBER("col_is_item_in_collection returning", error); - return error; -} - -/* SEARCH */ -/* Search function. Looks up an item in the collection based on the property. - Essentually it is a traverse function with spacial traversing logic. - */ -int col_get_item_and_do(struct collection_item *ci, - const char *property_to_find, - int type, - int mode_flags, - col_item_fn item_handler, - void *custom_data) -{ - int error = EOK; - - TRACE_FLOW_STRING("col_get_item_and_do","Entry."); - - error = col_find_item_and_do(ci, property_to_find, - type, mode_flags, - item_handler, - custom_data, - COLLECTION_ACTION_FIND); - - TRACE_FLOW_NUMBER("col_get_item_and_do returning", error); - return error; -} - - -/* Get raw item */ -int col_get_item(struct collection_item *ci, - const char *property_to_find, - int type, - int mode_flags, - struct collection_item **item) -{ - - int error = EOK; - - TRACE_FLOW_STRING("col_get_item", "Entry."); - - error = col_find_item_and_do(ci, property_to_find, - type, mode_flags, - NULL, (void *)item, - COLLECTION_ACTION_GET); - - TRACE_FLOW_NUMBER("col_get_item returning", error); - return error; -} - -/* DELETE */ -/* Delete property from the collection */ -int col_delete_property(struct collection_item *ci, - const char *property_to_find, - int type, - int mode_flags) -{ - int error = EOK; - int found; - - TRACE_FLOW_STRING("col_delete_property", "Entry."); - found = COL_NOMATCH; - - error = col_find_item_and_do(ci, property_to_find, - type, mode_flags, - NULL, (void *)(&found), - COLLECTION_ACTION_DEL); - - if ((error == EOK) && (found == COL_NOMATCH)) - error = ENOENT; - - TRACE_FLOW_NUMBER("col_delete_property returning", error); - return error; -} - -/* UPDATE */ -/* Update property in the collection */ -int col_update_property(struct collection_item *ci, - const char *property_to_find, - int type, - void *new_data, - int length, - int mode_flags) -{ - int error = EOK; - struct update_property update_data; - - TRACE_FLOW_STRING("col_update_property", "Entry."); - update_data.type = type; - update_data.data = new_data; - update_data.length = length; - update_data.found = COL_NOMATCH; - - error = col_find_item_and_do(ci, property_to_find, - type, mode_flags, - NULL, (void *)(&update_data), - COLLECTION_ACTION_UPDATE); - - if ((error == EOK) && (update_data.found == COL_NOMATCH)) - error = ENOENT; - - TRACE_FLOW_NUMBER("col_update_property returning", error); - return error; -} - - -/* Function to modify the item */ -int col_modify_item(struct collection_item *item, - const char *property, - int type, - const void *data, - int length) -{ - TRACE_FLOW_STRING("col_modify_item", "Entry"); - - if ((item == NULL) || - (item->type == COL_TYPE_COLLECTION) || - (item->type == COL_TYPE_COLLECTIONREF)) { - TRACE_ERROR_NUMBER("Invalid argument or invalid argument type", EINVAL); - return EINVAL; - } - - if (property != NULL) { - if (col_validate_property(property)) { - TRACE_ERROR_STRING("Invalid chracters in the property name", property); - return EINVAL; - } - free(item->property); - item->property = strdup(property); - if (item->property == NULL) { - TRACE_ERROR_STRING("Failed to allocate memory", ""); - return ENOMEM; - } - - /* Update property length and hash if we rename the property */ - item->phash = col_make_hash(property, 0, &(item->property_len)); - TRACE_INFO_NUMBER("Item hash", item->phash); - TRACE_INFO_NUMBER("Item property length", item->property_len); - TRACE_INFO_NUMBER("Item property strlen", strlen(item->property)); - - } - - /* We need to change data ? */ - if(length) { - - /* If type is different or same but it is string or binary we need to - * replace the storage */ - if ((item->type != type) || - ((item->type == type) && - ((item->type == COL_TYPE_STRING) || (item->type == COL_TYPE_BINARY)))) { - TRACE_INFO_STRING("Replacing item data buffer", ""); - free(item->data); - item->data = malloc(length); - if (item->data == NULL) { - TRACE_ERROR_STRING("Failed to allocate memory", ""); - item->length = 0; - return ENOMEM; - } - item->length = length; - } - - TRACE_INFO_STRING("Overwriting item data", ""); - memcpy(item->data, data, item->length); - item->type = type; - - if (item->type == COL_TYPE_STRING) - ((char *)(item->data))[item->length - 1] = '\0'; - } - - TRACE_FLOW_STRING("col_modify_item", "Exit"); - return EOK; -} - - -/* Set collection class */ -int col_set_collection_class(struct collection_item *item, - unsigned cclass) -{ - struct collection_header *header; - - TRACE_FLOW_STRING("col_set_collection_class", "Entry"); - - if (item->type != COL_TYPE_COLLECTION) { - TRACE_INFO_NUMBER("Not a collectin object. Type is", item->type); - return EINVAL; - } - - header = (struct collection_header *)item->data; - header->cclass = cclass; - TRACE_FLOW_STRING("col_set_collection_class", "Exit"); - return EOK; -} - -/* Get collection class */ -int col_get_collection_class(struct collection_item *item, - unsigned *cclass) -{ - struct collection_header *header; - - TRACE_FLOW_STRING("col_get_collection_class", "Entry"); - - if (item->type != COL_TYPE_COLLECTION) { - TRACE_ERROR_NUMBER("Not a collection object. Type is", item->type); - return EINVAL; - } - - header = (struct collection_header *)item->data; - *cclass = header->cclass; - TRACE_FLOW_STRING("col_get_collection_class", "Exit"); - return EOK; -} - -/* Get collection count */ -int col_get_collection_count(struct collection_item *item, - unsigned *count) -{ - struct collection_header *header; - - TRACE_FLOW_STRING("col_get_collection_count", "Entry"); - - if (item->type != COL_TYPE_COLLECTION) { - TRACE_ERROR_NUMBER("Not a collectin object. Type is", item->type); - return EINVAL; - } - - header = (struct collection_header *)item->data; - *count = header->count; - TRACE_FLOW_STRING("col_get_collection_count", "Exit"); - return EOK; - -} - -/* Convinience function to check if the collection is of the specific class */ -/* In case of internal error assumes that collection is not of the right class */ -int col_is_of_class(struct collection_item *item, unsigned cclass) -{ - int error = EOK; - unsigned ret_class = 0; - - TRACE_FLOW_STRING("col_is_of_class invoked", ""); - - error = col_get_collection_class(item, &ret_class); - if (error || (ret_class != cclass)) - return 0; - else - return 1; -} - -/* Get propery */ -const char *col_get_item_property(struct collection_item *ci, - int *property_len) -{ - if (property_len != NULL) *property_len = ci->property_len; - return ci->property; -} - -/* Get type */ -int col_get_item_type(struct collection_item *ci) -{ - return ci->type; -} - -/* Get length */ -int col_get_item_length(struct collection_item *ci) -{ - return ci->length; -} - -/* Get data */ -void *col_get_item_data(struct collection_item *ci) -{ - return ci->data; -} - -/* Get hash */ -uint64_t col_get_item_hash(struct collection_item *ci) -{ - return ci->phash; -} - -/* Calculates hash of the string using internal hashing - * algorithm. Populates "length" with length - * of the string not counting 0. - * Length argument can be NULL. - */ -uint64_t col_make_hash(const char *string, int sub_len, int *length) -{ - uint64_t hash = 0; - int str_len = 0; - - TRACE_FLOW_STRING("col_make_hash called for string:", string); - - if (string) { - hash = FNV1a_base; - while (string[str_len] != 0) { - - /* Check if we need to stop */ - if ((sub_len > 0) && (str_len == sub_len)) break; - - hash = hash ^ toupper(string[str_len]); - hash *= FNV1a_prime; - str_len++; - } - } - - if (length) *length = str_len; - - TRACE_FLOW_NUMBER("col_make_hash returning hash:", hash); - - return hash; -} |