/*************************************************************** MPU-ERROR.C This file contains error-message conversion and diagnostic output functions. PART OF : MPUIO - library . USAGE : Internal and external . NOTE : libc error messages are converted from the current LC_CTYPE multibyte encoding to strict UCS-2 before LIBMPUIO output. Copyright (C) 2000 - 2026 by Andrey V.Kosteltsev. All Rights Reserved. ***************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include static pthread_mutex_t __mpu_IO_strerror_lock = PTHREAD_MUTEX_INITIALIZER; /*************************************************************** Copy libc's error message for ERRNUM while serializing access to implementations whose strerror() storage is not reentrant. ***************************************************************/ static char *__mpu_IO_error_message_copy( int errnum ) { const char *message; char *copy; char fallback[64]; size_t length; pthread_mutex_lock( &__mpu_IO_strerror_lock ); message = strerror( errnum ); if( message == (const char *)0 ) { (void)snprintf( fallback, sizeof(fallback), "Unknown error %d", errnum ); message = fallback; } length = strlen( message ); copy = (char *)malloc( length + 1 ); if( copy != (char *)0 ) memcpy( copy, message, length + 1 ); pthread_mutex_unlock( &__mpu_IO_strerror_lock ); return( copy ); } /* End of __mpu_IO_error_message_copy() */ /*************************************************************** Count strict UCS-2 code units required for the current-locale multibyte string SOURCE, excluding the terminating NUL. ***************************************************************/ static int __mpu_IO_mbs_ucs2_length( const char *source, size_t *length ) { mbstate_t state; const char *p; size_t count = 0; if( source == (const char *)0 || length == (size_t *)0 ) { errno = EINVAL; return( -1 ); } memset( &state, 0, sizeof(state) ); p = source; while( *p != '\0' ) { char16_t wc; size_t n; n = mbrtoc16( &wc, p, MB_CUR_MAX, &state ); if( n == (size_t)-1 || n == (size_t)-2 || n == (size_t)-3 ) { errno = EILSEQ; return( -1 ); } if( n == 0 ) break; if( ((unsigned int)wc >= 0xd800U && (unsigned int)wc <= 0xdfffU) || count == SIZE_MAX ) { errno = ((unsigned int)wc >= 0xd800U && (unsigned int)wc <= 0xdfffU) ? EILSEQ : EOVERFLOW; return( -1 ); } ++count; p += n; } *length = count; return( 0 ); } /* End of __mpu_IO_mbs_ucs2_length() */ /*************************************************************** Convert SOURCE from the current locale's multibyte encoding to strict UCS-2 and terminate DESTINATION with a UCS-2 NUL. ***************************************************************/ static int __mpu_IO_mbs_to_ucs2( __mpu_char16_t *destination, size_t size, const char *source ) { mbstate_t state; const char *p; size_t used = 0; if( destination == (__mpu_char16_t *)0 || size == 0 || source == (const char *)0 ) { errno = EINVAL; return( -1 ); } memset( &state, 0, sizeof(state) ); p = source; while( *p != '\0' ) { char16_t wc; size_t n; n = mbrtoc16( &wc, p, MB_CUR_MAX, &state ); if( n == (size_t)-1 || n == (size_t)-2 || n == (size_t)-3 ) { errno = EILSEQ; destination[0] = 0; return( -1 ); } if( n == 0 ) break; if( (unsigned int)wc >= 0xd800U && (unsigned int)wc <= 0xdfffU ) { errno = EILSEQ; destination[0] = 0; return( -1 ); } if( used + 1 >= size ) { errno = ERANGE; destination[0] = 0; return( -1 ); } destination[used++] = (__mpu_char16_t)wc; p += n; } destination[used] = 0; return( 0 ); } /* End of __mpu_IO_mbs_to_ucs2() */ /*************************************************************** Convert the libc diagnostic for ERRNUM into caller-owned UCS-2 storage. SIZE is measured in __mpu_char16_t code units. ***************************************************************/ int mpu_strerror_r( int errnum, __mpu_char16_t *buffer, size_t size ) { char *message; size_t length; int saved_errno = errno; if( buffer == (__mpu_char16_t *)0 || size == 0 ) { errno = EINVAL; return( -1 ); } buffer[0] = 0; message = __mpu_IO_error_message_copy( errnum ); if( message == (char *)0 ) { errno = ENOMEM; return( -1 ); } if( __mpu_IO_mbs_ucs2_length( message, &length ) < 0 ) { int error = errno; free( message ); errno = error; return( -1 ); } if( length == SIZE_MAX || length + 1 > size ) { free( message ); errno = ERANGE; return( -1 ); } if( __mpu_IO_mbs_to_ucs2( buffer, size, message ) < 0 ) { int error = errno; free( message ); errno = error; return( -1 ); } free( message ); errno = saved_errno; return( 0 ); } /* End of mpu_strerror_r() */ /*************************************************************** Print the current errno diagnostic to mpu_stderr. PREFIX is a UCS-2 string; a nonempty prefix is followed by colon and space. ***************************************************************/ void mpu_perror( const __mpu_char16_t *prefix ) { __mpu_char16_t stack_buffer[256]; __mpu_char16_t *message = stack_buffer; size_t size = sizeof(stack_buffer) / sizeof(stack_buffer[0]); int errnum = errno; int error; if( mpu_strerror_r( errnum, message, size ) < 0 ) { size_t capacity = size * 2; error = errno; while( error == ERANGE && capacity <= 65536U ) { __mpu_char16_t *tmp = (__mpu_char16_t *)malloc( capacity * sizeof(*tmp) ); if( tmp == (__mpu_char16_t *)0 ) break; if( mpu_strerror_r( errnum, tmp, capacity ) == 0 ) { message = tmp; error = 0; break; } error = errno; free( tmp ); if( capacity > 32768U ) break; capacity *= 2; } if( error != 0 ) { char fallback[64]; (void)snprintf( fallback, sizeof(fallback), "Error %d", errnum ); for( size = 0; fallback[size] != '\0' && size + 1 < 256; ++size ) stack_buffer[size] = (__mpu_char16_t)(unsigned char)fallback[size]; stack_buffer[size] = 0; message = stack_buffer; } } mpu_flockfile( mpu_stderr ); if( prefix != (const __mpu_char16_t *)0 && *prefix != 0 ) { (void)mpu_fputs_unlocked( prefix, mpu_stderr ); (void)mpu_fputc_unlocked( (__mpu_char16_t)':', mpu_stderr ); (void)mpu_fputc_unlocked( (__mpu_char16_t)' ', mpu_stderr ); } (void)mpu_fputs_unlocked( message, mpu_stderr ); (void)mpu_fputc_unlocked( (__mpu_char16_t)'\n', mpu_stderr ); mpu_funlockfile( mpu_stderr ); if( message != stack_buffer ) free( message ); errno = errnum; return; } /* End of mpu_perror() */ /* End of mpu-error.c */