/* * Copyright (c) Likewise Software. All rights Reserved. * * This 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 2.1 of the license, or (at * your option) any later version. * * This 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 this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Module Name: * * data-print.c * * Abstract: * * Data graph printing * * Authors: Brian Koropoff (bkoropoff@likewisesoftware.com) * */ #include "data-private.h" #include "util-private.h" #include "convert.h" #include typedef struct { const LWMsgContext* context; unsigned int depth; LWMsgBool newline; LWMsgDataPrintFunction print; void* print_data; } PrintInfo; static LWMsgStatus lwmsg_data_print_graph_visit( LWMsgTypeIter* iter, unsigned char* object, void* data ); static LWMsgStatus print_wrap( const char* text, size_t length, void* data ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; PrintInfo* info = data; int i; if (info->newline) { for (i = 0; i < info->depth; i++) { BAIL_ON_ERROR(status = info->print(" ", 2, info->print_data)); } info->newline = LWMSG_FALSE; } BAIL_ON_ERROR(status = info->print(text, length, info->print_data)); error: return status; } static LWMsgStatus print( PrintInfo* info, const char* fmt, ... ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; va_list ap; char* text = NULL; va_start(ap, fmt); text = lwmsg_formatv(fmt, ap); va_end(ap); if (!text) { BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY); } print_wrap(text, strlen(text), info); error: if (text) { free(text); } return status; } static LWMsgStatus newline( PrintInfo* info ) { info->print("\n", 1, info->print_data); info->newline = LWMSG_TRUE; return LWMSG_STATUS_SUCCESS; } static LWMsgStatus lwmsg_data_print_integer( LWMsgTypeIter* iter, unsigned char* object, PrintInfo* info ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; uintmax_t value; BAIL_ON_ERROR(status = lwmsg_convert_integer( object, iter->size, LWMSG_NATIVE_ENDIAN, &value, sizeof(value), LWMSG_NATIVE_ENDIAN, iter->info.kind_integer.sign)); if (iter->info.kind_integer.sign == LWMSG_SIGNED) { BAIL_ON_ERROR(status = print(info, "%lld", (long long int) value)); } else { BAIL_ON_ERROR(status = print(info, "%llu", (unsigned long long int) value)); } error: return status; } static LWMsgStatus lwmsg_data_print_graph_visit_member( LWMsgTypeIter* iter, unsigned char* object, void* data ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; PrintInfo* info = (PrintInfo*) data; if (iter->meta.member_name) { BAIL_ON_ERROR(status = print(info, ".%s = ", iter->meta.member_name)); } BAIL_ON_ERROR(status = lwmsg_data_print_graph_visit( iter, object, data)); BAIL_ON_ERROR(status = newline(info)); error: return status; } static LWMsgStatus lwmsg_data_print_string( LWMsgTypeIter* iter, unsigned char* object, PrintInfo* info ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; void* input_string = NULL; void* output_string = NULL; size_t input_length = 0; ssize_t output_length = 0; size_t element_size = 0; size_t element_count = 0; if (iter->kind == LWMSG_KIND_POINTER) { input_string = *(void**) object; } else { input_string = object; } BAIL_ON_ERROR(status = lwmsg_data_calculate_indirect_metrics( iter, input_string, &element_count, &element_size)); input_length = element_count * element_size; if (!strcmp(iter->info.kind_indirect.encoding, "")) { BAIL_ON_ERROR(status = info->print(input_string, input_length, info->print_data)); } else { output_length = lwmsg_convert_string_alloc( input_string, input_length, (void**) (void*) &output_string, iter->info.kind_indirect.encoding, ""); if (output_length == -1) { BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY); } BAIL_ON_ERROR(status = info->print(output_string, (size_t) output_length, info->print_data)); } error: if (output_string) { free(output_string); } return status; } static LWMsgStatus lwmsg_data_print_graph_visit( LWMsgTypeIter* iter, unsigned char* object, void* data ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; PrintInfo* info = (PrintInfo*) data; const char* prefix = NULL; if (iter->attrs.sensitive) { BAIL_ON_ERROR(status = print(info, "")); } else { switch (iter->kind) { case LWMSG_KIND_UNION: case LWMSG_KIND_STRUCT: if (iter->meta.type_name) { BAIL_ON_ERROR(status = print(info, "<%s>", iter->meta.type_name)); } else { BAIL_ON_ERROR(status = print(info, iter->kind == LWMSG_KIND_STRUCT ? "" : "")); } BAIL_ON_ERROR(status = newline(info)); BAIL_ON_ERROR(status = print(info, "{")); BAIL_ON_ERROR(status = newline(info)); info->depth++; BAIL_ON_ERROR(status = lwmsg_data_visit_graph_children( iter, object, lwmsg_data_print_graph_visit_member, data)); info->depth--; BAIL_ON_ERROR(status = print(info, "}")); break; case LWMSG_KIND_ARRAY: case LWMSG_KIND_POINTER: if (iter->attrs.promoted) { prefix = ""; } else { prefix = iter->kind == LWMSG_KIND_ARRAY ? " " : " -> "; } /* NULL case */ if (iter->kind == LWMSG_KIND_POINTER && *(void**) object == NULL) { BAIL_ON_ERROR(status = print(info, "")); } /* String case */ else if (iter->info.kind_indirect.encoding != NULL) { BAIL_ON_ERROR(status = print(info, "%s\"", prefix)); BAIL_ON_ERROR(status = lwmsg_data_print_string(iter, object, info)); BAIL_ON_ERROR(status = print(info, "\"", prefix)); } /* Singleton case */ else if (iter->info.kind_indirect.term == LWMSG_TERM_STATIC && iter->info.kind_indirect.term_info.static_length == 1) { BAIL_ON_ERROR(status = print(info, "%s", prefix)); BAIL_ON_ERROR(status = lwmsg_data_visit_graph_children( iter, object, lwmsg_data_print_graph_visit, data)); } /* General case */ else { BAIL_ON_ERROR(status = print(info, prefix)); BAIL_ON_ERROR(status = newline(info)); BAIL_ON_ERROR(status = print(info, "{")); BAIL_ON_ERROR(status = newline(info)); info->depth++; BAIL_ON_ERROR(status = lwmsg_data_visit_graph_children( iter, object, lwmsg_data_print_graph_visit_member, data)); info->depth--; BAIL_ON_ERROR(status = print(info, "}")); } break; case LWMSG_KIND_INTEGER: BAIL_ON_ERROR(status = lwmsg_data_print_integer( iter, object, info)); break; case LWMSG_KIND_CUSTOM: if (iter->info.kind_custom.typeclass->print) { BAIL_ON_ERROR(status = iter->info.kind_custom.typeclass->print( info->context, iter->size, object, &iter->attrs, iter->info.kind_custom.typedata, print_wrap, info)); } else { BAIL_ON_ERROR(status = print(info, "")); } case LWMSG_KIND_NONE: case LWMSG_KIND_VOID: break; } } error: return status; } LWMsgStatus lwmsg_data_print_graph( LWMsgDataContext* context, LWMsgTypeSpec* type, void* object, LWMsgDataPrintFunction print, void* print_data ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; LWMsgTypeIter iter; PrintInfo info; info.newline = LWMSG_TRUE; info.depth = 0; info.context = context->context; info.print = print; info.print_data = print_data; lwmsg_type_iterate_promoted(type, &iter); BAIL_ON_ERROR(status = lwmsg_data_visit_graph( &iter, (unsigned char*) &object, lwmsg_data_print_graph_visit, &info)); BAIL_ON_ERROR(status = newline(&info)); error: return status; } typedef struct print_alloc_info { LWMsgDataContext* context; char* buffer; size_t buffer_size; size_t buffer_capacity; } print_alloc_info; static LWMsgStatus lwmsg_data_print_graph_alloc_print( const char* text, size_t length, void* data ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; print_alloc_info* info = (print_alloc_info*) data; size_t new_capacity = 0; char* new_buffer = NULL; while (info->buffer_size + (length + 1) > info->buffer_capacity) { if (info->buffer_capacity == 0) { new_capacity = 512; } else { new_capacity = info->buffer_capacity * 2; } BAIL_ON_ERROR(status = lwmsg_context_realloc( info->context->context, info->buffer, info->buffer_capacity, new_capacity, (void**) (void*) &new_buffer)); info->buffer = new_buffer; info->buffer_capacity = new_capacity; } memcpy(info->buffer + info->buffer_size, text, length); info->buffer[info->buffer_size + length] = '\0'; info->buffer_size += length; error: return status; } LWMsgStatus lwmsg_data_print_graph_alloc( LWMsgDataContext* context, LWMsgTypeSpec* type, void* object, char** result ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; print_alloc_info info = {0}; info.context = context; BAIL_ON_ERROR(status = lwmsg_data_print_graph( context, type, object, lwmsg_data_print_graph_alloc_print, &info)); *result = info.buffer; cleanup: return status; error: *result = NULL; if (info.buffer) { lwmsg_context_free(context->context, info.buffer); } goto cleanup; }