basic/qa/basic_coverage/test_format_function.bas |   17 +++++
 basic/source/sbx/sbxscan.cxx                     |   72 ++++++++++++-----------
 2 files changed, 55 insertions(+), 34 deletions(-)

New commits:
commit a3597edb52b671e6095f96c8705cd458a50d8799
Author:     Mike Kaganski <[email protected]>
AuthorDate: Fri Aug 29 03:48:15 2025 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Fri Aug 29 18:33:37 2025 +0200

    tdf#143182: fix string handling in SbxValue::Format
    
    When a value is convertible to number, check if the format string
    is for numbers of text (in SbxValue::Format). If the value is not
    convertible to number, handle it according to the format string in
    printfmtstr.
    
    Change-Id: I6e11aeffd8dd8581ebc868762944e9d6d9393514
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190348
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/basic/qa/basic_coverage/test_format_function.bas 
b/basic/qa/basic_coverage/test_format_function.bas
index d1c51fe791a5..f72e504a2379 100644
--- a/basic/qa/basic_coverage/test_format_function.bas
+++ b/basic/qa/basic_coverage/test_format_function.bas
@@ -21,6 +21,23 @@ Sub verify_testFormat
     d = "2024-09-16 17:03:30"
     TestUtil.AssertEqual(Format(d, "YYYY-MM-DD"), "2024-09-16", "Format(d, 
""YYYY-MM-DD"")")
     TestUtil.AssertEqual(Format("2024-09-16 05:03:30 PM", "hh-mm-ss"), 
"17-03-30", "Format(""2024-09-16 05:03:30 PM"", ""hh-mm-ss"")")
+    ' A string that can be converted to number, with a text-format string
+    TestUtil.AssertEqual(Format("001", "foo @ bar"), "foo 001 bar", 
"Format(""001"", ""foo @ bar"")")
+    ' A string that cannot be converted to number, with a text-format string
+    TestUtil.AssertEqual(Format("baz", "foo @ bar"), "foo baz bar", 
"Format(""baz"", ""foo @ bar"")")
+    ' Legacy format strings
+    ' leading '!': get only the first character of the source string
+    TestUtil.AssertEqual(Format("abc", "! @"), "a", "Format(""abc"", ""! @"")")
+    ' leading '\': get as many characters from source string, as in format 
string until the next '\', padding with spaces as needed
+    TestUtil.AssertEqual(Format("abcdefgh", "S%"), "abcde", 
"Format(""abcdefgh"", ""S%"")")
+    TestUtil.AssertEqual(Format("abcdefgh", "S45"), "abcdef", 
"Format(""abcdefgh"", ""S45"")")
+    TestUtil.AssertEqual(Format("abc", "S45\"), "abc    ", "Format(""abc"", 
""S45\"")")
+    ' leading '&': get the whole source string unmodified
+    TestUtil.AssertEqual(Format("abc", "& @"), "abc", "Format(""abc"", ""& 
@"")")
+    ' non-leading positions
+    TestUtil.AssertEqual(Format("abc", "@ !"), "abc !", "Format(""abc"", ""@ 
!"")")
+    TestUtil.AssertEqual(Format("abc", "1�5"), "abc", "Format(""abc"", 
""1�5"")")
+    TestUtil.AssertEqual(Format("abc", "@ &"), "abc &", "Format(""abc"", ""@ 
&"")")
 
     Exit Sub
 errorHandler:
diff --git a/basic/source/sbx/sbxscan.cxx b/basic/source/sbx/sbxscan.cxx
index 546366af5698..df6e704299b8 100644
--- a/basic/source/sbx/sbxscan.cxx
+++ b/basic/source/sbx/sbxscan.cxx
@@ -299,39 +299,6 @@ void ImpCvtNum( double nNum, short nPrec, OUString& rRes, 
bool bCoreString )
     rRes = rtl::math::doubleToUString(nNum, rtl_math_StringFormat_Automatic, 
nPrec, cDecimalSep, true);
 }
 
-// formatted number output
-
-static void printfmtstr(std::u16string_view rStr, OUString& rRes, 
std::u16string_view rFmt)
-{
-    if (rFmt.empty())
-        rFmt = u"&";
-
-    OUStringBuffer aTemp;
-    auto pStr = rStr.begin();
-    auto pFmt = rFmt.begin();
-
-    switch( *pFmt )
-    {
-    case '!':
-        if (pStr != rStr.end())
-            aTemp.append(*pStr);
-        break;
-    case '\':
-        do
-        {
-            aTemp.append(pStr != rStr.end() ? *pStr++ : u' ');
-        } while (++pFmt != rFmt.end() && *pFmt != '\');
-        aTemp.append(pStr != rStr.end() ? *pStr : u' ');
-        break;
-    case '&':
-    default:
-        aTemp = rStr;
-        break;
-    }
-    rRes = aTemp.makeStringAndClear();
-}
-
-
 bool SbxValue::Scan(std::u16string_view rSrc, sal_Int32* pLen)
 {
     ErrCode eRes = ERRCODE_NONE;
@@ -527,6 +494,40 @@ std::optional<double> GetNumberIntl(const SbxValue& val, 
OUString& rStrVal,
                        : std::nullopt;
     }
 }
+
+void printfmtstr(const OUString& rStr, OUString& rRes, const OUString& rFmt)
+{
+    rRes = rStr;
+
+    switch (rFmt.isEmpty() ? '&' : rFmt[0])
+    {
+        case '!':
+            if (!rStr.isEmpty())
+                rRes = rStr.copy(0, 1);
+            break;
+        case '\':
+            if (auto i = rFmt.indexOf('\', 1) + 1, l = i ? i : 
rFmt.getLength();
+                l < rStr.getLength())
+                rRes = rStr.copy(0, l);
+            else if (l > rStr.getLength())
+                rRes += OUString::Concat(RepeatedUChar(' ', l - 
rStr.getLength()));
+            break;
+        case '&':
+            break; // Already assigned the correct value
+        default:
+            if (auto formatter = GetFormatter())
+            {
+                sal_uInt32 nIndex;
+                formatter->PutandConvertEntry(
+                    // [-loplugin:redundantfcast] a temporary is required here
+                    o3tl::temporary(OUString(rFmt)), 
o3tl::temporary(sal_Int32()),
+                    o3tl::temporary(SvNumFormatType()), nIndex, 
LANGUAGE_ENGLISH_US,
+                    
Application::GetSettings().GetLanguageTag().getLanguageType(), true);
+                formatter->GetOutputString(rStr, nIndex, rRes, 
&o3tl::temporary<const Color*>({}));
+            }
+            break;
+    }
+}
 } // namespace
 
 void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
@@ -658,7 +659,10 @@ void SbxValue::Format( OUString& rRes, const OUString* 
pFmt ) const
     else
     {
         pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, 
LANGUAGE_ENGLISH_US, eLangType, true);
-        pFormatter->GetOutputString(*number, nIndex, rRes, &pCol);
+        if (nType == SvNumFormatType::TEXT && IsString())
+            pFormatter->GetOutputString(GetOUString(), nIndex, rRes, &pCol);
+        else
+            pFormatter->GetOutputString(*number, nIndex, rRes, &pCol);
     }
 #endif
 }

Reply via email to