formula/inc/core_resource.hrc                   |    1 +
 formula/source/core/api/FormulaCompiler.cxx     |    7 ++++++-
 formula/source/core/api/token.cxx               |    9 ++++-----
 include/formula/compiler.hxx                    |    3 ++-
 include/formula/opcode.hxx                      |    2 ++
 sc/inc/compiler.hxx                             |    2 +-
 sc/inc/global.hxx                               |    1 +
 sc/qa/unit/data/xls/user_defined_function.xls   |binary
 sc/qa/unit/data/xlsx/user_defined_function.xlsx |binary
 sc/qa/unit/subsequent_export_test4.cxx          |   17 +++++++++++++++++
 sc/source/core/data/global.cxx                  |    6 ++++++
 sc/source/core/opencl/formulagroupcl.cxx        |    1 +
 sc/source/core/tool/compiler.cxx                |   23 ++++++++++++++++++-----
 sc/source/core/tool/interpr4.cxx                |    3 ++-
 sc/source/core/tool/parclass.cxx                |    1 +
 sc/source/core/tool/token.cxx                   |    6 ++++--
 sc/source/filter/excel/excform.cxx              |    6 ++++--
 sc/source/filter/excel/excform8.cxx             |    8 ++++++--
 sc/source/filter/excel/xeformula.cxx            |    2 ++
 sc/source/filter/excel/xicontent.cxx            |    4 ++--
 sc/source/filter/excel/xlformula.cxx            |    4 +++-
 21 files changed, 83 insertions(+), 23 deletions(-)

New commits:
commit bce84f279755171addf29b61f4e8b09e89026c44
Author:     Karthik Godha <[email protected]>
AuthorDate: Wed Feb 18 18:21:54 2026 +0530
Commit:     Balazs Varga <[email protected]>
CommitDate: Tue Mar 10 15:01:00 2026 +0100

    sc: Add new ocUDExternal token for user-defined functions
    
    Excel uses "_xludf." prefix for formulas containing unkown external
    functions. A new token ocUDExternal is created to handle unknown
    external functions
    
    bug-document: forum-mso-en4-352508.xls
    Change-Id: If08469e496ff76bb8b912211bd82c67363711d74
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199631
    Reviewed-by: Balazs Varga <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/formula/inc/core_resource.hrc b/formula/inc/core_resource.hrc
index cb2359afc069..b4038b68b07b 100644
--- a/formula/inc/core_resource.hrc
+++ b/formula/inc/core_resource.hrc
@@ -964,6 +964,7 @@ const std::pair<const char *, int> 
RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] =
     { "_xlfn.ORG.LIBREOFFICE.RANDBETWEEN.NV" , SC_OPCODE_RANDBETWEEN_NV },
     { "_xlfn.RANDARRAY" , SC_OPCODE_RANDARRAY },
     { "#REF!", SC_OPCODE_STOP },
+    { "_xludf.", SC_OPCODE_UD_EXTERNAL },
     { nullptr,  -1 }
 };
 
diff --git a/formula/source/core/api/FormulaCompiler.cxx 
b/formula/source/core/api/FormulaCompiler.cxx
index 0b082ad5c86d..4cd3295075b8 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -1735,6 +1735,7 @@ void FormulaCompiler::Factor()
                     // functions may have to be recalculated or not, we don't
                     // know, classify as ONLOAD_LENIENT.
                 case ocExternal:
+                case ocUDExternal:
                     if (mpToken->GetExternal() == 
"com.sun.star.sheet.addin.Analysis.getRandbetween")
                         pArr->SetExclusiveRecalcModeAlways();
                     else
@@ -1885,6 +1886,7 @@ void FormulaCompiler::Factor()
         }
         else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
                 || eOp == ocExternal
+                || eOp == ocUDExternal
                 || eOp == ocMacro
                 || eOp == ocAnd
                 || eOp == ocOr
@@ -2653,7 +2655,10 @@ const FormulaToken* 
FormulaCompiler::CreateStringFromToken( OUStringBuffer& rBuf
         // Don't export "#name!" in OOXML
     }
     else if( static_cast<sal_uInt16>(eOp) < mxSymbols->getSymbolCount())       
 // Keyword:
