Date: prev next · Thread: first prev next last
2013 Archives by date, by thread · List index


Hi,

The attached patch implements the much requested IFERROR and IFNA
spreadsheet functions on the 4-0 branch. I would like to have them as
late feature in 4-0-0 if possible though string freeze was announced
already, so 3 reviews would be needed. At least 4-0 should have it for
4-1-0

Thanks
  Eike

-- 
LibreOffice Calc developer. Number formatter stricken i18n transpositionizer.
New GnuPG key 0x65632D3A : 2265 D7F3 A7B0 95CC 3918  630B 6A6C D5B7 6563 2D3A
Old GnuPG key 0x293C05FD : 997A 4C60 CE41 0149 0DB3  9E96 2F1A D073 293C 05FD
Support the FSFE, care about Free Software! https://fsfe.org/support/?erack
From cc101e678799c0f437dc02b6930fe858c560972d Mon Sep 17 00:00:00 2001
Message-Id: <cc101e678799c0f437dc02b6930fe858c560972d.1358013933.git.erack@redhat.com>
From: Eike Rathke <erack@redhat.com>
Date: Sat, 12 Jan 2013 17:48:59 +0100
Subject: [PATCH] fdo#56124 add functions IFERROR and IFNA to calc as in
 ODFF1.2
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------erAck-patch-parts"

This is a multi-part message in MIME format.
--------------erAck-patch-parts
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit


Change-Id: Ic282e1510e121be8fe52320f1f0fe0acc4b9a652
---
 formula/inc/formula/compiler.hrc               |   42 +++---
 formula/inc/formula/errorcodes.hxx             |    3 +
 formula/inc/formula/opcode.hxx                 |    2 +
 formula/source/core/api/FormulaCompiler.cxx    |   79 ++++++++--
 formula/source/core/api/token.cxx              |   22 ++-
 formula/source/core/resource/core_resource.src |   12 ++
 sc/inc/helpids.h                               |    2 +
 sc/qa/unit/ucalc.cxx                           |   76 +++++++++
 sc/source/core/inc/interpre.hxx                |    1 +
 sc/source/core/tool/interpr1.cxx               |  198 ++++++++++++++++++++++--
 sc/source/core/tool/interpr4.cxx               |   13 +-
 sc/source/core/tool/parclass.cxx               |    4 +
 sc/source/core/tool/token.cxx                  |    5 +
 sc/source/filter/excel/xlformula.cxx           |   13 ++-
 sc/source/filter/oox/formulabase.cxx           |   11 +-
 sc/source/ui/src/scfuncs.src                   |   64 ++++++++
 sc/util/hidother.src                           |    2 +
 17 files changed, 485 insertions(+), 64 deletions(-)


--------------erAck-patch-parts
Content-Type: text/x-patch; name="0001-fdo-56124-add-functions-IFERROR-and-IFNA-to-calc-as-.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; 
filename="0001-fdo-56124-add-functions-IFERROR-and-IFNA-to-calc-as-.patch"

diff --git a/formula/inc/formula/compiler.hrc b/formula/inc/formula/compiler.hrc
index 582e3a5..7f976ae 100644
--- a/formula/inc/formula/compiler.hrc
+++ b/formula/inc/formula/compiler.hrc
@@ -29,26 +29,28 @@
 #define SC_OPCODE_NAME                4
 #define SC_OPCODE_EXTERNAL_REF        5
 #define SC_OPCODE_IF                  6     /* jump commands */
-#define SC_OPCODE_CHOSE               7
-#define SC_OPCODE_OPEN                8     /* parentheses and separators */
-#define SC_OPCODE_CLOSE               9
-#define SC_OPCODE_SEP                10
-#define SC_OPCODE_MISSING            11     /* special OpCodes */
-#define SC_OPCODE_BAD                12
-#define SC_OPCODE_STRINGXML          13
-#define SC_OPCODE_SPACES             14
-#define SC_OPCODE_MAT_REF            15
-#define SC_OPCODE_DB_AREA            16     /* additional access operators */
-#define SC_OPCODE_MACRO              17
-#define SC_OPCODE_COL_ROW_NAME       18
-#define SC_OPCODE_COL_ROW_NAME_AUTO  19
-#define SC_OPCODE_PERCENT_SIGN       20     /* operator _follows_ value */
-#define SC_OPCODE_ARRAY_OPEN         21
-#define SC_OPCODE_ARRAY_CLOSE        22
-#define SC_OPCODE_ARRAY_ROW_SEP      23
-#define SC_OPCODE_ARRAY_COL_SEP      24     /* some convs use sep != col_sep */
-#define SC_OPCODE_STOP_DIV           25
-#define SC_OPCODE_SKIP               26     /* used to skip raw tokens during string compilation */
+#define SC_OPCODE_IF_ERROR            7
+#define SC_OPCODE_IF_NA               8
+#define SC_OPCODE_CHOSE               9
+#define SC_OPCODE_OPEN               10     /* parentheses and separators */
+#define SC_OPCODE_CLOSE              11
+#define SC_OPCODE_SEP                12
+#define SC_OPCODE_MISSING            13     /* special OpCodes */
+#define SC_OPCODE_BAD                14
+#define SC_OPCODE_STRINGXML          15
+#define SC_OPCODE_SPACES             16
+#define SC_OPCODE_MAT_REF            17
+#define SC_OPCODE_DB_AREA            18     /* additional access operators */
+#define SC_OPCODE_MACRO              19
+#define SC_OPCODE_COL_ROW_NAME       20
+#define SC_OPCODE_COL_ROW_NAME_AUTO  21
+#define SC_OPCODE_PERCENT_SIGN       22     /* operator _follows_ value */
+#define SC_OPCODE_ARRAY_OPEN         23
+#define SC_OPCODE_ARRAY_CLOSE        24
+#define SC_OPCODE_ARRAY_ROW_SEP      25
+#define SC_OPCODE_ARRAY_COL_SEP      26     /* some convs use sep != col_sep */
+#define SC_OPCODE_STOP_DIV           27
+#define SC_OPCODE_SKIP               28     /* used to skip raw tokens during string compilation */
 
 /*** error constants #... ***/
 #define SC_OPCODE_START_ERRORS       30
