diff --git a/inc/private/dmod_prf.h b/inc/private/dmod_prf.h index d7283a7..aecd470 100644 --- a/inc/private/dmod_prf.h +++ b/inc/private/dmod_prf.h @@ -64,6 +64,10 @@ extern "C" { * - %X: hexadecimal (uppercase) * - %p: pointer * - %%: literal % + * + * Supported format modifiers: + * - Width: Minimum field width (e.g., %30s for 30 characters) + * - Left-align: '-' flag for left-justification (e.g., %-30s) */ extern int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list Args ); diff --git a/src/system/if/dmod_prf.c b/src/system/if/dmod_prf.c index c9fd4b0..58317b7 100644 --- a/src/system/if/dmod_prf.c +++ b/src/system/if/dmod_prf.c @@ -41,6 +41,9 @@ // HELPER FUNCTIONS //============================================================================== +// Maximum field width to prevent integer overflow and unreasonable buffer usage +#define DMOD_PRINTF_MAX_WIDTH 1024 + static int Dmod_StrLen( const char* Str ) { int Len = 0; @@ -68,6 +71,49 @@ static void Dmod_Print_String( char** Buffer, size_t* Pos, size_t Size, const ch } } +static void Dmod_Print_String_Width( char** Buffer, size_t* Pos, size_t Size, const char* Str, int Width, bool LeftAlign, int* Count ) +{ + if( Str == NULL ) Str = "(null)"; + + int StrLen = Dmod_StrLen( Str ); + int PadLen = Width - StrLen; + + // If string is longer than or equal to width, no padding needed + if( PadLen <= 0 ) + { + while( *Str ) + { + Dmod_Print_Char( Buffer, Pos, Size, *Str++, Count ); + } + return; + } + + // Left-aligned: print string first, then padding + if( LeftAlign ) + { + while( *Str ) + { + Dmod_Print_Char( Buffer, Pos, Size, *Str++, Count ); + } + for( int i = 0; i < PadLen; i++ ) + { + Dmod_Print_Char( Buffer, Pos, Size, ' ', Count ); + } + } + // Right-aligned: print padding first, then string + else + { + for( int i = 0; i < PadLen; i++ ) + { + Dmod_Print_Char( Buffer, Pos, Size, ' ', Count ); + } + while( *Str ) + { + Dmod_Print_Char( Buffer, Pos, Size, *Str++, Count ); + } + } +} + static void Dmod_Print_Int( char** Buffer, size_t* Pos, size_t Size, int32_t Value, int* Count ) { char Temp[12]; // Enough for -2147483648 @@ -197,6 +243,34 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list { Format++; + // Parse flags + bool LeftAlign = false; + if( *Format == '-' ) + { + LeftAlign = true; + Format++; + } + + // Parse width + int Width = 0; + while( *Format >= '0' && *Format <= '9' ) + { + int NewWidth = Width * 10 + (*Format - '0'); + // Prevent overflow by capping at maximum width + if( NewWidth > DMOD_PRINTF_MAX_WIDTH ) + { + Width = DMOD_PRINTF_MAX_WIDTH; + // Skip remaining digits + while( *Format >= '0' && *Format <= '9' ) + { + Format++; + } + break; + } + Width = NewWidth; + Format++; + } + // Handle format specifiers switch( *Format ) { @@ -210,7 +284,14 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list case 's': { const char* Str = va_arg( Args, const char* ); - Dmod_Print_String( BufPtr, &Pos, Size, Str, &Count ); + if( Width > 0 ) + { + Dmod_Print_String_Width( BufPtr, &Pos, Size, Str, Width, LeftAlign, &Count ); + } + else + { + Dmod_Print_String( BufPtr, &Pos, Size, Str, &Count ); + } break; } @@ -248,6 +329,23 @@ int Dmod_VSnPrintf_Impl( char* Buffer, size_t Size, const char* Format, va_list default: // Unknown format specifier, just print it Dmod_Print_Char( BufPtr, &Pos, Size, '%', &Count ); + if( LeftAlign ) Dmod_Print_Char( BufPtr, &Pos, Size, '-', &Count ); + // Print width digits if any + if( Width > 0 ) + { + char WidthStr[12]; + int i = 0; + int TempWidth = Width; + do + { + WidthStr[i++] = '0' + (TempWidth % 10); + TempWidth /= 10; + } while( TempWidth > 0 ); + while( i > 0 ) + { + Dmod_Print_Char( BufPtr, &Pos, Size, WidthStr[--i], &Count ); + } + } Dmod_Print_Char( BufPtr, &Pos, Size, *Format, &Count ); break; } diff --git a/tests/system/if/tests_dmod_snprintf.cpp b/tests/system/if/tests_dmod_snprintf.cpp index 1d4ede8..ae42d13 100644 --- a/tests/system/if/tests_dmod_snprintf.cpp +++ b/tests/system/if/tests_dmod_snprintf.cpp @@ -122,3 +122,92 @@ TEST_F(DmodSnPrintfTest, SnPrintfZeroBuffer) ASSERT_EQ(result, 5); // Should return the required size ASSERT_EQ(buffer[0], 0); // Buffer should not be modified } + +/** + * @brief Test for Dmod_SnPrintf with left-aligned string (%-30s) + * + * The test checks if the function handles left-aligned strings with width. + */ +TEST_F(DmodSnPrintfTest, SnPrintfLeftAlignedString) +{ + char buffer[64]; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%-30s", "test"); + + ASSERT_EQ(result, 30); // Should be padded to 30 characters + ASSERT_STREQ(buffer, "test "); + ASSERT_EQ(strlen(buffer), 30); +} + +/** + * @brief Test for Dmod_SnPrintf with right-aligned string (%30s) + * + * The test checks if the function handles right-aligned strings with width. + */ +TEST_F(DmodSnPrintfTest, SnPrintfRightAlignedString) +{ + char buffer[64]; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%30s", "test"); + + ASSERT_EQ(result, 30); // Should be padded to 30 characters + ASSERT_STREQ(buffer, " test"); + ASSERT_EQ(strlen(buffer), 30); +} + +/** + * @brief Test for Dmod_SnPrintf with multiple width-formatted strings + * + * The test checks if the function handles multiple width-formatted strings like in module list. + */ +TEST_F(DmodSnPrintfTest, SnPrintfMultipleWidthStrings) +{ + char buffer[128]; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%-30s %-15s %-40s", + "Module Name", "Version", "Description"); + + ASSERT_EQ(result, 30 + 1 + 15 + 1 + 40); // 30 + space + 15 + space + 40 = 87 + ASSERT_STREQ(buffer, "Module Name Version Description "); +} + +/** + * @brief Test for Dmod_SnPrintf with string longer than width + * + * The test checks if the function handles strings longer than the specified width. + */ +TEST_F(DmodSnPrintfTest, SnPrintfStringLongerThanWidth) +{ + char buffer[64]; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%-10s", "This is a long string"); + + ASSERT_EQ(result, 21); // String length is 21, no padding needed + ASSERT_STREQ(buffer, "This is a long string"); +} + +/** + * @brief Test for Dmod_SnPrintf with mixed format specifiers and widths + * + * The test checks if the function handles both width-formatted and regular specifiers. + */ +TEST_F(DmodSnPrintfTest, SnPrintfMixedWidthFormats) +{ + char buffer[128]; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%-20s: %d", "Count", 42); + + ASSERT_GT(result, 0); + ASSERT_STREQ(buffer, "Count : 42"); +} + +/** + * @brief Test for Dmod_SnPrintf with zero width (should work like normal) + * + * The test checks if the function handles zero width correctly. + */ +TEST_F(DmodSnPrintfTest, SnPrintfZeroWidth) +{ + char buffer[64]; + int result = Dmod_SnPrintf(buffer, sizeof(buffer), "%-0s", "test"); + + ASSERT_EQ(result, 4); + ASSERT_STREQ(buffer, "test"); +} + +