-        rBuffer.append( mxSymbols->getSymbol( eOp));
+    {
+        if (eOp != ocUDExternal || maArrIterator.PeekNext()->GetOpCode() == 
ocOpen)
+            rBuffer.append(mxSymbols->getSymbol(eOp));
+    }
     else
     {
         SAL_WARN( "formula.core","unknown OpCode");
diff --git a/formula/source/core/api/token.cxx 
b/formula/source/core/api/token.cxx
index 51521a716183..71b85606ab50 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -79,7 +79,7 @@ bool FormulaToken::IsFunction() const
         || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)     // 
one parameter
         || (SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)     // 
x parameters (cByte==0 in
                                                                             // 
FuncAutoPilot)
-        || eOp == ocMacro || eOp == ocExternal                  // macros, 
AddIns
+        || eOp == ocMacro || eOp == ocExternal || eOp == ocUDExternal       // 
macros, AddIns
         || eOp == ocAnd || eOp == ocOr                          // former 
binary, now x parameters
         || (eOp >= ocInternalBegin && eOp <= ocInternalEnd)     // internal
         ));
@@ -88,9 +88,8 @@ bool FormulaToken::IsFunction() const
 
 sal_uInt8 FormulaToken::GetParamCount() const
 {
-    if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro &&
-         !FormulaCompiler::IsOpCodeJumpCommand( eOp ) &&
-         eOp != ocPercentSign )
+    if (eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro && eOp 
!= ocUDExternal
+        && !FormulaCompiler::IsOpCodeJumpCommand(eOp) && eOp != ocPercentSign)
         return 0;       // parameters and specials
                         // ocIf... jump commands not for FAP, have cByte then
 //2do: bool parameter whether FAP or not?
