/*
* 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;
}