From 645bd89d8ab7dbfed605f2d9a24ed94d86740277 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 25 Aug 2012 07:27:56 +0200 Subject: [PATCH] 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. --- src/context-private.h | 5 +++ src/context.c | 74 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/context-private.h b/src/context-private.h index 6eccd97..39323ed 100644 --- a/src/context-private.h +++ b/src/context-private.h @@ -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 */ diff --git a/src/context.c b/src/context.c index 89b06e4..26f9737 100644 --- a/src/context.c +++ b/src/context.c @@ -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; +}