diff --git a/platform-darwin/Source/MasterPassword-Prefix.pch b/platform-darwin/Source/MasterPassword-Prefix.pch
index 742bd70d..f20ec33e 100644
--- a/platform-darwin/Source/MasterPassword-Prefix.pch
+++ b/platform-darwin/Source/MasterPassword-Prefix.pch
@@ -16,63 +16,60 @@
// LICENSE file. Alternatively, see .
//==============================================================================
-#import
-#import "Pearl-Prefix.pch"
+#define MP_LIBS_BEGIN \
+_Pragma("clang diagnostic push") \
+_Pragma("clang diagnostic ignored \"-Weverything\"")
+#define MP_LIBS_END \
+_Pragma("clang diagnostic pop")
+
+MP_LIBS_BEGIN
+#include
+#include
+#include
+MP_LIBS_END
+
+#define mpw_log_os(level, file, line, function, format, ...) \
+ do { \
+ if (mpw_verbosity < level) { \
+ break; \
+ } \
+ \
+ switch (level) { \
+ case LogLevelTrace: \
+ os_log_debug( OS_LOG_DEFAULT, "%30s:%-3ld TRC | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
+ break; \
+ case LogLevelDebug: \
+ os_log_debug( OS_LOG_DEFAULT, "%30s:%-3ld DBG | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
+ break; \
+ case LogLevelInfo: \
+ os_log_info( OS_LOG_DEFAULT, "%30s:%-3ld INF | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
+ break; \
+ case LogLevelWarning: \
+ os_log( OS_LOG_DEFAULT, "%30s:%-3ld WRN | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
+ break; \
+ case LogLevelError: \
+ os_log_error( OS_LOG_DEFAULT, "%30s:%-3ld ERR | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
+ break; \
+ case LogLevelFatal: \
+ os_log_fault( OS_LOG_DEFAULT, "%30s:%-3ld FTL | " format, basename( (char *)file ), line, ##__VA_ARGS__ ); \
+ break; \
+ } \
+ \
+ mpw_log_sink( level, file, line, function, format, ##__VA_ARGS__ ); \
+ } while (0)
+
+#define MPW_LOG mpw_log_os
+
+#include "mpw-util.h"
#ifdef __OBJC__
+#import "Pearl-Prefix.pch"
+
#if TARGET_OS_IOS
#import
#elif TARGET_OS_OSX
#import
#endif
-#import
-#ifdef CRASHLYTICS
-#import
-#endif
-
-#if TARGET_OS_IOS
-#import "MPTypes.h"
-#import "MPiOSConfig.h"
-#elif TARGET_OS_OSX
-#import "MPTypes.h"
-#import "MPMacConfig.h"
-#endif
-
-#else
-
-#import
-#import
-#import
-#import
-#import
-#import
-
-#define mpw_log(level, format, ...) \
- do { \
- char *_msg = NULL; \
- asprintf( &_msg, format, ##__VA_ARGS__ ); \
- if (_msg) { \
- CFStringRef fileStr = CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ); \
- CFStringRef funcStr = CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ); \
- CFStringRef msgStr = CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ); \
- id (*_getLogger)(id, SEL) = (void *)objc_msgSend; \
- void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
- _sendMsg( _getLogger( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
- sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), fileStr, __LINE__, funcStr, level, msgStr ); \
- if (fileStr) { CFRelease( fileStr ); } \
- if (funcStr) { CFRelease( funcStr ); } \
- if (msgStr) { CFRelease( msgStr ); } \
- free(_msg); \
- } \
- } while (0)
-
-#define trc(format, ...) mpw_log( 0, format, ##__VA_ARGS__ );
-#define dbg(format, ...) mpw_log( 1, format, ##__VA_ARGS__ );
-#define inf(format, ...) mpw_log( 2, format, ##__VA_ARGS__ );
-#define wrn(format, ...) mpw_log( 3, format, ##__VA_ARGS__ );
-#define err(format, ...) mpw_log( 4, format, ##__VA_ARGS__ );
-#define ftl(format, ...) mpw_log( 5, format, ##__VA_ARGS__ );
-
#endif
diff --git a/platform-independent/c/core/src/mpw-util.c b/platform-independent/c/core/src/mpw-util.c
index e63558e6..5e195434 100644
--- a/platform-independent/c/core/src/mpw-util.c
+++ b/platform-independent/c/core/src/mpw-util.c
@@ -22,6 +22,7 @@ MP_LIBS_BEGIN
#include
#include
#include
+#include
#if MPW_CPERCIVA
#include
@@ -34,54 +35,113 @@ MP_LIBS_BEGIN
#include "aes.h"
MP_LIBS_END
-int mpw_verbosity = LogLevelInfo;
-FILE *mpw_log_cli_file;
+LogLevel mpw_verbosity = LogLevelInfo;
+FILE *mpw_log_sink_file_target = NULL;
-void mpw_log_cli(LogLevel level, const char *format, ...) {
- va_list args;
- va_start( args, format );
- mpw_vlog_cli( level, format, args );
- va_end( args );
-}
-void mpw_vlog_cli(LogLevel level, const char *format, va_list args) {
- if (!mpw_log_cli_file)
- mpw_log_cli_file = stderr;
+static MPLogSink **sinks;
+static size_t sinks_count;
- if (mpw_verbosity >= level) {
- if (mpw_verbosity >= LogLevelDebug) {
- switch (level) {
- case LogLevelTrace:
- fprintf( mpw_log_cli_file, "[TRC] " );
- break;
- case LogLevelDebug:
- fprintf( mpw_log_cli_file, "[DBG] " );
- break;
- case LogLevelInfo:
- fprintf( mpw_log_cli_file, "[INF] " );
- break;
- case LogLevelWarning:
- fprintf( mpw_log_cli_file, "[WRN] " );
- break;
- case LogLevelError:
- fprintf( mpw_log_cli_file, "[ERR] " );
- break;
- case LogLevelFatal:
- fprintf( mpw_log_cli_file, "[FTL] " );
- break;
- default:
- fprintf( mpw_log_cli_file, "[???] " );
- break;
- }
- }
+bool mpw_log_sink_register(MPLogSink *sink) {
- vfprintf( mpw_log_cli_file, format, args );
- fprintf( mpw_log_cli_file, "\n" );
+ if (!mpw_realloc( &sinks, NULL, sizeof( MPLogSink * ) * ++sinks_count )) {
+ --sinks_count;
+ return false;
}
- if (level <= LogLevelFatal)
+ sinks[sinks_count - 1] = sink;
+ return true;
+}
+
+bool mpw_log_sink_unregister(MPLogSink *sink) {
+
+ for (unsigned int r = 0; r < sinks_count; ++r) {
+ if (sinks[r] == sink) {
+ sinks[r] = NULL;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void mpw_log_sink(LogLevel level, const char *file, int line, const char *function, const char *format, ...) {
+
+ if (mpw_verbosity < level)
+ return;
+
+ va_list args;
+ va_start( args, format );
+ mpw_log_vsink( level, file, line, function, format, args );
+ va_end( args );
+}
+
+void mpw_log_vsink(LogLevel level, const char *file, int line, const char *function, const char *format, va_list args) {
+
+ if (mpw_verbosity < level)
+ return;
+
+ return mpw_log_ssink( level, file, line, function, mpw_vstr( format, args ) );
+}
+
+void mpw_log_ssink(LogLevel level, const char *file, int line, const char *function, const char *message) {
+
+ if (mpw_verbosity < level)
+ return;
+
+ MPLogEvent record = (MPLogEvent){
+ .occurrence = time( NULL ),
+ .level = level,
+ .file = file,
+ .line = line,
+ .function = function,
+ .message = message,
+ };
+
+ for (unsigned int s = 0; s < sinks_count; ++s) {
+ MPLogSink *sink = sinks[s];
+
+ if (sink)
+ sink( &record );
+ }
+
+ if (record.level <= LogLevelFatal)
abort();
}
+void mpw_log_sink_file(const MPLogEvent *record) {
+
+ if (!mpw_log_sink_file_target)
+ mpw_log_sink_file_target = stderr;
+
+ if (mpw_verbosity >= LogLevelDebug) {
+ switch (record->level) {
+ case LogLevelTrace:
+ fprintf( mpw_log_sink_file_target, "[TRC] " );
+ break;
+ case LogLevelDebug:
+ fprintf( mpw_log_sink_file_target, "[DBG] " );
+ break;
+ case LogLevelInfo:
+ fprintf( mpw_log_sink_file_target, "[INF] " );
+ break;
+ case LogLevelWarning:
+ fprintf( mpw_log_sink_file_target, "[WRN] " );
+ break;
+ case LogLevelError:
+ fprintf( mpw_log_sink_file_target, "[ERR] " );
+ break;
+ case LogLevelFatal:
+ fprintf( mpw_log_sink_file_target, "[FTL] " );
+ break;
+ default:
+ fprintf( mpw_log_sink_file_target, "[???] " );
+ break;
+ }
+ }
+
+ fprintf( mpw_log_sink_file_target, "%s\n", record->message );
+}
+
void mpw_uint16(const uint16_t number, uint8_t buf[2]) {
buf[0] = (uint8_t)((number >> 8L) & UINT8_MAX);
@@ -581,7 +641,7 @@ int mpw_strncasecmp(const char *s1, const char *s2, size_t max) {
int cmp = 0;
for (; !cmp && max-- > 0 && s1 && s2; ++s1, ++s2)
- cmp = tolower( *(unsigned char *)s1 ) - tolower( *(unsigned char *)s2 );
+ cmp = tolower( (unsigned char)*s1 ) - tolower( (unsigned char)*s2 );
return cmp;
}
diff --git a/platform-independent/c/core/src/mpw-util.h b/platform-independent/c/core/src/mpw-util.h
index 3735c4c6..978d2a51 100644
--- a/platform-independent/c/core/src/mpw-util.h
+++ b/platform-independent/c/core/src/mpw-util.h
@@ -27,42 +27,57 @@ MP_LIBS_BEGIN
MP_LIBS_END
//// Logging.
-typedef mpw_enum(int, LogLevel) {
+typedef mpw_enum( int, LogLevel ) {
/** Logging internal state. */
- LogLevelTrace = 3,
+ LogLevelTrace = 3,
/** Logging state and events interesting when investigating issues. */
- LogLevelDebug = 2,
+ LogLevelDebug = 2,
/** User messages. */
- LogLevelInfo = 1,
+ LogLevelInfo = 1,
/** Recoverable issues and user suggestions. */
- LogLevelWarning = 0,
+ LogLevelWarning = 0,
/** Unrecoverable issues. */
- LogLevelError = -1,
+ LogLevelError = -1,
/** Issues that lead to abortion. */
- LogLevelFatal = -2,
+ LogLevelFatal = -2,
};
extern LogLevel mpw_verbosity;
-/** mpw_log_cli is a sink that writes log messages to the mpw_log_cli_file, which defaults to stderr. */
-extern FILE *mpw_log_cli_file;
-void mpw_log_cli(LogLevel level, const char *format, ...);
-void mpw_vlog_cli(LogLevel level, const char *format, va_list args);
+typedef struct {
+ time_t occurrence;
+ LogLevel level;
+ const char *file;
+ int line;
+ const char *function;
+ const char *message;
+} MPLogEvent;
-/** mpw_log_app is a sink placeholder that an application can implement to consume log messages. */
-void mpw_log_app(LogLevel level, const char *format, ...);
+/** A log sink describes a function that can receive log events when registered. */
+typedef void (MPLogSink)(const MPLogEvent *event);
+bool mpw_log_sink_register(MPLogSink *sink);
+bool mpw_log_sink_unregister(MPLogSink *sink);
-/** The sink you want to channel the log messages into, defaults to mpw_log_cli. */
+/** mpw_log_sink_file is a sink that writes log messages to the mpw_log_cli_file, which defaults to stderr. */
+extern MPLogSink mpw_log_sink_file;
+extern FILE *mpw_log_sink_file_target;
+
+/** These functions dispatch log events to the registered sinks. */
+void mpw_log_sink(LogLevel level, const char *file, int line, const char *function, const char *format, ...);
+void mpw_log_vsink(LogLevel level, const char *file, int line, const char *function, const char *format, va_list args);
+void mpw_log_ssink(LogLevel level, const char *file, int line, const char *function, const char *message);
+
+/** The log dispatcher you want to channel log messages into; defaults to mpw_log_sink, enabling the log sink mechanism. */
#ifndef MPW_LOG
-#define MPW_LOG mpw_log_cli
+#define MPW_LOG mpw_log_sink
#endif
#ifndef trc
-#define trc(format, ...) MPW_LOG( LogLevelTrace, format, ##__VA_ARGS__ )
-#define dbg(format, ...) MPW_LOG( LogLevelDebug, format, ##__VA_ARGS__ )
-#define inf(format, ...) MPW_LOG( LogLevelInfo, format, ##__VA_ARGS__ )
-#define wrn(format, ...) MPW_LOG( LogLevelWarning, format, ##__VA_ARGS__ )
-#define err(format, ...) MPW_LOG( LogLevelError, format, ##__VA_ARGS__ )
-#define ftl(format, ...) MPW_LOG( LogLevelFatal, format, ##__VA_ARGS__ )
+#define trc(format, ...) MPW_LOG( LogLevelTrace, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
+#define dbg(format, ...) MPW_LOG( LogLevelDebug, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
+#define inf(format, ...) MPW_LOG( LogLevelInfo, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
+#define wrn(format, ...) MPW_LOG( LogLevelWarning, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
+#define err(format, ...) MPW_LOG( LogLevelError, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
+#define ftl(format, ...) MPW_LOG( LogLevelFatal, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#endif
#ifndef min