Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / data / global2.cxx
blob6f8352fd18623b945e5400261757f0da9586c0cb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sfx2/docfile.hxx>
21 #include <sfx2/objsh.hxx>
22 #include <unotools/configmgr.hxx>
23 #include <unotools/pathoptions.hxx>
24 #include <tools/urlobj.hxx>
25 #include <svl/numformat.hxx>
26 #include <svl/zforlist.hxx>
27 #include <formula/errorcodes.hxx>
28 #include <sal/log.hxx>
29 #include <rtl/character.hxx>
30 #include <rtl/math.hxx>
31 #include <o3tl/string_view.hxx>
33 #include <global.hxx>
34 #include <rangeutl.hxx>
35 #include <compiler.hxx>
36 #include <paramisc.hxx>
37 #include <calcconfig.hxx>
39 // struct ScImportParam:
41 ScImportParam::ScImportParam() :
42 nCol1(0),
43 nRow1(0),
44 nCol2(0),
45 nRow2(0),
46 bImport(false),
47 bNative(false),
48 bSql(true),
49 nType(ScDbTable)
53 ScImportParam::ScImportParam( const ScImportParam& r ) :
54 nCol1 (r.nCol1),
55 nRow1 (r.nRow1),
56 nCol2 (r.nCol2),
57 nRow2 (r.nRow2),
58 bImport (r.bImport),
59 aDBName (r.aDBName),
60 aStatement (r.aStatement),
61 bNative (r.bNative),
62 bSql (r.bSql),
63 nType (r.nType)
67 ScImportParam::~ScImportParam()
71 ScImportParam& ScImportParam::operator=( const ScImportParam& r )
73 nCol1 = r.nCol1;
74 nRow1 = r.nRow1;
75 nCol2 = r.nCol2;
76 nRow2 = r.nRow2;
77 bImport = r.bImport;
78 aDBName = r.aDBName;
79 aStatement = r.aStatement;
80 bNative = r.bNative;
81 bSql = r.bSql;
82 nType = r.nType;
84 return *this;
87 bool ScImportParam::operator==( const ScImportParam& rOther ) const
89 return( nCol1 == rOther.nCol1 &&
90 nRow1 == rOther.nRow1 &&
91 nCol2 == rOther.nCol2 &&
92 nRow2 == rOther.nRow2 &&
93 bImport == rOther.bImport &&
94 aDBName == rOther.aDBName &&
95 aStatement == rOther.aStatement &&
96 bNative == rOther.bNative &&
97 bSql == rOther.bSql &&
98 nType == rOther.nType );
100 //TODO: are nQuerySh and pConnection equal ?
103 // struct ScConsolidateParam:
105 ScConsolidateParam::ScConsolidateParam()
107 Clear();
110 ScConsolidateParam::ScConsolidateParam( const ScConsolidateParam& r )
112 operator=(r);
115 ScConsolidateParam::~ScConsolidateParam()
119 void ScConsolidateParam::ClearDataAreas()
121 pDataAreas.reset();
122 nDataAreaCount = 0;
125 void ScConsolidateParam::Clear()
127 ClearDataAreas();
129 nCol = 0;
130 nRow = 0;
131 nTab = 0;
132 bByCol = bByRow = bReferenceData = false;
133 eFunction = SUBTOTAL_FUNC_SUM;
136 ScConsolidateParam& ScConsolidateParam::operator=( const ScConsolidateParam& r )
138 if (this != &r)
140 nCol = r.nCol;
141 nRow = r.nRow;
142 nTab = r.nTab;
143 bByCol = r.bByCol;
144 bByRow = r.bByRow;
145 bReferenceData = r.bReferenceData;
146 eFunction = r.eFunction;
147 nDataAreaCount = r.nDataAreaCount;
148 if ( r.nDataAreaCount > 0 )
150 nDataAreaCount = r.nDataAreaCount;
151 pDataAreas.reset( new ScArea[nDataAreaCount] );
152 for ( sal_uInt16 i=0; i<nDataAreaCount; i++ )
153 pDataAreas[i] = r.pDataAreas[i];
155 else
156 pDataAreas.reset();
158 return *this;
161 bool ScConsolidateParam::operator==( const ScConsolidateParam& r ) const
163 bool bEqual = (nCol == r.nCol)
164 && (nRow == r.nRow)
165 && (nTab == r.nTab)
166 && (bByCol == r.bByCol)
167 && (bByRow == r.bByRow)
168 && (bReferenceData == r.bReferenceData)
169 && (nDataAreaCount == r.nDataAreaCount)
170 && (eFunction == r.eFunction);
172 if ( nDataAreaCount == 0 )
173 bEqual = bEqual && (pDataAreas == nullptr) && (r.pDataAreas == nullptr);
174 else
175 bEqual = bEqual && (pDataAreas != nullptr) && (r.pDataAreas != nullptr);
177 if ( bEqual && (nDataAreaCount > 0) )
178 for ( sal_uInt16 i=0; i<nDataAreaCount && bEqual; i++ )
179 bEqual = pDataAreas[i] == r.pDataAreas[i];
181 return bEqual;
184 void ScConsolidateParam::SetAreas( std::unique_ptr<ScArea[]> pAreas, sal_uInt16 nCount )
186 pDataAreas = std::move(pAreas);
187 nDataAreaCount = nCount;
190 // struct ScSolveParam
192 ScSolveParam::ScSolveParam()
196 ScSolveParam::ScSolveParam( const ScSolveParam& r )
197 : aRefFormulaCell ( r.aRefFormulaCell ),
198 aRefVariableCell( r.aRefVariableCell ),
199 pStrTargetVal ( r.pStrTargetVal )
203 ScSolveParam::ScSolveParam( const ScAddress& rFormulaCell,
204 const ScAddress& rVariableCell,
205 const OUString& rTargetValStr )
206 : aRefFormulaCell ( rFormulaCell ),
207 aRefVariableCell( rVariableCell ),
208 pStrTargetVal ( rTargetValStr )
212 ScSolveParam::~ScSolveParam()
216 ScSolveParam& ScSolveParam::operator=( const ScSolveParam& r )
218 aRefFormulaCell = r.aRefFormulaCell;
219 aRefVariableCell = r.aRefVariableCell;
220 pStrTargetVal = r.pStrTargetVal;
221 return *this;
224 bool ScSolveParam::operator==( const ScSolveParam& r ) const
226 bool bEqual = (aRefFormulaCell == r.aRefFormulaCell)
227 && (aRefVariableCell == r.aRefVariableCell);
229 if ( bEqual )
231 if ( !pStrTargetVal && !r.pStrTargetVal )
232 bEqual = true;
233 else if ( !pStrTargetVal || !r.pStrTargetVal )
234 bEqual = false;
235 else
236 bEqual = ( *pStrTargetVal == *(r.pStrTargetVal) );
239 return bEqual;
242 // struct ScTabOpParam
244 ScTabOpParam::ScTabOpParam() : meMode(Column) {}
246 ScTabOpParam::ScTabOpParam( const ScTabOpParam& r )
247 : aRefFormulaCell ( r.aRefFormulaCell ),
248 aRefFormulaEnd ( r.aRefFormulaEnd ),
249 aRefRowCell ( r.aRefRowCell ),
250 aRefColCell ( r.aRefColCell ),
251 meMode(r.meMode)
255 ScTabOpParam::ScTabOpParam( const ScRefAddress& rFormulaCell,
256 const ScRefAddress& rFormulaEnd,
257 const ScRefAddress& rRowCell,
258 const ScRefAddress& rColCell,
259 Mode eMode )
260 : aRefFormulaCell ( rFormulaCell ),
261 aRefFormulaEnd ( rFormulaEnd ),
262 aRefRowCell ( rRowCell ),
263 aRefColCell ( rColCell ),
264 meMode(eMode)
268 ScTabOpParam& ScTabOpParam::operator=( const ScTabOpParam& r )
270 aRefFormulaCell = r.aRefFormulaCell;
271 aRefFormulaEnd = r.aRefFormulaEnd;
272 aRefRowCell = r.aRefRowCell;
273 aRefColCell = r.aRefColCell;
274 meMode = r.meMode;
275 return *this;
278 bool ScTabOpParam::operator==( const ScTabOpParam& r ) const
280 return ( (aRefFormulaCell == r.aRefFormulaCell)
281 && (aRefFormulaEnd == r.aRefFormulaEnd)
282 && (aRefRowCell == r.aRefRowCell)
283 && (aRefColCell == r.aRefColCell)
284 && (meMode == r.meMode) );
287 OUString ScGlobal::GetAbsDocName( const OUString& rFileName,
288 const SfxObjectShell* pShell )
290 OUString aAbsName;
291 if (!pShell || !pShell->HasName())
292 { // maybe relative to document path working directory
293 INetURLObject aObj;
294 if (!utl::ConfigManager::IsFuzzing())
296 aObj.SetSmartURL(SvtPathOptions().GetWorkPath());
297 aObj.setFinalSlash(); // it IS a path
299 else
300 aObj.SetSmartURL(u"file:///tmp/document");
301 bool bWasAbs = true;
302 aAbsName = aObj.smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::DecodeMechanism::NONE);
303 // returned string must be encoded because it's used directly to create SfxMedium
305 else
307 const SfxMedium* pMedium = pShell->GetMedium();
308 if ( pMedium )
310 bool bWasAbs = true;
311 aAbsName = pMedium->GetURLObject().smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::DecodeMechanism::NONE);
313 else
314 { // This can't happen, but ...
315 // just to be sure to have the same encoding
316 INetURLObject aObj;
317 aObj.SetSmartURL( aAbsName );
318 aAbsName = aObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
321 return aAbsName;
324 OUString ScGlobal::GetDocTabName( std::u16string_view rFileName,
325 std::u16string_view rTabName )
327 OUString aDocTab(rFileName);
328 // "'Doc'#Tab"
329 aDocTab = "'" + aDocTab.replaceAll(u"'", u"\\'") + "'" + OUStringChar(SC_COMPILER_FILE_TAB_SEP) + rTabName;
330 return aDocTab;
333 namespace
335 bool isEmptyString( const OUString& rStr )
337 if (rStr.isEmpty())
338 return true;
339 else if (rStr[0] == ' ')
341 const sal_Unicode* p = rStr.getStr() + 1;
342 const sal_Unicode* const pStop = p - 1 + rStr.getLength();
343 while (p < pStop && *p == ' ')
344 ++p;
345 if (p == pStop)
346 return true;
348 return false;
352 double ScGlobal::ConvertStringToValue( const OUString& rStr, const ScCalcConfig& rConfig,
353 FormulaError & rError, FormulaError nStringNoValueError,
354 SvNumberFormatter* pFormatter, SvNumFormatType & rCurFmtType )
356 // We keep ScCalcConfig::StringConversion::LOCALE default until
357 // we provide a friendly way to convert string numbers into numbers in the UI.
359 double fValue = 0.0;
360 if (nStringNoValueError == FormulaError::CellNoValue)
362 // Requested that all strings result in 0, error handled by caller.
363 rError = nStringNoValueError;
364 return fValue;
367 switch (rConfig.meStringConversion)
369 case ScCalcConfig::StringConversion::ILLEGAL:
370 rError = nStringNoValueError;
371 return fValue;
372 case ScCalcConfig::StringConversion::ZERO:
373 return fValue;
374 case ScCalcConfig::StringConversion::LOCALE:
376 if (rConfig.mbEmptyStringAsZero)
378 // The number scanner does not accept empty strings or strings
379 // containing only spaces, be on par in these cases with what was
380 // accepted in OOo and is in AOO (see also the
381 // StringConversion::UNAMBIGUOUS branch) and convert to 0 to prevent
382 // interoperability nightmares.
384 if (isEmptyString( rStr))
385 return fValue;
388 if (!pFormatter)
389 goto Label_fallback_to_unambiguous;
391 sal_uInt32 nFIndex = 0;
392 if (!pFormatter->IsNumberFormat(rStr, nFIndex, fValue))
394 rError = nStringNoValueError;
395 fValue = 0.0;
397 return fValue;
399 break;
400 case ScCalcConfig::StringConversion::UNAMBIGUOUS:
401 Label_fallback_to_unambiguous:
403 if (!rConfig.mbEmptyStringAsZero)
405 if (isEmptyString( rStr))
407 rError = nStringNoValueError;
408 return fValue;
412 // continue below, pulled from switch case for better readability
413 break;
416 rtl_math_ConversionStatus eStatus;
417 sal_Int32 nParseEnd;
418 // Decimal and group separator 0 => only integer and possibly exponent,
419 // stops at first non-digit non-sign.
420 fValue = ::rtl::math::stringToDouble( rStr, 0, 0, &eStatus, &nParseEnd);
421 sal_Int32 nLen = rStr.getLength();
422 if (eStatus == rtl_math_ConversionStatus_Ok && nParseEnd < nLen)
424 // Not at string end, check for trailing blanks or switch to date or
425 // time parsing or bail out.
426 const sal_Unicode* const pStart = rStr.getStr();
427 const sal_Unicode* p = pStart + nParseEnd;
428 const sal_Unicode* const pStop = pStart + nLen;
429 switch (*p++)
431 case ' ':
432 while (p < pStop && *p == ' ')
433 ++p;
434 if (p < pStop)
435 rError = nStringNoValueError;
436 break;
437 case '-':
438 case ':':
440 bool bDate = (*(p-1) == '-');
441 enum State { year = 0, month, day, hour, minute, second, fraction, done, blank, stop };
442 sal_Int32 nUnit[done] = {0,0,0,0,0,0,0};
443 const sal_Int32 nLimit[done] = {0,12,31,0,59,59,0};
444 State eState = (bDate ? month : minute);
445 rCurFmtType = (bDate ? SvNumFormatType::DATE : SvNumFormatType::TIME);
446 nUnit[eState-1] = o3tl::toInt32(rStr.subView( 0, nParseEnd));
447 const sal_Unicode* pLastStart = p;
448 // Ensure there's no preceding sign. Negative dates
449 // currently aren't handled correctly. Also discard
450 // +CCYY-MM-DD
451 p = pStart;
452 while (p < pStop && *p == ' ')
453 ++p;
454 if (p < pStop && !rtl::isAsciiDigit(*p))
455 rError = nStringNoValueError;
456 p = pLastStart;
457 while (p < pStop && rError == FormulaError::NONE && eState < blank)
459 if (eState == minute)
460 rCurFmtType |= SvNumFormatType::TIME;
461 if (rtl::isAsciiDigit(*p))
463 // Maximum 2 digits per unit, except fractions.
464 if (p - pLastStart >= 2 && eState != fraction)
465 rError = nStringNoValueError;
467 else if (p > pLastStart)
469 // We had at least one digit.
470 if (eState < done)
472 nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
473 if (nLimit[eState] && nLimit[eState] < nUnit[eState])
474 rError = nStringNoValueError;
476 pLastStart = p + 1; // hypothetical next start
477 // Delimiters must match, a trailing delimiter
478 // yields an invalid date/time.
479 switch (eState)
481 case month:
482 // Month must be followed by separator and
483 // day, no trailing blanks.
484 if (*p != '-' || (p+1 == pStop))
485 rError = nStringNoValueError;
486 break;
487 case day:
488 if ((*p != 'T' || (p+1 == pStop)) && *p != ' ')
489 rError = nStringNoValueError;
490 // Take one blank as a valid delimiter
491 // between date and time.
492 break;
493 case hour:
494 // Hour must be followed by separator and
495 // minute, no trailing blanks.
496 if (*p != ':' || (p+1 == pStop))
497 rError = nStringNoValueError;
498 break;
499 case minute:
500 if ((*p != ':' || (p+1 == pStop)) && *p != ' ')
501 rError = nStringNoValueError;
502 if (*p == ' ')
503 eState = done;
504 break;
505 case second:
506 if (((*p != ',' && *p != '.') || (p+1 == pStop)) && *p != ' ')
507 rError = nStringNoValueError;
508 if (*p == ' ')
509 eState = done;
510 break;
511 case fraction:
512 eState = done;
513 break;
514 default:
515 rError = nStringNoValueError;
516 break;
518 eState = static_cast<State>(eState + 1);
520 else
521 rError = nStringNoValueError;
522 ++p;
524 if (eState == blank)
526 while (p < pStop && *p == ' ')
527 ++p;
528 if (p < pStop)
529 rError = nStringNoValueError;
530 eState = stop;
533 // Month without day, or hour without minute.
534 if (eState == month || (eState == day && p <= pLastStart) ||
535 eState == hour || (eState == minute && p <= pLastStart))
536 rError = nStringNoValueError;
538 if (rError == FormulaError::NONE)
540 // Catch the very last unit at end of string.
541 if (p > pLastStart && eState < done)
543 nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
544 if (nLimit[eState] && nLimit[eState] < nUnit[eState])
545 rError = nStringNoValueError;
547 if (bDate && nUnit[hour] > 23)
548 rError = nStringNoValueError;
549 if (rError == FormulaError::NONE)
551 if (bDate && nUnit[day] == 0)
552 nUnit[day] = 1;
553 double fFraction = (nUnit[fraction] <= 0 ? 0.0 :
554 ::rtl::math::pow10Exp( nUnit[fraction],
555 static_cast<int>( -ceil( log10( static_cast<double>( nUnit[fraction]))))));
556 if (!bDate)
557 fValue = 0.0;
558 else
560 Date aDate(
561 sal::static_int_cast<sal_Int16>(nUnit[day]),
562 sal::static_int_cast<sal_Int16>(nUnit[month]),
563 sal::static_int_cast<sal_Int16>(nUnit[year]));
564 if (!aDate.IsValidDate())
565 rError = nStringNoValueError;
566 else
568 if (pFormatter)
569 fValue = aDate - pFormatter->GetNullDate();
570 else
572 SAL_WARN("sc.core","ScGlobal::ConvertStringToValue - fixed null date");
573 static Date aDefaultNullDate( 30, 12, 1899);
574 fValue = aDate - aDefaultNullDate;
578 fValue += ((nUnit[hour] * 3600) + (nUnit[minute] * 60) + nUnit[second] + fFraction) / 86400.0;
582 break;
583 default:
584 rError = nStringNoValueError;
586 if (rError != FormulaError::NONE)
587 fValue = 0.0;
589 return fValue;
592 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */