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