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; +}