/* ELAPI Implemenation of the collection interface. Copyright (C) Dmitri Pal 2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include "elapi_collection.h" #include "elapi_debug.h" /* Internal constants defined to denote actions that can be performed by find handler */ #define ELAPI_ACTION_FIND 1 #define ELAPI_ACTION_DEL 2 #define ELAPI_ACTION_UPDATE 3 #define ELAPI_ACTION_GET 4 /* Special internal error code to indicate that collection search was interrupted */ #define EINTR_INTERNAL 10000 /* Potential subjest for management with libtools */ #define DATE_FORMAT "%c" #define TIME_ARRAY_SIZE 100 /* 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 find_item_and_do(struct collection_item *ci, char *property_to_find, int type, int mode_flags, item_fn item_handler, void *custom_data, int action); /* Traverse callback for find & delete function */ static int act_traverse_handler(struct collection_item *head, struct collection_item *previous, struct collection_item *current, void *passed_traverse_data, 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, item_fn user_item_handler, void *custom_data, int *stop); /******************** SUPPLIMENTARY FUNCTIONS ****************************/ /* BASIC OPERATIONS */ /* Function that checks if property can be added */ static int validate_property(char *property) { DEBUG_STRING("validate_property","Entry point."); /* Only alpha numeric characters are allowed in names of the properties */ int invalid = 0; char *check; check = property; while(*check != '\0') { if((!isalnum((int)(*check))) && (*check!='_')) { invalid = 1; break; } check++; } DEBUG_NUMBER("validate_property. Returning ",invalid); return invalid; } /* Function that cleans the item */ static void delete_item(struct collection_item *item) { DEBUG_STRING("delete_item","Entry point."); if(item == (struct collection_item *)NULL) return; if(item->property != NULL) free(item->property); if(item->data != NULL) free(item->data); free(item); DEBUG_STRING("delete_item","Exit."); } /* A generic function to allocate a property item */ static int allocate_item(struct collection_item **ci,char *property,void *item_data,int length, int type) { struct collection_item *item = NULL; int error = 0; errno = 0; DEBUG_STRING("allocate_item","Entry point."); DEBUG_NUMBER("Will be using type:",type); /* Check the length */ if(length >= ELAPI_MAX_DATA) { DEBUG_STRING("allocate_item","Data to long."); return EMSGSIZE; } if(validate_property(property)) { DEBUG_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 == (struct collection_item *)(NULL)) { error = errno; DEBUG_STRING("allocate_item","Malloc failed."); return error; } /* After we initialize "next" we can use delete_item() in case of error */ item->next = (struct collection_item *)(NULL); /* Copy property */ item->property = strdup(property); if(item->property == NULL) { error = errno; DEBUG_STRING("allocate_item","Failed to dup property."); delete_item(item); return error; } item->property_len = strlen(item->property); /* Deal with data */ item->data = malloc(length); if(item->data == NULL) { DEBUG_STRING("allocate_item","Failed to dup data."); delete_item(item); return errno; } memcpy(item->data,item_data,length); /* Deal with other properties of the item */ DEBUG_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 == ELAPI_TYPE_STRING) *(((char *)(item->data))+length-1) = '\0'; *ci = item; DEBUG_STRING("Item property",item->property); DEBUG_NUMBER("Item property type",item->type); DEBUG_NUMBER("Item data length",item->length); DEBUG_STRING("allocate_item","Success exit."); return 0; } /* Add item to the end of collection */ /* Can add itself to itself - nice...*/ static int add_item_to_collection(struct collection_item *collection,struct collection_item *item) { struct collection_header *header; DEBUG_STRING("add_item_to_collection","Entry point."); if(collection == (struct collection_item *)(NULL)) { DEBUG_STRING("add_item_to_collection","Collection accepting is NULL"); if((item != (struct collection_item *)(NULL)) && (item->type == ELAPI_TYPE_COLLECTION)) { /* This is a special case of self creation */ DEBUG_STRING("add_item_to_collection","Adding header item to new collection."); collection = item; } } /* We can add items only to collections */ if(collection->type != ELAPI_TYPE_COLLECTION) { DEBUG_STRING("add_item_to_collection","Attempt to add item to non collection."); DEBUG_STRING("Collection name:",collection->property); DEBUG_NUMBER("Collection type:",collection->type); return EINVAL; } header = (struct collection_header *)(collection->data); /* Link new item to the last item in the list if there any */ if(header->last != (struct collection_item *)(NULL)) { (header->last)->next = item; } /* Make sure we save a new last element */ header->last = item; header->count++; DEBUG_STRING("Collection:",collection->property); DEBUG_STRING("Just added item is:",item->property); DEBUG_NUMBER("Item type.",item->type); DEBUG_NUMBER("Number of items in collection now is.",header->count); DEBUG_STRING("add_item_to_collection","Success exit."); return EOK; } /* TRAVERSE HANDLERS */ /* Special handler to just set a flag if the item is found */ static int is_in_item_handler(char *property, int property_len, int type, void *data, int length, void *found, int *dummy) { DEBUG_STRING("is_in_item_handler","Entry."); DEBUG_STRING("Property:",property); DEBUG_NUMBER("Property length:",property_len); DEBUG_NUMBER("Type:",type); DEBUG_NUMBER("Length:",length); *((int *)(found)) = ELAPI_MATCH; DEBUG_STRING("is_in_item_handler","Success Exit."); return EOK; } /* Special handler to retrieve the sub collection */ static int get_subcollection(char *property, int property_len, int type, void *data, int length, void *found, int *dummy) { DEBUG_STRING("get_subcollection","Entry."); DEBUG_STRING("Property:",property); DEBUG_NUMBER("Property length:",property_len); DEBUG_NUMBER("Type:",type); DEBUG_NUMBER("Length:",length); *((struct collection_item **)(found)) = *((struct collection_item **)(data)); DEBUG_STRING("get_subcollection","Success Exit."); return EOK; } /* ADD PROPERTY */ /* Add a single property to a collection. Returns a pointer to a newly allocated property */ static struct collection_item *add_property(struct collection_item *collection, char *subcollection, char *property, void *item_data, int length, int type, int *error) { struct collection_item *item; struct collection_item *acceptor = (struct collection_item *)(NULL); DEBUG_STRING("add_property","Entry."); /* Allocate item */ DEBUG_NUMBER("Property type to add",type); *error = allocate_item(&item,property,item_data,length, type); if(*error) return (struct collection_item *)(NULL); DEBUG_STRING("Created item:",item->property); DEBUG_NUMBER("Item has type:",item->type); /* Add item to collection */ if(subcollection == NULL) acceptor = collection; else { DEBUG_STRING("Subcollection id not null, searching",subcollection); *error = find_item_and_do(collection, subcollection, ELAPI_TYPE_COLLECTIONREF, ELAPI_TRAVERSE_DEFAULT, get_subcollection,(void *)(&acceptor),ELAPI_ACTION_FIND); if(*error) { DEBUG_NUMBER("Search for subcollection returned error:",*error); delete_item(item); return (struct collection_item *)(NULL); } if(acceptor == (struct collection_item *)(NULL)) { DEBUG_STRING("Search for subcollection returned NULL pointer",""); delete_item(item); *error=ENOENT; return (struct collection_item *)(NULL); } } *error = add_item_to_collection(acceptor,item); if(*error) { delete_item(item); return (struct collection_item *)(NULL); } DEBUG_STRING("add_property","Success Exit."); return item; } /* CLEANUP */ /* Cleans the collection tree including current item. */ /* After the execution passed in variable should not be used - memory is gone!!! */ static void delete_collection(struct collection_item *ci) { struct collection_item *other_collection; DEBUG_STRING("delete_collection","Entry."); if(ci == (struct collection_item *)(NULL)) { DEBUG_STRING("delete_collection","Nothing to do Exit."); return; } DEBUG_STRING("Real work to do",""); delete_collection(ci->next); /* Handle external or embedded collection */ if(ci->type == ELAPI_TYPE_COLLECTIONREF) { /* Our data is a pointer to a whole external collection so dereference it or delete */ other_collection = *((struct collection_item **)(ci->data)); destroy_collection(other_collection); } /* Delete this item */ delete_item(ci); DEBUG_STRING("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 { char *name_to_find; int name_len_to_find; int type_to_match; char *given_name; int given_len; struct path_data *current_path; int action; }; /* Create a new name */ static int create_path_data(struct path_data **name_path, char *name, int length, char *property, int property_len) { int error = EOK; struct path_data *new_name_path; DEBUG_STRING("create_path_data","Entry."); DEBUG_STRING("Constructing path from name:",name); DEBUG_STRING("Constructing path from property:",property); /* Allocate structure */ errno = 0; new_name_path = (struct path_data *)(malloc(sizeof(struct path_data))); if(new_name_path == (struct path_data *)(NULL)) return errno; new_name_path->name=malloc(length+property_len+2); if(new_name_path->name == NULL) { error = errno; DEBUG_NUMBER("Failed to allocate memory for new path name. Errno",error); free((void *)(new_name_path)); return error; } /* 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'; DEBUG_STRING("Name so far:",new_name_path->name); DEBUG_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; DEBUG_STRING("Constructed path",new_name_path->name); DEBUG_NUMBER("create_path_data. Returning:",error); return error; } /* Matching item name and type */ static int match_item(struct collection_item *current, struct find_name *traverse_data) { char *find_str; char *start; char *data_str; DEBUG_STRING("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')) { DEBUG_STRING("match_item","Returning MATCH because there is no search criteria!"); return ELAPI_MATCH; } /* 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; DEBUG_STRING("Searching for:",traverse_data->name_to_find); DEBUG_STRING("Item name:",current->property); DEBUG_STRING("Current path:",traverse_data->current_path->name); DEBUG_NUMBER("Searching:",toupper(*find_str)); DEBUG_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)) { DEBUG_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 */ DEBUG_STRING("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 { DEBUG_STRING("match_item","Returning NO match!"); return ELAPI_NOMATCH; } } else { DEBUG_STRING("match_item","Returning MATCH!"); return ELAPI_MATCH; } } else if((find_str == traverse_data->name_to_find) && (*(data_str-1) == '.')) return ELAPI_MATCH; data_str--; find_str--; DEBUG_NUMBER("Searching:",toupper(*find_str)); DEBUG_NUMBER("Have:",toupper(*data_str)); } } DEBUG_STRING("match_item","Returning NO match!"); return ELAPI_NOMATCH; } /* Function to delete the data that contains search path */ static void delete_path_data(struct path_data *path) { DEBUG_STRING("delete_path_data","Entry."); if(path!= (struct path_data *)(NULL)) { DEBUG_STRING("delete_path_data","Item to delete exits."); if(path->previous_path != (struct path_data *)(NULL)) { DEBUG_STRING("delete_path_data","But previous item to delete exits to. Nesting."); delete_path_data(path->previous_path); } if(path->name != NULL) { DEBUG_STRING("delete_path_data Deleting path:",path->name); free(path->name); } DEBUG_STRING("delete_path_data","Deleting path element"); free((void *)(path)); } DEBUG_STRING("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 defferent traverse handlers for different cases */ static int walk_items(struct collection_item *ci, int mode_flags, internal_item_fn traverse_handler, void *traverse_data, item_fn user_item_handler, void *custom_data) { struct collection_item *current; struct collection_item *parent; struct collection_item *sub; int stop = 0; int error = EOK; DEBUG_STRING("walk_items","Entry."); DEBUG_NUMBER("Mode flags:",mode_flags); current = ci; while(current != (struct collection_item *)(NULL)) { DEBUG_STRING("Processing item:",current->property); DEBUG_NUMBER("Item type:",current->type); if(current->type == ELAPI_TYPE_COLLECTIONREF) { DEBUG_STRING("Subcollection:",current->property); if((mode_flags & ELAPI_TRAVERSE_IGNORE) == 0) { DEBUG_STRING("Subcollection is not ignored.",""); /* We are not ignoring sub collections */ error = traverse_handler(ci,parent,current,traverse_data,user_item_handler,custom_data,&stop); if(stop != 0) { DEBUG_STRING("Traverse handler returned STOP.",""); error = EINTR_INTERNAL; } if(error) { DEBUG_NUMBER("Traverse handler returned error.",error); return error; } if((mode_flags & ELAPI_TRAVERSE_ONELEVEL) == 0) { DEBUG_STRING("Before diving into sub collection",""); sub = *((struct collection_item **)(current->data)); DEBUG_STRING("Sub collection name",sub->property); DEBUG_NUMBER("Header type",sub->type); /* We need to go into sub collections */ error = walk_items(sub, mode_flags,traverse_handler,traverse_data, user_item_handler, custom_data); DEBUG_STRING("Returned from sub collection processing",""); DEBUG_STRING("Done processing item:",current->property); DEBUG_NUMBER("Done processing item type:",current->type); } } } else /* 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) { DEBUG_STRING("Traverse handler returned STOP.",""); error = EINTR_INTERNAL; } if(error) { DEBUG_NUMBER("Traverse handler returned error.",error); return error; } parent = current; current = current->next; } DEBUG_STRING("Out of loop",""); if((mode_flags & ELAPI_TRAVERSE_END) != 0) { DEBUG_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); } DEBUG_NUMBER("walk_items. Returns: ",error); 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 dotted notatation like this: */ /* parent.child.subchild.subsubchild etc. */ static int find_item_and_do(struct collection_item *ci, char *property_to_find, int type, int mode_flags, item_fn item_handler, void *custom_data, int action) { int error = EOK; struct find_name *traverse_data = NULL; DEBUG_STRING("find_item_and_do","Entry."); /* Item handler is always required */ if((item_handler == (item_fn)(NULL)) && (action ==ELAPI_ACTION_FIND)) { DEBUG_NUMBER("No item handler - returning error!",EINVAL); return EINVAL; } /* Make sure that there is anything to search */ type &= ELAPI_TYPE_ANY; if((ci == (struct collection_item *)(NULL)) || ((property_to_find == NULL) && (type == 0)) || ((*property_to_find == '\0') && (type == 0))) { DEBUG_NUMBER("No item search criteria specified - returning error!",ENOKEY); return ENOKEY; } /* Prepare data for traversal */ errno = 0; traverse_data= (struct find_name *)(malloc(sizeof(struct find_name))); if(traverse_data == (struct find_name *)(NULL)) { error = errno; DEBUG_NUMBER("Failed to allocate traverse data memory - returning error!",errno); return error; } DEBUG_STRING("find_item_and_do","Filling in traverse data."); traverse_data->name_to_find = property_to_find; traverse_data->name_len_to_find = strlen(property_to_find); traverse_data->type_to_match = type; traverse_data->given_name = NULL; traverse_data->given_len = 0; traverse_data->current_path = (struct path_data *)(NULL); traverse_data->action = action; mode_flags |= ELAPI_TRAVERSE_END; DEBUG_STRING("find_item_and_do","About to walk the tree."); DEBUG_NUMBER("Traverse flags",mode_flags); error = walk_items(ci, mode_flags, act_traverse_handler, (void *)traverse_data, item_handler, custom_data); if(traverse_data->current_path != (struct path_data *)(NULL)) { DEBUG_STRING("find_item_and_do","Path was not cleared - deleting"); delete_path_data(traverse_data->current_path); } free((void *)(traverse_data)); if(error != EINTR_INTERNAL) { DEBUG_NUMBER("Walk items returned error. Returning: ",error); return error; } else { DEBUG_STRING("Walk items returned SUCCESS.",""); return EOK; } } /* Function to replace data in the item */ static int update_item(struct collection_item *current, struct update_property *update_data) { int error = EOK; DEBUG_STRING("update_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 == ELAPI_TYPE_STRING) || (current->type == ELAPI_TYPE_BINARY)))) { DEBUG_STRING("Replacing item data buffer",""); free(current->data); current->data = malloc(update_data->length); if(current->data == NULL) { error = errno; DEBUG_STRING("Failed to allocate memory",""); return error; } current->length = update_data->length; } DEBUG_STRING("Overwriting item data",""); memcpy(current->data,update_data->data,current->length); current->type = update_data->type; if(current->type == ELAPI_TYPE_STRING) *(((char *)(current->data))+current->length-1) = '\0'; DEBUG_STRING("update_item","Exit"); return EOK; } /* TRAVERSE CALLBACKS */ /* Traverse handler for simple traverse function */ /* Handler must be able to deal with NULL current item */ static int simple_traverse_handler(struct collection_item *head, struct collection_item *previous, struct collection_item *current, void *traverse_data, item_fn user_item_handler, void *custom_data, int *stop) { int error = EOK; DEBUG_STRING("simple_traverse_handler","Entry."); if(current != (struct collection_item *)(NULL)) error = user_item_handler(current->property, current->property_len, current->type, current->data, current->length, custom_data, stop); else error = user_item_handler("", 0, ELAPI_TYPE_END, NULL, 0, custom_data, stop); DEBUG_NUMBER("simple_traverse_handler. Returning:",error); return error; } /* Traverse callback for find & delete function */ static int act_traverse_handler(struct collection_item *head, struct collection_item *previous, struct collection_item *current, void *passed_traverse_data, 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; struct collection_item *other; char *property; int property_len; struct update_property *update_data; DEBUG_STRING("act_traverse_handler","Entry."); traverse_data = (struct find_name *)(passed_traverse_data); /* We can be called when current points to NULL */ if(current==(struct collection_item *)(NULL)) { DEBUG_STRING("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 = (struct path_data *)(NULL); delete_path_data(temp); traverse_data->given_name = NULL; traverse_data->given_len = 0; DEBUG_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 == ELAPI_TYPE_COLLECTION) { DEBUG_STRING("act_traverse_handler","Processing collection handle."); /* Create new path */ if(traverse_data->current_path != (struct path_data *)(NULL)) { DEBUG_STRING("Already have part of the path",""); name = (traverse_data->current_path)->name; length = (traverse_data->current_path)->length; DEBUG_STRING("Path:",name); DEBUG_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; } DEBUG_STRING("act_traverse_handler","About to create path data."); error = create_path_data(&(traverse_data->current_path), name, length, property,property_len); DEBUG_NUMBER("create_path_data returned:", error); return error; } /* Handle the collection pointers */ if(current->type == ELAPI_TYPE_COLLECTIONREF) { traverse_data->given_name = current->property; traverse_data->given_len = current->property_len; DEBUG_STRING("Saved given name:",traverse_data->given_name); } DEBUG_STRING("Processing item with property:",current->property); /* Do here what we do with items */ if(match_item(current,traverse_data)) { DEBUG_STRING("Matched item:",current->property); switch(traverse_data->action) { case ELAPI_ACTION_FIND: DEBUG_STRING("It is a find action - calling handler.",""); if(user_item_handler != (item_fn)(NULL)) { /* Call user handler */ error = user_item_handler(current->property, current->property_len, current->type, current->data, current->length, custom_data, stop); DEBUG_NUMBER("Handler returned:",error); DEBUG_NUMBER("Handler set STOP to:",*stop); } break; case ELAPI_ACTION_GET: DEBUG_STRING("It is a get action.",""); if(custom_data != NULL) *((struct collection_item **)(custom_data)) = current; break; case ELAPI_ACTION_DEL: DEBUG_STRING("It is a delete action.",""); /* Make sure we tell the caller we found a match */ if(custom_data != NULL) *(int *)(custom_data) = ELAPI_MATCH; /* Dereference external collections */ if(current->type == ELAPI_TYPE_COLLECTIONREF) { DEBUG_STRING("Dereferencing a referenced collection.",""); other = *((struct collection_item **)(current->data)); header = (struct collection_header *)(other->data); destroy_collection(other); } /* Adjust header of the collection */ header = (struct collection_header *)(head->data); (header->count)--; if(current->next == (struct collection_item *)(NULL)) header->last = previous; /* Unlink and delete iteam */ /* Previous can't be NULL here becuase we never delete header elements */ previous->next = current->next; delete_item(current); DEBUG_STRING("Did the delete of the item.",""); break; case ELAPI_ACTION_UPDATE: DEBUG_STRING("It is an update action.",""); if((current->type == ELAPI_TYPE_COLLECTION) || (current->type == ELAPI_TYPE_COLLECTIONREF)) { DEBUG_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 = ELAPI_MATCH; error = update_item(current, update_data); } else { DEBUG_STRING("Error - update data is required",""); return EINVAL; } DEBUG_STRING("Did the delete of the item.",""); break; default: break; } /* Force interrupt if we found */ *stop = 1; } DEBUG_NUMBER("act_traverse_handler returning",error); return error; } /* Traverse handler for copy function */ static int copy_traverse_handler(struct collection_item *head, struct collection_item *previous, struct collection_item *current, void *passed_traverse_data, 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 = (struct collection_item *)(NULL); DEBUG_STRING("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 == (struct collection_item *)(NULL)) return error; /* Check if this is a special case of sub collection */ if(item->type == ELAPI_TYPE_COLLECTIONREF) { DEBUG_STRING("Found a subcollection we need to copy. Name:",item->property); error = copy_collection(&new_collection, *((struct collection_item **)(item->data)), item->property); if(error) { DEBUG_NUMBER("Copy subcollection returned error:",error); return error; } /* Add new item to a collection - all references are now sub collections */ (void)add_property(parent,NULL,item->property,(void *)(&new_collection), sizeof(struct collection_item **), ELAPI_TYPE_COLLECTIONREF, &error); if(error) { DEBUG_NUMBER("Add property returned error:",error); return error; } } else { (void)add_property(parent,NULL,item->property,item->data, item->length,item->type,&error); if(error) { DEBUG_NUMBER("Add property returned error:",error); return error; } } DEBUG_NUMBER("copy_traverse_handler returning",error); return error; } /********************* MAIN INTERFACE FUNCTIONS ***********************************/ /* CREATE */ /* Function that creates a named collection */ int create_collection(struct collection_item **ci,char *name) { struct collection_item *handle = (struct collection_item *)(NULL); struct collection_header header; int error=EOK; DEBUG_STRING("create_collection","Entry."); /* Prepare header */ header.last = (struct collection_item *)(NULL); header.reference_count = 1; header.count = 0; /* Create a collection type property */ handle = add_property((struct collection_item *)(NULL),NULL,name,&header,sizeof(header), ELAPI_TYPE_COLLECTION, &error); if(error) return error; *ci = handle; DEBUG_STRING("create_collection","Success Exit."); return 0; } /* DESTROY */ /* Function that destroys a collection */ void destroy_collection(struct collection_item *ci) { struct collection_header *header; DEBUG_STRING("destroy_collection","Entry."); /* Do not try to delete NULL */ if(ci == (struct collection_item *)(NULL)) return; /* You can delete only whole collection not a part of it */ if(ci->type != ELAPI_TYPE_COLLECTION) { DEBUG_STRING("Attempt to delete a non collection - BAD!",""); DEBUG_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) { DEBUG_STRING("Dereferencing a referenced collection.",""); (header->reference_count)--; DEBUG_NUMBER("Number after dereferencing.",header->reference_count); } else delete_collection(ci); DEBUG_STRING("destroy_collection","Exit."); } /* PROPERTIES */ /* Add a string property. If length equals 0, the length is determined based on the string. Lenght INCLUDES the terminating 0 */ int add_str_property(struct collection_item *ci,char *subcollection, char *property,char *string,int length) { int error = EOK; DEBUG_STRING("add_str_property","Entry."); if(length == 0) length = strlen(string) + 1; (void)(add_property(ci,subcollection,property,(void *)(string),length, ELAPI_TYPE_STRING, &error)); DEBUG_NUMBER("add_str_property returning",error); return error; } /* Add a binary property. */ int add_binary_property(struct collection_item *ci,char *subcollection, char *property,void *binary_data,int length) { int error = EOK; DEBUG_STRING("add_binary_property","Entry."); (void)(add_property(ci,subcollection,property,binary_data,length, ELAPI_TYPE_BINARY, &error)); DEBUG_NUMBER("add_binary_property returning",error); return error; } /* Add an int property. */ int add_int_property(struct collection_item *ci,char *subcollection, char *property,int number) { int error = EOK; DEBUG_STRING("add_int_property","Entry."); (void)(add_property(ci,subcollection,property,(void *)(&number),sizeof(int), ELAPI_TYPE_INTEGER, &error)); DEBUG_NUMBER("add_int_property returning",error); return error; } /* Add an unsigned int property. */ int add_unsigned_property(struct collection_item *ci,char *subcollection, char *property,unsigned int number) { int error = EOK; DEBUG_STRING("add_unsigned_property","Entry."); (void)(add_property(ci,subcollection,property,(void *)(&number),sizeof(int), ELAPI_TYPE_UNSIGNED, &error)); DEBUG_NUMBER("add_unsigned_property returning",error); return error; } /* Add an long property. */ int add_long_property(struct collection_item *ci,char *subcollection, char *property,long number) { int error = EOK; DEBUG_STRING("add_long_property","Entry."); (void)(add_property(ci,subcollection,property,(void *)(&number),sizeof(long), ELAPI_TYPE_LONG, &error)); DEBUG_NUMBER("add_long_property returning",error); return error; } /* Add an unsigned long property. */ int add_ulong_property(struct collection_item *ci,char *subcollection, char *property,unsigned long number) { int error = EOK; DEBUG_STRING("add_ulong_property","Entry."); (void)(add_property(ci,subcollection,property,(void *)(&number),sizeof(long), ELAPI_TYPE_ULONG, &error)); DEBUG_NUMBER("add_ulong_property returning",error); return error; } /* Add a double property. */ int add_double_property(struct collection_item *ci,char *subcollection, char *property,double number) { int error = EOK; DEBUG_STRING("add_double_property","Entry."); (void)(add_property(ci,subcollection,property,(void *)(&number),sizeof(double), ELAPI_TYPE_DOUBLE, &error)); DEBUG_NUMBER("add_double_property returning",error); return error; } /* A function to add a property */ int add_any_property(struct collection_item *ci, char *subcollection, char *property, int type, void *data, int length) { int error = EOK; DEBUG_STRING("add_any_property","Entry."); (void)(add_property(ci,subcollection,property,data,length, type, &error)); DEBUG_NUMBER("add_any_property returning",error); return error; } /* Add a string property. If length equals 0, the length is determined based on the string. Lenght INCLUDES the terminating 0 */ int add_str_property_with_ref(struct collection_item *ci,char *subcollection, char *property,char *string,int length, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_str_property_with_ref","Entry."); if(length == 0) length = strlen(string) + 1; item = add_property(ci,subcollection,property,(void *)(string),length, ELAPI_TYPE_STRING, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_str_property_with_ref returning",error); return error; } /* Add a binary property. */ int add_binary_property_with_ref(struct collection_item *ci,char *subcollection, char *property,void *binary_data,int length, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_binary_property_with_ref","Entry."); item = add_property(ci,subcollection,property,binary_data,length, ELAPI_TYPE_BINARY, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_binary_property_with_ref returning",error); return error; } /* Add an int property. */ int add_int_property_with_ref(struct collection_item *ci,char *subcollection, char *property,int number, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_int_property_with_ref","Entry."); item = add_property(ci,subcollection,property,(void *)(&number),sizeof(int), ELAPI_TYPE_INTEGER, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_int_property_with_ref returning",error); return error; } /* Add an unsigned int property. */ int add_unsigned_property_with_ref(struct collection_item *ci,char *subcollection, char *property,unsigned int number, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_unsigned_property_with_ref","Entry."); item = add_property(ci,subcollection,property,(void *)(&number),sizeof(int), ELAPI_TYPE_UNSIGNED, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_unsigned_property_with_ref returning",error); return error; } /* Add an long property. */ int add_long_property_with_ref(struct collection_item *ci,char *subcollection, char *property,long number, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_long_property_with_ref","Entry."); item = add_property(ci,subcollection,property,(void *)(&number),sizeof(long), ELAPI_TYPE_LONG, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_long_property_with_ref returning",error); return error; } /* Add an unsigned long property. */ int add_ulong_property_with_ref(struct collection_item *ci,char *subcollection, char *property,unsigned long number, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_ulong_property_with_ref","Entry."); item = add_property(ci,subcollection,property,(void *)(&number),sizeof(long), ELAPI_TYPE_ULONG, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_ulong_property_with_ref returning",error); return error; } /* Add a double property. */ int add_double_property_with_ref(struct collection_item *ci,char *subcollection, char *property,double number, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_double_property_with_ref","Entry."); item = add_property(ci,subcollection,property,(void *)(&number),sizeof(double), ELAPI_TYPE_DOUBLE, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_double_property_with_ref returning",error); return error; } /* A function to add a property */ int add_any_property_with_ref(struct collection_item *ci, char *subcollection, char *property, int type, void *data, int length, struct collection_item **ref_ret) { int error = EOK; struct collection_item *item; DEBUG_STRING("add_any_property_with_ref","Entry."); item = add_property(ci,subcollection,property,data,length, type, &error); if(ref_ret != (struct collection_item **)(NULL)) *ref_ret = item; DEBUG_NUMBER("add_any_property_with_ref returning",error); return error; } /* Set time stamp in the collection */ int 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 = (struct collection_item *)(NULL); struct collection_item *timeint = (struct collection_item *)(NULL); int error = EOK; DEBUG_STRING("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) { DEBUG_STRING("add_time","CODING ERROR - INCREASE THE BUFFER"); return EMSGSIZE; } DEBUG_STRING("Timestamp:",time_array); /* Check if we have the timestamp item already */ error = get_item(ci, TS_NAME, ELAPI_TYPE_STRING,ELAPI_TRAVERSE_IGNORE,×tr); if(error) { DEBUG_NUMBER("search failed with error:",error); return error; } if(timestr != (struct collection_item *)(NULL)) { /* There is a timestamp */ free(timestr->data); timestr->data = strdup(time_array); if(timestr->data == NULL) { DEBUG_NUMBER("failed to add timestamp property:",error); return ENOMEM; } timestr->length = len+1; *timestr_ref = timestr; } else { /* Add timestamp to the collection */ error = add_str_property_with_ref(ci,NULL, TS_NAME,time_array,len+1,timestr_ref); if(error) { DEBUG_NUMBER("failed to add timestamp property:",error); return error; } } /* Check if we have the time item already */ error = get_item(ci, T_NAME, ELAPI_TYPE_INTEGER,ELAPI_TRAVERSE_IGNORE,&timeint); if(error) { DEBUG_NUMBER("search failed with error:",error); return error; } if(timeint != (struct collection_item *)(NULL)) { /* There is a time property */ *((int *)(timeint->data)) = utctime; *timeint_ref = timeint; } else { /* Add time to the collection */ error = add_int_property_with_ref(ci,NULL, T_NAME,utctime,timeint_ref); if(error) { DEBUG_NUMBER("failed to add time property:",error); return error; } } DEBUG_STRING("set_timestamp","Exit point"); return EOK; } /* COPY */ /* Create a deep copy of the current collection. */ /* Referenced collections of the donor are copied as sub collections. */ int copy_collection(struct collection_item **collection_copy, struct collection_item *collection_to_copy, char *name_to_use) { int error = EOK; struct collection_item *new_collection = (struct collection_item *)(NULL); char *name; DEBUG_STRING("copy_collection","Entry."); /* Determine what name to use */ if(name_to_use != NULL) name = name_to_use; else name = collection_to_copy->property; /* Create a new collection */ error = create_collection(&new_collection,name); if(error) { DEBUG_NUMBER("Create_cllection failed returning",error); return error; } error = walk_items(collection_to_copy, ELAPI_TRAVERSE_ONELEVEL, copy_traverse_handler, new_collection, NULL, NULL); if(!error) *collection_copy = new_collection; else destroy_collection(new_collection); DEBUG_NUMBER("copy_collection returning",error); return error; } /* EXTRACTION */ /* Extract collection */ int get_collection_reference(struct collection_item *ci, /* High level collection */ struct collection_item **acceptor, /* The pointer that will accept extracted handle */ char *collection_to_find) /* Name to of the collection */ { struct collection_header *header; struct collection_item *subcollection = (struct collection_item *)(NULL); int error = EOK; DEBUG_STRING("get_collection_reference","Entry."); if((ci == (struct collection_item *)(NULL)) || (ci->type != ELAPI_TYPE_COLLECTION) || (acceptor == (struct collection_item **)(NULL)) || (collection_to_find == NULL)) { DEBUG_NUMBER("Invalid parameter - returning error",EINVAL); return EINVAL; } /* Find a sub collection */ DEBUG_STRING("We are given subcollection name - search it:",collection_to_find); error = find_item_and_do(ci,collection_to_find,ELAPI_TYPE_COLLECTIONREF, ELAPI_TRAVERSE_DEFAULT, get_subcollection,(void *)(&subcollection),ELAPI_ACTION_FIND); if(error) { DEBUG_NUMBER("Search failed returning error",error); return error; } if(subcollection == (struct collection_item *)(NULL)) { DEBUG_STRING("Search for subcollection returned NULL pointer",""); return ENOENT; } header = (struct collection_header *)(subcollection->data); DEBUG_NUMBER("Count:",header->count); DEBUG_NUMBER("Ref count:",header->reference_count); (header->reference_count)++; DEBUG_NUMBER("Ref count after increment:",header->reference_count); *acceptor = subcollection; DEBUG_STRING("get_collection_reference","Success Exit."); return EOK; } /* ADDITION */ /* Add collection to collection */ int add_collection_to_collection( struct collection_item *ci, /* Collection handle to with we add another collection */ char *sub_collection_name, /* Name of the sub collection to which collection needs to be added as a property. If NULL high level collection is assumed. */ char *as_property, /* Name of the collection property. If NULL, same property as the name of the collection being added will be used. */ struct collection_item *collection_to_add, /* Collection to add */ int mode) /* How this collection needs to be added */ { struct collection_item *acceptor = (struct collection_item *)(NULL); char *name_to_use; struct collection_header *header; struct collection_item *collection_copy; int error = EOK; DEBUG_STRING("add_collection_to_collection","Entry."); if((ci == (struct collection_item *)(NULL)) || (ci->type != ELAPI_TYPE_COLLECTION) || (collection_to_add == (struct collection_item *)(NULL)) || (collection_to_add->type != ELAPI_TYPE_COLLECTION)) { /* Need to debug here */ DEBUG_NUMBER("Missing parameter - returning error",EINVAL); return EINVAL; } if(sub_collection_name != NULL) { /* Find a sub collection */ DEBUG_STRING("We are given subcollection name - search it:",sub_collection_name); error = find_item_and_do(ci,sub_collection_name,ELAPI_TYPE_COLLECTIONREF, ELAPI_TRAVERSE_DEFAULT, get_subcollection,(void *)(&acceptor),ELAPI_ACTION_FIND); if(error) { DEBUG_NUMBER("Search failed returning error",error); return error; } if(acceptor == (struct collection_item *)(NULL)) { DEBUG_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; DEBUG_STRING("Going to use name:",name_to_use); switch(mode) { case ELAPI_ADD_MODE_REFERENCE: DEBUG_STRING("We are adding a reference.",""); DEBUG_NUMBER("Type of the header element:",collection_to_add->type); DEBUG_STRING("Header name we are adding.",collection_to_add->property); /* Create a pointer to external collection */ /* For future thread safety: Transaction start -> */ (void)(add_property(acceptor,NULL,name_to_use,(void *)(&collection_to_add), sizeof(struct collection_item **), ELAPI_TYPE_COLLECTIONREF, &error)); DEBUG_NUMBER("Type of the header element after add_property:",collection_to_add->type); DEBUG_STRING("Header name we just added.",collection_to_add->property); if(error) { DEBUG_NUMBER("Adding property failed with error:",error); return error; } header = (struct collection_header *)(collection_to_add->data); DEBUG_NUMBER("Count:",header->count); DEBUG_NUMBER("Ref count:",header->reference_count); (header->reference_count)++; DEBUG_NUMBER("Ref count after increment:",header->reference_count); /* -> Transaction end */ break; case ELAPI_ADD_MODE_EMBED: DEBUG_STRING("We are embedding the collection.",""); /* First check if the passed in collection is referenced more than once */ DEBUG_NUMBER("Type of the header element we are adding:",collection_to_add->type); DEBUG_STRING("Header name we are adding.",collection_to_add->property); DEBUG_NUMBER("Type of the header element we are adding to:",acceptor->type); DEBUG_STRING("Header name we are adding to.",acceptor->property); (void)(add_property(acceptor,NULL,name_to_use,(void *)(&collection_to_add), sizeof(struct collection_item **), ELAPI_TYPE_COLLECTIONREF, &error)); DEBUG_NUMBER("Adding property returned:",error); break; case ELAPI_ADD_MODE_CLONE: DEBUG_STRING("We are cloning the collection.",""); DEBUG_STRING("Name we will use.",name_to_use); /* For future thread safety: Transaction start -> */ error = copy_collection(&collection_copy, collection_to_add, name_to_use); if(error) return error; DEBUG_STRING("We have a collection copy.", collection_copy->property); DEBUG_NUMBER("Collection type.", collection_copy->type); DEBUG_STRING("Acceptor collection.", acceptor->property); DEBUG_NUMBER("Acceptor collection type.", acceptor->type); (void)(add_property(acceptor,NULL,name_to_use,(void *)(&collection_copy), sizeof(struct collection_item **), ELAPI_TYPE_COLLECTIONREF, &error)); /* -> Transaction end */ DEBUG_NUMBER("Adding property returned:",error); break; default: error = EINVAL; } DEBUG_NUMBER("add_collection_to_collection returning:",error); return error; } /* TRAVERSING */ /* Function to traverse the entire collection including optionally sub collections */ int traverse_collection(struct collection_item *ci, int mode_flags, item_fn item_handler, void *custom_data) { int error = EOK; DEBUG_STRING("traverse_collection","Entry."); error = walk_items(ci, mode_flags, simple_traverse_handler, NULL, item_handler, custom_data); if((error != 0) && (error != EINTR_INTERNAL)) { DEBUG_NUMBER("Error walking tree",error); return error; } DEBUG_STRING("traverse_collection","Success exit."); return EOK; } /* CHECK */ /* Convenience function to check if specific property is in the collection */ int is_item_in_collection(struct collection_item *ci, char *property_to_find, int type, int mode_flags, int *found) { int error; DEBUG_STRING("is_item_in_collection","Entry."); *found = ELAPI_NOMATCH; error = find_item_and_do(ci,property_to_find,type,mode_flags, is_in_item_handler,(void *)found,ELAPI_ACTION_FIND); DEBUG_NUMBER("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 get_item_and_do(struct collection_item *ci, /* Collection to find things in */ char *property_to_find, /* Name to match */ int type, /* Type filter */ int mode_flags, /* How to traverse the collection */ item_fn item_handler, /* Function to call when the item is found */ void *custom_data) /* Custom data passed around */ { int error = EOK; DEBUG_STRING("get_item_and_do","Entry."); error = find_item_and_do(ci,property_to_find,type,mode_flags,item_handler,custom_data,ELAPI_ACTION_FIND); DEBUG_NUMBER("get_item_and_do returning",error); return error; } /* Get raw item */ int get_item(struct collection_item *ci, /* Collection to find things in */ char *property_to_find, /* Name to match */ int type, /* Type filter */ int mode_flags, /* How to traverse the collection */ struct collection_item **item) /* Found item */ { int error = EOK; DEBUG_STRING("get_item","Entry."); error = find_item_and_do(ci,property_to_find,type,mode_flags,NULL,(void *)(item),ELAPI_ACTION_GET); DEBUG_NUMBER("get_item returning",error); return error; } /* DELETE */ /* Delete property from the collection */ int delete_property(struct collection_item *ci, /* Collection to find things in */ char *property_to_find, /* Name to match */ int type, /* Type filter */ int mode_flags) /* How to traverse the collection */ { int error = EOK; int found; DEBUG_STRING("delete_property","Entry."); found = ELAPI_NOMATCH; error = find_item_and_do(ci,property_to_find,type,mode_flags,NULL,(void *)(&found),ELAPI_ACTION_DEL); if((error == EOK) && (found == ELAPI_NOMATCH)) error = ENOENT; DEBUG_NUMBER("delete_property returning",error); return error; } /* UPDATE */ /* Update property in the collection */ int update_property(struct collection_item *ci, /* Collection to find things in */ char *property_to_find, /* Name to match */ int type, /* Type of the passed in data */ void *new_data, /* Pointer to the new data */ int length, /* Length of the data. For strings should include trailing 0 */ int mode_flags) /* How to traverse the collection */ { int error = EOK; struct update_property update_data; DEBUG_STRING("update_property","Entry."); update_data.type = type; update_data.data = new_data; update_data.length = length; update_data.found = ELAPI_NOMATCH; error = find_item_and_do(ci,property_to_find,type,mode_flags,NULL,(void *)(&update_data),ELAPI_ACTION_UPDATE); if((error == EOK) && (update_data.found == ELAPI_NOMATCH)) error = ENOENT; DEBUG_NUMBER("update_property returning",error); return error; } /* Update a string property in the collection. Length should include the null terminating 0 */ int update_str_property(struct collection_item *ci, char *property, int mode_flags, char *string, int length) { int error = EOK; DEBUG_STRING("update_str_property","Entry."); if(length == 0) length = strlen(string) + 1; error = update_property(ci,property, ELAPI_TYPE_STRING, (void *)(string),length,mode_flags); DEBUG_NUMBER("update_str_property Returning",error); return error; } /* Update a binary property in the collection. */ int update_binary_property(struct collection_item *ci, char *property, int mode_flags, void *binary_data, int length) { int error = EOK; DEBUG_STRING("update_binary_property","Entry."); error = update_property(ci,property, ELAPI_TYPE_BINARY, binary_data, length, mode_flags); DEBUG_NUMBER("update_binary_property Returning",error); return error; } /* Update an int property in the collection. */ int update_int_property(struct collection_item *ci, char *property, int mode_flags, int number) { int error = EOK; DEBUG_STRING("update_int_property","Entry."); error = update_property(ci,property, ELAPI_TYPE_INTEGER, (void *)(&number), sizeof(int), mode_flags); DEBUG_NUMBER("update_int_property Returning",error); return error; } /* Update an unsigned int property. */ int update_unsigned_property(struct collection_item *ci, char *property,int mode_flags, unsigned int number) { int error = EOK; DEBUG_STRING("update_unsigned_property","Entry."); error = update_property(ci,property, ELAPI_TYPE_UNSIGNED, (void *)(&number), sizeof(unsigned int), mode_flags); DEBUG_NUMBER("update_unsigned_property Returning",error); return error; } /* Update a long property. */ int update_long_property(struct collection_item *ci, char *property, int mode_flags, long number) { int error = EOK; DEBUG_STRING("update_long_property","Entry."); error = update_property(ci,property, ELAPI_TYPE_LONG, (void *)(&number), sizeof(long), mode_flags); DEBUG_NUMBER("update_long_property Returning",error); return error; } /* Update an unsigned long property. */ int update_ulong_property(struct collection_item *ci, char *property, int mode_flags, unsigned long number) { int error = EOK; DEBUG_STRING("update_ulong_property","Entry."); error = update_property(ci,property, ELAPI_TYPE_ULONG, (void *)(&number), sizeof(unsigned long), mode_flags); DEBUG_NUMBER("update_ulong_property Returning",error); return error; } /* Update a double property. */ int update_double_property(struct collection_item *ci, char *property, int mode_flags, double number) { int error = EOK; DEBUG_STRING("update_double_property","Entry."); error = update_property(ci,property, ELAPI_TYPE_DOUBLE, (void *)(&number), sizeof(double), mode_flags); DEBUG_NUMBER("update_double_property Returning",error); return error; }