diff --git a/formula/inc/formula/errorcodes.hxx b/formula/inc/formula/errorcodes.hxx
index a99dc7b..1e145e0 100644
--- a/formula/inc/formula/errorcodes.hxx
+++ b/formula/inc/formula/errorcodes.hxx
@@ -72,6 +72,9 @@ const sal_uInt16 errNestedArray          = 533;
 // be used to distinguish that condition from all other (inherited) errors. Do
 // not use for anything else! Never push or inherit the error otherwise!
 const sal_uInt16 errNotNumericString     = 534;
+// ScInterpreter internal:  jump matrix already has a result at this position,
+// do not overwrite in case of empty code path.
+const sal_uInt16 errJumpMatHasResult     = 535;
 
 // Interpreter: NA() not available condition, not a real error
 const sal_uInt16 NOTAVAILABLE            = 0x7fff;
diff --git a/formula/inc/formula/opcode.hxx b/formula/inc/formula/opcode.hxx
index cd1831b..e37395e 100644
--- a/formula/inc/formula/opcode.hxx
+++ b/formula/inc/formula/opcode.hxx
@@ -34,6 +34,8 @@ enum OpCodeEnum
         ocExternalRef       = SC_OPCODE_EXTERNAL_REF,
     // Jump commands
         ocIf                = SC_OPCODE_IF,
+        ocIfError           = SC_OPCODE_IF_ERROR,
+        ocIfNA              = SC_OPCODE_IF_NA,
         ocChose             = SC_OPCODE_CHOSE,
     // Parentheses and separators
         ocOpen              = SC_OPCODE_OPEN,
