[PATCH 4-0 4-0-0] Late Feature: IFERROR and IFNA spreadsheet functions

Eike Rathke erack at redhat.com
Sat Jan 12 10:13:50 PST 2013


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
-------------- next part --------------
From cc101e678799c0f437dc02b6930fe858c560972d Mon Sep 17 00:00:00 2001
Message-Id: <cc101e678799c0f437dc02b6930fe858c560972d.1358013933.git.erack at redhat.com>
From: Eike Rathke <erack at 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--


-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/libreoffice/attachments/20130112/8b9c1bee/attachment-0001.pgp>


More information about the LibreOffice mailing list