Add a convenience function for logging hexdumps.

The main logging function isn't really suitable for generating inline
hexdumps directly from the binary data. There is simply no format string
available for converting array data types with just a single printf
call.

A possible solution would be to require the caller to perform the string
conversion before calling the standard logging function. But that's not
acceptable, because it doesn't play well with the ability to disable the
logging at compile time, requires extra memory and clutters the calling
code unneccessary.

The new function is a compromise which sacrifices flexibility for
simplicity, by using a hardcoded output format with a custom prefix.
It's not a perfect solution, but it works well enough for the intended
purpose.
This commit is contained in:
Jef Driesen 2012-08-25 07:27:56 +02:00
parent fab606b00a
commit 645bd89d8a
2 changed files with 79 additions and 0 deletions

View File

@ -35,12 +35,14 @@ extern "C" {
#define UNUSED(x) (void)sizeof(x)
#ifdef ENABLE_LOGGING
#define HEXDUMP(context, loglevel, prefix, data, size) dc_context_hexdump (context, loglevel, __FILE__, __LINE__, __FUNCTION__, prefix, data, size)
#define SYSERROR(context, errcode) dc_context_syserror (context, DC_LOGLEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, errcode)
#define ERROR(context, ...) dc_context_log (context, DC_LOGLEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define WARNING(context, ...) dc_context_log (context, DC_LOGLEVEL_WARNING, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define INFO(context, ...) dc_context_log (context, DC_LOGLEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define DEBUG(context, ...) dc_context_log (context, DC_LOGLEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#else
#define HEXDUMP(context, loglevel, prefix, data, size) UNUSED(context)
#define SYSERROR(context, errcode) UNUSED(context)
#define ERROR(context, ...) UNUSED(context)
#define WARNING(context, ...) UNUSED(context)
@ -54,6 +56,9 @@ dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file,
dc_status_t
dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, int errcode);
dc_status_t
dc_context_hexdump (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *prefix, const unsigned char data[], unsigned int size);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -76,6 +76,51 @@ l_vsnprintf (char *str, size_t size, const char *format, va_list ap)
return n;
}
static int
l_snprintf (char *str, size_t size, const char *format, ...)
{
va_list ap;
int n;
va_start (ap, format);
n = l_vsnprintf (str, size, format, ap);
va_end (ap);
return n;
}
static int
l_hexdump (char *str, size_t size, const unsigned char data[], size_t n)
{
const unsigned char ascii[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
if (size == 0)
return -1;
/* The maximum number of bytes. */
size_t maxlength = (size - 1) / 2;
/* The actual number of bytes. */
size_t length = (n > maxlength ? maxlength : n);
for (size_t i = 0; i < length; ++i) {
/* Set the most-significant nibble. */
unsigned char msn = (data[i] >> 4) & 0x0F;
str[i * 2 + 0] = ascii[msn];
/* Set the least-significant nibble. */
unsigned char lsn = data[i] & 0x0F;
str[i * 2 + 1] = ascii[lsn];
}
/* Null terminate the hex string. */
str[length * 2] = 0;
return (n > maxlength ? -1 : length * 2);
}
static void
logfunc (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata)
{
@ -216,3 +261,32 @@ dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char *
return dc_context_log (context, loglevel, file, line, function, "%s (%d)", errmsg, errcode);
}
dc_status_t
dc_context_hexdump (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *prefix, const unsigned char data[], unsigned int size)
{
#ifdef ENABLE_LOGGING
int n;
#endif
if (context == NULL || prefix == NULL)
return DC_STATUS_INVALIDARGS;
#ifdef ENABLE_LOGGING
if (loglevel > context->loglevel)
return DC_STATUS_SUCCESS;
if (context->logfunc == NULL)
return DC_STATUS_SUCCESS;
n = l_snprintf (context->msg, sizeof (context->msg), "%s: size=%u, data=", prefix, size);
if (n >= 0) {
n = l_hexdump (context->msg + n, sizeof (context->msg) - n, data, size);
}
context->logfunc (context, loglevel, file, line, function, context->msg, context->userdata);
#endif
return DC_STATUS_SUCCESS;
}