diff --git a/formula/source/core/api/FormulaCompiler.cxx 
b/formula/source/core/api/FormulaCompiler.cxx
index f79201b..c402113 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -433,6 +433,8 @@ uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::create
             // Additional functions not within range of functions.
             static const sal_uInt16 aOpCodes[] = {
                 SC_OPCODE_IF,
+                SC_OPCODE_IF_ERROR,
+                SC_OPCODE_IF_NA,
                 SC_OPCODE_CHOSE,
                 SC_OPCODE_AND,
                 SC_OPCODE_OR,
@@ -824,7 +826,7 @@ sal_uInt16 FormulaCompiler::GetErrorConstant( const String& rName ) const
     {
         switch ((*iLook).second)
         {
-            // Not all may make sense in a formula, but these we know as 
+            // Not all may make sense in a formula, but these we know as
             // opcodes.
             case ocErrNull:
                 nError = errNoCode;
@@ -1125,7 +1127,8 @@ void FormulaCompiler::Factor()
                 || eOp == ocOr
                 || eOp == ocBad
                 || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
-                || (bCompileForFAP && ((eOp == ocIf) || (eOp == ocChose)))
+                || ( bCompileForFAP
+                     && ( eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose ) )
             )
         {
             pFacToken = mpToken;
@@ -1174,14 +1177,25 @@ void FormulaCompiler::Factor()
                 pFacToken->SetByte( nSepCount );
             PutCode( pFacToken );
         }
-        else if (eOp == ocIf || eOp == ocChose)
+        else if (eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose)
         {
             // the PC counters are -1
             pFacToken = mpToken;
-            if ( eOp == ocIf )
-                pFacToken->GetJump()[ 0 ] = 3;  // if, else, behind
-            else
-                pFacToken->GetJump()[ 0 ] = MAXJUMPCOUNT+1;
+            switch (eOp)
+            {
+                case ocIf:
+                    pFacToken->GetJump()[ 0 ] = 3;  // if, else, behind
+                    break;
+                case ocChose:
+                    pFacToken->GetJump()[ 0 ] = MAXJUMPCOUNT+1;
+                    break;
+                case ocIfError:
+                case ocIfNA:
+                    pFacToken->GetJump()[ 0 ] = 2;  // if, behind
+                    break;
+                default:
+                    SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump count 
case?");
+            }
             eOp = NextToken();
             if (eOp == ocOpen)
             {
@@ -1190,14 +1204,30 @@ void FormulaCompiler::Factor()
             }
             else
                 SetError(errPairExpected);
-            short nJumpCount = 0;
             PutCode( pFacToken );
-            // during AutoCorrect (since pArr->GetCodeError() is
+            // During AutoCorrect (since pArr->GetCodeError() is
             // ignored) an unlimited ocIf would crash because
             // ScRawToken::Clone() allocates the JumpBuffer according to
-            // nJump[0]*2+2, which is 3*2+2 on ocIf.
-            const short nJumpMax =
-                (pFacToken->GetOpCode() == ocIf ? 3 : MAXJUMPCOUNT);
+            // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
+            short nJumpMax;
+            OpCode eFacOpCode = pFacToken->GetOpCode();
+            switch (eFacOpCode)
+            {
+                case ocIf:
+                    nJumpMax = 3;
+                    break;
+                case ocChose:
+                    nJumpMax = MAXJUMPCOUNT;
+                    break;
+                case ocIfError:
+                case ocIfNA:
+                    nJumpMax = 2;
+                    break;
+                default:
+                    nJumpMax = 0;
+                    SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump max 
case?");
+            }
+            short nJumpCount = 0;
             while ( (nJumpCount < (MAXJUMPCOUNT - 1)) && (eOp == ocSep)
                     && (!pArr->GetCodeError() || bIgnoreErrors) )
             {
@@ -1216,11 +1246,28 @@ void FormulaCompiler::Factor()
                 // always limit to nJumpMax, no arbitrary overwrites
                 if ( ++nJumpCount <= nJumpMax )
                     pFacToken->GetJump()[ nJumpCount ] = pc-1;
-                if ((pFacToken->GetOpCode() == ocIf && (nJumpCount > 3)) ||
-                                 (nJumpCount >= MAXJUMPCOUNT))
-                    SetError(errIllegalParameter);
-                else
+                eFacOpCode = pFacToken->GetOpCode();
+                bool bLimitOk;
+                switch (eFacOpCode)
+                {
+                    case ocIf:
+                        bLimitOk = (nJumpCount <= 3);
+                        break;
+                    case ocChose:
+                        bLimitOk = (nJumpCount < MAXJUMPCOUNT); /* TODO: check, really <, not <=? 
*/
+                        break;
+                    case ocIfError:
+                    case ocIfNA:
+                        bLimitOk = (nJumpCount <= 2);
+                        break;
+                    default:
+                        bLimitOk = false;
+                        SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump 
limit case?");
+                }
+                if (bLimitOk)
                     pFacToken->GetJump()[ 0 ] = nJumpCount;
+                else
+                    SetError(errIllegalParameter);
             }
         }
         else if ( eOp == ocMissing )
diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx
index d187073..f6110e7c 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -76,7 +76,7 @@ bool FormulaToken::IsFunction() const
             eOp != ocColRowNameAuto && eOp != ocName && eOp != ocDBArea &&
            (GetByte() != 0                                                  // x parameters
         || (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)   // no parameter
-        || (ocIf == eOp ||  ocChose ==  eOp     )                           // @ jump commands
+        || (ocIf == eOp || ocIfError == eOp || ocIfNA == eOp || ocChose == eOp ) // @ jump commands
         || (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)
@@ -91,9 +91,10 @@ bool FormulaToken::IsFunction() const
 sal_uInt8 FormulaToken::GetParamCount() const
 {
     if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro &&
-            eOp != ocIf && eOp != ocChose && eOp != ocPercentSign )
+         eOp != ocIf && eOp != ocIfError && eOp != ocIfNA && eOp != ocChose &&
+         eOp != ocPercentSign )
         return 0;       // parameters and specials
-                        // ocIf and ocChose not for FAP, have cByte then
+                        // ocIf, ocIfError, ocIfNA and ocChose not for FAP, have cByte then
 //2do: bool parameter whether FAP or not?
     else if ( GetByte() )
         return GetByte();   // all functions, also ocExternal and ocMacro
@@ -106,7 +107,7 @@ sal_uInt8 FormulaToken::GetParamCount() const
         return 0;           // no parameter
     else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
         return 1;           // one parameter
-    else if ( eOp == ocIf || eOp == ocChose )
+    else if ( eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose )
         return 1;           // only the condition counts as parameter
     else
         return 0;           // all the rest, no Parameter, or
@@ -842,8 +843,8 @@ bool FormulaTokenArray::HasMatrixDoubleRefOps()
             }
             if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() )  )
                 pStack[sp++] = t;
-            else if ( eOp == ocIf || eOp == ocChose )
-            {   // Jumps ignorieren, vorheriges Result (Condition) poppen
+            else if ( eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose )
+            {   // ignore Jumps, pop previous Result (Condition)
                 if ( sp )
                     --sp;
             }
@@ -1178,10 +1179,17 @@ FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp )
             pRet = new FormulaToken( svSep,eOp );
             break;
         case ocIf:
+        case ocIfError:
+        case ocIfNA:
         case ocChose:
             {
                 short nJump[MAXJUMPCOUNT + 1];
-                nJump[ 0 ] = ocIf == eOp ? 3 : MAXJUMPCOUNT+1;
+                if ( eOp == ocIf )
+                    nJump[ 0 ] = 3;
+                else if ( eOp == ocChose )
+                    nJump[ 0 ] = MAXJUMPCOUNT + 1;
+                else
+                    nJump[ 0 ] = 2;
                 pRet = new FormulaJumpToken( eOp, (short*)nJump );
             }
             break;