@@ -435,7 +434,7 @@ bool FormulaTokenArray::AddFormulaToken(
                     AddStringXML( aStrVal );
                 else if ( eOpCode == ocStringName )
                     AddStringName( aStrVal );
-                else if ( eOpCode == ocExternal || eOpCode == ocMacro )
+                else if ( eOpCode == ocExternal || eOpCode == ocMacro || 
eOpCode == ocUDExternal)
                     Add( new formula::FormulaExternalToken( eOpCode, aStrVal ) 
);
                 else if ( eOpCode == ocWhitespace )
                 {
diff --git a/include/formula/compiler.hxx b/include/formula/compiler.hxx
index 64f6c1fad97a..6d8f34a32171 100644
--- a/include/formula/compiler.hxx
+++ b/include/formula/compiler.hxx
@@ -532,7 +532,8 @@
 #define SC_OPCODE_UNIQUE            517
 #define SC_OPCODE_WRAPCOLS          518
 #define SC_OPCODE_WRAPROWS          519
-#define SC_OPCODE_STOP_2_PAR        520     /* last function with two or more 
parameters' OpCode + 1 */
+#define SC_OPCODE_UD_EXTERNAL       520     /* User-defined external function 
*/
+#define SC_OPCODE_STOP_2_PAR        521     /* last function with two or more 
parameters' OpCode + 1 */
 
 #define SC_OPCODE_STOP_FUNCTION     SC_OPCODE_STOP_2_PAR            /* last 
function's OpCode + 1 */
 #define SC_OPCODE_LAST_OPCODE_ID    (SC_OPCODE_STOP_FUNCTION - 1)   /* last 
OpCode */
diff --git a/include/formula/opcode.hxx b/include/formula/opcode.hxx
index 5119b024f0d0..287887c10769 100644
--- a/include/formula/opcode.hxx
+++ b/include/formula/opcode.hxx
@@ -32,6 +32,7 @@ enum OpCode : sal_uInt16
         ocCall              = SC_OPCODE_CALL,
         ocStop              = SC_OPCODE_STOP,
         ocExternal          = SC_OPCODE_EXTERNAL,
+        ocUDExternal        = SC_OPCODE_UD_EXTERNAL,
         ocName              = SC_OPCODE_NAME,
     // Jump commands
         ocIf                = SC_OPCODE_IF,
@@ -552,6 +553,7 @@ inline std::string OpCodeEnumToString(OpCode eCode)
     case ocCall: return "Call";
     case ocStop: return "Stop";
     case ocExternal: return "External";
+    case ocUDExternal: return "UDExternal";
     case ocName: return "Name";
     case ocIf: return "If";
     case ocIfError: return "IfError";
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index 95ac76286f0f..01351c40a009 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -166,7 +166,7 @@ public:
     void SetExternalSingleRef( sal_uInt16 nFileId, const OUString& rTabName, 
const ScSingleRefData& rRef );
     void SetExternalDoubleRef( sal_uInt16 nFileId, const OUString& rTabName, 
const ScComplexRefData& rRef );
     void SetExternalName( sal_uInt16 nFileId, const OUString& rName );
-    void SetExternal(const OUString& rStr);
+    void SetExternal(const OUString& rStr, OpCode eCode = ocExternal);
 
     /** If the token is a non-external reference, determine if the reference is
         valid. If the token is an external reference, return true. Else return
diff --git a/sc/inc/global.hxx b/sc/inc/global.hxx
index 4692dc791220..154e8e1f9ba8 100644
--- a/sc/inc/global.hxx
+++ b/sc/inc/global.hxx
@@ -578,6 +578,7 @@ public:
     static void                 ClearAutoFormat(); //BugId 54209
     static LegacyFuncCollection*      GetLegacyFuncCollection();
     SC_DLLPUBLIC static ScUnoAddInCollection* GetAddInCollection();
+    SC_DLLPUBLIC static bool IsValidExternal(const OUString& rAddIn);
     SC_DLLPUBLIC static ScUserList&         GetUserList();
     static void                 SetUserList( const ScUserList* pNewList );
     /**
diff --git a/sc/qa/unit/data/xls/user_defined_function.xls 
b/sc/qa/unit/data/xls/user_defined_function.xls
new file mode 100644
index 000000000000..d272c1db1223
Binary files /dev/null and b/sc/qa/unit/data/xls/user_defined_function.xls 
differ
diff --git a/sc/qa/unit/data/xlsx/user_defined_function.xlsx 
b/sc/qa/unit/data/xlsx/user_defined_function.xlsx
new file mode 100644
index 000000000000..171e4e884333
Binary files /dev/null and b/sc/qa/unit/data/xlsx/user_defined_function.xlsx 
differ
diff --git a/sc/qa/unit/subsequent_export_test4.cxx 
b/sc/qa/unit/subsequent_export_test4.cxx
index 98749cd9a945..5f6ba6b30c3b 100644
--- a/sc/qa/unit/subsequent_export_test4.cxx
+++ b/sc/qa/unit/subsequent_export_test4.cxx
@@ -2487,6 +2487,23 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, testMacrosInXLSX)
     assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[2]/x:c[2]/x:f", 
u"");
 }
 
+CPPUNIT_TEST_FIXTURE(ScExportTest4, testUserDefinedFunctions)
+{
+    createScDoc("xls/user_defined_function.xls");
+    save(u"Calc Office Open XML"_ustr);
+    xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr);
+    CPPUNIT_ASSERT(pSheet);
+    assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[44]/x:c[1]/x:f",
+                       u"_xludf.Sum(B9:C42)");
+
+    createScDoc("xlsx/user_defined_function.xlsx");
+    save(u"Calc Office Open XML"_ustr);
+    pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr);
+    CPPUNIT_ASSERT(pSheet);
+    assertXPathContent(pSheet, "/x:worksheet/x:sheetData/x:row[42]/x:c[1]/x:f",
+                       u"_xludf.SUM(B9:C42)");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/global.cxx b/sc/source/core/data/global.cxx
index 56f19267f47f..6cfbfee4e12d 100644
--- a/sc/source/core/data/global.cxx
+++ b/sc/source/core/data/global.cxx
@@ -291,6 +291,12 @@ ScUnoAddInCollection* ScGlobal::GetAddInCollection()
     return comphelper::doubleCheckedInit( pAddInCollection, []() { return new 
ScUnoAddInCollection(); });
 }
 
+bool ScGlobal::IsValidExternal(const OUString& rAddIn)
+{
+    return (ScGlobal::GetLegacyFuncCollection()->findByName(rAddIn)
+            || !ScGlobal::GetAddInCollection()->FindFunction(rAddIn, 
false).isEmpty());
+}
+
 ScUserList& ScGlobal::GetUserList()
 {
     assert(!bThreadedGroupCalcInProgress);
diff --git a/sc/source/core/opencl/formulagroupcl.cxx 
b/sc/source/core/opencl/formulagroupcl.cxx
index 1a88b55c961c..aaba63d7b8e8 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -2369,6 +2369,7 @@ 
DynamicKernelSoPArguments::DynamicKernelSoPArguments(const ScCalcConfig& config,
             CASE(ocZTest, std::make_shared<OpZTest>())
 #undef CASE
             case ocExternal:
+            case ocUDExternal:
 #define EXTCASE( name, createCode ) \
                 else if (pChild->GetExternal() == name) \
                 { \
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 3ef89dad82d8..6a45f6e70076 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -3131,12 +3131,14 @@ bool ScCompiler::ParseOpCode( const OUString& rName, 
bool bInArray )
 
     if (!bFound)
     {
-        OUString aIntName;
+        OUString aIntName, aFuncName = rName, sUDPrefix = 
mxSymbols->getSymbol(ocUDExternal);
+        if (aFuncName.matchIgnoreAsciiCase(sUDPrefix))
+            aFuncName = aFuncName.copy(sUDPrefix.getLength());
+
         if (mxSymbols->hasExternals())
         {
             // If symbols are set by filters get mapping to exact name.
-            ExternalHashMap::const_iterator iExt(
-                    mxSymbols->getExternalHashMap().find( rName));
+            ExternalHashMap::const_iterator 
iExt(mxSymbols->getExternalHashMap().find(aFuncName));
             if (iExt != mxSymbols->getExternalHashMap().end())
             {
                 if (ScGlobal::GetAddInCollection()->GetFuncData( 
(*iExt).second))
@@ -3153,14 +3155,20 @@ bool ScCompiler::ParseOpCode( const OUString& rName, 
bool bInArray )
             else
                 // bLocalFirst=false for (English) upper full original name
                 // (service.function)
-                aIntName = ScGlobal::GetAddInCollection()->FindFunction(
-                        rName, !mxSymbols->isEnglish());
+                aIntName = 
ScGlobal::GetAddInCollection()->FindFunction(aFuncName,
+                                                                        
!mxSymbols->isEnglish());
         }
         if (!aIntName.isEmpty())
         {
             maRawToken.SetExternal( aIntName );     // international name
             bFound = true;
         }
+        else if (rName != aFuncName)
+        {
+            // User-defined function
+            maRawToken.SetExternal(aFuncName, ocUDExternal);
+            bFound = true;
+        }
     }
     if (!bFound)
         return false;
@@ -3789,6 +3797,11 @@ bool ScCompiler::ParseExternalNamedRange( const 
OUString& rSymbol, bool& rbInval
     if (!pConv->parseExternalName( rSymbol, aFile, aName, rDoc, 
&maExternalLinks))
         return false;
 
+    // Remove the user-defined flag
+    OUString sUDPrefix = mxSymbols->getSymbol(ocUDExternal);
+    if (aName.matchIgnoreAsciiCase(sUDPrefix))
+        aName = aName.copy(sUDPrefix.getLength());
+
     if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
         return false;
 
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index 71e53e77a3f2..e5c46726b822 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -4538,7 +4538,8 @@ StackVar ScInterpreter::Interpret()
                     case ocBetaInv          :
                     case ocBetaInv_MS       : ScBetaInv();                  
break;
                     case ocFourier          : ScFourier();                  
break;
-                    case ocExternal         : ScExternal();                 
break;
+                    case ocExternal         :
+                    case ocUDExternal       : ScExternal();                 
break;
                     case ocTableOp          : ScTableOp();                  
break;
                     case ocStop :                                           
break;
                     case ocErrorType        : ScErrorType();                
break;
diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx
index e7e07cdccdd2..4ecc1f64d807 100644
--- a/sc/source/core/tool/parclass.cxx
+++ b/sc/source/core/tool/parclass.cxx
@@ -390,6 +390,7 @@ formula::ParamClass 
ScParameterClassification::GetParameterType(
     switch ( eOp )
     {
         case ocExternal:
+        case ocUDExternal:
             return GetExternalParameterType( pToken, nParameter);
         case ocMacro:
             return (nParameter == SAL_MAX_UINT16 ? Value : Reference);
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 66e770943e30..32ba0c3a038b 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -338,9 +338,9 @@ void ScRawToken::SetExternalName( sal_uInt16 nFileId, const 
OUString& rName )
     maExternalName = rName;
 }
 
-void ScRawToken::SetExternal( const OUString& rStr )
+void ScRawToken::SetExternal( const OUString& rStr, OpCode eCode )
 {
-    eOp   = ocExternal;
+    eOp   = eCode;
     eType = svExternal;
     maExternalName = rStr;
 }
@@ -1342,6 +1342,7 @@ void ScTokenArray::CheckForThreading( const FormulaToken& 
r )
         ocText,
         ocSheet,
         ocExternal,
+        ocUDExternal,
         ocDde,
         ocWebservice,
         ocGetPivotData
@@ -1706,6 +1707,7 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
             // Known good, don't change state.
             case ocStop:
             case ocExternal:
+            case ocUDExternal:
             case ocOpen:
             case ocClose:
             case ocSep:
diff --git a/sc/source/filter/excel/excform.cxx 
b/sc/source/filter/excel/excform.cxx
index fb280752ec85..f5e0bb607ea3 100644
--- a/sc/source/filter/excel/excform.cxx
+++ b/sc/source/filter/excel/excform.cxx
@@ -532,8 +532,10 @@ ConvErr ExcelToSc::Convert( std::unique_ptr<ScTokenArray>& 
pResult, XclImpStream
                         aStack << aPool.Store(ocMacro, pName->GetXclName());
                     else if (pName->GetScRangeData())
                         aStack << aPool.StoreName(nUINT16, -1);
-                    else
+                    else if (ScGlobal::IsValidExternal(pName->GetXclName()))
                         aStack << aPool.Store(ocExternal, pName->GetXclName());
+                    else
+                        aStack << aPool.Store(ocUDExternal, 
pName->GetXclName());
                 }
             }
                 break;
@@ -1542,7 +1544,7 @@ void ExcelToSc::DoMulArgs( DefTokenId eId, sal_uInt8 nCnt 
)
     if( nPass < nCnt )
         nCnt = static_cast< sal_uInt8 >( nPass );
 
-    if( nCnt > 0 && eId == ocExternal )
+    if( nCnt > 0 && (eId == ocExternal || eId == ocUDExternal) )
     {
         TokenId             n = eParam[ nCnt - 1 ];
 //##### ADJUST STUPIDITY FOR BASIC-FUNCS!
diff --git a/sc/source/filter/excel/excform8.cxx 
b/sc/source/filter/excel/excform8.cxx
index 68eaf5710a23..ea79d17e42a2 100644
--- a/sc/source/filter/excel/excform8.cxx
+++ b/sc/source/filter/excel/excform8.cxx
@@ -486,8 +486,10 @@ ConvErr ExcelToSc8::Convert( 
std::unique_ptr<ScTokenArray>& rpTokArray, XclImpSt
                              || pName->HasTokens()) // check forward 
declaration
                         aStack << aPool.StoreName(nUINT16,
                                                   pName->IsGlobal() ? -1 : 
pName->GetScTab());
-                    else
+                    else if (ScGlobal::IsValidExternal(pName->GetXclName()))
                         aStack << aPool.Store(ocExternal, pName->GetXclName());
+                    else
+                        aStack << aPool.Store(ocUDExternal, 
pName->GetXclName());
                 }
                 break;
             }
@@ -671,8 +673,10 @@ ConvErr ExcelToSc8::Convert( 
std::unique_ptr<ScTokenArray>& rpTokArray, XclImpSt
                             aStack << aPool.StoreName( nNameIdx, 
pName->IsGlobal() ? -1 : pName->GetScTab());
                         else if (pName->IsMacro())
                             aStack << aPool.Store(ocMacro, 
pName->GetXclName());
-                        else
+                        else if 
(ScGlobal::IsValidExternal(pName->GetXclName()))
                             aStack << aPool.Store(ocExternal, 
pName->GetXclName());
+                        else
+                            aStack << aPool.Store(ocUDExternal, 
pName->GetXclName());
                     }
                 }
                 else if( const XclImpExtName* pExtName = 
rLinkMan.GetExternName( nXtiIndex, nNameIdx ) )
diff --git a/sc/source/filter/excel/xeformula.cxx 
b/sc/source/filter/excel/xeformula.cxx
index 91b7ec58fa89..4a0661728dc7 100644
--- a/sc/source/filter/excel/xeformula.cxx
+++ b/sc/source/filter/excel/xeformula.cxx
@@ -1686,6 +1686,7 @@ void XclExpFmlaCompImpl::AppendDefaultParam( 
XclExpFuncData& rFuncData )
     switch( rFuncData.GetOpCode() )
     {
         case ocExternal:
+        case ocUDExternal:
             AppendAddInCallToken( rFuncData.GetExtFuncData() );
         break;
         case ocEuroConvert:
@@ -1763,6 +1764,7 @@ void XclExpFmlaCompImpl::AppendTrailingParam( 
XclExpFuncData& rFuncData )
         break;
 
         case ocExternal:
+        case ocUDExternal:
         case ocMacro:
             // external or macro call without parameters needs the external 
name reference
             if( nParamCount == 0 )
diff --git a/sc/source/filter/excel/xicontent.cxx 
b/sc/source/filter/excel/xicontent.cxx
index a121cb6d4f61..bbf9e9127bb4 100644
--- a/sc/source/filter/excel/xicontent.cxx
+++ b/sc/source/filter/excel/xicontent.cxx
@@ -666,7 +666,7 @@ void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
             // If it's an external formula then skip the entry
             if (const formula::FormulaToken* pToken = pTokArr->FirstToken())
             {
-                if (pToken->GetOpCode() == ocExternal)
+                if (pToken->GetOpCode() == ocExternal || pToken->GetOpCode() 
== ocUDExternal)
                     return;
             }
             xTokArr1 = std::move( pTokArr );
@@ -686,7 +686,7 @@ void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
             // If it's an external formula then skip the entry
             if (const formula::FormulaToken* pToken = pTokArr->FirstToken())
             {
-                if (pToken->GetOpCode() == ocExternal)
+                if (pToken->GetOpCode() == ocExternal || pToken->GetOpCode() 
== ocUDExternal)
                     return;
             }
             xTokArr2 = std::move( pTokArr );
diff --git a/sc/source/filter/excel/xlformula.cxx 
b/sc/source/filter/excel/xlformula.cxx
index 2f8b2756d6c9..6d62d8ae8923 100644
--- a/sc/source/filter/excel/xlformula.cxx
+++ b/sc/source/filter/excel/xlformula.cxx
@@ -219,7 +219,8 @@ const XclFunctionInfo saFuncTable_2[] =
     { ocLenB,               211,    1,  1,  V, { VR }, 0, nullptr },
     { ocRoundUp,            212,    2,  2,  V, { VR }, 0, nullptr },
     { ocRoundDown,          213,    2,  2,  V, { VR }, 0, nullptr },
-    { ocExternal,           255,    1,  MX, R, { RO_E, RO }, 
EXC_FUNCFLAG_IMPORTONLY, nullptr }
+    { ocExternal,           255,    1,  MX, R, { RO_E, RO }, 
EXC_FUNCFLAG_IMPORTONLY, nullptr },
+    { ocUDExternal,         255,    1,  MX, R, { RO_E, RO }, 
EXC_FUNCFLAG_IMPORTONLY, nullptr }
 };
 
 /** Functions new in BIFF3. */
@@ -348,6 +349,7 @@ const XclFunctionInfo saFuncTable_5[] =
     { ocGetDiffDate360,     220,    2,  3,  V, { VR }, 0, nullptr },           
       // BIFF3-4: 2, BIFF5: 2-3
     { ocMacro,              255,    1,  MX, R, { RO_E, RO }, 
EXC_FUNCFLAG_EXPORTONLY, nullptr },
     { ocExternal,           255,    1,  MX, R, { RO_E, RO }, 
EXC_FUNCFLAG_EXPORTONLY, nullptr },
+    { ocUDExternal,         255,    1,  MX, R, { RO_E, RO }, 
EXC_FUNCFLAG_EXPORTONLY, nullptr },
     { ocConcat,             336,    0,  MX, V, { VR }, 0, nullptr },
     { ocPower,              337,    2,  2,  V, { VR }, 0, nullptr },
     { ocRad,                342,    1,  1,  V, { VR }, 0, nullptr },

Reply via email to