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  const OUString& 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.copy(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  OSL_ENSURE( FormulaGrammar::isSupported( meGrammar),
2029  "ScCompiler::SetRefConvention: unsupported grammar resulting");
2030 }
2031 
2033 {
2034  if( pArr->GetCodeError() == FormulaError::NONE)
2035  pArr->SetCodeError( nError);
2036 }
2037 
2038 static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax )
2039 {
2040  const sal_Unicode* const pStop = pDst + nMax;
2041  while ( pDst < pStop )
2042  {
2043  *pDst++ = *pSrc++;
2044  }
2045  *pDst = 0;
2046  return pDst;
2047 }
2048 
2049 // p1 MUST contain at least n characters, or terminate with NIL.
2050 // p2 MUST pass upper case letters, if any.
2051 // n MUST not be greater than length of p2
2052 static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode* p1, const char* p2, size_t n )
2053 {
2054  for (size_t i=0; i<n; ++i)
2055  {
2056  if (!p1[i])
2057  return false;
2058  if (p1[i] != p2[i])
2059  {
2060  if (p1[i] < 'a' || 'z' < p1[i])
2061  return false; // not a lower case letter
2062  if (p2[i] < 'A' || 'Z' < p2[i])
2063  return false; // not a letter to match
2064  if (p1[i] != p2[i] + 0x20)
2065  return false; // lower case doesn't match either
2066  }
2067  }
2068  return true;
2069 }
2070 
2071 // NextSymbol
2072 
2073 // Parses the formula into separate symbols for further processing.
2074 // XXX NOTE: this is a rough sketch of the original idea, there are other
2075 // states that were added and didn't make it into this table and things are
2076 // more complicated. Use the source, Luke.
2077 
2078 // initial state = GetChar
2079 
2080 // old state | read character | action | new state
2081 //---------------+-------------------+-----------------------+---------------
2082 // GetChar | ;()+-*/^=& | Symbol=char | Stop
2083 // | <> | Symbol=char | GetBool
2084 // | $ letter | Symbol=char | GetWord
2085 // | number | Symbol=char | GetValue
2086 // | " | none | GetString
2087 // | other | none | GetChar
2088 //---------------+-------------------+-----------------------+---------------
2089 // GetBool | => | Symbol=Symbol+char | Stop
2090 // | other | Dec(CharPos) | Stop
2091 //---------------+-------------------+-----------------------+---------------
2092 // GetWord | SepSymbol | Dec(CharPos) | Stop
2093 // | ()+-*/^=<>&~ | |
2094 // | space | Dec(CharPos) | Stop
2095 // | $_:. | |
2096 // | letter, number | Symbol=Symbol+char | GetWord
2097 // | other | error | Stop
2098 //---------------+-------------------+-----------------------+---------------
2099 // GetValue | ;()*/^=<>& | |
2100 // | space | Dec(CharPos) | Stop
2101 // | number E+-%,. | Symbol=Symbol+char | GetValue
2102 // | other | error | Stop
2103 //---------------+-------------------+-----------------------+---------------
2104 // GetString | " | none | Stop
2105 // | other | Symbol=Symbol+char | GetString
2106 //---------------+-------------------+-----------------------+---------------
2107 
2108 sal_Int32 ScCompiler::NextSymbol(bool bInArray)
2109 {
2110  cSymbol[MAXSTRLEN] = 0; // end
2111  sal_Unicode* pSym = cSymbol;
2112  const sal_Unicode* const pStart = aFormula.getStr();
2113  const sal_Unicode* pSrc = pStart + nSrcPos;
2114  bool bi18n = false;
2115  sal_Unicode c = *pSrc;
2116  sal_Unicode cLast = 0;
2117  bool bQuote = false;
2118  mnRangeOpPosInSymbol = -1;
2119  ScanState eState = ssGetChar;
2120  sal_Int32 nSpaces = 0;
2121  sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep);
2122  sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep);
2123  sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep);
2124  sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' : ScGlobal::getLocaleDataPtr()->getNumDecimalSep()[0]);
2125  sal_Unicode cDecSepAlt = (mxSymbols->isEnglish() ? 0 : ScGlobal::getLocaleDataPtr()->getNumDecimalSepAlt().toChar());
2126 
2127  // special symbols specific to address convention used
2130 
2131  int nDecSeps = 0;
2132  bool bAutoIntersection = false;
2133  int nRefInName = 0;
2134  bool bErrorConstantHadSlash = false;
2136  // try to parse simple tokens before calling i18n parser
2137  while ((c != 0) && (eState != ssStop) )
2138  {
2139  pSrc++;
2140  ScCharFlags nMask = GetCharTableFlags( c, cLast );
2141 
2142  // The parameter separator and the array column and row separators end
2143  // things unconditionally if not in string or reference.
2144  if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
2145  {
2146  switch (eState)
2147  {
2148  // these are to be continued
2149  case ssGetString:
2150  case ssSkipString:
2151  case ssGetReference:
2152  case ssSkipReference:
2153  case ssGetTableRefItem:
2154  case ssGetTableRefColumn:
2155  break;
2156  default:
2157  if (eState == ssGetChar)
2158  *pSym++ = c;
2159  else
2160  pSrc--;
2161  eState = ssStop;
2162  }
2163  }
2164 Label_MaskStateMachine:
2165  switch (eState)
2166  {
2167  case ssGetChar :
2168  {
2169  // Order is important!
2170  if (eLastOp == ocTableRefOpen && c != '[' && c != '#' && c != ']')
2171  {
2172  *pSym++ = c;
2173  eState = ssGetTableRefColumn;
2174  }
2175  else if( nMask & ScCharFlags::OdfLabelOp )
2176  {
2177  // '!!' automatic intersection
2178  if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfLabelOp)
2179  {
2180  /* TODO: For now the UI "space operator" is used, this
2181  * could be enhanced using a specialized OpCode to get
2182  * rid of the space ambiguity, which would need some
2183  * places to be adapted though. And we would still need
2184  * to support the ambiguous space operator for UI
2185  * purposes anyway. However, we then could check for
2186  * invalid usage of '!!', which currently isn't
2187  * possible. */
2188  if (!bAutoIntersection)
2189  {
2190  ++pSrc;
2191  nSpaces += 2; // must match the character count
2192  bAutoIntersection = true;
2193  }
2194  else
2195  {
2196  pSrc--;
2197  eState = ssStop;
2198  }
2199  }
2200  else
2201  {
2202  nMask &= ~ScCharFlags::OdfLabelOp;
2203  goto Label_MaskStateMachine;
2204  }
2205  }
2206  else if( nMask & ScCharFlags::OdfNameMarker )
2207  {
2208  // '$$' defined name marker
2209  if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfNameMarker)
2210  {
2211  // both eaten, not added to pSym
2212  ++pSrc;
2213  }
2214  else
2215  {
2216  nMask &= ~ScCharFlags::OdfNameMarker;
2217  goto Label_MaskStateMachine;
2218  }
2219  }
2220  else if( nMask & ScCharFlags::Char )
2221  {
2222  // '[' is a special case in Excel syntax, it can start an
2223  // external reference, ID in OOXML like [1]Sheet1!A1 or
2224  // Excel_A1 [filename]Sheet!A1 or Excel_R1C1
2225  // [filename]Sheet!R1C1 that needs to be scanned
2226  // entirely, or can be ocTableRefOpen, of which the first
2227  // transforms an ocDBArea into an ocTableRef.
2228  if (c == '[' && FormulaGrammar::isExcelSyntax( meGrammar)
2229  && eLastOp != ocDBArea && maTableRefs.empty())
2230  {
2231  nMask &= ~ScCharFlags::Char;
2232  goto Label_MaskStateMachine;
2233  }
2234  else
2235  {
2236  *pSym++ = c;
2237  eState = ssStop;
2238  }
2239  }
2240  else if( nMask & ScCharFlags::OdfLBracket )
2241  {
2242  // eaten, not added to pSym
2243  eState = ssGetReference;
2245  }
2246  else if( nMask & ScCharFlags::CharBool )
2247  {
2248  *pSym++ = c;
2249  eState = ssGetBool;
2250  }
2251  else if( nMask & ScCharFlags::CharValue )
2252  {
2253  *pSym++ = c;
2254  eState = ssGetValue;
2255  }
2256  else if( nMask & ScCharFlags::CharString )
2257  {
2258  *pSym++ = c;
2259  eState = ssGetString;
2260  }
2261  else if( nMask & ScCharFlags::CharErrConst )
2262  {
2263  *pSym++ = c;
2264  if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
2265  eState = ssGetTableRefItem;
2266  else
2267  eState = ssGetErrorConstant;
2268  }
2269  else if( nMask & ScCharFlags::CharDontCare )
2270  {
2271  nSpaces++;
2272  }
2273  else if( nMask & ScCharFlags::CharIdent )
2274  { // try to get a simple ASCII identifier before calling
2275  // i18n, to gain performance during import
2276  *pSym++ = c;
2277  eState = ssGetIdent;
2278  }
2279  else
2280  {
2281  bi18n = true;
2282  eState = ssStop;
2283  }
2284  }
2285  break;
2286  case ssGetIdent:
2287  {
2288  if ( nMask & ScCharFlags::Ident )
2289  { // This catches also $Sheet1.A$1, for example.
2290  if( pSym == &cSymbol[ MAXSTRLEN ] )
2291  {
2292  SetError(FormulaError::StringOverflow);
2293  eState = ssStop;
2294  }
2295  else
2296  *pSym++ = c;
2297  }
2298  else if (c == '#' && lcl_isUnicodeIgnoreAscii( pSrc, "REF!", 4))
2299  {
2300  // Completely ugly means to catch broken
2301  // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts)
2302  // references that were written in ODF named ranges
2303  // (without embracing [] hence no predetected reference)
2304  // and to OOXML and handle them as one symbol.
2305  // Also catches these in UI, so we can process them
2306  // further.
2307  int i = 0;
2308  for ( ; i<5; ++i)
2309  {
2310  if( pSym == &cSymbol[ MAXSTRLEN ] )
2311  {
2312  SetError(FormulaError::StringOverflow);
2313  eState = ssStop;
2314  break; // for
2315  }
2316  else
2317  {
2318  *pSym++ = c;
2319  c = *pSrc++;
2320  }
2321  }
2322  if (i == 5)
2323  c = *((--pSrc)-1); // position last/next character correctly
2324  }
2325  else if (c == ':' && mnRangeOpPosInSymbol < 0)
2326  {
2327  // One range operator may form Sheet1.A:A, which we need to
2328  // pass as one entity to IsReference().
2329  if( pSym == &cSymbol[ MAXSTRLEN ] )
2330  {
2331  SetError(FormulaError::StringOverflow);
2332  eState = ssStop;
2333  }
2334  else
2335  {
2336  mnRangeOpPosInSymbol = pSym - &cSymbol[0];
2337  *pSym++ = c;
2338  }
2339  }
2340  else if ( 128 <= c || '\'' == c )
2341  { // High values need reparsing with i18n,
2342  // single quoted $'sheet' names too (otherwise we'd had to
2343  // implement everything twice).
2344  bi18n = true;
2345  eState = ssStop;
2346  }
2347  else
2348  {
2349  pSrc--;
2350  eState = ssStop;
2351  }
2352  }
2353  break;
2354  case ssGetBool :
2355  {
2356  if( nMask & ScCharFlags::Bool )
2357  {
2358  *pSym++ = c;
2359  eState = ssStop;
2360  }
2361  else
2362  {
2363  pSrc--;
2364  eState = ssStop;
2365  }
2366  }
2367  break;
2368  case ssGetValue :
2369  {
2370  if( pSym == &cSymbol[ MAXSTRLEN ] )
2371  {
2372  SetError(FormulaError::StringOverflow);
2373  eState = ssStop;
2374  }
2375  else if (c == cDecSep || (cDecSepAlt && c == cDecSepAlt))
2376  {
2377  if (++nDecSeps > 1)
2378  {
2379  // reparse with i18n, may be numeric sheet name as well
2380  bi18n = true;
2381  eState = ssStop;
2382  }
2383  else
2384  *pSym++ = c;
2385  }
2386  else if( nMask & ScCharFlags::Value )
2387  *pSym++ = c;
2388  else if( nMask & ScCharFlags::ValueSep )
2389  {
2390  pSrc--;
2391  eState = ssStop;
2392  }
2393  else if (c == 'E' || c == 'e')
2394  {
2395  if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueExp)
2396  *pSym++ = c;
2397  else
2398  {
2399  // reparse with i18n
2400  bi18n = true;
2401  eState = ssStop;
2402  }
2403  }
2404  else if( nMask & ScCharFlags::ValueSign )
2405  {
2406  if (((cLast == 'E') || (cLast == 'e')) &&
2407  (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueValue))
2408  {
2409  *pSym++ = c;
2410  }
2411  else
2412  {
2413  pSrc--;
2414  eState = ssStop;
2415  }
2416  }
2417  else
2418  {
2419  // reparse with i18n
2420  bi18n = true;
2421  eState = ssStop;
2422  }
2423  }
2424  break;
2425  case ssGetString :
2426  {
2427  if( nMask & ScCharFlags::StringSep )
2428  {
2429  if ( !bQuote )
2430  {
2431  if ( *pSrc == '"' )
2432  bQuote = true; // "" => literal "
2433  else
2434  eState = ssStop;
2435  }
2436  else
2437  bQuote = false;
2438  }
2439  if ( !bQuote )
2440  {
2441  if( pSym == &cSymbol[ MAXSTRLEN ] )
2442  {
2443  SetError(FormulaError::StringOverflow);
2444  eState = ssSkipString;
2445  }
2446  else
2447  *pSym++ = c;
2448  }
2449  }
2450  break;
2451  case ssSkipString:
2452  if( nMask & ScCharFlags::StringSep )
2453  eState = ssStop;
2454  break;
2455  case ssGetErrorConstant:
2456  {
2457  // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2458  // BUT, in UI these may have been translated! So don't
2459  // check for ASCII alnum. Note that this construct can't be
2460  // parsed with i18n.
2461  /* TODO: be strict when reading ODFF, check for ASCII alnum
2462  * and proper continuation after '/'. However, even with
2463  * the lax parsing only the error constants we have defined
2464  * as opcode symbols will be recognized and others result
2465  * in ocBad, so the result is actually conformant. */
2466  bool bAdd = true;
2467  if ('?' == c)
2468  eState = ssStop;
2469  else if ('!' == c)
2470  {
2471  // Check if this is #REF! that starts an invalid reference.
2472  // Note we have an implicit '!' here at the end.
2473  if (pSym - &cSymbol[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol, "#REF", 4) &&
2474  (GetCharTableFlags( *pSrc, c) & ScCharFlags::Ident))
2475  eState = ssGetIdent;
2476  else
2477  eState = ssStop;
2478  }
2479  else if ('/' == c)
2480  {
2481  if (!bErrorConstantHadSlash)
2482  bErrorConstantHadSlash = true;
2483  else
2484  {
2485  bAdd = false;
2486  eState = ssStop;
2487  }
2488  }
2489  else if ((nMask & ScCharFlags::WordSep) ||
2490  (c < 128 && !rtl::isAsciiAlphanumeric( c)))
2491  {
2492  bAdd = false;
2493  eState = ssStop;
2494  }
2495  if (!bAdd)
2496  --pSrc;
2497  else
2498  {
2499  if (pSym == &cSymbol[ MAXSTRLEN ])
2500  {
2501  SetError( FormulaError::StringOverflow);
2502  eState = ssStop;
2503  }
2504  else
2505  *pSym++ = c;
2506  }
2507  }
2508  break;
2509  case ssGetTableRefItem:
2510  {
2511  // Scan whatever up to the next ']' closer.
2512  if (c != ']')
2513  {
2514  if( pSym == &cSymbol[ MAXSTRLEN ] )
2515  {
2516  SetError( FormulaError::StringOverflow);
2517  eState = ssStop;
2518  }
2519  else
2520  *pSym++ = c;
2521  }
2522  else
2523  {
2524  --pSrc;
2525  eState = ssStop;
2526  }
2527  }
2528  break;
2529  case ssGetTableRefColumn:
2530  {
2531  // Scan whatever up to the next unescaped ']' closer.
2532  if (c != ']' || cLast == '\'')
2533  {
2534  if( pSym == &cSymbol[ MAXSTRLEN ] )
2535  {
2536  SetError( FormulaError::StringOverflow);
2537  eState = ssStop;
2538  }
2539  else
2540  *pSym++ = c;
2541  }
2542  else
2543  {
2544  --pSrc;
2545  eState = ssStop;
2546  }
2547  }
2548  break;
2549  case ssGetReference:
2550  if( pSym == &cSymbol[ MAXSTRLEN ] )
2551  {
2552  SetError( FormulaError::StringOverflow);
2553  eState = ssSkipReference;
2554  }
2555  [[fallthrough]];
2556  case ssSkipReference:
2557  // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2558  // mandatory also if no sheet name. 'External'# is optional,
2559  // sheet name is optional, quotes around sheet name are
2560  // optional if no quote contained. [#REF!] is valid.
2561  // 2nd usage: ['Sheet'.$$'DefinedName']
2562  // 3rd usage: ['External'#$$'DefinedName']
2563  // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2564  // Also for all these names quotes are optional if no quote
2565  // contained.
2566  {
2567 
2568  // nRefInName: 0 := not in sheet name yet. 'External'
2569  // is parsed as if it was a sheet name and nRefInName
2570  // is reset when # is encountered immediately after closing
2571  // quote. Same with 'DefinedName', nRefInName is cleared
2572  // when : is encountered.
2573 
2574  // Encountered leading $ before sheet name.
2575  constexpr int kDollar = (1 << 1);
2576  // Encountered ' opening quote, which may be after $ or
2577  // not.
2578  constexpr int kOpen = (1 << 2);
2579  // Somewhere in name.
2580  constexpr int kName = (1 << 3);
2581  // Encountered ' in name, will be cleared if double or
2582  // transformed to kClose if not, in which case kOpen is
2583  // cleared.
2584  constexpr int kQuote = (1 << 4);
2585  // Past ' closing quote.
2586  constexpr int kClose = (1 << 5);
2587  // Encountered # file/sheet separator.
2588  constexpr int kFileSep = (1 << 6);
2589  // Past . sheet name separator.
2590  constexpr int kPast = (1 << 7);
2591  // Marked name $$ follows sheet name separator, detected
2592  // while we're still on the separator. Will be cleared when
2593  // entering the name.
2594  constexpr int kMarkAhead = (1 << 8);
2595  // In marked defined name.
2596  constexpr int kDefName = (1 << 9);
2597  // Encountered # of #REF!
2598  constexpr int kRefErr = (1 << 10);
2599 
2600  bool bAddToSymbol = true;
2601  if ((nMask & ScCharFlags::OdfRBracket) && !(nRefInName & kOpen))
2602  {
2603  OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
2604  "ScCompiler::NextSymbol: reference: "
2605  "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2606  // eaten, not added to pSym
2607  bAddToSymbol = false;
2608  eState = ssStop;
2609  }
2610  else if (cSheetSep == c && nRefInName == 0)
2611  {
2612  // eat it, no sheet name [.A1]
2613  bAddToSymbol = false;
2614  nRefInName |= kPast;
2615  if ('$' == pSrc[0] && '$' == pSrc[1])
2616  nRefInName |= kMarkAhead;
2617  }
2618  else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2619  {
2620  // Not in col/row yet.
2621 
2622  if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
2623  nRefInName = 0;
2624  else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2625  {
2626  nRefInName &= ~kMarkAhead;
2627  if (!(nRefInName & kDefName))
2628  {
2629  // eaten, not added to pSym (2 chars)
2630  bAddToSymbol = false;
2631  ++pSrc;
2632  nRefInName &= kPast;
2633  nRefInName |= kDefName;
2634  }
2635  else
2636  {
2637  // ScAddress::Parse() will recognize this as
2638  // invalid later.
2639  if (eState != ssSkipReference)
2640  {
2641  *pSym++ = c;
2642  *pSym++ = *pSrc++;
2643  }
2644  bAddToSymbol = false;
2645  }
2646  }
2647  else if (cSheetPrefix == c && nRefInName == 0)
2648  nRefInName |= kDollar;
2649  else if ('\'' == c)
2650  {
2651  // TODO: The conventions' parseExternalName()
2652  // should handle quoted names, but as long as they
2653  // don't remove non-embedded quotes here.
2654  if (!(nRefInName & kName))
2655  {
2656  nRefInName |= (kOpen | kName);
2657  bAddToSymbol = !(nRefInName & kDefName);
2658  }
2659  else if (!(nRefInName & kOpen))
2660  {
2661  OSL_FAIL("ScCompiler::NextSymbol: reference: "
2662  "a ''' without the name being enclosed in '...' violates ODF spec");
2663  }
2664  else if (nRefInName & kQuote)
2665  {
2666  // escaped embedded quote
2667  nRefInName &= ~kQuote;
2668  }
2669  else
2670  {
2671  switch (pSrc[0])
2672  {
2673  case '\'':
2674  // escapes embedded quote
2675  nRefInName |= kQuote;
2676  break;
2678  // sheet name should follow
2679  nRefInName |= kFileSep;
2680  [[fallthrough]];
2681  default:
2682  // quote not followed by quote => close
2683  nRefInName |= kClose;
2684  nRefInName &= ~kOpen;
2685  }
2686  bAddToSymbol = !(nRefInName & kDefName);
2687  }
2688  }
2689  else if ('#' == c && nRefInName == 0)
2690  nRefInName |= kRefErr;
2691  else if (cSheetSep == c && !(nRefInName & kOpen))
2692  {
2693  // unquoted sheet name separator
2694  nRefInName |= kPast;
2695  if ('$' == pSrc[0] && '$' == pSrc[1])
2696  nRefInName |= kMarkAhead;
2697  }
2698  else if (':' == c && !(nRefInName & kOpen))
2699  {
2700  OSL_FAIL("ScCompiler::NextSymbol: reference: "
2701  "range operator ':' without prior sheet name separator '.' violates ODF spec");
2702  nRefInName = 0;
2704  }
2705  else if (!(nRefInName & kName))
2706  {
2707  // start unquoted name
2708  nRefInName |= kName;
2709  }
2710  }
2711  else if (':' == c)
2712  {
2713  // range operator
2714  nRefInName = 0;
2716  }
2717  if (bAddToSymbol && eState != ssSkipReference)
2718  *pSym++ = c; // everything is part of reference
2719  }
2720  break;
2721  case ssStop:
2722  ; // nothing, prevent warning
2723  break;
2724  }
2725  cLast = c;
2726  c = *pSrc;
2727  }
2728  if ( bi18n )
2729  {
2730  const sal_Int32 nOldSrcPos = nSrcPos;
2731  nSrcPos = nSrcPos + nSpaces;
2732  // If group separator is not a possible operator and not one of any
2733  // separators then it may be parsed away in numbers. This is
2734  // specifically the case with NO-BREAK SPACE, which actually triggers
2735  // the bi18n case (which we don't want to include as yet another
2736  // special case above as it is rare enough and doesn't generally occur
2737  // in formulas).
2738  const sal_Unicode cGroupSep = ScGlobal::getLocaleDataPtr()->getNumThousandSep()[0];
2739  const bool bGroupSeparator = (128 <= cGroupSep && cGroupSep != cSep &&
2740  cGroupSep != cArrayColSep && cGroupSep != cArrayRowSep &&
2741  cGroupSep != cDecSep && cGroupSep != cDecSepAlt &&
2742  cGroupSep != cSheetPrefix && cGroupSep != cSheetSep);
2743  OUStringBuffer aSymbol;
2744  mnRangeOpPosInSymbol = -1;
2745  FormulaError nErr = FormulaError::NONE;
2746  do
2747  {
2748  bi18n = false;
2749  // special case (e.g. $'sheetname' in OOO A1)
2750  if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2751  aSymbol.append(pStart[nSrcPos++]);
2752 
2753  ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass, bGroupSeparator);
2754 
2755  if ( !aRes.TokenType )
2756  {
2757  nErr = FormulaError::IllegalChar;
2758  SetError( nErr ); // parsed chars as string
2759  }
2760  if ( aRes.EndPos <= nSrcPos )
2761  {
2762  // Could not parse anything meaningful.
2763  assert(!aRes.TokenType);
2764  nErr = FormulaError::IllegalChar;
2765  SetError( nErr );
2766  // Caller has to act on an empty symbol for
2767  // nSrcPos < aFormula.getLength()
2768  nSrcPos = nOldSrcPos;
2769  aSymbol.setLength(0);
2770  }
2771  else
2772  {
2773  // When having parsed a second reference part, ensure that the
2774  // i18n parser did not mistakingly parse a number that included
2775  // a separator which happened to be meant as a parameter
2776  // separator instead.
2777  if (mnRangeOpPosInSymbol >= 0 && (aRes.TokenType & KParseType::ASC_NUMBER))
2778  {
2779  for (sal_Int32 i = nSrcPos; i < aRes.EndPos; ++i)
2780  {
2781  if (pStart[i] == cSep)
2782  aRes.EndPos = i; // also ends for
2783  }
2784  }
2785  aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos);
2786  nSrcPos = aRes.EndPos;
2787  c = pStart[nSrcPos];
2788  if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2789  { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2790  bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2791  }
2792  // One range operator restarts parsing for second reference.
2793  if (c == ':' && mnRangeOpPosInSymbol < 0)
2794  {
2795  mnRangeOpPosInSymbol = aSymbol.getLength();
2796  bi18n = true;
2797  }
2798  if ( bi18n )
2799  aSymbol.append(pStart[nSrcPos++]);
2800  }
2801  } while ( bi18n && nErr == FormulaError::NONE );
2802  sal_Int32 nLen = aSymbol.getLength();
2803  if ( nLen > MAXSTRLEN )
2804  {
2805  SetError( FormulaError::StringOverflow );
2806  nLen = MAXSTRLEN;
2807  }
2808  if (mnRangeOpPosInSymbol >= nLen)
2809  mnRangeOpPosInSymbol = -1;
2810  lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen );
2811  pSym = &cSymbol[nLen];
2812  }
2813  else
2814  {
2815  nSrcPos = pSrc - pStart;
2816  *pSym = 0;
2817  }
2818  if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2819  {
2820  // This is a trailing range operator, which is nonsense. Will be caught
2821  // in next round.
2822  mnRangeOpPosInSymbol = -1;
2823  *--pSym = 0;
2824  --nSrcPos;
2825  }
2826  if ( bAutoCorrect )
2827  aCorrectedSymbol = OUString(cSymbol, pSym - cSymbol);
2828  if (bAutoIntersection && nSpaces > 1)
2829  --nSpaces; // replace '!!' with only one space
2830  return nSpaces;
2831 }
2832 
2833 // Convert symbol to token
2834 
2835 bool ScCompiler::IsOpCode( const OUString& rName, bool bInArray )
2836 {
2837  OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
2838  bool bFound = (iLook != mxSymbols->getHashMap().end());
2839  if (bFound)
2840  {
2841  OpCode eOp = iLook->second;
2842  if (bInArray)
2843  {
2844  if (rName == mxSymbols->getSymbol(ocArrayColSep))
2845  eOp = ocArrayColSep;
2846  else if (rName == mxSymbols->getSymbol(ocArrayRowSep))
2847  eOp = ocArrayRowSep;
2848  }
2849  else if (eOp == ocArrayColSep || eOp == ocArrayRowSep)
2850  {
2851  if (rName == mxSymbols->getSymbol(ocSep))
2852  eOp = ocSep;
2853  else if (rName == ";")
2854  {
2855  switch (FormulaGrammar::extractFormulaLanguage( meGrammar))
2856  {
2857  // Only for languages/grammars that actually use ';'
2858  // parameter separator.
2859  case css::sheet::FormulaLanguage::NATIVE:
2860  case css::sheet::FormulaLanguage::ENGLISH:
2861  case css::sheet::FormulaLanguage::ODFF:
2862  case css::sheet::FormulaLanguage::ODF_11:
2863  eOp = ocSep;
2864  }
2865  }
2866  }
2867  else if (eOp == ocCeil && mxSymbols->isOOXML())
2868  {
2869  // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
2870  // unassigned for import.
2871  eOp = ocCeil_Math;
2872  }
2873  else if (eOp == ocFloor && mxSymbols->isOOXML())
2874  {
2875  // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2876  // unassigned for import.
2877  eOp = ocFloor_Math;
2878  }
2879  maRawToken.SetOpCode(eOp);
2880  }
2881  else if (mxSymbols->isODFF())
2882  {
2883  // ODFF names that are not written in the current mapping but to be
2884  // recognized. New names will be written in a future release, then
2885  // exchange (!) with the names in
2886  // formula/source/core/resource/core_resource.src to be able to still
2887  // read the old names as well.
2888  struct FunctionName
2889  {
2890  const char* pName;
2891  OpCode eOp;
2892  };
2893  static const FunctionName aOdffAliases[] = {
2894  // Renamed old names, still accept them:
2895  { "B", ocB }, // B -> BINOM.DIST.RANGE
2896  { "TDIST", ocTDist }, // TDIST -> LEGACY.TDIST
2897  { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
2898  { "ZGZ", ocRRI }, // ZGZ -> RRI
2899  { "COLOR", ocColor }, // COLOR -> ORG.LIBREOFFICE.COLOR
2900  { "GOALSEEK", ocBackSolver }, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
2901  { "COM.MICROSOFT.F.DIST", ocFDist_LT }, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
2902  { "COM.MICROSOFT.F.INV", ocFInv_LT } // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
2903  // Renamed new names, prepare to read future names:
2904  //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX
2905  };
2906  for (const FunctionName& rOdffAlias : aOdffAliases)
2907  {
2908  if (rName.equalsIgnoreAsciiCaseAscii( rOdffAlias.pName))
2909  {
2910  maRawToken.SetOpCode( rOdffAlias.eOp);
2911  bFound = true;
2912  break; // for
2913  }
2914  }
2915  }
2916  else if (mxSymbols->isOOXML())
2917  {
2918  // OOXML names that are not written in the current mapping but to be
2919  // recognized as old versions wrote them.
2920  struct FunctionName
2921  {
2922  const char* pName;
2923  OpCode eOp;
2924  };
2925  static const FunctionName aOoxmlAliases[] = {
2926  { "EFFECTIVE", ocEffect }, // EFFECTIVE -> EFFECT
2927  { "ERRORTYPE", ocErrorType }, // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE
2928  { "MULTIRANGE", ocMultiArea }, // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE
2929  { "GOALSEEK", ocBackSolver }, // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK
2930  { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY
2931  { "CURRENT", ocCurrent }, // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT
2932  { "STYLE", ocStyle } // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE
2933  };
2934  for (const FunctionName& rOoxmlAlias : aOoxmlAliases)
2935  {
2936  if (rName.equalsIgnoreAsciiCaseAscii( rOoxmlAlias.pName))
2937  {
2938  maRawToken.SetOpCode( rOoxmlAlias.eOp);
2939  bFound = true;
2940  break; // for
2941  }
2942  }
2943  }
2944  else if (mxSymbols->isPODF())
2945  {
2946  // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess.
2947  // We can't rename them in
2948  // formula/source/core/resource/core_resource.src but can add
2949  // additional names to be recognized here so they match the UI names if
2950  // those are renamed.
2951  struct FunctionName
2952  {
2953  const char* pName;
2954  OpCode eOp;
2955  };
2956  static const FunctionName aPodfAliases[] = {
2957  { "EFFECT", ocEffect } // EFFECTIVE -> EFFECT
2958  };
2959  for (const FunctionName& rPodfAlias : aPodfAliases)
2960  {
2961  if (rName.equalsIgnoreAsciiCaseAscii( rPodfAlias.pName))
2962  {
2963  maRawToken.SetOpCode( rPodfAlias.eOp);
2964  bFound = true;
2965  break; // for
2966  }
2967  }
2968  }
2969 
2970  if (!bFound)
2971  {
2972  OUString aIntName;
2973  if (mxSymbols->hasExternals())
2974  {
2975  // If symbols are set by filters get mapping to exact name.
2976  ExternalHashMap::const_iterator iExt(
2977  mxSymbols->getExternalHashMap().find( rName));
2978  if (iExt != mxSymbols->getExternalHashMap().end())
2979  {
2980  if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
2981  aIntName = (*iExt).second;
2982  }
2983  }
2984  else
2985  {
2986  // Old (deprecated) addins first for legacy.
2988  {
2989  aIntName = cSymbol;
2990  }
2991  else
2992  // bLocalFirst=false for (English) upper full original name
2993  // (service.function)
2995  rName, !mxSymbols->isEnglish());
2996  }
2997  if (!aIntName.isEmpty())
2998  {
2999  maRawToken.SetExternal( aIntName ); // international name
3000  bFound = true;
3001  }
3002  }
3003  if (!bFound)
3004  return false;
3005  OpCode eOp = maRawToken.GetOpCode();
3006  if (eOp == ocSub || eOp == ocNegSub)
3007  {
3008  bool bShouldBeNegSub =
3009  (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
3011  eLastOp == ocArrayOpen ||
3013  if (bShouldBeNegSub && eOp == ocSub)
3015  //TODO: if ocNegSub had ForceArray we'd have to set it here
3016  else if (!bShouldBeNegSub && eOp == ocNegSub)
3018  }
3019  return bFound;
3020 }
3021 
3022 bool ScCompiler::IsOpCode2( const OUString& rName )
3023 {
3024  bool bFound = false;
3025  sal_uInt16 i;
3026 
3027  for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
3028  bFound = rName.equalsAscii( pInternal[ i-ocInternalBegin ] );
3029 
3030  if (bFound)
3031  {
3032  maRawToken.SetOpCode( static_cast<OpCode>(--i) );
3033  }
3034  return bFound;
3035 }
3036 
3037 static bool lcl_ParenthesisFollows( const sal_Unicode* p )
3038 {
3039  while (*p == ' ')
3040  p++;
3041  return *p == '(';
3042 }
3043 
3044 bool ScCompiler::IsValue( const OUString& rSym )
3045 {
3046  const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( GetGrammar());
3047  if (nFormulaLanguage == css::sheet::FormulaLanguage::ODFF || nFormulaLanguage == css::sheet::FormulaLanguage::OOXML)
3048  {
3049  // Speedup things for ODFF, only well-formed numbers, not locale
3050  // dependent nor user input.
3051  rtl_math_ConversionStatus eStatus;
3052  sal_Int32 nParseEnd;
3053  double fVal = rtl::math::stringToDouble( rSym, '.', 0, &eStatus, &nParseEnd);
3054  if (nParseEnd != rSym.getLength())
3055  {
3056  // Not (only) a number.
3057 
3058  if (nParseEnd > 0)
3059  return false; // partially a number => no such thing
3060 
3061  if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3062  return false; // some function name, not a constant
3063 
3064  // Could be TRUE or FALSE constant.
3065  OpCode eOpFunc = ocNone;
3066  if (rSym.equalsIgnoreAsciiCase("TRUE"))
3067  eOpFunc = ocTrue;
3068  else if (rSym.equalsIgnoreAsciiCase("FALSE"))
3069  eOpFunc = ocFalse;
3070  if (eOpFunc != ocNone)
3071  {
3072  maRawToken.SetOpCode(eOpFunc);
3073  // add missing trailing parentheses
3074  maPendingOpCodes.push(ocOpen);
3075  maPendingOpCodes.push(ocClose);
3076  return true;
3077  }
3078  return false;
3079  }
3080  if (eStatus == rtl_math_ConversionStatus_OutOfRange)
3081  SetError( FormulaError::IllegalArgument );
3082  maRawToken.SetDouble( fVal );
3083  return true;
3084  }
3085 
3086  double fVal;
3087  sal_uInt32 nIndex = mxSymbols->isEnglish() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0;
3088 
3089  if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal))
3090  return false;
3091 
3093 
3094  // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
3095  // Dates should never be entered directly and automatically converted
3096  // to serial, because the serial would be wrong if null-date changed.
3097  // Usually it wouldn't be accepted anyway because the date separator
3098  // clashed with other separators or operators.
3099  if (nType & (SvNumFormatType::TIME | SvNumFormatType::DATE))
3100  return false;
3101 
3102  if (nType == SvNumFormatType::LOGICAL)
3103  {
3104  if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3105  return false; // Boolean function instead.
3106  }
3107 
3108  if( nType == SvNumFormatType::TEXT )
3109  // HACK: number too big!
3110  SetError( FormulaError::IllegalArgument );
3111  maRawToken.SetDouble( fVal );
3112  return true;
3113 }
3114 
3116 {
3117  if ( cSymbol[0] != '"' )
3118  return false;
3119  const sal_Unicode* p = cSymbol+1;
3120  while ( *p )
3121  p++;
3122  sal_Int32 nLen = sal::static_int_cast<sal_Int32>( p - cSymbol - 1 );
3123  if (!nLen || cSymbol[nLen] != '"')
3124  return false;
3125  svl::SharedString aSS = rDoc.GetSharedStringPool().intern(OUString(cSymbol+1, nLen-1));
3127  return true;
3128 }
3129 
3130 bool ScCompiler::IsPredetectedErrRefReference( const OUString& rName, const OUString* pErrRef )
3131 {
3132  switch (mnPredetectedReference)
3133  {
3134  case 1:
3135  return IsSingleReference( rName, pErrRef);
3136  case 2:
3137  return IsDoubleReference( rName, pErrRef);
3138  default:
3139  return false;
3140  }
3141 }
3142 
3143 bool ScCompiler::IsPredetectedReference( const OUString& rName )
3144 {
3145  // Speedup documents with lots of broken references, e.g. sheet deleted.
3146  // It could also be a broken invalidated reference that contains #REF!
3147  // (but is not equal to), which we wrote prior to ODFF and also to ODFF
3148  // between 2013 and 2016 until 5.1.4
3149  const OUString aErrRef("#REF!"); // not localized in ODFF
3150  sal_Int32 nPos = rName.indexOf( aErrRef);
3151  if (nPos != -1)
3152  {
3153  /* TODO: this may be enhanced by reusing scan information from
3154  * NextSymbol(), the positions of quotes and special characters found
3155  * there for $'sheet'.A1:... could be stored in a vector. We don't
3156  * fully rescan here whether found positions are within single quotes
3157  * for performance reasons. This code does not check for possible
3158  * occurrences of insane "valid" sheet names like
3159  * 'haha.#REF!1fooledyou' and will generate an error on such. */
3160  if (nPos == 0)
3161  {
3162  // Per ODFF the correct string for a reference error is just #REF!,
3163  // so pass it on.
3164  if (rName.getLength() == 5)
3165  return IsErrorConstant( rName);
3166  // #REF!.AB42 or #REF!42 or #REF!#REF!
3167  return IsPredetectedErrRefReference( rName, &aErrRef);
3168  }
3169  sal_Unicode c = rName[nPos-1]; // before #REF!
3170  if ('$' == c)
3171  {
3172  if (nPos == 1)
3173  {
3174  // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
3175  return IsPredetectedErrRefReference( rName, &aErrRef);
3176  }
3177  c = rName[nPos-2]; // before $#REF!
3178  }
3179  sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF!
3180  switch (c)
3181  {
3182  case '.':
3183  if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
3184  {
3185  // sheet.#REF!42 or sheet.#REF!#REF!
3186  return IsPredetectedErrRefReference( rName, &aErrRef);
3187  }
3188  break;
3189  case ':':
3190  if (mnPredetectedReference > 1 &&
3191  ('.' == c2 || '$' == c2 || '#' == c2 ||
3192  ('0' <= c2 && c2 <= '9')))
3193  {
3194  // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
3195  return IsPredetectedErrRefReference( rName, &aErrRef);
3196  }
3197  break;
3198  default:
3199  if (rtl::isAsciiAlpha(c) &&
3200  ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
3201  {
3202  // AB#REF!: or AB#REF!
3203  return IsPredetectedErrRefReference( rName, &aErrRef);
3204  }
3205  }
3206  }
3207  switch (mnPredetectedReference)
3208  {
3209  case 1:
3210  return IsSingleReference( rName);
3211  case 2:
3212  return IsDoubleReference( rName);
3213  }
3214  return false;
3215 }
3216 
3217 bool ScCompiler::IsDoubleReference( const OUString& rName, const OUString* pErrRef )
3218 {
3219  ScRange aRange( aPos, aPos );
3220  const ScAddress::Details aDetails( pConv->meConv, aPos );
3221  ScAddress::ExternalInfo aExtInfo;
3222  ScRefFlags nFlags = aRange.Parse( rName, rDoc, aDetails, &aExtInfo, &maExternalLinks, pErrRef );
3223  if( nFlags & ScRefFlags::VALID )
3224  {
3225  ScComplexRefData aRef;
3226  aRef.InitRange( aRange );
3227  aRef.Ref1.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3228  aRef.Ref1.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3229  aRef.Ref1.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3230  if ( !(nFlags & ScRefFlags::TAB_VALID) )
3231  aRef.Ref1.SetTabDeleted( true ); // #REF!
3232  aRef.Ref1.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3233  aRef.Ref2.SetColRel( (nFlags & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO );
3234  aRef.Ref2.SetRowRel( (nFlags & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO );
3235  aRef.Ref2.SetTabRel( (nFlags & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO );
3236  if ( !(nFlags & ScRefFlags::TAB2_VALID) )
3237  aRef.Ref2.SetTabDeleted( true ); // #REF!
3238  aRef.Ref2.SetFlag3D( ( nFlags & ScRefFlags::TAB2_3D ) != ScRefFlags::ZERO );
3239  aRef.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
3240  if (aExtInfo.mbExternal)
3241  {
3243  const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3245  aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3246  maExternalFiles.push_back(aExtInfo.mnFileId);
3247  }
3248  else
3249  {
3251  }
3252  }
3253 
3254  return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3255 }
3256 
3257 bool ScCompiler::IsSingleReference( const OUString& rName, const OUString* pErrRef )
3258 {
3260  mnCurrentSheetTab = -1;
3261  ScAddress aAddr( aPos );
3262  const ScAddress::Details aDetails( pConv->meConv, aPos );
3263  ScAddress::ExternalInfo aExtInfo;
3264  ScRefFlags nFlags = aAddr.Parse( rName, rDoc, aDetails,
3265  &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos, pErrRef);
3266  // Something must be valid in order to recognize Sheet1.blah or blah.a1
3267  // as a (wrong) reference.
3269  {
3270  // Valid given tab and invalid col or row may indicate a sheet-local
3271  // named expression, bail out early and don't create a reference token.
3272  if (!(nFlags & ScRefFlags::VALID) && mnCurrentSheetEndPos > 0 &&
3273  (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
3274  {
3275  if (aExtInfo.mbExternal)
3276  {
3277  // External names are handled separately.
3279  mnCurrentSheetTab = -1;
3280  }
3281  else
3282  {
3283  mnCurrentSheetTab = aAddr.Tab();
3284  }
3285  return false;
3286  }
3287 
3288  ScSingleRefData aRef;
3289  aRef.InitAddress( aAddr );
3290  aRef.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3291  aRef.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3292  aRef.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3293  aRef.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3294  // the reference is really invalid
3295  if( !( nFlags & ScRefFlags::VALID ) )
3296  {
3297  if( !( nFlags & ScRefFlags::COL_VALID ) )
3298  aRef.SetColDeleted(true);
3299  if( !( nFlags & ScRefFlags::ROW_VALID ) )
3300  aRef.SetRowDeleted(true);
3301  if( !( nFlags & ScRefFlags::TAB_VALID ) )
3302  aRef.SetTabDeleted(true);
3303  nFlags |= ScRefFlags::VALID;
3304  }
3305  aRef.SetAddress(rDoc.GetSheetLimits(), aAddr, aPos);
3306 
3307  if (aExtInfo.mbExternal)
3308  {
3310  const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3312  aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3313  maExternalFiles.push_back(aExtInfo.mnFileId);
3314  }
3315  else
3317  }
3318 
3319  return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3320 }
3321 
3322 bool ScCompiler::IsReference( const OUString& rName, const OUString* pErrRef )
3323 {
3324  // Has to be called before IsValue
3325  sal_Unicode ch1 = rName[0];
3326  sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' : ScGlobal::getLocaleDataPtr()->getNumDecimalSep()[0] );
3327  if ( ch1 == cDecSep )
3328  return false;
3329  // Code further down checks only if cDecSep=='.' so simply obtaining the
3330  // alternative decimal separator if it's not is sufficient.
3331  if (cDecSep != '.')
3332  {
3333  cDecSep = ScGlobal::getLocaleDataPtr()->getNumDecimalSepAlt().toChar();
3334  if ( ch1 == cDecSep )
3335  return false;
3336  }
3337  // Who was that imbecile introducing '.' as the sheet name separator!?!
3338  if ( rtl::isAsciiDigit( ch1 ) && pConv->getSpecialSymbol( Convention::SHEET_SEPARATOR) == '.' )
3339  {
3340  // Numerical sheet name is valid.
3341  // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
3342  // Don't create a #REF! of values. But also do not bail out on
3343  // something like 3:3, meaning entire row 3.
3344  do
3345  {
3346  const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.');
3347  if ( nPos == -1 )
3348  {
3349  if (ScGlobal::FindUnquoted( rName, ':') != -1)
3350  break; // may be 3:3, continue as usual
3351  return false;
3352  }
3353  sal_Unicode const * const pTabSep = rName.getStr() + nPos;
3354  sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
3355  if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) )
3356  return false;
3357  if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
3358  && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & ScCharFlags::ValueExp) )
3359  {
3360  // If it is an 1.E2 expression check if "1" is an existent sheet
3361  // name. If so, a desired value 1.E2 would have to be entered as
3362  // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
3363  // require numerical sheet names always being entered quoted, which
3364  // is not desirable (too many 1999, 2000, 2001 sheets in use).
3365  // Furthermore, XML files created with versions prior to SRC640e
3366  // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
3367  // and would produce wrong formulas if the conditions here are met.
3368  // If you can live with these restrictions you may remove the
3369  // check and return an unconditional FALSE.
3370  OUString aTabName( rName.copy( 0, nPos ) );
3371  SCTAB nTab;
3372  if ( !rDoc.GetTable( aTabName, nTab ) )
3373  return false;
3374  // If sheet "1" exists and the expression is 1.E+2 continue as
3375  // usual, the ScRange/ScAddress parser will take care of it.
3376  }
3377  } while(false);
3378  }
3379 
3380  if (IsSingleReference( rName, pErrRef))
3381  return true;
3382 
3383  // Though the range operator is handled explicitly, when encountering
3384  // something like Sheet1.A:A we will have to treat it as one entity if it
3385  // doesn't pass as single cell reference.
3386  if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
3387  {
3388  if (IsDoubleReference( rName, pErrRef))
3389  return true;
3390  // Now try with a symbol up to the range operator, rewind source
3391  // position.
3392  assert(mnRangeOpPosInSymbol < MAXSTRLEN); // We should have caught the maldoers.
3393  if (mnRangeOpPosInSymbol >= MAXSTRLEN) // TODO: this check and return
3394  return false; // can be removed when sure.
3395  sal_Int32 nLen = mnRangeOpPosInSymbol;
3396  while (cSymbol[++nLen])
3397  ;
3399  nSrcPos -= (nLen - mnRangeOpPosInSymbol);
3400  mnRangeOpPosInSymbol = -1;
3401  mbRewind = true;
3402  return true; // end all checks
3403  }
3404  else
3405  {
3406  switch (pConv->meConv)
3407  {
3408  case FormulaGrammar::CONV_XL_A1:
3409  case FormulaGrammar::CONV_XL_OOX:
3410  // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel
3411  // sickness, mnRangeOpPosInSymbol did not catch the range
3412  // operator as it is within a quoted name.
3413  if (rName[0] != '\'')
3414  return false; // Document name has to be single quoted.
3415  [[fallthrough]];
3416  case FormulaGrammar::CONV_XL_R1C1:
3417  // C2 or C[1] are valid entire column references.
3418  if (IsDoubleReference( rName, pErrRef))
3419  return true;
3420  break;
3421  default:
3422  ; // nothing
3423  }
3424  }
3425  return false;
3426 }
3427 
3428 bool ScCompiler::IsMacro( const OUString& rName )
3429 {
3430 #if !HAVE_FEATURE_SCRIPTING
3431  (void) rName;
3432 
3433  return false;
3434 #else
3435 
3436  // Calling SfxObjectShell::GetBasic() may result in all sort of things
3437  // including obtaining the model and deep down in
3438  // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
3439  // formulas are compiled from a threaded import may result in a deadlock.
3440  // Check first if we actually could acquire it and if not bail out.
3441  /* FIXME: yes, but how ... */
3443  if (!g.isAcquired())
3444  {
3445  SAL_WARN( "sc.core", "ScCompiler::IsMacro - SolarMutex would deadlock, not obtaining Basic");
3446  return false; // bad luck
3447  }
3448 
3449  OUString aName( rName);
3450  StarBASIC* pObj = nullptr;
3451  SfxObjectShell* pDocSh = rDoc.GetDocumentShell();
3452 
3453  try
3454  {
3455  if( pDocSh )//XXX
3456  pObj = pDocSh->GetBasic();
3457  else
3458  pObj = SfxApplication::GetBasic();
3459  }
3460  catch (...)
3461  {
3462  return false;
3463  }
3464 
3465  if (!pObj)
3466  return false;
3467 
3468  // ODFF recommends to store user-defined functions prefixed with "USER.",
3469  // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
3470  // function name so a function "USER.FOO" could not exist, and macro check
3471  // is assigned the lowest priority in function name check.
3472  if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER."))
3473  aName = aName.copy(5);
3474 
3475  SbxMethod* pMeth = static_cast<SbxMethod*>(pObj->Find( aName, SbxClassType::Method ));
3476  if( !pMeth )
3477  {
3478  return false;
3479  }
3480  // It really should be a BASIC function!
3481  if( pMeth->GetType() == SbxVOID
3482  || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
3483  || dynamic_cast<const SbMethod*>( pMeth) == nullptr )
3484  {
3485  return false;
3486  }
3487  maRawToken.SetExternal( aName );
3489  return true;
3490 #endif
3491 }
3492 
3493 bool ScCompiler::IsNamedRange( const OUString& rUpperName )
3494 {
3495  // IsNamedRange is called only from NextNewToken, with an upper-case string
3496 
3497  // try local names first
3498  sal_Int16 nSheet = aPos.Tab();
3499  ScRangeName* pRangeName = rDoc.GetRangeName(nSheet);
3500  const ScRangeData* pData = nullptr;
3501  if (pRangeName)
3502  pData = pRangeName->findByUpperName(rUpperName);
3503  if (!pData)
3504  {
3505  pRangeName = rDoc.GetRangeName();
3506  if (pRangeName)
3507  pData = pRangeName->findByUpperName(rUpperName);
3508  if (pData)
3509  nSheet = -1;
3510  }
3511 
3512  if (pData)
3513  {
3514  maRawToken.SetName( nSheet, pData->GetIndex());
3515  return true;
3516  }
3517 
3518  // Sheet-local name with sheet specified.
3519  if (mnCurrentSheetEndPos > 0 && mnCurrentSheetTab >= 0)
3520  {
3521  OUString aName( rUpperName.copy( mnCurrentSheetEndPos));
3522  pRangeName = rDoc.GetRangeName( mnCurrentSheetTab);
3523  if (pRangeName)
3524  {
3525  pData = pRangeName->findByUpperName(aName);
3526  if (pData)
3527  {
3529  return true;
3530  }
3531  }
3532  }
3533 
3534  return false;
3535 }
3536 
3537 bool ScCompiler::IsExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange )
3538 {
3539  /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
3540  * correctly parses external named references in OOo, as required per RFE
3541  * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
3542  * spec first. Until then don't pretend to support external names that
3543  * wouldn't survive a save and reload cycle, return false instead. */
3544 
3545  rbInvalidExternalNameRange = false;
3546 
3547  if (!pConv)
3548  return false;
3549 
3550  OUString aFile, aName;
3551  if (!pConv->parseExternalName( rSymbol, aFile, aName, rDoc, &maExternalLinks))
3552  return false;
3553 
3554  if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
3555  return false;
3556 
3558  OUString aTmp = aFile;
3559  pRefMgr->convertToAbsName(aTmp);
3560  aFile = aTmp;
3561  sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
3562  if (!pRefMgr->isValidRangeName(nFileId, aName))
3563  {
3564  rbInvalidExternalNameRange = true;
3565  // range name doesn't exist in the source document.
3566  return false;
3567  }
3568 
3569  const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
3570  maRawToken.SetExternalName(nFileId, pRealName ? *pRealName : aTmp);
3571  maExternalFiles.push_back(nFileId);
3572  return true;
3573 }
3574 
3575 bool ScCompiler::IsDBRange( const OUString& rName )
3576 {
3578  const ScDBData* p = rDBs.findByUpperName(rName);
3579  if (!p)
3580  return false;
3581 
3582  maRawToken.SetName( -1, p->GetIndex()); // DB range is always global.
3584  return true;
3585 }
3586 
3587 bool ScCompiler::IsColRowName( const OUString& rName )
3588 {
3589  bool bInList = false;
3590  bool bFound = false;
3591  ScSingleRefData aRef;
3592  OUString aName( rName );
3593  DeQuote( aName );
3594  SCTAB nThisTab = aPos.Tab();
3595  for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
3596  { // first check ranges on this sheet, in case of duplicated names
3597  for ( short jRow=0; jRow<2 && !bInList; jRow++ )
3598  {
3599  ScRangePairList* pRL;
3600  if ( !jRow )
3601  pRL = rDoc.GetColNameRanges();
3602  else
3603  pRL = rDoc.GetRowNameRanges();
3604  for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
3605  {
3606  const ScRangePair & rR = (*pRL)[iPair];
3607  const ScRange& rNameRange = rR.GetRange(0);
3608  if ( jThisTab && (rNameRange.aStart.Tab() > nThisTab ||
3609  nThisTab > rNameRange.aEnd.Tab()) )
3610  continue; // for
3611  ScCellIterator aIter( rDoc, rNameRange );
3612  for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next())
3613  {
3614  // Don't crash if cell (via CompileNameFormula) encounters
3615  // a formula cell without code and
3616  // HasStringData/Interpret/Compile is executed and all that
3617  // recursively...
3618  // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3619  CellType eType = aIter.getType();
3620  bool bOk = false;
3621  if (eType == CELLTYPE_FORMULA)
3622  {
3623  ScFormulaCell* pFC = aIter.getFormulaCell();
3624  bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3625  }
3626  else
3627  bOk = true;
3628 
3629  if (bOk && aIter.hasString())
3630  {
3631  OUString aStr = aIter.getString();
3632  if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3633  {
3634  aRef.InitFlags();
3635  if ( !jRow )
3636  aRef.SetColRel( true ); // ColName
3637  else
3638  aRef.SetRowRel( true ); // RowName
3639  aRef.SetAddress(rDoc.GetSheetLimits(), aIter.GetPos(), aPos);
3640  bInList = bFound = true;
3641  }
3642  }
3643  }
3644  }
3645  }
3646  }
3647  if ( !bInList && rDoc.GetDocOptions().IsLookUpColRowNames() )
3648  { // search in current sheet
3649  tools::Long nDistance = 0, nMax = 0;
3650  tools::Long nMyCol = static_cast<tools::Long>(aPos.Col());
3651  tools::Long nMyRow = static_cast<tools::Long>(aPos.Row());
3652  bool bTwo = false;
3653  ScAddress aOne( 0, 0, aPos.Tab() );
3654  ScAddress aTwo( rDoc.MaxCol(), rDoc.MaxRow(), aPos.Tab() );
3655 
3656  ScAutoNameCache* pNameCache = rDoc.GetAutoNameCache();
3657  if ( pNameCache )
3658  {
3659  // use GetNameOccurrences to collect all positions of aName on the sheet
3660  // (only once), similar to the outer part of the loop in the "else" branch.
3661 
3662  const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() );
3663 
3664  // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3665  // The order of addresses in the vector is the same as from ScCellIterator.
3666 
3667  for ( const ScAddress& aAddress : rAddresses )
3668  {
3669  if ( bFound )
3670  { // stop if everything else is further away
3671  if ( nMax < static_cast<tools::Long>(aAddress.Col()) )
3672  break; // aIter
3673  }
3674  if ( aAddress != aPos )
3675  {
3676  // same treatment as in isEqual case below
3677 
3678  SCCOL nCol = aAddress.Col();
3679  SCROW nRow = aAddress.Row();
3680  tools::Long nC = nMyCol - nCol;
3681  tools::Long nR = nMyRow - nRow;
3682  if ( bFound )
3683  {
3684  tools::Long nD = nC * nC + nR * nR;
3685  if ( nD < nDistance )
3686  {
3687  if ( nC < 0 || nR < 0 )
3688  { // right or below
3689  bTwo = true;
3690  aTwo.Set( nCol, nRow, aAddress.Tab() );
3691  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3692  nDistance = nD;
3693  }
3694  else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
3695  {
3696  // upper left, only if not further up than the
3697  // current entry and nMyRow is below (CellIter
3698  // runs column-wise)
3699  bTwo = false;
3700  aOne.Set( nCol, nRow, aAddress.Tab() );
3701  nMax = std::max( nMyCol + nC, nMyRow + nR );
3702  nDistance = nD;
3703  }
3704  }
3705  }
3706  else
3707  {
3708  aOne.Set( nCol, nRow, aAddress.Tab() );
3709  nDistance = nC * nC + nR * nR;
3710  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3711 
3712  }
3713  bFound = true;
3714  }
3715  }
3716  }
3717  else
3718  {
3719  ScCellIterator aIter( rDoc, ScRange( aOne, aTwo ) );
3720  for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3721  {
3722  if ( bFound )
3723  { // stop if everything else is further away
3724  if ( nMax < static_cast<tools::Long>(aIter.GetPos().Col()) )
3725  break; // aIter
3726  }
3727  CellType eType = aIter.getType();
3728  bool bOk = false;
3729  if (eType == CELLTYPE_FORMULA)
3730  {
3731  ScFormulaCell* pFC = aIter.getFormulaCell();
3732  bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3733  }
3734  else
3735  bOk = true;
3736 
3737  if (bOk && aIter.hasString())
3738  {
3739  OUString aStr = aIter.getString();
3740  if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3741  {
3742  SCCOL nCol = aIter.GetPos().Col();
3743  SCROW nRow = aIter.GetPos().Row();
3744  tools::Long nC = nMyCol - nCol;
3745  tools::Long nR = nMyRow - nRow;
3746  if ( bFound )
3747  {
3748  tools::Long nD = nC * nC + nR * nR;
3749  if ( nD < nDistance )
3750  {
3751  if ( nC < 0 || nR < 0 )
3752  { // right or below
3753  bTwo = true;
3754  aTwo.Set( nCol, nRow, aIter.GetPos().Tab() );
3755  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3756  nDistance = nD;
3757  }
3758  else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
3759  {
3760  // upper left, only if not further up than the
3761  // current entry and nMyRow is below (CellIter
3762  // runs column-wise)
3763  bTwo = false;
3764  aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3765  nMax = std::max( nMyCol + nC, nMyRow + nR );
3766  nDistance = nD;
3767  }
3768  }
3769  }
3770  else
3771  {
3772  aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3773  nDistance = nC * nC + nR * nR;
3774  nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3775  }
3776  bFound = true;
3777  }
3778  }
3779  }
3780  }
3781 
3782  if ( bFound )
3783  {
3784  ScAddress aAdr;
3785  if ( bTwo )
3786  {
3787  if ( nMyCol >= static_cast<tools::Long>(aOne.Col()) && nMyRow >= static_cast<tools::Long>(aOne.Row()) )
3788  aAdr = aOne; // upper left takes precedence
3789  else
3790  {
3791  if ( nMyCol < static_cast<tools::Long>(aOne.Col()) )
3792  { // two to the right
3793  if ( nMyRow >= static_cast<tools::Long>(aTwo.Row()) )
3794  aAdr = aTwo; // directly right
3795  else
3796  aAdr = aOne;
3797  }
3798  else
3799  { // two below or below and right, take the nearest
3800  tools::Long nC1 = nMyCol - aOne.Col();
3801  tools::Long nR1 = nMyRow - aOne.Row();
3802  tools::Long nC2 = nMyCol - aTwo.Col();
3803  tools::Long nR2 = nMyRow - aTwo.Row();
3804  if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3805  aAdr = aOne;
3806  else
3807  aAdr = aTwo;
3808  }
3809  }
3810  }
3811  else
3812  aAdr = aOne;
3813  aRef.InitAddress( aAdr );
3814  if ( (aAdr.Row() != rDoc.MaxRow() && rDoc.HasStringData(
3815  aAdr.Col(), aAdr.Row() + 1, aAdr.Tab()))
3816  || (aAdr.Row() != 0 && rDoc.HasStringData(
3817  aAdr.Col(), aAdr.Row() - 1, aAdr.Tab())))
3818  aRef.SetRowRel( true ); // RowName
3819  else
3820  aRef.SetColRel( true ); // ColName
3821  aRef.SetAddress(rDoc.GetSheetLimits(), aAdr, aPos);
3822  }
3823  }
3824  if ( bFound )
3825  {
3828  return true;
3829  }
3830  else
3831  return false;
3832 }
3833 
3834 bool ScCompiler::IsBoolean( const OUString& rName )
3835 {
3836  OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName ) );
3837  if( iLook != mxSymbols->getHashMap().end() &&
3838  ((*iLook).second == ocTrue ||
3839  (*iLook).second == ocFalse) )
3840  {
3841  maRawToken.SetOpCode( (*iLook).second );
3842  return true;
3843  }
3844  else
3845  return false;
3846 }
3847 
3848 bool ScCompiler::IsErrorConstant( const OUString& rName ) const
3849 {
3850  FormulaError nError = GetErrorConstant( rName);
3851  if (nError != FormulaError::NONE)
3852  {
3853  maRawToken.SetErrorConstant( nError);
3854  return true;
3855  }
3856  else
3857  return false;
3858 }
3859 
3860 bool ScCompiler::IsTableRefItem( const OUString& rName ) const
3861 {
3862  bool bItem = false;
3863  OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
3864  if (iLook != mxSymbols->getHashMap().end())
3865  {
3866  // Only called when there actually is a current TableRef, hence
3867  // accessing maTableRefs.back() is safe.
3868  ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3869  assert(p); // not a ScTableRefToken can't be
3870 
3871  switch ((*iLook).second)
3872  {
3873  case ocTableRefItemAll:
3874  bItem = true;
3876  break;
3877  case ocTableRefItemHeaders:
3878  bItem = true;
3880  break;
3881  case ocTableRefItemData:
3882  bItem = true;
3884  break;
3885  case ocTableRefItemTotals:
3886  bItem = true;
3888  break;
3889  case ocTableRefItemThisRow:
3890  bItem = true;
3892  break;
3893  default:
3894  ;
3895  }
3896  if (bItem)
3897  maRawToken.SetOpCode( (*iLook).second );
3898  }
3899  return bItem;
3900 }
3901 
3902 namespace {
3903 OUString unescapeTableRefColumnSpecifier( const OUString& rStr )
3904 {
3905  // '#', '[', ']' and '\'' are escaped with '\''
3906 
3907  if (rStr.indexOf( '\'' ) < 0)
3908  return rStr;
3909 
3910  const sal_Int32 n = rStr.getLength();
3911  OUStringBuffer aBuf( n );
3912  const sal_Unicode* p = rStr.getStr();
3913  const sal_Unicode* const pStop = p + n;
3914  bool bEscaped = false;
3915  for ( ; p < pStop; ++p)
3916  {
3917  const sal_Unicode c = *p;
3918  if (bEscaped)
3919  {
3920  aBuf.append( c );
3921  bEscaped = false;
3922  }
3923  else if (c == '\'')
3924  bEscaped = true; // unescaped escaping '\''
3925  else
3926  aBuf.append( c );
3927  }
3928  return aBuf.makeStringAndClear();
3929 }
3930 }
3931 
3932 bool ScCompiler::IsTableRefColumn( const OUString& rName ) const
3933 {
3934  // Only called when there actually is a current TableRef, hence
3935  // accessing maTableRefs.back() is safe.
3936  ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3937  assert(p); // not a ScTableRefToken can't be
3938 
3940  if (!pDBData)
3941  return false;
3942 
3943  OUString aName( unescapeTableRefColumnSpecifier( rName));
3944 
3945  ScRange aRange;
3946  pDBData->GetArea( aRange);
3947  aRange.aEnd.SetTab( aRange.aStart.Tab());
3948  aRange.aEnd.SetRow( aRange.aStart.Row());
3949 
3950  // Prefer the stored internal table column name, which is also needed for
3951  // named expressions during document load time when cell content isn't
3952  // available yet. Also, avoiding a possible calculation step in case the
3953  // header cell is a formula cell is "a good thing".
3954  sal_Int32 nOffset = pDBData->GetColumnNameOffset( aName);
3955  if (nOffset >= 0)
3956  {
3957  // This is sneaky... we always use the top row of the database range,
3958  // regardless of whether it is a header row or not. Code evaluating
3959  // this reference must take that into account and may have to act
3960  // differently if it is a header-less table. Which are two places,
3961  // HandleTableRef() (no change necessary there) and
3962  // CreateStringFromSingleRef() (must not fallback to cell lookup).
3963  ScSingleRefData aRef;
3964  ScAddress aAdr( aRange.aStart);
3965  aAdr.IncCol( nOffset);
3966  aRef.InitAddress( aAdr);
3968  return true;
3969  }
3970 
3971  if (pDBData->HasHeader())
3972  {
3973  // Quite similar to IsColRowName() but limited to one row of headers.
3974  ScCellIterator aIter( rDoc, aRange);
3975  for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3976  {
3977  CellType eType = aIter.getType();
3978  bool bOk = false;
3979  if (eType == CELLTYPE_FORMULA)
3980  {
3981  ScFormulaCell* pFC = aIter.getFormulaCell();
3982  bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3983  }
3984  else
3985  bOk = true;
3986 
3987  if (bOk && aIter.hasString())
3988  {
3989  OUString aStr = aIter.getString();
3990  if (ScGlobal::GetpTransliteration()->isEqual( aStr, aName))
3991  {
3992  // If this is successful and the internal column name
3993  // lookup was not, it may be worth a warning.
3994  SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup");
3995 
3996  /* XXX NOTE: we could init the column as relative so copying a
3997  * formula across columns would point to the relative column,
3998  * but do it absolute because:
3999  * a) it makes the reference work in named expressions without
4000  * having to distinguish
4001  * b) Excel does it the same. */
4002  ScSingleRefData aRef;
4003  aRef.InitAddress( aIter.GetPos());
4005  return true;
4006  }
4007  }
4008  }
4009  }
4010 
4011  return false;
4012 }
4013 
4015 {
4017  bAutoCorrect = bVal;
4018  mbStopOnError = !bVal;
4019 }
4020 
4022 {
4023  sal_Int32 nPos = aCorrectedSymbol.getLength();
4024  if ( !nPos )
4025  return;
4026 
4027  nPos--;
4028  const sal_Unicode cQuote = '\"';
4029  const sal_Unicode cx = 'x';
4030  const sal_Unicode cX = 'X';
4031  sal_Unicode c1 = aCorrectedSymbol[0];
4032  sal_Unicode c2 = aCorrectedSymbol[nPos];
4033  sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0;
4034  if ( c1 == cQuote && c2 != cQuote )
4035  { // "...
4036  // What's not a word doesn't belong to it.
4037  // Don't be pedantic: c < 128 should be sufficient here.
4038  while ( nPos && ((aCorrectedSymbol[nPos] < 128) &&
4041  nPos--;
4042  if ( nPos == MAXSTRLEN - 1 )
4043  aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, OUString(cQuote) ); // '"' the MAXSTRLENth character
4044  else
4045  aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, OUString(cQuote) );
4046  bCorrected = true;
4047  }
4048  else if ( c1 != cQuote && c2 == cQuote )
4049  { // ..."
4050  aCorrectedSymbol = OUStringChar(cQuote) + aCorrectedSymbol;
4051  bCorrected = true;
4052  }
4053  else if ( nPos == 0 && (c1 == cx || c1 == cX) )
4054  { // x => *
4055  aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
4056  bCorrected = true;
4057  }
4058  else if ( (GetCharTableFlags( c1, 0 ) & ScCharFlags::CharValue)
4059  && (GetCharTableFlags( c2, c2p ) & ScCharFlags::CharValue) )
4060  {
4061  if ( aCorrectedSymbol.indexOf(cx) >= 0 ) // At least two tokens separated by cx
4062  { // x => *
4063  sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4064  aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cx), OUStringChar(c));
4065  bCorrected = true;
4066  }
4067  if ( aCorrectedSymbol.indexOf(cX) >= 0 ) // At least two tokens separated by cX
4068  { // X => *
4069  sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4070  aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cX), OUStringChar(c));
4071  bCorrected = true;
4072  }
4073  }
4074  else
4075  {
4076  OUString aSymbol( aCorrectedSymbol );
4077  OUString aDoc;
4078  if ( aSymbol[0] == '\'' )
4079  {
4080  sal_Int32 nPosition = aSymbol.indexOf( "'#" );
4081  if (nPosition != -1)
4082  { // Split off 'Doc'#, may be d:\... or whatever
4083  aDoc = aSymbol.copy(0, nPosition + 2);
4084  aSymbol = aSymbol.copy(nPosition + 2);
4085  }
4086  }
4087  sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':');
4088  bool bColons;
4089  if ( nRefs > 2 )
4090  { // duplicated or too many ':'? B:2::C10 => B2:C10
4091  bColons = true;
4092  sal_Int32 nIndex = 0;
4093  OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) );
4094  sal_Int32 nLen1 = aTmp1.getLength();
4095  OUStringBuffer aSym;
4096  OUString aTmp2;
4097  bool bLastAlp = true;
4098  sal_Int32 nStrip = 0;
4099  sal_Int32 nCount = nRefs;
4100  for ( sal_Int32 j=1; j<nCount; j++ )
4101  {
4102  aTmp2 = aSymbol.getToken( 0, ':', nIndex );
4103  sal_Int32 nLen2 = aTmp2.getLength();
4104  if ( nLen1 || nLen2 )
4105  {
4106  if ( nLen1 )
4107  {
4108  aSym.append(aTmp1);
4109  bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
4110  }
4111  if ( nLen2 )
4112  {
4113  bool bNextNum = CharClass::isAsciiNumeric( aTmp2 );
4114  if ( bLastAlp == bNextNum && nStrip < 1 )
4115  {
4116  // Must be alternating number/string, only
4117  // strip within a reference.
4118  nRefs--;
4119  nStrip++;
4120  }
4121  else
4122  {
4123  if ( !aSym.isEmpty() && aSym[aSym.getLength()-1] != ':')
4124  aSym.append(":");
4125  nStrip = 0;
4126  }
4127  bLastAlp = !bNextNum;
4128  }
4129  else
4130  { // ::
4131  nRefs--;
4132  if ( nLen1 )
4133  { // B10::C10 ? append ':' on next round
4134  if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
4135  nStrip++;
4136  }
4137  }
4138  aTmp1 = aTmp2;
4139  nLen1 = nLen2;
4140  }
4141  else
4142  nRefs--;
4143  }
4144  aSymbol = aSym.makeStringAndClear() + aTmp1;
4145  }
4146  else
4147  bColons = false;
4148  if ( nRefs && nRefs <= 2 )
4149  { // reference twisted? 4A => A4 etc.
4150  OUString aTab[2], aRef[2];
4151  const ScAddress::Details aDetails( pConv->meConv, aPos );
4152  if ( nRefs == 2 )
4153  {
4154  sal_Int32 nIdx{ 0 };
4155  aRef[0] = aSymbol.getToken( 0, ':', nIdx );
4156  aRef[1] = aSymbol.getToken( 0, ':', nIdx );
4157  }
4158  else
4159  aRef[0] = aSymbol;
4160 
4161  bool bChanged = false;
4162  bool bOk = true;
4164  for ( int j=0; j<nRefs; j++ )
4165  {
4166  sal_Int32 nTmp = 0;
4167  sal_Int32 nDotPos = -1;
4168  while ( (nTmp = aRef[j].indexOf( '.', nTmp )) != -1 )
4169  nDotPos = nTmp++; // the last one counts
4170  if ( nDotPos != -1 )
4171  {
4172  aTab[j] = aRef[j].copy( 0, nDotPos + 1 ); // with '.'
4173  aRef[j] = aRef[j].copy( nDotPos + 1 );
4174  }
4175  OUString aOld( aRef[j] );
4176  OUStringBuffer aStr2;
4177  const sal_Unicode* p = aRef[j].getStr();
4178  while ( *p && rtl::isAsciiDigit( *p ) )
4179  aStr2.append(*p++);
4180  aRef[j] = OUString( p );
4181  aRef[j] += aStr2.makeStringAndClear();
4182  if ( bColons || aRef[j] != aOld )
4183  {
4184  bChanged = true;
4185  ScAddress aAdr;
4186  bOk &= ((aAdr.Parse( aRef[j], rDoc, aDetails ) & nMask) == nMask);
4187  }
4188  }
4189  if ( bChanged && bOk )
4190  {
4191  aCorrectedSymbol = aDoc;
4192  aCorrectedSymbol += aTab[0];
4193  aCorrectedSymbol += aRef[0];
4194  if ( nRefs == 2 )
4195  {
4196  aCorrectedSymbol += ":";
4197  aCorrectedSymbol += aTab[1];
4198  aCorrectedSymbol += aRef[1];
4199  }
4200  bCorrected = true;
4201  }
4202  }
4203  }
4204 }
4205 
4206 bool ScCompiler::ToUpperAsciiOrI18nIsAscii( OUString& rUpper, const OUString& rOrg ) const
4207 {
4208  if (FormulaGrammar::isODFF( meGrammar ))
4209  {
4210  // ODFF has a defined set of English function names, avoid i18n
4211  // overhead.
4212  rUpper = rOrg.toAsciiUpperCase();
4213  return true;
4214  }
4215  else
4216  {
4217  // One of localized or English.
4218  rUpper = pCharClass->uppercase(rOrg);
4219  return false;
4220  }
4221 }
4222 
4223 bool ScCompiler::NextNewToken( bool bInArray )
4224 {
4225  if (!maPendingOpCodes.empty())
4226  {
4228  maPendingOpCodes.pop();
4229  return true;
4230  }
4231 
4232  bool bAllowBooleans = bInArray;
4233  sal_Int32 nSpaces = NextSymbol(bInArray);
4234 
4235  if (!cSymbol[0])
4236  {
4237  if (nSrcPos < aFormula.getLength())
4238  {
4239  // Nothing could be parsed, remainder as bad string.
4240  // NextSymbol() must had set an error for this.
4241  assert( pArr->GetCodeError() != FormulaError::NONE);
4242  const OUString aBad( aFormula.copy( nSrcPos));
4244  maRawToken.SetString( aSS.getData(), aSS.getDataIgnoreCase());
4246  nSrcPos = aFormula.getLength();
4247  // Add bad string as last token.
4248  return true;
4249  }
4250  return false;
4251  }
4252 
4253  if( nSpaces )
4254  {
4255  ScRawToken aToken;
4256  aToken.SetOpCode( ocSpaces );
4257  aToken.sbyte.cByte = static_cast<sal_uInt8>( std::min<sal_Int32>(nSpaces, 255) );
4258  if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
4259  {
4260  SetError(FormulaError::CodeOverflow);
4261  return false;
4262  }
4263  }
4264 
4265  // Short cut for references when reading ODF to speedup things.
4267  {
4268  OUString aStr( cSymbol);
4269  bool bInvalidExternalNameRange;
4270  if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr, bInvalidExternalNameRange ))
4271  {
4275  }
4276  return true;
4277  }
4278 
4279  if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
4280  !bAutoCorrect )
4281  { // special case to speed up broken [$]#REF documents
4282  /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
4283  * be processed as usual. That would need some special treatment,
4284  * also in NextSymbol() because of possible combinations of
4285  * #REF!.#REF!#REF! parts. In case of reading ODF that is all
4286  * handled by IsPredetectedReference(), this case here remains for
4287  * manual/API input. */
4288  OUString aBad( aFormula.copy( nSrcPos-1 ) );
4289  eLastOp = pArr->AddBad( aBad )->GetOpCode();
4290  return false;
4291  }
4292 
4293  if( IsString() )
4294  return true;
4295 
4296  bool bMayBeFuncName;
4297  bool bAsciiNonAlnum; // operators, separators, ...
4298  if ( cSymbol[0] < 128 )
4299  {
4300  bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
4301  if (!bMayBeFuncName && (cSymbol[0] == '_' && cSymbol[1] == '_') && !utl::ConfigManager::IsFuzzing())
4302  {
4303  bMayBeFuncName = officecfg::Office::Common::Misc::ExperimentalMode::get();
4304  }
4305 
4306  bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
4307  }
4308  else
4309  {
4310  OUString aTmpStr( cSymbol[0] );
4311  bMayBeFuncName = pCharClass->isLetter( aTmpStr, 0 );
4312  bAsciiNonAlnum = false;
4313  }
4314 
4315  // Within a TableRef anything except an unescaped '[' or ']' is an item
4316  // or a column specifier, do not attempt to recognize any other single
4317  // operator there so even [,] or [+] for a single character column
4318  // specifier works. Note that space between two ocTableRefOpen is not
4319  // supported (Table[ [ColumnSpec]]), not only here. Note also that Table[]
4320  // without any item or column specifier is valid.
4321  if (bAsciiNonAlnum && cSymbol[1] == 0 && (eLastOp != ocTableRefOpen || cSymbol[0] == '[' || cSymbol[0] == ']'))
4322  {
4323  // Shortcut for operators and separators that need no further checks or upper.
4324  if (IsOpCode( OUString( cSymbol), bInArray ))
4325  return true;
4326  }
4327 
4328  if ( bMayBeFuncName )
4329  {
4330  // a function name must be followed by a parenthesis
4331  const sal_Unicode* p = aFormula.getStr() + nSrcPos;
4332  while( *p == ' ' )
4333  p++;
4334  bMayBeFuncName = ( *p == '(' );
4335  }
4336 
4337  // Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
4338  // IsReference().
4339 
4340  OUString aUpper;
4341 
4342  do
4343  {
4344  const OUString aOrg( cSymbol );
4345 
4346  // Check for TableRef column specifier first, it may be anything.
4347  if (cSymbol[0] != '#' && !maTableRefs.empty() && maTableRefs.back().mnLevel)
4348  {
4349  if (IsTableRefColumn( aOrg ))
4350  return true;
4351  // Do not attempt to resolve as any other name.
4352  aUpper = aOrg; // for ocBad
4353  break; // do; create ocBad token or set error.
4354  }
4355 
4356  mbRewind = false;
4357  aUpper.clear();
4358  bool bAsciiUpper = false;
4359 
4360  if (bAsciiNonAlnum)
4361  {
4362  bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4363  if (cSymbol[0] == '#')
4364  {
4365  // Check for TableRef item specifiers first.
4366  if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
4367  {
4368  if (IsTableRefItem( aUpper ))
4369  return true;
4370  }
4371 
4372  // This can be either an error constant ...
4373  if (IsErrorConstant( aUpper))
4374  return true;
4375 
4376  // ... or some invalidated reference starting with #REF!
4377  // which is handled after the do loop.
4378 
4379  break; // do; create ocBad token or set error.
4380  }
4381  if (IsOpCode( aUpper, bInArray ))
4382  return true;
4383  }
4384 
4385  if (bMayBeFuncName)
4386  {
4387  if (aUpper.isEmpty())
4388  bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4389  if (IsOpCode( aUpper, bInArray ))
4390  return true;
4391  }
4392 
4393  // Column 'DM' ("Deutsche Mark", German currency) couldn't be
4394  // referred => IsReference() before IsValue().
4395  // Preserve case of file names in external references.
4396  if (IsReference( aOrg ))
4397  {
4398  if (mbRewind) // Range operator, but no direct reference.
4399  continue; // do; up to range operator.
4400  // If a syntactically correct reference was recognized but invalid
4401  // e.g. because of non-existing sheet name => entire reference
4402  // ocBad to preserve input instead of #REF!.A1
4404  {
4405  aUpper = aOrg; // ensure for ocBad
4406  break; // do; create ocBad token or set error.
4407  }
4408  return true;
4409  }
4410 
4411  if (aUpper.isEmpty())
4412  bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4413 
4414  // IsBoolean() before IsValue() to catch inline bools without the kludge
4415  // for inline arrays.
4416  if (bAllowBooleans && IsBoolean( aUpper ))
4417  return true;
4418 
4419  if (IsValue( aUpper ))
4420  return true;
4421 
4422  // User defined names and such do need i18n upper also in ODF.
4423  if (bAsciiUpper || mbCharClassesDiffer)
4424  {
4425  // Use current system locale here because user defined symbols are
4426  // more likely in that localized language than in the formula
4427  // language. This in corner cases needs to continue to work for
4428  // existing documents and environments.
4429  aUpper = ScGlobal::getCharClassPtr()->uppercase( aOrg );
4430  }
4431 
4432  if (IsNamedRange( aUpper ))
4433  return true;
4434 
4435  // Compiling a named expression during collecting them in import shall
4436  // not match arbitrary names that otherwise if all named expressions
4437  // were present would be recognized as named expression. Such name will
4438  // flag an error below and will be recompiled in a second step later
4439  // with ScRangeData::CompileUnresolvedXML()
4441  break; // while
4442 
4443  // Preserve case of file names in external references.
4444  bool bInvalidExternalNameRange;
4445  if (IsExternalNamedRange( aOrg, bInvalidExternalNameRange ))
4446  return true;
4447  // Preserve case of file names in external references even when range
4448  // is not valid and previous check failed tdf#89330
4449  if (bInvalidExternalNameRange)
4450  {
4451  // add ocBad but do not lowercase
4455  return true;
4456  }
4457  if (IsDBRange( aUpper ))
4458  return true;
4459  // If followed by '(' (with or without space inbetween) it can not be a
4460  // column/row label. Prevent arbitrary content detection.
4461  if (!bMayBeFuncName && IsColRowName( aUpper ))
4462  return true;
4463  if (bMayBeFuncName && IsMacro( aUpper ))
4464  return true;
4465  if (bMayBeFuncName && IsOpCode2( aUpper ))
4466  return true;
4467 
4468  } while (mbRewind);
4469 
4470  // Last chance: it could be a broken invalidated reference that contains
4471  // #REF! (but is not equal to), which we also wrote to ODFF between 2013
4472  // and 2016 until 5.1.4
4473  OUString aErrRef( mxSymbols->getSymbol( ocErrRef));
4474  if (aUpper.indexOf( aErrRef) >= 0 && IsReference( aUpper, &aErrRef))
4475  return true;
4476 
4478  {
4479  // set an error
4480  SetError( FormulaError::NoName );
4482  return false; // end compilation
4483  }
4484 
4485  // Provide single token information and continue. Do not set an error, that
4486  // would prematurely end compilation. Simple unknown names are handled by
4487  // the interpreter.
4488  aUpper = pCharClass->lowercase( aUpper );
4492  if ( bAutoCorrect )
4494  return true;
4495 }
4496 
4497 void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp )
4498 {
4499  bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
4500  sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
4501  OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
4502  if( pArr->GetLen() == nExpectedCount )
4503  {
4504  FormulaToken** ppTokens = pArr->GetArray();
4505  // string tokens expected, GetString() will assert if token type is wrong
4506  rFormula = ppTokens[0]->GetString().getString();
4507  if( bExternal )
4508  rFormulaNmsp = ppTokens[1]->GetString().getString();
4509  }
4510 }
4511 
4512 namespace {
4513 
4514 class ExternalFileInserter
4515 {
4516  ScAddress maPos;
4517  ScExternalRefManager& mrRefMgr;
4518 public:
4519  ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) :
4520  maPos(rPos), mrRefMgr(rRefMgr) {}
4521 
4522  void operator() (sal_uInt16 nFileId) const
4523  {
4524  mrRefMgr.insertRefCell(nFileId, maPos);
4525  }
4526 };
4527 
4528 }
4529 
4530 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula )
4531 {
4532  OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
4533  if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
4534  SetGrammar( FormulaGrammar::GRAM_PODF );
4535 
4537  pArr = &aArr;
4539  aFormula = comphelper::string::strip(rFormula, ' ');
4540 
4541  nSrcPos = 0;
4542  bCorrected = false;
4543  if ( bAutoCorrect )
4544  {
4545  aCorrectedFormula.clear();
4546  aCorrectedSymbol.clear();
4547  }
4548  sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible
4549  if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4550  {
4551  nSrcPos++;
4552  nForced++;
4553  if ( bAutoCorrect )
4554  aCorrectedFormula += "=";
4555  }
4556  if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4557  {
4558  nSrcPos++;
4559  nForced++;
4560  if ( bAutoCorrect )
4561  aCorrectedFormula += "=";
4562  }
4563  struct FunctionStack
4564  {
4565  OpCode eOp;
4566  short nSep;
4567  };
4568  // FunctionStack only used if PODF or OOXML!
4569  bool bPODF = FormulaGrammar::isPODF( meGrammar);
4570  bool bOOXML = FormulaGrammar::isOOXML( meGrammar);
4571  bool bUseFunctionStack = (bPODF || bOOXML);
4572  const size_t nAlloc = 512;
4573  FunctionStack aFuncs[ nAlloc ];
4574  FunctionStack* pFunctionStack = (bUseFunctionStack && o3tl::make_unsigned(rFormula.getLength()) > nAlloc ?
4575  new FunctionStack[rFormula.getLength()] : &aFuncs[0]);
4576  pFunctionStack[0].eOp = ocNone;
4577  pFunctionStack[0].nSep = 0;
4578  size_t nFunction = 0;
4579  short nBrackets = 0;
4580  bool bInArray = false;
4581  eLastOp = ocOpen;
4582  while( NextNewToken( bInArray ) )
4583  {
4584  const OpCode eOp = maRawToken.GetOpCode();
4585  if (eOp == ocSkip)
4586  continue;
4587 
4588  switch (eOp)
4589  {
4590  case ocOpen:
4591  {
4592  ++nBrackets;
4593  if (bUseFunctionStack)
4594  {
4595  ++nFunction;
4596  pFunctionStack[ nFunction ].eOp = eLastOp;
4597  pFunctionStack[ nFunction ].nSep = 0;
4598  }
4599  }
4600  break;
4601  case ocClose:
4602  {
4603  if( !nBrackets )
4604  {
4605  SetError( FormulaError::PairExpected );
4606  if ( bAutoCorrect )
4607  {
4608  bCorrected = true;
4609  aCorrectedSymbol.clear();
4610  }
4611  }
4612  else
4613  nBrackets--;
4614  if (bUseFunctionStack && nFunction)
4615  --nFunction;
4616  }
4617  break;
4618  case ocSep:
4619  {
4620  if (bUseFunctionStack)
4621  ++pFunctionStack[ nFunction ].nSep;
4622  }
4623  break;
4624  case ocArrayOpen:
4625  {
4626  if( bInArray )
4627  SetError( FormulaError::NestedArray );
4628  else
4629  bInArray = true;
4630  // Don't count following column separator as parameter separator.
4631  if (bUseFunctionStack)
4632  {
4633  ++nFunction;
4634  pFunctionStack[ nFunction ].eOp = eOp;
4635  pFunctionStack[ nFunction ].nSep = 0;
4636  }
4637  }
4638  break;
4639  case ocArrayClose:
4640  {
4641  if( bInArray )
4642  {
4643  bInArray = false;
4644  }
4645  else
4646  {
4647  SetError( FormulaError::PairExpected );
4648  if ( bAutoCorrect )
4649  {
4650  bCorrected = true;
4651  aCorrectedSymbol.clear();
4652  }
4653  }
4654  if (bUseFunctionStack && nFunction)
4655  --nFunction;
4656  }
4657  break;
4658  case ocTableRefOpen:
4659  {
4660  // Don't count following item separator as parameter separator.
4661  if (bUseFunctionStack)
4662  {
4663  ++nFunction;
4664  pFunctionStack[ nFunction ].eOp = eOp;
4665  pFunctionStack[ nFunction ].nSep = 0;
4666  }
4667  }
4668  break;
4669  case ocTableRefClose:
4670  {
4671  if (bUseFunctionStack && nFunction)
4672  --nFunction;
4673  }
4674  break;
4675  case ocColRowName:
4676  case ocColRowNameAuto:
4677  // The current implementation of column / row labels doesn't
4678  // function correctly in grouped cells.
4679  aArr.SetShareable(false);
4680  break;
4681  default:
4682  break;
4683  }
4684  if ((eLastOp != ocOpen || eOp != ocClose) &&
4685  (eLastOp == ocOpen ||
4686  eLastOp == ocSep ||
4687  eLastOp == ocArrayRowSep ||
4688  eLastOp == ocArrayColSep ||
4689  eLastOp == ocArrayOpen) &&
4690  (eOp == ocSep ||
4691  eOp == ocClose ||
4692  eOp == ocArrayRowSep ||
4693  eOp == ocArrayColSep ||
4694  eOp == ocArrayClose))
4695  {
4696  // TODO: should we check for known functions with optional empty
4697  // args so the correction dialog can do better?
4698  if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
4699  {
4700  SetError(FormulaError::CodeOverflow); break;
4701  }
4702  }
4703  if (bOOXML)
4704  {
4705  // Append a parameter for WEEKNUM, all 1.0
4706  // Function is already closed, parameter count is nSep+1
4707  size_t nFunc = nFunction + 1;
4708  if (eOp == ocClose &&
4709  (pFunctionStack[ nFunc ].eOp == ocWeek && // 2nd week start
4710  pFunctionStack[ nFunc ].nSep == 0))
4711  {
4712  if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4713  !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4714  {
4715  SetError(FormulaError::CodeOverflow); break;
4716  }
4717  }
4718  }
4719  else if (bPODF)
4720  {
4721  /* TODO: for now this is the only PODF adapter. If there were more,
4722  * factor this out. */
4723  // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
4724  if (eOp == ocSep &&
4725  pFunctionStack[ nFunction ].eOp == ocAddress &&
4726  pFunctionStack[ nFunction ].nSep == 3)
4727  {
4728  if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4729  !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4730  {
4731  SetError(FormulaError::CodeOverflow); break;
4732  }
4733  ++pFunctionStack[ nFunction ].nSep;
4734  }
4735  }
4736  FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( maRawToken.CreateToken(rDoc.GetSheetLimits()));
4737  if (!pNewToken && eOp == ocArrayClose && pArr->OpCodeBefore( pArr->GetLen()) == ocArrayClose)
4738  {
4739  // Nested inline array or non-value/non-string in array. The
4740  // original tokens are still in the ScTokenArray and not merged
4741  // into an ScMatrixToken. Set error but keep on tokenizing.
4742  SetError( FormulaError::BadArrayContent);
4743  }
4744  else if (!pNewToken)
4745  {
4746  SetError(FormulaError::CodeOverflow);
4747  break;
4748  }
4749  else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && pNewToken->GetType() == svSingleRef)
4750  {
4751  static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
4752  }
4753  else if (eLastOp == ocDBArea && pNewToken->GetOpCode() == ocTableRefOpen)
4754  {
4755  sal_uInt16 nIdx = pArr->GetLen() - 1;
4756  const FormulaToken* pPrev = pArr->PeekPrev( nIdx);
4757  if (pPrev && pPrev->GetOpCode() == ocDBArea)
4758  {
4759  FormulaToken* pTableRefToken = new ScTableRefToken( pPrev->GetIndex(), ScTableRefToken::TABLE);
4760  maTableRefs.emplace_back( pTableRefToken);
4761  // pPrev may be dead hereafter.
4762  static_cast<ScTokenArray*>(pArr)->ReplaceToken( nIdx, pTableRefToken,
4763  FormulaTokenArray::ReplaceMode::CODE_ONLY);
4764  }
4765  }
4766  switch (eOp)
4767  {
4768  case ocTableRefOpen:
4769  SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
4770  if (maTableRefs.empty())
4771  SetError(FormulaError::Pair);
4772  else
4773  ++maTableRefs.back().mnLevel;
4774  break;
4775  case ocTableRefClose:
4776  SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
4777  if (maTableRefs.empty())
4778  SetError(FormulaError::Pair);
4779  else
4780  {
4781  if (--maTableRefs.back().mnLevel == 0)
4782  maTableRefs.pop_back();
4783  }
4784  break;
4785  default:
4786  break;
4787  }
4789  if ( bAutoCorrect )
4791  }
4792  if ( mbCloseBrackets )
4793  {
4794  if( bInArray )
4795  {
4796  FormulaByteToken aToken( ocArrayClose );
4797  if( !pArr->AddToken( aToken ) )
4798  {
4799  SetError(FormulaError::CodeOverflow);
4800  }
4801  else if ( bAutoCorrect )
4802  aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
4803  }
4804 
4805  if (nBrackets)
4806  {
4807  FormulaToken aToken( svSep, ocClose );
4808  while( nBrackets-- )
4809  {
4810  if( !pArr->AddToken( aToken ) )
4811  {
4812  SetError(FormulaError::CodeOverflow);
4813  break; // while
4814  }
4815  if ( bAutoCorrect )
4816  aCorrectedFormula += mxSymbols->getSymbol(ocClose);
4817  }
4818  }
4819  }
4820  if ( nForced >= 2 )
4822 
4823  if (pFunctionStack != &aFuncs[0])
4824  delete [] pFunctionStack;
4825 
4826  // remember pArr, in case a subsequent CompileTokenArray() is executed.
4827  std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aArr ));
4828  pNew->GenHash();
4829  // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
4830  pArr = pNew.get();
4832 
4833  if (!maExternalFiles.empty())
4834  {
4835  // Remove duplicates, and register all external files found in this cell.
4836  std::sort(maExternalFiles.begin(), maExternalFiles.end());
4837  std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end());
4838  std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *rDoc.GetExternalRefManager()));
4839  maExternalFiles.erase(itEnd, maExternalFiles.end());
4840  }
4841 
4842  return pNew;
4843 }
4844 
4845 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp )
4846 {
4847  OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(),
4848  "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
4849  if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
4850  {
4852  uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
4853  table::CellAddress aReferencePos;
4854  ScUnoConversion::FillApiAddress( aReferencePos, aPos );
4855  uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
4856  ScTokenArray aTokenArray(rDoc);
4857  if( ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, aTokenSeq ) )
4858  {
4859  // remember pArr, in case a subsequent CompileTokenArray() is executed.
4860  std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aTokenArray ));
4861  // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
4862  pArr = pNew.get();
4864  return pNew;
4865  }
4866  }
4867  catch( uno::Exception& )
4868  {
4869  }
4870  // no success - fallback to some internal grammar and hope the best
4871  return CompileString( rFormula );
4872 }
4873 
4875 {
4876  return rDoc.FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex());
4877 }
4878 
4880 {
4881  ScTokenArray* pNew;
4882  const ScRangeData* pRangeData = GetRangeData( *mpToken);
4883  if (pRangeData)
4884  {
4885  FormulaError nErr = pRangeData->GetErrCode();
4886  if( nErr != FormulaError::NONE )
4887  SetError( nErr );
4888  else if (mbJumpCommandReorder)
4889  {
4890  // put named formula into parentheses.
4891  // But only if there aren't any yet, parenthetical
4892  // ocSep doesn't work, e.g. SUM((...;...))
4893  // or if not directly between ocSep/parenthesis,
4894  // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
4895  // in short: if it isn't a self-contained expression.
4898  OpCode eOp1 = (p1 ? p1->GetOpCode() : ocSep);
4899  OpCode eOp2 = (p2 ? p2->GetOpCode() : ocSep);
4900  bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
4901  bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
4902  bool bAddPair = !(bBorder1 && bBorder2);
4903  if ( bAddPair )
4904  {
4905  pNew = new ScTokenArray(rDoc);
4906  pNew->AddOpCode( ocClose );
4907  PushTokenArray( pNew, true );
4908  }
4909  pNew = pRangeData->GetCode()->Clone().release();
4910  pNew->SetFromRangeName( true );
4911  PushTokenArray( pNew, true );
4912  if( pRangeData->HasReferences() )
4913  {
4914  // Relative sheet references in sheet-local named expressions
4915  // shall still point to the same sheet as if used on the
4916  // original sheet, not shifted to the current position where
4917  // they are used.
4918  SCTAB nSheetTab = mpToken->GetSheet();
4919  if (nSheetTab >= 0 && nSheetTab != aPos.Tab())
4920  AdjustSheetLocalNameRelReferences( nSheetTab - aPos.Tab());
4921 
4923  MoveRelWrap();
4924  }
4925  maArrIterator.Reset();
4926  if ( bAddPair )
4927  {
4928  pNew = new ScTokenArray(rDoc);
4929  pNew->AddOpCode( ocOpen );
4930  PushTokenArray( pNew, true );
4931  }
4932  return GetToken();
4933  }
4934  }
4935  else
4936  {
4937  // No ScRangeData for an already compiled token can happen in BIFF .xls
4938  // import if the original range is not present in the document.
4939  pNew = new ScTokenArray(rDoc);
4940  pNew->Add( new FormulaErrorToken( FormulaError::NoName));
4941  PushTokenArray( pNew, true );
4942  return GetToken();
4943  }
4944  return true;
4945 }
4946 
4948 {
4949  // Handle external range names.
4950  switch (_aToken.GetType())
4951  {
4952  case svExternalSingleRef:
4953  case svExternalDoubleRef:
4954  break;
4955  case svExternalName:
4956  {
4958  const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
4959  if (!pFile)
4960  {
4961  SetError(FormulaError::NoName);
4962  return true;
4963  }
4964 
4965  OUString aName = _aToken.GetString().getString();
4967  _aToken.GetIndex(), aName, &aPos);
4968 
4969  if (!xNew)
4970  {
4971  SetError(FormulaError::NoName);
4972  return true;
4973  }
4974 
4975  ScTokenArray* pNew = xNew->Clone().release();
4976  PushTokenArray( pNew, true);
4977  if (FormulaTokenArrayPlainIterator(*pNew).GetNextReference() != nullptr)
4978  {
4980  MoveRelWrap();
4981  }
4982  maArrIterator.Reset();
4983  return GetToken();
4984  }
4985  default:
4986  OSL_FAIL("Wrong type for external reference!");
4987  return false;
4988  }
4989  return true;
4990 }
4991 
4993 {
4994  for ( auto t: pArr->References() )
4995  {
4996  ScSingleRefData& rRef1 = *t->GetSingleRef();
4997  if (rRef1.IsTabRel())
4998  rRef1.IncTab( nDelta);
4999  if ( t->GetType() == svDoubleRef )
5000  {
5001  ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
5002  if (rRef2.IsTabRel())
5003  rRef2.IncTab( nDelta);
5004  }
5005  }
5006 }
5007 
5008 // reference of named range with relative references
5009 
5011 {
5012  for ( auto t: pArr->References() )
5013  {
5014  ScSingleRefData& rRef1 = *t->GetSingleRef();
5015  if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
5016  rRef1.SetRelName( true );
5017  if ( t->GetType() == svDoubleRef )
5018  {
5019  ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
5020  if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
5021  rRef2.SetRelName( true );
5022  }
5023  }
5024 }
5025 
5026 // Wrap-adjust relative references of a RangeName to current position,
5027 // don't call for other token arrays!
5029 {
5030  for ( auto t: pArr->References() )
5031  {
5032  if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
5034  else
5035  ScRefUpdate::MoveRelWrap( rDoc, aPos, rDoc.MaxCol(), rDoc.MaxRow(), *t->GetDoubleRef() );
5036  }
5037 }
5038 
5039 // Wrap-adjust relative references of a RangeName to current position,
5040 // don't call for other token arrays!
5041 void ScCompiler::MoveRelWrap( const ScTokenArray& rArr, const ScDocument& rDoc, const ScAddress& rPos,
5042  SCCOL nMaxCol, SCROW nMaxRow )
5043 {
5044  for ( auto t: rArr.References() )
5045  {
5046  if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
5047  ScRefUpdate::MoveRelWrap( rDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
5048  else
5049  ScRefUpdate::MoveRelWrap( rDoc, rPos, nMaxCol, nMaxRow, *t->GetDoubleRef() );
5050  }
5051 }
5052 
5054  OUString const & rStr, sal_Int32 nPos, ScCharFlags nFlags )
5055 {
5056  sal_Unicode c = rStr[ nPos ];
5057  sal_Unicode cLast = nPos > 0 ? rStr[ nPos-1 ] : 0;
5058  if (c < 128)
5059  {
5062  {
5063  if (pConventions[nConv] &&
5064  ((pConventions[nConv]->getCharTableFlags(c, cLast) & nFlags) != nFlags))
5065  return false;
5066  // convention not known => assume valid
5067  }
5068  return true;
5069  }
5070  else
5071  return ScGlobal::getCharClassPtr()->isLetterNumeric( rStr, nPos );
5072 }
5073 
5074 void ScCompiler::CreateStringFromExternal( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
5075 {
5076  const FormulaToken* t = pTokenP;
5077  sal_uInt16 nFileId = t->GetIndex();
5079  sal_uInt16 nUsedFileId = pRefMgr->convertFileIdToUsedFileId(nFileId);
5080  const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
5081  if (!pFileName)
5082  return;
5083 
5084  switch (t->GetType())
5085  {
5086  case svExternalName:
5087  rBuffer.append(pConv->makeExternalNameStr( nFileId, *pFileName, t->GetString().getString()));
5088  break;
5089  case svExternalSingleRef:
5091  rBuffer, GetPos(), nFileId, *pFileName, t->GetString().getString(),
5092  *t->GetSingleRef());
5093  break;