/* 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 teal 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; }; /******************** 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 */ static 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 "next" we can use delete_item() in case of error */ item->next = NULL; /* 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 = FNV1a_base; item->property_len = 0; while (property[item->property_len] != 0) { item->phash = item->phash ^ toupper(property[item->property_len]); item->phash *= FNV1a_prime; 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); /* Deal with other properties of the item */ TRACE_INFO_NUMBER("About to set type to:", type); item->type = type; 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; 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; 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->last != NULL) 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 */ if (header->last != NULL) 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 = NULL; 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 = NULL; 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 = NULL; } /* 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 wropper. */ 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; } /* 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",""); 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 path_data { char *name; int length; struct path_data *previous_path; }; 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) { 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] = '!'; 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 *item; struct collection_item *new_collection = NULL; TRACE_FLOW_STRING("col_copy_traverse_handler", "Entry."); parent = (struct collection_item *)passed_traverse_data; /* Skip current element but rather work with next if it is not NULL */ item = current->next; if (item == NULL) return error; /* Check if this is a special case of sub collection */ if (item->type == COL_TYPE_COLLECTIONREF) { TRACE_INFO_STRING("Found a subcollection we need to copy. Name:", item->property); error = col_copy_collection(&new_collection, *((struct collection_item **)(item->data)), item->property); 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, item->property, COL_TYPE_COLLECTIONREF, (void *)(&new_collection), sizeof(struct collection_item **), NULL); if (error) { TRACE_ERROR_NUMBER("Add property returned error:", error); return error; } } else { error = col_insert_property_with_ref_int(parent, NULL, COL_DSP_END, NULL, 0, 0, item->property, item->type, item->data, item->length, NULL); if (error) { TRACE_ERROR_NUMBER("Add property returned error:", error); return error; } } TRACE_FLOW_NUMBER("col_copy_traverse_handler returning", error); return error; } /* Function to copy collection as flat set */ static int col_copy_collection_flat(struct collection_item *acceptor, struct collection_item *donor) { int error = EOK; struct collection_iterator *iter = NULL; struct collection_item *item = NULL; struct collection_item *new_item = NULL; struct collection_item *current = NULL; TRACE_FLOW_STRING("col_copy_collection_flat", "Entry"); /* Bind iterator in flat mode */ error = col_bind_iterator(&iter, donor, COL_TRAVERSE_FLAT); if (error) { TRACE_ERROR_NUMBER("Failed to bind iterator:", error); return error; } /* Skip header */ error = col_iterate_collection(iter, &item); if (error) { TRACE_ERROR_NUMBER("Failed to iterate on header:", error); col_unbind_iterator(iter); return error; } current = item->next; while (current) { /* Loop through a collection */ error = col_iterate_collection(iter, &item); if (error) { TRACE_ERROR_NUMBER("Failed to iterate:", error); col_unbind_iterator(iter); return error; } /* Create new item */ error = col_allocate_item(&new_item, item->property, item->data, item->length, item->type); if(error) { TRACE_ERROR_NUMBER("Error allocating end item.", error); col_unbind_iterator(iter); return error; } /* Insert this item into collection */ error = col_insert_item(acceptor, NULL, new_item, COL_DSP_END, NULL, 0, COL_INSERT_DUPOVER); if(error) { TRACE_ERROR_NUMBER("Error allocating end item.", error); col_unbind_iterator(iter); col_delete_item(new_item); return error; } current = item->next; } col_unbind_iterator(iter); TRACE_FLOW_NUMBER("col_copy_collection_flat Exit 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; } /* Collection can be referenced by other collection */ header = (struct collection_header *)(ci->data); 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 */ /* Create a deep copy of the current collection. */ /* Referenced collections of the donor are copied as sub collections. */ int col_copy_collection(struct collection_item **collection_copy, struct collection_item *collection_to_copy, const char *name_to_use) { int error = EOK; struct collection_item *new_collection = NULL; const char *name; struct collection_header *header; unsigned depth = 0; TRACE_FLOW_STRING("col_copy_collection", "Entry."); /* Collection is requered */ 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; } /* 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; } error = col_walk_items(collection_to_copy, COL_TRAVERSE_ONELEVEL, col_copy_traverse_handler, new_collection, NULL, NULL, &depth); if (!error) *collection_copy = new_collection; else col_destroy_collection(new_collection); TRACE_FLOW_NUMBER("col_copy_collection 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) || (collection_to_find == NULL)) { TRACE_ERROR_NUMBER("Invalid parameter - returning error",EINVAL); return EINVAL; } /* 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; } 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 */ /* FIXME - allow to add collection to a collection with disposition */ 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; 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); 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.", ""); error = col_copy_collection_flat(acceptor, collection_to_add); TRACE_INFO_NUMBER("Copy collection flat 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 = FNV1a_base; item->property_len = 0; while (property[item->property_len] != 0) { item->phash = item->phash ^ toupper(property[item->property_len]); item->phash *= FNV1a_prime; item->property_len++; } } /* 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; } /* Grow iteration stack */ static int col_grow_stack(struct collection_iterator *iterator, unsigned desired) { int grow_by = 0; struct collection_item **temp; TRACE_FLOW_STRING("col_grow_stack", "Entry."); if (desired > iterator->stack_size) { grow_by = (((desired - iterator->stack_size) / STACK_DEPTH_BLOCK) + 1) * STACK_DEPTH_BLOCK; temp = (struct collection_item **)realloc(iterator->stack, grow_by * sizeof(struct collection_item *)); if (temp == NULL) { TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM); return ENOMEM; } iterator->stack = temp; iterator->stack_size += grow_by; } TRACE_FLOW_STRING("col_grow_stack", "Exit."); return EOK; } /* Bind iterator to a collection */ int col_bind_iterator(struct collection_iterator **iterator, struct collection_item *ci, int mode_flags) { int error; struct collection_header *header; struct collection_iterator *iter = NULL; TRACE_FLOW_STRING("col_bind_iterator", "Entry."); /* Do some argument checking first */ if ((iterator == NULL) || (ci == NULL)) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } iter = (struct collection_iterator *)malloc(sizeof(struct collection_iterator)); if (iter == NULL) { TRACE_ERROR_NUMBER("Error allocating memory for the iterator.", ENOMEM); return ENOMEM; } /* Allocate memory for the stack */ iter->stack = NULL; iter->stack_size = 0; iter->stack_depth = 0; iter->item_level = 0; iter->flags = mode_flags; TRACE_INFO_NUMBER("Iterator flags", iter->flags); /* Allocate memory for stack */ error = col_grow_stack(iter, 1); if(error) { free(iter); TRACE_ERROR_NUMBER("Error growing stack.", error); return error; } /* Create a special end item */ error = col_allocate_item(&(iter->end_item), "", NULL, 0, COL_TYPE_END); if(error) { free(iter); TRACE_ERROR_NUMBER("Error allocating end item.", error); return error; } /* Make sure that we tie iterator to the collection */ header = (struct collection_header *)ci->data; header->reference_count++; iter->top = ci; *(iter->stack) = ci; iter->stack_depth++; *iterator = iter; TRACE_FLOW_STRING("col_bind_iterator", "Exit"); return EOK; } /* Stop processing this subcollection and move to the next item in the * collection 'level' levels up.*/ int col_iterate_up(struct collection_iterator *iterator, unsigned level) { TRACE_FLOW_STRING("iterate_up", "Entry"); if (iterator == NULL) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } TRACE_INFO_NUMBER("Going up:", level); TRACE_INFO_NUMBER("Current stack depth:", iterator->stack_depth); /* If level is big just move to the top, * that will end the iteration process. */ if (level >= iterator->stack_depth) iterator->stack_depth = 0; else iterator->stack_depth -= level; TRACE_INFO_NUMBER("Stack depth at the end:", iterator->stack_depth); TRACE_FLOW_STRING("col_iterate_up", "Exit"); return EOK; } /* How deep are we relative to the top level.*/ int col_get_iterator_depth(struct collection_iterator *iterator, int *depth) { TRACE_FLOW_STRING("col_get_iterator_depth", "Entry"); if ((iterator == NULL) || (depth == NULL)) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } *depth = iterator->stack_depth - 1; TRACE_INFO_NUMBER("Stack depth at the end:", iterator->stack_depth); TRACE_FLOW_STRING("col_get_iterator_depth","Exit"); return EOK; } /* What was the level of the last item we got? */ int col_get_item_depth(struct collection_iterator *iterator, int *depth) { TRACE_FLOW_STRING("col_get_item_depth", "Entry"); if ((iterator == NULL) || (depth == NULL)) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } *depth = iterator->item_level; TRACE_INFO_NUMBER("Item level at the end:", iterator->item_level); TRACE_FLOW_STRING("col_get_item_depth","Exit"); return EOK; } /* Unbind the iterator from the collection */ void col_unbind_iterator(struct collection_iterator *iterator) { TRACE_FLOW_STRING("col_unbind_iterator", "Entry."); if (iterator != NULL) { col_destroy_collection(iterator->top); col_delete_item(iterator->end_item); free(iterator->stack); free(iterator); } TRACE_FLOW_STRING("col_unbind_iterator", "Exit"); } /* Get items from the collection one by one following the tree */ int col_iterate_collection(struct collection_iterator *iterator, struct collection_item **item) { int error; struct collection_item *current; struct collection_item *other; TRACE_FLOW_STRING("col_iterate_collection", "Entry."); /* Check if we have storage for item */ if (item == NULL) { TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL); return EINVAL; } while (1) { TRACE_INFO_NUMBER("Stack depth:", iterator->stack_depth); /* Are we done? */ if (iterator->stack_depth == 0) { TRACE_FLOW_STRING("We are done.", ""); *item = NULL; return EOK; } /* Is current item available */ current = iterator->stack[iterator->stack_depth - 1]; iterator->item_level = iterator->stack_depth - 1; /* We are not done so check if we have an item */ if (current != NULL) { TRACE_INFO_STRING("Current item:", current->property); TRACE_INFO_NUMBER("Current item type:", current->type); /* Is this a collection reference */ if (current->type == COL_TYPE_COLLECTIONREF) { /* We do follow references? */ TRACE_INFO_STRING("Current item:", "collection reference"); if ((iterator->flags & COL_TRAVERSE_IGNORE) == 0) { /* We should not ignore - then move on */ TRACE_INFO_STRING("Collection references are not ignored", ""); error = col_grow_stack(iterator, iterator->stack_depth + 1); if (error) { TRACE_ERROR_NUMBER("Error growing stack.", error); return error; } /* Do we need to go deeper than one level ? */ if ((iterator->flags & COL_TRAVERSE_ONELEVEL) == 0) { TRACE_INFO_STRING("Need to go deeper", ""); /* We need to go deeper... */ /* Do we need to show headers but not reference? */ if ((iterator->flags & COL_TRAVERSE_ONLYSUB) != 0) { TRACE_INFO_STRING("Instructed to show header not reference", ""); other = *((struct collection_item **)current->data); iterator->stack[iterator->stack_depth] = other->next; iterator->item_level = iterator->stack_depth; *item = other; } /* Do we need to show both? */ else if ((iterator->flags & COL_TRAVERSE_SHOWSUB) != 0) { TRACE_INFO_STRING("Instructed to show header and reference",""); iterator->stack[iterator->stack_depth] = *((struct collection_item **)(current->data)); *item = current; /* Do not need to adjust level here */ } /* Do not show either */ else if ((iterator->flags & COL_TRAVERSE_FLAT) != 0) { TRACE_INFO_STRING("Instructed not to show header and reference",""); other = *((struct collection_item **)current->data); iterator->stack[iterator->stack_depth] = other->next; iterator->stack[iterator->stack_depth - 1] = current->next; iterator->stack_depth++; /* Do not need to adjust level here */ continue; } /* We need to show reference only */ else { TRACE_INFO_STRING("Instructed to show reference only", ""); other = *((struct collection_item **)current->data); TRACE_INFO_STRING("Sub collection:", other->property); TRACE_INFO_NUMBER("Sub collection type:", other->type); iterator->stack[iterator->stack_depth] = other->next; if (other->next != NULL) { TRACE_INFO_STRING("Will show this item next time:", other->next->property); TRACE_INFO_NUMBER("Will show this item next time type:", other->next->type); } *item = current; TRACE_INFO_NUMBER("Level of the reference:", iterator->item_level); /* Do not need to adjust level here */ } TRACE_INFO_STRING("We return item:", (*item)->property); TRACE_INFO_NUMBER("We return item type:", (*item)->type); TRACE_INFO_STRING("Moving to the next item on the previous item in stack", ""); iterator->stack[iterator->stack_depth - 1] = current->next; iterator->stack_depth++; } else { TRACE_INFO_STRING("Instructed to parse just one level", ""); /* On one level - just return current */ *item = current; TRACE_INFO_STRING("Moving to the next item on one level", ""); iterator->stack[iterator->stack_depth - 1] = current->next; } break; } else { /* We need to ignore references so move to the next item */ TRACE_INFO_STRING("Stepping over the reference", ""); iterator->stack[iterator->stack_depth - 1] = current->next; continue; } } else { /* Got a normal item - return it and move to the next one */ if ((current->type == COL_TYPE_COLLECTION) && ((iterator->flags & COL_TRAVERSE_FLAT) != 0) && (iterator->stack_depth > 1)) { TRACE_INFO_STRING("Header of the sub collection in flat case ", ""); iterator->stack[iterator->stack_depth - 1] = current->next; continue; } else { TRACE_INFO_STRING("Simple item", ""); *item = current; iterator->stack[iterator->stack_depth - 1] = current->next; } break; } } else { /* Item is NULL */ TRACE_INFO_STRING("Finished level", "moving to upper level"); iterator->stack_depth--; /* Remember that item_level is zero based while depth is size * so we decrease and then assign. */ TRACE_INFO_NUMBER("Stack depth at the end:", iterator->stack_depth); if ((iterator->flags & COL_TRAVERSE_END) != 0) { /* Show end element * a) If we are flattening but at the top * b) We are not flattening */ if ((((iterator->flags & COL_TRAVERSE_FLAT) != 0) && (iterator->stack_depth == 0)) || ((iterator->flags & COL_TRAVERSE_FLAT) == 0)) { /* Return dummy entry to indicate the end of the collection */ TRACE_INFO_STRING("Finished level", "told to return END"); *item = iterator->end_item; break; } } else { /* Move to next level */ continue; } } } TRACE_FLOW_STRING("iterate_collection", "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; } /* Set time stamp in the collection - FIXME move to another level */ int col_set_timestamp(struct collection_item *ci, struct collection_item **timestr_ref, struct collection_item **timeint_ref) { time_t utctime; struct tm time_struct; char time_array[TIME_ARRAY_SIZE+1]; int len; struct collection_item *timestr = NULL; struct collection_item *timeint = NULL; int error = EOK; TRACE_FLOW_STRING("col_set_timestamp", "Entry point"); utctime = time(NULL); localtime_r(&utctime, &time_struct); len = strftime(time_array, TIME_ARRAY_SIZE, DATE_FORMAT, &time_struct); if (len == 0) { TRACE_ERROR_STRING("col_set_timestamp", "CODING ERROR - INCREASE THE BUFFER"); return EMSGSIZE; } TRACE_INFO_STRING("Timestamp:", time_array); /* Check if we have the timestamp item already */ error = col_get_item(ci, TS_NAME, COL_TYPE_STRING, COL_TRAVERSE_IGNORE, ×tr); if (error) { TRACE_ERROR_NUMBER("search failed with error:", error); return error; } if (timestr != NULL) { /* There is a timestamp */ free(timestr->data); timestr->data = strdup(time_array); if (timestr->data == NULL) { TRACE_ERROR_NUMBER("failed to add timestamp property:", error); return ENOMEM; } timestr->length = len+1; *timestr_ref = timestr; } else { /* Add timestamp to the collection */ error = col_add_str_property_with_ref(ci, NULL, TS_NAME, time_array, len+1, timestr_ref); if (error) { TRACE_ERROR_NUMBER("failed to add timestamp property:", error); return error; } } /* Check if we have the time item already */ error = col_get_item(ci, T_NAME, COL_TYPE_INTEGER, COL_TRAVERSE_IGNORE, &timeint); if (error) { TRACE_ERROR_NUMBER("search failed with error:", error); return error; } if (timeint != NULL) { /* There is a time property */ *((int *)(timeint->data)) = utctime; *timeint_ref = timeint; } else { /* Add time to the collection */ error = col_add_int_property_with_ref(ci, NULL, T_NAME, utctime, timeint_ref); if (error) { TRACE_ERROR_NUMBER("failed to add time property:", error); return error; } } TRACE_FLOW_STRING("col_set_timestamp", "Exit point"); return EOK; }