Line data Source code
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 <regexp.hxx>
21 :
22 : #include <cstddef>
23 :
24 : #include "osl/diagnose.h"
25 : #include <com/sun/star/lang/IllegalArgumentException.hpp>
26 : #include <rtl/ustrbuf.hxx>
27 : #include <rtl/ustring.hxx>
28 : #include <comphelper/string.hxx>
29 :
30 : namespace unnamed_ucb_regexp {} using namespace unnamed_ucb_regexp;
31 : // unnamed namespaces don't work well yet...
32 :
33 : using namespace com::sun::star;
34 : using namespace ucb_impl;
35 :
36 : //============================================================================
37 : //
38 : // Regexp
39 : //
40 : //============================================================================
41 :
42 312 : inline Regexp::Regexp(Kind eTheKind, rtl::OUString const & rThePrefix,
43 : bool bTheEmptyDomain, rtl::OUString const & rTheInfix,
44 : bool bTheTranslation,
45 : rtl::OUString const & rTheReversePrefix):
46 : m_eKind(eTheKind),
47 : m_aPrefix(rThePrefix),
48 : m_aInfix(rTheInfix),
49 : m_aReversePrefix(rTheReversePrefix),
50 : m_bEmptyDomain(bTheEmptyDomain),
51 312 : m_bTranslation(bTheTranslation)
52 : {
53 : OSL_ASSERT(m_eKind == KIND_DOMAIN
54 : || (!m_bEmptyDomain && m_aInfix.isEmpty()));
55 : OSL_ASSERT(m_bTranslation || m_aReversePrefix.isEmpty());
56 312 : }
57 :
58 : //============================================================================
59 : namespace unnamed_ucb_regexp {
60 :
61 44140 : bool matchStringIgnoreCase(sal_Unicode const ** pBegin,
62 : sal_Unicode const * pEnd,
63 : rtl::OUString const & rString)
64 : {
65 44140 : sal_Unicode const * p = *pBegin;
66 :
67 44140 : sal_Unicode const * q = rString.getStr();
68 44140 : sal_Unicode const * qEnd = q + rString.getLength();
69 :
70 44140 : if (pEnd - p < qEnd - q)
71 617 : return false;
72 :
73 285561 : while (q != qEnd)
74 : {
75 202481 : sal_Unicode c1 = *p++;
76 202481 : sal_Unicode c2 = *q++;
77 202481 : if (c1 >= 'a' && c1 <= 'z')
78 162756 : c1 -= 'a' - 'A';
79 202481 : if (c2 >= 'a' && c2 <= 'z')
80 162756 : c2 -= 'a' - 'A';
81 202481 : if (c1 != c2)
82 3966 : return false;
83 : }
84 :
85 39557 : *pBegin = p;
86 39557 : return true;
87 : }
88 :
89 : }
90 :
91 44140 : bool Regexp::matches(rtl::OUString const & rString,
92 : rtl::OUString * pTranslation, bool * pTranslated) const
93 : {
94 44140 : sal_Unicode const * pBegin = rString.getStr();
95 44140 : sal_Unicode const * pEnd = pBegin + rString.getLength();
96 :
97 44140 : bool bMatches = false;
98 :
99 44140 : sal_Unicode const * p = pBegin;
100 44140 : if (matchStringIgnoreCase(&p, pEnd, m_aPrefix))
101 : {
102 39557 : sal_Unicode const * pBlock1Begin = p;
103 39557 : sal_Unicode const * pBlock1End = pEnd;
104 :
105 39557 : sal_Unicode const * pBlock2Begin = 0;
106 39557 : sal_Unicode const * pBlock2End = 0;
107 :
108 39557 : switch (m_eKind)
109 : {
110 : case KIND_PREFIX:
111 39557 : bMatches = true;
112 39557 : break;
113 :
114 : case KIND_AUTHORITY:
115 0 : bMatches = p == pEnd || *p == '/' || *p == '?' || *p == '#';
116 0 : break;
117 :
118 : case KIND_DOMAIN:
119 0 : if (!m_bEmptyDomain)
120 : {
121 0 : if (p == pEnd || *p == '/' || *p == '?' || *p == '#')
122 0 : break;
123 0 : ++p;
124 : }
125 0 : for (;;)
126 : {
127 0 : sal_Unicode const * q = p;
128 0 : if (matchStringIgnoreCase(&q, pEnd, m_aInfix)
129 : && (q == pEnd || *q == '/' || *q == '?' || *q == '#'))
130 : {
131 0 : bMatches = true;
132 0 : pBlock1End = p;
133 0 : pBlock2Begin = q;
134 0 : pBlock2End = pEnd;
135 : break;
136 : }
137 :
138 0 : if (p == pEnd)
139 : break;
140 :
141 0 : sal_Unicode c = *p++;
142 0 : if (c == '/' || c == '?' || c == '#')
143 : break;
144 : }
145 0 : break;
146 : }
147 :
148 39557 : if (bMatches)
149 : {
150 39557 : if (m_bTranslation)
151 : {
152 0 : if (pTranslation)
153 : {
154 0 : rtl::OUStringBuffer aBuffer(m_aReversePrefix);
155 0 : aBuffer.append(pBlock1Begin, pBlock1End - pBlock1Begin);
156 0 : aBuffer.append(m_aInfix);
157 0 : aBuffer.append(pBlock2Begin, pBlock2End - pBlock2Begin);
158 0 : *pTranslation = aBuffer.makeStringAndClear();
159 : }
160 0 : if (pTranslated)
161 0 : *pTranslated = true;
162 : }
163 : else
164 : {
165 39557 : if (pTranslation)
166 0 : *pTranslation = rString;
167 39557 : if (pTranslated)
168 0 : *pTranslated = false;
169 : }
170 : }
171 : }
172 :
173 44140 : return bMatches;
174 : }
175 :
176 : //============================================================================
177 : namespace unnamed_ucb_regexp {
178 :
179 526 : bool isScheme(rtl::OUString const & rString, bool bColon)
180 : {
181 : using comphelper::string::isalphaAscii;
182 : using comphelper::string::isdigitAscii;
183 : // Return true if rString matches <scheme> (plus a trailing ":" if bColon
184 : // is true) from RFC 2396:
185 526 : sal_Unicode const * p = rString.getStr();
186 526 : sal_Unicode const * pEnd = p + rString.getLength();
187 526 : if (p != pEnd && isalphaAscii(*p))
188 4446 : for (++p;;)
189 : {
190 4446 : if (p == pEnd)
191 312 : return !bColon;
192 4134 : sal_Unicode c = *p++;
193 9012 : if (!(isalphaAscii(c) || isdigitAscii(c)
194 4878 : || c == '+' || c == '-' || c == '.'))
195 214 : return bColon && c == ':' && p == pEnd;
196 : }
197 0 : return false;
198 : }
199 :
200 0 : void appendStringLiteral(rtl::OUStringBuffer * pBuffer,
201 : rtl::OUString const & rString)
202 : {
203 : OSL_ASSERT(pBuffer);
204 :
205 0 : pBuffer->append(sal_Unicode('"'));
206 0 : sal_Unicode const * p = rString.getStr();
207 0 : sal_Unicode const * pEnd = p + rString.getLength();
208 0 : while (p != pEnd)
209 : {
210 0 : sal_Unicode c = *p++;
211 0 : if (c == '"' || c == '\\')
212 0 : pBuffer->append(sal_Unicode('\\'));
213 0 : pBuffer->append(c);
214 : }
215 0 : pBuffer->append(sal_Unicode('"'));
216 0 : }
217 :
218 : }
219 :
220 214 : rtl::OUString Regexp::getRegexp(bool bReverse) const
221 : {
222 214 : if (m_bTranslation)
223 : {
224 0 : rtl::OUStringBuffer aBuffer;
225 0 : if (bReverse)
226 : {
227 0 : if (!m_aReversePrefix.isEmpty())
228 0 : appendStringLiteral(&aBuffer, m_aReversePrefix);
229 : }
230 : else
231 : {
232 0 : if (!m_aPrefix.isEmpty())
233 0 : appendStringLiteral(&aBuffer, m_aPrefix);
234 : }
235 0 : switch (m_eKind)
236 : {
237 : case KIND_PREFIX:
238 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("(.*)"));
239 0 : break;
240 :
241 : case KIND_AUTHORITY:
242 : aBuffer.
243 0 : appendAscii(RTL_CONSTASCII_STRINGPARAM("(([/?#].*)?)"));
244 0 : break;
245 :
246 : case KIND_DOMAIN:
247 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("([^/?#]"));
248 0 : aBuffer.append(sal_Unicode(m_bEmptyDomain ? '*' : '+'));
249 0 : if (!m_aInfix.isEmpty())
250 0 : appendStringLiteral(&aBuffer, m_aInfix);
251 : aBuffer.
252 0 : appendAscii(RTL_CONSTASCII_STRINGPARAM("([/?#].*)?)"));
253 0 : break;
254 : }
255 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("->"));
256 0 : if (bReverse)
257 : {
258 0 : if (!m_aPrefix.isEmpty())
259 0 : appendStringLiteral(&aBuffer, m_aPrefix);
260 : }
261 : else
262 : {
263 0 : if (!m_aReversePrefix.isEmpty())
264 0 : appendStringLiteral(&aBuffer, m_aReversePrefix);
265 : }
266 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("\\1"));
267 0 : return aBuffer.makeStringAndClear();
268 : }
269 214 : else if (m_eKind == KIND_PREFIX && isScheme(m_aPrefix, true))
270 214 : return m_aPrefix.copy(0, m_aPrefix.getLength() - 1);
271 : else
272 : {
273 0 : rtl::OUStringBuffer aBuffer;
274 0 : if (!m_aPrefix.isEmpty())
275 0 : appendStringLiteral(&aBuffer, m_aPrefix);
276 0 : switch (m_eKind)
277 : {
278 : case KIND_PREFIX:
279 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM(".*"));
280 0 : break;
281 :
282 : case KIND_AUTHORITY:
283 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("([/?#].*)?"));
284 0 : break;
285 :
286 : case KIND_DOMAIN:
287 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("[^/?#]"));
288 0 : aBuffer.append(sal_Unicode(m_bEmptyDomain ? '*' : '+'));
289 0 : if (!m_aInfix.isEmpty())
290 0 : appendStringLiteral(&aBuffer, m_aInfix);
291 0 : aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("([/?#].*)?"));
292 0 : break;
293 : }
294 0 : return aBuffer.makeStringAndClear();
295 : }
296 : }
297 :
298 : //============================================================================
299 : namespace unnamed_ucb_regexp {
300 :
301 0 : bool matchString(sal_Unicode const ** pBegin, sal_Unicode const * pEnd,
302 : sal_Char const * pString, size_t nStringLength)
303 : {
304 0 : sal_Unicode const * p = *pBegin;
305 :
306 0 : sal_uChar const * q = reinterpret_cast< sal_uChar const * >(pString);
307 0 : sal_uChar const * qEnd = q + nStringLength;
308 :
309 0 : if (pEnd - p < qEnd - q)
310 0 : return false;
311 :
312 0 : while (q != qEnd)
313 : {
314 0 : sal_Unicode c1 = *p++;
315 0 : sal_Unicode c2 = *q++;
316 0 : if (c1 != c2)
317 0 : return false;
318 : }
319 :
320 0 : *pBegin = p;
321 0 : return true;
322 : }
323 :
324 0 : bool scanStringLiteral(sal_Unicode const ** pBegin, sal_Unicode const * pEnd,
325 : rtl::OUString * pString)
326 : {
327 0 : sal_Unicode const * p = *pBegin;
328 :
329 0 : if (p == pEnd || *p++ != '"')
330 0 : return false;
331 :
332 0 : rtl::OUStringBuffer aBuffer;
333 0 : for (;;)
334 : {
335 0 : if (p == pEnd)
336 0 : return false;
337 0 : sal_Unicode c = *p++;
338 0 : if (c == '"')
339 0 : break;
340 0 : if (c == '\\')
341 : {
342 0 : if (p == pEnd)
343 0 : return false;
344 0 : c = *p++;
345 0 : if (c != '"' && c != '\\')
346 0 : return false;
347 : }
348 0 : aBuffer.append(c);
349 : }
350 :
351 0 : *pBegin = p;
352 0 : *pString = aBuffer.makeStringAndClear();
353 0 : return true;
354 : }
355 :
356 : }
357 :
358 312 : Regexp Regexp::parse(rtl::OUString const & rRegexp)
359 : {
360 : // Detect an input of '<scheme>' as an abbreviation of '"<scheme>:".*'
361 : // where <scheme> is as defined in RFC 2396:
362 312 : if (isScheme(rRegexp, false))
363 : return Regexp(Regexp::KIND_PREFIX,
364 : rRegexp
365 624 : + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(":")),
366 : false,
367 : rtl::OUString(),
368 : false,
369 936 : rtl::OUString());
370 :
371 0 : sal_Unicode const * p = rRegexp.getStr();
372 0 : sal_Unicode const * pEnd = p + rRegexp.getLength();
373 :
374 0 : rtl::OUString aPrefix;
375 0 : scanStringLiteral(&p, pEnd, &aPrefix);
376 :
377 0 : if (p == pEnd)
378 0 : throw lang::IllegalArgumentException();
379 :
380 0 : if (matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM(".*")))
381 : {
382 0 : if (p != pEnd)
383 0 : throw lang::IllegalArgumentException();
384 :
385 : return Regexp(Regexp::KIND_PREFIX, aPrefix, false, rtl::OUString(),
386 0 : false, rtl::OUString());
387 : }
388 0 : else if (matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM("(.*)->")))
389 : {
390 0 : rtl::OUString aReversePrefix;
391 0 : scanStringLiteral(&p, pEnd, &aReversePrefix);
392 :
393 0 : if (!matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM("\\1"))
394 : || p != pEnd)
395 0 : throw lang::IllegalArgumentException();
396 :
397 : return Regexp(Regexp::KIND_PREFIX, aPrefix, false, rtl::OUString(),
398 0 : true, aReversePrefix);
399 : }
400 0 : else if (matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM("([/?#].*)?")))
401 : {
402 0 : if (p != pEnd)
403 0 : throw lang::IllegalArgumentException();
404 :
405 : return Regexp(Regexp::KIND_AUTHORITY, aPrefix, false, rtl::OUString(),
406 0 : false, rtl::OUString());
407 : }
408 0 : else if (matchString(&p, pEnd,
409 : RTL_CONSTASCII_STRINGPARAM("(([/?#].*)?)->")))
410 : {
411 0 : rtl::OUString aReversePrefix;
412 0 : if (!(scanStringLiteral(&p, pEnd, &aReversePrefix)
413 0 : && matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM("\\1"))
414 0 : && p == pEnd))
415 0 : throw lang::IllegalArgumentException();
416 :
417 : return Regexp(Regexp::KIND_AUTHORITY, aPrefix, false, rtl::OUString(),
418 0 : true, aReversePrefix);
419 : }
420 : else
421 : {
422 0 : bool bOpen = false;
423 0 : if (p != pEnd && *p == '(')
424 : {
425 0 : ++p;
426 0 : bOpen = true;
427 : }
428 :
429 0 : if (!matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM("[^/?#]")))
430 0 : throw lang::IllegalArgumentException();
431 :
432 0 : if (p == pEnd || (*p != '*' && *p != '+'))
433 0 : throw lang::IllegalArgumentException();
434 0 : bool bEmptyDomain = *p++ == '*';
435 :
436 0 : rtl::OUString aInfix;
437 0 : scanStringLiteral(&p, pEnd, &aInfix);
438 :
439 0 : if (!matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM("([/?#].*)?")))
440 0 : throw lang::IllegalArgumentException();
441 :
442 0 : rtl::OUString aReversePrefix;
443 0 : if (bOpen
444 0 : && !(matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM(")->"))
445 0 : && scanStringLiteral(&p, pEnd, &aReversePrefix)
446 0 : && matchString(&p, pEnd, RTL_CONSTASCII_STRINGPARAM("\\1"))))
447 0 : throw lang::IllegalArgumentException();
448 :
449 0 : if (p != pEnd)
450 0 : throw lang::IllegalArgumentException();
451 :
452 : return Regexp(Regexp::KIND_DOMAIN, aPrefix, bEmptyDomain, aInfix,
453 0 : bOpen, aReversePrefix);
454 0 : }
455 : }
456 :
457 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|