Avoid potential negative array index access to cached text.
[LibreOffice.git] / formula / source / ui / dlg / FormulaHelper.cxx
blob225cef3c7be0d74c085401575d757a75f27d46ac
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 <algorithm>
22 #include <formula/formulahelper.hxx>
23 #include <formula/IFunctionDescription.hxx>
24 #include <unotools/charclass.hxx>
25 #include <unotools/syslocale.hxx>
27 namespace formula
30 namespace
33 class OEmptyFunctionDescription : public IFunctionDescription
35 public:
36 OEmptyFunctionDescription(){}
37 virtual ~OEmptyFunctionDescription(){}
39 virtual OUString getFunctionName() const override { return OUString(); }
40 virtual const IFunctionCategory* getCategory() const override { return nullptr; }
41 virtual OUString getDescription() const override { return OUString(); }
42 virtual sal_Int32 getSuppressedArgumentCount() const override { return 0; }
43 virtual OUString getFormula(const ::std::vector< OUString >& ) const override { return OUString(); }
44 virtual void fillVisibleArgumentMapping(::std::vector<sal_uInt16>& ) const override {}
45 virtual void initArgumentInfo() const override {}
46 virtual OUString getSignature() const override { return OUString(); }
47 virtual OUString getHelpId() const override { return ""; }
48 virtual bool isHidden() const override { return false; }
49 virtual sal_uInt32 getParameterCount() const override { return 0; }
50 virtual sal_uInt32 getVarArgsStart() const override { return 0; }
51 virtual sal_uInt32 getVarArgsLimit() const override { return 0; }
52 virtual OUString getParameterName(sal_uInt32 ) const override { return OUString(); }
53 virtual OUString getParameterDescription(sal_uInt32 ) const override { return OUString(); }
54 virtual bool isParameterOptional(sal_uInt32 ) const override { return false; }
58 // class FormulaHelper - static Method
61 #define FUNC_NOTFOUND -1
63 FormulaHelper::FormulaHelper(const IFunctionManager* _pFunctionManager)
64 :m_rCharClass(m_aSysLocale.GetCharClass())
65 ,m_pFunctionManager(_pFunctionManager)
66 ,open(_pFunctionManager->getSingleToken(IFunctionManager::eOk))
67 ,close(_pFunctionManager->getSingleToken(IFunctionManager::eClose))
68 ,sep(_pFunctionManager->getSingleToken(IFunctionManager::eSep))
69 ,arrayOpen(_pFunctionManager->getSingleToken(IFunctionManager::eArrayOpen))
70 ,arrayClose(_pFunctionManager->getSingleToken(IFunctionManager::eArrayClose))
74 sal_Int32 FormulaHelper::GetCategoryCount() const
76 return m_pFunctionManager->getCount();
79 bool FormulaHelper::GetNextFunc( const OUString& rFormula,
80 bool bBack,
81 sal_Int32& rFStart, // Input and output
82 sal_Int32* pFEnd, // = NULL
83 const IFunctionDescription** ppFDesc, // = NULL
84 ::std::vector< OUString>* pArgs ) const // = NULL
86 sal_Int32 nOldStart = rFStart;
87 OUString aFname;
89 rFStart = GetFunctionStart( rFormula, rFStart, bBack, ppFDesc ? &aFname : nullptr );
90 bool bFound = ( rFStart != FUNC_NOTFOUND );
92 if ( bFound )
94 if ( pFEnd )
95 *pFEnd = GetFunctionEnd( rFormula, rFStart );
97 if ( ppFDesc )
99 *ppFDesc = nullptr;
100 const sal_uInt32 nCategoryCount = m_pFunctionManager->getCount();
101 for(sal_uInt32 j= 0; j < nCategoryCount && !*ppFDesc; ++j)
103 const IFunctionCategory* pCategory = m_pFunctionManager->getCategory(j);
104 const sal_uInt32 nCount = pCategory->getCount();
105 for(sal_uInt32 i = 0 ; i < nCount; ++i)
107 const IFunctionDescription* pCurrent = pCategory->getFunction(i);
108 if ( pCurrent->getFunctionName().equalsIgnoreAsciiCase(aFname) )
110 *ppFDesc = pCurrent;
111 break;
113 }// for(sal_uInt32 i = 0 ; i < nCount; ++i)
115 if ( *ppFDesc && pArgs )
117 GetArgStrings( *pArgs,rFormula, rFStart, static_cast<sal_uInt16>((*ppFDesc)->getParameterCount() ));
119 else
121 static OEmptyFunctionDescription s_aFunctionDescription;
122 *ppFDesc = &s_aFunctionDescription;
126 else
127 rFStart = nOldStart;
129 return bFound;
133 void FormulaHelper::FillArgStrings( std::u16string_view rFormula,
134 sal_Int32 nFuncPos,
135 sal_uInt16 nArgs,
136 ::std::vector< OUString >& _rArgs ) const
138 sal_Int32 nStart = 0;
139 sal_Int32 nEnd = 0;
140 sal_uInt16 i;
141 bool bLast = false;
143 for ( i=0; i<nArgs && !bLast; i++ )
145 nStart = GetArgStart( rFormula, nFuncPos, i );
147 if ( i+1<nArgs ) // last argument?
149 nEnd = GetArgStart( rFormula, nFuncPos, i+1 );
151 if ( nEnd != nStart )
152 _rArgs.push_back(OUString(rFormula.substr( nStart, nEnd-1-nStart )));
153 else
155 _rArgs.emplace_back();
156 bLast = true;
159 else
161 nEnd = GetFunctionEnd( rFormula, nFuncPos )-1;
162 if ( nStart < nEnd )
163 _rArgs.push_back( OUString(rFormula.substr( nStart, nEnd-nStart )) );
164 else
165 _rArgs.emplace_back();
169 if ( bLast )
170 for ( ; i<nArgs; i++ )
171 _rArgs.emplace_back();
175 void FormulaHelper::GetArgStrings( ::std::vector< OUString >& _rArgs,
176 std::u16string_view rFormula,
177 sal_Int32 nFuncPos,
178 sal_uInt16 nArgs ) const
180 if (nArgs)
182 FillArgStrings( rFormula, nFuncPos, nArgs, _rArgs );
187 static bool IsFormulaText(const CharClass& rCharClass, const OUString& rStr, sal_Int32 nPos)
189 if( rCharClass.isLetterNumeric( rStr, nPos ) )
190 return true;
191 else
192 { // In internationalized versions function names may contain a dot
193 // and in every version also an underscore... ;-)
194 sal_Unicode c = rStr[nPos];
195 return c == '.' || c == '_';
200 sal_Int32 FormulaHelper::GetFunctionStart( const OUString& rFormula,
201 sal_Int32 nStart,
202 bool bBack,
203 OUString* pFuncName ) const
205 sal_Int32 nStrLen = rFormula.getLength();
207 if ( nStrLen < nStart )
208 return nStart;
210 sal_Int32 nFStart = FUNC_NOTFOUND;
211 sal_Int32 nParPos = bBack ? ::std::min( nStart, nStrLen - 1) : nStart;
213 bool bRepeat;
216 bool bFound = false;
217 bRepeat = false;
219 if ( bBack )
221 while ( !bFound && (nParPos > 0) )
223 if ( rFormula[nParPos] == '"' )
225 nParPos--;
226 while ( (nParPos > 0) && rFormula[nParPos] != '"' )
227 nParPos--;
228 if (nParPos > 0)
229 nParPos--;
231 else
233 bFound = rFormula[nParPos] == '(';
234 if ( !bFound )
235 nParPos--;
239 else
241 while ( !bFound && (0 <= nParPos && nParPos < nStrLen) )
243 if ( rFormula[nParPos] == '"' )
245 nParPos++;
246 while ( (nParPos < nStrLen) && rFormula[nParPos] != '"' )
247 nParPos++;
248 nParPos++;
250 else
252 bFound = rFormula[nParPos] == '(';
253 if ( !bFound )
254 nParPos++;
259 if ( bFound && (nParPos > 0) )
261 nFStart = nParPos-1;
263 while ( (nFStart > 0) && IsFormulaText(m_rCharClass, rFormula, nFStart ))
264 nFStart--;
267 nFStart++;
269 if ( bFound )
271 if ( IsFormulaText( m_rCharClass, rFormula, nFStart ) )
273 // Function found
274 if ( pFuncName )
275 *pFuncName = rFormula.copy( nFStart, nParPos-nFStart );
277 else // Brackets without function -> keep searching
279 bRepeat = true;
280 if ( !bBack )
281 nParPos++;
282 else if (nParPos > 0)
283 nParPos--;
284 else
285 bRepeat = false;
288 else // No brackets found
290 nFStart = FUNC_NOTFOUND;
291 if ( pFuncName )
292 pFuncName->clear();
295 while(bRepeat);
297 return nFStart;
301 sal_Int32 FormulaHelper::GetFunctionEnd( std::u16string_view rStr, sal_Int32 nStart ) const
303 sal_Int32 nStrLen = rStr.size();
305 if ( nStrLen < nStart )
306 return nStart;
308 short nParCount = 0;
309 bool bInArray = false;
310 bool bFound = false;
312 while ( !bFound && (nStart < nStrLen) )
314 sal_Unicode c = rStr[nStart];
316 if ( c == '"' )
318 nStart++;
319 while ( (nStart < nStrLen) && rStr[nStart] != '"' )
320 nStart++;
322 else if ( c == open )
323 nParCount++;
324 else if ( c == close )
326 nParCount--;
327 if ( nParCount == 0 )
328 bFound = true;
329 else if ( nParCount < 0 )
331 bFound = true;
332 nStart--; // read one too far
335 else if ( c == arrayOpen )
337 bInArray = true;
339 else if ( c == arrayClose )
341 bInArray = false;
343 else if ( c == sep )
345 if ( !bInArray && nParCount == 0 )
347 bFound = true;
348 nStart--; // read one too far
351 nStart++; // Set behind found position
354 // nStart > nStrLen can happen if there was an unclosed quote; instead of
355 // checking that in every loop iteration check it once here.
356 return std::min(nStart, nStrLen);
360 sal_Int32 FormulaHelper::GetArgStart( std::u16string_view rStr, sal_Int32 nStart, sal_uInt16 nArg ) const
362 sal_Int32 nStrLen = rStr.size();
364 if ( nStrLen < nStart )
365 return nStart;
367 short nParCount = 0;
368 bool bInArray = false;
369 bool bFound = false;
371 while ( !bFound && (nStart < nStrLen) )
373 sal_Unicode c = rStr[nStart];
375 if ( c == '"' )
377 nStart++;
378 while ( (nStart < nStrLen) && rStr[nStart] != '"' )
379 nStart++;
381 else if ( c == open )
383 bFound = ( nArg == 0 );
384 nParCount++;
386 else if ( c == close )
388 nParCount--;
389 bFound = ( nParCount == 0 );
391 else if ( c == arrayOpen )
393 bInArray = true;
395 else if ( c == arrayClose )
397 bInArray = false;
399 else if ( c == sep )
401 if ( !bInArray && nParCount == 1 )
403 nArg--;
404 bFound = ( nArg == 0 );
407 nStart++;
410 return nStart;
413 } // formula
416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */