2017-04-03 13:07:53 +02:00
/** printing utilities to replace the large printf from the standard library (code)
2019-12-21 19:14:08 +01:00
* @ file
2017-04-03 13:07:53 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-06-14 18:58:36 +02:00
* @ date 2017 - 2020
2017-04-03 13:07:53 +02:00
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // standard definitions
# include <stdbool.h> // boolean types
# include <stdarg.h> // variadic utilities
2018-02-18 15:18:42 +01:00
# include <math.h> // mathematics utilities to handle floating points
2017-04-03 13:07:53 +02:00
/* own libraries */
2019-03-27 18:42:09 +01:00
# include "global.h" // some macro definitions
2017-04-03 13:07:53 +02:00
# include "print.h" // printing utilities
2018-02-18 15:18:42 +01:00
uint8_t print_error ;
2020-01-08 20:27:42 +01:00
size_t puts ( const char * str )
2019-12-21 19:24:55 +01:00
{
size_t printed = 0 ; // number of characters printed
while ( * str ) { // go until end of string (\0 string termination character)
ADDU32_SAFE ( printed , putc ( * str + + ) ) ; // print character
}
return printed ;
}
2018-02-18 15:18:42 +01:00
/** add printed length to total printed length, and sets error if maximum size is exceeded
* @ param [ in , out ] length total printed length
* @ param [ in ] printed printed length
*/
static void print_printed ( size_t * length , size_t printed )
{
2019-12-21 19:14:08 +01:00
if ( NULL = = length ) { // check if total is provided
2018-02-18 15:18:42 +01:00
return ;
}
2019-12-21 19:14:08 +01:00
if ( * length > SIZE_MAX - printed ) { // prevent integer overflow
2018-02-18 15:18:42 +01:00
* length = SIZE_MAX ; // set to maximum
print_error | = PRINT_ERROR_MAX ; // set error
} else {
* length + = printed ; // save printed length
}
}
2017-04-03 13:07:53 +02:00
/** print character
* @ param [ out ] str string to print character on ( use NULL to print on user output )
* @ param [ in , out ] size size of string
* @ param [ in ] c character to be printed
* @ return number of characters printed
* */
static size_t print_char ( char * * str , size_t * size , char c )
{
size_t length = 1 ; // remember how many characters have been printed or should have been added on string (normally just one)
2019-03-27 18:50:29 +01:00
if ( 0 = = c ) { // don't print string termination character
2017-04-03 13:07:53 +02:00
length = 0 ; // remember we didn't print anything
2019-03-27 18:50:29 +01:00
} else if ( NULL = = str | | NULL = = * str | | NULL = = size ) { // character should not be saved on string
2017-04-03 13:07:53 +02:00
length = putc ( c ) ; // print on user define output
2019-03-27 18:50:29 +01:00
} else if ( * size > 1 ) { // there is enough space in the string to store the character
2017-04-03 13:07:53 +02:00
* * str = c ; // add provided character to string
* str + = 1 ; // go to next character on string
* size - = 1 ; // remember we used one character on string
2018-02-18 15:18:42 +01:00
} else { // string is reached its end
print_error | = PRINT_ERROR_TRUNCATED ; // indicate we did not save the character
2017-04-03 13:07:53 +02:00
}
return length ;
}
/** print string
* @ param [ out ] str string to print string on ( use NULL to print on user output )
* @ param [ in , out ] size size of string
* @ param [ in ] s string to be printed
* @ return number of characters printed
* */
static size_t print_string ( char * * str , size_t * size , const char * s )
{
size_t length = 0 ; // number of characters printed
while ( * s ) { // stop at end of string
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , * ( s + + ) ) ) ; // print character
2017-04-03 13:07:53 +02:00
}
return length ;
}
2018-02-18 15:18:42 +01:00
/** print unsigned integer
* @ param [ out ] str string to print unsigned integer on ( use NULL to print on user output )
2017-04-03 13:07:53 +02:00
* @ param [ in , out ] size size of string
2018-02-18 15:18:42 +01:00
* @ param [ in ] u unsigned integer to be printed
2017-04-03 13:07:53 +02:00
* @ param [ in ] padding number of 0 ' s to pad
* @ param [ in ] sign if sign should be printed
* @ return number of characters printed
* */
2018-02-18 15:18:42 +01:00
static size_t print_unsigned ( char * * str , size_t * size , uint64_t u , uint32_t padding , bool sign ) {
2017-04-03 13:07:53 +02:00
char number [ 20 ] = { 0 } ; // construct the number in reverse order (20 chars are required to store UINT64_MAX)
uint8_t digits = 0 ; // to count the number of digits
size_t length = 0 ; // number of characters printed
do {
2020-06-14 18:58:36 +02:00
number [ digits + + ] = ' 0 ' + ( u % 10 ) ; // store digit
2017-04-03 13:07:53 +02:00
u / = 10 ; // go to next digit
2019-03-27 18:50:29 +01:00
} while ( u > 0 ) ;
2017-04-03 13:07:53 +02:00
if ( sign ) { // print sign
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' + ' ) ) ; // we only have positive numbers
2017-04-03 13:07:53 +02:00
}
2018-02-18 15:18:42 +01:00
for ( uint32_t zeros = digits ; zeros < padding ; zeros + + ) { // print padding 0's
print_printed ( & length , print_char ( str , size , ' 0 ' ) ) ; // print 0
2017-04-03 13:07:53 +02:00
}
for ( uint8_t digit = 0 ; digit < digits ; digit + + ) { // go through all digits
2019-03-27 18:50:29 +01:00
print_printed ( & length , print_char ( str , size , number [ digits - digit - 1 ] ) ) ; // print digit (in reverse order)
2017-04-03 13:07:53 +02:00
}
return length ; // return number of characters printed
}
2018-02-18 15:18:42 +01:00
/** print signed integer
* @ param [ out ] str string to print signed integer on ( use NULL to print on user output )
2017-04-03 13:07:53 +02:00
* @ param [ in , out ] size size of string
2018-02-18 15:18:42 +01:00
* @ param [ in ] d signed integer to be printed
2017-04-03 13:07:53 +02:00
* @ param [ in ] padding number of 0 ' s to pad
* @ param [ in ] sign if sign should be printed
* @ return number of characters printed
* */
2018-02-18 15:18:42 +01:00
static size_t print_signed ( char * * str , size_t * size , int64_t d , uint32_t padding , bool sign ) {
2017-04-03 13:07:53 +02:00
size_t length = 0 ; // number of characters printed
2020-06-14 18:58:36 +02:00
if ( d < 0 ) {
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' - ' ) ) ; // print sign
2020-06-14 18:58:36 +02:00
print_printed ( & length , print_unsigned ( str , size , ( uint64_t ) - d , padding , false ) ) ; // print number (casting because there is one more negative value then positive value)
2017-04-03 13:07:53 +02:00
} else {
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_unsigned ( str , size , d , padding , sign ) ) ; // print number
}
return length ; // return number of characters printed
}
/** print floating number
* @ param [ out ] str string to print floating number on ( use NULL to print on user output )
* @ param [ in , out ] size size of string
* @ param [ in ] f floating number to be printed
* @ param [ in ] padding number of 0 ' s to pad
2018-04-03 16:59:02 +02:00
* @ param [ in ] fractional numbers of digits after the decimal point
2018-02-18 15:18:42 +01:00
* @ param [ in ] sign if sign should be printed
* @ return number of characters printed
* */
static size_t print_float ( char * * str , size_t * size , double f , uint32_t padding , uint32_t fractional , bool sign ) {
size_t length = 0 ; // number of characters printed
if ( isnan ( f ) ) { // not a number
print_printed ( & length , print_string ( str , size , " NaN " ) ) ; // print NaN
} else if ( isinf ( f ) ) { // infinite
2020-06-24 11:49:52 +02:00
if ( - 1 = = isinf ( f ) ) {
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' - ' ) ) ; // print sign
} else if ( sign ) {
print_printed ( & length , print_char ( str , size , ' + ' ) ) ; // print sign
}
print_printed ( & length , print_string ( str , size , " inf " ) ) ; // print inf
} else if ( isnormal ( f ) ) { // it should be not 0
2020-06-24 11:49:52 +02:00
if ( f < 0 ) {
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' - ' ) ) ; // print sign
} else if ( sign ) {
print_printed ( & length , print_char ( str , size , ' + ' ) ) ; // print sign
}
double f_abs = fabs ( f ) ; // only work using the absolute value now that the sign is printed
// get the exponent
int8_t exponent = 0 ; // exponent min/max for double is 37
2019-03-27 18:50:29 +01:00
if ( f_abs < 1.0 ) {
while ( f_abs < pow ( 10.0 , exponent - 1 ) ) { // find negative exponent, base 10
2018-02-18 15:18:42 +01:00
exponent - = 1 ; // decrement in deci
}
if ( padding ) { // respect padding wish
exponent - = padding ;
}
} else {
2019-03-27 18:50:29 +01:00
while ( f_abs > = pow ( 10.0 , exponent ) ) { // find the positive exponent, base 10
2018-02-18 15:18:42 +01:00
exponent + = 3 ; // increment in kilo
}
if ( padding ) { // respect padding wish
exponent - = padding ;
} else {
exponent - = 3 ;
}
}
// print integer part
f_abs / = pow ( 10.0 , exponent ) ; // convert to scientific format
print_printed ( & length , print_unsigned ( str , size , f_abs , padding , false ) ) ; // print integer part as scientific number
// print fractional part
if ( fractional ) {
print_printed ( & length , print_char ( str , size , ' . ' ) ) ; // print decimal point
f_abs - = ( uint64_t ) f_abs ; // remove integer part
2019-03-27 18:50:29 +01:00
for ( uint32_t frac = 0 ; frac < fractional ; frac + + ) { // print fractional parts
2018-02-18 15:18:42 +01:00
f_abs * = 10.0 ;
print_printed ( & length , print_unsigned ( str , size , f_abs , 0 , false ) ) ;
f_abs - = ( uint64_t ) f_abs ;
}
}
// print exponent
if ( exponent ) {
print_printed ( & length , print_char ( str , size , ' E ' ) ) ; // print exponent mark
print_printed ( & length , print_signed ( str , size , exponent , 0 , false ) ) ;
}
2020-06-24 11:49:52 +02:00
} else { // f = 0
2018-02-18 15:18:42 +01:00
// print sign
2019-03-27 18:50:29 +01:00
if ( f < 0 ) {
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' - ' ) ) ; // print sign
} else if ( sign ) {
print_printed ( & length , print_char ( str , size , ' + ' ) ) ; // print sign
}
print_printed ( & length , print_unsigned ( str , size , 0 , padding , false ) ) ; // print integer part
if ( fractional ) {
print_printed ( & length , print_char ( str , size , ' . ' ) ) ; // print decimal point
print_printed ( & length , print_unsigned ( str , size , 0 , fractional , false ) ) ; // print fractional part
}
2017-04-03 13:07:53 +02:00
}
return length ; // return number of characters printed
}
/** print nibble (half-byte)
* @ param [ out ] str string to print nibble on ( use NULL to print on user output )
* @ param [ in , out ] size size of string
* @ param [ in ] nibble nibble to be printed
* @ param [ in ] upcase use upcase digits ( A - F )
* @ return number of characters printed
* */
static size_t print_nibble ( char * * str , size_t * size , uint8_t nibble , bool upcase ) {
size_t length = 0 ; // number of characters printed
nibble & = 0x0f ; // ensure we only have a nibble
2019-03-27 18:50:29 +01:00
if ( nibble < 10 ) {
print_printed ( & length , print_char ( str , size , ' 0 ' + nibble ) ) ;
2017-04-03 13:07:53 +02:00
} else if ( upcase ) {
2019-03-27 18:50:29 +01:00
print_printed ( & length , print_char ( str , size , ' A ' + nibble - 10 ) ) ;
2017-04-03 13:07:53 +02:00
} else {
2019-03-27 18:50:29 +01:00
print_printed ( & length , print_char ( str , size , ' a ' + nibble - 10 ) ) ;
2017-04-03 13:07:53 +02:00
}
return length ; // return number of characters printed
}
/** print hex value
* @ param [ out ] str string to print hex on ( use NULL to print on user output )
* @ param [ in , out ] size size of string
* @ param [ in ] hex hex value to be printed
* @ param [ in ] padding number of 0 ' s to pad
* @ param [ in ] prefix if 0 x prefix should be printed
* @ param [ in ] upcase use upcase digits ( A - F )
* @ return number of characters printed
* */
2018-02-18 15:18:42 +01:00
static size_t print_hex ( char * * str , size_t * size , uint64_t hex , uint32_t padding , bool prefix , bool upcase ) {
2017-04-03 13:07:53 +02:00
size_t length = 0 ; // number of characters printed
if ( prefix ) { // print 0x prefix
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' 0 ' ) ) ;
print_printed ( & length , print_char ( str , size , ' x ' ) ) ;
2017-04-03 13:07:53 +02:00
}
// figure out number of digits to print
2020-06-14 18:59:32 +02:00
uint8_t nibbles ; // number of nibbles to print
for ( nibbles = 0 ; nibbles < 16 ; nibbles + + ) {
if ( 0 = = ( hex > > ( nibbles * 4 ) ) ) {
break ;
}
2017-04-03 13:07:53 +02:00
}
2020-06-14 18:59:32 +02:00
2020-06-24 11:49:52 +02:00
if ( 0 = = padding ) {
padding = 1 ; // show at least one hex
}
2020-06-14 18:59:32 +02:00
for ( uint32_t zeros = nibbles ; zeros < padding ; zeros + + ) { // print padding 0's
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' 0 ' ) ) ; // print 0
2017-04-03 13:07:53 +02:00
}
2020-06-14 18:59:32 +02:00
for ( uint8_t nibble = 0 ; nibble < nibbles ; nibble + + ) { // go through all nibbles
print_printed ( & length , print_nibble ( str , size , hex > > ( ( nibbles - nibble - 1 ) * 4 ) , upcase ) ) ; // print nibble (in reverse order)
2017-04-03 13:07:53 +02:00
}
return length ; // return number of characters printed
2019-03-26 19:27:40 +01:00
}
2017-04-03 13:07:53 +02:00
/** print bits
* @ param [ out ] str string to print bits on ( use NULL to print on user output )
* @ param [ in , out ] size size of string
* @ param [ in ] u bits to be printed
* @ param [ in ] padding number of 0 ' s to pad
* @ param [ in ] prefix if 0 b prefix should be printed
* @ return number of characters printed
* */
2018-02-18 15:18:42 +01:00
static size_t print_bits ( char * * str , size_t * size , uint64_t u , uint32_t padding , bool prefix ) {
char bits [ 64 ] = { 0 } ; // construct the bit string in reverse order
2017-04-03 13:07:53 +02:00
uint8_t digits = 0 ; // to count the number of digits
size_t length = 0 ; // number of characters printed
do {
2019-03-27 18:50:29 +01:00
bits [ digits + + ] = ' 0 ' + ( u & 0x1 ) ; // store bit
2017-04-03 13:07:53 +02:00
u > > = 1 ; // go to next bit
2019-03-27 18:50:29 +01:00
} while ( u > 0 ) ;
if ( digits > sizeof ( bits ) ) { // prevent buffer underflow
2017-04-03 13:07:53 +02:00
return 0 ;
}
if ( prefix ) { // print prefix
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , ' 0 ' ) ) ;
print_printed ( & length , print_char ( str , size , ' b ' ) ) ;
2017-04-03 13:07:53 +02:00
}
2018-02-18 15:18:42 +01:00
for ( uint32_t zeros = digits ; zeros < padding ; zeros + + ) { // print padding 0's
print_printed ( & length , print_char ( str , size , ' 0 ' ) ) ; // print 0
2017-04-03 13:07:53 +02:00
}
for ( uint8_t digit = 0 ; digit < digits ; digit + + ) { // go through all bits
2019-03-27 18:50:29 +01:00
print_printed ( & length , print_char ( str , size , bits [ digits - digit - 1 ] ) ) ; // print bit (in reverse order)
2017-04-03 13:07:53 +02:00
}
return length ; // return number of characters printed
}
/** print format string on string or user output
* @ param [ out ] str string to print format string on , or user output if str is set to NULL ( str will always be terminated with a null character ' \0 ' )
* @ param [ in , out ] size size of string ( writes at most size characters on str , including the termination null character ' \0 ' )
* @ param [ in ] format format string to be printed
* @ param [ in ] va arguments referenced by format string to be printed
2019-03-26 19:27:40 +01:00
* @ return number of characters printed ( a return value of size or more means that the output was truncated )
2017-04-03 13:07:53 +02:00
* */
static size_t vsnprintf ( char * * str , size_t * size , const char * format , va_list va )
{
2018-02-18 15:18:42 +01:00
size_t length = 0 ; // total number of characters printed
uint32_t padding = 0 ; // number of padding 0's
uint32_t fractional = 0 ; // number or fractional digits for floating point numbers
2017-04-03 13:07:53 +02:00
bool sign = false ; // if sign needs to be printed
while ( * format ) { // go through format string
padding = 0 ; // reset padding
sign = false ; // reset sign
2019-03-27 18:50:29 +01:00
if ( ' % ' ! = * format ) { // check for format specifier prefix
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_char ( str , size , * format + + ) ) ; // print character (no interpretation needed)
2017-04-03 13:07:53 +02:00
} else {
format + + ; // go to format specifier
2019-03-27 18:50:29 +01:00
if ( 0 = = * format ) { // end of string detected
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_MALFORMED ; // set error
2017-04-03 13:07:53 +02:00
goto end ;
}
// check if sign need to be printed
2019-03-27 18:50:29 +01:00
if ( ' + ' = = * format ) { // sign required
2017-04-03 13:07:53 +02:00
sign = true ; // remember sign is required
format + + ; // go to padding number
2019-03-27 18:50:29 +01:00
if ( 0 = = * format ) { // end of string detected
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_MALFORMED ; // set error
2017-04-03 13:07:53 +02:00
goto end ;
}
2019-03-26 19:27:40 +01:00
}
2017-04-03 13:07:53 +02:00
// check padding
2019-03-27 18:50:29 +01:00
if ( ' 0 ' = = * format ) { // padding required
2018-04-03 16:59:02 +02:00
padding = 0 ; // reset padding
2017-04-03 13:07:53 +02:00
format + + ; // go to padding number
2019-03-27 18:50:29 +01:00
while ( * format > = ' 0 ' & & * format < = ' 9 ' ) {
if ( padding > UINT32_MAX / 10 ) { // check for overflow
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_UNSUPPORTED ; // set error
goto end ;
}
padding * = 10 ; // go to next magnitude
2019-03-27 18:50:29 +01:00
if ( padding > UINT32_MAX - ( * format - ' 0 ' ) ) { // check for overflow
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_UNSUPPORTED ; // set error
goto end ;
}
2019-03-27 18:50:29 +01:00
padding + = * format - ' 0 ' ; // save digit
2018-02-18 15:18:42 +01:00
format + + ; // go to next character
}
2019-03-27 18:50:29 +01:00
if ( 0 = = * format ) { // end of string detected
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_MALFORMED ; // set error
2017-04-03 13:07:53 +02:00
goto end ;
}
2018-02-18 15:18:42 +01:00
}
// check fractional
2019-03-27 18:50:29 +01:00
if ( ' . ' = = * format ) { // fractional required
2018-04-03 16:59:02 +02:00
fractional = 0 ; // reset fractional
2018-02-18 15:18:42 +01:00
format + + ; // go to fractional number
2019-03-27 18:50:29 +01:00
while ( * format > = ' 0 ' & & * format < = ' 9 ' ) {
if ( fractional > UINT32_MAX / 10 ) { // check for overflow
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_UNSUPPORTED ; // set error
goto end ;
}
fractional * = 10 ; // go to next magnitude
2019-03-27 18:50:29 +01:00
if ( fractional > UINT32_MAX - ( * format - ' 0 ' ) ) { // check for overflow
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_UNSUPPORTED ; // set error
2017-04-03 13:07:53 +02:00
goto end ;
}
2019-03-27 18:50:29 +01:00
fractional + = * format - ' 0 ' ; // save digit
2018-02-18 15:18:42 +01:00
format + + ; // go to next character
}
2019-03-27 18:50:29 +01:00
if ( 0 = = * format ) { // end of string detected
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_MALFORMED ; // set error
goto end ;
2017-04-03 13:07:53 +02:00
}
2018-02-18 15:18:42 +01:00
} else {
fractional = 2 ; // default fractional precision
2017-04-03 13:07:53 +02:00
}
// check format specifier
switch ( * format ) {
2018-02-18 15:18:42 +01:00
case ' c ' : // for char, unsigned char
print_printed ( & length , print_char ( str , size , ( char ) ( va_arg ( va , int ) ) ) ) ; // needs casting because the returned value is promoted
break ;
case ' s ' : // for strings
print_printed ( & length , print_string ( str , size , va_arg ( va , char * ) ) ) ;
break ;
2017-04-03 13:07:53 +02:00
case ' u ' : // for uint8_t, uint16_t, uint32_t, unsigned int, unsigned long
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_unsigned ( str , size , va_arg ( va , uint32_t ) , padding , sign ) ) ;
2017-04-03 13:07:53 +02:00
break ;
case ' U ' : // for uint64_t, unsigned long long
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_unsigned ( str , size , va_arg ( va , uint64_t ) , padding , sign ) ) ;
2017-04-03 13:07:53 +02:00
break ;
case ' d ' : // for int8_t, int16_t, int32_t, int, long
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_signed ( str , size , va_arg ( va , int32_t ) , padding , sign ) ) ;
2017-04-03 13:07:53 +02:00
break ;
case ' D ' : // for int64_t, long long
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_signed ( str , size , va_arg ( va , int64_t ) , padding , sign ) ) ;
2017-04-03 13:07:53 +02:00
break ;
2018-02-18 15:18:42 +01:00
case ' f ' :
print_printed ( & length , print_float ( str , size , va_arg ( va , double ) , padding , fractional , sign ) ) ;
2017-04-03 13:07:53 +02:00
break ;
2019-03-26 19:27:40 +01:00
case ' x ' : // for uint8_t, uint16_t, uint32_t downcase hexadecimal
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_hex ( str , size , va_arg ( va , uint32_t ) , padding , sign , false ) ) ;
2017-04-03 13:07:53 +02:00
break ;
2018-02-18 15:18:42 +01:00
case ' X ' : // for uint64_t downcase hexadecimal
print_printed ( & length , print_hex ( str , size , va_arg ( va , uint64_t ) , padding , sign , false ) ) ;
2017-04-03 13:07:53 +02:00
break ;
2019-03-26 19:27:40 +01:00
case ' h ' : // for uint8_t, uint16_t, uint32_t upcase hexadecimal
2018-02-18 15:18:42 +01:00
print_printed ( & length , print_hex ( str , size , va_arg ( va , uint32_t ) , padding , sign , true ) ) ;
2017-04-03 13:07:53 +02:00
break ;
2018-02-18 15:18:42 +01:00
case ' H ' : // for uint64_t upcase hexadecimal
print_printed ( & length , print_hex ( str , size , va_arg ( va , uint64_t ) , padding , sign , true ) ) ;
break ;
case ' b ' : // for uint8_t, uint16_t, uint32_t bits
print_printed ( & length , print_bits ( str , size , va_arg ( va , uint32_t ) , padding , sign ) ) ;
break ;
case ' B ' : // for uint64_t bits
print_printed ( & length , print_bits ( str , size , va_arg ( va , uint64_t ) , padding , sign ) ) ;
2017-04-03 13:07:53 +02:00
break ;
default :
2018-02-18 15:18:42 +01:00
print_error | = PRINT_ERROR_UNSUPPORTED ; // set error
print_printed ( & length , print_char ( str , size , * format ) ) ; // print character (unknown format specifier)
2017-04-03 13:07:53 +02:00
}
format + + ; // go to next character
}
}
end :
2019-03-27 18:50:29 +01:00
if ( NULL ! = str & & NULL ! = * str & & NULL ! = size ) { // when working on a string
* * str = ' \0 ' ; // enforce null termination
if ( * size > 0 ) {
2018-02-18 15:18:42 +01:00
* size - = 1 ; // remember we used memory
} else {
print_error | = PRINT_ERROR_TRUNCATED ; // indicate we truncated the string
}
2017-04-03 13:07:53 +02:00
}
2018-02-18 15:18:42 +01:00
return length ; // return number of characters it should have written (not including the '\0' null termination character)
2017-04-03 13:07:53 +02:00
}
size_t printf ( const char * format , . . . )
{
2018-02-18 15:18:42 +01:00
print_error = PRINT_ERROR_NONE ; // clear error
2017-04-03 13:07:53 +02:00
va_list arglist ;
va_start ( arglist , format ) ;
2018-02-18 15:18:42 +01:00
size_t length = vsnprintf ( NULL , NULL , format , arglist ) ;
2017-04-03 13:07:53 +02:00
va_end ( arglist ) ;
return length ;
}
size_t snprintf ( char * str , size_t size , const char * format , . . . )
{
2018-02-18 15:18:42 +01:00
print_error = PRINT_ERROR_NONE ; // clear error
2017-04-03 13:07:53 +02:00
va_list arglist ;
va_start ( arglist , format ) ;
2018-02-18 15:18:42 +01:00
size_t length = vsnprintf ( & str , & size , format , arglist ) ;
2017-04-03 13:07:53 +02:00
va_end ( arglist ) ;
return length ;
}
2019-03-27 18:42:09 +01:00
2019-03-27 19:06:15 +01:00
size_t print_xxd ( uint32_t offset , const uint8_t * data , size_t length )
2019-03-27 18:42:09 +01:00
{
size_t to_return = 0 ; // total number of characters printed
uint32_t address = ( offset / 16 ) * 16 ; // address of the data to print
if ( offset > SIZE_MAX - length ) { // prevent integer overflow on address
return 0 ;
}
while ( address < offset + length ) { // print data lines until the end
ADDU32_SAFE ( to_return , printf ( " %08x: " , address ) ) ; // print address
for ( uint8_t i = 0 ; i < 16 ; i + + ) {
if ( address < offset | | address > = offset + length ) {
ADDU32_SAFE ( to_return , printf ( " " ) ) ;
} else {
ADDU32_SAFE ( to_return , printf ( " %02x " , data [ address - offset ] ) ) ;
}
address + + ;
}
address - = 16 ;
ADDU32_SAFE ( to_return , putc ( ' ' ) ) ;
for ( uint8_t i = 0 ; i < 16 ; i + + ) {
if ( address < offset | | address > = offset + length ) {
ADDU32_SAFE ( to_return , putc ( ' ' ) ) ;
} else if ( data [ address - offset ] < ' ' | | data [ address - offset ] > ' ~ ' ) {
ADDU32_SAFE ( to_return , putc ( ' . ' ) ) ;
} else {
ADDU32_SAFE ( to_return , putc ( data [ address - offset ] ) ) ;
}
address + + ;
}
ADDU32_SAFE ( to_return , putc ( ' \n ' ) ) ;
}
return to_return ;
}