diff options
Diffstat (limited to 'common/collection/collection_tools.c')
-rw-r--r-- | common/collection/collection_tools.c | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/common/collection/collection_tools.c b/common/collection/collection_tools.c new file mode 100644 index 00000000..1c96f6f8 --- /dev/null +++ b/common/collection/collection_tools.c @@ -0,0 +1,587 @@ +/* + COLLECTION LIBRARY + + Additional functions for printing and debugging collections. + + 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/>. +*/ + + +#include <stdio.h> +#include <malloc.h> +#include <errno.h> +#include "trace.h" +#include "collection_priv.h" +#include "collection.h" +#include "collection_tools.h" + +/* Debug handle */ +int debug_handle(char *property, + int property_len, + int type, + void *data, + int length, + void *custom_data, + int *dummy) +{ + int i; + int nest_level; + + TRACE_FLOW_STRING("debug_handle","Entry."); + + + nest_level = *(int *)(custom_data); + TRACE_INFO_NUMBER("We are getting this pointer:",custom_data); + TRACE_INFO_NUMBER("Nest level:",nest_level); + + switch(type) { + case COL_TYPE_STRING: + printf("%*s %s[%d] str: %s (%d)\n", + (nest_level-1)*4,"", + property, + length, + (char *)(data), + (int)(nest_level)); + break; + case COL_TYPE_BINARY: + printf("%*s %s[%d] bin: ", + (nest_level-1)*4,"", + property, + length); + for(i=0;i<length;i++) printf("%02X",*((unsigned char *)(data+i))); + printf(" (%d)\n",(int)(nest_level)); + break; + case COL_TYPE_INTEGER: + printf("%*s %s[%d] int: %d (%d)\n", + (nest_level-1)*4,"", + property, + length, + *((int *)(data)), + nest_level); + break; + case COL_TYPE_UNSIGNED: + printf("%*s %s[%d] uint: %u (%d)\n", + (nest_level-1)*4,"", + property, + length, + *((unsigned int *)(data)), + nest_level); + break; + case COL_TYPE_LONG: + printf("%*s %s[%d] long: %ld (%d)\n", + (nest_level-1)*4,"", + property, + length, + *((long *)(data)), + nest_level); + break; + case COL_TYPE_ULONG: + printf("%*s %s[%d] ulong: %lu (%d)\n", + (nest_level-1)*4,"", + property, + length, + *((unsigned long *)(data)), + nest_level); + break; + case COL_TYPE_DOUBLE: + printf("%*s %s[%d] double: %.4f (%d)\n", + (nest_level-1)*4,"", + property, + length, + *((double *)(data)), + nest_level); + break; + case COL_TYPE_BOOL: + printf("%*s %s[%d] bool: %s (%d)\n", + (nest_level-1)*4,"", + property, + length, + (*((unsigned char *)(data)) == '\0') ? "flase" : "true", + (int)(nest_level)); + break; + case COL_TYPE_COLLECTION: + nest_level++; + printf("%*s %s[%d] header: count %d, ref_count %d class %d data: ", + (nest_level-1)*4,"", + property, + length, + ((struct collection_header *)(data))->count, + ((struct collection_header *)(data))->reference_count, + ((struct collection_header *)(data))->class); + for(i=0;i<length;i++) printf("%02X",*((unsigned char *)(data+i))); + printf(" (%d)\n",(int)(nest_level)); + break; + case COL_TYPE_COLLECTIONREF: + printf("%*s %s[%d] external link: ", + (nest_level-1)*4,"", + property, + length); + for(i=0;i<length;i++) printf("%02X",*((unsigned char *)(data+i))); + printf(" (%d)\n",(int)(nest_level)); + break; + case COL_TYPE_END: + nest_level--; + /* printf("Reduced nest level\n");*/ + break; + default: + printf("Not implemented yet.\n"); + break; + } + *(int *)(custom_data) = nest_level; + TRACE_INFO_NUMBER("Nest level at the end:",nest_level); + TRACE_FLOW_STRING("debug_handle","Success exit."); + return EOK; +} + +/* Convenience function to debug an item */ +inline int debug_item(struct collection_item *item) +{ + int dummy = 0; + int nest_level = 0; + return debug_handle(item->property, + item->property_len, + item->type, + item->data, + item->length, + (void *)(&nest_level), + &dummy); +} + + +/* Print collection for debugging purposes */ +int debug_collection(struct collection_item *handle,int flag) +{ + int error = EOK; + int nest_level=0; + + TRACE_FLOW_STRING("debug_collection","Entry."); + + printf("DEBUG COLLECTION %s\n",handle->property); + + flag |= COL_TRAVERSE_END; + + printf("Traverse flags %d\n",flag); + + /* Traverse collection */ + error = traverse_collection(handle,flag,debug_handle,(void *)(&nest_level)); + if(error) printf("Error debuging collection %d\n",error); + + TRACE_FLOW_STRING("debug_collection","Exit."); + return error; +} + + +/* Return a static string based on type of the element */ +static inline const char *get_type(int type) +{ + switch(type) { + case COL_TYPE_STRING: return COL_TYPE_NAME_STRING; + case COL_TYPE_INTEGER: return COL_TYPE_NAME_INTEGER; + case COL_TYPE_UNSIGNED: return COL_TYPE_NAME_UNSIGNED; + case COL_TYPE_LONG: return COL_TYPE_NAME_LONG; + case COL_TYPE_ULONG: return COL_TYPE_NAME_ULONG; + case COL_TYPE_BINARY: return COL_TYPE_NAME_BINARY; + case COL_TYPE_DOUBLE: return COL_TYPE_NAME_DOUBLE; + case COL_TYPE_BOOL: return COL_TYPE_NAME_BOOL; + default: return COL_TYPE_NAME_UNKNOWN; + } + +} + +/* Calculate the potential size of the item */ +int get_data_len(int type, int length) +{ + int len = 0; + + TRACE_FLOW_STRING("util_get_item_len","Entry point"); + + switch(type) { + case COL_TYPE_INTEGER: + case COL_TYPE_UNSIGNED: + case COL_TYPE_LONG: + case COL_TYPE_ULONG: + len = 15; + break; + case COL_TYPE_STRING: + case COL_TYPE_BINARY: + len = length * 2 + 2; + break; + case COL_TYPE_DOUBLE: + len = 64; + break; + case COL_TYPE_BOOL: + len = 6; + break; + default: + len = 0; + break; + } + + TRACE_FLOW_STRING("util_get_item_len","Exit point"); + + return len; +} + +/* Copy data escaping characters */ +static int copy_esc(char *dest,char *source,char esc) +{ + int i=0; + int j=0; + + *(dest +j) = esc; + j++; + + while(*(source+i) != '\0') { + if((*(source+i) == '\\') || + (*(source+i) == esc)) { + + *(dest +j) = '\\'; + j++; + + } + *(dest +j) = *(source +i); + i++; + j++; + } + *(dest +j) = esc; + j++; + + return j; +} + +/* Grow buffer to accomodate more space */ +int grow_buffer(struct serial_data *buf_data, int len) +{ + void *tmp; + + TRACE_FLOW_STRING("grow_buffer","Entry point"); + TRACE_INFO_NUMBER("Current length: ",buf_data->length); + TRACE_INFO_NUMBER("Increment length: ",len); + TRACE_INFO_NUMBER("Expected length: ",buf_data->length+len); + TRACE_INFO_NUMBER("Current size: ",buf_data->size); + + /* Grow buffer if needed */ + while(buf_data->length+len >= buf_data->size) { + errno = 0; + tmp = realloc(buf_data->buffer,buf_data->size+BLOCK_SIZE); + if(tmp == NULL) { + TRACE_ERROR_NUMBER("Error. Failed to allocate memory. Errno: ",errno); + return errno; + } + buf_data->buffer = (char *)(tmp); + buf_data->size += BLOCK_SIZE; + TRACE_INFO_NUMBER("New size: ",buf_data->size); + + } + + TRACE_INFO_NUMBER("Final size: ",buf_data->size); + TRACE_FLOW_STRING("grow_buffer","Success Exit."); + return EOK; +} + +/* Specail function to add different formatting symbols to the output */ +int put_marker(struct serial_data *buf_data, void *data, int len) +{ + int error = EOK; + + TRACE_FLOW_STRING("put_marker","Entry point"); + TRACE_INFO_NUMBER("Marker length: ",len); + + error = grow_buffer(buf_data, len); + if(error) { + TRACE_ERROR_NUMBER("grow_buffer failed with: ",error); + return error; + } + memcpy(buf_data->buffer+buf_data->length,data,len); + buf_data->length+=len; + *(buf_data->buffer+buf_data->length) = '\0'; + + TRACE_FLOW_STRING("put_marker","Success exit"); + return error; +} + +/* Add item's data */ +int serialize(char *property_in, + int property_len_in, + int type, + void *data_in, + int length_in, + void *custom_data, + int *dummy) +{ + int len; + struct serial_data *buf_data; + char *property; + void *data; + int property_len; + int length; + int error = EOK; + int i; + + TRACE_FLOW_STRING("serialize","Entry point"); + + *dummy = 0; + + /* Check is there is buffer. If not allocate */ + buf_data = (struct serial_data *)(custom_data); + if(buf_data == (struct serial_data *)(NULL)) { + TRACE_ERROR_STRING("Error.","Storage data is not passed in!"); + return EINVAL; + } + if(buf_data->buffer == NULL) { + TRACE_INFO_STRING("First time use.","Allocating buffer."); + errno = 0; + buf_data->buffer = malloc(BLOCK_SIZE); + if(buf_data->buffer == NULL) { + TRACE_ERROR_NUMBER("Error. Failed to allocate memory. Errno: ",errno); + return errno; + } + *(buf_data->buffer)='\0'; + buf_data->length=0; + buf_data->size = BLOCK_SIZE; + } + + TRACE_INFO_NUMBER("Buffer len: ", buf_data->length); + TRACE_INFO_NUMBER("Buffer size: ", buf_data->size); + TRACE_INFO_STRING("Buffer: ", buf_data->buffer); + + /* Check the beginning of the collection */ + if(type == COL_TYPE_COLLECTION) { + TRACE_INFO_STRING("Serializing collection: ", property_in); + TRACE_INFO_STRING("First header. ", ""); + if((error=put_marker(buf_data,"(",1))) return error; + property = TEXT_COLLECTION; + property_len = TEXT_COLLEN; + data = property_in; + length = property_len_in+1; + type=COL_TYPE_STRING; + buf_data->nest_level++; + } + /* Check for subcollections */ + else if(type==COL_TYPE_COLLECTIONREF) { + /* Skip */ + TRACE_FLOW_STRING("serialize","skip reference return"); + return EOK; + } + /* Check for the end of the collection */ + else if(type==COL_TYPE_END) { + if((buf_data->length>0) && + (*(buf_data->buffer+buf_data->length-1) == ',')) { + buf_data->length--; + *(buf_data->buffer+buf_data->length) = '\0'; + } + if(buf_data->nest_level>0) { + buf_data->nest_level--; + if((error=put_marker(buf_data,")",1))) return error; + } + TRACE_FLOW_STRING("serialize","end collection item processed returning"); + return EOK; + } + else { + property = property_in; + property_len = property_len_in; + data = data_in; + length = length_in; + } + + TRACE_INFO_STRING("Property: ", property); + TRACE_INFO_NUMBER("Property length: ", property_len); + + /* Start with property and "=" */ + if((error=put_marker(buf_data,property,property_len)) || + (error=put_marker(buf_data,"=",1))) { + TRACE_ERROR_NUMBER("put_marker returned error: ",error); + return error; + } + /* Get projected length of the item */ + len = get_data_len(type,length); + TRACE_INFO_NUMBER("Expected data length: ",len); + TRACE_INFO_STRING("Buffer so far: ", buf_data->buffer); + + /* Make sure we have enough space */ + if((error=grow_buffer(buf_data,len))) { + TRACE_ERROR_NUMBER("grow_buffer returned error: ",error); + return error; + } + + /* Add the value */ + switch(type) { + case COL_TYPE_STRING: + /* Escape double quotes */ + len = copy_esc(buf_data->buffer+buf_data->length,(char *)(data),'"'); + break; + case COL_TYPE_BINARY: + *(buf_data->buffer+buf_data->length) = '\''; + for(i=0;i<length;i++) sprintf(buf_data->buffer+buf_data->length+i*2 + 1,"%02X",*((unsigned char *)(data+i))); + len = length * 2 + 1; + *(buf_data->buffer+buf_data->length + len) = '\''; + len++; + break; + case COL_TYPE_INTEGER: + len = sprintf(buf_data->buffer+buf_data->length,"%d",*((int *)(data))); + break; + case COL_TYPE_UNSIGNED: + len = sprintf(buf_data->buffer+buf_data->length,"%u",*((unsigned int *)(data))); + break; + case COL_TYPE_LONG: + len = sprintf(buf_data->buffer+buf_data->length,"%ld",*((long *)(data))); + break; + case COL_TYPE_ULONG: + len = sprintf(buf_data->buffer+buf_data->length,"%lu",*((unsigned long *)(data))); + break; + case COL_TYPE_DOUBLE: + len = sprintf(buf_data->buffer+buf_data->length,"%.4f",*((double *)(data))); + break; + case COL_TYPE_BOOL: + len = sprintf(buf_data->buffer+buf_data->length,"%s",(*((unsigned char *)(data)) == '\0') ? "false" : "true"); + break; + default: + *(buf_data->buffer+buf_data->length) = '\0'; + len = 0; + break; + } + + /* Adjust length */ + buf_data->length+=len; + *(buf_data->buffer+buf_data->length) = '\0'; + + /* Always put a comma at the end */ + if((error=put_marker(buf_data,",",1))) { + TRACE_ERROR_NUMBER("put_marker returned error: ",error); + return error; + } + + TRACE_INFO_STRING("Data: ",buf_data->buffer); + TRACE_FLOW_STRING("serialize","Exit point"); + return EOK; + +} + +/* Print the collection using default serialization */ +int print_collection(struct collection_item *handle) +{ + struct serial_data buf_data; + int error = EOK; + + TRACE_FLOW_STRING("print_collection", "Entry"); + + printf("COLLECTION:\n"); + + buf_data.buffer=NULL; + buf_data.length=0; + buf_data.size=0; + buf_data.nest_level=0; + + /* Traverse collection */ + error = traverse_collection(handle,COL_TRAVERSE_DEFAULT | COL_TRAVERSE_END ,serialize,(void *)(&buf_data)); + if(error) printf("Error traversing collection %d\n",error); + else printf("%s\n",buf_data.buffer); + + free(buf_data.buffer); + + TRACE_FLOW_NUMBER("print_collection returning", error); + return error; +} + +/* Print the collection using iterator */ +int print_collection2(struct collection_item *handle) +{ + struct collection_iterator *iterator = (struct collection_iterator *)(NULL); + int error = EOK; + struct collection_item *item = (struct collection_item *)(NULL); + int nest_level=0; + int dummy = 0; + int line = 1; + + TRACE_FLOW_STRING("print_collection2", "Entry"); + + /* If we have something to print print it */ + if(handle == (struct collection_item *)(NULL)) { + TRACE_ERROR_STRING("No error list",""); + return EINVAL; + } + + /* Bind iterator */ + error = bind_iterator(&iterator,handle,COL_TRAVERSE_DEFAULT|COL_TRAVERSE_END|COL_TRAVERSE_SHOWSUB); + if(error) { + TRACE_ERROR_NUMBER("Error (bind):",error); + return error; + } + + do { + /* Loop through a collection */ + error = iterate_collection(iterator, &item); + if(error) { + TRACE_ERROR_NUMBER("Error (iterate):",error); + unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if(item == (struct collection_item *)(NULL)) break; + + if(item->type != COL_TYPE_END) printf("%05d",line); + + debug_handle(item->property, + item->property_len, + item->type, + item->data, + item->length, + (void *)(&nest_level), + &dummy); + line++; + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + unbind_iterator(iterator); + + TRACE_INFO_STRING("print_collection2", "Exit"); + return EOK; +} + +/* Find and print one item using default serialization */ +int print_item(struct collection_item *handle, char *name) +{ + struct serial_data buf_data; + int error = EOK; + + TRACE_FLOW_STRING("print_item", "Entry"); + + printf("FIND ITEM:\n"); + + buf_data.buffer=NULL; + buf_data.length=0; + buf_data.size=0; + buf_data.nest_level=0; + + error = get_item_and_do(handle, name, COL_TYPE_ANY,COL_TRAVERSE_DEFAULT, serialize,&buf_data); + if(error) printf("Error searching collection %d\n",error); + else { + if(buf_data.buffer != NULL) { + if(buf_data.length> 0) buf_data.length--; + *(buf_data.buffer+buf_data.length)= '\0', + printf("%s\n",buf_data.buffer); + free(buf_data.buffer); + } + else printf("Name %s is not found in the collection %s.\n",name,handle->property); + } + + TRACE_FLOW_NUMBER("print_item returning", error); + return error; +} + + |