diff --git a/formula/source/core/resource/core_resource.src 
b/formula/source/core/resource/core_resource.src
index 8b0ee14..d21a737 100644
--- a/formula/source/core/resource/core_resource.src
+++ b/formula/source/core/resource/core_resource.src
@@ -24,6 +24,8 @@
 Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
 {
     String SC_OPCODE_IF { Text = "IF" ; };
+    String SC_OPCODE_IF_ERROR { Text = "IFERROR" ; };
+    String SC_OPCODE_IF_NA { Text = "IFNA" ; };
     String SC_OPCODE_CHOSE { Text = "CHOOSE" ; };
     String SC_OPCODE_OPEN { Text = "(" ; };
     String SC_OPCODE_CLOSE { Text = ")" ; };
@@ -362,6 +364,8 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
 Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH
 {
     String SC_OPCODE_IF { Text = "IF" ; };
+    String SC_OPCODE_IF_ERROR { Text = "IFERROR" ; };
+    String SC_OPCODE_IF_NA { Text = "IFNA" ; };
     String SC_OPCODE_CHOSE { Text = "CHOOSE" ; };
     String SC_OPCODE_OPEN { Text = "(" ; };
     String SC_OPCODE_CLOSE { Text = ")" ; };
@@ -700,6 +704,14 @@ Resource RID_STRLIST_FUNCTION_NAMES
     {
         Text [ en-US ] = "IF" ;
     };
+    String SC_OPCODE_IF_ERROR
+    {
+        Text [ en-US ] = "IFERROR" ;
+    };
+    String SC_OPCODE_IF_NA
+    {
+        Text [ en-US ] = "IFNA" ;
+    };
     String SC_OPCODE_CHOSE
     {
         Text [ en-US ] = "CHOOSE" ;
diff --git a/sc/inc/helpids.h b/sc/inc/helpids.h
index 2e2aa4c..e273ce4 100644
--- a/sc/inc/helpids.h
+++ b/sc/inc/helpids.h
@@ -433,6 +433,8 @@
 #define HID_FUNC_NICHT                                          "SC_HID_FUNC_NICHT"
 #define HID_FUNC_WAHR                                           "SC_HID_FUNC_WAHR"
 #define HID_FUNC_WENN                                           "SC_HID_FUNC_WENN"
+#define HID_FUNC_IFERROR                                        "SC_HID_FUNC_IFERROR"
+#define HID_FUNC_IFNA                                           "SC_HID_FUNC_IFNA"
 #define HID_FUNC_ODER                                           "SC_HID_FUNC_ODER"
 #define HID_FUNC_UND                                            "SC_HID_FUNC_UND"
 #define HID_FUNC_XOR                                            "SC_HID_FUNC_XOR"
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index d9d643f..14d0da1 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -671,6 +671,79 @@ void testFuncCOUNTIF(ScDocument* pDoc)
     CPPUNIT_ASSERT_MESSAGE("We shouldn't count empty string as valid number.", result == 0.0);
 }
 
+void testFuncIFERROR(ScDocument* pDoc)
+{
+    // IFERROR/IFNA (fdo#56124)
+
+    // Empty A1:A39 first.
+    clearRange(pDoc, ScRange(0, 0, 0, 0, 40, 0));
+
+    // Raw data (rows 1 through 12)
+    const char* aData[] = {
+        "1",
+        "e",
+        "=SQRT(4)",
+        "=SQRT(-2)",
+        "=A4",
+        "=1/0",
+        "=NA()",
+        "bar",
+        "4",
+        "gee",
+        "=1/0",
+        "23"
+    };
+
+    SCROW nRows = SAL_N_ELEMENTS(aData);
+    for (SCROW i = 0; i < nRows; ++i)
+        pDoc->SetString(0, i, 0, rtl::OUString::createFromAscii(aData[i]));
+
+    printRange(pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
+
+    // formulas and results
+    struct {
+        const char* pFormula; const char* pResult;
+    } aChecks[] = {
+        { "=IFERROR(A1;9)",                         "1" },
+        { "=IFERROR(A2;9)",                         "e" },
+        { "=IFERROR(A3;9)",                         "2" },
+        { "=IFERROR(A4;-7)",                       "-7" },
+        { "=IFERROR(A5;-7)",                       "-7" },
+        { "=IFERROR(A6;-7)",                       "-7" },
+        { "=IFERROR(A7;-7)",                       "-7" },
+        { "=IFNA(A6;9)",                      "#DIV/0!" },
+        { "=IFNA(A7;-7)",                          "-7" },
+        { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)",    "4" },
+        { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
+        { "{=IFERROR(3*A11:A12;1998)}[0]",       "1998" },  // um.. this is not the correct way to 
insert a
+        { "{=IFERROR(3*A11:A12;1998)}[1]",         "69" }   // matrix formula, just a place 
holder, see below
+    };
+
+    nRows = SAL_N_ELEMENTS(aChecks);
+    for (SCROW i = 0; i < nRows-2; ++i)
+    {
+        SCROW nRow = 20 + i;
+        pDoc->SetString(0, nRow, 0, rtl::OUString::createFromAscii(aChecks[i].pFormula));
+    }
+
+    // Create a matrix range in last two rows of the range above, actual data
+    // of the placeholders.
+    ScMarkData aMark;
+    aMark.SelectOneTable(0);
+    pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)", 
NULL);
+
+    pDoc->CalcAll();
+
+    for (SCROW i = 0; i < nRows; ++i)
+    {
+        rtl::OUString aResult;
+        SCROW nRow = 20 + i;
+        pDoc->GetString(0, nRow, 0, aResult);
+        CPPUNIT_ASSERT_EQUAL_MESSAGE(
+            aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
+    }
+}
+
 void testFuncVLOOKUP(ScDocument* pDoc)
 {
     // VLOOKUP
@@ -1063,6 +1136,7 @@ void Test::testCellFunctions()
     testFuncPRODUCT(m_pDoc);
     testFuncN(m_pDoc);
     testFuncCOUNTIF(m_pDoc);
+    testFuncIFERROR(m_pDoc);
     testFuncVLOOKUP(m_pDoc);
     testFuncMATCH(m_pDoc);
     testFuncCELL(m_pDoc);
@@ -4175,6 +4249,8 @@ void Test::testFunctionLists()
         "AND",
         "FALSE",
         "IF",
+        "IFERROR",
+        "IFNA",
         "NOT",
         "OR",
         "TRUE",
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index 762b818..267e659 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -368,6 +368,7 @@ bool LookupQueryWithCache( ScAddress & o_rResultPos,
         const ScQueryParam & rParam ) const;
 
 void ScIfJump();
+void ScIfError( bool bNAonly );
 void ScChoseJump();
 
 // Be sure to only call this if pStack[sp-nStackLevel] really contains a
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index ec85a01..0ef12c7 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -54,6 +54,7 @@
 #include <math.h>
 #include <vector>
 #include <memory>
+#include <limits>
 #include "cellkeytranslator.hxx"
 #include "lookupcache.hxx"
 #include "rangenam.hxx"
@@ -243,6 +244,188 @@ void ScInterpreter::ScIfJump()
 }
 
 
+/** Store a matrix value in another matrix in the context of that other matrix
+    is the result matrix of a jump matrix. All arguments must be valid and are
+    not checked. */
+static void lcl_storeJumpMatResult( const ScMatrix* pMat, ScMatrix* pResMat, SCSIZE nC, SCSIZE nR )
+{
+    if ( pMat->IsValue( nC, nR ) )
+    {
+        double fVal = pMat->GetDouble( nC, nR );
+        pResMat->PutDouble( fVal, nC, nR );
+    }
+    else if ( pMat->IsEmpty( nC, nR ) )
+    {
+        pResMat->PutEmpty( nC, nR );
+    }
+    else
+    {
+        const String& rStr = pMat->GetString( nC, nR );
+        pResMat->PutString( rStr, nC, nR );
+    }
+}
+
+
+void ScInterpreter::ScIfError( bool bNAonly )
+{
+    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Donkers/erAck", "ScInterpreter::ScIfError" );
+    const short* pJump = pCur->GetJump();
+    short nJumpCount = pJump[ 0 ];
+    if (!sp)
+    {
+        PushError( errUnknownStackVariable);
+        aCode.Jump( pJump[ nJumpCount  ], pJump[ nJumpCount ] );
+        return;
+    }
+
+    FormulaTokenRef xToken( pStack[ sp - 1 ] );
+    bool bError = false;
+    sal_uInt16 nOldGlobalError = nGlobalError;
+    nGlobalError = 0;
+
+    MatrixDoubleRefToMatrix();
+    switch (GetStackType())
+    {
+        default:
+            Pop();
+            break;
+        case svError:
+            PopError();
+            bError = true;
+            break;
+        case svDoubleRef:
+        case svSingleRef:
+            {
+                ScAddress aAdr;
+                if (!PopDoubleRefOrSingleRef( aAdr))
+                    bError = true;
+                else
+                {
+                    ScBaseCell* pCell = GetCell( aAdr);
+                    nGlobalError = GetCellErrCode( pCell);
+                    if (nGlobalError)
+                        bError = true;
+                }
+            }
+            break;
+        case svExternalSingleRef:
+        case svExternalDoubleRef:
+            {
+                double fVal;
+                String aStr;
+                // Handles also existing jump matrix case and sets error on
+                // elements.
+                GetDoubleOrStringFromMatrix( fVal, aStr);
+                if (nGlobalError)
+                    bError = true;
+            }
+            break;
+        case svMatrix:
+            {
+                const ScMatrixRef pMat = PopMatrix();
+                if (!pMat || (nGlobalError && (!bNAonly || nGlobalError == NOTAVAILABLE)))
+                {
+                    bError = true;
+                    break;  // switch
+                }
+                // If the matrix has no queried error at all we can simply use
+                // it as result and don't need to bother with jump matrix.
+                SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
+                       nErrorRow = ::std::numeric_limits<SCSIZE>::max();
+                SCSIZE nCols, nRows;
+                pMat->GetDimensions( nCols, nRows );
+                if (nCols == 0 || nRows == 0)
+                {
+                    bError = true;
+                    break;  // switch
+                }
+                for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
+                {
+                    for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
+                    {
+                        sal_uInt16 nErr = pMat->GetError( nC, nR );
+                        if (nErr && (!bNAonly || nErr == NOTAVAILABLE))
+                        {
+                            bError = true;
+                            nErrorCol = nC;
+                            nErrorRow = nR;
+                        }
+                    }
+                }
+                if (!bError)
+                    break;  // switch, we're done and have the result
+
+                FormulaTokenRef xNew;
+                ScTokenMatrixMap::const_iterator aMapIter;
+                if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != 
pTokenMatrixMap->end()))
+                {
+                    xNew = (*aMapIter).second;
+                }
+                else
+                {
+                    const ScMatrix* pMatPtr = pMat.get();
+                    ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
+                    ScMatrix* pResMatPtr = pJumpMat->GetResultMatrix();
+                    // Init all jumps to no error to save single calls. Error
+                    // is the exceptional condition.
+                    const double fFlagResult = CreateDoubleError( errJumpMatHasResult);
+                    pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
+                    // Up to first error position simply store results, no need
+                    // to evaluate error conditions again.
+                    SCSIZE nC = 0, nR = 0;
+                    for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
+                    {
+                        for ( ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
+                        {
+                            lcl_storeJumpMatResult( pMatPtr, pResMatPtr, nC, nR);
+                        }
+                        if (nC != nErrorCol || nR != nErrorRow)
+                            ++nC;
+                    }
+                    // Now the mixed cases.
+                    for ( ; nC < nCols; ++nC)
+                    {
+                        for ( ; nR < nRows; ++nR)
+                        {
+                            sal_uInt16 nErr = pMat->GetError( nC, nR );
+                            if (nErr && (!bNAonly || nErr == NOTAVAILABLE))
+                            {   // TRUE, THEN path
+                                pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
+                            }
+                            else
+                            {   // FALSE, EMPTY path, store result instead
+                                lcl_storeJumpMatResult( pMatPtr, pResMatPtr, nC, nR);
+                            }
+                        }
+                    }
+                    xNew = new ScJumpMatrixToken( pJumpMat );
+                    GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( pCur, xNew ));
+                }
+                nGlobalError = nOldGlobalError;
+                PushTempToken( xNew.get() );
+                // set endpoint of path for main code line
+                aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+                return;
+            }
+            break;
+    }
+
+    if (bError && (!bNAonly || nGlobalError == NOTAVAILABLE))
+    {
+        // error, calculate 2nd argument
+        nGlobalError = 0;
+        aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
+    }
+    else
+    {
+        // no error, push 1st argument and continue
+        nGlobalError = nOldGlobalError;
+        PushTempToken( xToken.get());
+        aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+    }
+}
+
+
 void ScInterpreter::ScChoseJump()
 {
     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChoseJump" );
@@ -560,18 +743,7 @@ bool ScInterpreter::JumpMatrix( short nStackLevel )
                         }
                         else
                         {
-                            if ( pMat->IsValue( nC, nR ) )
-                            {
-                                fVal = pMat->GetDouble( nC, nR );
-                                pResMat->PutDouble( fVal, nC, nR );
-                            }
-                            else if ( pMat->IsEmpty( nC, nR ) )
-                                pResMat->PutEmpty( nC, nR );
-                            else
-                            {
-                                const String& rStr = pMat->GetString( nC, nR );
-                                pResMat->PutString( rStr, nC, nR );
-                            }
+                            lcl_storeJumpMatResult( pMat.get(), pResMat.get(), nC, nR);
                         }
                         lcl_AdjustJumpMatrix( pJumpMatrix, pResMat, nCols, nRows );
                     }
