1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <rtl/character.hxx>
21 #include <rtl/strbuf.hxx>
22 #include <rtl/textenc.h>
23 #include <rtl/textcvt.h>
25 #include <rtl/uri.hxx>
26 #include <rtl/ustrbuf.h>
27 #include <rtl/ustrbuf.hxx>
28 #include <rtl/ustring.h>
29 #include <rtl/ustring.hxx>
30 #include <sal/types.h>
31 #include <sal/macros.h>
33 #include <uri_internal.hxx>
40 sal_Unicode
const cEscapePrefix
= 0x25; // '%'
42 int getHexWeight(sal_uInt32 nUtf32
)
44 return nUtf32
>= 0x30 && nUtf32
<= 0x39 ? // '0'--'9'
45 static_cast< int >(nUtf32
- 0x30) :
46 nUtf32
>= 0x41 && nUtf32
<= 0x46 ? // 'A'--'F'
47 static_cast< int >(nUtf32
- 0x41 + 10) :
48 nUtf32
>= 0x61 && nUtf32
<= 0x66 ? // 'a'--'f'
49 static_cast< int >(nUtf32
- 0x61 + 10) :
50 -1; // not a hex digit
53 bool isValid(sal_Bool
const * pCharClass
, sal_uInt32 nUtf32
)
55 return nUtf32
< rtl::UriCharClassSize
&& pCharClass
[nUtf32
];
58 void writeUnicode(rtl_uString
** pBuffer
, sal_Int32
* pCapacity
,
61 rtl_uStringbuffer_insert(pBuffer
, pCapacity
, (*pBuffer
)->length
, &cChar
, 1);
66 namespace rtl::uri::detail
{
68 /** Read any of the following:
70 @li sequence of escape sequences representing character from eCharset,
71 translated to single UCS4 character; or
72 @li pair of UTF-16 surrogates, translated to single UCS4 character; or
73 @li single UTF-16 character, extended to UCS4 character.
75 sal_uInt32
readUcs4(sal_Unicode
const ** pBegin
, sal_Unicode
const * pEnd
,
76 bool bEncoded
, rtl_TextEncoding eCharset
,
79 sal_uInt32 nChar
= *(*pBegin
)++;
82 if (nChar
== cEscapePrefix
&& bEncoded
&& pEnd
- *pBegin
>= 2
83 && (nWeight1
= getHexWeight((*pBegin
)[0])) >= 0
84 && (nWeight2
= getHexWeight((*pBegin
)[1])) >= 0)
87 nChar
= static_cast< sal_uInt32
>(nWeight1
<< 4 | nWeight2
);
92 else if (eCharset
== RTL_TEXTENCODING_UTF8
)
94 if (nChar
>= 0xC0 && nChar
<= 0xF4)
101 nEncoded
= (nChar
& 0x1F) << 6;
105 else if (nChar
<= 0xEF)
107 nEncoded
= (nChar
& 0x0F) << 12;
113 nEncoded
= (nChar
& 0x07) << 18;
118 sal_Unicode
const * p
= *pBegin
;
121 for (; nShift
>= 0; nShift
-= 6)
123 if (pEnd
- p
< 3 || p
[0] != cEscapePrefix
124 || (nWeight1
= getHexWeight(p
[1])) < 8
126 || (nWeight2
= getHexWeight(p
[2])) < 0)
132 nEncoded
|= ((nWeight1
& 3) << 4 | nWeight2
) << nShift
;
134 if (bUTF8
&& rtl::isUnicodeScalarValue(nEncoded
)
142 *pType
= EscapeOctet
;
146 OStringBuffer
aBuf(16);
147 aBuf
.append(static_cast< char >(nChar
));
148 rtl_TextToUnicodeConverter aConverter
149 = rtl_createTextToUnicodeConverter(eCharset
);
150 sal_Unicode
const * p
= *pBegin
;
157 sal_Size nDstSize
= rtl_convertTextToUnicode(
158 aConverter
, nullptr, aBuf
.getStr(), aBuf
.getLength(), aDst
,
159 SAL_N_ELEMENTS( aDst
),
160 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
161 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
162 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR
),
163 &nInfo
, &nConverted
);
168 == sal::static_int_cast
< sal_uInt32
>(
171 rtl_destroyTextToUnicodeConverter(aConverter
);
175 assert( nDstSize
== 1
176 || (nDstSize
== 2 && rtl::isHighSurrogate(aDst
[0])
177 && rtl::isLowSurrogate(aDst
[1])));
180 ? aDst
[0] : rtl::combineSurrogates(aDst
[0], aDst
[1]);
182 if (nInfo
== RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOOSMALL
183 && pEnd
- p
>= 3 && p
[0] == cEscapePrefix
184 && (nWeight1
= getHexWeight(p
[1])) >= 0
185 && (nWeight2
= getHexWeight(p
[2])) >= 0)
188 aBuf
.append(static_cast< char >(nWeight1
<< 4 | nWeight2
));
190 else if (nInfo
== RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOOSMALL
191 && p
!= pEnd
&& *p
<= 0x7F)
193 aBuf
.append(static_cast< char >(*p
++));
198 (nInfo
& RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL
)
203 rtl_destroyTextToUnicodeConverter(aConverter
);
204 *pType
= EscapeOctet
;
210 return rtl::isHighSurrogate(nChar
) && *pBegin
< pEnd
211 && rtl::isLowSurrogate(**pBegin
) ?
212 rtl::combineSurrogates(nChar
, *(*pBegin
)++) : nChar
;
219 void writeUcs4(rtl_uString
** pBuffer
, sal_Int32
* pCapacity
, sal_uInt32 nUtf32
)
221 rtl_uStringbuffer_insertUtf32(pBuffer
, pCapacity
, (*pBuffer
)->length
, nUtf32
);
224 void writeEscapeOctet(rtl_uString
** pBuffer
, sal_Int32
* pCapacity
,
227 assert(nOctet
<= 0xFF); // bad octet
229 static sal_Unicode
const aHex
[16]
230 = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
231 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; /* '0'--'9', 'A'--'F' */
233 writeUnicode(pBuffer
, pCapacity
, cEscapePrefix
);
234 writeUnicode(pBuffer
, pCapacity
, aHex
[nOctet
>> 4]);
235 writeUnicode(pBuffer
, pCapacity
, aHex
[nOctet
& 15]);
238 bool writeEscapeChar(rtl_uString
** pBuffer
, sal_Int32
* pCapacity
,
239 sal_uInt32 nUtf32
, rtl_TextEncoding eCharset
, bool bStrict
)
241 assert(rtl::isUnicodeCodePoint(nUtf32
));
242 if (eCharset
== RTL_TEXTENCODING_UTF8
)
246 writeEscapeOctet(pBuffer
, pCapacity
, nUtf32
);
248 else if (nUtf32
< 0x800)
250 writeEscapeOctet(pBuffer
, pCapacity
, nUtf32
>> 6 | 0xC0);
251 writeEscapeOctet(pBuffer
, pCapacity
, (nUtf32
& 0x3F) | 0x80);
253 else if (nUtf32
< 0x10000)
255 writeEscapeOctet(pBuffer
, pCapacity
, nUtf32
>> 12 | 0xE0);
256 writeEscapeOctet(pBuffer
, pCapacity
, (nUtf32
>> 6 & 0x3F) | 0x80);
257 writeEscapeOctet(pBuffer
, pCapacity
, (nUtf32
& 0x3F) | 0x80);
261 writeEscapeOctet(pBuffer
, pCapacity
, nUtf32
>> 18 | 0xF0);
262 writeEscapeOctet(pBuffer
, pCapacity
, (nUtf32
>> 12 & 0x3F) | 0x80);
263 writeEscapeOctet(pBuffer
, pCapacity
, (nUtf32
>> 6 & 0x3F) | 0x80);
264 writeEscapeOctet(pBuffer
, pCapacity
, (nUtf32
& 0x3F) | 0x80);
269 rtl_UnicodeToTextConverter aConverter
270 = rtl_createUnicodeToTextConverter(eCharset
);
272 sal_Size nSrcSize
= rtl::splitSurrogates(nUtf32
, aSrc
);
274 char aDst
[32]; // FIXME random value
277 sal_Size nDstSize
= rtl_convertUnicodeToText(
278 aConverter
, nullptr, aSrc
, nSrcSize
, aDst
, sizeof aDst
,
279 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
280 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
281 | RTL_UNICODETOTEXT_FLAGS_FLUSH
,
282 &nInfo
, &nConverted
);
283 assert((nInfo
& RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL
) == 0);
284 rtl_destroyUnicodeToTextConverter(aConverter
);
288 assert(nConverted
== nSrcSize
); // bad rtl_convertUnicodeToText
290 for (sal_Size i
= 0; i
< nDstSize
; ++i
)
292 writeEscapeOctet(pBuffer
, pCapacity
,
293 static_cast< unsigned char >(aDst
[i
]));
294 // FIXME all octets are escaped, even if there is no need
302 writeUcs4(pBuffer
, pCapacity
, nUtf32
);
310 sal_Unicode
const * pBegin
;
311 sal_Unicode
const * pEnd
;
313 Component(): pBegin(nullptr), pEnd(nullptr) {}
315 bool isPresent() const { return pBegin
!= nullptr; }
317 sal_Int32
getLength() const;
320 sal_Int32
Component::getLength() const
322 assert(isPresent()); // taking length of non-present component
323 return static_cast< sal_Int32
>(pEnd
- pBegin
);
329 Component aAuthority
;
335 void parseUriRef(rtl_uString
const * pUriRef
, Components
* pComponents
)
337 // This algorithm is liberal and accepts various forms of illegal input.
339 sal_Unicode
const * pBegin
= pUriRef
->buffer
;
340 sal_Unicode
const * pEnd
= pBegin
+ pUriRef
->length
;
341 sal_Unicode
const * pPos
= pBegin
;
343 if (pPos
!= pEnd
&& rtl::isAsciiAlpha(*pPos
))
345 for (sal_Unicode
const * p
= pPos
+ 1; p
!= pEnd
; ++p
)
349 pComponents
->aScheme
.pBegin
= pBegin
;
350 pComponents
->aScheme
.pEnd
= ++p
;
355 if (!rtl::isAsciiAlphanumeric(*p
) && *p
!= '+' && *p
!= '-'
363 if (pEnd
- pPos
>= 2 && pPos
[0] == '/' && pPos
[1] == '/')
365 pComponents
->aAuthority
.pBegin
= pPos
;
367 while (pPos
!= pEnd
&& *pPos
!= '/' && *pPos
!= '?' && *pPos
!= '#')
372 pComponents
->aAuthority
.pEnd
= pPos
;
375 pComponents
->aPath
.pBegin
= pPos
;
376 while (pPos
!= pEnd
&& *pPos
!= '?' && * pPos
!= '#')
381 pComponents
->aPath
.pEnd
= pPos
;
383 if (pPos
!= pEnd
&& *pPos
== '?')
385 pComponents
->aQuery
.pBegin
= pPos
++;
386 while (pPos
!= pEnd
&& * pPos
!= '#')
391 pComponents
->aQuery
.pEnd
= pPos
;
396 assert(*pPos
== '#');
397 pComponents
->aFragment
.pBegin
= pPos
;
398 pComponents
->aFragment
.pEnd
= pEnd
;
403 OUStringBuffer
& buffer
, sal_Int32 bufferStart
, bool precedingSlash
,
404 sal_Unicode
const * pathBegin
, sal_Unicode
const * pathEnd
)
406 while (precedingSlash
|| pathBegin
!= pathEnd
)
408 sal_Unicode
const * p
= pathBegin
;
409 while (p
!= pathEnd
&& *p
!= '/')
414 std::size_t n
= p
- pathBegin
;
415 if (n
== 1 && pathBegin
[0] == '.')
417 // input begins with "." -> remove from input (and done):
418 // i.e., !precedingSlash -> !precedingSlash
419 // input begins with "./" -> remove from input:
420 // i.e., !precedingSlash -> !precedingSlash
421 // input begins with "/." -> replace with "/" in input (and not yet
423 // i.e., precedingSlash -> precedingSlash
424 // input begins with "/./" -> replace with "/" in input:
425 // i.e., precedingSlash -> precedingSlash
427 else if (n
== 2 && pathBegin
[0] == '.' && pathBegin
[1] == '.')
429 // input begins with ".." -> remove from input (and done):
430 // i.e., !precedingSlash -> !precedingSlash
431 // input begins with "../" -> remove from input
432 // i.e., !precedingSlash -> !precedingSlash
433 // input begins with "/.." -> replace with "/" in input, and shrink
434 // output (not yet done):
435 // i.e., precedingSlash -> precedingSlash
436 // input begins with "/../" -> replace with "/" in input, and shrink
438 // i.e., precedingSlash -> precedingSlash
443 + std::max
<sal_Int32
>(
444 rtl_ustr_lastIndexOfChar_WithLength(
445 buffer
.getStr() + bufferStart
,
446 buffer
.getLength() - bufferStart
, '/'),
455 buffer
.append(pathBegin
, n
);
456 precedingSlash
= p
!= pathEnd
;
458 pathBegin
= p
+ (p
== pathEnd
? 0 : 1);
464 sal_Bool
const * SAL_CALL
rtl_getUriCharClass(rtl_UriCharClass eCharClass
)
467 static constexpr std::array
<sal_Bool
, rtl::UriCharClassSize
> aCharClass
[] = {
468 rtl::createUriCharClass(u8
""), // None
469 rtl::createUriCharClass(
470 u8
"!$&'()*+,-./:;=?@[]_~"
471 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Uric
472 rtl::createUriCharClass(
473 u8
"!$&'()*+,-.:;=?@_~"
474 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // UricNoSlash
475 rtl::createUriCharClass(
477 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // RelSegment
478 rtl::createUriCharClass(
479 u8
"!$&'()*+,-.:;=@_~"
480 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // RegName
481 rtl::createUriCharClass(
483 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Userinfo
484 rtl::createUriCharClass(
486 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Pchar
487 rtl::createUriCharClass(
489 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")}; // UnoParamValue
493 && (sal::static_int_cast
< std::size_t >(eCharClass
)
494 < SAL_N_ELEMENTS(aCharClass
)))); // bad eCharClass
495 return aCharClass
[eCharClass
].data();
498 void SAL_CALL
rtl_uriEncode(rtl_uString
* pText
, sal_Bool
const * pCharClass
,
499 rtl_UriEncodeMechanism eMechanism
,
500 rtl_TextEncoding eCharset
, rtl_uString
** pResult
)
503 assert(!pCharClass
[0x25]); // make sure the percent sign is encoded...
505 sal_Unicode
const * p
= pText
->buffer
;
506 sal_Unicode
const * pEnd
= p
+ pText
->length
;
507 sal_Int32 nCapacity
= 256;
508 rtl_uString_new_WithLength(pResult
, nCapacity
);
512 rtl::uri::detail::EscapeType eType
;
513 sal_uInt32 nUtf32
= rtl::uri::detail::readUcs4(
515 (eMechanism
== rtl_UriEncodeKeepEscapes
516 || eMechanism
== rtl_UriEncodeCheckEscapes
517 || eMechanism
== rtl_UriEncodeStrictKeepEscapes
),
518 eMechanism
== rtl_UriEncodeStrictKeepEscapes
? RTL_TEXTENCODING_UTF8
: eCharset
,
523 case rtl::uri::detail::EscapeNo
:
524 if (isValid(pCharClass
, nUtf32
)) // implies nUtf32 <= 0x7F
526 writeUnicode(pResult
, &nCapacity
,
527 static_cast< sal_Unicode
>(nUtf32
));
529 else if (!writeEscapeChar(
530 pResult
, &nCapacity
, nUtf32
, eCharset
,
531 (eMechanism
== rtl_UriEncodeStrict
532 || eMechanism
== rtl_UriEncodeStrictKeepEscapes
)))
534 rtl_uString_new(pResult
);
539 case rtl::uri::detail::EscapeChar
:
540 if (eMechanism
== rtl_UriEncodeCheckEscapes
541 && isValid(pCharClass
, nUtf32
)) // implies nUtf32 <= 0x7F
543 writeUnicode(pResult
, &nCapacity
,
544 static_cast< sal_Unicode
>(nUtf32
));
546 else if (!writeEscapeChar(
547 pResult
, &nCapacity
, nUtf32
, eCharset
,
548 (eMechanism
== rtl_UriEncodeStrict
549 || eMechanism
== rtl_UriEncodeStrictKeepEscapes
)))
551 rtl_uString_new(pResult
);
556 case rtl::uri::detail::EscapeOctet
:
557 writeEscapeOctet(pResult
, &nCapacity
, nUtf32
);
561 *pResult
= rtl_uStringBuffer_makeStringAndClear(pResult
, &nCapacity
);
564 void SAL_CALL
rtl_uriDecode(rtl_uString
* pText
,
565 rtl_UriDecodeMechanism eMechanism
,
566 rtl_TextEncoding eCharset
, rtl_uString
** pResult
)
571 case rtl_UriDecodeNone
:
572 rtl_uString_assign(pResult
, pText
);
575 case rtl_UriDecodeToIuri
:
576 eCharset
= RTL_TEXTENCODING_UTF8
;
578 default: // rtl_UriDecodeWithCharset, rtl_UriDecodeStrict
580 sal_Unicode
const * p
= pText
->buffer
;
581 sal_Unicode
const * pEnd
= p
+ pText
->length
;
582 sal_Int32 nCapacity
= pText
->length
;
583 rtl_uString_new_WithLength(pResult
, nCapacity
);
587 rtl::uri::detail::EscapeType eType
;
588 sal_uInt32 nUtf32
= rtl::uri::detail::readUcs4(&p
, pEnd
, true, eCharset
, &eType
);
591 case rtl::uri::detail::EscapeChar
:
592 if (nUtf32
<= 0x7F && eMechanism
== rtl_UriDecodeToIuri
)
594 writeEscapeOctet(pResult
, &nCapacity
, nUtf32
);
599 case rtl::uri::detail::EscapeNo
:
600 writeUcs4(pResult
, &nCapacity
, nUtf32
);
603 case rtl::uri::detail::EscapeOctet
:
604 if (eMechanism
== rtl_UriDecodeStrict
)
606 rtl_uString_new(pResult
);
609 writeEscapeOctet(pResult
, &nCapacity
, nUtf32
);
614 *pResult
= rtl_uStringBuffer_makeStringAndClear( pResult
, &nCapacity
);
620 sal_Bool SAL_CALL
rtl_uriConvertRelToAbs(rtl_uString
* pBaseUriRef
,
621 rtl_uString
* pRelUriRef
,
622 rtl_uString
** pResult
,
623 rtl_uString
** pException
)
626 // Use the strict parser algorithm from RFC 3986, section 5.2, to turn the
627 // relative URI into an absolute one:
628 Components aRelComponents
;
629 parseUriRef(pRelUriRef
, &aRelComponents
);
630 OUStringBuffer
aBuffer(256);
632 if (aRelComponents
.aScheme
.isPresent())
634 aBuffer
.append(aRelComponents
.aScheme
.pBegin
,
635 aRelComponents
.aScheme
.getLength());
637 if (aRelComponents
.aAuthority
.isPresent())
639 aBuffer
.append(aRelComponents
.aAuthority
.pBegin
,
640 aRelComponents
.aAuthority
.getLength());
644 aBuffer
, aBuffer
.getLength(), false, aRelComponents
.aPath
.pBegin
,
645 aRelComponents
.aPath
.pEnd
);
647 if (aRelComponents
.aQuery
.isPresent())
649 aBuffer
.append(aRelComponents
.aQuery
.pBegin
,
650 aRelComponents
.aQuery
.getLength());
655 Components aBaseComponents
;
656 parseUriRef(pBaseUriRef
, &aBaseComponents
);
657 if (!aBaseComponents
.aScheme
.isPresent())
662 "<" + OUString::unacquired(&pBaseUriRef
)
663 + "> does not start with a scheme component")
668 aBuffer
.append(aBaseComponents
.aScheme
.pBegin
,
669 aBaseComponents
.aScheme
.getLength());
670 if (aRelComponents
.aAuthority
.isPresent())
672 aBuffer
.append(aRelComponents
.aAuthority
.pBegin
,
673 aRelComponents
.aAuthority
.getLength());
675 aBuffer
, aBuffer
.getLength(), false,
676 aRelComponents
.aPath
.pBegin
, aRelComponents
.aPath
.pEnd
);
678 if (aRelComponents
.aQuery
.isPresent())
680 aBuffer
.append(aRelComponents
.aQuery
.pBegin
,
681 aRelComponents
.aQuery
.getLength());
686 if (aBaseComponents
.aAuthority
.isPresent())
688 aBuffer
.append(aBaseComponents
.aAuthority
.pBegin
,
689 aBaseComponents
.aAuthority
.getLength());
692 if (aRelComponents
.aPath
.pBegin
== aRelComponents
.aPath
.pEnd
)
694 aBuffer
.append(aBaseComponents
.aPath
.pBegin
,
695 aBaseComponents
.aPath
.getLength());
696 if (aRelComponents
.aQuery
.isPresent())
698 aBuffer
.append(aRelComponents
.aQuery
.pBegin
,
699 aRelComponents
.aQuery
.getLength());
701 else if (aBaseComponents
.aQuery
.isPresent())
703 aBuffer
.append(aBaseComponents
.aQuery
.pBegin
,
704 aBaseComponents
.aQuery
.getLength());
709 if (*aRelComponents
.aPath
.pBegin
== '/')
712 aBuffer
, aBuffer
.getLength(), false,
713 aRelComponents
.aPath
.pBegin
, aRelComponents
.aPath
.pEnd
);
715 else if (aBaseComponents
.aAuthority
.isPresent()
716 && aBaseComponents
.aPath
.pBegin
717 == aBaseComponents
.aPath
.pEnd
)
720 aBuffer
, aBuffer
.getLength(), true,
721 aRelComponents
.aPath
.pBegin
, aRelComponents
.aPath
.pEnd
);
725 sal_Int32 n
= aBuffer
.getLength();
726 sal_Int32 i
= rtl_ustr_lastIndexOfChar_WithLength(
727 aBaseComponents
.aPath
.pBegin
,
728 aBaseComponents
.aPath
.getLength(), '/');
733 aBuffer
, n
, false, aBaseComponents
.aPath
.pBegin
,
734 aBaseComponents
.aPath
.pBegin
+ i
);
738 aBuffer
, n
, i
>= 0, aRelComponents
.aPath
.pBegin
,
739 aRelComponents
.aPath
.pEnd
);
742 if (aRelComponents
.aQuery
.isPresent())
744 aBuffer
.append(aRelComponents
.aQuery
.pBegin
,
745 aRelComponents
.aQuery
.getLength());
750 if (aRelComponents
.aFragment
.isPresent())
752 aBuffer
.append(aRelComponents
.aFragment
.pBegin
,
753 aRelComponents
.aFragment
.getLength());
756 rtl_uString_assign(pResult
, aBuffer
.makeStringAndClear().pData
);
760 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */