diff options
Diffstat (limited to 'sc/source/core/tool/compiler.cxx')
-rw-r--r-- | sc/source/core/tool/compiler.cxx | 142 |
1 files changed, 118 insertions, 24 deletions
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx index 7340de6f8a37..cc2ddfbc2dbd 100644 --- a/sc/source/core/tool/compiler.cxx +++ b/sc/source/core/tool/compiler.cxx @@ -644,7 +644,7 @@ static bool lcl_parseExternalName( if (aTmpName[nNameLen-1] == '!') { // Check against #REF!. - if (aTmpName == "#REF!") + if (aTmpName.equalsIgnoreAsciiCase("#REF!")) return false; } @@ -1920,6 +1920,28 @@ static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pS return pDst; } +// p1 MUST contain at least n characters, or terminate with NIL. +// p2 MUST pass upper case letters, if any. +// n MUST not be greater than length of p2 +static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode* p1, const char* p2, size_t n ) +{ + for (size_t i=0; i<n; ++i) + { + if (!p1[i]) + return false; + if (p1[i] != p2[i]) + { + if (p1[i] < 'a' || 'z' < p1[i]) + return false; // not a lower case letter + if (p2[i] < 'A' || 'Z' < p2[i]) + return false; // not a letter to match + if (p1[i] != p2[i] + 0x20) + return false; // lower case doesn't match either + } + } + return true; +} + // NextSymbol // Parses the formula into separate symbols for further processing. @@ -2142,6 +2164,33 @@ Label_MaskStateMachine: else *pSym++ = c; } + else if (c == '#' && lcl_isUnicodeIgnoreAscii( pSrc, "REF!", 4)) + { + // Completely ugly means to catch broken + // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts) + // references that were written in ODF named ranges + // (without embracing [] hence no predetected reference) + // and to OOXML and handle them as one symbol. + // Also catches these in UI, so we can process them + // further. + int i = 0; + for ( ; i<5; ++i) + { + if( pSym == &cSymbol[ MAXSTRLEN-1 ] ) + { + SetError(errStringOverflow); + eState = ssStop; + break; // for + } + else + { + *pSym++ = c; + c = *pSrc++; + } + } + if (i == 5) + c = *((--pSrc)-1); // position last/next character correctly + } else if (c == ':' && mnRangeOpPosInSymbol < 0) { // One range operator may form Sheet1.A:A, which we need to @@ -2282,8 +2331,18 @@ Label_MaskStateMachine: * as opcode symbols will be recognized and others result * in ocBad, so the result is actually conformant. */ bool bAdd = true; - if ('!' == c || '?' == c) + if ('?' == c) eState = ssStop; + else if ('!' == c) + { + // Check if this is #REF! that starts an invalid reference. + // Note we have an implicit '!' here at the end. + if (pSym - &cSymbol[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol, "#REF", 4) && + ((GetCharTableFlags( *pSrc, c) & SC_COMPILER_C_IDENT) != 0)) + eState = ssGetIdent; + else + eState = ssStop; + } else if ('/' == c) { if (!bErrorConstantHadSlash) @@ -2811,10 +2870,27 @@ bool ScCompiler::IsString() return false; } -bool ScCompiler::IsPredetectedReference(const OUString& rName) +bool ScCompiler::IsPredetectedErrRefReference( const OUString& rName, const OUString* pErrRef ) +{ + switch (mnPredetectedReference) + { + case 1: + return IsSingleReference( rName, pErrRef); + case 2: + return IsDoubleReference( rName, pErrRef); + default: + return false; + } +} + +bool ScCompiler::IsPredetectedReference( const OUString& rName ) { // Speedup documents with lots of broken references, e.g. sheet deleted. - sal_Int32 nPos = rName.indexOf("#REF!"); + // It could also be a broken invalidated reference that contains #REF! + // (but is not equal to), which we wrote prior to ODFF and also to ODFF + // between 2013 and 2016 until 5.1.4 + const OUString aErrRef("#REF!"); // not localized in ODFF + sal_Int32 nPos = rName.indexOf( aErrRef); if (nPos != -1) { /* TODO: this may be enhanced by reusing scan information from @@ -2830,13 +2906,17 @@ bool ScCompiler::IsPredetectedReference(const OUString& rName) // so pass it on. if (rName.getLength() == 5) return IsErrorConstant( rName); - return false; // #REF!.AB42 or #REF!42 or #REF!#REF! + // #REF!.AB42 or #REF!42 or #REF!#REF! + return IsPredetectedErrRefReference( rName, &aErrRef); } sal_Unicode c = rName[nPos-1]; // before #REF! if ('$' == c) { if (nPos == 1) - return false; // $#REF!.AB42 or $#REF!42 or $#REF!#REF! + { + // $#REF!.AB42 or $#REF!42 or $#REF!#REF! + return IsPredetectedErrRefReference( rName, &aErrRef); + } c = rName[nPos-2]; // before $#REF! } sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF! @@ -2844,18 +2924,27 @@ bool ScCompiler::IsPredetectedReference(const OUString& rName) { case '.': if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9')) - return false; // sheet.#REF!42 or sheet.#REF!#REF! + { + // sheet.#REF!42 or sheet.#REF!#REF! + return IsPredetectedErrRefReference( rName, &aErrRef); + } break; case ':': if (mnPredetectedReference > 1 && ('.' == c2 || '$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))) - return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF! + { + // :#REF!.AB42 or :#REF!42 or :#REF!#REF! + return IsPredetectedErrRefReference( rName, &aErrRef); + } break; default: if (rtl::isAsciiAlpha(c) && ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2)) - return false; // AB#REF!: or AB#REF! + { + // AB#REF!: or AB#REF! + return IsPredetectedErrRefReference( rName, &aErrRef); + } } } switch (mnPredetectedReference) @@ -2868,12 +2957,12 @@ bool ScCompiler::IsPredetectedReference(const OUString& rName) return false; } -bool ScCompiler::IsDoubleReference( const OUString& rName ) +bool ScCompiler::IsDoubleReference( const OUString& rName, const OUString* pErrRef ) { ScRange aRange( aPos, aPos ); const ScAddress::Details aDetails( pConv->meConv, aPos ); ScAddress::ExternalInfo aExtInfo; - ScRefFlags nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks ); + ScRefFlags nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks, pErrRef ); if( nFlags & ScRefFlags::VALID ) { ScComplexRefData aRef; @@ -2908,14 +2997,15 @@ bool ScCompiler::IsDoubleReference( const OUString& rName ) return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO; } -bool ScCompiler::IsSingleReference( const OUString& rName ) +bool ScCompiler::IsSingleReference( const OUString& rName, const OUString* pErrRef ) { mnCurrentSheetEndPos = 0; mnCurrentSheetTab = -1; ScAddress aAddr( aPos ); const ScAddress::Details aDetails( pConv->meConv, aPos ); ScAddress::ExternalInfo aExtInfo; - ScRefFlags nFlags = aAddr.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos); + ScRefFlags nFlags = aAddr.Parse( rName, pDoc, aDetails, + &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos, pErrRef); // Something must be valid in order to recognize Sheet1.blah or blah.a1 // as a (wrong) reference. if( nFlags & ( ScRefFlags::COL_VALID|ScRefFlags::ROW_VALID|ScRefFlags::TAB_VALID ) ) @@ -2972,7 +3062,7 @@ bool ScCompiler::IsSingleReference( const OUString& rName ) return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO; } -bool ScCompiler::IsReference( const OUString& rName ) +bool ScCompiler::IsReference( const OUString& rName, const OUString* pErrRef ) { // Has to be called before IsValue sal_Unicode ch1 = rName[0]; @@ -3023,7 +3113,7 @@ bool ScCompiler::IsReference( const OUString& rName ) } while(false); } - if (IsSingleReference( rName)) + if (IsSingleReference( rName, pErrRef)) return true; // Though the range operator is handled explicitly, when encountering @@ -3031,7 +3121,7 @@ bool ScCompiler::IsReference( const OUString& rName ) // doesn't pass as single cell reference. if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense { - if (IsDoubleReference( rName)) + if (IsDoubleReference( rName, pErrRef)) return true; // Now try with a symbol up to the range operator, rewind source // position. @@ -3058,7 +3148,7 @@ bool ScCompiler::IsReference( const OUString& rName ) SAL_FALLTHROUGH; case FormulaGrammar::CONV_XL_R1C1: // C2 or C[1] are valid entire column references. - if (IsDoubleReference( rName)) + if (IsDoubleReference( rName, pErrRef)) return true; break; default: @@ -3887,12 +3977,6 @@ bool ScCompiler::NextNewToken( bool bInArray ) bool bInvalidExternalNameRange; if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr, bInvalidExternalNameRange )) { - /* TODO: it would be nice to generate a #REF! error here, which - * would need an ocBad token with additional error value. - * FormulaErrorToken wouldn't do because we want to preserve the - * original string containing partial valid address - * information if not ODFF (in that case it was already handled). - * */ svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aStr); maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase()); maRawToken.NewOpCode( ocBad ); @@ -3986,10 +4070,13 @@ bool ScCompiler::NextNewToken( bool bInArray ) return true; } - // This can be only an error constant, if any. + // This can be either an error constant ... if (IsErrorConstant( aUpper)) return true; + // ... or some invalidated reference starting with #REF! + // which is handled after the do loop. + break; // do; create ocBad token or set error. } if (IsOpCode( aUpper, bInArray )) @@ -4066,6 +4153,13 @@ bool ScCompiler::NextNewToken( bool bInArray ) } while (mbRewind); + // Last chance: it could be a broken invalidated reference that contains + // #REF! (but is not equal to), which we also wrote to ODFF between 2013 + // and 2016 until 5.1.4 + OUString aErrRef( mxSymbols->getSymbol( ocErrRef)); + if (aUpper.indexOf( aErrRef) >= 0 && IsReference( aUpper, &aErrRef)) + return true; + if ( meExtendedErrorDetection != EXTENDED_ERROR_DETECTION_NONE ) { // set an error |