@@ -602,7 +774,7 @@ bool ScInterpreter::JumpMatrix( short nStackLevel )
         pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
         while ( bCont && nStart == nNext )
         {   // push all results that have no jump path
-            if ( pResMat )
+            if ( pResMat && (GetDoubleErrorValue( fBool) != errJumpMatHasResult) )
             {
                 // a false without path results in an empty path value
                 if ( fBool == 0.0 )
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index 7155445..2f3b5ac 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -2712,7 +2712,7 @@ void ScInterpreter::ScExternal()
     }
     else if ( ( aUnoName = ScGlobal::GetAddInCollection()->FindFunction(aFuncName, false) ).Len()  
)
     {
-        //  bLocalFirst=false in FindFunction, cFunc should be the stored 
+        //  bLocalFirst=false in FindFunction, cFunc should be the stored
         //  internal name
 
         ScUnoAddInCall aCall( *ScGlobal::GetAddInCollection(), aUnoName, nParamCount );
@@ -3815,7 +3815,8 @@ StackVar ScInterpreter::Interpret()
             // RPN code push without error
             PushWithoutError( (FormulaToken&) *pCur );
         }
-        else if (pTokenMatrixMap && !(eOp == ocIf || eOp == ocChose) &&
+        else if (pTokenMatrixMap &&
+                 !(eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose) &&
                 ((aTokenMatrixMapIter = pTokenMatrixMap->find( pCur)) !=
                  pTokenMatrixMap->end()) &&
                 (*aTokenMatrixMapIter).second->GetType() != svJumpMatrix)
@@ -3836,7 +3837,7 @@ StackVar ScInterpreter::Interpret()
             nFuncFmtType = NUMBERFORMAT_NUMBER;
             nFuncFmtIndex = 0;
 
-            if ( eOp == ocIf || eOp == ocChose )
+            if ( eOp == ocIf || eOp == ocChose || eOp == ocIfError || eOp == ocIfNA )
                 nStackBase = sp;        // don't mess around with the jumps
             else
             {
@@ -3863,6 +3864,8 @@ StackVar ScInterpreter::Interpret()
                 case ocDBArea           : ScDBArea();                   break;
                 case ocColRowNameAuto   : ScColRowNameAuto();           break;
                 case ocIf               : ScIfJump();                   break;
+                case ocIfError          : ScIfError( false );           break;
+                case ocIfNA             : ScIfError( true );            break;
                 case ocChose            : ScChoseJump();                break;
                 case ocAdd              : ScAdd();                      break;
                 case ocSub              : ScSub();                      break;
@@ -4258,7 +4261,9 @@ StackVar ScInterpreter::Interpret()
     case ocIsString : \
     case ocIsValue : \
     case ocN : \
-    case ocType :
+    case ocType : \
+    case ocIfError : \
+    case ocIfNA :
 
         switch ( eOp )
         {
diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx
index ebdbb4a..6f3cae07 100644
--- a/sc/source/core/tool/parclass.cxx
+++ b/sc/source/core/tool/parclass.cxx
@@ -55,6 +55,8 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
     // created inside those functions and ConvertMatrixParameters() is not
     // called for them.
     { ocIf,              {{ Array, Reference, Reference                          }, 0 }},
+    { ocIfError,         {{ Array, Reference                                     }, 0 }},
+    { ocIfNA,            {{ Array, Reference                                     }, 0 }},
     { ocChose,           {{ Array, Reference                                     }, 1 }},
     // Other specials.
     { ocOpen,            {{ Bounds                                               }, 0 }},
@@ -495,6 +497,8 @@ void ScParameterClassification::GenerateDocumentation()
                     case ocIf:
                         aToken.SetByte(3);
                     break;
+                    case ocIfError:
+                    case ocIfNA:
                     case ocChose:
                         aToken.SetByte(2);
                     break;
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 539cad0..4ea3d4e 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -129,6 +129,11 @@ void ScRawToken::SetOpCode( OpCode e )
             eType = svJump;
             nJump[ 0 ] = 3; // If, Else, Behind
             break;
+        case ocIfError:
+        case ocIfNA:
+            eType = svJump;
+            nJump[ 0 ] = 2; // If, Behind
+            break;
         case ocChose:
             eType = svJump;
             nJump[ 0 ] = MAXJUMPCOUNT+1;
diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx
index 58b3aa9..0cee777 100644
--- a/sc/source/filter/excel/xlformula.cxx
+++ b/sc/source/filter/excel/xlformula.cxx
@@ -365,7 +365,16 @@ static const XclFunctionInfo saFuncTable_Oox[] =
     { ocAverageIf,          NOID,   2,  3,  V, { RO, VR, RO }, EXC_FUNCFLAG_IMPORTONLY, 
EXC_FUNCNAME( "AVERAGEIF" ) },
     { ocAverageIf,          255,    3,  4,  V, { RO_E, RO, VR, RO }, EXC_FUNCFLAG_EXPORTONLY, 
EXC_FUNCNAME( "AVERAGEIF" ) },
     { ocAverageIfs,         NOID,   3,  MX, V, { RO, RO, VR }, 
EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
-    { ocAverageIfs,         255,    4,  MX, V, { RO_E, RO, RO, VR }, 
EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) }
+    { ocAverageIfs,         255,    4,  MX, V, { RO_E, RO, RO, VR }, 
EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
+    { ocIfError,            NOID,   2,  2,  V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( 
"IFERROR" ) },
+    { ocIfError,            255,    3,  3,  V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, 
EXC_FUNCNAME( "IFERROR" ) }
+};
+
+/** Functions new in Excel 2013. */
+static const XclFunctionInfo saFuncTable_2013[] =
+{
+    { ocIfNA,               NOID,   2,  2,  V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( 
"IFNA" ) },
+    { ocIfNA,               255,    3,  3,  V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, 
EXC_FUNCNAME( "IFNA" ) }
 };
 
 #define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \
@@ -393,7 +402,6 @@ static const XclFunctionInfo saFuncTable_Odf[] =
     EXC_FUNCENTRY_ODF( ocFormula,       1,  1,  0,  "FORMULA" ),
     EXC_FUNCENTRY_ODF( ocGamma,         1,  1,  0,  "GAMMA" ),
     EXC_FUNCENTRY_ODF( ocGauss,         1,  1,  0,  "GAUSS" ),
-    EXC_FUNCENTRY_ODF( ocNoName,        2,  2,  0,  "IFNA" ),
     EXC_FUNCENTRY_ODF( ocIsFormula,     1,  1,  0,  "ISFORMULA" ),
     EXC_FUNCENTRY_ODF( ocWeek,          1,  2,  0,  "ISOWEEKNUM" ),
     EXC_FUNCENTRY_ODF( ocMatrixUnit,    1,  1,  0,  "MUNIT" ),
@@ -434,6 +442,7 @@ XclFunctionProvider::XclFunctionProvider( const XclRoot& rRoot )
     if( eBiff >= EXC_BIFF8 )
         (this->*pFillFunc)( saFuncTable_8, STATIC_ARRAY_END( saFuncTable_8 ) );
     (this->*pFillFunc)( saFuncTable_Oox, STATIC_ARRAY_END( saFuncTable_Oox ) );
+    (this->*pFillFunc)( saFuncTable_2013, STATIC_ARRAY_END( saFuncTable_2013 ) );
     (this->*pFillFunc)( saFuncTable_Odf, STATIC_ARRAY_END( saFuncTable_Odf ) );
 }
 
diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx
index a6f7e453..90470ab 100644
--- a/sc/source/filter/oox/formulabase.cxx
+++ b/sc/source/filter/oox/formulabase.cxx
@@ -711,13 +711,20 @@ static const FunctionData saFuncTableOox[] =
     { 0,                        "CUBEKPIMEMBER",        477,    NOID,   3,  4,  V, { VR }, 0 },
     { 0,                        "CUBESET",              478,    NOID,   2,  5,  V, { VR, RX, VR }, 
0 },
     { 0,                        "CUBESETCOUNT",         479,    NOID,   1,  1,  V, { VR }, 0 },
-    { 0,                        "IFERROR",              480,    NOID,   2,  2,  V, { VO, RO }, 0 },
+    { "IFERROR",                "IFERROR",              480,    NOID,   2,  2,  V, { VO, RO }, 
FUNCFLAG_MACROCALL },
     { "COUNTIFS",               "COUNTIFS",             481,    NOID,   2,  MX, V, { RO, VR }, 
FUNCFLAG_MACROCALL | FUNCFLAG_PARAMPAIRS },
     { "SUMIFS",                 "SUMIFS",               482,    NOID,   3,  MX, V, { RO, RO, VR }, 
FUNCFLAG_MACROCALL | FUNCFLAG_PARAMPAIRS },
     { "AVERAGEIF",              "AVERAGEIF",            483,    NOID,   2,  3,  V, { RO, VR, RO }, 
FUNCFLAG_MACROCALL },
     { "AVERAGEIFS",             "AVERAGEIFS",           484,    NOID,   3,  MX, V, { RO, RO, VR }, 
FUNCFLAG_MACROCALL | FUNCFLAG_PARAMPAIRS }
 };
 
+/** Functions new in Excel 2013. */
+/* FIXME: BIFF12 function identifer available? Where to obtain? */
+static const FunctionData saFuncTable2013[] =
+{
+    { "IFNA",                   "IFNA",                 NOID,   NOID,   2,  2,  V, { VO, RO }, 
FUNCFLAG_MACROCALL },
+};
+
 /** Functions defined by OpenFormula, but not supported by Calc or by Excel. */
 static const FunctionData saFuncTableOdf[] =
 {
@@ -739,7 +746,6 @@ static const FunctionData saFuncTableOdf[] =
     { "FORMULA",                0,                      NOID,   NOID,   1,  1,  V, { RO }, 
FUNCFLAG_MACROCALLODF },
     { "GAMMA",                  0,                      NOID,   NOID,   1,  1,  V, { VR }, 
FUNCFLAG_MACROCALLODF },
     { "GAUSS",                  0,                      NOID,   NOID,   1,  1,  V, { VR }, 
FUNCFLAG_MACROCALLODF },
-    { "IFNA",                   0,                      NOID,   NOID,   2,  2,  V, { VR, RO }, 
FUNCFLAG_MACROCALLODF },
     { "ISFORMULA",              0,                      NOID,   NOID,   1,  1,  V, { RO }, 
FUNCFLAG_MACROCALLODF },
     { "ISOWEEKNUM",             0,                      NOID,   NOID,   1,  2,  V, { VR }, 
FUNCFLAG_MACROCALLODF },
     { "MUNIT",                  0,                      NOID,   NOID,   1,  1,  A, { VR }, 
FUNCFLAG_MACROCALLODF },
@@ -866,6 +872,7 @@ FunctionProviderImpl::FunctionProviderImpl( FilterType eFilter, BiffType eBiff,
     if( eBiff >= BIFF8 )
         initFuncs( saFuncTableBiff8, STATIC_ARRAY_END( saFuncTableBiff8 ), nMaxParam, 
bImportFilter );
     initFuncs( saFuncTableOox, STATIC_ARRAY_END( saFuncTableOox ), nMaxParam, bImportFilter );
+    initFuncs( saFuncTable2013, STATIC_ARRAY_END( saFuncTable2013 ), nMaxParam, bImportFilter );
     initFuncs( saFuncTableOdf, STATIC_ARRAY_END( saFuncTableOdf ), nMaxParam, bImportFilter );
 }
 
diff --git a/sc/source/ui/src/scfuncs.src b/sc/source/ui/src/scfuncs.src
index 1b68b2e..ce354e6 100644
--- a/sc/source/ui/src/scfuncs.src
+++ b/sc/source/ui/src/scfuncs.src
@@ -2651,6 +2651,70 @@ Resource RID_SC_FUNCTION_DESCRIPTIONS1
             Text [ en-US ] = "The result of the function if the logical test returns FALSE." ;
         };
     };
+     // -=*# Resource for function IFERROR #*=-
+    Resource SC_OPCODE_IF_ERROR
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Returns value if not an error value, else alternative." ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_LOGIC;
+            U2S( HID_FUNC_IFERROR );
+            2;  0;  0;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The value to be calculated." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "alternative value" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The alternative to be returned, should value be an error value." ;
+        };
+    };
+     // -=*# Resource for function IFNA #*=-
+    Resource SC_OPCODE_IF_NA
+    {
+        String 1 // Description
+        {
+            Text [ en-US ] = "Returns value if not a #N/A error, else alternative." ;
+        };
+        ExtraData =
+        {
+            0;
+            ID_FUNCTION_GRP_LOGIC;
+            U2S( HID_FUNC_IFNA );
+            2;  0;  0;
+            0;
+        };
+        String 2 // Name of Parameter 1
+        {
+            Text [ en-US ] = "value" ;
+        };
+        String 3 // Description of Parameter 1
+        {
+            Text [ en-US ] = "The value to be calculated." ;
+        };
+        String 4 // Name of Parameter 2
+        {
+            Text [ en-US ] = "alternative value" ;
+        };
+        String 5 // Description of Parameter 2
+        {
+            Text [ en-US ] = "The alternative to be returned, should value be a #N/A error." ;
+      };
+    };
      // -=*# Resource for function ODER #*=-
     Resource SC_OPCODE_OR
     {
diff --git a/sc/util/hidother.src b/sc/util/hidother.src
index d339229..718dce2 100644
--- a/sc/util/hidother.src
+++ b/sc/util/hidother.src
@@ -149,6 +149,8 @@ hidspecial HID_FUNC_FALSCH       { HelpID = HID_FUNC_FALSCH; };
 hidspecial HID_FUNC_NICHT        { HelpID = HID_FUNC_NICHT; };
 hidspecial HID_FUNC_WAHR         { HelpID = HID_FUNC_WAHR; };
 hidspecial HID_FUNC_WENN         { HelpID = HID_FUNC_WENN; };
+hidspecial HID_FUNC_IFERROR      { HelpID = HID_FUNC_IFERROR; };
+hidspecial HID_FUNC_IFNA         { HelpID = HID_FUNC_IFNA; };
 hidspecial HID_FUNC_ODER         { HelpID = HID_FUNC_ODER; };
 hidspecial HID_FUNC_UND      { HelpID = HID_FUNC_UND; };
 hidspecial HID_FUNC_XOR      { HelpID = HID_FUNC_XOR; };

--------------erAck-patch-parts--


Attachment: pgpcWe6oIatun.pgp
Description: PGP signature


Context


Privacy Policy | Impressum (Legal Info) | Copyright information: Unless otherwise specified, all text and images on this website are licensed under the Creative Commons Attribution-Share Alike 3.0 License. This does not include the source code of LibreOffice, which is licensed under the Mozilla Public License (MPLv2). "LibreOffice" and "The Document Foundation" are registered trademarks of their corresponding registered owners or are in actual use as trademarks in one or more countries. Their respective logos and icons are also subject to international copyright laws. Use thereof is explained in our trademark policy.