From 612aec09e8896810e4ec7ae94b289262817447d5 Mon Sep 17 00:00:00 2001 From: "jonath.re@gmail.com" Date: Wed, 27 Jul 2022 02:39:14 +0200 Subject: [PATCH] Support long and long long types in TEST_PRINTF This change helps Unity parse and print correctly in cases where a long or long long type is passed to TEST_PRINTF. Example situations: ```C // With %u: TEST_PRINTF("%u %d\n", ((1ULL << 63) - 1), 5); // --> prints 11982546 -1 (both arguments incorrect because only 4 of the 8 bytes were read out of the va_list) // With %llu, UNITY_SUPPORT_64=0 TEST_PRINTF("%llu %d\n", ((1ULL << 63) - 1), 5); // --> prints 4294967295 5 (first argument wrapped, second argument intact) // With %llu, UNITY_SUPPORT_64=1 TEST_PRINTF("%llu %d\n", ((1ULL << 63) - 1), 5); // --> prints 9223372036854775807 5 (both arguments correct) ``` --- docs/UnityConfigurationGuide.md | 12 +++- src/unity.c | 113 ++++++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 7 deletions(-) diff --git a/docs/UnityConfigurationGuide.md b/docs/UnityConfigurationGuide.md index 9ac0cdd..7a0fa4c 100644 --- a/docs/UnityConfigurationGuide.md +++ b/docs/UnityConfigurationGuide.md @@ -240,7 +240,7 @@ _Example:_ Unity provides a simple (and very basic) printf-like string output implementation, which is able to print a string modified by the following format string modifiers: - __%d__ - signed value (decimal) -- __%i__ - same as __%i__ +- __%i__ - same as __%d__ - __%u__ - unsigned value (decimal) - __%f__ - float/Double (if float support is activated) - __%g__ - same as __%f__ @@ -252,6 +252,15 @@ Unity provides a simple (and very basic) printf-like string output implementatio - __%s__ - a string (e.g. "string") - __%%__ - The "%" symbol (escaped) +Length specifiers are also supported. If you are using long long types, make sure UNITY_SUPPORT_64 is true to ensure they are printed correctly. + +- __%ld__ - signed long value (decimal) +- __%lld__ - signed long long value (decimal) +- __%lu__ - unsigned long value (decimal) +- __%llu__ - unsigned long long value (decimal) +- __%lx__ - unsigned long value (hexadecimal) +- __%llx__ - unsigned long long value (hexadecimal) + _Example:_ ```C @@ -267,6 +276,7 @@ TEST_PRINTF("Pointer %p\n", &a); TEST_PRINTF("Character %c\n", 'F'); TEST_PRINTF("String %s\n", "My string"); TEST_PRINTF("Percent %%\n"); +TEST_PRINTF("Unsigned long long %llu\n", 922337203685477580); TEST_PRINTF("Color Red \033[41mFAIL\033[0m\n"); TEST_PRINTF("\n"); TEST_PRINTF("Multiple (%d) (%i) (%u) (%x)\n", -100, 0, 200, 0x12345); diff --git a/src/unity.c b/src/unity.c index 14d5cda..dfab4b4 100644 --- a/src/unity.c +++ b/src/unity.c @@ -1823,10 +1823,96 @@ UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) } #endif +#ifdef UNITY_INCLUDE_PRINT_FORMATTED + +/*----------------------------------------------- + * printf length modifier helpers + *-----------------------------------------------*/ + +enum UnityLengthModifier { + UNITY_LENGTH_MODIFIER_NONE, + UNITY_LENGTH_MODIFIER_LONG_LONG, + UNITY_LENGTH_MODIFIER_LONG, +}; + +#define UNITY_EXTRACT_ARG(NUMBER_T, NUMBER, LENGTH_MOD, VA, ARG_T) \ +do { \ + switch (LENGTH_MOD) \ + { \ + case UNITY_LENGTH_MODIFIER_LONG_LONG: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, long long ARG_T); \ + break; \ + } \ + case UNITY_LENGTH_MODIFIER_LONG: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, long ARG_T); \ + break; \ + } \ + case UNITY_LENGTH_MODIFIER_NONE: \ + default: \ + { \ + NUMBER = (NUMBER_T)va_arg(VA, ARG_T); \ + break; \ + } \ + } \ +} while (0) + +static enum UnityLengthModifier UnityLengthModifierGet(const char *pch, int *length) +{ + enum UnityLengthModifier length_mod; + switch (pch[0]) + { + case 'l': + { + if (pch[1] == 'l') + { + *length = 2; + length_mod = UNITY_LENGTH_MODIFIER_LONG_LONG; + } + else + { + *length = 1; + length_mod = UNITY_LENGTH_MODIFIER_LONG; + } + break; + } + case 'h': + { + // short and char are converted to int + length_mod = UNITY_LENGTH_MODIFIER_NONE; + if (pch[1] == 'h') + { + *length = 2; + } + else + { + *length = 1; + } + break; + } + case 'j': + case 'z': + case 't': + case 'L': + { + // Not supported, but should gobble up the length specifier anyway + length_mod = UNITY_LENGTH_MODIFIER_NONE; + *length = 1; + break; + } + default: + { + length_mod = UNITY_LENGTH_MODIFIER_NONE; + *length = 0; + } + } + return length_mod; +} + /*----------------------------------------------- * printf helper function *-----------------------------------------------*/ -#ifdef UNITY_INCLUDE_PRINT_FORMATTED static void UnityPrintFVA(const char* format, va_list va) { const char* pch = format; @@ -1841,12 +1927,17 @@ static void UnityPrintFVA(const char* format, va_list va) if (pch != NULL) { + int length_mod_size; + enum UnityLengthModifier length_mod = UnityLengthModifierGet(pch, &length_mod_size); + pch += length_mod_size; + switch (*pch) { case 'd': case 'i': { - const int number = va_arg(va, int); + UNITY_INT number; + UNITY_EXTRACT_ARG(UNITY_INT, number, length_mod, va, int); UnityPrintNumber((UNITY_INT)number); break; } @@ -1861,21 +1952,31 @@ static void UnityPrintFVA(const char* format, va_list va) #endif case 'u': { - const unsigned int number = va_arg(va, unsigned int); - UnityPrintNumberUnsigned((UNITY_UINT)number); + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + UnityPrintNumberUnsigned(number); break; } case 'b': { - const unsigned int number = va_arg(va, unsigned int); + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; UNITY_OUTPUT_CHAR('0'); UNITY_OUTPUT_CHAR('b'); - UnityPrintMask(mask, (UNITY_UINT)number); + UnityPrintMask(mask, number); break; } case 'x': case 'X': + { + UNITY_UINT number; + UNITY_EXTRACT_ARG(UNITY_UINT, number, length_mod, va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex(number, 8); + break; + } case 'p': { const unsigned int number = va_arg(va, unsigned int);