Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / tool / reffind.cxx
blob10522310f851b3e4f259e2efd7b2bc72508af1c1
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 <sal/config.h>
22 #include <o3tl/underlyingenumvalue.hxx>
24 #include <reffind.hxx>
25 #include <global.hxx>
26 #include <compiler.hxx>
27 #include <document.hxx>
28 #include <utility>
30 namespace {
32 // Include colon; addresses in range reference are handled individually.
33 const sal_Unicode pDelimiters[] = {
34 '=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
37 bool IsText( sal_Unicode c )
39 bool bFound = ScGlobal::UnicodeStrChr( pDelimiters, c );
40 if (bFound)
41 // This is one of delimiters, therefore not text.
42 return false;
44 // argument separator is configurable.
45 const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
46 return c != sep;
49 bool IsText( bool& bQuote, sal_Unicode c )
51 if (c == '\'')
53 bQuote = !bQuote;
54 return true;
56 if (bQuote)
57 return true;
59 return IsText(c);
62 /**
63 * Find first character position that is considered text. A character is
64 * considered a text when it's within the ascii range and when it's not a
65 * delimiter.
67 sal_Int32 FindStartPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
69 while (nStartPos <= nEndPos && !IsText(p[nStartPos]))
70 ++nStartPos;
72 return nStartPos;
75 sal_Int32 FindEndPosA1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
77 bool bQuote = false;
78 sal_Int32 nNewEnd = nStartPos;
79 while (nNewEnd <= nEndPos && IsText(bQuote, p[nNewEnd]))
80 ++nNewEnd;
82 return nNewEnd;
85 sal_Int32 FindEndPosR1C1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
87 sal_Int32 nNewEnd = nStartPos;
88 p = &p[nStartPos];
89 for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
91 if (*p == '\'')
93 // Skip until the closing quote.
94 for (++p, ++nNewEnd; nNewEnd <= nEndPos; ++p, ++nNewEnd)
95 if (*p == '\'')
96 break;
97 if (nNewEnd > nEndPos)
98 break;
100 else if (*p == '[')
102 // Skip until the closing bracket.
103 for (++p, ++nNewEnd; nNewEnd <= nEndPos; ++p, ++nNewEnd)
104 if (*p == ']')
105 break;
106 if (nNewEnd > nEndPos)
107 break;
109 else if (!IsText(*p))
110 break;
113 return nNewEnd;
117 * Find last character position that is considered text, from the specified
118 * start position.
120 sal_Int32 FindEndPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos,
121 formula::FormulaGrammar::AddressConvention eConv)
123 switch (eConv)
125 case formula::FormulaGrammar::CONV_XL_R1C1:
126 return FindEndPosR1C1(p, nStartPos, nEndPos);
127 case formula::FormulaGrammar::CONV_OOO:
128 case formula::FormulaGrammar::CONV_XL_A1:
129 default:
130 return FindEndPosA1(p, nStartPos, nEndPos);
134 void ExpandToTextA1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
136 bool bQuote = false; // skip quoted text
137 while (rStartPos > 0 && IsText(bQuote, p[rStartPos - 1]) )
138 --rStartPos;
139 if (rEndPos)
140 --rEndPos;
141 while (rEndPos+1 < nLen && IsText(p[rEndPos + 1]) )
142 ++rEndPos;
145 void ExpandToTextR1C1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
147 // move back the start position to the first text character.
148 if (rStartPos > 0)
150 for (--rStartPos; rStartPos > 0; --rStartPos)
152 sal_Unicode c = p[rStartPos];
153 if (c == '\'')
155 // Skip until the opening quote.
156 for (--rStartPos; rStartPos > 0; --rStartPos)
158 c = p[rStartPos];
159 if (c == '\'')
160 break;
162 if (rStartPos == 0)
163 break;
165 else if (c == ']')
167 // Skip until the opening bracket.
168 for (--rStartPos; rStartPos > 0; --rStartPos)
170 c = p[rStartPos];
171 if (c == '[')
172 break;
174 if (rStartPos == 0)
175 break;
177 else if (!IsText(c))
179 ++rStartPos;
180 break;
185 // move forward the end position to the last text character.
186 rEndPos = FindEndPosR1C1(p, rEndPos, nLen-1);
189 void ExpandToText(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos,
190 formula::FormulaGrammar::AddressConvention eConv)
192 switch (eConv)
194 case formula::FormulaGrammar::CONV_XL_R1C1:
195 ExpandToTextR1C1(p, nLen, rStartPos, rEndPos);
196 break;
197 case formula::FormulaGrammar::CONV_OOO:
198 case formula::FormulaGrammar::CONV_XL_A1:
199 default:
200 ExpandToTextA1(p, nLen, rStartPos, rEndPos);
206 ScRefFinder::ScRefFinder(
207 OUString aFormula, const ScAddress& rPos,
208 ScDocument& rDoc, formula::FormulaGrammar::AddressConvention eConvP) :
209 maFormula(std::move(aFormula)),
210 meConv(eConvP),
211 mrDoc(rDoc),
212 maPos(rPos),
213 mnFound(0),
214 mnSelStart(0),
215 mnSelEnd(0)
219 ScRefFinder::~ScRefFinder()
223 static ScRefFlags lcl_NextFlags( ScRefFlags nOld )
225 const ScRefFlags Mask_ABS = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS | ScRefFlags::TAB_ABS;
226 ScRefFlags nNew = nOld & Mask_ABS;
227 nNew = ScRefFlags( o3tl::to_underlying(nNew) - 1 ) & Mask_ABS; // weiterzaehlen
229 if (!(nOld & ScRefFlags::TAB_3D))
230 nNew &= ~ScRefFlags::TAB_ABS; // not 3D -> never absolute!
232 return (nOld & ~Mask_ABS) | nNew;
235 void ScRefFinder::ToggleRel( sal_Int32 nStartPos, sal_Int32 nEndPos )
237 sal_Int32 nLen = maFormula.getLength();
238 if (nLen <= 0)
239 return;
240 const sal_Unicode* pSource = maFormula.getStr(); // for quick access
242 // expand selection, and instead of selection start- and end-index
244 if ( nEndPos < nStartPos )
245 ::std::swap(nEndPos, nStartPos);
247 ExpandToText(pSource, nLen, nStartPos, nEndPos, meConv);
249 OUStringBuffer aResult;
250 OUString aExpr;
251 OUString aSep;
252 ScAddress aAddr;
253 mnFound = 0;
255 sal_Int32 nLoopStart = nStartPos;
256 while ( nLoopStart <= nEndPos )
258 // Determine the start and end positions of a text segment. Note that
259 // the end position returned from FindEndPos may be one position after
260 // the last character position in case of the last segment.
261 sal_Int32 nEStart = FindStartPos(pSource, nLoopStart, nEndPos);
262 sal_Int32 nEEnd = FindEndPos(pSource, nEStart, nEndPos, meConv);
264 aSep = maFormula.copy(nLoopStart, nEStart-nLoopStart);
265 if (nEEnd < maFormula.getLength())
266 aExpr = maFormula.copy(nEStart, nEEnd-nEStart);
267 else
268 aExpr = maFormula.copy(nEStart);
270 // Check the validity of the expression, and toggle the relative flag.
271 ScAddress::Details aDetails(meConv, maPos.Row(), maPos.Col());
272 ScAddress::ExternalInfo aExtInfo;
273 ScRefFlags nResult = aAddr.Parse(aExpr, mrDoc, aDetails, &aExtInfo);
274 if ( nResult & ScRefFlags::VALID )
276 ScRefFlags nFlags;
277 if( aExtInfo.mbExternal )
278 { // retain external doc name and tab name before toggle relative flag
279 sal_Int32 nSep;
280 switch(meConv)
282 case formula::FormulaGrammar::CONV_XL_A1 :
283 case formula::FormulaGrammar::CONV_XL_OOX :
284 case formula::FormulaGrammar::CONV_XL_R1C1 :
285 nSep = aExpr.lastIndexOf('!');
286 break;
287 case formula::FormulaGrammar::CONV_OOO :
288 default:
289 nSep = aExpr.lastIndexOf('.');
290 break;
292 if (nSep >= 0)
294 OUString aRef = aExpr.copy(nSep+1);
295 std::u16string_view aExtDocNameTabName = aExpr.subView(0, nSep+1);
296 nResult = aAddr.Parse(aRef, mrDoc, aDetails);
297 aAddr.SetTab(0); // force to first tab to avoid error on checking
298 nFlags = lcl_NextFlags( nResult );
299 aExpr = aExtDocNameTabName + aAddr.Format(nFlags, &mrDoc, aDetails);
301 else
303 assert(!"Invalid syntax according to address convention.");
306 else
308 nFlags = lcl_NextFlags( nResult );
309 aExpr = aAddr.Format(nFlags, &mrDoc, aDetails);
312 sal_Int32 nAbsStart = nStartPos+aResult.getLength()+aSep.getLength();
314 if (!mnFound) // first reference ?
315 mnSelStart = nAbsStart;
316 mnSelEnd = nAbsStart + aExpr.getLength(); // selection, no indices
317 ++mnFound;
320 // assemble
322 aResult.append(aSep + aExpr);
324 nLoopStart = nEEnd;
327 OUString aTotal = maFormula.subView(0, nStartPos) + aResult;
328 if (nEndPos < maFormula.getLength()-1)
329 aTotal += maFormula.subView(nEndPos+1);
331 maFormula = aTotal;
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */