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