2026-01-10 10:20:35 -05:00
# ifndef _AKERR_H_
# define _AKERR_H_
2025-07-20 21:40:14 -04:00
2026-01-10 22:03:14 -05:00
# if (defined(AKERR_USE_STDLIB) && AKERR_USE_STDLIB == 1) || (!defined(AKERR_USE_STDLIB))
2025-07-20 21:40:14 -04:00
# include <stdlib.h>
# include <stdbool.h>
# include <string.h>
# include <stdio.h>
2026-01-04 22:56:31 -05:00
# endif
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
# define AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH 1024
# define AKERR_MAX_ERROR_NAME_LENGTH 64
# define AKERR_MAX_ERROR_FNAME_LENGTH 256
# define AKERR_MAX_ERROR_FUNCTION_LENGTH 128
# define AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH 2048
2026-01-12 08:33:31 -05:00
# define AKERR_LAST_ERRNO_VALUE AKERR_LAST_ERRNO_VALUE_SED
# define AKERR_NULLPOINTER (AKERR_LAST_ERRNO_VALUE + 1)
# define AKERR_OUTOFBOUNDS (AKERR_LAST_ERRNO_VALUE + 2)
# define AKERR_API (AKERR_LAST_ERRNO_VALUE + 3)
# define AKERR_ATTRIBUTE (AKERR_LAST_ERRNO_VALUE + 4)
# define AKERR_TYPE (AKERR_LAST_ERRNO_VALUE + 5)
# define AKERR_KEY (AKERR_LAST_ERRNO_VALUE + 6)
# define AKERR_HEAP (AKERR_LAST_ERRNO_VALUE + 7)
# define AKERR_INDEX (AKERR_LAST_ERRNO_VALUE + 8)
# define AKERR_FORMAT (AKERR_LAST_ERRNO_VALUE + 9)
# define AKERR_IO (AKERR_LAST_ERRNO_VALUE + 10)
# define AKERR_REGISTRY (AKERR_LAST_ERRNO_VALUE + 11)
# define AKERR_VALUE (AKERR_LAST_ERRNO_VALUE + 12)
# define AKERR_BEHAVIOR (AKERR_LAST_ERRNO_VALUE + 13)
# define AKERR_RELATIONSHIP (AKERR_LAST_ERRNO_VALUE + 14)
2026-05-12 16:42:33 -04:00
# define AKERR_EOF (AKERR_LAST_ERRNO_VALUE + 15)
2026-01-10 10:20:35 -05:00
# ifndef AKERR_MAX_ERR_VALUE
2026-05-12 16:42:33 -04:00
# define AKERR_MAX_ERR_VALUE (AKERR_LAST_ERRNO_VALUE + 15)
2026-01-12 08:33:31 -05:00
# elif AKERR_MAX_ERR_VALUE < 256
# error user-defined AKERR_MAX_ERR_VALUE must be >= 256
2025-08-02 15:07:08 -04:00
# endif
2026-01-10 10:20:35 -05:00
extern char __AKERR_ERROR_NAMES [ AKERR_MAX_ERR_VALUE + 1 ] [ AKERR_MAX_ERROR_NAME_LENGTH ] ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
# define AKERR_MAX_ARRAY_ERROR 128
2025-07-20 21:40:14 -04:00
typedef struct
{
2026-01-10 10:20:35 -05:00
char message [ AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH ] ;
2026-01-10 09:50:17 -05:00
int arrayid ;
2025-07-20 21:40:14 -04:00
int status ;
bool handled ;
int refcount ;
2026-01-10 10:20:35 -05:00
char fname [ AKERR_MAX_ERROR_FNAME_LENGTH ] ;
char function [ AKERR_MAX_ERROR_FNAME_LENGTH ] ;
2025-07-20 21:40:14 -04:00
int lineno ;
bool reported ;
2026-01-10 10:20:35 -05:00
char stacktracebuf [ AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH ] ;
2025-07-20 21:40:14 -04:00
char * stacktracebufptr ;
2026-01-10 10:20:35 -05:00
} akerr_ErrorContext ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
# define AKERR_NOIGNORE __attribute__((warn_unused_result))
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
typedef void ( * akerr_ErrorUnhandledErrorHandler ) ( akerr_ErrorContext * errctx ) ;
typedef void ( * akerr_ErrorLogFunction ) ( const char * f , . . . ) ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
extern akerr_ErrorContext AKERR_ARRAY_ERROR [ AKERR_MAX_ARRAY_ERROR ] ;
extern akerr_ErrorUnhandledErrorHandler akerr_handler_unhandled_error ;
extern akerr_ErrorLogFunction akerr_log_method ;
extern akerr_ErrorContext * __akerr_last_ignored ;
2025-07-20 21:40:14 -04:00
2026-01-10 10:20:35 -05:00
akerr_ErrorContext AKERR_NOIGNORE * akerr_release_error ( akerr_ErrorContext * ptr ) ;
akerr_ErrorContext AKERR_NOIGNORE * akerr_next_error ( ) ;
char * akerr_name_for_status ( int status , char * name ) ;
void akerr_init ( ) ;
void akerr_default_handler_unhandled_error ( akerr_ErrorContext * ptr ) ;
void akerr_default_logger ( const char * f , . . . ) ;
2026-01-12 08:33:31 -05:00
/* defined in src/errno.c which is built dynamically at build time from system errno definitions */
void akerr_init_errno ( void ) ;
2025-07-20 21:40:14 -04:00
# define LOG_ERROR_WITH_MESSAGE(__err_context, __err_message) \
2026-01-10 10:20:35 -05:00
akerr_log_method ( " %s%s:%s:%d: %s %d (%s): %s " , ( char * ) & __err_context - > stacktracebuf , ( char * ) __FILE__ , ( char * ) __func__ , __LINE__ , __err_message , __err_context - > status , akerr_name_for_status ( __err_context - > status , NULL ) , __err_context - > message ) ; \
2025-07-20 21:40:14 -04:00
# define LOG_ERROR(__err_context) \
LOG_ERROR_WITH_MESSAGE ( __err_context , " " ) ;
# define RELEASE_ERROR(__err_context) \
if ( __err_context ! = NULL ) { \
2026-01-10 10:20:35 -05:00
__err_context = akerr_release_error ( __err_context ) ; \
2025-07-20 21:40:14 -04:00
}
# define PREPARE_ERROR(__err_context) \
2026-01-10 10:20:35 -05:00
akerr_init ( ) ; \
akerr_ErrorContext __attribute__ ( ( unused ) ) * __err_context = NULL ;
2025-07-20 21:40:14 -04:00
# define ENSURE_ERROR_READY(__err_context) \
if ( __err_context = = NULL ) { \
2026-01-10 10:20:35 -05:00
__err_context = akerr_next_error ( ) ; \
2025-07-20 21:40:14 -04:00
if ( __err_context = = NULL ) { \
2026-01-10 10:20:35 -05:00
akerr_log_method ( " %s:%s:%d: Unable to pull an error context from the array! " , __FILE__ , ( char * ) __func__ , __LINE__ ) ; \
2025-07-20 21:40:14 -04:00
exit ( 1 ) ; \
} \
} \
__err_context - > refcount + = 1 ;
/*
2026-01-10 10:20:35 -05:00
* Failure and success methods for functions that return akerr_ErrorContext *
2025-07-20 21:40:14 -04:00
*/
# define FAIL_ZERO_RETURN(__err_context, __x, __err, __message, ...) \
if ( __x = = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
return __err_context ; \
}
# define FAIL_NONZERO_RETURN(__err_context, __x, __err, __message, ...) \
if ( __x ! = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
return __err_context ; \
}
# define FAIL_RETURN(__err_context, __err, __message, ...) \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
return __err_context ;
# define SUCCEED_RETURN(__err_context) \
RELEASE_ERROR ( __err_context ) ; \
return NULL ;
/*
* Failure and success methods for use inside of ATTEMPT ( ) blocks
*/
# define FAIL_ZERO_BREAK(__err_context, __x, __err, __message, ...) \
if ( __x = = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
break ; \
}
# define FAIL_NONZERO_BREAK(__err_context, __x, __err, __message, ...) \
if ( __x ! = 0 ) { \
FAIL ( __err_context , __err , __message , # # __VA_ARGS__ ) ; \
break ; \
}
# define FAIL_BREAK(__err_context, __err_, __message, ...) \
FAIL ( __err_context , __err_ , __message , # # __VA_ARGS__ ) ; \
break ;
# define SUCCEED_BREAK(__err_context) \
SUCCEED ( __err_context ) ; \
break ;
/*
* General failure and success methods
*/
# define FAIL(__err_context, __err, __message, ...) \
ENSURE_ERROR_READY ( __err_context ) ; \
__err_context - > status = __err ; \
2026-01-10 10:20:35 -05:00
snprintf ( ( char * ) __err_context - > fname , AKERR_MAX_ERROR_FNAME_LENGTH , __FILE__ ) ; \
snprintf ( ( char * ) __err_context - > function , AKERR_MAX_ERROR_FUNCTION_LENGTH , __func__ ) ; \
2025-07-20 21:40:14 -04:00
__err_context - > lineno = __LINE__ ; \
2026-01-10 10:20:35 -05:00
snprintf ( ( char * ) __err_context - > message , AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH , __message , # # __VA_ARGS__ ) ; \
2026-01-12 08:33:31 -05:00
__err_context - > stacktracebufptr + = snprintf ( __err_context - > stacktracebufptr , AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH , " %s:%s:%d: %d (%s) : %s \n " , ( char * ) __err_context - > fname , ( char * ) __err_context - > function , __err_context - > lineno , __err_context - > status , akerr_name_for_status ( __err_context - > status , NULL ) , ( __err_context - > message = = NULL ? " " : __err_context - > message ) ) ;
2025-07-20 21:40:14 -04:00
# define SUCCEED(__err_context) \
ENSURE_ERROR_READY ( __err_context ) ; \
__err_context - > status = 0 ;
/*
* Defines for the ATTEMPT / CATCH / CLEANUP / PROCESS / HANDLE / FINISH process
*/
# define ATTEMPT \
switch ( 0 ) { \
case 0 : \
2026-05-15 19:41:22 -04:00
# define VALID(__err_context, __stmt) \
__stmt ; \
if ( akerr_valid_error_address ( __err_context ) = = 0 ) { \
FAIL ( __err_context , AKERR_BEHAVIOR , " Received (akerr_Error *) from an invalid memory region. (Did the method finish without calling SUCCEED_RETURN?) " ) ; \
}
2025-07-20 21:40:14 -04:00
# define DETECT(__err_context, __stmt) \
2026-05-15 19:41:22 -04:00
VALID ( __err_context , __stmt ) ; \
2025-07-20 21:40:14 -04:00
if ( __err_context ! = NULL ) { \
2026-01-10 10:20:35 -05:00
__err_context - > stacktracebufptr + = snprintf ( __err_context - > stacktracebufptr , AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH , " %s:%s:%d: Detected error %d from array (refcount %d) \n " , ( char * ) __FILE__ , ( char * ) __func__ , __LINE__ , __err_context - > arrayid , __err_context - > refcount ) ; \
2025-07-20 21:40:14 -04:00
if ( __err_context - > status ! = 0 ) { \
2026-01-10 10:20:35 -05:00
__err_context - > stacktracebufptr + = snprintf ( __err_context - > stacktracebufptr , AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH , " %s:%s:%d \n " , ( char * ) __FILE__ , ( char * ) __func__ , __LINE__ ) ; \
2025-07-20 21:40:14 -04:00
break ; \
} \
}
# define CATCH(__err_context, __stmt) \
DETECT ( __err_context , __err_context = __stmt ) ;
2026-05-15 19:41:22 -04:00
# define PASS(__err_context, __stmt) \
switch ( 0 ) { \
case 0 : \
DETECT ( __err_context , __err_context = __stmt ) ; \
} \
FINISH_LOGIC ( __err_context , true ) ;
2025-07-20 21:40:14 -04:00
# define IGNORE(__stmt) \
2026-01-10 10:20:35 -05:00
__akerr_last_ignored = __stmt ; \
if ( __akerr_last_ignored ! = NULL ) { \
LOG_ERROR_WITH_MESSAGE ( __akerr_last_ignored , " ** IGNORED ERROR ** " ) ; \
2025-07-20 21:40:14 -04:00
}
# define CLEANUP \
} ;
# define PROCESS(__err_context) \
if ( __err_context ! = NULL ) { \
switch ( __err_context - > status ) { \
case 0 : \
__err_context - > handled = true ;
# define HANDLE(__err_context, __err_status) \
break ; \
case __err_status : \
__err_context - > stacktracebufptr = ( char * ) & __err_context - > stacktracebuf ; \
__err_context - > handled = true ;
# define HANDLE_GROUP(__err_context, __err_status) \
case __err_status : \
__err_context - > stacktracebufptr = ( char * ) & __err_context - > stacktracebuf ; \
__err_context - > handled = true ;
# define HANDLE_DEFAULT(__err_context) \
break ; \
default : \
__err_context - > stacktracebufptr = ( char * ) & __err_context - > stacktracebuf ; \
__err_context - > handled = true ;
2026-05-15 19:41:22 -04:00
# define FINISH_LOGIC(__err_context, __pass_up) \
2025-07-20 21:40:14 -04:00
if ( __err_context ! = NULL ) { \
if ( __err_context - > handled = = false & & __pass_up = = true ) { \
2026-01-10 10:20:35 -05:00
__err_context - > stacktracebufptr + = snprintf ( __err_context - > stacktracebufptr , AKERR_MAX_ERROR_STACKTRACE_BUF_LENGTH , " %s:%s:%d \n " , ( char * ) __FILE__ , ( char * ) __func__ , __LINE__ ) ; \
2025-07-20 21:40:14 -04:00
return __err_context ; \
} \
} \
2026-05-15 19:41:22 -04:00
# define FINISH(__err_context, __pass_up) \
} ; \
} ; \
FINISH_LOGIC ( __err_context , __pass_up ) \
2025-07-20 21:40:14 -04:00
RELEASE_ERROR ( __err_context ) ;
# define FINISH_NORETURN(__err_context) \
} ; \
} ; \
if ( __err_context ! = NULL ) { \
if ( __err_context - > handled = = false ) { \
LOG_ERROR_WITH_MESSAGE ( __err_context , " Unhandled Error " ) ; \
2026-01-10 10:20:35 -05:00
akerr_handler_unhandled_error ( __err_context ) ; \
2025-07-20 21:40:14 -04:00
} \
} \
RELEASE_ERROR ( __err_context ) ;
2026-01-10 10:20:35 -05:00
# endif // _AKERR_H_