LibreOffice Module sc (master)  1
compiler.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <compiler.hxx>
23 
24 #include <vcl/svapp.hxx>
25 #include <vcl/settings.hxx>
26 #include <sfx2/app.hxx>
27 #include <sfx2/objsh.hxx>
28 #include <basic/sbmeth.hxx>
29 #include <basic/sbstar.hxx>
30 #include <svl/numformat.hxx>
31 #include <svl/zforlist.hxx>
32 #include <svl/sharedstringpool.hxx>
33 #include <sal/log.hxx>
34 #include <o3tl/safeint.hxx>
35 #include <osl/diagnose.h>
36 #include <rtl/character.hxx>
37 #include <unotools/charclass.hxx>
38 #include <unotools/configmgr.hxx>
39 #include <com/sun/star/lang/Locale.hpp>
40 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
41 #include <com/sun/star/sheet/FormulaLanguage.hpp>
42 #include <com/sun/star/i18n/KParseTokens.hpp>
43 #include <com/sun/star/i18n/KParseType.hpp>
45 #include <comphelper/string.hxx>
47 #include <tools/urlobj.hxx>
48 #include <rtl/math.hxx>
49 #include <rtl/ustring.hxx>
50 #include <stdlib.h>
51 #include <math.h>
52 #include <rangenam.hxx>
53 #include <dbdata.hxx>
54 #include <document.hxx>
55 #include <callform.hxx>
56 #include <addincol.hxx>
57 #include <refupdat.hxx>
58 #include <globstr.hrc>
59 #include <scresid.hxx>
60 #include <formulacell.hxx>
61 #include <dociter.hxx>
62 #include <docoptio.hxx>
63 #include <formula/errorcodes.hxx>
64 #include <parclass.hxx>
65 #include <autonamecache.hxx>
66 #include <externalrefmgr.hxx>
67 #include <rangeutl.hxx>
68 #include <convuno.hxx>
69 #include <tokenuno.hxx>
70 #include <formulaparserpool.hxx>
71 #include <tokenarray.hxx>
72 #include <scmatrix.hxx>
73 #include <tokenstringcontext.hxx>
74 #include <officecfg/Office/Common.hxx>
75 
76 using namespace formula;
77 using namespace ::com::sun::star;
78 using ::std::vector;
79 
82 const ScCompiler::Convention* ScCompiler::pConventions[ ] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
83 
84 namespace {
85 
87 {
88  ssGetChar,
89  ssGetBool,
90  ssGetValue,
91  ssGetString,
92  ssSkipString,
93  ssGetIdent,
94  ssGetReference,
95  ssSkipReference,
96  ssGetErrorConstant,
97  ssGetTableRefItem,
98  ssGetTableRefColumn,
99  ssStop
100 };
101 
102 }
103 
104 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
105 
106 using namespace ::com::sun::star::i18n;
107 
109 {
110  size_t nSymbolOffset;
111  switch( _eGrammar )
112  {
114  nSymbolOffset = offsetof( AddInMap, pUpper);
115  break;
116  default:
118  nSymbolOffset = offsetof( AddInMap, pODFF);
119  break;
121  nSymbolOffset = offsetof( AddInMap, pEnglish);
122  break;
123  }
124  const AddInMap* pMap = g_aAddInMap;
125  const AddInMap* const pStop = pMap + GetAddInMapCount();
126  for ( ; pMap < pStop; ++pMap)
127  {
128  char const * const * ppSymbol =
129  reinterpret_cast< char const * const * >(
130  reinterpret_cast< char const * >(pMap) + nSymbolOffset);
131  xMap->putExternal( OUString::createFromAscii( *ppSymbol),
132  OUString::createFromAscii( pMap->pOriginal));
133  }
134 }
135 
137 {
139  tools::Long nCount = pColl->GetFuncCount();
140  for (tools::Long i=0; i < nCount; ++i)
141  {
142  const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
143  if (pFuncData)
144  xMap->putExternalSoftly( pFuncData->GetUpperName(),
145  pFuncData->GetOriginalName());
146  }
147 }
148 
150 {
152  tools::Long nCount = pColl->GetFuncCount();
153  for (tools::Long i=0; i < nCount; ++i)
154  {
155  const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
156  if (pFuncData)
157  {
158  OUString aName;
159  if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
160  xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
161  else
162  xMap->putExternalSoftly( pFuncData->GetUpperName(),
163  pFuncData->GetOriginalName());
164  }
165  }
166 }
167 
169 {
170  if (pCharClassEnglish)
171  {
172  delete pCharClassEnglish;
173  pCharClassEnglish = nullptr;
174  }
175  if (pCharClassLocalized)
176  {
177  delete pCharClassLocalized;
178  pCharClassLocalized = nullptr;
179  }
180 }
181 
182 bool ScCompiler::IsEnglishSymbol( const OUString& rName )
183 {
184  // function names are always case-insensitive
185  OUString aUpper = GetCharClassEnglish()->uppercase(rName);
186 
187  // 1. built-in function name
188  formula::FormulaCompiler aCompiler;
189  OpCode eOp = aCompiler.GetEnglishOpCode( aUpper );
190  if ( eOp != ocNone )
191  {
192  return true;
193  }
194  // 2. old add in functions
195  if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper))
196  {
197  return true;
198  }
199 
200  // 3. new (uno) add in functions
201  OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
202  return !aIntName.isEmpty(); // no valid function name
203 }
204 
206 {
207  if (!pCharClassEnglish)
208  {
209  css::lang::Locale aLocale( "en", "US", "");
210  pCharClassEnglish = new CharClass(
212  }
213  return pCharClassEnglish;
214 }
215 
217 {
218  if (!pCharClassLocalized)
219  {
220  // Switching UI language requires restart; if not, we would have to
221  // keep track of that.
222  pCharClassLocalized = new CharClass(
224  }
225  return pCharClassLocalized;
226 }
227 
229 {
230  assert( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED && "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
231  if (eGrammar == GetGrammar())
232  return; // nothing to be done
233 
234  if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
235  {
236  meGrammar = eGrammar;
237  mxSymbols = GetOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
238  }
239  else
240  {
241  FormulaGrammar::Grammar eMyGrammar = eGrammar;
242  const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
243  OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage);
244  OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language");
245  if (!xMap)
246  {
247  xMap = GetOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
248  eMyGrammar = xMap->getGrammar();
249  }
250 
251  // Save old grammar for call to SetGrammarAndRefConvention().
252  FormulaGrammar::Grammar eOldGrammar = GetGrammar();
253  // This also sets the grammar associated with the map!
254  SetFormulaLanguage( xMap);
255 
256  // Override if necessary.
257  if (eMyGrammar != GetGrammar())
258  SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
259  }
260 }
261 
262 // Unclear how this was intended to be refreshed when the
263 // grammar or sheet count is changed ? Ideally this would be
264 // a method on Document that would globally cache these.
265 std::vector<OUString> &ScCompiler::GetSetupTabNames() const
266 {
267  std::vector<OUString> &rTabNames = const_cast<ScCompiler *>(this)->maTabNames;
268 
269  if (rTabNames.empty())
270  {
271  rTabNames = rDoc.GetAllTableNames();
272  for (auto& rTabName : rTabNames)
274  }
275 
276  return rTabNames;
277 }
278 
280 {
281  mpFormatter = pFormatter;
282 }
283 
285 {
286  if (!xMap)
287  return;
288 
289  mxSymbols = xMap;
290  if (mxSymbols->isEnglish())
291  pCharClass = GetCharClassEnglish();
292  else
293  pCharClass = GetCharClassLocalized();
294 
295  // The difference is needed for an uppercase() call that usually does not
296  // result in different strings but for a few languages like Turkish;
297  // though even de-DE and de-CH may differ in ß/SS handling..
298  // At least don't care if both are English.
299  // The current locale is more likely to not be "en" so check first.
301  const LanguageTag& rLT2 = pCharClass->getLanguageTag();
302  mbCharClassesDiffer = (rLT1 != rLT2 && (rLT1.getLanguage() != "en" || rLT2.getLanguage() != "en"));
303 
304  SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
305 }
306 
308  const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
309 {
310  meGrammar = eNewGrammar; // SetRefConvention needs the new grammar set!
313  SetRefConvention( rDoc.GetAddressConvention());
314  else
315  SetRefConvention( eConv );
316 }
317 
318 OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const
319 {
320  return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=false for english
321 }
322 
324 {
325 }
326 
328  :
329  meConv( eConv )
330 {
331  int i;
332  ScCharFlags *t= new ScCharFlags [128];
333 
334  ScCompiler::pConventions[ meConv ] = this;
335  mpCharTable.reset( t );
336 
337  for (i = 0; i < 128; i++)
338  t[i] = ScCharFlags::Illegal;
339 
340 // Allow tabs/newlines.
341 // Allow saving whitespace as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace").
345 
348  if (FormulaGrammar::CONV_ODF == meConv)
349 /* ! */ t[33] |= ScCharFlags::OdfLabelOp;
353  if (FormulaGrammar::CONV_ODF == meConv)
354 /* $ */ t[36] |= ScCharFlags::OdfNameMarker;
357 /* ' */ t[39] = ScCharFlags::NameSep;
366 
367  for (i = 48; i < 58; i++)
369 
370 /* : */ t[58] = ScCharFlags::Char | ScCharFlags::Word;
376 /* @ */ // FREE
377 
378  for (i = 65; i < 91; i++)
380 
381  if (FormulaGrammar::CONV_ODF == meConv)
382  {
383 /* [ */ t[91] = ScCharFlags::OdfLBracket;
384 /* \ */ // FREE
385 /* ] */ t[93] = ScCharFlags::OdfRBracket;
386  }
387  else if (FormulaGrammar::CONV_OOO == meConv)
388  {
389 /* [ */ t[91] = ScCharFlags::Char;
390 /* \ */ // FREE
391 /* ] */ t[93] = ScCharFlags::Char;
392  }
393  else if (FormulaGrammar::CONV_XL_OOX == meConv)
394  {
395 /* [ */ t[91] = ScCharFlags::Char | ScCharFlags::CharIdent;
396 /* \ */ // FREE
397 /* ] */ t[93] = ScCharFlags::Char | ScCharFlags::Ident;
398  }
399  else if (FormulaGrammar::CONV_XL_A1 == meConv)
400  {
401 /* [ */ t[91] = ScCharFlags::Char;
402 /* \ */ // FREE
403 /* ] */ t[93] = ScCharFlags::Char;
404  }
405  else if( FormulaGrammar::CONV_XL_R1C1 == meConv )
406  {
407 /* [ */ t[91] = ScCharFlags::Ident;
408 /* \ */ // FREE
409 /* ] */ t[93] = ScCharFlags::Ident;
410  }
411  else
412  {
413 /* [ */ // FREE
414 /* \ */ // FREE
415 /* ] */ // FREE
416  }
417 
420 /* ` */ // FREE
421 
422  for (i = 97; i < 123; i++)
424 
425 /* { */ t[123] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array open
426 /* | */ t[124] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array row sep (Should be OOo specific)
427 /* } */ t[125] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array close
428 /* ~ */ t[126] = ScCharFlags::Char; // OOo specific
429 /* 127 */ // FREE
430 
431  if( !(FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv) )
432 return;
433 
434 /* */ t[32] |= ScCharFlags::Word;
435 /* ! */ t[33] |= ScCharFlags::Ident | ScCharFlags::Word;
436 /* " */ t[34] |= ScCharFlags::Word;
437 /* # */ t[35] &= ~ScCharFlags::WordSep;
438 /* # */ t[35] |= ScCharFlags::Word;
439 /* % */ t[37] |= ScCharFlags::Word;
440 /* ' */ t[39] |= ScCharFlags::Word;
441 
442 /* % */ t[37] |= ScCharFlags::Word;
443 /* & */ t[38] |= ScCharFlags::Word;
444 /* ' */ t[39] |= ScCharFlags::Word;
445 /* ( */ t[40] |= ScCharFlags::Word;
446 /* ) */ t[41] |= ScCharFlags::Word;
447 /* * */ t[42] |= ScCharFlags::Word;
448 /* + */ t[43] |= ScCharFlags::Word;
449 #if 0 /* this really needs to be locale specific. */
451 #else
452 /* , */ t[44] |= ScCharFlags::Word;
453 #endif
454 /* - */ t[45] |= ScCharFlags::Word;
455 
456 /* ; */ t[59] |= ScCharFlags::Word;
457 /* < */ t[60] |= ScCharFlags::Word;
458 /* = */ t[61] |= ScCharFlags::Word;
459 /* > */ t[62] |= ScCharFlags::Word;
460 /* ? */ // question really is not permitted in sheet name
461 /* @ */ t[64] |= ScCharFlags::Word;
462 /* [ */ t[91] |= ScCharFlags::Word;
463 /* ] */ t[93] |= ScCharFlags::Word;
464 /* { */ t[123]|= ScCharFlags::Word;
465 /* | */ t[124]|= ScCharFlags::Word;
466 /* } */ t[125]|= ScCharFlags::Word;
467 /* ~ */ t[126]|= ScCharFlags::Word;
468 }
469 
470 static bool lcl_isValidQuotedText( const OUString& rFormula, sal_Int32 nSrcPos, ParseResult& rRes )
471 {
472  // Tokens that start at ' can have anything in them until a final '
473  // but '' marks an escaped '
474  // We've earlier guaranteed that a string containing '' will be
475  // surrounded by '
476  if (nSrcPos < rFormula.getLength() && rFormula[nSrcPos] == '\'')
477  {
478  sal_Int32 nPos = nSrcPos+1;
479  while (nPos < rFormula.getLength())
480  {
481  if (rFormula[nPos] == '\'')
482  {
483  if ( (nPos+1 == rFormula.getLength()) || (rFormula[nPos+1] != '\'') )
484  {
485  rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
486  rRes.EndPos = nPos+1;
487  return true;
488  }
489  ++nPos;
490  }
491  ++nPos;
492  }
493  }
494 
495  return false;
496 }
497 
499  const OUString& rSymbol,
500  OUString& rFile,
501  OUString& rName,
502  const sal_Unicode cSep,
503  const ScDocument& rDoc,
504  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
505 {
506  /* TODO: future versions will have to support sheet-local names too, thus
507  * return a possible sheet name as well. */
508  const sal_Unicode* const pStart = rSymbol.getStr();
509  const sal_Unicode* p = pStart;
510  sal_Int32 nLen = rSymbol.getLength();
511  OUString aTmpFile;
512  OUStringBuffer aTmpName;
513  sal_Int32 i = 0;
514  bool bInName = false;
515  if (cSep == '!')
516  {
517  // For XL use existing parser that resolves bracketed and quoted and
518  // indexed external document names.
519  ScRange aRange;
520  OUString aStartTabName, aEndTabName;
521  ScRefFlags nFlags = ScRefFlags::ZERO;
522  p = aRange.Parse_XL_Header( p, rDoc, aTmpFile, aStartTabName,
523  aEndTabName, nFlags, true, pExternalLinks );
524  if (!p || p == pStart)
525  return false;
526  i = sal_Int32(p - pStart);
527  }
528  for ( ; i < nLen; ++i, ++p)
529  {
530  sal_Unicode c = *p;
531  if (i == 0)
532  {
533  if (c == '.' || c == cSep)
534  return false;
535 
536  if (c == '\'')
537  {
538  // Move to the next char and loop until the second single
539  // quote.
540  sal_Unicode cPrev = c;
541  ++i; ++p;
542  for (sal_Int32 j = i; j < nLen; ++j, ++p)
543  {
544  c = *p;
545  if (c == '\'')
546  {
547  if (j == i)
548  {
549  // empty quote e.g. (=''!Name)
550  return false;
551  }
552 
553  if (cPrev == '\'')
554  {
555  // two consecutive quotes equal a single quote in
556  // the file name.
557  aTmpFile += OUStringChar(c);
558  cPrev = 'a';
559  }
560  else
561  cPrev = c;
562 
563  continue;
564  }
565 
566  if (cPrev == '\'' && j != i)
567  {
568  // this is not a quote but the previous one is. This
569  // ends the parsing of the quoted segment. At this
570  // point, the current char must equal the separator
571  // char.
572 
573  i = j;
574  bInName = true;
575  aTmpName.append(c); // Keep the separator as part of the name.
576  break;
577  }
578  aTmpFile += OUStringChar(c);
579  cPrev = c;
580  }
581 
582  if (!bInName)
583  {
584  // premature ending of the quoted segment.
585  return false;
586  }
587 
588  if (c != cSep)
589  {
590  // only the separator is allowed after the closing quote.
591  return false;
592  }
593 
594  continue;
595  }
596  }
597 
598  if (bInName)
599  {
600  if (c == cSep)
601  {
602  // A second separator ? Not a valid external name.
603  return false;
604  }
605  aTmpName.append(c);
606  }
607  else
608  {
609  if (c == cSep)
610  {
611  bInName = true;
612  aTmpName.append(c); // Keep the separator as part of the name.
613  }
614  else
615  {
616  do
617  {
618  if (rtl::isAsciiAlphanumeric(c))
619  // allowed.
620  break;
621 
622  if (c > 128)
623  // non-ASCII character is allowed.
624  break;
625 
626  bool bValid = false;
627  switch (c)
628  {
629  case '_':
630  case '-':
631  case '.':
632  // these special characters are allowed.
633  bValid = true;
634  break;
635  }
636  if (bValid)
637  break;
638 
639  return false;
640  }
641  while (false);
642  aTmpFile += OUStringChar(c);
643  }
644  }
645  }
646 
647  if (!bInName)
648  {
649  // No name found - most likely the symbol has no '!'s.
650  return false;
651  }
652 
653  sal_Int32 nNameLen = aTmpName.getLength();
654  if (nNameLen < 2)
655  {
656  // Name must be at least 2-char long (separator plus name).
657  return false;
658  }
659 
660  if (aTmpName[0] != cSep)
661  {
662  // 1st char of the name must equal the separator.
663  return false;
664  }
665 
666  if (aTmpName[nNameLen-1] == '!')
667  {
668  // Check against #REF!.
669  if (aTmpName.toString().equalsIgnoreAsciiCase("#REF!"))
670  return false;
671  }
672 
673  rFile = aTmpFile;
674  rName = aTmpName.makeStringAndClear().copy(1); // Skip the first char as it is always the separator.
675  return true;
676 }
677 
678 static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName,
679  const sal_Unicode cSep, bool bODF )
680 {
681  OUString aEscQuote("''");
682  OUString aFile(rFile.replaceAll("'", aEscQuote));
683  OUString aName(rName);
684  if (bODF)
685  aName = aName.replaceAll("'", aEscQuote);
686  OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9);
687  if (bODF)
688  aBuf.append( '[');
689  aBuf.append( "'" + aFile + "'" + OUStringChar(cSep) );
690  if (bODF)
691  aBuf.append( "$$'" );
692  aBuf.append( aName);
693  if (bODF)
694  aBuf.append( "']" );
695  return aBuf.makeStringAndClear();
696 }
697 
698 static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1,
699  const vector<OUString>& rTabNames, const ScRange& rRef )
700 {
701  SCTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab();
702  if (nTabSpan > 0)
703  {
704  size_t nCount = rTabNames.size();
705  vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
706  vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
707  if (itr == rTabNames.end())
708  {
709  rTabName2 = ScResId(STR_NO_REF_TABLE);
710  return false;
711  }
712 
713  size_t nDist = ::std::distance(itrBeg, itr);
714  if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
715  {
716  rTabName2 = ScResId(STR_NO_REF_TABLE);
717  return false;
718  }
719 
720  rTabName2 = rTabNames[nDist+nTabSpan];
721  }
722  else
723  rTabName2 = rTabName1;
724 
725  return true;
726 }
727 
728 namespace {
729 
730 struct Convention_A1 : public ScCompiler::Convention
731 {
732  explicit Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
733  static void MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol );
734  static void MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow );
735 
736  ParseResult parseAnyToken( const OUString& rFormula,
737  sal_Int32 nSrcPos,
738  const CharClass* pCharClass,
739  bool bGroupSeparator) const override
740  {
741  ParseResult aRet;
742  if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
743  return aRet;
744 
745  constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
746  KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
747  constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
748  // '?' allowed in range names because of Xcl :-/
749  static constexpr OUStringLiteral aAddAllowed(u"?#");
750  return pCharClass->parseAnyToken( rFormula,
751  nSrcPos, nStartFlags, aAddAllowed,
752  (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
753  aAddAllowed );
754  }
755 
756  virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const override
757  {
758  return mpCharTable[static_cast<sal_uInt8>(c)];
759  }
760 };
761 
762 }
763 
764 void Convention_A1::MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol )
765 {
766  if ( !rLimits.ValidCol(nCol) )
767  rBuffer.append(ScResId(STR_NO_REF_TABLE));
768  else
769  ::ScColToAlpha( rBuffer, nCol);
770 }
771 
772 void Convention_A1::MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow )
773 {
774  if ( !rLimits.ValidRow(nRow) )
775  rBuffer.append(ScResId(STR_NO_REF_TABLE));
776  else
777  rBuffer.append(sal_Int32(nRow + 1));
778 }
779 
780 namespace {
781 
782 struct ConventionOOO_A1 : public Convention_A1
783 {
784  ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
785  explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
786 
787  static void MakeTabStr( OUStringBuffer &rBuf, const std::vector<OUString>& rTabNames, SCTAB nTab )
788  {
789  if (o3tl::make_unsigned(nTab) >= rTabNames.size())
790  rBuf.append(ScResId(STR_NO_REF_TABLE));
791  else
792  rBuf.append(rTabNames[nTab]);
793  rBuf.append('.');
794  }
795 
796  enum SingletonDisplay
797  {
798  SINGLETON_NONE,
799  SINGLETON_COL,
800  SINGLETON_ROW
801  };
802 
803  static void MakeOneRefStrImpl(
804  const ScSheetLimits& rLimits, OUStringBuffer& rBuffer,
805  std::u16string_view rErrRef, const std::vector<OUString>& rTabNames,
806  const ScSingleRefData& rRef, const ScAddress& rAbsRef,
807  bool bForceTab, bool bODF, SingletonDisplay eSingletonDisplay )
808  {
809  // For ODF override singleton so earlier releases still can read what
810  // we write now as of 2015-06-26.
811  /* TODO: we may want to change that in future in a few releases. */
812  if (bODF)
813  eSingletonDisplay = SINGLETON_NONE;
814 
815  if( rRef.IsFlag3D() || bForceTab )
816  {
817  if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted())
818  {
819  if (!rRef.IsTabRel())
820  rBuffer.append('$');
821  rBuffer.append(rErrRef);
822  rBuffer.append('.');
823  }
824  else
825  {
826  if (!rRef.IsTabRel())
827  rBuffer.append('$');
828  MakeTabStr(rBuffer, rTabNames, rAbsRef.Tab());
829  }
830  }
831  else if (bODF)
832  rBuffer.append('.');
833 
834  if (eSingletonDisplay != SINGLETON_ROW)
835  {
836  if (!rRef.IsColRel())
837  rBuffer.append('$');
838  if (!rLimits.ValidCol(rAbsRef.Col()) || rRef.IsColDeleted())
839  rBuffer.append(rErrRef);
840  else
841  MakeColStr(rLimits, rBuffer, rAbsRef.Col());
842  }
843 
844  if (eSingletonDisplay != SINGLETON_COL)
845  {
846  if (!rRef.IsRowRel())
847  rBuffer.append('$');
848  if (!rLimits.ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted())
849  rBuffer.append(rErrRef);
850  else
851  MakeRowStr(rLimits, rBuffer, rAbsRef.Row());
852  }
853  }
854 
855  static SingletonDisplay getSingletonDisplay( const ScSheetLimits& rLimits, const ScAddress& rAbs1, const ScAddress& rAbs2,
856  const ScComplexRefData& rRef, bool bFromRangeName )
857  {
858  // If any part is error, display as such.
859  if (!rLimits.ValidCol(rAbs1.Col()) || rRef.Ref1.IsColDeleted() || !rLimits.ValidRow(rAbs1.Row()) || rRef.Ref1.IsRowDeleted() ||
860  !rLimits.ValidCol(rAbs2.Col()) || rRef.Ref2.IsColDeleted() || !rLimits.ValidRow(rAbs2.Row()) || rRef.Ref2.IsRowDeleted())
861  return SINGLETON_NONE;
862 
863  // A:A or $A:$A or A:$A or $A:A
864  if (rRef.IsEntireCol())
865  return SINGLETON_COL;
866 
867  // Same if not in named expression and both rows of entire columns are
868  // relative references.
869  if (!bFromRangeName && rAbs1.Row() == 0 && rAbs2.Row() == rLimits.mnMaxRow &&
870  rRef.Ref1.IsRowRel() && rRef.Ref2.IsRowRel())
871  return SINGLETON_COL;
872 
873  // 1:1 or $1:$1 or 1:$1 or $1:1
874  if (rRef.IsEntireRow())
875  return SINGLETON_ROW;
876 
877  // Same if not in named expression and both columns of entire rows are
878  // relative references.
879  if (!bFromRangeName && rAbs1.Col() == 0 && rAbs2.Col() == rLimits.mnMaxCol &&
880  rRef.Ref1.IsColRel() && rRef.Ref2.IsColRel())
881  return SINGLETON_ROW;
882 
883  return SINGLETON_NONE;
884  }
885 
886  virtual void makeRefStr(
887  ScSheetLimits& rLimits,
888  OUStringBuffer& rBuffer,
890  const ScAddress& rPos,
891  const OUString& rErrRef, const std::vector<OUString>& rTabNames,
892  const ScComplexRefData& rRef,
893  bool bSingleRef,
894  bool bFromRangeName ) const override
895  {
896  // In case absolute/relative positions weren't separately available:
897  // transform relative to absolute!
898  ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
899  if( !bSingleRef )
900  aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
901 
902  SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
903  getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
904  MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, false, eSingleton);
905  if (!bSingleRef)
906  {
907  rBuffer.append(':');
908  MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false,
909  eSingleton);
910  }
911  }
912 
913  virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
914  {
915  switch (eSymType)
916  {
918  return '$';
920  return '.';
921  }
922 
923  return u'\0';
924  }
925 
926  virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
927  const ScDocument& rDoc,
928  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
929  {
930  return lcl_parseExternalName(rSymbol, rFile, rName, '#', rDoc, pExternalLinks);
931  }
932 
933  virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
934  const OUString& rName ) const override
935  {
936  return lcl_makeExternalNameStr( rFile, rName, '#', false);
937  }
938 
939  static bool makeExternalSingleRefStr(
940  const ScSheetLimits& rLimits,
941  OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName,
942  const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl )
943  {
944  ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
945  if (bDisplayTabName)
946  {
947  OUString aFile;
948  if (bEncodeUrl)
949  aFile = rFileName;
950  else
952 
953  rBuffer.append("'" + aFile.replaceAll("'", "''") + "'#");
954 
955  if (!rRef.IsTabRel())
956  rBuffer.append('$');
957  ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
958 
959  rBuffer.append('.');
960  }
961 
962  if (!rRef.IsColRel())
963  rBuffer.append('$');
964  MakeColStr( rLimits, rBuffer, aAbsRef.Col());
965  if (!rRef.IsRowRel())
966  rBuffer.append('$');
967  MakeRowStr( rLimits, rBuffer, aAbsRef.Row());
968 
969  return true;
970  }
971 
972  static void makeExternalRefStrImpl(
973  const ScSheetLimits& rLimits,
974  OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
975  const OUString& rTabName, const ScSingleRefData& rRef, bool bODF )
976  {
977  if (bODF)
978  rBuffer.append( '[');
979 
980  bool bEncodeUrl = bODF;
981  makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl);
982  if (bODF)
983  rBuffer.append( ']');
984  }
985 
986  virtual void makeExternalRefStr(
987  ScSheetLimits& rLimits,
988  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
989  const OUString& rTabName, const ScSingleRefData& rRef ) const override
990  {
991  makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, false);
992  }
993 
994  static void makeExternalRefStrImpl(
995  const ScSheetLimits& rLimits,
996  OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
997  const std::vector<OUString>& rTabNames, const OUString& rTabName,
998  const ScComplexRefData& rRef, bool bODF )
999  {
1000  ScRange aAbsRange = rRef.toAbs(rLimits, rPos);
1001 
1002  if (bODF)
1003  rBuffer.append( '[');
1004  // Ensure that there's always a closing bracket, no premature returns.
1005  bool bEncodeUrl = bODF;
1006 
1007  do
1008  {
1009  if (!makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl))
1010  break;
1011 
1012  rBuffer.append(':');
1013 
1014  OUString aLastTabName;
1015  bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab());
1016  if (bDisplayTabName)
1017  {
1018  // Get the name of the last table.
1019  if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange))
1020  {
1021  OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
1022  // aLastTabName contains #REF!, proceed.
1023  }
1024  }
1025  else if (bODF)
1026  rBuffer.append( '.'); // need at least the sheet separator in ODF
1027  makeExternalSingleRefStr(rLimits,
1028  rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl);
1029  } while (false);
1030 
1031  if (bODF)
1032  rBuffer.append( ']');
1033  }
1034 
1035  virtual void makeExternalRefStr(
1036  ScSheetLimits& rLimits,
1037  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1038  const std::vector<OUString>& rTabNames, const OUString& rTabName,
1039  const ScComplexRefData& rRef ) const override
1040  {
1041  makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false);
1042  }
1043 };
1044 
1045 struct ConventionOOO_A1_ODF : public ConventionOOO_A1
1046 {
1047  ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
1048 
1049  virtual void makeRefStr(
1050  ScSheetLimits& rLimits,
1051  OUStringBuffer& rBuffer,
1053  const ScAddress& rPos,
1054  const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1055  const ScComplexRefData& rRef,
1056  bool bSingleRef,
1057  bool bFromRangeName ) const override
1058  {
1059  rBuffer.append('[');
1060  // In case absolute/relative positions weren't separately available:
1061  // transform relative to absolute!
1062  ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
1063  if( !bSingleRef )
1064  aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
1065 
1066  if (FormulaGrammar::isODFF(eGram) && (rRef.Ref1.IsDeleted() || !rLimits.ValidAddress(aAbs1) ||
1067  (!bSingleRef && (rRef.Ref2.IsDeleted() || !rLimits.ValidAddress(aAbs2)))))
1068  {
1069  rBuffer.append(rErrRef);
1070  // For ODFF write [#REF!], but not for PODF so apps reading ODF
1071  // 1.0/1.1 may have a better chance if they implemented the old
1072  // form.
1073  }
1074  else
1075  {
1076  SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
1077  getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
1078  MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, true, eSingleton);
1079  if (!bSingleRef)
1080  {
1081  rBuffer.append(':');
1082  MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true,
1083  eSingleton);
1084  }
1085  }
1086  rBuffer.append(']');
1087  }
1088 
1089  virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1090  const OUString& rName ) const override
1091  {
1092  return lcl_makeExternalNameStr( rFile, rName, '#', true);
1093  }
1094 
1095  virtual void makeExternalRefStr(
1096  ScSheetLimits& rLimits,
1097  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1098  const OUString& rTabName, const ScSingleRefData& rRef ) const override
1099  {
1100  makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, true);
1101  }
1102 
1103  virtual void makeExternalRefStr(
1104  ScSheetLimits& rLimits,
1105  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1106  const std::vector<OUString>& rTabNames,
1107  const OUString& rTabName, const ScComplexRefData& rRef ) const override
1108  {
1109  makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true);
1110  }
1111 };
1112 
1113 struct ConventionXL
1114 {
1115  virtual ~ConventionXL()
1116  {
1117  }
1118 
1119  static void GetTab(
1120  const ScSheetLimits& rLimits,
1121  const ScAddress& rPos, const std::vector<OUString>& rTabNames,
1122  const ScSingleRefData& rRef, OUString& rTabName )
1123  {
1124  ScAddress aAbs = rRef.toAbs(rLimits, rPos);
1125  if (rRef.IsTabDeleted() || o3tl::make_unsigned(aAbs.Tab()) >= rTabNames.size())
1126  {
1127  rTabName = ScResId( STR_NO_REF_TABLE );
1128  return;
1129  }
1130  rTabName = rTabNames[aAbs.Tab()];
1131  }
1132 
1133  static void MakeTabStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf,
1134  const ScAddress& rPos,
1135  const std::vector<OUString>& rTabNames,
1136  const ScComplexRefData& rRef,
1137  bool bSingleRef )
1138  {
1139  if( !rRef.Ref1.IsFlag3D() )
1140  return;
1141 
1142  OUString aStartTabName, aEndTabName;
1143 
1144  GetTab(rLimits, rPos, rTabNames, rRef.Ref1, aStartTabName);
1145 
1146  if( !bSingleRef && rRef.Ref2.IsFlag3D() )
1147  {
1148  GetTab(rLimits, rPos, rTabNames, rRef.Ref2, aEndTabName);
1149  }
1150 
1151  rBuf.append( aStartTabName );
1152  if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
1153  {
1154  rBuf.append( ':' );
1155  rBuf.append( aEndTabName );
1156  }
1157 
1158  rBuf.append( '!' );
1159  }
1160 
1161  static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
1162  {
1163  switch (eSymType)
1164  {
1166  return u'\0';
1168  return '!';
1169  }
1170  return u'\0';
1171  }
1172 
1173  static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1174  const ScDocument& rDoc,
1175  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1176  {
1177  return lcl_parseExternalName( rSymbol, rFile, rName, '!', rDoc, pExternalLinks);
1178  }
1179 
1180  static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName )
1181  {
1182  return lcl_makeExternalNameStr( rFile, rName, '!', false);
1183  }
1184 
1185  static void makeExternalDocStr( OUStringBuffer& rBuffer, std::u16string_view rFullName )
1186  {
1187  // Format that is easier to deal with inside OOo, because we use file
1188  // URL, and all characters are allowed. Check if it makes sense to do
1189  // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1190  // and allows relative file path.
1191  //
1192  // ['file:///path/to/source/filename.xls']
1193 
1194  rBuffer.append('[');
1195  rBuffer.append('\'');
1196  OUString aFullName = INetURLObject::decode(rFullName, INetURLObject::DecodeMechanism::Unambiguous);
1197 
1198  const sal_Unicode* pBuf = aFullName.getStr();
1199  sal_Int32 nLen = aFullName.getLength();
1200  for (sal_Int32 i = 0; i < nLen; ++i)
1201  {
1202  const sal_Unicode c = pBuf[i];
1203  if (c == '\'')
1204  rBuffer.append(c);
1205  rBuffer.append(c);
1206  }
1207  rBuffer.append('\'');
1208  rBuffer.append(']');
1209  }
1210 
1211  static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName,
1212  const vector<OUString>& rTabNames,
1213  const ScRange& rRef )
1214  {
1215  OUString aLastTabName;
1216  if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
1217  {
1218  ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1219  return;
1220  }
1221 
1223  if (rTabName != aLastTabName)
1224  {
1225  rBuf.append(':');
1226  ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1227  }
1228  }
1229 
1230  virtual void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos ) const
1231  {
1232  sal_Int32 nLen = rFormula.getLength();
1233  const sal_Unicode* p = rFormula.getStr();
1234  sal_Unicode cPrev = 0;
1235  for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1236  {
1237  sal_Unicode c = p[i];
1238  if (i == rSrcPos)
1239  {
1240  // first character must be '['.
1241  if (c != '[')
1242  return;
1243  }
1244  else if (i == rSrcPos + 1)
1245  {
1246  // second character must be a single quote.
1247  if (c != '\'')
1248  return;
1249  }
1250  else if (c == '\'')
1251  {
1252  if (cPrev == '\'')
1253  // two successive single quote is treated as a single
1254  // valid character.
1255  c = 'a';
1256  }
1257  else if (c == ']')
1258  {
1259  if (cPrev == '\'')
1260  {
1261  // valid source document path found. Increment the
1262  // current position to skip the source path.
1263  rSrcPos = i + 1;
1264  if (rSrcPos >= nLen)
1265  rSrcPos = nLen - 1;
1266  return;
1267  }
1268  else
1269  return;
1270  }
1271  else
1272  {
1273  // any other character
1274  if (i > rSrcPos + 2 && cPrev == '\'')
1275  // unless it's the 3rd character, a normal character
1276  // following immediately a single quote is invalid.
1277  return;
1278  }
1279  cPrev = c;
1280  }
1281  }
1282 };
1283 
1284 struct ConventionXL_A1 : public Convention_A1, public ConventionXL
1285 {
1286  ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
1287  explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
1288 
1289  static void makeSingleCellStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs )
1290  {
1291  if (!rRef.IsColRel())
1292  rBuf.append('$');
1293  MakeColStr(rLimits, rBuf, rAbs.Col());
1294  if (!rRef.IsRowRel())
1295  rBuf.append('$');
1296  MakeRowStr(rLimits, rBuf, rAbs.Row());
1297  }
1298 
1299  virtual void makeRefStr(
1300  ScSheetLimits& rLimits,
1301  OUStringBuffer& rBuf,
1303  const ScAddress& rPos,
1304  const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1305  const ScComplexRefData& rRef,
1306  bool bSingleRef,
1307  bool /*bFromRangeName*/ ) const override
1308  {
1309  ScComplexRefData aRef( rRef );
1310 
1311  // Play fast and loose with invalid refs. There is not much point in producing
1312  // Foo!A1:#REF! versus #REF! at this point
1313  ScAddress aAbs1 = aRef.Ref1.toAbs(rLimits, rPos), aAbs2;
1314 
1315  MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
1316 
1317  if (!rLimits.ValidAddress(aAbs1))
1318  {
1319  rBuf.append(rErrRef);
1320  return;
1321  }
1322 
1323  if( !bSingleRef )
1324  {
1325  aAbs2 = aRef.Ref2.toAbs(rLimits, rPos);
1326  if (!rLimits.ValidAddress(aAbs2))
1327  {
1328  rBuf.append(rErrRef);
1329  return;
1330  }
1331 
1332  if (aAbs1.Col() == 0 && aAbs2.Col() >= rLimits.mnMaxCol)
1333  {
1334  if (!aRef.Ref1.IsRowRel())
1335  rBuf.append( '$' );
1336  MakeRowStr(rLimits, rBuf, aAbs1.Row());
1337  rBuf.append( ':' );
1338  if (!aRef.Ref2.IsRowRel())
1339  rBuf.append( '$' );
1340  MakeRowStr(rLimits, rBuf, aAbs2.Row());
1341  return;
1342  }
1343 
1344  if (aAbs1.Row() == 0 && aAbs2.Row() >= rLimits.mnMaxRow)
1345  {
1346  if (!aRef.Ref1.IsColRel())
1347  rBuf.append( '$' );
1348  MakeColStr(rLimits, rBuf, aAbs1.Col());
1349  rBuf.append( ':' );
1350  if (!aRef.Ref2.IsColRel())
1351  rBuf.append( '$' );
1352  MakeColStr(rLimits, rBuf, aAbs2.Col());
1353  return;
1354  }
1355  }
1356 
1357  makeSingleCellStr(rLimits, rBuf, aRef.Ref1, aAbs1);
1358  if (!bSingleRef)
1359  {
1360  rBuf.append( ':' );
1361  makeSingleCellStr(rLimits, rBuf, aRef.Ref2, aAbs2);
1362  }
1363  }
1364 
1365  virtual ParseResult parseAnyToken( const OUString& rFormula,
1366  sal_Int32 nSrcPos,
1367  const CharClass* pCharClass,
1368  bool bGroupSeparator) const override
1369  {
1370  parseExternalDocName(rFormula, nSrcPos);
1371 
1372  ParseResult aRet;
1373  if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1374  return aRet;
1375 
1376  constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1377  KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
1378  constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1379  // '?' allowed in range names
1380  static constexpr OUStringLiteral aAddAllowed(u"?!");
1381  return pCharClass->parseAnyToken( rFormula,
1382  nSrcPos, nStartFlags, aAddAllowed,
1383  (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
1384  aAddAllowed );
1385  }
1386 
1387  virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
1388  {
1389  return ConventionXL::getSpecialSymbol(eSymType);
1390  }
1391 
1392  virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1393  const ScDocument& rDoc,
1394  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
1395  {
1396  return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
1397  }
1398 
1399  virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1400  const OUString& rName ) const override
1401  {
1402  return ConventionXL::makeExternalNameStr(rFile, rName);
1403  }
1404 
1405  virtual void makeExternalRefStr(
1406  ScSheetLimits& rLimits,
1407  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1408  const OUString& rTabName, const ScSingleRefData& rRef ) const override
1409  {
1410  // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1411  // This is a little different from the format Excel uses, as Excel
1412  // puts [] only around the file name. But we need to enclose the
1413  // whole file path with [] because the file name can contain any
1414  // characters.
1415 
1416  ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1417  ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1418  rBuffer.append('!');
1419 
1420  makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
1421  }
1422 
1423  virtual void makeExternalRefStr(
1424  ScSheetLimits& rLimits,
1425  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1426  const std::vector<OUString>& rTabNames, const OUString& rTabName,
1427  const ScComplexRefData& rRef ) const override
1428  {
1429  ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1430 
1431  ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1432  ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1433  rBuffer.append('!');
1434 
1435  makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
1436  if (aAbsRef.aStart != aAbsRef.aEnd)
1437  {
1438  rBuffer.append(':');
1439  makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
1440  }
1441  }
1442 };
1443 
1444 struct ConventionXL_OOX : public ConventionXL_A1
1445 {
1446  ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
1447 
1448  virtual void makeRefStr( ScSheetLimits& rLimits,
1449  OUStringBuffer& rBuf,
1451  const ScAddress& rPos,
1452  const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1453  const ScComplexRefData& rRef,
1454  bool bSingleRef,
1455  bool bFromRangeName ) const override
1456  {
1457  // In OOXML relative references in named expressions are relative to
1458  // column 0 and row 0. Relative sheet references don't exist.
1459  ScAddress aPos( rPos );
1460  if (bFromRangeName)
1461  {
1462  // XXX NOTE: by decrementing the reference position we may end up
1463  // with resolved references with negative values. There's no proper
1464  // way to solve that or wrap them around without sheet dimensions
1465  // that are stored along. That, or blindly assume fixed dimensions
1466  // here and in import.
1467  /* TODO: maybe do that blind fixed dimensions wrap? */
1468  aPos.SetCol(0);
1469  aPos.SetRow(0);
1470  }
1471 
1472  if (rRef.Ref1.IsDeleted() || (!bSingleRef && rRef.Ref2.IsDeleted()))
1473  {
1474  // For OOXML write plain "#REF!" instead of detailed sheet/col/row
1475  // information.
1476  rBuf.append(rErrRef);
1477  return;
1478  }
1479 
1480  {
1481  ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos);
1482  if (!rLimits.ValidAddress(aAbs1)
1483  || o3tl::make_unsigned(aAbs1.Tab()) >= rTabNames.size())
1484  {
1485  rBuf.append(rErrRef);
1486  return;
1487  }
1488  }
1489 
1490  if (!bSingleRef)
1491  {
1492  ScAddress aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
1493  if (!rLimits.ValidAddress(aAbs2)
1494  || o3tl::make_unsigned(aAbs2.Tab()) >= rTabNames.size())
1495  {
1496  rBuf.append(rErrRef);
1497  return;
1498  }
1499  }
1500 
1501  ConventionXL_A1::makeRefStr( rLimits, rBuf, eGram, aPos, rErrRef, rTabNames, rRef, bSingleRef, bFromRangeName);
1502  }
1503 
1504  virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/,
1505  const OUString& rName ) const override
1506  {
1507  // [N]!DefinedName is a workbook global name.
1508  return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName );
1509 
1510  /* TODO: add support for sheet local names, would be
1511  * [N]'Sheet Name'!DefinedName
1512  * Similar to makeExternalRefStr() but with DefinedName instead of
1513  * CellStr. */
1514  }
1515 
1516  virtual void parseExternalDocName(const OUString& rFormula, sal_Int32& rSrcPos) const override
1517  {
1518  sal_Int32 nLen = rFormula.getLength();
1519  const sal_Unicode* p = rFormula.getStr();
1520  for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1521  {
1522  sal_Unicode c = p[i];
1523  if (i == rSrcPos)
1524  {
1525  // first character must be '['.
1526  if (c != '[')
1527  return;
1528  }
1529  else if (c == ']')
1530  {
1531  rSrcPos = i + 1;
1532  return;
1533  }
1534  }
1535  }
1536 
1537  virtual void makeExternalRefStr(
1538  ScSheetLimits& rLimits,
1539  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1540  const OUString& rTabName, const ScSingleRefData& rRef ) const override
1541  {
1542  // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1
1543  // Where N is a 1-based positive integer number of a file name in OOXML
1544  // xl/externalLinks/externalLinkN.xml
1545 
1546  OUString aQuotedTab( rTabName);
1547  ScCompiler::CheckTabQuotes( aQuotedTab);
1548  if (!aQuotedTab.isEmpty() && aQuotedTab[0] == '\'')
1549  {
1550  rBuffer.append('\'');
1551  ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1552  rBuffer.append( aQuotedTab.subView(1));
1553  }
1554  else
1555  {
1556  ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1557  rBuffer.append( aQuotedTab);
1558  }
1559  rBuffer.append('!');
1560 
1561  makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
1562  }
1563 
1564  virtual void makeExternalRefStr(
1565  ScSheetLimits& rLimits,
1566  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1567  const std::vector<OUString>& rTabNames, const OUString& rTabName,
1568  const ScComplexRefData& rRef ) const override
1569  {
1570  // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2
1571  // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the
1572  // simpler to produce and more logical form with independently quoted
1573  // sheet names as well. The [N] having to be within the quoted sheet
1574  // name is ugly enough...
1575 
1576  ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1577 
1578  OUStringBuffer aBuf;
1579  ConventionXL::makeExternalTabNameRange( aBuf, rTabName, rTabNames, aAbsRef);
1580  if (!aBuf.isEmpty() && aBuf[0] == '\'')
1581  {
1582  rBuffer.append('\'');
1583  ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1584  rBuffer.append( aBuf.copy(1));
1585  }
1586  else
1587  {
1588  ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1589  rBuffer.append( aBuf);
1590  }
1591  rBuffer.append('!');
1592 
1593  makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
1594  if (aAbsRef.aStart != aAbsRef.aEnd)
1595  {
1596  rBuffer.append(':');
1597  makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
1598  }
1599  }
1600 
1601  static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId )
1602  {
1603  rBuffer.append('[').append( static_cast<sal_Int32>(nFileId+1) ).append(']');
1604  }
1605 };
1606 
1607 }
1608 
1609 static void
1610 r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1611 {
1612  rBuf.append( 'C' );
1613  if( rRef.IsColRel() )
1614  {
1615  SCCOL nCol = rRef.Col();
1616  if (nCol != 0)
1617  rBuf.append("[" + OUString::number(nCol) + "]");
1618  }
1619  else
1620  rBuf.append( static_cast<sal_Int32>(rAbsRef.Col() + 1) );
1621 }
1622 static void
1623 r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1624 {
1625  rBuf.append( 'R' );
1626  if( rRef.IsRowRel() )
1627  {
1628  if (rRef.Row() != 0)
1629  {
1630  rBuf.append("[" + OUString::number(rRef.Row()) + "]");
1631  }
1632  }
1633  else
1634  rBuf.append( rAbsRef.Row() + 1 );
1635 }
1636 
1637 namespace {
1638 
1639 struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
1640 {
1641  ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
1642 
1643  virtual void makeRefStr( ScSheetLimits& rLimits,
1644  OUStringBuffer& rBuf,
1646  const ScAddress& rPos,
1647  const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1648  const ScComplexRefData& rRef,
1649  bool bSingleRef,
1650  bool /*bFromRangeName*/ ) const override
1651  {
1652  ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1653  ScComplexRefData aRef( rRef );
1654 
1655  MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
1656 
1657  // Play fast and loose with invalid refs. There is not much point in producing
1658  // Foo!A1:#REF! versus #REF! at this point
1659  if (!rLimits.ValidCol(aAbsRef.aStart.Col()) || !rLimits.ValidRow(aAbsRef.aStart.Row()))
1660  {
1661  rBuf.append(rErrRef);
1662  return;
1663  }
1664 
1665  if( !bSingleRef )
1666  {
1667  if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
1668  {
1669  rBuf.append(rErrRef);
1670  return;
1671  }
1672 
1673  if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
1674  {
1675  r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1676  if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() ||
1677  rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() )
1678  {
1679  rBuf.append( ':' );
1680  r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1681  }
1682  return;
1683 
1684  }
1685 
1686  if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
1687  {
1688  r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1689  if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() ||
1690  rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1691  {
1692  rBuf.append( ':' );
1693  r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1694  }
1695  return;
1696  }
1697  }
1698 
1699  r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1700  r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1701  if (!bSingleRef)
1702  {
1703  rBuf.append( ':' );
1704  r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1705  r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1706  }
1707  }
1708 
1709  ParseResult parseAnyToken( const OUString& rFormula,
1710  sal_Int32 nSrcPos,
1711  const CharClass* pCharClass,
1712  bool bGroupSeparator) const override
1713  {
1714  parseExternalDocName(rFormula, nSrcPos);
1715 
1716  ParseResult aRet;
1717  if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1718  return aRet;
1719 
1720  constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1721  KParseTokens::ASC_UNDERSCORE ;
1722  constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1723  // '?' allowed in range names
1724  static constexpr OUStringLiteral aAddAllowed(u"?-[]!");
1725 
1726  return pCharClass->parseAnyToken( rFormula,
1727  nSrcPos, nStartFlags, aAddAllowed,
1728  (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
1729  aAddAllowed );
1730  }
1731 
1732  virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
1733  {
1734  return ConventionXL::getSpecialSymbol(eSymType);
1735  }
1736 
1737  virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1738  const ScDocument& rDoc,
1739  const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
1740  {
1741  return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
1742  }
1743 
1744  virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1745  const OUString& rName ) const override
1746  {
1747  return ConventionXL::makeExternalNameStr(rFile, rName);
1748  }
1749 
1750  virtual void makeExternalRefStr(
1751  ScSheetLimits& rLimits,
1752  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1753  const OUString& rTabName, const ScSingleRefData& rRef ) const override
1754  {
1755  // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1756  // This is a little different from the format Excel uses, as Excel
1757  // puts [] only around the file name. But we need to enclose the
1758  // whole file path with [] because the file name can contain any
1759  // characters.
1760 
1761  ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
1762  ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1763  ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1764  rBuffer.append('!');
1765 
1766  r1c1_add_row(rBuffer, rRef, aAbsRef);
1767  r1c1_add_col(rBuffer, rRef, aAbsRef);
1768  }
1769 
1770  virtual void makeExternalRefStr(
1771  ScSheetLimits& rLimits,
1772  OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1773  const std::vector<OUString>& rTabNames, const OUString& rTabName,
1774  const ScComplexRefData& rRef ) const override
1775  {
1776  ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1777 
1778  ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1779  ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1780  rBuffer.append('!');
1781 
1782  if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
1783  {
1784  rBuffer.append(ScResId(STR_NO_REF_TABLE));
1785  return;
1786  }
1787 
1788  if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
1789  {
1790  r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1791  if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
1792  {
1793  rBuffer.append(':');
1794  r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1795  }
1796  return;
1797  }
1798 
1799  if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
1800  {
1801  r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1802  if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1803  {
1804  rBuffer.append(':');
1805  r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1806  }
1807  return;
1808  }
1809 
1810  r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1811  r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1812  rBuffer.append(':');
1813  r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1814  r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1815  }
1816 
1817  virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const override
1818  {
1819  ScCharFlags nFlags = mpCharTable[static_cast<sal_uInt8>(c)];
1820  if (c == '-' && cLast == '[')
1821  // '-' can occur within a reference string only after '[' e.g. R[-1]C.
1822  nFlags |= ScCharFlags::Ident;
1823  return nFlags;
1824  }
1825 };
1826 
1827 }
1828 
1830  bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1831  : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
1832  rDoc(rCxt.getDoc()),
1833  aPos(rPos),
1834  mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1835  mpInterpreterContext(pContext),
1836  mnCurrentSheetTab(-1),
1838  pCharClass(&ScGlobal::getCharClass()),
1839  mbCharClassesDiffer(false),
1844  mbCloseBrackets(true),
1845  mbRewind(false),
1847  maTabNames(rCxt.getTabNames())
1848 {
1849  SetGrammar(rCxt.getGrammar());
1850 }
1851 
1854  bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1855  : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
1856  rDoc( rDocument ),
1857  aPos( rPos ),
1858  mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1859  mpInterpreterContext(pContext),
1860  mnCurrentSheetTab(-1),
1861  mnCurrentSheetEndPos(0),
1862  nSrcPos(0),
1863  pCharClass( &ScGlobal::getCharClass() ),
1864  mbCharClassesDiffer(false),
1865  mnPredetectedReference(0),
1866  mnRangeOpPosInSymbol(-1),
1867  pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1868  meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1869  mbCloseBrackets( true ),
1870  mbRewind( false ),
1871  mbRefConventionChartOOXML( false )
1872 {
1874  rDocument.GetGrammar() :
1875  eGrammar );
1876 }
1877 
1879  bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1880  : FormulaCompiler(bComputeII, bMatrixFlag),
1881  rDoc(rCxt.getDoc()),
1882  aPos(rPos),
1883  mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1884  mpInterpreterContext(pContext),
1885  mnCurrentSheetTab(-1),
1886  mnCurrentSheetEndPos(0),
1887  pCharClass(&ScGlobal::getCharClass()),
1888  mbCharClassesDiffer(false),
1889  mnPredetectedReference(0),
1890  mnRangeOpPosInSymbol(-1),
1891  pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
1892  meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
1893  mbCloseBrackets(true),
1894  mbRewind(false),
1895  mbRefConventionChartOOXML(false),
1896  maTabNames(rCxt.getTabNames())
1897 {
1898  SetGrammar(rCxt.getGrammar());
1899 }
1900 
1903  bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1904  : FormulaCompiler(bComputeII, bMatrixFlag),
1905  rDoc( rDocument ),
1906  aPos( rPos ),
1907  mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1908  mpInterpreterContext(pContext),
1909  mnCurrentSheetTab(-1),
1910  mnCurrentSheetEndPos(0),
1911  nSrcPos(0),
1912  pCharClass( &ScGlobal::getCharClass() ),
1913  mbCharClassesDiffer(false),
1914  mnPredetectedReference(0),
1915  mnRangeOpPosInSymbol(-1),
1916  pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1917  meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1918  mbCloseBrackets( true ),
1919  mbRewind( false ),
1920  mbRefConventionChartOOXML( false )
1921 {
1923  rDocument.GetGrammar() :
1924  eGrammar );
1925 }
1926 
1928 {
1929 }
1930 
1931 void ScCompiler::CheckTabQuotes( OUString& rString,
1932  const FormulaGrammar::AddressConvention eConv )
1933 {
1934  sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
1935  sal_Int32 nContFlags = nStartFlags;
1936  ParseResult aRes = ScGlobal::getCharClass().parsePredefinedToken(
1937  KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_OUSTRING, nContFlags, EMPTY_OUSTRING);
1938  bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.getLength());
1939 
1940  switch ( eConv )
1941  {
1942  default :
1943  case FormulaGrammar::CONV_UNSPECIFIED :
1944  break;
1945  case FormulaGrammar::CONV_OOO :
1946  case FormulaGrammar::CONV_XL_A1 :
1947  case FormulaGrammar::CONV_XL_R1C1 :
1948  case FormulaGrammar::CONV_XL_OOX :
1949  case FormulaGrammar::CONV_ODF :
1950  if( bNeedsQuote )
1951  {
1952  // escape embedded quotes
1953  rString = rString.replaceAll( "'", "''" );
1954  }
1955  break;
1956  }
1957 
1958  if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
1959  {
1960  // Prevent any possible confusion resulting from pure numeric sheet names.
1961  bNeedsQuote = true;
1962  }
1963 
1964  if( bNeedsQuote )
1965  {
1966  rString = "'" + rString + "'";
1967  }
1968 }
1969 
1970 sal_Int32 ScCompiler::GetDocTabPos( const OUString& rString )
1971 {
1972  if (rString[0] != '\'')
1973  return -1;
1974  sal_Int32 nPos = ScGlobal::FindUnquoted( rString, SC_COMPILER_FILE_TAB_SEP);
1975  // it must be 'Doc'#
1976  if (nPos != -1 && rString[nPos-1] != '\'')
1977  nPos = -1;
1978  return nPos;
1979 }
1980 
1982 {
1983  const Convention* p = GetRefConvention(eConv);
1984  if (p)
1985  SetRefConvention(p);
1986 }
1987 
1989 {
1990 
1991  switch (eConv)
1992  {
1993  case FormulaGrammar::CONV_OOO:
1994  {
1995  static const ConventionOOO_A1 ConvOOO_A1;
1996  return &ConvOOO_A1;
1997  }
1998  case FormulaGrammar::CONV_ODF:
1999  {
2000  static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
2001  return &ConvOOO_A1_ODF;
2002  }
2003  case FormulaGrammar::CONV_XL_A1:
2004  {
2005  static const ConventionXL_A1 ConvXL_A1;
2006  return &ConvXL_A1;
2007  }
2008  case FormulaGrammar::CONV_XL_R1C1:
2009  {
2010  static const ConventionXL_R1C1 ConvXL_R1C1;
2011  return &ConvXL_R1C1;
2012  }
2013  case FormulaGrammar::CONV_XL_OOX:
2014  {
2015  static const ConventionXL_OOX ConvXL_OOX;
2016  return &ConvXL_OOX;
2017  }
2018  case FormulaGrammar::CONV_UNSPECIFIED:
2019  default:
2020  ;
2021  }
2022 
2023  return nullptr;
2024 }
2025 
2027 {
2028  pConv = pConvP;
2029  meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
2030  assert( FormulaGrammar::isSupported( meGrammar));
2031 }
2032 
2034 {
2035  if( pArr->GetCodeError() == FormulaError::NONE)
2036  pArr->SetCodeError( nError);
2037 }
2038 
2039 static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax )
2040 {
2041  const sal_Unicode* const pStop = pDst + nMax;
2042  while ( pDst < pStop )
2043  {
2044  *pDst++ = *pSrc++;
2045  }
2046  *pDst = 0;
2047  return pDst;
2048 }
2049 
2050 // p1 MUST contain at least n characters, or terminate with NIL.
2051 // p2 MUST pass upper case letters, if any.
2052 // n MUST not be greater than length of p2
2053 static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode* p1, const char* p2, size_t n )
2054 {
2055  for (size_t i=0; i<n; ++i)
2056  {
2057  if (!p1[i])
2058  return false;
2059  if (p1[i] != p2[i])
2060  {
2061  if (p1[i] < 'a' || 'z' < p1[i])
2062  return false; // not a lower case letter
2063  if (p2[i] < 'A' || 'Z' < p2[i])
2064  return false; // not a letter to match
2065  if (p1[i] != p2[i] + 0x20)
2066  return false; // lower case doesn't match either
2067  }
2068  }
2069  return true;
2070 }
2071 
2072 // static
2073 void ScCompiler::addWhitespace( std::vector<ScCompiler::Whitespace> & rvSpaces,
2074  ScCompiler::Whitespace & rSpace, sal_Unicode c, sal_Int32 n )
2075 {
2076  if (rSpace.cChar != c)
2077  {
2078  if (rSpace.cChar && rSpace.nCount > 0)
2079  rvSpaces.emplace_back(rSpace);
2080  rSpace.reset(c);
2081  }
2082  rSpace.nCount += n;
2083 }
2084 
2085 // NextSymbol
2086 
2087 // Parses the formula into separate symbols for further processing.
2088 // XXX NOTE: this is a rough sketch of the original idea, there are other
2089 // states that were added and didn't make it into this table and things are
2090 // more complicated. Use the source, Luke.
2091 
2092 // initial state = GetChar
2093 
2094 // old state | read character | action | new state
2095 //---------------+-------------------+-----------------------+---------------
2096 // GetChar | ;()+-*/^=& | Symbol=char | Stop
2097 // | <> | Symbol=char | GetBool
2098 // | $ letter | Symbol=char | GetWord
2099 // | number | Symbol=char | GetValue
2100 // | " | none | GetString
2101 // | other | none | GetChar
2102 //---------------+-------------------+-----------------------+---------------
2103 // GetBool | => | Symbol=Symbol+char | Stop
2104 // | other | Dec(CharPos) | Stop
2105 //---------------+-------------------+-----------------------+---------------
2106 // GetWord | SepSymbol | Dec(CharPos) | Stop
2107 // | ()+-*/^=<>&~ | |
2108 // | space | Dec(CharPos) | Stop
2109 // | $_:. | |
2110 // | letter, number | Symbol=Symbol+char | GetWord
2111 // | other | error | Stop
2112 //---------------+-------------------+-----------------------+---------------
2113 // GetValue | ;()*/^=<>& | |
2114 // | space | Dec(CharPos) | Stop
2115 // | number E+-%,. | Symbol=Symbol+char | GetValue
2116 // | other | error | Stop
2117 //---------------+-------------------+-----------------------+---------------
2118 // GetString | " | none | Stop
2119 // | other | Symbol=Symbol+char | GetString
2120 //---------------+-------------------+-----------------------+---------------
2121 
2122 std::vector<ScCompiler::Whitespace> ScCompiler::NextSymbol(bool bInArray)
2123 {
2124  std::vector<Whitespace> vSpaces;
2125  cSymbol[MAXSTRLEN] = 0; // end
2126  sal_Unicode* pSym = cSymbol;
2127  const sal_Unicode* const pStart = aFormula.getStr();
2128  const sal_Unicode* pSrc = pStart + nSrcPos;
2129  bool bi18n = false;
2130  sal_Unicode c = *pSrc;
2131  sal_Unicode cLast = 0;
2132  bool bQuote = false;
2133  mnRangeOpPosInSymbol = -1;
2134  ScanState eState = ssGetChar;
2135  Whitespace aSpace;
2136  sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep);
2137  sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep);
2138  sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep);
2139  sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0]);
2140  sal_Unicode cDecSepAlt = (mxSymbols->isEnglish() ? 0 : ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar());
2141 
2142  // special symbols specific to address convention used
2145 
2146  int nDecSeps = 0;
2147  bool bAutoIntersection = false;
2148  size_t nAutoIntersectionSpacesPos = 0;
2149  int nRefInName = 0;
2150  bool bErrorConstantHadSlash = false;
2152  // try to parse simple tokens before calling i18n parser
2153  while ((c != 0) && (eState != ssStop) )
2154  {
2155  pSrc++;
2156  ScCharFlags nMask = GetCharTableFlags( c, cLast );
2157 
2158  // The parameter separator and the array column and row separators end
2159  // things unconditionally if not in string or reference.
2160  if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
2161  {
2162  switch (eState)
2163  {
2164  // these are to be continued
2165  case ssGetString:
2166  case ssSkipString:
2167  case ssGetReference:
2168  case ssSkipReference:
2169  case ssGetTableRefItem:
2170  case ssGetTableRefColumn:
2171  break;
2172  default:
2173  if (eState == ssGetChar)
2174  *pSym++ = c;
2175  else
2176  pSrc--;
2177  eState = ssStop;
2178  }
2179  }
2180 Label_MaskStateMachine:
2181  switch (eState)
2182  {
2183  case ssGetChar :
2184  {
2185  // Order is important!
2186  if (eLastOp == ocTableRefOpen && c != '[' && c != '#' && c != ']')
2187  {
2188  *pSym++ = c;
2189  eState = ssGetTableRefColumn;
2190  }
2191  else if( nMask & ScCharFlags::OdfLabelOp )
2192  {
2193  // '!!' automatic intersection
2194  if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfLabelOp)
2195  {
2196  /* TODO: For now the UI "space operator" is used, this
2197  * could be enhanced using a specialized OpCode to get
2198  * rid of the space ambiguity, which would need some
2199  * places to be adapted though. And we would still need
2200  * to support the ambiguous space operator for UI
2201  * purposes anyway. However, we then could check for
2202  * invalid usage of '!!', which currently isn't
2203  * possible. */
2204  if (!bAutoIntersection)
2205  {
2206  ++pSrc;
2207  // Add 2 because it must match the character count
2208  // for bi18n.
2209  addWhitespace( vSpaces, aSpace, 0x20, 2);
2210  // Position of Whitespace where it will be added to
2211  // vector.
2212  nAutoIntersectionSpacesPos = vSpaces.size();
2213  bAutoIntersection = true;
2214  }
2215  else
2216  {
2217  pSrc--;
2218  eState = ssStop;
2219  }
2220  }
2221  else
2222  {
2223  nMask &= ~ScCharFlags::OdfLabelOp;
2224  goto Label_MaskStateMachine;
2225  }
2226  }
2227  else if( nMask & ScCharFlags::OdfNameMarker )
2228  {
2229  // '$$' defined name marker
2230  if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfNameMarker)
2231  {
2232  // both eaten, not added to pSym
2233  ++pSrc;
2234  }
2235  else
2236  {
2237  nMask &= ~ScCharFlags::OdfNameMarker;
2238  goto Label_MaskStateMachine;
2239  }
2240  }
2241  else if( nMask & ScCharFlags::Char )
2242  {
2243  // '[' is a special case in Excel syntax, it can start an
2244  // external reference, ID in OOXML like [1]Sheet1!A1 or
2245  // Excel_A1 [filename]Sheet!A1 or Excel_R1C1
2246  // [filename]Sheet!R1C1 that needs to be scanned
2247  // entirely, or can be ocTableRefOpen, of which the first
2248  // transforms an ocDBArea into an ocTableRef.
2249  if (c == '[' && FormulaGrammar::isExcelSyntax( meGrammar)
2250  && eLastOp != ocDBArea && maTableRefs.empty())
2251  {
2252  // [0]!Global_Range_Name, is a special case in OOXML
2253  // syntax, where the '0' is referencing to self and we
2254  // do not need it, so we should skip it, in order to
2255  // later it will be more recognisable for IsNamedRange.
2256  if (FormulaGrammar::isRefConventionOOXML(meGrammar) &&
2257  pSrc[0] == '0' && pSrc[1] == ']' && pSrc[2] == '!')
2258  {
2259  pSrc += 3;
2260  c = *pSrc;
2261  continue;
2262  }
2263 
2264  nMask &= ~ScCharFlags::Char;
2265  goto Label_MaskStateMachine;
2266  }
2267  else
2268  {
2269  *pSym++ = c;
2270  eState = ssStop;
2271  }
2272  }
2273  else if( nMask & ScCharFlags::OdfLBracket )
2274  {
2275  // eaten, not added to pSym
2276  eState = ssGetReference;
2278  }
2279  else if( nMask & ScCharFlags::CharBool )
2280  {
2281  *pSym++ = c;
2282  eState = ssGetBool;
2283  }
2284  else if( nMask & ScCharFlags::CharValue )
2285  {
2286  *pSym++ = c;
2287  eState = ssGetValue;
2288  }
2289  else if( nMask & ScCharFlags::CharString )
2290  {
2291  *pSym++ = c;
2292  eState = ssGetString;
2293  }
2294  else if( nMask & ScCharFlags::CharErrConst )
2295  {
2296  *pSym++ = c;
2297  if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
2298  eState = ssGetTableRefItem;
2299  else
2300  eState = ssGetErrorConstant;
2301  }
2302  else if( nMask & ScCharFlags::CharDontCare )
2303  {
2304  addWhitespace( vSpaces, aSpace, c);
2305  }
2306  else if( nMask & ScCharFlags::CharIdent )
2307  { // try to get a simple ASCII identifier before calling
2308  // i18n, to gain performance during import
2309  *pSym++ = c;
2310  eState = ssGetIdent;
2311  }
2312  else
2313  {
2314  bi18n = true;
2315  eState = ssStop;
2316  }
2317  }
2318  break;
2319  case ssGetIdent:
2320  {
2321  if ( nMask & ScCharFlags::Ident )
2322  { // This catches also $Sheet1.A$1, for example.
2323  if( pSym == &cSymbol[ MAXSTRLEN ] )
2324  {
2325  SetError(FormulaError::StringOverflow);
2326  eState = ssStop;
2327  }
2328  else
2329  *pSym++ = c;
2330  }
2331  else if (c == '#' && lcl_isUnicodeIgnoreAscii( pSrc, "REF!", 4))
2332  {
2333  // Completely ugly means to catch broken
2334  // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts)
2335  // references that were written in ODF named ranges
2336  // (without embracing [] hence no predetected reference)
2337  // and to OOXML and handle them as one symbol.
2338  // Also catches these in UI, so we can process them
2339  // further.
2340  int i = 0;
2341  for ( ; i<5; ++i)
2342  {
2343  if( pSym == &cSymbol[ MAXSTRLEN ] )
2344  {
2345  SetError(FormulaError::StringOverflow);
2346  eState = ssStop;
2347  break; // for
2348  }
2349  else
2350  {
2351  *pSym++ = c;
2352  c = *pSrc++;
2353  }
2354  }
2355  if (i == 5)
2356  c = *((--pSrc)-1); // position last/next character correctly
2357  }
2358  else if (c == ':' && mnRangeOpPosInSymbol < 0)
2359  {
2360  // One range operator may form Sheet1.A:A, which we need to
2361  // pass as one entity to IsReference().
2362  if( pSym == &cSymbol[ MAXSTRLEN ] )
2363  {
2364  SetError(FormulaError::StringOverflow);
2365  eState = ssStop;
2366  }
2367  else
2368  {
2369  mnRangeOpPosInSymbol = pSym - &cSymbol[0];
2370  *pSym++ = c;
2371  }
2372  }
2373  else if ( 128 <= c || '\'' == c )
2374  { // High values need reparsing with i18n,
2375  // single quoted $'sheet' names too (otherwise we'd had to
2376  // implement everything twice).
2377  bi18n = true;
2378  eState = ssStop;
2379  }
2380  else
2381  {
2382  pSrc--;
2383  eState = ssStop;
2384  }
2385  }
2386  break;
2387  case ssGetBool :
2388  {
2389  if( nMask & ScCharFlags::Bool )
2390  {
2391  *pSym++ = c;
2392  eState = ssStop;
2393  }
2394  else
2395  {
2396  pSrc--;
2397  eState = ssStop;
2398  }
2399  }
2400  break;
2401  case ssGetValue :
2402  {
2403  if( pSym == &cSymbol[ MAXSTRLEN ] )
2404  {
2405  SetError(FormulaError::StringOverflow);
2406  eState = ssStop;
2407  }
2408  else if (c == cDecSep || (cDecSepAlt && c == cDecSepAlt))
2409  {
2410  if (++nDecSeps > 1)
2411  {
2412  // reparse with i18n, may be numeric sheet name as well
2413  bi18n = true;
2414  eState = ssStop;
2415  }
2416  else
2417  *pSym++ = c;
2418  }
2419  else if( nMask & ScCharFlags::Value )
2420  *pSym++ = c;
2421  else if( nMask & ScCharFlags::ValueSep )
2422  {
2423  pSrc--;
2424  eState = ssStop;
2425  }
2426  else if (c == 'E' || c == 'e')
2427  {
2428  if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueExp)
2429  *pSym++ = c;
2430  else
2431  {
2432  // reparse with i18n
2433  bi18n = true;
2434  eState = ssStop;
2435  }
2436  }
2437  else if( nMask & ScCharFlags::ValueSign )
2438  {
2439  if (((cLast == 'E') || (cLast == 'e')) &&
2440  (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueValue))
2441  {
2442  *pSym++ = c;
2443  }
2444  else
2445  {
2446  pSrc--;
2447  eState = ssStop;
2448  }
2449  }
2450  else
2451  {
2452  // reparse with i18n
2453  bi18n = true;
2454  eState = ssStop;
2455  }
2456  }
2457  break;
2458  case ssGetString :
2459  {
2460  if( nMask & ScCharFlags::StringSep )
2461  {
2462  if ( !bQuote )
2463  {
2464  if ( *pSrc == '"' )
2465  bQuote = true; // "" => literal "
2466  else
2467  eState = ssStop;
2468  }
2469  else
2470  bQuote = false;
2471  }
2472  if ( !bQuote )
2473  {
2474  if( pSym == &cSymbol[ MAXSTRLEN ] )
2475  {
2476  SetError(FormulaError::StringOverflow);
2477  eState = ssSkipString;
2478  }
2479  else
2480  *pSym++ = c;
2481  }
2482  }
2483  break;
2484  case ssSkipString:
2485  if( nMask & ScCharFlags::StringSep )
2486  eState = ssStop;
2487  break;
2488  case ssGetErrorConstant:
2489  {
2490  // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2491  // BUT, in UI these may have been translated! So don't
2492  // check for ASCII alnum. Note that this construct can't be
2493  // parsed with i18n.
2494  /* TODO: be strict when reading ODFF, check for ASCII alnum
2495  * and proper continuation after '/'. However, even with
2496  * the lax parsing only the error constants we have defined
2497  * as opcode symbols will be recognized and others result
2498  * in ocBad, so the result is actually conformant. */
2499  bool bAdd = true;
2500  if ('?' == c)
2501  eState = ssStop;
2502  else if ('!' == c)
2503  {
2504  // Check if this is #REF! that starts an invalid reference.
2505  // Note we have an implicit '!' here at the end.
2506  if (pSym - &cSymbol[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol, "#REF", 4) &&
2507  (GetCharTableFlags( *pSrc, c) & ScCharFlags::Ident))
2508  eState = ssGetIdent;
2509  else
2510  eState = ssStop;
2511  }
2512  else if ('/' == c)
2513  {
2514  if (!bErrorConstantHadSlash)
2515  bErrorConstantHadSlash = true;
2516  else
2517  {
2518  bAdd = false;
2519  eState = ssStop;
2520  }
2521  }
2522  else if ((nMask & ScCharFlags::WordSep) ||
2523  (c < 128 && !rtl::isAsciiAlphanumeric( c)))
2524  {
2525  bAdd = false;
2526  eState = ssStop;
2527  }
2528  if (!bAdd)
2529  --pSrc;
2530  else
2531  {
2532  if (pSym == &cSymbol[ MAXSTRLEN ])
2533  {
2534  SetError( FormulaError::StringOverflow);
2535  eState = ssStop;
2536  }
2537  else
2538  *pSym++ = c;
2539  }
2540  }
2541  break;
2542  case ssGetTableRefItem:
2543  {
2544  // Scan whatever up to the next ']' closer.
2545  if (c != ']')
2546  {
2547  if( pSym == &cSymbol[ MAXSTRLEN ] )
2548  {
2549  SetError( FormulaError::StringOverflow);
2550  eState = ssStop;
2551  }
2552  else
2553  *pSym++ = c;
2554  }
2555  else
2556  {
2557  --pSrc;
2558  eState = ssStop;
2559  }
2560  }
2561  break;
2562  case ssGetTableRefColumn:
2563  {
2564  // Scan whatever up to the next unescaped ']' closer.
2565  if (c != ']' || cLast == '\'')
2566  {
2567  if( pSym == &cSymbol[ MAXSTRLEN ] )
2568  {
2569  SetError( FormulaError::StringOverflow);
2570  eState = ssStop;
2571  }
2572  else
2573  *pSym++ = c;
2574  }
2575  else
2576  {
2577  --pSrc;
2578  eState = ssStop;
2579  }
2580  }
2581  break;
2582  case ssGetReference:
2583  if( pSym == &cSymbol[ MAXSTRLEN ] )
2584  {
2585  SetError( FormulaError::StringOverflow);
2586  eState = ssSkipReference;
2587  }
2588  [[fallthrough]];
2589  case ssSkipReference:
2590  // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2591  // mandatory also if no sheet name. 'External'# is optional,
2592  // sheet name is optional, quotes around sheet name are
2593  // optional if no quote contained. [#REF!] is valid.
2594  // 2nd usage: ['Sheet'.$$'DefinedName']
2595  // 3rd usage: ['External'#$$'DefinedName']
2596  // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2597  // Also for all these names quotes are optional if no quote
2598  // contained.
2599  {
2600 
2601  // nRefInName: 0 := not in sheet name yet. 'External'
2602  // is parsed as if it was a sheet name and nRefInName
2603  // is reset when # is encountered immediately after closing
2604  // quote. Same with 'DefinedName', nRefInName is cleared
2605  // when : is encountered.
2606 
2607  // Encountered leading $ before sheet name.
2608  constexpr int kDollar = (1 << 1);
2609  // Encountered ' opening quote, which may be after $ or
2610  // not.
2611  constexpr int kOpen = (1 << 2);
2612  // Somewhere in name.
2613  constexpr int kName = (1 << 3);
2614  // Encountered ' in name, will be cleared if double or
2615  // transformed to kClose if not, in which case kOpen is
2616  // cleared.
2617  constexpr int kQuote = (1 << 4);
2618  // Past ' closing quote.
2619  constexpr int kClose = (1 << 5);
2620  // Encountered # file/sheet separator.
2621  constexpr int kFileSep = (1 << 6);
2622  // Past . sheet name separator.
2623  constexpr int kPast = (1 << 7);
2624  // Marked name $$ follows sheet name separator, detected
2625  // while we're still on the separator. Will be cleared when
2626  // entering the name.
2627  constexpr int kMarkAhead = (1 << 8);
2628  // In marked defined name.
2629  constexpr int kDefName = (1 << 9);
2630  // Encountered # of #REF!
2631  constexpr int kRefErr = (1 << 10);
2632 
2633  bool bAddToSymbol = true;
2634  if ((nMask & ScCharFlags::OdfRBracket) && !(nRefInName & kOpen))
2635  {
2636  OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
2637  "ScCompiler::NextSymbol: reference: "
2638  "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2639  // eaten, not added to pSym
2640  bAddToSymbol = false;
2641  eState = ssStop;
2642  }
2643  else if (cSheetSep == c && nRefInName == 0)
2644  {
2645  // eat it, no sheet name [.A1]
2646  bAddToSymbol = false;
2647  nRefInName |= kPast;
2648  if ('$' == pSrc[0] && '$' == pSrc[1])
2649  nRefInName |= kMarkAhead;
2650  }
2651  else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2652  {
2653  // Not in col/row yet.
2654 
2655  if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
2656  nRefInName = 0;
2657  else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2658  {
2659  nRefInName &= ~kMarkAhead;
2660  if (!(nRefInName & kDefName))
2661  {
2662  // eaten, not added to pSym (2 chars)
2663  bAddToSymbol = false;
2664  ++pSrc;
2665  nRefInName &= kPast;
2666  nRefInName |= kDefName;
2667  }
2668  else
2669  {
2670  // ScAddress::Parse() will recognize this as
2671  // invalid later.
2672  if (eState != ssSkipReference)
2673  {
2674  *pSym++ = c;
2675 
2676  if( pSym == &cSymbol[ MAXSTRLEN ] )
2677  {
2678  SetError( FormulaError::StringOverflow);
2679  eState = ssStop;
2680  }
2681  else
2682  *pSym++ = *pSrc++;
2683  }
2684  bAddToSymbol = false;
2685  }
2686  }
2687  else if (cSheetPrefix == c && nRefInName == 0)
2688  nRefInName |= kDollar;
2689  else if ('\'' == c)
2690  {
2691  // TODO: The conventions' parseExternalName()
2692  // should handle quoted names, but as long as they
2693  // don't remove non-embedded quotes here.
2694  if (!(nRefInName & kName))
2695  {
2696  nRefInName |= (kOpen | kName);
2697  bAddToSymbol = !(nRefInName & kDefName);
2698  }
2699  else if (!(nRefInName & kOpen))
2700  {
2701  OSL_FAIL("ScCompiler::NextSymbol: reference: "
2702  "a ''' without the name being enclosed in '...' violates ODF spec");
2703  }
2704  else if (nRefInName & kQuote)
2705  {
2706  // escaped embedded quote
2707  nRefInName &= ~kQuote;
2708  }
2709  else
2710  {
2711  switch (pSrc[0])
2712  {
2713  case '\'':
2714  // escapes embedded quote
2715  nRefInName |= kQuote;
2716  break;
2718  // sheet name should follow
2719  nRefInName |= kFileSep;
2720  [[fallthrough]];
2721  default:
2722  // quote not followed by quote => close
2723  nRefInName |= kClose;
2724  nRefInName &= ~kOpen;
2725  }
2726  bAddToSymbol = !(nRefInName & kDefName);
2727  }
2728  }
2729  else if ('#' == c && nRefInName == 0)
2730  nRefInName |= kRefErr;
2731  else if (cSheetSep == c && !(nRefInName & kOpen))
2732  {
2733  // unquoted sheet name separator
2734  nRefInName |= kPast;
2735  if ('$' == pSrc[0] && '$' == pSrc[1])
2736  nRefInName |= kMarkAhead;
2737  }
2738  else if (':' == c && !(nRefInName & kOpen))
2739  {
2740  OSL_FAIL("ScCompiler::NextSymbol: reference: "
2741  "range operator ':' without prior sheet name separator '.' violates ODF spec");
2742  nRefInName = 0;
2744  }
2745  else if (!(nRefInName & kName))
2746  {
2747  // start unquoted name
2748  nRefInName |= kName;
2749  }
2750  }
2751  else if (':' == c)
2752  {
2753  // range operator
2754  nRefInName = 0;
2756  }
2757  if (bAddToSymbol && eState != ssSkipReference)
2758  *pSym++ = c; // everything is part of reference
2759  }
2760  break;
2761  case ssStop:
2762  ; // nothing, prevent warning
2763  break;
2764  }
2765  cLast = c;
2766  c = *pSrc;
2767  }
2768 
2769  if (aSpace.nCount && aSpace.cChar)
2770  vSpaces.emplace_back(aSpace);
2771 
2772  if ( bi18n )
2773  {
2774  const sal_Int32 nOldSrcPos = nSrcPos;
2775  for (const auto& r : vSpaces)
2776  nSrcPos += r.nCount;
2777  // If group separator is not a possible operator and not one of any
2778  // separators then it may be parsed away in numbers. This is
2779  // specifically the case with NO-BREAK SPACE, which actually triggers
2780  // the bi18n case (which we don't want to include as yet another
2781  // special case above as it is rare enough and doesn't generally occur
2782  // in formulas).
2783  const sal_Unicode cGroupSep = ScGlobal::getLocaleData().getNumThousandSep()[0];
2784  const bool bGroupSeparator = (128 <= cGroupSep && cGroupSep != cSep &&
2785  cGroupSep != cArrayColSep && cGroupSep != cArrayRowSep &&
2786  cGroupSep != cDecSep && cGroupSep != cDecSepAlt &&
2787  cGroupSep != cSheetPrefix && cGroupSep != cSheetSep);
2788  // If a numeric context triggered bi18n then use the default locale's
2789  // CharClass, this may accept group separator as well.
2790  const CharClass* pMyCharClass = (ScGlobal::getCharClass().isDigit( OUString(pStart[nSrcPos]), 0) ?
2792  OUStringBuffer aSymbol;
2793  mnRangeOpPosInSymbol = -1;
2794  FormulaError nErr = FormulaError::NONE;
2795  do
2796  {
2797  bi18n = false;
2798  // special case (e.g. $'sheetname' in OOO A1)
2799  if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2800  aSymbol.append(pStart[nSrcPos++]);
2801 
2802  ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pMyCharClass, bGroupSeparator);
2803 
2804  if ( !aRes.TokenType )
2805  {
2806  nErr = FormulaError::IllegalChar;
2807  SetError( nErr ); // parsed chars as string
2808  }
2809  if ( aRes.EndPos <= nSrcPos )
2810  {
2811  // Could not parse anything meaningful.
2812  assert(!aRes.TokenType);
2813  nErr = FormulaError::IllegalChar;
2814  SetError( nErr );
2815  // Caller has to act on an empty symbol for
2816  // nSrcPos < aFormula.getLength()
2817  nSrcPos = nOldSrcPos;
2818  aSymbol.setLength(0);
2819  }
2820  else
2821  {
2822  // When having parsed a second reference part, ensure that the
2823  // i18n parser did not mistakingly parse a number that included
2824  // a separator which happened to be meant as a parameter
2825  // separator instead.
2826  if (mnRangeOpPosInSymbol >= 0 && (aRes.TokenType & KParseType::ASC_NUMBER))
2827  {
2828  for (sal_Int32 i = nSrcPos; i < aRes.EndPos; ++i)
2829  {
2830  if (pStart[i] == cSep)
2831  aRes.EndPos = i; // also ends for
2832  }
2833  }
2834  aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos);
2835  nSrcPos = aRes.EndPos;
2836  c = pStart[nSrcPos];
2837  if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2838  { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2839  bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2840  }
2841  // One range operator restarts parsing for second reference.
2842  if (c == ':' && mnRangeOpPosInSymbol < 0)
2843  {
2844  mnRangeOpPosInSymbol = aSymbol.getLength();
2845  bi18n = true;
2846  }
2847  if ( bi18n )
2848  aSymbol.append(pStart[nSrcPos++]);
2849  }
2850  } while ( bi18n && nErr == FormulaError::NONE );
2851  sal_Int32 nLen = aSymbol.getLength();
2852  if ( nLen > MAXSTRLEN )
2853  {
2854  SetError( FormulaError::StringOverflow );
2855  nLen = MAXSTRLEN;
2856  }
2857  if (mnRangeOpPosInSymbol >= nLen)
2858  mnRangeOpPosInSymbol = -1;
2859  lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen );
2860  pSym = &cSymbol[nLen];
2861  }
2862  else
2863  {
2864  nSrcPos = pSrc - pStart;
2865  *pSym = 0;
2866  }
2867  if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2868  {
2869  // This is a trailing range operator, which is nonsense. Will be caught
2870  // in next round.
2871  mnRangeOpPosInSymbol = -1;
2872  *--pSym = 0;
2873  --nSrcPos;
2874  }
2875  if ( bAutoCorrect )
2876  aCorrectedSymbol = OUString(cSymbol, pSym - cSymbol);
2877  if (bAutoIntersection && vSpaces[nAutoIntersectionSpacesPos].nCount > 1)
2878  --vSpaces[nAutoIntersectionSpacesPos].nCount; // replace '!!' with only one space
2879  return vSpaces;
2880 }
2881 
2882 // Convert symbol to token
2883 
2884 bool ScCompiler::IsOpCode( const OUString& rName, bool bInArray )
2885 {
2886  OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
2887  bool bFound = (iLook != mxSymbols->getHashMap().end());
2888  if (bFound)
2889  {
2890  OpCode eOp = iLook->second;
2891  if (bInArray)
2892  {
2893  if (rName == mxSymbols->getSymbol(ocArrayColSep))
2894  eOp = ocArrayColSep;
2895  else if (rName == mxSymbols->getSymbol(ocArrayRowSep))
2896  eOp = ocArrayRowSep;
2897  }
2898  else if (eOp == ocArrayColSep || eOp == ocArrayRowSep)
2899  {
2900  if (rName == mxSymbols->getSymbol(ocSep))
2901  eOp = ocSep;
2902  else if (rName == ";")
2903  {
2904  switch (FormulaGrammar::extractFormulaLanguage( meGrammar))
2905  {
2906  // Only for languages/grammars that actually use ';'
2907  // parameter separator.
2908  case css::sheet::FormulaLanguage::NATIVE:
2909  case css::sheet::FormulaLanguage::ENGLISH:
2910  case css::sheet::FormulaLanguage::ODFF:
2911  case css::sheet::FormulaLanguage::ODF_11:
2912  eOp = ocSep;
2913  }
2914  }
2915  }
2916  else if (eOp == ocCeil && mxSymbols->isOOXML())
2917  {
2918  // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
2919  // unassigned for import.
2920  eOp = ocCeil_Math;
2921  }
2922  else if (eOp == ocFloor && mxSymbols->isOOXML())
2923  {
2924  // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2925  // unassigned for import.
2926  eOp = ocFloor_Math;
2927  }
2928  maRawToken.SetOpCode(eOp);
2929  }
2930  else if (mxSymbols->isODFF())
2931  {
2932  // ODFF names that are not written in the current mapping but to be
2933  // recognized. New names will be written in a future release, then
2934  // exchange (!) with the names in
2935  // formula/source/core/resource/core_resource.src to be able to still
2936  // read the old names as well.
2937  struct FunctionName
2938  {
2939  const char* pName;
2940  OpCode eOp;
2941  };
2942  static const FunctionName aOdffAliases[] = {
2943  // Renamed old names, still accept them:
2944  { "B", ocB }, // B -> BINOM.DIST.RANGE
2945  { "TDIST", ocTDist }, // TDIST -> LEGACY.TDIST
2946  { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
2947  { "ZGZ", ocRRI }, // ZGZ -> RRI
2948  { "COLOR", ocColor }, // COLOR -> ORG.LIBREOFFICE.COLOR
2949  { "GOALSEEK", ocBackSolver }, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
2950  { "COM.MICROSOFT.F.DIST", ocFDist_LT }, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
2951  { "COM.MICROSOFT.F.INV", ocFInv_LT } // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
2952  // Renamed new names, prepare to read future names:
2953  //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX
2954  };
2955  for (const FunctionName& rOdffAlias : aOdffAliases)
2956  {
2957  if (rName.equalsIgnoreAsciiCaseAscii( rOdffAlias.pName))
2958  {
2959  maRawToken.SetOpCode( rOdffAlias.eOp);
2960  bFound = true;
2961  break; // for
2962  }
2963  }
2964  }
2965  else if (mxSymbols->isOOXML())
2966  {
2967  // OOXML names that are not written in the current mapping but to be
2968  // recognized as old versions wrote them.
2969  struct FunctionName
2970  {
2971  const char* pName;
2972  OpCode eOp;
2973  };
2974  static const FunctionName aOoxmlAliases[] = {
2975  { "EFFECTIVE", ocEffect }, // EFFECTIVE -> EFFECT
2976  { "ERRORTYPE", ocErrorType }, // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE
2977  { "MULTIRANGE", ocMultiArea }, // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE
2978  { "GOALSEEK", ocBackSolver }, // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK
2979  { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY
2980  { "CURRENT", ocCurrent }, // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT
2981  { "STYLE", ocStyle } // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE
2982  };
2983  for (const FunctionName& rOoxmlAlias : aOoxmlAliases)
2984  {
2985  if (rName.equalsIgnoreAsciiCaseAscii( rOoxmlAlias.pName))
2986  {
2987  maRawToken.SetOpCode( rOoxmlAlias.eOp);
2988  bFound = true;
2989  break; // for
2990  }
2991  }
2992  }
2993  else if (mxSymbols->isPODF())
2994  {
2995  // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess.
2996  // We can't rename them in
2997  // formula/source/core/resource/core_resource.src but can add
2998  // additional names to be recognized here so they match the UI names if
2999  // those are renamed.
3000  struct FunctionName
3001  {
3002  const char* pName;
3003  OpCode eOp;
3004  };
3005  static const FunctionName aPodfAliases[] = {
3006  { "EFFECT", ocEffect } // EFFECTIVE -> EFFECT
3007  };
3008  for (const FunctionName& rPodfAlias : aPodfAliases)
3009  {
3010  if (rName.equalsIgnoreAsciiCaseAscii( rPodfAlias.pName))
3011  {
3012  maRawToken.SetOpCode( rPodfAlias.eOp);
3013  bFound = true;
3014  break; // for
3015  }
3016  }
3017  }
3018 
3019  if (!bFound)
3020  {
3021  OUString aIntName;
3022  if (mxSymbols->hasExternals())
3023  {
3024  // If symbols are set by filters get mapping to exact name.
3025  ExternalHashMap::const_iterator iExt(
3026  mxSymbols->getExternalHashMap().find( rName));
3027  if (iExt != mxSymbols->getExternalHashMap().end())
3028  {
3029  if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
3030  aIntName = (*iExt).second;
3031  }
3032  }
3033  else
3034  {
3035  // Old (deprecated) addins first for legacy.
3037  {
3038  aIntName = cSymbol;
3039  }
3040  else
3041  // bLocalFirst=false for (English) upper full original name
3042  // (service.function)
3044  rName, !mxSymbols->isEnglish());
3045  }
3046  if (!aIntName.isEmpty())
3047  {
3048  maRawToken.SetExternal( aIntName ); // international name
3049  bFound = true;
3050  }
3051  }
3052  if (!bFound)
3053  return false;
3054  OpCode eOp = maRawToken.GetOpCode();
3055  if (eOp == ocSub || eOp == ocNegSub)
3056  {
3057  bool bShouldBeNegSub =
3058  (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
3060  eLastOp == ocArrayOpen ||
3062  if (bShouldBeNegSub && eOp == ocSub)
3064  //TODO: if ocNegSub had ForceArray we'd have to set it here
3065  else if (!bShouldBeNegSub && eOp == ocNegSub)
3067  }
3068  return bFound;
3069 }
3070 
3071 bool ScCompiler::IsOpCode2( const OUString& rName )
3072 {
3073  bool bFound = false;
3074  sal_uInt16 i;
3075 
3076  for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
3077  bFound = rName.equalsAscii( pInternal[ i-ocInternalBegin ] );
3078 
3079  if (bFound)
3080  {
3081  maRawToken.SetOpCode( static_cast<OpCode>(--i) );
3082  }
3083  return bFound;
3084 }
3085 
3086 static bool lcl_ParenthesisFollows( const sal_Unicode* p )
3087 {
3088  while (*p == ' ')
3089  p++;
3090  return *p == '(';
3091 }
3092 
3093 bool ScCompiler::IsValue( const OUString& rSym )
3094 {
3095  const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( GetGrammar());
3096  if (nFormulaLanguage == css::sheet::FormulaLanguage::ODFF || nFormulaLanguage == css::sheet::FormulaLanguage::OOXML)
3097  {
3098  // Speedup things for ODFF, only well-formed numbers, not locale
3099  // dependent nor user input.
3100  rtl_math_ConversionStatus eStatus;
3101  sal_Int32 nParseEnd;
3102  double fVal = rtl::math::stringToDouble( rSym, '.', 0, &eStatus, &nParseEnd);
3103  if (nParseEnd != rSym.getLength())
3104  {
3105  // Not (only) a number.
3106 
3107  if (nParseEnd > 0)
3108  return false; // partially a number => no such thing
3109 
3110  if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3111  return false; // some function name, not a constant
3112 
3113  // Could be TRUE or FALSE constant.
3114  OpCode eOpFunc = ocNone;
3115  if (rSym.equalsIgnoreAsciiCase("TRUE"))
3116  eOpFunc = ocTrue;
3117  else if (rSym.equalsIgnoreAsciiCase("FALSE"))
3118  eOpFunc = ocFalse;
3119  if (eOpFunc != ocNone)
3120  {
3121  maRawToken.SetOpCode(eOpFunc);
3122  // add missing trailing parentheses
3123  maPendingOpCodes.push(ocOpen);
3124  maPendingOpCodes.push(ocClose);
3125  return true;
3126  }
3127  return false;
3128  }
3129  if (eStatus == rtl_math_ConversionStatus_OutOfRange)
3130  {
3131  // rtl::math::stringToDouble() recognizes XMLSchema-2 "INF" and
3132  // "NaN" (case sensitive) that could be named expressions or DB
3133  // areas as well.
3134  // rSym is already upper so "NaN" is not possible here.
3135  if (!std::isfinite(fVal) && rSym == "INF")
3136  {
3137  SCTAB nSheet = -1;
3138  if (GetRangeData( nSheet, rSym))
3139  return false;
3141  return false;
3142  }
3143  /* TODO: is there a specific reason why we don't accept an infinity
3144  * value that would raise an error in the interpreter, instead of
3145  * setting the hard error at the token array already? */
3146  SetError( FormulaError::IllegalArgument );
3147  }
3148  maRawToken.SetDouble( fVal );
3149  return true;
3150  }
3151 
3152  double fVal;
3153  sal_uInt32 nIndex = mxSymbols->isEnglish() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0;
3154 
3155  if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal))
3156  return false;
3157 
3159 
3160  // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
3161  // Dates should never be entered directly and automatically converted
3162  // to serial, because the serial would be wrong if null-date changed.
3163  // Usually it wouldn't be accepted anyway because the date separator
3164  // clashed with other separators or operators.
3165  if (nType & (SvNumFormatType::TIME | SvNumFormatType::DATE))
3166  return false;
3167 
3168  if (nType == SvNumFormatType::LOGICAL)
3169  {
3170  if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3171  return false; // Boolean function instead.
3172  }
3173 
3174  if( nType == SvNumFormatType::TEXT )
3175  // HACK: number too big!
3176  SetError( FormulaError::IllegalArgument );
3177  maRawToken.SetDouble( fVal );
3178  return true;
3179 }
3180 
3182 {
3183  if ( cSymbol[0] != '"' )
3184  return false;
3185  const sal_Unicode* p = cSymbol+1;
3186  while ( *p )
3187  p++;
3188  sal_Int32 nLen = sal::static_int_cast<sal_Int32>( p - cSymbol - 1 );
3189  if (!nLen || cSymbol[nLen] != '"')
3190  return false;
3191  svl::SharedString aSS = rDoc.GetSharedStringPool().intern(OUString(cSymbol+1, nLen-1));
3193  return true;
3194 }
3195 
3196 bool ScCompiler::IsPredetectedErrRefReference( const OUString& rName, const OUString* pErrRef )
3197 {
3198  switch (mnPredetectedReference)
3199  {
3200  case 1:
3201  return IsSingleReference( rName, pErrRef);
3202  case 2:
3203  return IsDoubleReference( rName, pErrRef);
3204  default:
3205  return false;
3206  }
3207 }
3208 
3209 bool ScCompiler::IsPredetectedReference( const OUString& rName )
3210 {
3211  // Speedup documents with lots of broken references, e.g. sheet deleted.
3212  // It could also be a broken invalidated reference that contains #REF!
3213  // (but is not equal to), which we wrote prior to ODFF and also to ODFF
3214  // between 2013 and 2016 until 5.1.4
3215  const OUString aErrRef("#REF!"); // not localized in ODFF
3216  sal_Int32 nPos = rName.indexOf( aErrRef);
3217  if (nPos != -1)
3218  {
3219  /* TODO: this may be enhanced by reusing scan information from
3220  * NextSymbol(), the positions of quotes and special characters found
3221  * there for $'sheet'.A1:... could be stored in a vector. We don't
3222  * fully rescan here whether found positions are within single quotes
3223  * for performance reasons. This code does not check for possible
3224  * occurrences of insane "valid" sheet names like
3225  * 'haha.#REF!1fooledyou' and will generate an error on such. */
3226  if (nPos == 0)
3227  {
3228  // Per ODFF the correct string for a reference error is just #REF!,
3229  // so pass it on.
3230  if (rName.getLength() == 5)
3231  return IsErrorConstant( rName);
3232  // #REF!.AB42 or #REF!42 or #REF!#REF!
3233  return IsPredetectedErrRefReference( rName, &aErrRef);
3234  }
3235  sal_Unicode c = rName[nPos-1]; // before #REF!
3236  if ('$' == c)
3237  {
3238  if (nPos == 1)
3239  {
3240  // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
3241  return IsPredetectedErrRefReference( rName, &aErrRef);
3242  }
3243  c = rName[nPos-2]; // before $#REF!
3244  }
3245  sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF!
3246  switch (c)
3247  {
3248  case '.':
3249  if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
3250  {
3251  // sheet.#REF!42 or sheet.#REF!#REF!
3252  return IsPredetectedErrRefReference( rName, &aErrRef);
3253  }
3254  break;
3255  case ':':
3256  if (mnPredetectedReference > 1 &&
3257  ('.' == c2 || '$' == c2 || '#' == c2 ||
3258  ('0' <= c2 && c2 <= '9')))
3259  {
3260  // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
3261  return IsPredetectedErrRefReference( rName, &aErrRef);
3262  }
3263  break;
3264  default:
3265  if (rtl::isAsciiAlpha(c) &&
3266  ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
3267  {
3268  // AB#REF!: or AB#REF!
3269  return IsPredetectedErrRefReference( rName, &aErrRef);
3270  }
3271  }
3272  }
3273  switch (mnPredetectedReference)
3274  {
3275  case 1:
3276  return IsSingleReference( rName);
3277  case 2:
3278  return IsDoubleReference( rName);
3279  }
3280  return false;
3281 }
3282 
3283 bool ScCompiler::IsDoubleReference( const OUString& rName, const OUString* pErrRef )
3284 {
3285  ScRange aRange( aPos, aPos );
3286  const ScAddress::Details aDetails( pConv->meConv, aPos );
3287  ScAddress::ExternalInfo aExtInfo;
3288  ScRefFlags nFlags = aRange.Parse( rName, rDoc, aDetails, &aExtInfo, &maExternalLinks, pErrRef );
3289  if( nFlags & ScRefFlags::VALID )
3290  {
3291  ScComplexRefData aRef;
3292  aRef.InitRange( aRange );
3293  aRef.Ref1.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3294  aRef.Ref1.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3295  aRef.Ref1.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3296  if ( !(nFlags & ScRefFlags::TAB_VALID) )
3297  aRef.Ref1.SetTabDeleted( true ); // #REF!
3298  aRef.Ref1.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3299  aRef.Ref2.SetColRel( (nFlags & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO );
3300  aRef.Ref2.SetRowRel( (nFlags & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO );
3301  aRef.Ref2.SetTabRel( (nFlags & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO );
3302  if ( !(nFlags & ScRefFlags::TAB2_VALID) )
3303  aRef.Ref2.SetTabDeleted( true ); // #REF!
3304  aRef.Ref2.SetFlag3D( ( nFlags & ScRefFlags::TAB2_3D ) != ScRefFlags::ZERO );
3305  aRef.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
3306  if (aExtInfo.mbExternal)
3307  {
3309  const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3311  aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3312  maExternalFiles.push_back(aExtInfo.mnFileId);
3313  }
3314  else
3315  {
3317  }
3318  }
3319 
3320  return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3321 }
3322 
3323 bool ScCompiler::IsSingleReference( const OUString& rName, const OUString* pErrRef )
3324 {
3326  mnCurrentSheetTab = -1;
3327  ScAddress aAddr( aPos );
3328  const ScAddress::Details aDetails( pConv->meConv, aPos );
3329  ScAddress::ExternalInfo aExtInfo;
3330  ScRefFlags nFlags = aAddr.Parse( rName, rDoc, aDetails,
3331  &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos, pErrRef);
3332  // Something must be valid in order to recognize Sheet1.blah or blah.a1
3333  // as a (wrong) reference.
3335  {
3336  // Valid given tab and invalid col or row may indicate a sheet-local
3337  // named expression, bail out early and don't create a reference token.
3338  if (!(nFlags & ScRefFlags::VALID) && mnCurrentSheetEndPos > 0 &&
3339  (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
3340  {
3341  if (aExtInfo.mbExternal)
3342  {
3343  // External names are handled separately.
3345  mnCurrentSheetTab = -1;
3346  }
3347  else
3348  {
3349  mnCurrentSheetTab = aAddr.Tab();
3350  }
3351  return false;
3352  }
3353 
3354  ScSingleRefData aRef;
3355  aRef.InitAddress( aAddr );
3356  aRef.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3357  aRef.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3358  aRef.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3359  aRef.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3360  // the reference is really invalid
3361  if( !( nFlags & ScRefFlags::VALID ) )
3362  {
3363  if( !( nFlags & ScRefFlags::COL_VALID ) )
3364  aRef.SetColDeleted(true);
3365  if( !( nFlags & ScRefFlags::ROW_VALID ) )
3366  aRef.SetRowDeleted(true);
3367  if( !( nFlags & ScRefFlags::TAB_VALID ) )
3368  aRef.SetTabDeleted(true);
3369  nFlags |= ScRefFlags::VALID;
3370  }
3371  aRef.SetAddress(rDoc.GetSheetLimits(), aAddr, aPos);
3372 
3373  if (aExtInfo.mbExternal)
3374  {
3376  const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3378  aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3379  maExternalFiles.push_back(aExtInfo.mnFileId);
3380  }
3381  else
3383  }
3384 
3385  return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3386 }
3387 
3388 bool ScCompiler::IsReference( const OUString& rName, const OUString* pErrRef )
3389 {
3390  // Has to be called before IsValue
3391 
3392  // A later IsNamedRange() relies on these, being set in IsSingleReference()
3393  // if so, reset in all cases.
3395  mnCurrentSheetTab = -1;
3396 
3397  sal_Unicode ch1 = rName[0];
3398  sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0] );
3399  if ( ch1 == cDecSep )
3400  return false;
3401  // Code further down checks only if cDecSep=='.' so simply obtaining the
3402  // alternative decimal separator if it's not is sufficient.
3403  if (cDecSep != '.')
3404  {
3405  cDecSep = ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar();
3406  if ( ch1 == cDecSep )
3407  return false;
3408  }
3409  // Who was that imbecile introducing '.' as the sheet name separator!?!
3410  if ( rtl::isAsciiDigit( ch1 ) && pConv->getSpecialSymbol( Convention::SHEET_SEPARATOR) == '.' )
3411  {
3412  // Numerical sheet name is valid.
3413  // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
3414  // Don't create a #REF! of values. But also do not bail out on
3415  // something like 3:3, meaning entire row 3.
3416  do
3417  {
3418  const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.');
3419  if ( nPos == -1 )
3420  {
3421  if (ScGlobal::FindUnquoted( rName, ':') != -1)
3422  break; // may be 3:3, continue as usual
3423  return false;
3424  }
3425  sal_Unicode const * const pTabSep = rName.getStr() + nPos;
3426  sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
3427  if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) )
3428  return false;
3429  if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
3430  && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & ScCharFlags::ValueExp) )
3431  {
3432  // If it is an 1.E2 expression check if "1" is an existent sheet
3433  // name. If so, a desired value 1.E2 would have to be entered as
3434  // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
3435  // require numerical sheet names always being entered quoted, which
3436  // is not desirable (too many 1999, 2000, 2001 sheets in use).
3437  // Furthermore, XML files created with versions prior to SRC640e
3438  // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
3439  // and would produce wrong formulas if the conditions here are met.
3440  // If you can live with these restrictions you may remove the
3441  // check and return an unconditional FALSE.
3442  OUString aTabName( rName.copy( 0, nPos ) );
3443  SCTAB nTab;
3444  if ( !rDoc.GetTable( aTabName, nTab ) )
3445  return false;
3446  // If sheet "1" exists and the expression is 1.E+2 continue as
3447  // usual, the ScRange/ScAddress parser will take care of it.
3448  }
3449  } while(false);
3450  }
3451 
3452  if (IsSingleReference( rName, pErrRef))
3453  return true;
3454 
3455  // Though the range operator is handled explicitly, when encountering
3456  // something like Sheet1.A:A we will have to treat it as one entity if it
3457  // doesn't pass as single cell reference.
3458  if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
3459  {
3460  if (IsDoubleReference( rName, pErrRef))
3461  return true;
3462  // Now try with a symbol up to the range operator, rewind source
3463  // position.
3464  assert(mnRangeOpPosInSymbol < MAXSTRLEN); // We should have caught the maldoers.
3465  if (mnRangeOpPosInSymbol >= MAXSTRLEN) // TODO: this check and return
3466  return false; // can be removed when sure.
3467  sal_Int32 nLen = mnRangeOpPosInSymbol;
3468  while (cSymbol[++nLen])
3469  ;
3471  nSrcPos -= (nLen - mnRangeOpPosInSymbol);
3472  mnRangeOpPosInSymbol = -1;
3473  mbRewind = true;
3474  return true; // end all checks
3475  }
3476  else
3477  {
3478  switch (pConv->meConv)
3479  {
3480  case FormulaGrammar::CONV_XL_A1:
3481  case FormulaGrammar::CONV_XL_OOX:
3482  // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel
3483  // sickness, mnRangeOpPosInSymbol did not catch the range
3484  // operator as it is within a quoted name.
3485  if (rName[0] != '\'')
3486  return false; // Document name has to be single quoted.
3487  [[fallthrough]];
3488  case FormulaGrammar::CONV_XL_R1C1:
3489  // C2 or C[1] are valid entire column references.
3490  if (IsDoubleReference( rName, pErrRef))
3491  return true;
3492  break;
3493  default:
3494  ; // nothing
3495  }
3496  }
3497  return false;
3498 }
3499 
3500 bool ScCompiler::IsMacro( const OUString& rName )
3501 {
3502 #if !HAVE_FEATURE_SCRIPTING
3503  (void) rName;
3504 
3505  return false;
3506 #else
3507 
3508  // Calling SfxObjectShell::GetBasic() may result in all sort of things
3509  // including obtaining the model and deep down in
3510  // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
3511  // formulas are compiled from a threaded import may result in a deadlock.
3512  // Check first if we actually could acquire it and if not bail out.
3513  /* FIXME: yes, but how ... */
3515  if (!g.isAcquired())
3516  {
3517  SAL_WARN( "sc.core", "ScCompiler::IsMacro - SolarMutex would deadlock, not obtaining Basic");
3518  return false; // bad luck
3519  }
3520 
3521  OUString aName( rName);
3522  StarBASIC* pObj = nullptr;
3523  SfxObjectShell* pDocSh = rDoc.GetDocumentShell();
3524 
3525  try
3526  {
3527  if( pDocSh )//XXX
3528  pObj = pDocSh->GetBasic();
3529  else
3530  pObj = SfxApplication::GetBasic();
3531  }
3532  catch (...)
3533  {
3534  return false;
3535  }
3536 
3537  if (!pObj)
3538  return false;
3539 
3540  // ODFF recommends to store user-defined functions prefixed with "USER.",
3541  // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
3542  // function name so a function "USER.FOO" could not exist, and macro check
3543  // is assigned the lowest priority in function name check.
3544  if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER."))
3545  aName = aName.copy(5);
3546 
3547  SbxMethod* pMeth = static_cast<SbxMethod*>(pObj->Find( aName, SbxClassType::Method ));
3548  if( !pMeth )
3549  {
3550  return false;
3551  }
3552  // It really should be a BASIC function!
3553  if( pMeth->GetType() == SbxVOID
3554  || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
3555  || dynamic_cast<const SbMethod*>( pMeth) == nullptr )
3556  {
3557  return false;
3558  }
3559  maRawToken.SetExternal( aName );
3561  return true;
3562 #endif
3563 }
3564 
3565 const ScRangeData* ScCompiler::GetRangeData( SCTAB& rSheet, const OUString& rUpperName ) const
3566 {
3567  // try local names first
3568  rSheet = aPos.Tab();
3569  const ScRangeName* pRangeName = rDoc.GetRangeName(rSheet);
3570  const ScRangeData* pData = nullptr;
3571  if (pRangeName)
3572  pData = pRangeName->findByUpperName(rUpperName);
3573  if (!pData)
3574  {
3575  pRangeName = rDoc.GetRangeName();
3576  if (pRangeName)
3577  pData = pRangeName->findByUpperName(rUpperName);
3578  if (pData)
3579  rSheet = -1;
3580  }
3581  return pData;
3582 }
3583 
3584 bool ScCompiler::IsNamedRange( const OUString& rUpperName )
3585 {
3586  // IsNamedRange is called only from NextNewToken, with an upper-case string
3587 
3588  SCTAB nSheet = -1;
3589  const ScRangeData* pData = GetRangeData( nSheet, rUpperName);
3590  if (pData)
3591  {
3592  maRawToken.SetName( nSheet, pData->GetIndex());
3593  return true;
3594  }
3595 
3596  // Sheet-local name with sheet specified.
3597  if (mnCurrentSheetEndPos > 0 && mnCurrentSheetTab >= 0)
3598  {
3599  OUString aName( rUpperName.copy( mnCurrentSheetEndPos));
3600  const ScRangeName* pRangeName = rDoc.GetRangeName( mnCurrentSheetTab);
3601  if (pRangeName)
3602  {
3603  pData = pRangeName->findByUpperName(aName);
3604  if (pData)
3605  {
3607  return true;
3608  }
3609  }
3610  }
3611 
3612  return false;
3613 }
3614 
3615 bool ScCompiler::IsExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange )
3616 {
3617  /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
3618  * correctly parses external named references in OOo, as required per RFE
3619  * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
3620  * spec first. Until then don't pretend to support external names that
3621  * wouldn't survive a save and reload cycle, return false instead. */
3622 
3623  rbInvalidExternalNameRange = false;
3624 
3625  if (!pConv)
3626  return false;
3627 
3628  OUString aFile, aName;
3629  if (!pConv->parseExternalName( rSymbol, aFile, aName, rDoc, &maExternalLinks))
3630  return false;
3631 
3632  if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
3633  return false;
3634 
3636  OUString aTmp = aFile;
3637  pRefMgr->convertToAbsName(aTmp);
3638  aFile = aTmp;
3639  sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
3640  if (!pRefMgr->isValidRangeName(nFileId, aName))
3641  {
3642  rbInvalidExternalNameRange = true;
3643  // range name doesn't exist in the source document.
3644  return false;
3645  }
3646 
3647  const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
3648  maRawToken.SetExternalName(nFileId, pRealName ? *pRealName : aTmp);
3649  maExternalFiles.push_back(nFileId);
3650  return true;
3651 }
3652 
3653 bool ScCompiler::IsDBRange( const OUString& rName )
3654 {
3656  const ScDBData* p = rDBs.findByUpperName(rName);
3657  if (!p)
3658  return false;
3659 
3660  maRawToken.SetName( -1, p->GetIndex()); // DB range is always global.
3662  return true;
3663 }
3664 
3665 bool ScCompiler::IsColRowName( const OUString& rName )
3666 {
3667  bool bInList = false;
3668  bool bFound = false;
3669  ScSingleRefData aRef;
3670  OUString aName( rName );
3671  DeQuote( aName );
3672  SCTAB nThisTab = aPos.Tab();
3673  for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
3674  { // first check ranges on this sheet, in case of duplicated names
3675  for ( short jRow=0; jRow<2 && !bInList; jRow++ )
3676  {
3677  ScRangePairList* pRL;
3678  if ( !jRow )
3679  pRL = rDoc.GetColNameRanges();
3680  else
3681  pRL = rDoc.GetRowNameRanges();
3682  for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
3683  {
3684  const ScRangePair & rR = (*pRL)[iPair];
3685  const ScRange& rNameRange = rR.GetRange(0);
3686  if ( jThisTab && (rNameRange.aStart.Tab() > nThisTab ||
3687  nThisTab > rNameRange.aEnd.Tab()) )
3688  continue; // for
3689  ScCellIterator aIter( rDoc, rNameRange );
3690  for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next())
3691  {
3692  // Don't crash if cell (via CompileNameFormula) encounters
3693  // a formula cell without code and
3694  // HasStringData/Interpret/Compile is executed and all that
3695  // recursively...
3696  // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3697  CellType eType = aIter.getType();
3698  bool bOk = false;
3699  if (eType == CELLTYPE_FORMULA)
3700  {
3701  ScFormulaCell* pFC = aIter.getFormulaCell();
3702  bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3703  }
3704  else
3705  bOk = true;
3706 
3707  if (bOk && aIter.hasString())
3708  {
3709  OUString aStr = aIter.getString();
3710  if ( ScGlobal::GetTransliteration().isEqual( aStr, aName ) )
3711  {
3712  aRef.InitFlags();
3713  if ( !jRow )
3714  aRef.SetColRel( true ); // ColName
3715  else
3716  aRef.SetRowRel( true ); // RowName
3717  aRef.SetAddress(rDoc.GetSheetLimits(), aIter.GetPos(), aPos);
3718  bInList = bFound = true;
3719  }
3720  }
3721  }
3722  }
3723  }
3724  }
3725  if ( !bInList && rDoc.GetDocOptions().IsLookUpColRowNames() )
3726  { // search in current sheet
3727  tools::Long nDistance = 0, nMax = 0;
3728  tools::Long nMyCol = static_cast<tools::Long>(aPos.Col());
3729  tools::Long nMyRow = static_cast<tools::Long>(aPos.Row());
3730  bool bTwo = false;
3731  ScAddress aOne( 0, 0, aPos.Tab() );
3732  ScAddress aTwo( rDoc.MaxCol(), rDoc.MaxRow(), aPos.Tab() );
3733 
3734  ScAutoNameCache* pNameCache = rDoc.GetAutoNameCache();
3735  if ( pNameCache )
3736  {
3737  // use GetNameOccurrences to collect all positions of aName on the sheet
3738  // (only once), similar to the outer part of the loop in the "else" branch.
3739 
3740  const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() );
3741 
3742  // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3743  // The order of addresses in the vector is the same as from ScCellIterator.
3744 
3745  for ( const ScAddress& aAddress : rAddresses )
3746  {
3747  if ( bFound )
3748  { // stop if everything else is further away
3749  if ( nMax < static_cast<tools::Long>(aAddress.Col()) )
3750  break; // aIter
3751  }
3752  if ( aAddress != aPos )
3753  {
3754  // same treatment as in isEqual case below
3755 
3756  SCCOL nCol = aAddress.Col();
3757  SCROW nRow = aAddress.Row();
3758  tools::Long nC = nMyCol - nCol;
3759  tools::Long nR = nMyRow - nRow;
3760  if ( bFound )
3761  {
3762  tools::Long nD = nC * nC + nR * nR;
3763  if ( nD < nDistance )
3764  {
3765  if ( nC < 0 || nR < 0 )
3766  { // right or below
3767  bTwo = true;
3768  aTwo.Set( nCol, nRow, aAddress.Tab() );
3769  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3770  nDistance = nD;
3771  }
3772  else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
3773  {
3774  // upper left, only if not further up than the
3775  // current entry and nMyRow is below (CellIter
3776  // runs column-wise)
3777  bTwo = false;
3778  aOne.Set( nCol, nRow, aAddress.Tab() );
3779  nMax = std::max( nMyCol + nC, nMyRow + nR );
3780  nDistance = nD;
3781  }
3782  }
3783  }
3784  else
3785  {
3786  aOne.Set( nCol, nRow, aAddress.Tab() );
3787  nDistance = nC * nC + nR * nR;
3788  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3789 
3790  }
3791  bFound = true;
3792  }
3793  }
3794  }
3795  else
3796  {
3797  ScCellIterator aIter( rDoc, ScRange( aOne, aTwo ) );
3798  for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3799  {
3800  if ( bFound )
3801  { // stop if everything else is further away
3802  if ( nMax < static_cast<tools::Long>(aIter.GetPos().Col()) )
3803  break; // aIter
3804  }
3805  CellType eType = aIter.getType();
3806  bool bOk = false;
3807  if (eType == CELLTYPE_FORMULA)
3808  {
3809  ScFormulaCell* pFC = aIter.getFormulaCell();
3810  bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3811  }
3812  else
3813  bOk = true;
3814 
3815  if (bOk && aIter.hasString())
3816  {
3817  OUString aStr = aIter.getString();
3818  if ( ScGlobal::GetTransliteration().isEqual( aStr, aName ) )
3819  {
3820  SCCOL nCol = aIter.GetPos().Col();
3821  SCROW nRow = aIter.GetPos().Row();
3822  tools::Long nC = nMyCol - nCol;
3823  tools::Long nR = nMyRow - nRow;
3824  if ( bFound )
3825  {
3826  tools::Long nD = nC * nC + nR * nR;
3827  if ( nD < nDistance )
3828  {
3829  if ( nC < 0 || nR < 0 )
3830  { // right or below
3831  bTwo = true;
3832  aTwo.Set( nCol, nRow, aIter.GetPos().Tab() );
3833  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3834  nDistance = nD;
3835  }
3836  else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
3837  {
3838  // upper left, only if not further up than the
3839  // current entry and nMyRow is below (CellIter
3840  // runs column-wise)
3841  bTwo = false;
3842  aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3843  nMax = std::max( nMyCol + nC, nMyRow + nR );
3844  nDistance = nD;
3845  }
3846  }
3847  }
3848  else
3849  {
3850  aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3851  nDistance = nC * nC + nR * nR;
3852  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3853  }
3854  bFound = true;
3855  }
3856  }
3857  }
3858  }
3859 
3860  if ( bFound )
3861  {
3862  ScAddress aAdr;
3863  if ( bTwo )
3864  {
3865  if ( nMyCol >= static_cast<tools::Long>(aOne.Col()) && nMyRow >= static_cast<tools::Long>(aOne.Row()) )
3866  aAdr = aOne; // upper left takes precedence
3867  else
3868  {
3869  if ( nMyCol < static_cast<tools::Long>(aOne.Col()) )
3870  { // two to the right
3871  if ( nMyRow >= static_cast<tools::Long>(aTwo.Row()) )
3872  aAdr = aTwo; // directly right
3873  else
3874  aAdr = aOne;
3875  }
3876  else
3877  { // two below or below and right, take the nearest
3878  tools::Long nC1 = nMyCol - aOne.Col();
3879  tools::Long nR1 = nMyRow - aOne.Row();
3880  tools::Long nC2 = nMyCol - aTwo.Col();
3881  tools::Long nR2 = nMyRow - aTwo.Row();
3882  if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3883  aAdr = aOne;
3884  else
3885  aAdr = aTwo;
3886  }
3887  }
3888  }
3889  else
3890  aAdr = aOne;
3891  aRef.InitAddress( aAdr );
3892  if ( (aAdr.Row() != rDoc.MaxRow() && rDoc.HasStringData(
3893  aAdr.Col(), aAdr.Row() + 1, aAdr.Tab()))
3894  || (aAdr.Row() != 0 && rDoc.HasStringData(
3895  aAdr.Col(), aAdr.Row() - 1, aAdr.Tab())))
3896  aRef.SetRowRel( true ); // RowName
3897  else
3898  aRef.SetColRel( true ); // ColName
3899  aRef.SetAddress(rDoc.GetSheetLimits(), aAdr, aPos);
3900  }
3901  }
3902  if ( bFound )
3903  {
3906  return true;
3907  }
3908  else
3909  return false;
3910 }
3911 
3912 bool ScCompiler::IsBoolean( const OUString& rName )
3913 {
3914  OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName ) );
3915  if( iLook != mxSymbols->getHashMap().end() &&
3916  ((*iLook).second == ocTrue ||
3917  (*iLook).second == ocFalse) )
3918  {
3919  maRawToken.SetOpCode( (*iLook).second );
3920  return true;
3921  }
3922  else
3923  return false;
3924 }
3925 
3926 bool ScCompiler::IsErrorConstant( const OUString& rName ) const
3927 {
3928  FormulaError nError = GetErrorConstant( rName);
3929  if (nError != FormulaError::NONE)
3930  {
3931  maRawToken.SetErrorConstant( nError);
3932  return true;
3933  }
3934  else
3935  return false;
3936 }
3937 
3938 bool ScCompiler::IsTableRefItem( const OUString& rName ) const
3939 {
3940  bool bItem = false;
3941  OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
3942  if (iLook != mxSymbols->getHashMap().end())
3943  {
3944  // Only called when there actually is a current TableRef, hence
3945  // accessing maTableRefs.back() is safe.
3946  ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3947  assert(p); // not a ScTableRefToken can't be
3948 
3949  switch ((*iLook).second)
3950  {
3951  case ocTableRefItemAll:
3952  bItem = true;
3954  break;
3955  case ocTableRefItemHeaders:
3956  bItem = true;
3958  break;
3959  case ocTableRefItemData:
3960  bItem = true;
3962  break;
3963  case ocTableRefItemTotals:
3964  bItem = true;
3966  break;
3967  case ocTableRefItemThisRow:
3968  bItem = true;
3970  break;
3971  default:
3972  ;
3973  }
3974  if (bItem)
3975  maRawToken.SetOpCode( (*iLook).second );
3976  }
3977  return bItem;
3978 }
3979 
3980 namespace {
3981 OUString unescapeTableRefColumnSpecifier( const OUString& rStr )
3982 {
3983  // '#', '[', ']' and '\'' are escaped with '\''
3984 
3985  if (rStr.indexOf( '\'' ) < 0)
3986  return rStr;
3987 
3988  const sal_Int32 n = rStr.getLength();
3989  OUStringBuffer aBuf( n );
3990  const sal_Unicode* p = rStr.getStr();
3991  const sal_Unicode* const pStop = p + n;
3992  bool bEscaped = false;
3993  for ( ; p < pStop; ++p)
3994  {
3995  const sal_Unicode c = *p;
3996  if (bEscaped)
3997  {
3998  aBuf.append( c );
3999  bEscaped = false;
4000  }
4001  else if (c == '\'')
4002  bEscaped = true; // unescaped escaping '\''
4003  else
4004  aBuf.append( c );
4005  }
4006  return aBuf.makeStringAndClear();
4007 }
4008 }
4009 
4010 bool ScCompiler::IsTableRefColumn( const OUString& rName ) const
4011 {
4012  // Only called when there actually is a current TableRef, hence
4013  // accessing maTableRefs.back() is safe.
4014  ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
4015  assert(p); // not a ScTableRefToken can't be
4016 
4018  if (!pDBData)
4019  return false;
4020 
4021  OUString aName( unescapeTableRefColumnSpecifier( rName));
4022 
4023  ScRange aRange;
4024  pDBData->GetArea( aRange);
4025  aRange.aEnd.SetTab( aRange.aStart.Tab());
4026  aRange.aEnd.SetRow( aRange.aStart.Row());
4027 
4028  // Prefer the stored internal table column name, which is also needed for
4029  // named expressions during document load time when cell content isn't
4030  // available yet. Also, avoiding a possible calculation step in case the
4031  // header cell is a formula cell is "a good thing".
4032  sal_Int32 nOffset = pDBData->GetColumnNameOffset( aName);
4033  if (nOffset >= 0)
4034  {
4035  // This is sneaky... we always use the top row of the database range,
4036  // regardless of whether it is a header row or not. Code evaluating
4037  // this reference must take that into account and may have to act
4038  // differently if it is a header-less table. Which are two places,
4039  // HandleTableRef() (no change necessary there) and
4040  // CreateStringFromSingleRef() (must not fallback to cell lookup).
4041  ScSingleRefData aRef;
4042  ScAddress aAdr( aRange.aStart);
4043  aAdr.IncCol( nOffset);
4044  aRef.InitAddress( aAdr);
4046  return true;
4047  }
4048 
4049  if (pDBData->HasHeader())
4050  {
4051  // Quite similar to IsColRowName() but limited to one row of headers.
4052  ScCellIterator aIter( rDoc, aRange);
4053  for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
4054  {
4055  CellType eType = aIter.getType();
4056  bool bOk = false;
4057  if (eType == CELLTYPE_FORMULA)
4058  {
4059  ScFormulaCell* pFC = aIter.getFormulaCell();
4060  bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
4061  }
4062  else
4063  bOk = true;
4064 
4065  if (bOk && aIter.hasString())
4066  {
4067  OUString aStr = aIter.getString();
4068  if (ScGlobal::GetTransliteration().isEqual( aStr, aName))
4069  {
4070  // If this is successful and the internal column name
4071  // lookup was not, it may be worth a warning.
4072  SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup");
4073 
4074  /* XXX NOTE: we could init the column as relative so copying a
4075  * formula across columns would point to the relative column,
4076  * but do it absolute because:
4077  * a) it makes the reference work in named expressions without
4078  * having to distinguish
4079  * b) Excel does it the same. */
4080  ScSingleRefData aRef;
4081  aRef.InitAddress( aIter.GetPos());
4083  return true;
4084  }
4085  }
4086  }
4087  }
4088 
4089  return false;
4090 }
4091 
4093 {
4094  assert(mbJumpCommandReorder);
4095  bAutoCorrect = bVal;
4096  mbStopOnError = !bVal;
4097 }
4098 
4100 {
4101  sal_Int32 nPos = aCorrectedSymbol.getLength();
4102  if ( !nPos )
4103  return;
4104 
4105  nPos--;
4106  const sal_Unicode cQuote = '\"';
4107  const sal_Unicode cx = 'x';
4108  const sal_Unicode cX = 'X';
4109  sal_Unicode c1 = aCorrectedSymbol[0];
4110  sal_Unicode c2 = aCorrectedSymbol[nPos];
4111  sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0;
4112  if ( c1 == cQuote && c2 != cQuote )
4113  { // "...
4114  // What's not a word doesn't belong to it.
4115  // Don't be pedantic: c < 128 should be sufficient here.
4116  while ( nPos && ((aCorrectedSymbol[nPos] < 128) &&
4119  nPos--;
4120  if ( nPos == MAXSTRLEN - 1 )
4121  aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, OUString(cQuote) ); // '"' the MAXSTRLENth character
4122  else
4123  aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, OUString(cQuote) );
4124  bCorrected = true;
4125  }
4126  else if ( c1 != cQuote && c2 == cQuote )
4127  { // ..."
4128  aCorrectedSymbol = OUStringChar(cQuote) + aCorrectedSymbol;
4129  bCorrected = true;
4130  }
4131  else if ( nPos == 0 && (c1 == cx || c1 == cX) )
4132  { // x => *
4133  aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
4134  bCorrected = true;
4135  }
4136  else if ( (GetCharTableFlags( c1, 0 ) & ScCharFlags::CharValue)
4137  && (GetCharTableFlags( c2, c2p ) & ScCharFlags::CharValue) )
4138  {
4139  if ( aCorrectedSymbol.indexOf(cx) >= 0 ) // At least two tokens separated by cx
4140  { // x => *
4141  sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4142  aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cx), OUStringChar(c));
4143  bCorrected = true;
4144  }
4145  if ( aCorrectedSymbol.indexOf(cX) >= 0 ) // At least two tokens separated by cX
4146  { // X => *
4147  sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4148  aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cX), OUStringChar(c));
4149  bCorrected = true;
4150  }
4151  }
4152  else
4153  {
4154  OUString aSymbol( aCorrectedSymbol );
4155  OUString aDoc;
4156  if ( aSymbol[0] == '\'' )
4157  {
4158  sal_Int32 nPosition = aSymbol.indexOf( "'#" );
4159  if (nPosition != -1)
4160  { // Split off 'Doc'#, may be d:\... or whatever
4161  aDoc = aSymbol.copy(0, nPosition + 2);
4162  aSymbol = aSymbol.copy(nPosition + 2);
4163  }
4164  }
4165  sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':');
4166  bool bColons;
4167  if ( nRefs > 2 )
4168  { // duplicated or too many ':'? B:2::C10 => B2:C10
4169  bColons = true;
4170  sal_Int32 nIndex = 0;
4171  OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) );
4172  sal_Int32 nLen1 = aTmp1.getLength();
4173  OUStringBuffer aSym;
4174  OUString aTmp2;
4175  bool bLastAlp = true;
4176  sal_Int32 nStrip = 0;
4177  sal_Int32 nCount = nRefs;
4178  for ( sal_Int32 j=1; j<nCount; j++ )
4179  {
4180  aTmp2 = aSymbol.getToken( 0, ':', nIndex );
4181  sal_Int32 nLen2 = aTmp2.getLength();
4182  if ( nLen1 || nLen2 )
4183  {
4184  if ( nLen1 )
4185  {
4186  aSym.append(aTmp1);
4187  bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
4188  }
4189  if ( nLen2 )
4190  {
4191  bool bNextNum = CharClass::isAsciiNumeric( aTmp2 );
4192  if ( bLastAlp == bNextNum && nStrip < 1 )
4193  {
4194  // Must be alternating number/string, only
4195  // strip within a reference.
4196  nRefs--;
4197  nStrip++;
4198  }
4199  else
4200  {
4201  if ( !aSym.isEmpty() && aSym[aSym.getLength()-1] != ':')
4202  aSym.append(":");
4203  nStrip = 0;
4204  }
4205  bLastAlp = !bNextNum;
4206  }
4207  else
4208  { // ::
4209  nRefs--;
4210  if ( nLen1 )
4211  { // B10::C10 ? append ':' on next round
4212  if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
4213  nStrip++;
4214  }
4215  }
4216  aTmp1 = aTmp2;
4217  nLen1 = nLen2;
4218  }
4219  else
4220  nRefs--;
4221  }
4222  aSymbol = aSym.makeStringAndClear() + aTmp1;
4223  }
4224  else
4225  bColons = false;
4226  if ( nRefs && nRefs <= 2 )
4227  { // reference twisted? 4A => A4 etc.
4228  OUString aTab[2], aRef[2];
4229  const ScAddress::Details aDetails( pConv->meConv, aPos );
4230  if ( nRefs == 2 )
4231  {
4232  sal_Int32 nIdx{ 0 };
4233  aRef[0] = aSymbol.getToken( 0, ':', nIdx );
4234  aRef[1] = aSymbol.getToken( 0, ':', nIdx );
4235  }
4236  else
4237  aRef[0] = aSymbol;
4238 
4239  bool bChanged = false;
4240  bool bOk = true;
4242  for ( int j=0; j<nRefs; j++ )
4243  {
4244  sal_Int32 nTmp = 0;
4245  sal_Int32 nDotPos = -1;
4246  while ( (nTmp = aRef[j].indexOf( '.', nTmp )) != -1 )
4247  nDotPos = nTmp++; // the last one counts
4248  if ( nDotPos != -1 )
4249  {
4250  aTab[j] = aRef[j].copy( 0, nDotPos + 1 ); // with '.'
4251  aRef[j] = aRef[j].copy( nDotPos + 1 );
4252  }
4253  OUString aOld( aRef[j] );
4254  OUStringBuffer aStr2;
4255  const sal_Unicode* p = aRef[j].getStr();
4256  while ( *p && rtl::isAsciiDigit( *p ) )
4257  aStr2.append(*p++);
4258  aRef[j] = OUString( p );
4259  aRef[j] += aStr2.makeStringAndClear();
4260  if ( bColons || aRef[j] != aOld )
4261  {
4262  bChanged = true;
4263  ScAddress aAdr;
4264  bOk &= ((aAdr.Parse( aRef[j], rDoc, aDetails ) & nMask) == nMask);
4265  }
4266  }
4267  if ( bChanged && bOk )
4268  {
4269  aCorrectedSymbol = aDoc;
4270  aCorrectedSymbol += aTab[0];
4271  aCorrectedSymbol += aRef[0];
4272  if ( nRefs == 2 )
4273  {
4274  aCorrectedSymbol += ":";
4275  aCorrectedSymbol += aTab[1];
4276  aCorrectedSymbol += aRef[1];
4277  }
4278  bCorrected = true;
4279  }
4280  }
4281  }
4282 }
4283 
4284 bool ScCompiler::ToUpperAsciiOrI18nIsAscii( OUString& rUpper, const OUString& rOrg ) const
4285 {
4286  if (FormulaGrammar::isODFF( meGrammar ))
4287  {
4288  // ODFF has a defined set of English function names, avoid i18n
4289  // overhead.
4290  rUpper = rOrg.toAsciiUpperCase();
4291  return true;
4292  }
4293  else
4294  {
4295  // One of localized or English.
4296  rUpper = pCharClass->uppercase(rOrg);
4297  return false;
4298  }
4299 }
4300 
4301 bool ScCompiler::NextNewToken( bool bInArray )
4302 {
4303  if (!maPendingOpCodes.empty())
4304  {
4306  maPendingOpCodes.pop();
4307  return true;
4308  }
4309 
4310  bool bAllowBooleans = bInArray;
4311  const std::vector<Whitespace> & vSpaces = NextSymbol(bInArray);
4312 
4313  if (!cSymbol[0])
4314  {
4315  if (nSrcPos < aFormula.getLength())
4316  {
4317  // Nothing could be parsed, remainder as bad string.
4318  // NextSymbol() must had set an error for this.
4319  assert( pArr->GetCodeError() != FormulaError::NONE);
4320  const OUString aBad( aFormula.copy( nSrcPos));
4322  maRawToken.SetString( aSS.getData(), aSS.getDataIgnoreCase());
4324  nSrcPos = aFormula.getLength();
4325  // Add bad string as last token.
4326  return true;
4327  }
4328  return false;
4329  }
4330 
4331  if (!vSpaces.empty())
4332  {
4333  ScRawToken aToken;
4334  for (const auto& rSpace : vSpaces)
4335  {
4336  if (rSpace.cChar == 0x20)
4337  {
4338  // For now keep this a FormulaByteToken for the nasty
4339  // significant whitespace intersection. This probably can be
4340  // changed to a FormulaSpaceToken but then other places may
4341  // need to be adapted.
4342  aToken.SetOpCode( ocSpaces );
4343  aToken.sbyte.cByte = static_cast<sal_uInt8>( std::min<sal_Int32>(rSpace.nCount, 255) );
4344  }
4345  else
4346  {
4347  aToken.SetOpCode( ocWhitespace );
4348  aToken.whitespace.nCount = static_cast<sal_uInt8>( std::min<sal_Int32>(rSpace.nCount, 255) );
4349  aToken.whitespace.cChar = rSpace.cChar;
4350  }
4351  if (!static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ))
4352  {
4353  SetError(FormulaError::CodeOverflow);
4354  return false;
4355  }
4356  }
4357  }
4358 
4359  // Short cut for references when reading ODF to speedup things.
4361  {
4362  OUString aStr( cSymbol);
4363  bool bInvalidExternalNameRange;
4364  if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr, bInvalidExternalNameRange ))
4365  {
4369  }
4370  return true;
4371  }
4372 
4373  if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
4374  !bAutoCorrect )
4375  { // special case to speed up broken [$]#REF documents
4376  /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
4377  * be processed as usual. That would need some special treatment,
4378  * also in NextSymbol() because of possible combinations of
4379  * #REF!.#REF!#REF! parts. In case of reading ODF that is all
4380  * handled by IsPredetectedReference(), this case here remains for
4381  * manual/API input. */
4382  OUString aBad( aFormula.copy( nSrcPos-1 ) );
4383  eLastOp = pArr->AddBad( aBad )->GetOpCode();
4384  return false;
4385  }
4386 
4387  if( IsString() )
4388  return true;
4389 
4390  bool bMayBeFuncName;
4391  bool bAsciiNonAlnum; // operators, separators, ...
4392  if ( cSymbol[0] < 128 )
4393  {
4394  bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
4395  if (!bMayBeFuncName && (cSymbol[0] == '_' && cSymbol[1] == '_') && !utl::ConfigManager::IsFuzzing())
4396  {
4397  bMayBeFuncName = officecfg::Office::Common::Misc::ExperimentalMode::get();
4398  }
4399 
4400  bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
4401  }
4402  else
4403  {
4404  OUString aTmpStr( cSymbol[0] );
4405  bMayBeFuncName = pCharClass->isLetter( aTmpStr, 0 );
4406  bAsciiNonAlnum = false;
4407  }
4408 
4409  // Within a TableRef anything except an unescaped '[' or ']' is an item
4410  // or a column specifier, do not attempt to recognize any other single
4411  // operator there so even [,] or [+] for a single character column
4412  // specifier works. Note that space between two ocTableRefOpen is not
4413  // supported (Table[ [ColumnSpec]]), not only here. Note also that Table[]
4414  // without any item or column specifier is valid.
4415  if (bAsciiNonAlnum && cSymbol[1] == 0 && (eLastOp != ocTableRefOpen || cSymbol[0] == '[' || cSymbol[0] == ']'))
4416  {
4417  // Shortcut for operators and separators that need no further checks or upper.
4418  if (IsOpCode( OUString( cSymbol), bInArray ))
4419  return true;
4420  }
4421 
4422  if ( bMayBeFuncName )
4423  {
4424  // a function name must be followed by a parenthesis
4425  const sal_Unicode* p = aFormula.getStr() + nSrcPos;
4426  while( *p == ' ' )
4427  p++;
4428  bMayBeFuncName = ( *p == '(' );
4429  }
4430 
4431  // Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
4432  // IsReference().
4433 
4434  OUString aUpper;
4435 
4436  do
4437  {
4438  const OUString aOrg( cSymbol );
4439 
4440  // Check for TableRef column specifier first, it may be anything.
4441  if (cSymbol[0] != '#' && !maTableRefs.empty() && maTableRefs.back().mnLevel)
4442  {
4443  if (IsTableRefColumn( aOrg ))
4444  return true;
4445  // Do not attempt to resolve as any other name.
4446  aUpper = aOrg; // for ocBad
4447  break; // do; create ocBad token or set error.
4448  }
4449 
4450  mbRewind = false;
4451  aUpper.clear();
4452  bool bAsciiUpper = false;
4453 
4454  if (bAsciiNonAlnum)
4455  {
4456  bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4457  if (cSymbol[0] == '#')
4458  {
4459  // Check for TableRef item specifiers first.
4460  if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
4461  {
4462  if (IsTableRefItem( aUpper ))
4463  return true;
4464  }
4465 
4466  // This can be either an error constant ...
4467  if (IsErrorConstant( aUpper))
4468  return true;
4469 
4470  // ... or some invalidated reference starting with #REF!
4471  // which is handled after the do loop.
4472 
4473  break; // do; create ocBad token or set error.
4474  }
4475  if (IsOpCode( aUpper, bInArray ))
4476  return true;
4477  }
4478 
4479  if (bMayBeFuncName)
4480  {
4481  if (aUpper.isEmpty())
4482  bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4483  if (IsOpCode( aUpper, bInArray ))
4484  return true;
4485  }
4486 
4487  // Column 'DM' ("Deutsche Mark", German currency) couldn't be
4488  // referred => IsReference() before IsValue().
4489  // Preserve case of file names in external references.
4490  if (IsReference( aOrg ))
4491  {
4492  if (mbRewind) // Range operator, but no direct reference.
4493  continue; // do; up to range operator.
4494  // If a syntactically correct reference was recognized but invalid
4495  // e.g. because of non-existing sheet name => entire reference
4496  // ocBad to preserve input instead of #REF!.A1
4498  {
4499  aUpper = aOrg; // ensure for ocBad
4500  break; // do; create ocBad token or set error.
4501  }
4502  return true;
4503  }
4504 
4505  if (aUpper.isEmpty())
4506  bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4507 
4508  // IsBoolean() before IsValue() to catch inline bools without the kludge
4509  // for inline arrays.
4510  if (bAllowBooleans && IsBoolean( aUpper ))
4511  return true;
4512 
4513  if (IsValue( aUpper ))
4514  return true;
4515 
4516  // User defined names and such do need i18n upper also in ODF.
4517  if (bAsciiUpper || mbCharClassesDiffer)
4518  {
4519  // Use current system locale here because user defined symbols are
4520  // more likely in that localized language than in the formula
4521  // language. This in corner cases needs to continue to work for
4522  // existing documents and environments.
4523  aUpper = ScGlobal::getCharClass().uppercase( aOrg );
4524  }
4525 
4526  if (IsNamedRange( aUpper ))
4527  return true;
4528 
4529  // Compiling a named expression during collecting them in import shall
4530  // not match arbitrary names that otherwise if all named expressions
4531  // were present would be recognized as named expression. Such name will
4532  // flag an error below and will be recompiled in a second step later
4533  // with ScRangeData::CompileUnresolvedXML()
4535  break; // while
4536 
4537  // Preserve case of file names in external references.
4538  bool bInvalidExternalNameRange;
4539  if (IsExternalNamedRange( aOrg, bInvalidExternalNameRange ))
4540  return true;
4541  // Preserve case of file names in external references even when range
4542  // is not valid and previous check failed tdf#89330
4543  if (bInvalidExternalNameRange)
4544  {
4545  // add ocBad but do not lowercase
4549  return true;
4550  }
4551  if (IsDBRange( aUpper ))
4552  return true;
4553  // If followed by '(' (with or without space inbetween) it can not be a
4554  // column/row label. Prevent arbitrary content detection.
4555  if (!bMayBeFuncName && IsColRowName( aUpper ))
4556  return true;
4557  if (bMayBeFuncName && IsMacro( aUpper ))
4558  return true;
4559  if (bMayBeFuncName && IsOpCode2( aUpper ))
4560  return true;
4561 
4562  } while (mbRewind);
4563 
4564  // Last chance: it could be a broken invalidated reference that contains
4565  // #REF! (but is not equal to), which we also wrote to ODFF between 2013
4566  // and 2016 until 5.1.4
4567  OUString aErrRef( mxSymbols->getSymbol( ocErrRef));
4568  if (aUpper.indexOf( aErrRef) >= 0 && IsReference( aUpper, &aErrRef))
4569  return true;
4570 
4572  {
4573  // set an error
4574  SetError( FormulaError::NoName );
4576  return false; // end compilation
4577  }
4578 
4579  // Provide single token information and continue. Do not set an error, that
4580  // would prematurely end compilation. Simple unknown names are handled by
4581  // the interpreter.
4582  aUpper = pCharClass->lowercase( aUpper );
4586  if ( bAutoCorrect )
4588  return true;
4589 }
4590 
4591 void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp )
4592 {
4593  bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
4594  sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
4595  OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
4596  if( pArr->GetLen() == nExpectedCount )
4597  {
4598  FormulaToken** ppTokens = pArr->GetArray();
4599  // string tokens expected, GetString() will assert if token type is wrong
4600  rFormula = ppTokens[0]->GetString().getString();
4601  if( bExternal )
4602  rFormulaNmsp = ppTokens[1]->GetString().getString();
4603  }
4604 }
4605 
4606 namespace {
4607 
4608 class ExternalFileInserter
4609 {
4610  ScAddress maPos;
4611  ScExternalRefManager& mrRefMgr;
4612 public:
4613  ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) :
4614  maPos(rPos), mrRefMgr(rRefMgr) {}
4615 
4616  void operator() (sal_uInt16 nFileId) const
4617  {
4618  mrRefMgr.insertRefCell(nFileId, maPos);
4619  }
4620 };
4621 
4622 }
4623 
4624 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula )
4625 {
4626  OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
4627  if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
4628  SetGrammar( FormulaGrammar::GRAM_PODF );
4629 
4631  pArr = &aArr;
4633  aFormula = comphelper::string::strip(rFormula, ' ');
4634 
4635  nSrcPos = 0;
4636  bCorrected = false;
4637  if ( bAutoCorrect )
4638  {
4639  aCorrectedFormula.clear();
4640  aCorrectedSymbol.clear();
4641  }
4642  sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible
4643  if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4644  {
4645  nSrcPos++;
4646  nForced++;
4647  if ( bAutoCorrect )
4648  aCorrectedFormula += "=";
4649  }
4650  if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4651  {
4652  nSrcPos++;
4653  nForced++;
4654  if ( bAutoCorrect )
4655  aCorrectedFormula += "=";
4656  }
4657  struct FunctionStack
4658  {
4659  OpCode eOp;
4660  short nSep;
4661  };
4662  // FunctionStack only used if PODF or OOXML!
4663  bool bPODF = FormulaGrammar::isPODF( meGrammar);
4664  bool bOOXML = FormulaGrammar::isOOXML( meGrammar);
4665  bool bUseFunctionStack = (bPODF || bOOXML);
4666  const size_t nAlloc = 512;
4667  FunctionStack aFuncs[ nAlloc ];
4668  FunctionStack* pFunctionStack = (bUseFunctionStack && o3tl::make_unsigned(rFormula.getLength()) > nAlloc ?
4669  new FunctionStack[rFormula.getLength()] : &aFuncs[0]);
4670  pFunctionStack[0].eOp = ocNone;
4671  pFunctionStack[0].nSep = 0;
4672  size_t nFunction = 0;
4673  short nBrackets = 0;
4674  bool bInArray = false;
4675  eLastOp = ocOpen;
4676  while( NextNewToken( bInArray ) )
4677  {
4678  const OpCode eOp = maRawToken.GetOpCode();
4679  if (eOp == ocSkip)
4680  continue;
4681 
4682  switch (eOp)
4683  {
4684  case ocOpen:
4685  {
4686  ++nBrackets;
4687  if (bUseFunctionStack)
4688  {
4689  ++nFunction;
4690  pFunctionStack[ nFunction ].eOp = eLastOp;
4691  pFunctionStack[ nFunction ].nSep = 0;
4692  }
4693  }
4694  break;
4695  case ocClose:
4696  {
4697  if( !nBrackets )
4698  {
4699  SetError( FormulaError::PairExpected );
4700  if ( bAutoCorrect )
4701  {
4702  bCorrected = true;
4703  aCorrectedSymbol.clear();
4704  }
4705  }
4706  else
4707  nBrackets--;
4708  if (bUseFunctionStack && nFunction)
4709  --nFunction;
4710  }
4711  break;
4712  case ocSep:
4713  {
4714  if (bUseFunctionStack)
4715  ++pFunctionStack[ nFunction ].nSep;
4716  }
4717  break;
4718  case ocArrayOpen:
4719  {
4720  if( bInArray )
4721  SetError( FormulaError::NestedArray );
4722  else
4723  bInArray = true;
4724  // Don't count following column separator as parameter separator.
4725  if (bUseFunctionStack)
4726  {
4727  ++nFunction;
4728  pFunctionStack[ nFunction ].eOp = eOp;
4729  pFunctionStack[ nFunction ].nSep = 0;
4730  }
4731  }
4732  break;
4733  case ocArrayClose:
4734  {
4735  if( bInArray )
4736  {
4737  bInArray = false;
4738  }
4739  else
4740  {
4741  SetError( FormulaError::PairExpected );
4742  if ( bAutoCorrect )
4743  {
4744  bCorrected = true;
4745  aCorrectedSymbol.clear();
4746  }
4747  }
4748  if (bUseFunctionStack && nFunction)
4749  --nFunction;
4750  }
4751  break;
4752  case ocTableRefOpen:
4753  {
4754  // Don't count following item separator as parameter separator.
4755  if (bUseFunctionStack)
4756  {
4757  ++nFunction;
4758  pFunctionStack[ nFunction ].eOp = eOp;
4759  pFunctionStack[ nFunction ].nSep = 0;
4760  }
4761  }
4762  break;
4763  case ocTableRefClose:
4764  {
4765  if (bUseFunctionStack && nFunction)
4766  --nFunction;
4767  }
4768  break;
4769  case ocColRowName:
4770  case ocColRowNameAuto:
4771  // The current implementation of column / row labels doesn't
4772  // function correctly in grouped cells.
4773  aArr.SetShareable(false);
4774  break;
4775  default:
4776  break;
4777  }
4778  if ((eLastOp != ocOpen || eOp != ocClose) &&
4779  (eLastOp == ocOpen ||
4780  eLastOp == ocSep ||
4781  eLastOp == ocArrayRowSep ||
4782  eLastOp == ocArrayColSep ||
4783  eLastOp == ocArrayOpen) &&
4784  (eOp == ocSep ||
4785  eOp == ocClose ||
4786  eOp == ocArrayRowSep ||
4787  eOp == ocArrayColSep ||
4788  eOp == ocArrayClose))
4789  {
4790  // TODO: should we check for known functions with optional empty
4791  // args so the correction dialog can do better?
4792  if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
4793  {
4794  SetError(FormulaError::CodeOverflow); break;
4795  }
4796  }
4797  if (bOOXML)
4798  {
4799  // Append a parameter for WEEKNUM, all 1.0
4800  // Function is already closed, parameter count is nSep+1
4801  size_t nFunc = nFunction + 1;
4802  if (eOp == ocClose &&
4803  (pFunctionStack[ nFunc ].eOp == ocWeek && // 2nd week start
4804  pFunctionStack[ nFunc ].nSep == 0))
4805  {
4806  if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4807  !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4808  {
4809  SetError(FormulaError::CodeOverflow); break;
4810  }
4811  }
4812  }
4813  else if (bPODF)
4814  {
4815  /* TODO: for now this is the only PODF adapter. If there were more,
4816  * factor this out. */
4817  // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
4818  if (eOp == ocSep &&
4819  pFunctionStack[ nFunction ].eOp == ocAddress &&
4820  pFunctionStack[ nFunction ].nSep == 3)
4821  {
4822  if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4823  !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4824  {
4825  SetError(FormulaError::CodeOverflow); break;
4826  }
4827  ++pFunctionStack[ nFunction ].nSep;
4828  }
4829  }
4830  FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( maRawToken.CreateToken(rDoc.GetSheetLimits()));
4831  if (!pNewToken && eOp == ocArrayClose && pArr->OpCodeBefore( pArr->GetLen()) == ocArrayClose)
4832  {
4833  // Nested inline array or non-value/non-string in array. The
4834  // original tokens are still in the ScTokenArray and not merged
4835  // into an ScMatrixToken. Set error but keep on tokenizing.
4836  SetError( FormulaError::BadArrayContent);
4837  }
4838  else if (!pNewToken)
4839  {
4840  SetError(FormulaError::CodeOverflow);
4841  break;
4842  }
4843  else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && pNewToken->GetType() == svSingleRef)
4844  {
4845  static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
4846  }
4847  else if (eLastOp == ocDBArea && pNewToken->GetOpCode() == ocTableRefOpen)
4848  {
4849  sal_uInt16 nIdx = pArr->GetLen() - 1;
4850  const FormulaToken* pPrev = pArr->PeekPrev( nIdx);
4851  if (pPrev && pPrev->GetOpCode() == ocDBArea)
4852  {
4853  FormulaToken* pTableRefToken = new ScTableRefToken( pPrev->GetIndex(), ScTableRefToken::TABLE);
4854  maTableRefs.emplace_back( pTableRefToken);
4855  // pPrev may be dead hereafter.
4856  static_cast<ScTokenArray*>(pArr)->ReplaceToken( nIdx, pTableRefToken,
4857  FormulaTokenArray::ReplaceMode::CODE_ONLY);
4858  }
4859  }
4860  switch (eOp)
4861  {
4862  case ocTableRefOpen:
4863  SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
4864  if (maTableRefs.empty())
4865  SetError(FormulaError::Pair);
4866  else
4867  ++maTableRefs.back().mnLevel;
4868  break;
4869  case ocTableRefClose:
4870  SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
4871  if (maTableRefs.empty())
4872  SetError(FormulaError::Pair);
4873  else
4874  {
4875  if (--maTableRefs.back().mnLevel == 0)
4876  maTableRefs.pop_back();
4877  }
4878  break;
4879  default:
4880  break;
4881  }
4883  if ( bAutoCorrect )
4885  }
4886  if ( mbCloseBrackets )
4887  {
4888  if( bInArray )
4889  {
4890  FormulaByteToken aToken( ocArrayClose );
4891  if( !pArr->AddToken( aToken ) )
4892  {
4893  SetError(FormulaError::CodeOverflow);
4894  }
4895  else if ( bAutoCorrect )
4896  aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
4897  }
4898 
4899  if (nBrackets)
4900  {
4901  FormulaToken aToken( svSep, ocClose );
4902  while( nBrackets-- )
4903  {
4904  if( !pArr->AddToken( aToken ) )
4905  {
4906  SetError(FormulaError::CodeOverflow);
4907  break; // while
4908  }
4909  if ( bAutoCorrect )
4910  aCorrectedFormula += mxSymbols->getSymbol(ocClose);
4911  }
4912  }
4913  }
4914  if ( nForced >= 2 )
4916 
4917  if (pFunctionStack != &aFuncs[0])
4918  delete [] pFunctionStack;
4919 
4920  // remember pArr, in case a subsequent CompileTokenArray() is executed.
4921  std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aArr ));
4922  pNew->GenHash();
4923  // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
4924  pArr = pNew.get();
4926 
4927  if (!maExternalFiles.empty())
4928  {
4929  // Remove duplicates, and register all external files found in this cell.
4930  std::sort(maExternalFiles.begin(), maExternalFiles.end());
4931  std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end());
4932  std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *rDoc.GetExternalRefManager()));
4933  maExternalFiles.erase(itEnd, maExternalFiles.end());
4934  }
4935 
4936  return pNew;
4937 }
4938 
4939 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp )
4940 {
4941  OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(),
4942  "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
4943  if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
4944  {
4946  uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
4947  table::CellAddress aReferencePos;
4948  ScUnoConversion::FillApiAddress( aReferencePos, aPos );
4949  uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
4950  ScTokenArray aTokenArray(rDoc);
4951  if( ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, aTokenSeq ) )
4952  {
4953  // remember pArr, in case a subsequent CompileTokenArray() is executed.
4954  std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aTokenArray ));
4955  // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
4956  pArr = pNew.get();
4958  return pNew;
4959  }
4960  }
4961  catch( uno::Exception& )
4962  {
4963  }
4964  // no success - fallback to some internal grammar and hope the best
4965  return CompileString( rFormula );
4966 }
4967 
4969 {
4970  return rDoc.FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex());
4971 }
4972 
4974 {
4975  ScTokenArray* pNew;
4976  const ScRangeData* pRangeData = GetRangeData( *mpToken);
4977  if (pRangeData)
4978  {
4979  FormulaError nErr = pRangeData->GetErrCode();
4980  if( nErr != FormulaError::NONE )
4981  SetError( nErr );
4982  else if (mbJumpCommandReorder)
4983  {
4984  // put named formula into parentheses.
4985  // But only if there aren't any yet, parenthetical
4986  // ocSep doesn't work, e.g. SUM((...;...))
4987  // or if not directly between ocSep/parenthesis,
4988  // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
4989  // in short: if it isn't a self-contained expression.
4992  OpCode eOp1 = (p1 ? p1->GetOpCode() : ocSep);
4993  OpCode eOp2 = (p2 ? p2->GetOpCode() : ocSep);
4994  bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
4995  bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
4996  bool bAddPair = !(bBorder1 && bBorder2);
4997  if ( bAddPair )
4998  {
4999  pNew = new ScTokenArray(rDoc);
5000  pNew->AddOpCode( ocClose );
5001  PushTokenArray( pNew, true );
5002  }
5003  pNew = pRangeData->GetCode()->Clone().release();
5004  pNew->SetFromRangeName( true );
5005  PushTokenArray( pNew, true );
5006  if( pRangeData->HasReferences() )
5007  {
5008  // Relative sheet references in sheet-local named expressions
5009  // shall still point to the same sheet as if used on the
5010  // original sheet, not shifted to the current position where
5011  // they are used.
5012  SCTAB nSheetTab = mpToken->GetSheet();
5013  if (nSheetTab >= 0 && nSheetTab != aPos.Tab())
5014  AdjustSheetLocalNameRelReferences( nSheetTab - aPos.Tab());
5015 
5017  MoveRelWrap();
5018  }
5019  maArrIterator.Reset();
5020  if ( bAddPair )
5021  {
5022  pNew = new ScTokenArray(rDoc);
5023  pNew->AddOpCode( ocOpen );
5024  PushTokenArray( pNew, true );
5025  }
5026  return GetToken();
5027  }
5028  }
5029  else
5030  {
5031  // No ScRangeData for an already compiled token can happen in BIFF .xls
5032  // import if the original range is not present in the document.
5033  pNew = new ScTokenArray(rDoc);
5034  pNew->Add( new FormulaErrorToken( FormulaError::NoName));
5035  PushTokenArray( pNew, true );
5036  return GetToken();
5037  }
5038  return true;
5039 }
5040 
5042 {
5043  // Handle external range names.
5044  switch (_aToken.GetType())
5045  {
5046  case svExternalSingleRef:
5047  case svExternalDoubleRef:
5048  break;
5049  case svExternalName:
5050  {
5052  const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
5053  if (!pFile)
5054  {
5055  SetError(FormulaError::NoName);
5056  return true;
5057  }
5058 
5059  OUString aName = _aToken.GetString().getString();
5061  _aToken.GetIndex(), aName, &aPos);
5062 
5063  if (!xNew)
5064  {
5065  SetError(FormulaError::NoName);
5066  return true;
5067  }
5068 
5069  ScTokenArray* pNew = xNew->Clone().release();
5070  PushTokenArray( pNew, true);
5071  if (FormulaTokenArrayPlainIterator(*pNew).GetNextReference() != nullptr)
5072  {
5074  MoveRelWrap();
5075  }
5076  maArrIterator.Reset();
5077  return GetToken();
5078  }
5079  default:
5080  OSL_FAIL("Wrong type for external reference!");
5081  return false;
5082  }
5083  return true;
5084 }
5085 
5087 {
5088  for ( auto t: pArr->References() )
5089  {
5090  ScSingleRefData& rRef1 = *t->GetSingleRef();
5091  if (rRef1.IsTabRel())
5092  rRef1.IncTab( nDelta);
5093  if ( t->GetType() == svDoubleRef )
5094  {
5095  ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
5096  if (rRef2.IsTabRel())
5097  rRef2.IncTab( nDelta);
5098  }
5099  }
5100 }
5101 
5102 // reference of named range with relative references
5103 
5105 {
5106  for ( auto t: pArr->References() )
5107  {
5108  ScSingleRefData& rRef1 = *t->GetSingleRef();
5109  if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
5110  rRef1.SetRelName( true );
5111  if ( t->GetType() == svDoubleRef )
5112  {
5113  ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
5114  if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
5115  rRef2.SetRelName( true );
5116  }
5117  }
5118 }
5119 
5120 // Wrap-adjust relative references of a RangeName to current position,
5121 // don't call for other token arrays!
5123 {
5124  for ( auto t: pArr->References() )
5125  {
5126  if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )