/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <sal/config.h>
 
#include <tools/urlobj.hxx>
#include <tools/debug.hxx>
#include <tools/inetmime.hxx>
#include <tools/stream.hxx>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/util/XStringWidth.hpp>
#include <o3tl/enumarray.hxx>
#include <osl/diagnose.h>
#include <osl/file.hxx>
#include <rtl/character.hxx>
#include <rtl/string.h>
#include <rtl/textenc.h>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <sal/types.h>
 
#include <algorithm>
#include <cassert>
#include <limits>
#include <memory>
#include <string_view>
 
#include <string.h>
 
#include <com/sun/star/uno/Sequence.hxx>
#include <comphelper/base64.hxx>
#include <comphelper/string.hxx>
 
using namespace css;
 
//  INetURLObject
 
/* The URI grammar (using RFC 2234 conventions).
 
   Constructs of the form
       {reference <rule1> using rule2}
   stand for a rule matching the given rule1 specified in the given reference,
   encoded to URI syntax using rule2 (as specified in this URI grammar).
 
 
   ; RFC 1738, RFC 2396, RFC 2732, private
   login = [user [":" password] "@"] hostport
   user = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ";" / "=" / "_" / "~")
   password = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ";" / "=" / "_" / "~")
   hostport = host [":" port]
   host = incomplete-hostname / hostname / IPv4address / IPv6reference
   incomplete-hostname = *(domainlabel ".") domainlabel
   hostname = *(domainlabel ".") toplabel ["."]
   domainlabel = alphanum [*(alphanum / "-") alphanum]
   toplabel = ALPHA [*(alphanum / "-") alphanum]
   IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
   IPv6reference = "[" hexpart [":" IPv4address] "]"
   hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq])
   hexseq = hex4 *(":" hex4)
   hex4 = 1*4HEXDIG
   port = *DIGIT
   escaped = "%" HEXDIG HEXDIG
   reserved = "$" / "&" / "+" / "," / "/" / ":" / ";" / "=" / "?" / "@" / "[" / "]"
   mark = "!" / "'" / "(" / ")" / "*" / "-" / "." / "_" / "~"
   alphanum = ALPHA / DIGIT
   unreserved = alphanum / mark
   uric = escaped / reserved / unreserved
   pchar = escaped / unreserved / "$" / "&" / "+" / "," / ":" / "=" / "@"
 
 
   ; RFC 1738, RFC 2396
   ftp-url = "FTP://" login ["/" segment *("/" segment) [";TYPE=" ("A" / "D" / "I")]]
   segment = *pchar
 
 
   ; RFC 1738, RFC 2396
   http-url = "HTTP://" hostport ["/" segment *("/" segment) ["?" *uric]]
   segment = *(pchar / ";")
 
 
   ; RFC 1738, RFC 2396, <http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q188997&>
   file-url = "FILE://" [host / "LOCALHOST" / netbios-name] ["/" segment *("/" segment)]
   segment = *pchar
   netbios-name = 1*{<alphanum / "!" / "#" / "$" / "%" / "&" / "'" / "(" / ")" / "-" / "." / "@" / "^" / "_" / "{" / "}" / "~"> using (escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "-" / "." / "@" / "_" / "~")}
 
 
   ; RFC 2368, RFC 2396
   mailto-url = "MAILTO:" [to] [headers]
   to = {RFC 822 <#mailbox> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")}
   headers = "?" header *("&" header)
   header = hname "=" hvalue
   hname = {RFC 822 <field-name> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")} / "BODY"
   hvalue = {RFC 822 <field-body> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")}
 
 
   ; private (see RFC 1738, RFC 2396)
   vnd-sun-star-webdav-url = "VND.SUN.STAR.WEBDAV://" hostport ["/" segment *("/" segment) ["?" *uric]]
   segment = *(pchar / ";")
 
 
   ; private
   private-url = "PRIVATE:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   vnd-sun-star-help-url = "VND.SUN.STAR.HELP://" name *("/" segment) ["?" *uric]
   name = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")
   segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   https-url = "HTTPS://" hostport ["/" segment *("/" segment) ["?" *uric]]
   segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   slot-url = "SLOT:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   macro-url = "MACRO:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   javascript-url = "JAVASCRIPT:" *uric
 
 
   ; RFC 2397
   data-url = "DATA:" [mediatype] [";BASE64"] "," *uric
   mediatype = [type "/" subtype] *(";" attribute "=" value)
   type = {RFC 2045 <type> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}
   subtype = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}
   attribute = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}
   value = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")}
 
 
   ; RFC 2392, RFC 2396
   cid-url = "CID:" {RFC 822 <addr-spec> using *uric}
 
 
   ; private
   vnd-sun-star-hier-url = "VND.SUN.STAR.HIER:" ["//"reg_name] *("/" *pchar)
   reg_name = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   uno-url = ".UNO:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   component-url = ".COMPONENT:" path ["?" *uric]
   path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; private
   vnd-sun-star-pkg-url = "VND.SUN.STAR.PKG://" reg_name *("/" *pchar) ["?" *uric]
   reg_name = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~")
 
 
   ; RFC 2255
   ldap-url = "LDAP://" [hostport] ["/" [dn ["?" [attrdesct *("," attrdesc)] ["?" ["base" / "one" / "sub"] ["?" [filter] ["?" extension *("," extension)]]]]]]
   dn = {RFC 2253 <distinguishedName> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}
   attrdesc = {RFC 2251 <AttributeDescription> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}
   filter = {RFC 2254 <filter> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}
   extension = ["!"] ["X-"] extoken ["=" exvalue]
   extoken = {RFC 2252 <oid> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")}
   exvalue = {RFC 2251 <LDAPString> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")}
 
 
   ; private
   db-url = "DB:" *uric
 
 
   ; private
   vnd-sun-star-cmd-url = "VND.SUN.STAR.CMD:" opaque_part
   opaque_part = uric_no_slash *uric
   uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","
 
 
   ; RFC 1738
   telnet-url = "TELNET://" login ["/"]
 
 
   ; private
   vnd-sun-star-expand-url = "VND.SUN.STAR.EXPAND:" opaque_part
   opaque_part = uric_no_slash *uric
   uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","
 
 
   ; private
   vnd-sun-star-tdoc-url = "VND.SUN.STAR.TDOC:/" segment *("/" segment)
   segment = *pchar
 
 
   ; private
   unknown-url = scheme ":" 1*uric
   scheme = ALPHA *(alphanum / "+" / "-" / ".")
 
 
   ; private (http://ubiqx.org/cifs/Appendix-D.html):
   smb-url = "SMB://" login ["/" segment *("/" segment) ["?" *uric]]
   segment = *(pchar / ";")
 */
 
sal_Int32 INetURLObject::SubString::clear()
{
    sal_Int32 nDelta = -m_nLength;
    m_nBegin = -1;
    m_nLength = 0;
    return nDelta;
}
 
sal_Int32 INetURLObject::SubString::set(OUStringBuffer & rString,
                                       std::u16string_view rSubString)
{
    sal_Int32 nDelta = rSubString.size() - m_nLength;
 
    rString.remove(m_nBegin, m_nLength);
    rString.insert(m_nBegin, rSubString);
 
    m_nLength = rSubString.size();
    return nDelta;
}
 
sal_Int32 INetURLObject::SubString::set(OUString & rString,
                                       std::u16string_view rSubString)
{
    sal_Int32 nDelta = rSubString.size() - m_nLength;
 
    rString = OUString::Concat(rString.subView(0, m_nBegin)) + 
             rSubString + rString.subView(m_nBegin + m_nLength);
 
    m_nLength = rSubString.size();
    return nDelta;
}
 
sal_Int32 INetURLObject::SubString::set(OUStringBuffer & rString,
                                        std::u16string_view rSubString,
                                        sal_Int32 nTheBegin)
{
    m_nBegin = nTheBegin;
    return set(rString, rSubString);
}
 
inline void INetURLObject::SubString::operator +=(sal_Int32 nDelta)
{
    if (isPresent())
        m_nBegin = m_nBegin + nDelta;
}
 
int INetURLObject::SubString::compare(SubString const & rOther,
                                      OUStringBuffer const & rThisString,
                                      OUStringBuffer const & rOtherString) const
{
    sal_Int32 len = std::min(m_nLength, rOther.m_nLength);
    sal_Unicode const * p1 = rThisString.getStr() + m_nBegin;
    sal_Unicode const * end = p1 + len;
    sal_Unicode const * p2 = rOtherString.getStr() + rOther.m_nBegin;
    while (p1 != end) {
        if (*p1 < *p2) {
            return -1;
        } else if (*p1 > *p2) {
            return 1;
        }
        ++p1;
        ++p2;
    }
    return m_nLength < rOther.m_nLength ? -1
        : m_nLength > rOther.m_nLength ? 1
        : 0;
}
 
struct INetURLObject::SchemeInfo
{
    OUString m_sScheme;
    OUString m_aPrefix;
    bool m_bAuthority;
    bool m_bUser;
    bool m_bAuth;
    bool m_bPassword;
    bool m_bHost;
    bool m_bPort;
    bool m_bHierarchical;
    bool m_bQuery;
};
 
struct INetURLObject::PrefixInfo
{
    enum class Kind { Official, Internal, External }; // order is important!
 
    OUString     m_aPrefix;
    OUString     m_aTranslatedPrefix;
    INetProtocol m_eScheme;
    Kind         m_eKind;
};
 
// static
inline INetURLObject::SchemeInfo const &
INetURLObject::getSchemeInfo(INetProtocol eTheScheme)
{
    static constexpr OUString EMPTY = u""_ustr;
    static constexpr OUString FTP = u"ftp"_ustr;
    static constexpr OUString HTTP = u"http"_ustr;
    static constexpr OUString FILE1 = u"file"_ustr; // because FILE is already defined
    static constexpr OUString MAILTO = u"mailto"_ustr;
    static constexpr OUString VND_WEBDAV = u"vnd.sun.star.webdav"_ustr;
    static constexpr OUString PRIVATE = u"private"_ustr;
    static constexpr OUString VND_HELP = u"vnd.sun.star.help"_ustr;
    static constexpr OUString HTTPS = u"https"_ustr;
    static constexpr OUString SLOT = u"slot"_ustr;
    static constexpr OUString MACRO = u"macro"_ustr;
    static constexpr OUString JAVASCRIPT = u"javascript"_ustr;
    static constexpr OUString DATA = u"data"_ustr;
    static constexpr OUString CID = u"cid"_ustr;
    static constexpr OUString VND_HIER = u"vnd.sun.star.hier"_ustr;
    static constexpr OUString UNO = u".uno"_ustr;
    static constexpr OUString COMPONENT = u".component"_ustr;
    static constexpr OUString VND_PKG = u"vnd.sun.star.pkg"_ustr;
    static constexpr OUString LDAP = u"ldap"_ustr;
    static constexpr OUString DB = u"db"_ustr;
    static constexpr OUString VND_CMD = u"vnd.sun.star.cmd"_ustr;
    static constexpr OUString TELNET = u"telnet"_ustr;
    static constexpr OUString VND_EXPAND = u"vnd.sun.star.expand"_ustr;
    static constexpr OUString VND_TDOC = u"vnd.sun.star.tdoc"_ustr;
    static constexpr OUString SMB = u"smb"_ustr;
    static constexpr OUString HID = u"hid"_ustr;
    static constexpr OUString SFTP = u"sftp"_ustr;
    static constexpr OUString VND_CMIS = u"vnd.libreoffice.cmis"_ustr;
 
    static o3tl::enumarray<INetProtocol, SchemeInfo> constexpr map = {
        // [-loplugin:redundantfcast]:
        SchemeInfo{
            EMPTY, u""_ustr, false, false, false, false, false, false, false, false},
        SchemeInfo{
            FTP, u"ftp://"_ustr, true, true, false, true, true, true, true,
            false},
        SchemeInfo{
            HTTP, u"http://"_ustr, true, false, false, false, true, true, true,
            true},
        SchemeInfo{
            FILE1, u"file://"_ustr, true, false, false, false, true, false, true,
            false},
        SchemeInfo{
            MAILTO, u"mailto:"_ustr, false, false, false, false, false, false,
            false, true},
        SchemeInfo{
            VND_WEBDAV, u"vnd.sun.star.webdav://"_ustr, true, false,
            false, false, true, true, true, true},
        SchemeInfo{
            PRIVATE, u"private:"_ustr, false, false, false, false, false, false,
            false, true},
        SchemeInfo{
            VND_HELP, u"vnd.sun.star.help://"_ustr, true, false, false,
            false, false, false, true, true},
        SchemeInfo{
            HTTPS, u"https://"_ustr, true, false, false, false, true, true,
            true, true},
        SchemeInfo{
            SLOT, u"slot:"_ustr, false, false, false, false, false, false, false,
            true},
        SchemeInfo{
            MACRO, u"macro:"_ustr, false, false, false, false, false, false,
            false, true},
        SchemeInfo{
            JAVASCRIPT, u"javascript:"_ustr, false, false, false, false, false,
            false, false, false},
        SchemeInfo{
            DATA, u"data:"_ustr, false, false, false, false, false, false, false,
            false},
        SchemeInfo{
            CID, u"cid:"_ustr, false, false, false, false, false, false, false,
            false},
        SchemeInfo{
            VND_HIER, u"vnd.sun.star.hier:"_ustr, true, false, false,
            false, false, false, true, false},
        SchemeInfo{
            UNO, u".uno:"_ustr, false, false, false, false, false, false, false,
            true},
        SchemeInfo{
            COMPONENT, u".component:"_ustr, false, false, false, false, false,
            false, false, true},
        SchemeInfo{
            VND_PKG, u"vnd.sun.star.pkg://"_ustr, true, false, false,
            false, false, false, true, true},
        SchemeInfo{
            LDAP, u"ldap://"_ustr, true, false, false, false, true, true,
            false, true},
        SchemeInfo{
            DB, u"db:"_ustr, false, false, false, false, false, false, false,
            false},
        SchemeInfo{
            VND_CMD, u"vnd.sun.star.cmd:"_ustr, false, false, false,
            false, false, false, false, false},
        SchemeInfo{
            TELNET, u"telnet://"_ustr, true, true, false, true, true, true,
            true, false},
        SchemeInfo{
            VND_EXPAND, u"vnd.sun.star.expand:"_ustr, false, false,
            false, false, false, false, false, false},
        SchemeInfo{
            VND_TDOC, u"vnd.sun.star.tdoc:"_ustr, false, false, false,
            false, false, false, true, false},
        SchemeInfo{
            EMPTY, u""_ustr, false, false, false, false, true, true, true, false },
        SchemeInfo{
            SMB, u"smb://"_ustr, true, true, false, true, true, true, true,
            true},
        SchemeInfo{
            HID, u"hid:"_ustr, false, false, false, false, false, false, false,
            true},
        SchemeInfo{
            SFTP, u"sftp://"_ustr, true, true, false, true, true, true, true,
            true},
        SchemeInfo{
            VND_CMIS, u"vnd.libreoffice.cmis://"_ustr, true, true,
            false, false, true, false, true, true} };
    return map[eTheScheme];
};
 
inline INetURLObject::SchemeInfo const & INetURLObject::getSchemeInfo() const
{
    return getSchemeInfo(m_eScheme);
}
 
namespace {
 
sal_Unicode getHexDigit(sal_uInt32 nWeight)
{
    assert(nWeight < 16);
    static const sal_Unicode aDigits[16]
        = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
            'D', 'E', 'F' };
    return aDigits[nWeight];
}
 
}
 
// static
inline void INetURLObject::appendEscape(OUStringBuffer & rTheText,
                                        sal_uInt32 nOctet)
{
    rTheText.append( '%' );
    rTheText.append( getHexDigit(nOctet >> 4) );
    rTheText.append( getHexDigit(nOctet & 15) );
}
 
namespace {
 
enum
{
    PA = INetURLObject::PART_USER_PASSWORD,
    PD = INetURLObject::PART_FPATH,
    PE = INetURLObject::PART_AUTHORITY,
    PF = INetURLObject::PART_REL_SEGMENT_EXTRA,
    PG = INetURLObject::PART_URIC,
    PH = INetURLObject::PART_HTTP_PATH,
    PI = INetURLObject::PART_MESSAGE_ID_PATH,
    PJ = INetURLObject::PART_MAILTO,
    PK = INetURLObject::PART_PATH_BEFORE_QUERY,
    PL = INetURLObject::PART_PCHAR,
    PM = INetURLObject::PART_VISIBLE,
    PN = INetURLObject::PART_VISIBLE_NONSPECIAL,
    PO = INetURLObject::PART_UNO_PARAM_VALUE,
    PP = INetURLObject::PART_UNAMBIGUOUS,
    PQ = INetURLObject::PART_URIC_NO_SLASH,
    PR = INetURLObject::PART_HTTP_QUERY,
};
 
sal_uInt32 const aMustEncodeMap[128]
    = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*   */                                              PP,
/* ! */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* " */                                  PM+PN   +PP,
/* # */                                  PM,
/* $ */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* % */                                  PM,
/* & */ PA   +PD+PE+PF+PG+PH+PI   +PK+PL+PM+PN+PO   +PQ+PR,
/* ' */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* ( */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* ) */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* * */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* + */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO   +PQ+PR,
/* , */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN      +PQ+PR,
/* - */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* . */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* / */      +PD      +PG+PH+PI+PJ+PK   +PM+PN+PO,
/* 0 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 1 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 2 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 3 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 4 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 5 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 6 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 7 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 8 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* 9 */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* : */      +PD+PE   +PG+PH+PI+PJ+PK+PL+PM+PN+PO   +PQ+PR,
/* ; */ PA      +PE+PF+PG+PH+PI+PJ+PK   +PM         +PQ+PR,
/* < */                     +PI         +PM+PN   +PP,
/* = */ PA   +PD+PE+PF+PG+PH      +PK+PL+PM+PN      +PQ+PR,
/* > */                     +PI         +PM+PN   +PP,
/* ? */               +PG               +PM   +PO   +PQ,
/* @ */      +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* A */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* B */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* C */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* D */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* E */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* F */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* G */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* H */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* I */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* J */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* K */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* L */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* M */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* N */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* O */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* P */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* Q */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* R */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* S */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* T */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* U */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* V */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* W */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* X */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* Y */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* Z */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* [ */                PG               +PM+PN+PO,
/* \ */                                 +PM+PN   +PP,
/* ] */                PG               +PM+PN+PO,
/* ^ */                                  PM+PN   +PP,
/* _ */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* ` */                                  PM+PN   +PP,
/* a */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* b */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* c */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* d */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* e */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* f */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* g */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* h */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* i */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* j */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* k */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* l */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* m */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* n */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* o */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* p */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* q */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* r */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* s */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* t */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* u */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* v */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* w */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* x */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* y */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* z */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR,
/* { */                                  PM+PN   +PP,
/* | */                                 +PM+PN   +PP,
/* } */                                  PM+PN   +PP,
/* ~ */ PA   +PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ,
        0 };
 
bool mustEncode(sal_uInt32 nUTF32, INetURLObject::Part ePart)
{
    return !rtl::isAscii(nUTF32) || !(aMustEncodeMap[nUTF32] & ePart);
}
 
}
 
void INetURLObject::setInvalid()
{
    m_aAbsURIRef.setLength(0);
    m_eScheme = INetProtocol::NotValid;
    m_aScheme.clear();
    m_aUser.clear();
    m_aAuth.clear();
    m_aHost.clear();
    m_aPort.clear();
    m_aPath.clear();
    m_aQuery.clear();
    m_aFragment.clear();
}
 
namespace {
 
std::unique_ptr<SvMemoryStream> memoryStream(
        void const * data, sal_Int32 length)
{
    std::unique_ptr<char[]> b(
        new char[length]);
    memcpy(b.get(), data, length);
    std::unique_ptr<SvMemoryStream> s(
        new SvMemoryStream(b.get(), length, StreamMode::READ));
    s->ObjectOwnsMemory(true);
    // coverity[leaked_storage : FALSE] - belongs to SvMemoryStream s at this point
    b.release();
    return s;
}
 
}
 
std::unique_ptr<SvMemoryStream> INetURLObject::getData() const
{
    if( GetProtocol() != INetProtocol::Data )
    {
        return nullptr;
    }
 
    OUString sURLPath = GetURLPath( DecodeMechanism::WithCharset, RTL_TEXTENCODING_ISO_8859_1 );
    sal_Unicode const * pSkippedMediatype = INetMIME::scanContentType( sURLPath );
    sal_Int32 nCharactersSkipped = pSkippedMediatype == nullptr
        ? 0 : pSkippedMediatype-sURLPath.getStr();
    if (sURLPath.match(",", nCharactersSkipped))
    {
        nCharactersSkipped += strlen(",");
        OString sURLEncodedData(
            sURLPath.getStr() + nCharactersSkipped,
            sURLPath.getLength() - nCharactersSkipped,
            RTL_TEXTENCODING_ISO_8859_1, OUSTRING_TO_OSTRING_CVTFLAGS);
        return memoryStream(
            sURLEncodedData.getStr(), sURLEncodedData.getLength());
    }
    else if (sURLPath.matchIgnoreAsciiCase(";base64,", nCharactersSkipped))
    {
        nCharactersSkipped += strlen(";base64,");
        std::u16string_view sBase64Data = sURLPath.subView( nCharactersSkipped );
        css::uno::Sequence< sal_Int8 > aDecodedData;
        if (comphelper::Base64::decodeSomeChars(aDecodedData, sBase64Data)
            == sBase64Data.size())
        {
            return memoryStream(
                aDecodedData.getArray(), aDecodedData.getLength());
        }
    }
    return nullptr;
}
 
namespace {
 
FSysStyle guessFSysStyleByCounting(sal_Unicode const * pBegin,
                                                  sal_Unicode const * pEnd,
                                                  FSysStyle eStyle)
{
    DBG_ASSERT(eStyle
                   & (FSysStyle::Unix
                          | FSysStyle::Dos),
               "guessFSysStyleByCounting(): Bad style");
    DBG_ASSERT(std::numeric_limits< sal_Int32 >::min() < pBegin - pEnd
               && pEnd - pBegin <= std::numeric_limits< sal_Int32 >::max(),
               "guessFSysStyleByCounting(): Too big");
    sal_Int32 nSlashCount
        = (eStyle & FSysStyle::Unix) ?
              0 : std::numeric_limits< sal_Int32 >::min();
    sal_Int32 nBackslashCount
        = (eStyle & FSysStyle::Dos) ?
              0 : std::numeric_limits< sal_Int32 >::min();
    while (pBegin != pEnd)
        switch (*pBegin++)
        {
            case '/':
                ++nSlashCount;
                break;
 
            case '\\':
                ++nBackslashCount;
                break;
        }
    return nSlashCount >= nBackslashCount ?
                   FSysStyle::Unix : FSysStyle::Dos;
}
 
OUString parseScheme(
    sal_Unicode const ** begin, sal_Unicode const * end,
    sal_uInt32 fragmentDelimiter)
{
    sal_Unicode const * p = *begin;
    if (p != end && rtl::isAsciiAlpha(*p)) {
        do {
            ++p;
        } while (p != end
                 && (rtl::isAsciiAlphanumeric(*p) || *p == '+' || *p == '-'
                     || *p == '.'));
        // #i34835# To avoid problems with Windows file paths like "C:\foo",
        // do not accept generic schemes that are only one character long:
        if (end - p > 1 && p[0] == ':' && p[1] != fragmentDelimiter
            && p - *begin >= 2)
        {
            OUString scheme(
                OUString(*begin, p - *begin).toAsciiLowerCase());
            *begin = p + 1;
            return scheme;
        }
    }
    return OUString();
}
 
}
 
bool INetURLObject::setAbsURIRef(std::u16string_view rTheAbsURIRef,
                                 EncodeMechanism eMechanism,
                                 rtl_TextEncoding eCharset,
                                 bool bSmart,
                                 FSysStyle eStyle)
{
    sal_Unicode const * pPos = rTheAbsURIRef.data();
    sal_Unicode const * pEnd = pPos + rTheAbsURIRef.size();
 
    setInvalid();
 
    sal_uInt32 nFragmentDelimiter = '#';
 
    m_aAbsURIRef.setLength(0);
 
    // Parse <scheme>:
    sal_Unicode const * p = pPos;
    PrefixInfo const * pPrefix = getPrefix(p, pEnd);
    if (pPrefix)
    {
        pPos = p;
        m_eScheme = pPrefix->m_eScheme;
 
        const OUString & rTemp = pPrefix->m_eKind >= PrefixInfo::Kind::External ?
                                             pPrefix->m_aTranslatedPrefix :
                                             pPrefix->m_aPrefix;
        m_aAbsURIRef.append(rTemp);
        m_aScheme = SubString( 0, rTemp.indexOf(':') );
    }
    else
    {
        if (bSmart)
        {
            // For scheme detection, the first (if any) of the following
            // productions that matches the input string (and for which the
            // appropriate style bit is set in eStyle, if applicable)
            // determines the scheme. The productions use the auxiliary rules
 
            //    domain = label *("." label)
            //    label = alphanum [*(alphanum / "-") alphanum]
            //    alphanum = ALPHA / DIGIT
            //    IPv6reference = "[" IPv6address "]"
            //    IPv6address = hexpart [":" IPv4address]
            //    IPv4address = 1*3DIGIT 3("." 1*3DIGIT)
            //    hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq])
            //    hexseq = hex4 *(":" hex4)
            //    hex4 = 1*4HEXDIG
            //    UCS4 = <any UCS4 character>
 
            // 1st Production (known scheme; handled by the "if (pPrefix)" branch above):
            //    <one of the known schemes, ignoring case> ":" *UCS4
            // 2nd Production (mailto):
            //    domain "@" domain
            // 3rd Production (ftp):
            //    "FTP" 2*("." label) ["/" *UCS4]
            // 4th Production (http):
            //    label 2*("." label) ["/" *UCS4]
            // 5th Production (file):
            //    "//" (domain / IPv6reference) ["/" *UCS4]
            // 6th Production (Unix file):
            //    "/" *UCS4
            // 7th Production (UNC file; FSysStyle::Dos only):
            //    "\\" domain ["\" *UCS4]
            // 8th Production (Unix-like DOS file; FSysStyle::Dos only):
            //    ALPHA ":" ["/" *UCS4]
            // 9th Production (DOS file; FSysStyle::Dos only):
            //    ALPHA ":" ["\" *UCS4]
            // 10th Production (any scheme; handled by the "m_eScheme = INetProtocol::Generic;" code
            // after this else branch):
            //    <any scheme> ":" *UCS4
 
            // For the 'non URL' file productions 6--9, the interpretation of
            // the input as a (degenerate) URI is turned off, i.e., escape
            // sequences and fragments are never detected as such, but are
            // taken as literal characters.
 
            sal_Unicode const * p1 = pPos;
            if (eStyle & FSysStyle::Dos
                && pEnd - p1 >= 2
                && rtl::isAsciiAlpha(p1[0])
                && p1[1] == ':'
                && (pEnd - p1 == 2 || p1[2] == '/' || p1[2] == '\\'))
            {
                m_eScheme = INetProtocol::File; // 8th, 9th
                eMechanism = EncodeMechanism::All;
                nFragmentDelimiter = 0x80000000;
            }
            else if (eStyle & FSysStyle::Dos
                && pEnd - p1 >= 6
                && p1[0] == '\\' && p1[1] == '\\' && p1[2] == '?' && p1[3] == '\\'
                && rtl::isAsciiAlpha(p1[4])
                && p1[5] == ':'
                && (pEnd - p1 == 6 || p1[6] == '/' || p1[6] == '\\'))
            {
                m_eScheme = INetProtocol::File; // 8th, 9th
                eMechanism = EncodeMechanism::All;
                nFragmentDelimiter = 0x80000000;
            }
            else if (pEnd - p1 >= 2 && p1[0] == '/' && p1[1] == '/')
            {
                p1 += 2;
                if ((scanDomain(p1, pEnd) > 0 || scanIPv6reference(p1, pEnd))
                    && (p1 == pEnd || *p1 == '/'))
                    m_eScheme = INetProtocol::File; // 5th
            }
            else if (p1 != pEnd && *p1 == '/')
            {
                m_eScheme = INetProtocol::File; // 6th
                eMechanism = EncodeMechanism::All;
                nFragmentDelimiter = 0x80000000;
            }
            else if (eStyle & FSysStyle::Dos
                     && pEnd - p1 >= 2
                     && p1[0] == '\\'
                     && p1[1] == '\\')
            {
                p1 += 2;
                if (pEnd - p1 >= 6 && p1[0] == '?' && p1[1] == '\\' && p1[5] == '\\'
                    && rtl::toAsciiLowerCase(p1[2]) == 'u'
                    && rtl::toAsciiLowerCase(p1[3]) == 'n'
                    && rtl::toAsciiLowerCase(p1[4]) == 'c')
                {
                    p1 += 6; // "\\?\UNC\Servername\..."
                }
 
                sal_Int32 n = rtl_ustr_indexOfChar_WithLength(
                    p1, pEnd - p1, '\\');
                sal_Unicode const * pe = n == -1 ? pEnd : p1 + n;
                if (
                    parseHostOrNetBiosName(
                        p1, pe, EncodeMechanism::All, RTL_TEXTENCODING_DONTKNOW,
                        true, nullptr) ||
                    (scanDomain(p1, pe) > 0 && p1 == pe)
                   )
                {
                    m_eScheme = INetProtocol::File; // 7th
                    eMechanism = EncodeMechanism::All;
                    nFragmentDelimiter = 0x80000000;
                }
            }
            else
            {
                sal_Unicode const * pDomainEnd = p1;
                sal_uInt32 nLabels = scanDomain(pDomainEnd, pEnd);
                if (nLabels > 0 && pDomainEnd != pEnd && *pDomainEnd == '@')
                {
                    ++pDomainEnd;
                    if (scanDomain(pDomainEnd, pEnd) > 0
                        && pDomainEnd == pEnd)
                        m_eScheme = INetProtocol::Mailto; // 2nd
                }
                else if (nLabels >= 3
                         && (pDomainEnd == pEnd || *pDomainEnd == '/'))
                    m_eScheme
                        = pDomainEnd - p1 >= 4
                          && (p1[0] == 'f' || p1[0] == 'F')
                          && (p1[1] == 't' || p1[1] == 'T')
                          && (p1[2] == 'p' || p1[2] == 'P')
                          && p1[3] == '.' ?
                              INetProtocol::Ftp : INetProtocol::Http; // 3rd, 4th
            }
        }
 
        OUString aSynScheme;
        if (m_eScheme == INetProtocol::NotValid) {
            sal_Unicode const * p1 = pPos;
            aSynScheme = parseScheme(&p1, pEnd, nFragmentDelimiter);
            if (!aSynScheme.isEmpty())
            {
                if (bSmart && m_eSmartScheme != m_eScheme && p1 != pEnd && rtl::isAsciiDigit(*p1))
                {
                    // rTheAbsURIRef doesn't define a known scheme (handled by the "if (pPrefix)"
                    // branch above); but a known scheme is defined in m_eSmartScheme. If this
                    // scheme may have a port in authority component, then avoid misinterpreting
                    // URLs like www.foo.bar:123/baz as using unknown "www.foo.bar" scheme with
                    // 123/baz rootless path. For now, do not try to handle possible colons in
                    // user information, require such ambiguous URLs to have explicit scheme part.
                    // Also ignore possibility of empty port.
                    const SchemeInfo& rInfo = getSchemeInfo(m_eSmartScheme);
                    if (rInfo.m_bAuthority && rInfo.m_bPort)
                    {
                        // Make sure that all characters from colon to [/?#] or to EOL are digits.
                        // Or maybe make it simple, and just assume that "xyz:1..." is more likely
                        // to be host "xyz" and port "1...", than scheme "xyz" and path "1..."?
                        sal_Unicode const* p2 = p1 + 1;
                        while (p2 != pEnd && rtl::isAsciiDigit(*p2))
                            ++p2;
                        if (p2 == pEnd || *p2 == '/' || *p2 == '?' || *p2 == '#')
                            m_eScheme = m_eSmartScheme;
                    }
                }
 
                if (m_eScheme == INetProtocol::NotValid)
                {
                    m_eScheme = INetProtocol::Generic;
                    pPos = p1;
                }
            }
        }
 
        if (bSmart && m_eScheme == INetProtocol::NotValid && pPos != pEnd
            && *pPos != nFragmentDelimiter)
        {
            m_eScheme = m_eSmartScheme;
        }
 
        if (m_eScheme == INetProtocol::NotValid)
        {
            setInvalid();
            return false;
        }
 
        if (m_eScheme != INetProtocol::Generic) {
            aSynScheme = getSchemeInfo().m_sScheme;
        }
        m_aScheme.set(m_aAbsURIRef, aSynScheme, m_aAbsURIRef.getLength());
        m_aAbsURIRef.append(':');
    }
 
    sal_uInt32 nSegmentDelimiter = '/';
    sal_uInt32 nAltSegmentDelimiter = 0x80000000;
    bool bSkippedInitialSlash = false;
 
    // Parse //<user>;AUTH=<auth>@<host>:<port> or
    // //<user>:<password>@<host>:<port> or
    // //<reg_name>
    if (getSchemeInfo().m_bAuthority)
    {
        sal_Unicode const * pUserInfoBegin = nullptr;
        sal_Unicode const * pUserInfoEnd = nullptr;
        sal_Unicode const * pHostPortBegin = nullptr;
        sal_Unicode const * pHostPortEnd = nullptr;
 
        switch (m_eScheme)
        {
            case INetProtocol::VndSunStarHelp:
            {
                if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '/')
                {
                    setInvalid();
                    return false;
                }
                m_aAbsURIRef.append("//");
                OUStringBuffer aSynAuthority;
                while (pPos < pEnd
                       && *pPos != '/' && *pPos != '?'
                       && *pPos != nFragmentDelimiter)
                {
                    EscapeType eEscapeType;
                    sal_uInt32 nUTF32 = getUTF32(pPos, pEnd,
                                                 eMechanism,
                                                 eCharset, eEscapeType);
                    appendUCS4(aSynAuthority, nUTF32, eEscapeType,
                               PART_AUTHORITY, eCharset, false);
                }
                m_aHost.set(m_aAbsURIRef,
                            aSynAuthority,
                            m_aAbsURIRef.getLength());
                    // misusing m_aHost to store the authority
                break;
            }
 
            case INetProtocol::VndSunStarHier:
            {
                if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
                {
                    pPos += 2;
                    m_aAbsURIRef.append("//");
                    OUStringBuffer aSynAuthority;
                    while (pPos < pEnd
                           && *pPos != '/' && *pPos != '?'
                           && *pPos != nFragmentDelimiter)
                    {
                        EscapeType eEscapeType;
                        sal_uInt32 nUTF32 = getUTF32(pPos,
                                                     pEnd,
                                                     eMechanism,
                                                     eCharset,
                                                     eEscapeType);
                        appendUCS4(aSynAuthority,
                                   nUTF32,
                                   eEscapeType,
                                   PART_AUTHORITY,
                                   eCharset,
                                   false);
                    }
                    if (aSynAuthority.isEmpty())
                    {
                        setInvalid();
                        return false;
                    }
                    m_aHost.set(m_aAbsURIRef,
                                aSynAuthority,
                                m_aAbsURIRef.getLength());
                        // misusing m_aHost to store the authority
                }
                break;
            }
 
            case INetProtocol::VndSunStarPkg:
            case INetProtocol::Cmis:
            {
                if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '/')
                {
                    setInvalid();
                    return false;
                }
                m_aAbsURIRef.append("//");
                OUStringBuffer aSynUser(128);
 
                bool bHasUser = false;
                while (pPos < pEnd && *pPos != '@'
                       && *pPos != '/' && *pPos != '?'
                       && *pPos != nFragmentDelimiter)
                {
                    EscapeType eEscapeType;
                    sal_uInt32 nUTF32 = getUTF32(pPos, pEnd,
                                                 eMechanism,
                                                 eCharset, eEscapeType);
                    appendUCS4(aSynUser, nUTF32, eEscapeType,
                               PART_USER_PASSWORD, eCharset, false);
 
                    bHasUser = *pPos == '@';
                }
 
                OUStringBuffer aSynAuthority(64);
                if ( !bHasUser )
                {
                    aSynAuthority = std::move(aSynUser);
                }
                else
                {
                    m_aUser.set(m_aAbsURIRef,
                            aSynUser,
                            m_aAbsURIRef.getLength());
                    m_aAbsURIRef.append("@");
                    ++pPos;
 
                    while (pPos < pEnd
                           && *pPos != '/' && *pPos != '?'
                           && *pPos != nFragmentDelimiter)
                    {
                        EscapeType eEscapeType;
                        sal_uInt32 nUTF32 = getUTF32(pPos, pEnd,
                                                     eMechanism,
                                                     eCharset, eEscapeType);
                        appendUCS4(aSynAuthority, nUTF32, eEscapeType,
                                   PART_AUTHORITY, eCharset, false);
                    }
                }
                if (aSynAuthority.isEmpty())
                {
                    setInvalid();
                    return false;
                }
                m_aHost.set(m_aAbsURIRef,
                            aSynAuthority,
                            m_aAbsURIRef.getLength());
                    // misusing m_aHost to store the authority
                break;
            }
 
            case INetProtocol::File:
                if (bSmart)
                {
                    // The first of the following seven productions that
                    // matches the rest of the input string (and for which the
                    // appropriate style bit is set in eStyle, if applicable)
                    // determines the used notation.  The productions use the
                    // auxiliary rules
 
                    //    domain = label *("." label)
                    //    label = alphanum [*(alphanum / "-") alphanum]
                    //    alphanum = ALPHA / DIGIT
                    //    IPv6reference = "[" IPv6address "]"
                    //    IPv6address = hexpart [":" IPv4address]
                    //    IPv4address = 1*3DIGIT 3("." 1*3DIGIT)
                    //    hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq])
                    //    hexseq = hex4 *(":" hex4)
                    //    hex4 = 1*4HEXDIG
                    //    path = <any UCS4 character except "#">
                    //    UCS4 = <any UCS4 character>
 
                    // 1st Production (URL):
                    //    "//" [domain / IPv6reference] ["/" *path]
                    //        ["#" *UCS4]
                    //  becomes
                    //    "file://" domain "/" *path ["#" *UCS4]
                    if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
                    {
                        sal_Unicode const * p1 = pPos + 2;
                        while (p1 != pEnd && *p1 != '/' &&
                               *p1 != nFragmentDelimiter)
                        {
                            ++p1;
                        }
                        if (parseHostOrNetBiosName(
                                pPos + 2, p1, EncodeMechanism::All,
                                RTL_TEXTENCODING_DONTKNOW, true, nullptr))
                        {
                            m_aAbsURIRef.append("//");
                            pHostPortBegin = pPos + 2;
                            pHostPortEnd = p1;
                            pPos = p1;
                            break;
                        }
                    }
 
                    // 2nd Production (MS IE generated 1; FSysStyle::Dos only):
                    //    "//" ALPHA ":" ["/" *path] ["#" *UCS4]
                    //  becomes
                    //    "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
                    //  replacing "\" by "/" within <*path>
                    // 3rd Production (MS IE generated 2; FSysStyle::Dos only):
                    //    "//" ALPHA ":" ["\" *path] ["#" *UCS4]
                    //  becomes
                    //    "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
                    //  replacing "\" by "/" within <*path>
                    // 4th Production (miscounted slashes):
                    //    "//" *path ["#" *UCS4]
                    //  becomes
                    //    "file:///" *path ["#" *UCS4]
                    if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
                    {
                        m_aAbsURIRef.append("//");
                        pPos += 2;
                        bSkippedInitialSlash = true;
                        if ((eStyle & FSysStyle::Dos)
                            && pEnd - pPos >= 2
                            && rtl::isAsciiAlpha(pPos[0])
                            && pPos[1] == ':'
                            && (pEnd - pPos == 2
                                || pPos[2] == '/' || pPos[2] == '\\'))
                            nAltSegmentDelimiter = '\\';
                        break;
                    }
 
                    // 5th Production (Unix):
                    //    "/" *path ["#" *UCS4]
                    //  becomes
                    //    "file:///" *path ["#" *UCS4]
                    if (pPos < pEnd && *pPos == '/')
                    {
                        m_aAbsURIRef.append("//");
                        break;
                    }
 
                    // 6th Production (UNC; FSysStyle::Dos only):
                    //    "\\" domain ["\" *path] ["#" *UCS4]
                    //  becomes
                    //    "file://" domain "/" *path ["#" *UCS4]
                    //  replacing "\" by "/" within <*path>
                    if (eStyle & FSysStyle::Dos
                        && pEnd - pPos >= 2
                        && pPos[0] == '\\'
                        && pPos[1] == '\\')
                    {
                        sal_Unicode const * p1 = pPos + 2;
                        sal_Unicode const * pHostPortTentativeBegin = p1;
                        if (pEnd - p1 >= 6 && p1[0] == '?' && p1[1] == '\\' && p1[5] == '\\'
                            && rtl::toAsciiLowerCase(p1[2]) == 'u'
                            && rtl::toAsciiLowerCase(p1[3]) == 'n'
                            && rtl::toAsciiLowerCase(p1[4]) == 'c')
                        {
                            p1 += 6; // "\\?\UNC\Servername\..."
                            pHostPortTentativeBegin = p1;
                        }
 
                        sal_Unicode const * pe = p1;
                        while (pe < pEnd && *pe != '\\' &&
                               *pe != nFragmentDelimiter)
                        {
                            ++pe;
                        }
                        if (
                             parseHostOrNetBiosName(
                                p1, pe, EncodeMechanism::All,
                                RTL_TEXTENCODING_DONTKNOW, true, nullptr) ||
                             (scanDomain(p1, pe) > 0 && p1 == pe)
                           )
                        {
                            m_aAbsURIRef.append("//");
                            pHostPortBegin = pHostPortTentativeBegin;
                            pHostPortEnd = pe;
                            pPos = pe;
                            nSegmentDelimiter = '\\';
                            break;
                        }
                    }
 
                    // 7th Production (Unix-like DOS; FSysStyle::Dos only):
                    //    ALPHA ":" ["/" *path] ["#" *UCS4]
                    //  becomes
                    //    "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
                    //  replacing "\" by "/" within <*path>
                    // 8th Production (DOS; FSysStyle::Dos only):
                    //    ALPHA ":" ["\" *path] ["#" *UCS4]
                    //  becomes
                    //    "file:///" ALPHA ":" ["/" *path] ["#" *UCS4]
                    //  replacing "\" by "/" within <*path>
                    if (eStyle & FSysStyle::Dos)
                    {
                        sal_Unicode const* p1 = pPos;
                        if (pEnd - p1 >= 4 && p1[0] == '\\' && p1[1] == '\\' && p1[2] == '?'
                            && p1[3] == '\\')
                            p1 += 4; // "\\?\c:\..."
 
                        if (pEnd - p1 >= 2
                            && rtl::isAsciiAlpha(p1[0])
                            && p1[1] == ':'
                            && (pEnd - p1 == 2
                                || p1[2] == '/'
                                || p1[2] == '\\'))
                        {
                            pPos = p1;
                            m_aAbsURIRef.append("//");
                            nAltSegmentDelimiter = '\\';
                            bSkippedInitialSlash = true;
                            break;
                        }
                    }
 
                    // 9th Production (any):
                    //    *path ["#" *UCS4]
                    //  becomes
                    //    "file:///" *path ["#" *UCS4]
                    //  replacing the delimiter by "/" within <*path>.  The
                    //  delimiter is that character from the set { "/", "\"}
                    // which appears most often in <*path> (if FSysStyle::Unix
                    //  is not among the style bits, "/" is removed from the
                    //  set; if FSysStyle::Dos is not among the style bits, "\" is
                    //  removed from the set).  If two or
                    //  more characters appear the same number of times, the
                    //  character mentioned first in that set is chosen.  If
                    //  the first character of <*path> is the delimiter, that
                    //  character is not copied
                    if (eStyle & (FSysStyle::Unix | FSysStyle::Dos))
                    {
                        m_aAbsURIRef.append("//");
                        switch (guessFSysStyleByCounting(pPos, pEnd, eStyle))
                        {
                            case FSysStyle::Unix:
                                nSegmentDelimiter = '/';
                                break;
 
                            case FSysStyle::Dos:
                                nSegmentDelimiter = '\\';
                                break;
 
                            default:
                                OSL_FAIL(
                                    "INetURLObject::setAbsURIRef():"
                                        " Bad guessFSysStyleByCounting");
                                break;
                        }
                        bSkippedInitialSlash
                            = pPos != pEnd && *pPos != nSegmentDelimiter;
                        break;
                    }
                }
                [[fallthrough]];
            default:
            {
                // For INetProtocol::File, allow an empty authority ("//") to be
                // missing if the following path starts with an explicit "/"
                // (Java is notorious in generating such file URLs, so be
                // liberal here):
                if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
                    pPos += 2;
                else if (!bSmart
                         && !(m_eScheme == INetProtocol::File
                              && pPos != pEnd && *pPos == '/'))
                {
                    setInvalid();
                    return false;
                }
                m_aAbsURIRef.append("//");
 
                sal_Unicode const * pAuthority = pPos;
                sal_uInt32 c = getSchemeInfo().m_bQuery ? '?' : 0x80000000;
                while (pPos < pEnd && *pPos != '/' && *pPos != c
                       && *pPos != nFragmentDelimiter)
                    ++pPos;
                if (getSchemeInfo().m_bUser)
                    if (getSchemeInfo().m_bHost)
                    {
                        sal_Unicode const * p1 = pAuthority;
                        while (p1 < pPos && *p1 != '@')
                            ++p1;
                        if (p1 == pPos)
                        {
                            pHostPortBegin = pAuthority;
                            pHostPortEnd = pPos;
                        }
                        else
                        {
                            pUserInfoBegin = pAuthority;
                            pUserInfoEnd = p1;
                            pHostPortBegin = p1 + 1;
                            pHostPortEnd = pPos;
                        }
                    }
                    else
                    {
                        pUserInfoBegin = pAuthority;
                        pUserInfoEnd = pPos;
                    }
                else if (getSchemeInfo().m_bHost)
                {
                    pHostPortBegin = pAuthority;
                    pHostPortEnd = pPos;
                }
                else if (pPos != pAuthority)
                {
                    setInvalid();
                    return false;
                }
                break;
            }
        }
 
        if (pUserInfoBegin)
        {
            Part ePart = PART_USER_PASSWORD;
            bool bSupportsPassword = getSchemeInfo().m_bPassword;
            bool bSupportsAuth
                = !bSupportsPassword && getSchemeInfo().m_bAuth;
            bool bHasAuth = false;
            OUStringBuffer aSynUser;
            sal_Unicode const * p1 = pUserInfoBegin;
            while (p1 < pUserInfoEnd)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd,
                                             eMechanism, eCharset, eEscapeType);
                if (eEscapeType == EscapeType::NONE)
                {
                    if (nUTF32 == ':' && bSupportsPassword)
                    {
                        bHasAuth = true;
                        break;
                    }
                    else if (nUTF32 == ';' && bSupportsAuth
                             && pUserInfoEnd - p1
                                    > RTL_CONSTASCII_LENGTH("auth=")
                             && INetMIME::equalIgnoreCase(
                                    p1,
                                    p1 + RTL_CONSTASCII_LENGTH("auth="),
                                    "auth="))
                    {
                        p1 += RTL_CONSTASCII_LENGTH("auth=");
                        bHasAuth = true;
                        break;
                    }
                }
                appendUCS4(aSynUser, nUTF32, eEscapeType, ePart,
                           eCharset, false);
            }
            m_aUser.set(m_aAbsURIRef, aSynUser, m_aAbsURIRef.getLength());
            if (bHasAuth)
            {
                if (bSupportsPassword)
                {
                    m_aAbsURIRef.append(':');
                    OUStringBuffer aSynAuth;
                    while (p1 < pUserInfoEnd)
                    {
                        EscapeType eEscapeType;
                        sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd,
                                                     eMechanism, eCharset,
                                                     eEscapeType);
                        appendUCS4(aSynAuth, nUTF32, eEscapeType,
                                   ePart, eCharset, false);
                    }
                    m_aAuth.set(m_aAbsURIRef, aSynAuth, m_aAbsURIRef.getLength());
                }
                else
                {
                    m_aAbsURIRef.append(";AUTH=");
                    OUStringBuffer aSynAuth;
                    while (p1 < pUserInfoEnd)
                    {
                        EscapeType eEscapeType;
                        sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd,
                                                     eMechanism, eCharset,
                                                     eEscapeType);
                        if (!INetMIME::isIMAPAtomChar(nUTF32))
                        {
                            setInvalid();
                            return false;
                        }
                        appendUCS4(aSynAuth, nUTF32, eEscapeType,
                                   ePart, eCharset, false);
                    }
                    m_aAuth.set(m_aAbsURIRef, aSynAuth, m_aAbsURIRef.getLength());
                }
            }
            if (pHostPortBegin)
                m_aAbsURIRef.append('@');
        }
 
        if (pHostPortBegin)
        {
            sal_Unicode const * pPort = pHostPortEnd;
            if ( getSchemeInfo().m_bPort && pHostPortBegin < pHostPortEnd )
            {
                sal_Unicode const * p1 = pHostPortEnd - 1;
                while (p1 > pHostPortBegin && rtl::isAsciiDigit(*p1))
                    --p1;
                if (*p1 == ':')
                    pPort = p1;
            }
            bool bNetBiosName = false;
            switch (m_eScheme)
            {
                case INetProtocol::File:
                    // If the host equals "LOCALHOST" (unencoded and ignoring
                    // case), turn it into an empty host:
                    if (INetMIME::equalIgnoreCase(pHostPortBegin, pPort,
                                                  "localhost"))
                        pHostPortBegin = pPort;
                    bNetBiosName = true;
                    break;
 
                case INetProtocol::Ldap:
                case INetProtocol::Smb:
                    if (pHostPortBegin == pPort && pPort != pHostPortEnd)
                    {
                        setInvalid();
                        return false;
                    }
                    break;
                default:
                    if (pHostPortBegin == pPort)
                    {
                        setInvalid();
                        return false;
                    }
                    break;
            }
            sal_Int32 nLenBeforeHost = m_aAbsURIRef.getLength();
            if (!parseHostOrNetBiosName(
                    pHostPortBegin, pPort, eMechanism, eCharset,
                    bNetBiosName, &m_aAbsURIRef))
            {
                setInvalid();
                return false;
            }
            m_aHost = SubString(nLenBeforeHost, m_aAbsURIRef.getLength() - nLenBeforeHost);
            if (pPort != pHostPortEnd)
            {
                m_aAbsURIRef.append(':');
                m_aPort.set(m_aAbsURIRef,
                    std::u16string_view{pPort + 1, static_cast<size_t>(pHostPortEnd - (pPort + 1))},
                    m_aAbsURIRef.getLength());
            }
        }
    }
 
    // Parse <path>
    sal_Int32 nBeforePathLength = m_aAbsURIRef.getLength();
    if (!parsePath(m_eScheme, &pPos, pEnd, eMechanism, eCharset,
                   bSkippedInitialSlash, nSegmentDelimiter,
                   nAltSegmentDelimiter,
                   getSchemeInfo().m_bQuery ? '?' : 0x80000000,
                   nFragmentDelimiter, m_aAbsURIRef))
    {
        setInvalid();
        return false;
    }
    m_aPath = SubString(nBeforePathLength, m_aAbsURIRef.getLength() - nBeforePathLength);
 
    // Parse ?<query>
    if (getSchemeInfo().m_bQuery && pPos < pEnd && *pPos == '?')
    {
        m_aAbsURIRef.append('?');
        OUStringBuffer aSynQuery;
        for (++pPos; pPos < pEnd && *pPos != nFragmentDelimiter;)
        {
            EscapeType eEscapeType;
            sal_uInt32 nUTF32 = getUTF32(pPos, pEnd,
                                         eMechanism, eCharset, eEscapeType);
            appendUCS4(aSynQuery, nUTF32, eEscapeType,
                       PART_URIC, eCharset, true);
        }
        m_aQuery.set(m_aAbsURIRef, aSynQuery, m_aAbsURIRef.getLength());
    }
 
    // Parse #<fragment>
    if (pPos < pEnd && *pPos == nFragmentDelimiter)
    {
        m_aAbsURIRef.append(sal_Unicode(nFragmentDelimiter));
        OUStringBuffer aSynFragment;
        for (++pPos; pPos < pEnd;)
        {
            EscapeType eEscapeType;
            sal_uInt32 nUTF32 = getUTF32(pPos, pEnd,
                                         eMechanism, eCharset, eEscapeType);
            appendUCS4(aSynFragment, nUTF32, eEscapeType, PART_URIC,
                       eCharset, true);
        }
        m_aFragment.set(m_aAbsURIRef, aSynFragment, m_aAbsURIRef.getLength());
    }
 
    if (pPos != pEnd)
    {
        setInvalid();
        return false;
    }
 
    return true;
}
 
void INetURLObject::changeScheme(INetProtocol eTargetScheme) {
    sal_Int32 oldSchemeLen = 0;
    const OUString& rOldSchemeName = getSchemeInfo().m_sScheme;
    if (m_eScheme == INetProtocol::Generic)
        oldSchemeLen = m_aScheme.getLength();
    else
        oldSchemeLen = rOldSchemeName.getLength();
    m_eScheme=eTargetScheme;
    const OUString& rNewSchemeName = getSchemeInfo().m_sScheme;
    sal_Int32 newSchemeLen = rNewSchemeName.getLength();
    m_aAbsURIRef.remove(0, oldSchemeLen);
    m_aAbsURIRef.insert(0, rNewSchemeName);
    sal_Int32 delta=newSchemeLen-oldSchemeLen;
    m_aUser+=delta;
    m_aAuth+=delta;
    m_aHost+=delta;
    m_aPort+=delta;
    m_aPath+=delta;
    m_aQuery+=delta;
    m_aFragment+=delta;
}
 
bool INetURLObject::convertRelToAbs(OUString const & rTheRelURIRef,
                                    INetURLObject & rTheAbsURIRef,
                                    bool & rWasAbsolute,
                                    EncodeMechanism eMechanism,
                                    rtl_TextEncoding eCharset,
                                    bool bIgnoreFragment, bool bSmart,
                                    bool bRelativeNonURIs, FSysStyle eStyle)
    const
{
    sal_Unicode const * p = rTheRelURIRef.getStr();
    sal_Unicode const * pEnd = p + rTheRelURIRef.getLength();
 
    sal_Unicode const * pPrefixBegin = p;
    PrefixInfo const * pPrefix = getPrefix(pPrefixBegin, pEnd);
    bool hasScheme = pPrefix != nullptr;
    if (!hasScheme) {
        pPrefixBegin = p;
        hasScheme = !parseScheme(&pPrefixBegin, pEnd, '#').isEmpty();
    }
 
    sal_uInt32 nSegmentDelimiter = '/';
    sal_uInt32 nQueryDelimiter
        = !bSmart || getSchemeInfo().m_bQuery ? '?' : 0x80000000;
    sal_uInt32 nFragmentDelimiter = '#';
    Part ePart = PART_VISIBLE;
 
    if (!hasScheme && bSmart)
    {
        // If the input matches any of the following productions (for which
        // the appropriate style bit is set in eStyle), it is assumed to be an
        // absolute file system path, rather than a relative URI reference.
        // (This is only a subset of the productions used for scheme detection
        // in INetURLObject::setAbsURIRef(), because most of those productions
        // interfere with the syntax of relative URI references.)  The
        // productions use the auxiliary rules
 
        //    domain = label *("." label)
        //    label = alphanum [*(alphanum / "-") alphanum]
        //    alphanum = ALPHA / DIGIT
        //    UCS4 = <any UCS4 character>
 
        // 1st Production (UNC file; FSysStyle::Dos only):
        //    "\\" domain ["\" *UCS4]
        // 2nd Production (Unix-like DOS file; FSysStyle::Dos only):
        //    ALPHA ":" ["/" *UCS4]
        // 3rd Production (DOS file; FSysStyle::Dos only):
        //    ALPHA ":" ["\" *UCS4]
        if (eStyle & FSysStyle::Dos)
        {
            bool bFSys = false;
            sal_Unicode const * q = p;
            if (pEnd - q >= 2
                && rtl::isAsciiAlpha(q[0])
                && q[1] == ':'
                && (pEnd - q == 2 || q[2] == '/' || q[2] == '\\'))
                bFSys = true; // 2nd, 3rd
            else if (pEnd - q >= 2 && q[0] == '\\' && q[1] == '\\')
            {
                q += 2;
                sal_Int32 n = rtl_ustr_indexOfChar_WithLength(
                    q, pEnd - q, '\\');
                if (n == 1 && q[0] == '?')
                {
                    // "\\?\c:\..." or "\\?\UNC\servername\..."
                    q += 2;
                    if (pEnd - q >= 2
                        && rtl::isAsciiAlpha(q[0])
                        && q[1] == ':'
                        && (pEnd - q == 2 || q[2] == '/' || q[2] == '\\'))
                    {
                        bFSys = true; // 2nd, 3rd
                    }
                    else if (pEnd - q >= 4
                        && q[3] == '\\'
                        && rtl::toAsciiLowerCase(q[0]) == 'u'
                        && rtl::toAsciiLowerCase(q[1]) == 'n'
                        && rtl::toAsciiLowerCase(q[2]) == 'c')
                    {
                        q += 4; // Check if it's 1st below
                    }
                }
                if (!bFSys)
                {
                    sal_Unicode const * qe = n == -1 ? pEnd : q + n;
                    if (parseHostOrNetBiosName(
                            q, qe, EncodeMechanism::All, RTL_TEXTENCODING_DONTKNOW,
                            true, nullptr))
                    {
                        bFSys = true; // 1st
                    }
                }
            }
            if (bFSys)
            {
                INetURLObject aNewURI;
                aNewURI.setAbsURIRef(rTheRelURIRef, eMechanism,
                                     eCharset, true, eStyle);
                if (!aNewURI.HasError())
                {
                    rTheAbsURIRef = std::move(aNewURI);
                    rWasAbsolute = true;
                    return true;
                }
            }
        }
 
        // When the base URL is a file URL, accept relative file system paths
        // using "\" or ":" as delimiter (and ignoring URI conventions for "%"
        // and "#"), as well as relative URIs using "/" as delimiter:
        if (m_eScheme == INetProtocol::File)
            switch (guessFSysStyleByCounting(p, pEnd, eStyle))
            {
                case FSysStyle::Unix:
                    nSegmentDelimiter = '/';
                    break;
 
                case FSysStyle::Dos:
                    nSegmentDelimiter = '\\';
                    bRelativeNonURIs = true;
                    break;
 
                default:
                    OSL_FAIL("INetURLObject::convertRelToAbs():"
                                  " Bad guessFSysStyleByCounting");
                    break;
            }
 
        if (bRelativeNonURIs)
        {
            eMechanism = EncodeMechanism::All;
            nQueryDelimiter = 0x80000000;
            nFragmentDelimiter = 0x80000000;
            ePart = PART_VISIBLE_NONSPECIAL;
        }
    }
 
    // If the relative URI has the same scheme as the base URI, and that
    // scheme is hierarchical, then ignore its presence in the relative
    // URI in order to be backward compatible (cf. RFC 2396 section 5.2
    // step 3):
    if (pPrefix && pPrefix->m_eScheme == m_eScheme
        && getSchemeInfo().m_bHierarchical)
    {
        hasScheme = false;
        while (p != pEnd && *p++ != ':') ;
    }
    rWasAbsolute = hasScheme;
 
    // Fast solution for non-relative URIs:
    if (hasScheme)
    {
        INetURLObject aNewURI(rTheRelURIRef, eMechanism, eCharset);
        if (aNewURI.HasError())
        {
            rWasAbsolute = false;
            return false;
        }
 
        if (bIgnoreFragment)
            aNewURI.clearFragment();
        rTheAbsURIRef = std::move(aNewURI);
        return true;
    }
 
    enum State { STATE_AUTH, STATE_ABS_PATH, STATE_REL_PATH, STATE_FRAGMENT,
                 STATE_DONE };
 
    OUStringBuffer aSynAbsURIRef(128);
    // make sure that the scheme is copied for generic schemes: getSchemeInfo().m_pScheme
    // is empty ("") in that case, so take the scheme from m_aAbsURIRef
    if (m_eScheme != INetProtocol::Generic)
    {
        aSynAbsURIRef.append(getSchemeInfo().m_sScheme);
    }
    else
    {
        sal_Unicode const * pSchemeBegin
            = m_aAbsURIRef.getStr();
        sal_Unicode const * pSchemeEnd = pSchemeBegin;
        while (pSchemeEnd[0] != ':')
        {
            ++pSchemeEnd;
        }
        aSynAbsURIRef.append(pSchemeBegin, pSchemeEnd - pSchemeBegin);
    }
    aSynAbsURIRef.append(':');
 
    State eState = STATE_AUTH;
    bool bSameDoc = true;
 
    if (getSchemeInfo().m_bAuthority)
    {
        if (pEnd - p >= 2 && p[0] == '/' && p[1] == '/')
        {
            aSynAbsURIRef.append("//");
            p += 2;
            eState = STATE_ABS_PATH;
            bSameDoc = false;
            while (p != pEnd)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32
                    = getUTF32(p, pEnd, eMechanism,
                               eCharset, eEscapeType);
                if (eEscapeType == EscapeType::NONE)
                {
                    if (nUTF32 == nSegmentDelimiter)
                        break;
                    else if (nUTF32 == nFragmentDelimiter)
                    {
                        eState = STATE_FRAGMENT;
                        break;
                    }
                }
                appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType,
                           PART_VISIBLE, eCharset, true);
            }
        }
        else
        {
            SubString aAuthority(getAuthority());
            aSynAbsURIRef.append(m_aAbsURIRef.getStr()
                                     + aAuthority.getBegin(),
                                 aAuthority.getLength());
        }
    }
 
    if (eState == STATE_AUTH)
    {
        if (p == pEnd)
            eState = STATE_DONE;
        else if (*p == nFragmentDelimiter)
        {
            ++p;
            eState = STATE_FRAGMENT;
        }
        else if (*p == nSegmentDelimiter)
        {
            ++p;
            eState = STATE_ABS_PATH;
            bSameDoc = false;
        }
        else
        {
            eState = STATE_REL_PATH;
            bSameDoc = false;
        }
    }
 
    if (eState == STATE_ABS_PATH)
    {
        aSynAbsURIRef.append('/');
        eState = STATE_DONE;
        while (p != pEnd)
        {
            EscapeType eEscapeType;
            sal_uInt32 nUTF32
                = getUTF32(p, pEnd, eMechanism, eCharset, eEscapeType);
            if (eEscapeType == EscapeType::NONE)
            {
                if (nUTF32 == nFragmentDelimiter)
                {
                    eState = STATE_FRAGMENT;
                    break;
                }
                else if (nUTF32 == nSegmentDelimiter)
                    nUTF32 = '/';
            }
            appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, ePart,
                       eCharset, true);
        }
    }
    else if (eState == STATE_REL_PATH)
    {
        if (!getSchemeInfo().m_bHierarchical)
        {
            // Detect cases where a relative input could not be made absolute
            // because the given base URL is broken (most probably because it is
            // empty):
            SAL_WARN_IF(
                HasError(), "tools.urlobj",
                "cannot make <" << rTheRelURIRef
                    << "> absolute against broken base <"
                    << GetMainURL(DecodeMechanism::NONE) << ">");
            rWasAbsolute = false;
            return false;
        }
 
        sal_Unicode const * pBasePathBegin
            = m_aAbsURIRef.getStr() + m_aPath.getBegin();
        sal_Unicode const * pBasePathEnd
            = pBasePathBegin + m_aPath.getLength();
        while (pBasePathEnd != pBasePathBegin)
            if (*(--pBasePathEnd) == '/')
            {
                ++pBasePathEnd;
                break;
            }
 
        sal_Int32 nPathBegin = aSynAbsURIRef.getLength();
        aSynAbsURIRef.append(pBasePathBegin, pBasePathEnd - pBasePathBegin);
        DBG_ASSERT(aSynAbsURIRef.getLength() > nPathBegin
                 && aSynAbsURIRef[aSynAbsURIRef.getLength() - 1] == '/',
                 "INetURLObject::convertRelToAbs(): Bad base path");
 
        while (p != pEnd && *p != nQueryDelimiter && *p != nFragmentDelimiter)
        {
            if (*p == '.')
            {
                if (pEnd - p == 1
                    || p[1] == nSegmentDelimiter
                    || p[1] == nQueryDelimiter
                    || p[1] == nFragmentDelimiter)
                {
                    ++p;
                    if (p != pEnd && *p == nSegmentDelimiter)
                        ++p;
                    continue;
                }
                else if (pEnd - p >= 2
                         && p[1] == '.'
                         && (pEnd - p == 2
                             || p[2] == nSegmentDelimiter
                             || p[2] == nQueryDelimiter
                             || p[2] == nFragmentDelimiter)
                         && aSynAbsURIRef.getLength() - nPathBegin > 1)
                {
                    p += 2;
                    if (p != pEnd && *p == nSegmentDelimiter)
                        ++p;
 
                    sal_Int32 i = aSynAbsURIRef.getLength() - 2;
                    while (i > nPathBegin && aSynAbsURIRef[i] != '/')
                        --i;
                    aSynAbsURIRef.setLength(i + 1);
                    DBG_ASSERT(
                        aSynAbsURIRef.getLength() > nPathBegin
                        && aSynAbsURIRef[aSynAbsURIRef.getLength() - 1] == '/',
                        "INetURLObject::convertRelToAbs(): Bad base path");
                    continue;
                }
            }
 
            while (p != pEnd
                   && *p != nSegmentDelimiter
                   && *p != nQueryDelimiter
                   && *p != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32
                    = getUTF32(p, pEnd, eMechanism,
                               eCharset, eEscapeType);
                appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, ePart,
                           eCharset, true);
            }
            if (p != pEnd && *p == nSegmentDelimiter)
            {
                aSynAbsURIRef.append('/');
                ++p;
            }
        }
 
        while (p != pEnd && *p != nFragmentDelimiter)
        {
            EscapeType eEscapeType;
            sal_uInt32 nUTF32
                = getUTF32(p, pEnd, eMechanism, eCharset, eEscapeType);
            appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, ePart,
                       eCharset, true);
        }
 
        if (p == pEnd)
            eState = STATE_DONE;
        else
        {
            ++p;
            eState = STATE_FRAGMENT;
        }
    }
    else if (bSameDoc)
    {
        aSynAbsURIRef.append(m_aAbsURIRef.getStr() + m_aPath.getBegin(),
                             m_aPath.getLength());
        if (m_aQuery.isPresent())
            aSynAbsURIRef.append(m_aAbsURIRef.getStr()
                                     + m_aQuery.getBegin() - 1,
                                 m_aQuery.getLength() + 1);
    }
 
    if (eState == STATE_FRAGMENT && !bIgnoreFragment)
    {
        aSynAbsURIRef.append('#');
        while (p != pEnd)
        {
            EscapeType eEscapeType;
            sal_uInt32 nUTF32
                = getUTF32(p, pEnd, eMechanism, eCharset, eEscapeType);
            appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType,
                       PART_VISIBLE, eCharset, true);
        }
    }
 
    INetURLObject aNewURI(aSynAbsURIRef);
    if (aNewURI.HasError())
    {
        // Detect cases where a relative input could not be made absolute
        // because the given base URL is broken (most probably because it is
        // empty):
        SAL_WARN_IF(
            HasError(), "tools.urlobj",
            "cannot make <" << rTheRelURIRef
                << "> absolute against broken base <" << GetMainURL(DecodeMechanism::NONE)
                << ">");
        rWasAbsolute = false;
        return false;
    }
 
    rTheAbsURIRef = std::move(aNewURI);
    return true;
}
 
bool INetURLObject::convertAbsToRel(OUString const & rTheAbsURIRef,
                                    OUString & rTheRelURIRef,
                                    EncodeMechanism eEncodeMechanism,
                                    DecodeMechanism eDecodeMechanism,
                                    rtl_TextEncoding eCharset,
                                    FSysStyle eStyle) const
{
    // Check for hierarchical base URL:
    if (!getSchemeInfo().m_bHierarchical)
    {
        rTheRelURIRef = decode(rTheAbsURIRef, eDecodeMechanism, eCharset);
        return false;
    }
 
    // Convert the input (absolute or relative URI ref) to an absolute URI
    // ref:
    INetURLObject aSubject;
    bool bWasAbsolute;
    if (!convertRelToAbs(rTheAbsURIRef, aSubject, bWasAbsolute,
                         eEncodeMechanism, eCharset, false, false, false,
                         eStyle))
    {
        rTheRelURIRef = decode(rTheAbsURIRef, eDecodeMechanism, eCharset);
        return false;
    }
 
    // Check for differing scheme or authority parts:
    if ((m_aScheme.compare(
             aSubject.m_aScheme, m_aAbsURIRef, aSubject.m_aAbsURIRef)
         != 0)
        || (m_aUser.compare(
                aSubject.m_aUser, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0)
        || (m_aAuth.compare(
                aSubject.m_aAuth, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0)
        || (m_aHost.compare(
                aSubject.m_aHost, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0)
        || (m_aPort.compare(
                aSubject.m_aPort, m_aAbsURIRef, aSubject.m_aAbsURIRef)
            != 0))
    {
        rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset);
        return false;
    }
 
    sal_Unicode const * pBasePathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pBasePathEnd = pBasePathBegin + m_aPath.getLength();
    sal_Unicode const * pSubjectPathBegin
        = aSubject.m_aAbsURIRef.getStr() + aSubject.m_aPath.getBegin();
    sal_Unicode const * pSubjectPathEnd
        = pSubjectPathBegin + aSubject.m_aPath.getLength();
 
    // Make nMatch point past the last matching slash, or past the end of the
    // paths, in case they are equal:
    sal_Unicode const * pSlash = nullptr;
    sal_Unicode const * p1 = pBasePathBegin;
    sal_Unicode const * p2 = pSubjectPathBegin;
    for (;;)
    {
        if (p1 == pBasePathEnd || p2 == pSubjectPathEnd)
        {
            if (p1 == pBasePathEnd && p2 == pSubjectPathEnd)
                pSlash = p1;
            break;
        }
 
        sal_Unicode c = *p1++;
        if (c != *p2++)
            break;
        if (c == '/')
            pSlash = p1;
    }
    if (!pSlash)
    {
        // One of the paths does not start with '/':
        rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset);
        return false;
    }
    sal_Int32 nMatch = pSlash - pBasePathBegin;
 
    // If the two URLs are DOS file URLs starting with different volumes
    // (e.g., file:///a:/... and file:///b:/...), the subject is not made
    // relative (it could be, but some people do not like that):
    if (m_eScheme == INetProtocol::File
        && nMatch <= 1
        && hasDosVolume(eStyle)
        && aSubject.hasDosVolume(eStyle)) //TODO! ok to use eStyle for these?
    {
        rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset);
        return false;
    }
 
    // For every slash in the base path after nMatch, a prefix of "../" is
    // added to the new relative URL (if the common prefix of the two paths is
    // only "/"---but see handling of file URLs above---, the complete subject
    // path could go into the new relative URL instead, but some people don't
    // like that):
    OUStringBuffer aSynRelURIRef;
    for (sal_Unicode const * p = pBasePathBegin + nMatch; p != pBasePathEnd;
         ++p)
    {
        if (*p == '/')
            aSynRelURIRef.append("../");
    }
 
    // If the new relative URL would start with "//" (i.e., it would be
    // mistaken for a relative URL starting with an authority part), or if the
    // new relative URL would neither be empty nor start with <"/"> nor start
    // with <1*rseg> (i.e., it could be mistaken for an absolute URL starting
    // with a scheme part), then the new relative URL is prefixed with "./":
    if (aSynRelURIRef.isEmpty())
    {
        if (pSubjectPathEnd - pSubjectPathBegin >= nMatch + 2
            && pSubjectPathBegin[nMatch] == '/'
            && pSubjectPathBegin[nMatch + 1] == '/')
        {
            aSynRelURIRef.append("./");
        }
        else
        {
            for (sal_Unicode const * p = pSubjectPathBegin + nMatch;
                 p != pSubjectPathEnd && *p != '/'; ++p)
            {
                if (mustEncode(*p, PART_REL_SEGMENT_EXTRA))
                {
                    aSynRelURIRef.append("./");
                    break;
                }
            }
        }
    }
 
    // The remainder of the subject path, starting at nMatch, is appended to
    // the new relative URL:
    aSynRelURIRef.append(decode(pSubjectPathBegin + nMatch, pSubjectPathEnd,
                            eDecodeMechanism, eCharset));
 
    // If the subject has defined query or fragment parts, they are appended
    // to the new relative URL:
    if (aSubject.m_aQuery.isPresent())
    {
        aSynRelURIRef.append("?"
            + aSubject.decode(aSubject.m_aQuery, eDecodeMechanism, eCharset));
    }
    if (aSubject.m_aFragment.isPresent())
    {
        aSynRelURIRef.append("#"
            + aSubject.decode(aSubject.m_aFragment, eDecodeMechanism, eCharset));
    }
 
    rTheRelURIRef = aSynRelURIRef.makeStringAndClear();
    return true;
}
 
// static
bool INetURLObject::convertIntToExt(std::u16string_view rTheIntURIRef,
                                    OUString & rTheExtURIRef,
                                    DecodeMechanism eDecodeMechanism,
                                    rtl_TextEncoding eCharset)
{
    OUStringBuffer aSynExtURIRef(256);
    encodeText(aSynExtURIRef, rTheIntURIRef, PART_VISIBLE,
               EncodeMechanism::NotCanonical, eCharset, true);
    sal_Unicode const * pBegin = aSynExtURIRef.getStr();
    sal_Unicode const * pEnd = pBegin + aSynExtURIRef.getLength();
    sal_Unicode const * p = pBegin;
    PrefixInfo const * pPrefix = getPrefix(p, pEnd);
    bool bConvert = pPrefix && pPrefix->m_eKind == PrefixInfo::Kind::Internal;
    if (bConvert)
    {
        comphelper::string::replaceAt(aSynExtURIRef, 0, p - pBegin,
                pPrefix->m_aTranslatedPrefix);
    }
    rTheExtURIRef = decode(aSynExtURIRef, eDecodeMechanism, eCharset);
    return bConvert;
}
 
// static
bool INetURLObject::convertExtToInt(std::u16string_view rTheExtURIRef,
                                    OUString & rTheIntURIRef,
                                    DecodeMechanism eDecodeMechanism,
                                    rtl_TextEncoding eCharset)
{
    OUStringBuffer aSynIntURIRef(256);
    encodeText(aSynIntURIRef, rTheExtURIRef, PART_VISIBLE,
               EncodeMechanism::NotCanonical, eCharset, true);
    sal_Unicode const * pBegin = aSynIntURIRef.getStr();
    sal_Unicode const * pEnd = pBegin + aSynIntURIRef.getLength();
    sal_Unicode const * p = pBegin;
    PrefixInfo const * pPrefix = getPrefix(p, pEnd);
    bool bConvert = pPrefix && pPrefix->m_eKind == PrefixInfo::Kind::External;
    if (bConvert)
    {
        comphelper::string::replaceAt(aSynIntURIRef, 0, p - pBegin,
            pPrefix->m_aTranslatedPrefix);
    }
    rTheIntURIRef = decode(aSynIntURIRef, eDecodeMechanism, eCharset);
    return bConvert;
}
 
// static
INetURLObject::PrefixInfo const * INetURLObject::getPrefix(sal_Unicode const *& rBegin,
                                                           sal_Unicode const * pEnd)
{
    static PrefixInfo constexpr aMap[]
          { // dummy entry at front needed, because pLast may point here:
            { u""_ustr, u""_ustr, INetProtocol::NotValid, PrefixInfo::Kind::Internal },
            { u".component:"_ustr, u"staroffice.component:"_ustr, INetProtocol::Component,
              PrefixInfo::Kind::Internal },
            { u".uno:"_ustr, u"staroffice.uno:"_ustr, INetProtocol::Uno,
              PrefixInfo::Kind::Internal },
            { u"cid:"_ustr, u""_ustr, INetProtocol::Cid, PrefixInfo::Kind::Official },
            { u"data:"_ustr, u""_ustr, INetProtocol::Data, PrefixInfo::Kind::Official },
            { u"db:"_ustr, u"staroffice.db:"_ustr, INetProtocol::Db, PrefixInfo::Kind::Internal },
            { u"file:"_ustr, u""_ustr, INetProtocol::File, PrefixInfo::Kind::Official },
            { u"ftp:"_ustr, u""_ustr, INetProtocol::Ftp, PrefixInfo::Kind::Official },
            { u"hid:"_ustr, u"staroffice.hid:"_ustr, INetProtocol::Hid,
              PrefixInfo::Kind::Internal },
            { u"http:"_ustr, u""_ustr, INetProtocol::Http, PrefixInfo::Kind::Official },
            { u"https:"_ustr, u""_ustr, INetProtocol::Https, PrefixInfo::Kind::Official },
            { u"javascript:"_ustr, u""_ustr, INetProtocol::Javascript, PrefixInfo::Kind::Official },
            { u"ldap:"_ustr, u""_ustr, INetProtocol::Ldap, PrefixInfo::Kind::Official },
            { u"macro:"_ustr, u"staroffice.macro:"_ustr, INetProtocol::Macro,
              PrefixInfo::Kind::Internal },
            { u"mailto:"_ustr, u""_ustr, INetProtocol::Mailto, PrefixInfo::Kind::Official },
            { u"private:"_ustr, u"staroffice.private:"_ustr, INetProtocol::PrivSoffice,
              PrefixInfo::Kind::Internal },
            { u"private:factory/"_ustr, u"staroffice.factory:"_ustr,
              INetProtocol::PrivSoffice, PrefixInfo::Kind::Internal },
            { u"private:helpid/"_ustr, u"staroffice.helpid:"_ustr, INetProtocol::PrivSoffice,
              PrefixInfo::Kind::Internal },
            { u"private:java/"_ustr, u"staroffice.java:"_ustr, INetProtocol::PrivSoffice,
              PrefixInfo::Kind::Internal },
            { u"private:searchfolder:"_ustr, u"staroffice.searchfolder:"_ustr,
              INetProtocol::PrivSoffice, PrefixInfo::Kind::Internal },
            { u"private:trashcan:"_ustr, u"staroffice.trashcan:"_ustr,
              INetProtocol::PrivSoffice, PrefixInfo::Kind::Internal },
            { u"sftp:"_ustr, u""_ustr, INetProtocol::Sftp, PrefixInfo::Kind::Official },
            { u"slot:"_ustr, u"staroffice.slot:"_ustr, INetProtocol::Slot,
              PrefixInfo::Kind::Internal },
            { u"smb:"_ustr, u""_ustr, INetProtocol::Smb, PrefixInfo::Kind::Official },
            { u"staroffice.component:"_ustr, u".component:"_ustr, INetProtocol::Component,
              PrefixInfo::Kind::External },
            { u"staroffice.db:"_ustr, u"db:"_ustr, INetProtocol::Db, PrefixInfo::Kind::External },
            { u"staroffice.factory:"_ustr, u"private:factory/"_ustr,
              INetProtocol::PrivSoffice, PrefixInfo::Kind::External },
            { u"staroffice.helpid:"_ustr, u"private:helpid/"_ustr, INetProtocol::PrivSoffice,
              PrefixInfo::Kind::External },
            { u"staroffice.hid:"_ustr, u"hid:"_ustr, INetProtocol::Hid,
              PrefixInfo::Kind::External },
            { u"staroffice.java:"_ustr, u"private:java/"_ustr, INetProtocol::PrivSoffice,
              PrefixInfo::Kind::External },
            { u"staroffice.macro:"_ustr, u"macro:"_ustr, INetProtocol::Macro,
              PrefixInfo::Kind::External },
            { u"staroffice.private:"_ustr, u"private:"_ustr, INetProtocol::PrivSoffice,
              PrefixInfo::Kind::External },
            { u"staroffice.searchfolder:"_ustr, u"private:searchfolder:"_ustr,
              INetProtocol::PrivSoffice, PrefixInfo::Kind::External },
            { u"staroffice.slot:"_ustr, u"slot:"_ustr, INetProtocol::Slot,
              PrefixInfo::Kind::External },
            { u"staroffice.trashcan:"_ustr, u"private:trashcan:"_ustr,
              INetProtocol::PrivSoffice, PrefixInfo::Kind::External },
            { u"staroffice.uno:"_ustr, u".uno:"_ustr, INetProtocol::Uno,
              PrefixInfo::Kind::External },
            { u"staroffice:"_ustr, u"private:"_ustr, INetProtocol::PrivSoffice,
              PrefixInfo::Kind::External },
            { u"telnet:"_ustr, u""_ustr, INetProtocol::Telnet, PrefixInfo::Kind::Official },
            { u"vnd.libreoffice.cmis:"_ustr, u""_ustr, INetProtocol::Cmis, PrefixInfo::Kind::Internal },
            { u"vnd.sun.star.cmd:"_ustr, u""_ustr, INetProtocol::VndSunStarCmd,
              PrefixInfo::Kind::Official },
            { u"vnd.sun.star.expand:"_ustr, u""_ustr, INetProtocol::VndSunStarExpand,
              PrefixInfo::Kind::Official },
            { u"vnd.sun.star.help:"_ustr, u""_ustr, INetProtocol::VndSunStarHelp,
              PrefixInfo::Kind::Official },
            { u"vnd.sun.star.hier:"_ustr, u""_ustr, INetProtocol::VndSunStarHier,
              PrefixInfo::Kind::Official },
            { u"vnd.sun.star.pkg:"_ustr, u""_ustr, INetProtocol::VndSunStarPkg,
              PrefixInfo::Kind::Official },
            { u"vnd.sun.star.tdoc:"_ustr, u""_ustr, INetProtocol::VndSunStarTdoc,
              PrefixInfo::Kind::Official },
            { u"vnd.sun.star.webdav:"_ustr, u""_ustr, INetProtocol::VndSunStarWebdav,
              PrefixInfo::Kind::Official }
        };
/* This list needs to be sorted, or you'll introduce serious bugs */
 
    PrefixInfo const * pFirst = aMap + 1;
    PrefixInfo const * pLast = aMap + sizeof aMap / sizeof (PrefixInfo) - 1;
    PrefixInfo const * pMatch = nullptr;
    sal_Unicode const * pMatched = rBegin;
    sal_Unicode const * p = rBegin;
    sal_Int32 i = 0;
    for (; pFirst < pLast; ++i)
    {
        assert(!pFirst->m_aPrefix.isEmpty() && "we start search after the dummy entry");
        if (i == pFirst->m_aPrefix.getLength())
        {
            pMatch = pFirst++;
            pMatched = p;
        }
        if (p >= pEnd)
            break;
        sal_uInt32 nChar = rtl::toAsciiLowerCase(*p++);
        while (pFirst <= pLast && static_cast<unsigned char>(pFirst->m_aPrefix[i]) < nChar)
            ++pFirst;
        while (pFirst <= pLast && static_cast<unsigned char>(pLast->m_aPrefix[i]) > nChar)
            --pLast;
    }
    if (pFirst == pLast)
    {
        sal_Unicode const * q = pFirst->m_aPrefix.getStr() + i;
        while (p < pEnd && *q != '\0'
               && rtl::toAsciiLowerCase(*p) == static_cast<unsigned char>(*q))
        {
            ++p;
            ++q;
        }
        if (*q == '\0')
        {
            rBegin = p;
            return pFirst;
        }
    }
    rBegin = pMatched;
    return pMatch;
}
 
sal_Int32 INetURLObject::getAuthorityBegin() const
{
    DBG_ASSERT(getSchemeInfo().m_bAuthority,
               "INetURLObject::getAuthority(): Bad scheme");
    sal_Int32 nBegin;
    if (m_aUser.isPresent())
        nBegin = m_aUser.getBegin();
    else if (m_aHost.isPresent())
        nBegin = m_aHost.getBegin();
    else
        nBegin = m_aPath.getBegin();
    nBegin -= RTL_CONSTASCII_LENGTH("//");
    DBG_ASSERT(m_aAbsURIRef[nBegin] == '/' && m_aAbsURIRef[nBegin + 1] == '/',
               "INetURLObject::getAuthority(): Bad authority");
    return nBegin;
}
 
INetURLObject::SubString INetURLObject::getAuthority() const
{
    sal_Int32 nBegin = getAuthorityBegin();
    sal_Int32 nEnd = m_aPort.isPresent() ? m_aPort.getEnd() :
                      m_aHost.isPresent() ? m_aHost.getEnd() :
                      m_aAuth.isPresent() ? m_aAuth.getEnd() :
                      m_aUser.isPresent() ? m_aUser.getEnd() :
                          nBegin + RTL_CONSTASCII_LENGTH("//");
    return SubString(nBegin, nEnd - nBegin);
}
 
bool INetURLObject::setUser(std::u16string_view rTheUser,
                            rtl_TextEncoding eCharset)
{
    if (
         !getSchemeInfo().m_bUser
       )
    {
        return false;
    }
 
    OUStringBuffer aNewUser;
    encodeText(aNewUser, rTheUser, PART_USER_PASSWORD,
              EncodeMechanism::WasEncoded, eCharset, false);
    sal_Int32 nDelta;
    if (m_aUser.isPresent())
        nDelta = m_aUser.set(m_aAbsURIRef, aNewUser);
    else if (m_aHost.isPresent())
    {
        m_aAbsURIRef.insert(m_aHost.getBegin(), u'@');
        nDelta = m_aUser.set(m_aAbsURIRef, aNewUser, m_aHost.getBegin()) + 1;
    }
    else if (getSchemeInfo().m_bHost)
        return false;
    else
        nDelta = m_aUser.set(m_aAbsURIRef, aNewUser, m_aPath.getBegin());
    m_aAuth += nDelta;
    m_aHost += nDelta;
    m_aPort += nDelta;
    m_aPath += nDelta;
    m_aQuery += nDelta;
    m_aFragment += nDelta;
    return true;
}
 
namespace
{
    void lcl_Erase(OUStringBuffer &rBuf, sal_Int32 index, sal_Int32 count)
    {
        OUString sTemp(rBuf.makeStringAndClear());
        rBuf.append(sTemp.replaceAt(index, count, u""));
    }
}
 
bool INetURLObject::clearPassword()
{
    if (!getSchemeInfo().m_bPassword)
        return false;
    if (m_aAuth.isPresent())
    {
        lcl_Erase(m_aAbsURIRef, m_aAuth.getBegin() - 1,
            m_aAuth.getLength() + 1);
        sal_Int32 nDelta = m_aAuth.clear() - 1;
        m_aHost += nDelta;
        m_aPort += nDelta;
        m_aPath += nDelta;
        m_aQuery += nDelta;
        m_aFragment += nDelta;
    }
    return true;
}
 
bool INetURLObject::setPassword(std::u16string_view rThePassword,
                                rtl_TextEncoding eCharset)
{
    if (!getSchemeInfo().m_bPassword)
        return false;
    OUStringBuffer aNewAuth;
    encodeText(aNewAuth, rThePassword, PART_USER_PASSWORD,
                  EncodeMechanism::WasEncoded, eCharset, false);
    sal_Int32 nDelta;
    if (m_aAuth.isPresent())
        nDelta = m_aAuth.set(m_aAbsURIRef, aNewAuth);
    else if (m_aUser.isPresent())
    {
        m_aAbsURIRef.insert(m_aUser.getEnd(), u':');
        nDelta
            = m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aUser.getEnd() + 1) + 1;
    }
    else if (m_aHost.isPresent())
    {
        m_aAbsURIRef.insert(m_aHost.getBegin(), ":@" );
        m_aUser.set(m_aAbsURIRef, std::u16string_view{}, m_aHost.getBegin());
        nDelta
            = m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aHost.getBegin() + 1) + 2;
    }
    else if (getSchemeInfo().m_bHost)
        return false;
    else
    {
        m_aAbsURIRef.insert(m_aPath.getBegin(), u':');
        m_aUser.set(m_aAbsURIRef, std::u16string_view{}, m_aPath.getBegin());
        nDelta
            = m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aPath.getBegin() + 1) + 1;
    }
    m_aHost += nDelta;
    m_aPort += nDelta;
    m_aPath += nDelta;
    m_aQuery += nDelta;
    m_aFragment += nDelta;
    return true;
}
 
// static
bool INetURLObject::parseHost(sal_Unicode const *& rBegin, sal_Unicode const * pEnd,
    OUStringBuffer* pCanonic)
{
    // RFC 2373 is inconsistent about how to write an IPv6 address in which an
    // IPv4 address directly follows the abbreviating "::".  The ABNF in
    // Appendix B suggests ":::13.1.68.3", while an example in 2.2/3 explicitly
    // mentions "::13:1.68.3".  This algorithm accepts both variants:
    enum State { STATE_INITIAL, STATE_LABEL, STATE_LABEL_HYPHEN,
                 STATE_LABEL_DOT, STATE_TOPLABEL, STATE_TOPLABEL_HYPHEN,
                 STATE_TOPLABEL_DOT, STATE_IP4, STATE_IP4_DOT, STATE_IP6,
                 STATE_IP6_COLON, STATE_IP6_2COLON, STATE_IP6_3COLON,
                 STATE_IP6_HEXSEQ1, STATE_IP6_HEXSEQ1_COLON,
                 STATE_IP6_HEXSEQ1_MAYBE_IP4, STATE_IP6_HEXSEQ2,
                 STATE_IP6_HEXSEQ2_COLON, STATE_IP6_HEXSEQ2_MAYBE_IP4,
                 STATE_IP6_IP4, STATE_IP6_IP4_DOT, STATE_IP6_DONE };
    sal_uInt32 nNumber = 0;
    int nDigits = 0;
    int nOctets = 0;
    State eState = STATE_INITIAL;
    sal_Unicode const * p = rBegin;
    sal_Int32 nOriginalCanonicLength = pCanonic ? pCanonic->getLength() : 0;
    for (; p != pEnd; ++p)
        switch (eState)
        {
            case STATE_INITIAL:
                if (*p == '[')
                {
                    if (pCanonic)
                        pCanonic->append('[');
                    eState = STATE_IP6;
                }
                else if (rtl::isAsciiAlpha(*p) || *p == '_')
                    eState = STATE_TOPLABEL;
                else if (rtl::isAsciiDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    nOctets = 1;
                    eState = STATE_IP4;
                }
                else
                    goto done;
                break;
 
            case STATE_LABEL:
                if (*p == '.')
                    eState = STATE_LABEL_DOT;
                else if (*p == '-')
                    eState = STATE_LABEL_HYPHEN;
                else if (!rtl::isAsciiAlphanumeric(*p) && *p != '_')
                    goto done;
                break;
 
            case STATE_LABEL_HYPHEN:
                if (rtl::isAsciiAlphanumeric(*p) || *p == '_')
                    eState = STATE_LABEL;
                else if (*p != '-')
                    goto done;
                break;
 
            case STATE_LABEL_DOT:
                if (rtl::isAsciiAlpha(*p) || *p == '_')
                    eState = STATE_TOPLABEL;
                else if (rtl::isAsciiDigit(*p))
                    eState = STATE_LABEL;
                else
                    goto done;
                break;
 
            case STATE_TOPLABEL:
                if (*p == '.')
                    eState = STATE_TOPLABEL_DOT;
                else if (*p == '-')
                    eState = STATE_TOPLABEL_HYPHEN;
                else if (!rtl::isAsciiAlphanumeric(*p) && *p != '_')
                    goto done;
                break;
 
            case STATE_TOPLABEL_HYPHEN:
                if (rtl::isAsciiAlphanumeric(*p) || *p == '_')
                    eState = STATE_TOPLABEL;
                else if (*p != '-')
                    goto done;
                break;
 
            case STATE_TOPLABEL_DOT:
                if (rtl::isAsciiAlpha(*p) || *p == '_')
                    eState = STATE_TOPLABEL;
                else if (rtl::isAsciiDigit(*p))
                    eState = STATE_LABEL;
                else
                    goto done;
                break;
 
            case STATE_IP4:
                if (*p == '.')
                    if (nOctets < 4)
                    {
                        if (pCanonic)
                        {
                            pCanonic->append(static_cast<sal_Int64>(nNumber));
                            pCanonic->append( '.' );
                        }
                        ++nOctets;
                        eState = STATE_IP4_DOT;
                    }
                    else
                        eState = STATE_LABEL_DOT;
                else if (*p == '-')
                    eState = STATE_LABEL_HYPHEN;
                else if (rtl::isAsciiAlpha(*p) || *p == '_')
                    eState = STATE_LABEL;
                else if (rtl::isAsciiDigit(*p))
                    if (nDigits < 3)
                    {
                        nNumber = 10 * nNumber + INetMIME::getWeight(*p);
                        ++nDigits;
                    }
                    else
                        eState = STATE_LABEL;
                else
                    goto done;
                break;
 
            case STATE_IP4_DOT:
                if (rtl::isAsciiAlpha(*p) || *p == '_')
                    eState = STATE_TOPLABEL;
                else if (rtl::isAsciiDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP4;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6:
                if (*p == ':')
                    eState = STATE_IP6_COLON;
                else if (rtl::isAsciiHexDigit(*p))
                {
                    nNumber = INetMIME::getHexWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ1;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_COLON:
                if (*p == ':')
                {
                    if (pCanonic)
                        pCanonic->append("::");
                    eState = STATE_IP6_2COLON;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_2COLON:
                if (*p == ']')
                    eState = STATE_IP6_DONE;
                else if (*p == ':')
                {
                    if (pCanonic)
                        pCanonic->append(':');
                    eState = STATE_IP6_3COLON;
                }
                else if (rtl::isAsciiDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ2_MAYBE_IP4;
                }
                else if (rtl::isAsciiHexDigit(*p))
                {
                    nNumber = INetMIME::getHexWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ2;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_3COLON:
                if (rtl::isAsciiDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    nOctets = 1;
                    eState = STATE_IP6_IP4;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_HEXSEQ1:
                if (*p == ']')
                {
                    if (pCanonic)
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                    eState = STATE_IP6_DONE;
                }
                else if (*p == ':')
                {
                    if (pCanonic)
                    {
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                        pCanonic->append(':');
                    }
                    eState = STATE_IP6_HEXSEQ1_COLON;
                }
                else if (rtl::isAsciiHexDigit(*p) && nDigits < 4)
                {
                    assert(nNumber < 0xffff);
                    nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
                    ++nDigits;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_HEXSEQ1_COLON:
                if (*p == ':')
                {
                    if (pCanonic)
                        pCanonic->append(':');
                    eState = STATE_IP6_2COLON;
                }
                else if (rtl::isAsciiDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ1_MAYBE_IP4;
                }
                else if (rtl::isAsciiHexDigit(*p))
                {
                    nNumber = INetMIME::getHexWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ1;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_HEXSEQ1_MAYBE_IP4:
                if (*p == ']')
                {
                    if (pCanonic)
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                    eState = STATE_IP6_DONE;
                }
                else if (*p == ':')
                {
                    if (pCanonic)
                    {
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                        pCanonic->append(':');
                    }
                    eState = STATE_IP6_HEXSEQ1_COLON;
                }
                else if (*p == '.')
                {
                    nNumber = 100 * (nNumber >> 8) + 10 * (nNumber >> 4 & 15)
                                  + (nNumber & 15);
                    if (pCanonic)
                    {
                        pCanonic->append(
                            OUString::number(nNumber));
                        pCanonic->append('.');
                    }
                    nOctets = 2;
                    eState = STATE_IP6_IP4_DOT;
                }
                else if (rtl::isAsciiDigit(*p) && nDigits < 3)
                {
                    assert(nNumber < 0xfff);
                    nNumber = 16 * nNumber + INetMIME::getWeight(*p);
                    ++nDigits;
                }
                else if (rtl::isAsciiHexDigit(*p) && nDigits < 4)
                {
                    nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
                    ++nDigits;
                    eState = STATE_IP6_HEXSEQ1;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_HEXSEQ2:
                if (*p == ']')
                {
                    if (pCanonic)
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                    eState = STATE_IP6_DONE;
                }
                else if (*p == ':')
                {
                    if (pCanonic)
                    {
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                        pCanonic->append(':');
                    }
                    eState = STATE_IP6_HEXSEQ2_COLON;
                }
                else if (rtl::isAsciiHexDigit(*p) && nDigits < 4)
                {
                    assert(nNumber < 0xffff);
                    nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
                    ++nDigits;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_HEXSEQ2_COLON:
                if (rtl::isAsciiDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ2_MAYBE_IP4;
                }
                else if (rtl::isAsciiHexDigit(*p))
                {
                    nNumber = INetMIME::getHexWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_HEXSEQ2;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_HEXSEQ2_MAYBE_IP4:
                if (*p == ']')
                {
                    if (pCanonic)
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                    eState = STATE_IP6_DONE;
                }
                else if (*p == ':')
                {
                    if (pCanonic)
                    {
                        pCanonic->append(
                            OUString::number(nNumber, 16));
                        pCanonic->append(':');
                    }
                    eState = STATE_IP6_HEXSEQ2_COLON;
                }
                else if (*p == '.')
                {
                    nNumber = 100 * (nNumber >> 8) + 10 * (nNumber >> 4 & 15)
                                  + (nNumber & 15);
                    if (pCanonic)
                    {
                        pCanonic->append(
                            OUString::number(nNumber));
                        pCanonic->append('.');
                    }
                    nOctets = 2;
                    eState = STATE_IP6_IP4_DOT;
                }
                else if (rtl::isAsciiDigit(*p) && nDigits < 3)
                {
                    assert(nNumber < 0xfff);
                    nNumber = 16 * nNumber + INetMIME::getWeight(*p);
                    ++nDigits;
                }
                else if (rtl::isAsciiHexDigit(*p) && nDigits < 4)
                {
                    nNumber = 16 * nNumber + INetMIME::getHexWeight(*p);
                    ++nDigits;
                    eState = STATE_IP6_HEXSEQ2;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_IP4:
                if (*p == ']')
                    if (nOctets == 4)
                    {
                        if (pCanonic)
                            pCanonic->append(
                                OUString::number(nNumber));
                        eState = STATE_IP6_DONE;
                    }
                    else
                        goto done;
                else if (*p == '.')
                    if (nOctets < 4)
                    {
                        if (pCanonic)
                        {
                            pCanonic->append(
                                OUString::number(nNumber));
                            pCanonic->append('.');
                        }
                        ++nOctets;
                        eState = STATE_IP6_IP4_DOT;
                    }
                    else
                        goto done;
                else if (rtl::isAsciiDigit(*p) && nDigits < 3)
                {
                    assert(nNumber < 999);
                    nNumber = 10 * nNumber + INetMIME::getWeight(*p);
                    ++nDigits;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_IP4_DOT:
                if (rtl::isAsciiDigit(*p))
                {
                    nNumber = INetMIME::getWeight(*p);
                    nDigits = 1;
                    eState = STATE_IP6_IP4;
                }
                else
                    goto done;
                break;
 
            case STATE_IP6_DONE:
                goto done;
        }
 done:
    switch (eState)
    {
        case STATE_LABEL:
        case STATE_TOPLABEL:
        case STATE_TOPLABEL_DOT:
            if (pCanonic)
            {
                pCanonic->setLength(nOriginalCanonicLength);
                pCanonic->append(rBegin, p - rBegin);
            }
            rBegin = p;
            return true;
 
        case STATE_IP4:
            if (nOctets == 4)
            {
                if (pCanonic)
                    pCanonic->append(
                        OUString::number(nNumber));
                rBegin = p;
                return true;
            }
            if (pCanonic)
                pCanonic->setLength(nOriginalCanonicLength);
            return false;
 
        case STATE_IP6_DONE:
            if (pCanonic)
                pCanonic->append(']');
            rBegin = p;
            return true;
 
        default:
            if (pCanonic)
                pCanonic->setLength(nOriginalCanonicLength);
            return false;
    }
}
 
// static
bool INetURLObject::parseHostOrNetBiosName(
    sal_Unicode const * pBegin, sal_Unicode const * pEnd,
    EncodeMechanism eMechanism, rtl_TextEncoding eCharset, bool bNetBiosName,
    OUStringBuffer* pCanonic)
{
    if (pBegin >= pEnd)
        return true;
    sal_Int32 nOriginalCanonicLength = pCanonic ? pCanonic->getLength() : 0;
    if (sal_Unicode const* p = pBegin; parseHost(p, pEnd, pCanonic) && p == pEnd)
        return true;
    if (pCanonic)
        pCanonic->setLength(nOriginalCanonicLength); // discard parseHost results
    if (!bNetBiosName)
        return false;
    while (pBegin < pEnd)
    {
        EscapeType eEscapeType;
        switch (sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd, eMechanism, eCharset, eEscapeType))
        {
            default:
                if (INetMIME::isVisible(nUTF32))
                {
                    if (pCanonic)
                        appendUCS4(*pCanonic, nUTF32, eEscapeType, PART_URIC, eCharset, true);
                    break;
                }
                [[fallthrough]];
            case '"':
            case '*':
            case '+':
            case ',':
            case '/':
            case ':':
            case ';':
            case '<':
            case '=':
            case '>':
            case '?':
            case '[':
            case '\\':
            case ']':
            case '`':
            case '|':
                if (pCanonic)
                    pCanonic->setLength(nOriginalCanonicLength);
                return false;
        }
    }
    return true;
}
 
bool INetURLObject::setHost(std::u16string_view rTheHost,
                            rtl_TextEncoding eCharset)
{
    if (!getSchemeInfo().m_bHost)
        return false;
    OUStringBuffer aSynHost(rTheHost);
    bool bNetBiosName = false;
    switch (m_eScheme)
    {
        case INetProtocol::File:
            {
                if (OUString::unacquired(aSynHost).equalsIgnoreAsciiCase("localhost"))
                {
                    aSynHost.setLength(0);
                }
                bNetBiosName = true;
            }
            break;
        case INetProtocol::Ldap:
            if (aSynHost.isEmpty() && m_aPort.isPresent())
                return false;
            break;
 
        default:
            if (aSynHost.isEmpty())
                return false;
            break;
    }
    if (!parseHostOrNetBiosName(
            aSynHost.getStr(), aSynHost.getStr() + aSynHost.getLength(),
            EncodeMechanism::WasEncoded, eCharset, bNetBiosName, &aSynHost))
        return false;
    sal_Int32 nDelta = m_aHost.set(m_aAbsURIRef, aSynHost);
    m_aPort += nDelta;
    m_aPath += nDelta;
    m_aQuery += nDelta;
    m_aFragment += nDelta;
    return true;
}
 
// static
bool INetURLObject::parsePath(INetProtocol eScheme,
                              sal_Unicode const ** pBegin,
                              sal_Unicode const * pEnd,
                              EncodeMechanism eMechanism,
                              rtl_TextEncoding eCharset,
                              bool bSkippedInitialSlash,
                              sal_uInt32 nSegmentDelimiter,
                              sal_uInt32 nAltSegmentDelimiter,
                              sal_uInt32 nQueryDelimiter,
                              sal_uInt32 nFragmentDelimiter,
                              OUStringBuffer &rSynPath)
{
    DBG_ASSERT(pBegin, "INetURLObject::parsePath(): Null output param");
 
    sal_Unicode const * pPos = *pBegin;
    const sal_Int32 nSynPathBeforeLen = rSynPath.getLength();
    switch (eScheme)
    {
        case INetProtocol::NotValid:
            return false;
 
        case INetProtocol::Ftp:
            if (pPos < pEnd && *pPos != '/' && *pPos != nFragmentDelimiter)
                goto failed;
            while (pPos < pEnd && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                appendUCS4(rSynPath, nUTF32, eEscapeType,
                           PART_HTTP_PATH, eCharset, true);
            }
            if (rSynPath.getLength() - nSynPathBeforeLen == 0)
                rSynPath.append('/');
            break;
 
        case INetProtocol::Http:
        case INetProtocol::VndSunStarWebdav:
        case INetProtocol::Https:
        case INetProtocol::Smb:
        case INetProtocol::Cmis:
            if (pPos < pEnd && *pPos != '/' && *pPos != nQueryDelimiter
                && *pPos != nFragmentDelimiter)
                goto failed;
            while (pPos < pEnd && *pPos != nQueryDelimiter
                   && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                appendUCS4(rSynPath, nUTF32, eEscapeType,
                           PART_HTTP_PATH, eCharset, true);
            }
            if (rSynPath.getLength() - nSynPathBeforeLen == 0)
                rSynPath.append('/');
            break;
 
        case INetProtocol::File:
        {
            if (bSkippedInitialSlash)
                rSynPath.append('/');
            else if (pPos < pEnd
                     && *pPos != nSegmentDelimiter
                     && *pPos != nAltSegmentDelimiter)
                goto failed;
            while (pPos < pEnd && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                if (eEscapeType == EscapeType::NONE)
                {
                    if (nUTF32 == nSegmentDelimiter
                        || nUTF32 == nAltSegmentDelimiter)
                    {
                        rSynPath.append('/');
                        continue;
                    }
                    else if (nUTF32 == '|'
                             && (pPos == pEnd
                                 || *pPos == nFragmentDelimiter
                                 || *pPos == nSegmentDelimiter
                                 || *pPos == nAltSegmentDelimiter)
                             && rSynPath.getLength() - nSynPathBeforeLen == 2
                             && rtl::isAsciiAlpha(rSynPath[nSynPathBeforeLen + 1]))
                    {
                        // A first segment of <ALPHA "|"> is translated to
                        // <ALPHA ":">:
                        rSynPath.append(':');
                        continue;
                    }
                }
                appendUCS4(rSynPath, nUTF32, eEscapeType,
                           PART_PCHAR, eCharset, true);
            }
            if (rSynPath.getLength() - nSynPathBeforeLen == 0)
                rSynPath.append('/');
            break;
        }
 
        case INetProtocol::Mailto:
            while (pPos < pEnd && *pPos != nQueryDelimiter
                   && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                appendUCS4(rSynPath, nUTF32, eEscapeType,
                           PART_MAILTO, eCharset, true);
            }
            break;
 
 
        case INetProtocol::PrivSoffice:
        case INetProtocol::Slot:
        case INetProtocol::Hid:
        case INetProtocol::Macro:
        case INetProtocol::Uno:
        case INetProtocol::Component:
        case INetProtocol::Ldap:
            while (pPos < pEnd && *pPos != nQueryDelimiter
                   && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                appendUCS4(rSynPath, nUTF32, eEscapeType,
                           PART_PATH_BEFORE_QUERY, eCharset, true);
            }
            break;
 
        case INetProtocol::VndSunStarHelp:
            if (pPos == pEnd
                || *pPos == nQueryDelimiter
                || *pPos == nFragmentDelimiter)
                rSynPath.append('/');
            else
            {
                if (*pPos != '/')
                    goto failed;
                while (pPos < pEnd && *pPos != nQueryDelimiter
                       && *pPos != nFragmentDelimiter)
                {
                    EscapeType eEscapeType;
                    sal_uInt32 nUTF32 = getUTF32(pPos, pEnd,
                                                 eMechanism,
                                                 eCharset, eEscapeType);
                    appendUCS4(rSynPath, nUTF32, eEscapeType,
                               PART_HTTP_PATH, eCharset, true);
                }
            }
            break;
 
        case INetProtocol::Javascript:
        case INetProtocol::Data:
        case INetProtocol::Cid:
        case INetProtocol::Db:
            while (pPos < pEnd && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                appendUCS4(rSynPath, nUTF32, eEscapeType,
                           PART_URIC, eCharset, true);
            }
            break;
 
        case INetProtocol::VndSunStarHier:
        case INetProtocol::VndSunStarPkg:
            if (pPos < pEnd && *pPos != '/'
                && *pPos != nQueryDelimiter && *pPos != nFragmentDelimiter)
                goto failed;
            while (pPos < pEnd && *pPos != nQueryDelimiter
                   && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                if (eEscapeType == EscapeType::NONE && nUTF32 == '/')
                    rSynPath.append('/');
                else
                    appendUCS4(rSynPath, nUTF32, eEscapeType,
                               PART_PCHAR, eCharset, false);
            }
            if (rSynPath.getLength() - nSynPathBeforeLen == 0)
                rSynPath.append('/');
            break;
 
        case INetProtocol::VndSunStarCmd:
        case INetProtocol::VndSunStarExpand:
        {
            if (pPos == pEnd || *pPos == nFragmentDelimiter)
                goto failed;
            Part ePart = PART_URIC_NO_SLASH;
            while (pPos != pEnd && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                appendUCS4(rSynPath, nUTF32, eEscapeType, ePart,
                           eCharset, true);
                ePart = PART_URIC;
            }
            break;
        }
 
        case INetProtocol::Telnet:
            if (pPos < pEnd)
            {
                if (*pPos != '/' || pEnd - pPos > 1)
                    goto failed;
                ++pPos;
            }
            rSynPath.append('/');
            break;
 
        case INetProtocol::VndSunStarTdoc:
            if (pPos == pEnd || *pPos != '/')
                goto failed;
            while (pPos < pEnd && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                if (eEscapeType == EscapeType::NONE && nUTF32 == '/')
                    rSynPath.append('/');
                else
                    appendUCS4(rSynPath, nUTF32, eEscapeType,
                               PART_PCHAR, eCharset, false);
            }
            break;
 
        case INetProtocol::Generic:
        case INetProtocol::Sftp:
            while (pPos < pEnd && *pPos != nFragmentDelimiter)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, eMechanism,
                                             eCharset, eEscapeType);
                appendUCS4(rSynPath, nUTF32, eEscapeType,
                           PART_URIC, eCharset, true);
            }
            if (rSynPath.isEmpty())
                goto failed;
            break;
        default:
            OSL_ASSERT(false);
            break;
    }
 
    *pBegin = pPos;
    return true;
failed:
    rSynPath.setLength(nSynPathBeforeLen);
    return false;
}
 
bool INetURLObject::setPath(std::u16string_view rThePath,
                            EncodeMechanism eMechanism,
                            rtl_TextEncoding eCharset)
{
    OUStringBuffer aSynPath(256);
    sal_Unicode const * p = rThePath.data();
    sal_Unicode const * pEnd = p + rThePath.size();
    if (!parsePath(m_eScheme, &p, pEnd, eMechanism, eCharset, false,
                   '/', 0x80000000, 0x80000000, 0x80000000, aSynPath)
        || p != pEnd)
        return false;
    sal_Int32 nDelta = m_aPath.set(m_aAbsURIRef, aSynPath);
    m_aQuery += nDelta;
    m_aFragment += nDelta;
    return true;
}
 
bool INetURLObject::checkHierarchical() const {
    if (m_eScheme == INetProtocol::VndSunStarExpand) {
        OSL_FAIL(
            "INetURLObject::checkHierarchical vnd.sun.star.expand");
        return true;
    } else {
        return getSchemeInfo().m_bHierarchical;
    }
}
 
bool INetURLObject::Append(std::u16string_view rTheSegment,
                           EncodeMechanism eMechanism,
                           rtl_TextEncoding eCharset)
{
    return insertName(rTheSegment, false, LAST_SEGMENT, eMechanism, eCharset);
}
 
INetURLObject::SubString INetURLObject::getSegment(sal_Int32 nIndex,
                                                   bool bIgnoreFinalSlash)
    const
{
    DBG_ASSERT(nIndex >= 0 || nIndex == LAST_SEGMENT,
               "INetURLObject::getSegment(): Bad index");
 
    if (!checkHierarchical())
        return SubString();
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    sal_Unicode const * pSegBegin;
    sal_Unicode const * pSegEnd;
    if (nIndex == LAST_SEGMENT)
    {
        pSegEnd = pPathEnd;
        if (bIgnoreFinalSlash && pSegEnd > pPathBegin && pSegEnd[-1] == '/')
            --pSegEnd;
        if (pSegEnd <= pPathBegin)
            return SubString();
        pSegBegin = pSegEnd - 1;
        while (pSegBegin > pPathBegin && *pSegBegin != '/')
            --pSegBegin;
    }
    else
    {
        pSegBegin = pPathBegin;
        while (nIndex-- > 0)
            do
            {
                ++pSegBegin;
                if (pSegBegin >= pPathEnd)
                    return SubString();
            }
            while (*pSegBegin != '/');
        pSegEnd = pSegBegin + 1;
        while (pSegEnd < pPathEnd && *pSegEnd != '/')
            ++pSegEnd;
    }
 
    return SubString(pSegBegin - m_aAbsURIRef.getStr(),
                     pSegEnd - pSegBegin);
}
 
bool INetURLObject::insertName(std::u16string_view rTheName,
                               bool bAppendFinalSlash, sal_Int32 nIndex,
                               EncodeMechanism eMechanism,
                               rtl_TextEncoding eCharset)
{
    DBG_ASSERT(nIndex >= 0 || nIndex == LAST_SEGMENT,
               "INetURLObject::insertName(): Bad index");
 
    if (!checkHierarchical())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    sal_Unicode const * pPrefixEnd;
    bool bInsertSlash;
    sal_Unicode const * pSuffixBegin;
    if (nIndex == LAST_SEGMENT)
    {
        pPrefixEnd = pPathEnd;
        if (pPrefixEnd > pPathBegin &&
            pPrefixEnd[-1] == '/')
        {
            --pPrefixEnd;
        }
        bInsertSlash = bAppendFinalSlash;
        pSuffixBegin = pPathEnd;
    }
    else if (nIndex == 0)
    {
        pPrefixEnd = pPathBegin;
        bInsertSlash =
            (pPathBegin < pPathEnd && *pPathBegin != '/') ||
            (pPathBegin == pPathEnd && bAppendFinalSlash);
        pSuffixBegin =
            (pPathEnd - pPathBegin == 1 && *pPathBegin == '/' &&
             !bAppendFinalSlash)
            ? pPathEnd : pPathBegin;
    }
    else
    {
        pPrefixEnd = pPathBegin;
        sal_Unicode const * pEnd = pPathEnd;
        if (pEnd > pPathBegin && pEnd[-1] == '/')
            --pEnd;
        bool bSkip = pPrefixEnd < pEnd && *pPrefixEnd == '/';
        bInsertSlash = false;
        pSuffixBegin = pPathEnd;
        while (nIndex-- > 0)
            for (;;)
            {
                if (bSkip)
                    ++pPrefixEnd;
                bSkip = true;
                if (pPrefixEnd >= pEnd)
                {
                    if (nIndex == 0)
                    {
                        bInsertSlash = bAppendFinalSlash;
                        break;
                    }
                    else
                        return false;
                }
                if (*pPrefixEnd == '/')
                {
                    pSuffixBegin = pPrefixEnd;
                    break;
                }
            }
    }
 
    OUStringBuffer aNewPath(256);
    aNewPath.append(
        OUString::Concat(std::u16string_view(pPathBegin, pPrefixEnd - pPathBegin))
        + "/");
    encodeText(aNewPath, rTheName, PART_PCHAR,
               eMechanism, eCharset, true);
    if (bInsertSlash) {
        aNewPath.append('/');
    }
    aNewPath.append(pSuffixBegin, pPathEnd - pSuffixBegin);
 
    return setPath(aNewPath, EncodeMechanism::NotCanonical,
        RTL_TEXTENCODING_UTF8);
}
 
void INetURLObject::clearQuery()
{
    if (HasError())
        return;
    if (m_aQuery.isPresent())
    {
        lcl_Erase(m_aAbsURIRef, m_aQuery.getBegin() - 1,
            m_aQuery.getLength() + 1);
        m_aFragment += m_aQuery.clear() - 1;
    }
}
 
bool INetURLObject::setQuery(std::u16string_view rTheQuery,
                             EncodeMechanism eMechanism,
                             rtl_TextEncoding eCharset)
{
    if (!getSchemeInfo().m_bQuery)
        return false;
    OUStringBuffer aNewQuery;
    encodeText(aNewQuery, rTheQuery, PART_URIC,
               eMechanism, eCharset, true);
    sal_Int32 nDelta;
    if (m_aQuery.isPresent())
        nDelta = m_aQuery.set(m_aAbsURIRef, aNewQuery);
    else
    {
        m_aAbsURIRef.insert(m_aPath.getEnd(), u'?');
        nDelta = m_aQuery.set(m_aAbsURIRef, aNewQuery, m_aPath.getEnd() + 1)
                     + 1;
    }
    m_aFragment += nDelta;
    return true;
}
 
bool INetURLObject::clearFragment()
{
    if (HasError())
        return false;
    if (m_aFragment.isPresent())
    {
        m_aAbsURIRef.setLength(m_aFragment.getBegin() - 1);
        m_aFragment.clear();
    }
    return true;
}
 
bool INetURLObject::setFragment(std::u16string_view rTheFragment,
                                EncodeMechanism eMechanism,
                                rtl_TextEncoding eCharset)
{
    if (HasError())
        return false;
    OUStringBuffer aNewFragment;
    encodeText(aNewFragment, rTheFragment, PART_URIC,
              eMechanism, eCharset, true);
    if (m_aFragment.isPresent())
        m_aFragment.set(m_aAbsURIRef, aNewFragment);
    else
    {
        m_aAbsURIRef.append('#');
        m_aFragment.set(m_aAbsURIRef, aNewFragment, m_aAbsURIRef.getLength());
    }
    return true;
}
 
bool INetURLObject::hasDosVolume(FSysStyle eStyle) const
{
    sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    return (eStyle & FSysStyle::Dos)
           && m_aPath.getLength() >= 3
           && p[0] == '/'
           && rtl::isAsciiAlpha(p[1])
           && p[2] == ':'
           && (m_aPath.getLength() == 3 || p[3] == '/');
}
 
// static
void INetURLObject::encodeText( OUStringBuffer& rOutputBuffer,
                                sal_Unicode const * pBegin,
                                sal_Unicode const * pEnd,
                                Part ePart, EncodeMechanism eMechanism,
                                rtl_TextEncoding eCharset,
                                bool bKeepVisibleEscapes)
{
    while (pBegin < pEnd)
    {
        EscapeType eEscapeType;
        sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd,
                                     eMechanism, eCharset, eEscapeType);
        appendUCS4(rOutputBuffer, nUTF32, eEscapeType, ePart,
                   eCharset, bKeepVisibleEscapes);
    }
}
 
// static
OUString INetURLObject::decode(sal_Unicode const * pBegin,
                                sal_Unicode const * pEnd,
                                DecodeMechanism eMechanism,
                                rtl_TextEncoding eCharset)
{
    switch (eMechanism)
    {
        case DecodeMechanism::NONE:
            return OUString(pBegin, pEnd - pBegin);
 
        case DecodeMechanism::ToIUri:
            eCharset = RTL_TEXTENCODING_UTF8;
            break;
 
        default:
            break;
    }
    OUStringBuffer aResult(static_cast<int>(pEnd-pBegin));
    while (pBegin < pEnd)
    {
        EscapeType eEscapeType;
        sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd,
                                     EncodeMechanism::WasEncoded, eCharset, eEscapeType);
        switch (eEscapeType)
        {
            case EscapeType::NONE:
                aResult.appendUtf32(nUTF32);
                break;
 
            case EscapeType::Octet:
                appendEscape(aResult, nUTF32);
                break;
 
            case EscapeType::Utf32:
                if (
                     rtl::isAscii(nUTF32) &&
                     (
                       eMechanism == DecodeMechanism::ToIUri ||
                       (
                         eMechanism == DecodeMechanism::Unambiguous &&
                         mustEncode(nUTF32, PART_UNAMBIGUOUS)
                       )
                     )
                   )
                {
                    appendEscape(aResult, nUTF32);
                }
                else
                    aResult.appendUtf32(nUTF32);
                break;
        }
    }
    return aResult.makeStringAndClear();
}
 
OUString INetURLObject::GetURLNoPass(DecodeMechanism eMechanism,
                                      rtl_TextEncoding eCharset) const
{
    INetURLObject aTemp(*this);
    aTemp.clearPassword();
    return aTemp.GetMainURL(eMechanism, eCharset);
}
 
OUString INetURLObject::GetURLNoMark(DecodeMechanism eMechanism,
                                      rtl_TextEncoding eCharset) const
{
    INetURLObject aTemp(*this);
    aTemp.clearFragment();
    return aTemp.GetMainURL(eMechanism, eCharset);
}
 
OUString
INetURLObject::getAbbreviated(
    uno::Reference< util::XStringWidth > const & rStringWidth,
    sal_Int32 nWidth,
    DecodeMechanism eMechanism,
    rtl_TextEncoding eCharset)
    const
{
    OSL_ENSURE(rStringWidth.is(), "specification violation");
    OUStringBuffer aBuffer;
    // make sure that the scheme is copied for generic schemes: getSchemeInfo().m_pScheme
    // is empty ("") in that case, so take the scheme from m_aAbsURIRef
    if (m_eScheme != INetProtocol::Generic)
    {
        aBuffer.append(getSchemeInfo().m_sScheme);
    }
    else
    {
        if (!m_aAbsURIRef.isEmpty())
        {
            sal_Unicode const * pSchemeBegin
                = m_aAbsURIRef.getStr();
            sal_Unicode const * pSchemeEnd = pSchemeBegin;
 
            while (pSchemeEnd[0] != ':')
            {
                ++pSchemeEnd;
            }
            aBuffer.append(pSchemeBegin, pSchemeEnd - pSchemeBegin);
        }
    }
    aBuffer.append(':');
    bool bAuthority = getSchemeInfo().m_bAuthority;
    sal_Unicode const * pCoreBegin
        = m_aAbsURIRef.getStr() + (bAuthority ? getAuthorityBegin() :
                                                   m_aPath.getBegin());
    sal_Unicode const * pCoreEnd
        = m_aAbsURIRef.getStr() + m_aPath.getBegin() + m_aPath.getLength();
    bool bSegment = false;
    if (getSchemeInfo().m_bHierarchical)
    {
        OUString aRest;
        if (m_aQuery.isPresent())
            aRest = "?...";
        else if (m_aFragment.isPresent())
            aRest = "#...";
        OUStringBuffer aTrailer;
        sal_Unicode const * pBegin = pCoreBegin;
        sal_Unicode const * pEnd = pCoreEnd;
        sal_Unicode const * pPrefixBegin = pBegin;
        sal_Unicode const * pSuffixEnd = pEnd;
        bool bPrefix = true;
        bool bSuffix = true;
        do
        {
            if (bSuffix)
            {
                sal_Unicode const * p = pSuffixEnd - 1;
                if (pSuffixEnd == pCoreEnd && *p == '/')
                    --p;
                while (*p != '/')
                    --p;
                if (bAuthority && p == pCoreBegin + 1)
                    --p;
                OUString
                    aSegment(decode(p + (p == pBegin && pBegin != pCoreBegin ?
                                             1 : 0),
                                    pSuffixEnd,
                                    eMechanism,
                                    eCharset));
                pSuffixEnd = p;
                OUStringBuffer aResult(aBuffer);
                if (pSuffixEnd != pBegin)
                    aResult.append("...");
                aResult.append(aSegment + aTrailer + aRest);
                if (rStringWidth->
                            queryStringWidth(aResult.makeStringAndClear())
                        <= nWidth)
                {
                    aTrailer.insert(0, aSegment);
                    bSegment = true;
                    pEnd = pSuffixEnd;
                }
                else
                    bSuffix = false;
                if (pPrefixBegin > pSuffixEnd)
                    pPrefixBegin = pSuffixEnd;
                if (pBegin == pEnd)
                    break;
            }
            if (bPrefix)
            {
                sal_Unicode const * p
                    = pPrefixBegin
                          + (bAuthority && pPrefixBegin == pCoreBegin ? 2 :
                                                                        1);
                OSL_ASSERT(p <= pEnd);
                while (p < pEnd && *p != '/')
                    ++p;
                if (p == pCoreEnd - 1 && *p == '/')
                    ++p;
                OUString
                    aSegment(decode(pPrefixBegin
                                        + (pPrefixBegin == pCoreBegin ? 0 :
                                                                        1),
                                    p == pEnd ? p : p + 1,
                                    eMechanism,
                                    eCharset));
                pPrefixBegin = p;
                OUStringBuffer aResult(aBuffer + aSegment);
                if (pPrefixBegin != pEnd)
                    aResult.append("...");
                aResult.append(aTrailer + aRest);
                if (rStringWidth->
                            queryStringWidth(aResult.makeStringAndClear())
                        <= nWidth)
                {
                    aBuffer.append(aSegment);
                    bSegment = true;
                    pBegin = pPrefixBegin;
                }
                else
                    bPrefix = false;
                if (pPrefixBegin > pSuffixEnd)
                    pSuffixEnd = pPrefixBegin;
                if (pBegin == pEnd)
                    break;
            }
        }
        while (bPrefix || bSuffix);
        if (bSegment)
        {
            if (pPrefixBegin != pBegin || pSuffixEnd != pEnd)
                aBuffer.append("...");
            aBuffer.append(aTrailer);
        }
    }
    if (!bSegment)
        aBuffer.append(decode(pCoreBegin,
                              pCoreEnd,
                              eMechanism,
                              eCharset));
    if (m_aQuery.isPresent())
    {
        aBuffer.append("?" + decode(m_aQuery, eMechanism, eCharset));
    }
    if (m_aFragment.isPresent())
    {
        aBuffer.append("#" + decode(m_aFragment, eMechanism, eCharset));
    }
    if (!aBuffer.isEmpty())
    {
        OUStringBuffer aResult(aBuffer);
        if (rStringWidth->queryStringWidth(aResult.makeStringAndClear())
                > nWidth)
            for (sal_Int32 i = aBuffer.getLength();;)
            {
                if (i == 0)
                {
                    aBuffer.setLength(aBuffer.getLength() - 1);
                    if (aBuffer.isEmpty())
                        break;
                }
                else
                {
                    aBuffer.setLength(--i);
                    aBuffer.append("...");
                }
                aResult = aBuffer;
                if (rStringWidth->
                            queryStringWidth(aResult.makeStringAndClear())
                        <= nWidth)
                    break;
            }
    }
    return aBuffer.makeStringAndClear();
}
 
bool INetURLObject::operator ==(INetURLObject const & rObject) const
{
    if (m_eScheme != rObject.m_eScheme)
        return false;
    if (m_eScheme == INetProtocol::NotValid)
        return std::u16string_view(m_aAbsURIRef) == std::u16string_view(rObject.m_aAbsURIRef);
    if ((m_aScheme.compare(
             rObject.m_aScheme, m_aAbsURIRef, rObject.m_aAbsURIRef)
         != 0)
        || GetUser(DecodeMechanism::NONE) != rObject.GetUser(DecodeMechanism::NONE)
        || GetPass(DecodeMechanism::NONE) != rObject.GetPass(DecodeMechanism::NONE)
        || !GetHost(DecodeMechanism::NONE).equalsIgnoreAsciiCase(
            rObject.GetHost(DecodeMechanism::NONE))
        || GetPort() != rObject.GetPort()
        || HasParam() != rObject.HasParam()
        || GetParam() != rObject.GetParam())
        return false;
    OUString aPath1(GetURLPath(DecodeMechanism::NONE));
    OUString aPath2(rObject.GetURLPath(DecodeMechanism::NONE));
    switch (m_eScheme)
    {
        case INetProtocol::File:
        {
            // If the URL paths of two file URLs only differ in that one has a
            // final '/' and the other has not, take the two paths as
            // equivalent (this could be useful for other schemes, too):
            sal_Int32 nLength = aPath1.getLength();
            switch (nLength - aPath2.getLength())
            {
                case -1:
                    if (aPath2[nLength] != '/')
                        return false;
                    break;
 
                case 0:
                    break;
 
                case 1:
                    if (aPath1[--nLength] != '/')
                        return false;
                    break;
 
                default:
                    return false;
            }
            return aPath1.compareTo(aPath2, nLength) == 0;
        }
 
        default:
            return aPath1 == aPath2;
    }
}
 
bool INetURLObject::ConcatData(INetProtocol eTheScheme,
                               std::u16string_view rTheUser,
                               std::u16string_view rThePassword,
                               std::u16string_view rTheHost,
                               sal_uInt32 nThePort,
                               std::u16string_view rThePath)
{
    setInvalid();
    m_eScheme = eTheScheme;
    if (HasError() || m_eScheme == INetProtocol::Generic)
        return false;
    m_aAbsURIRef.setLength(0);
    m_aAbsURIRef.append(getSchemeInfo().m_sScheme);
    m_aAbsURIRef.append(':');
    if (getSchemeInfo().m_bAuthority)
    {
        m_aAbsURIRef.append("//");
        bool bUserInfo = false;
        if (getSchemeInfo().m_bUser)
        {
            if (!rTheUser.empty())
            {
                OUStringBuffer aNewUser;
                encodeText(aNewUser, rTheUser, PART_USER_PASSWORD,
                           EncodeMechanism::WasEncoded, RTL_TEXTENCODING_UTF8, false);
                m_aUser.set(m_aAbsURIRef, aNewUser, m_aAbsURIRef.getLength());
                bUserInfo = true;
            }
        }
        else if (!rTheUser.empty())
        {
            setInvalid();
            return false;
        }
        if (!rThePassword.empty())
        {
            if (getSchemeInfo().m_bPassword)
            {
                m_aAbsURIRef.append(':');
                OUStringBuffer aNewAuth;
                encodeText(aNewAuth, rThePassword, PART_USER_PASSWORD,
                           EncodeMechanism::WasEncoded, RTL_TEXTENCODING_UTF8, false);
                m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aAbsURIRef.getLength());
                bUserInfo = true;
            }
            else
            {
                setInvalid();
                return false;
            }
        }
        if (bUserInfo && getSchemeInfo().m_bHost)
            m_aAbsURIRef.append('@');
        if (getSchemeInfo().m_bHost)
        {
            OUStringBuffer aSynHost(rTheHost);
            bool bNetBiosName = false;
            switch (m_eScheme)
            {
                case INetProtocol::File:
                    {
                        if (OUString::unacquired(aSynHost).equalsIgnoreAsciiCase( "localhost" ))
                        {
                            aSynHost.setLength(0);
                        }
                        bNetBiosName = true;
                    }
                    break;
 
                case INetProtocol::Ldap:
                    if (aSynHost.isEmpty() && nThePort != 0)
                    {
                        setInvalid();
                        return false;
                    }
                    break;
 
                default:
                    if (aSynHost.isEmpty())
                    {
                        setInvalid();
                        return false;
                    }
                    break;
            }
            if (!parseHostOrNetBiosName(
                    aSynHost.getStr(), aSynHost.getStr() + aSynHost.getLength(),
                    EncodeMechanism::WasEncoded, RTL_TEXTENCODING_UTF8, bNetBiosName, &aSynHost))
            {
                setInvalid();
                return false;
            }
            m_aHost.set(m_aAbsURIRef, aSynHost, m_aAbsURIRef.getLength());
            if (nThePort != 0)
            {
                if (getSchemeInfo().m_bPort)
                {
                    m_aAbsURIRef.append(':');
                    m_aPort.set(m_aAbsURIRef,
                                OUString::number(nThePort),
                                m_aAbsURIRef.getLength());
                }
                else
                {
                    setInvalid();
                    return false;
                }
            }
        }
        else if (!rTheHost.empty() || nThePort != 0)
        {
            setInvalid();
            return false;
        }
    }
    OUStringBuffer aSynPath(256);
    sal_Unicode const * p = rThePath.data();
    sal_Unicode const * pEnd = p + rThePath.size();
    if (!parsePath(m_eScheme, &p, pEnd, EncodeMechanism::WasEncoded, RTL_TEXTENCODING_UTF8, false, '/',
                   0x80000000, 0x80000000, 0x80000000, aSynPath)
        || p != pEnd)
    {
        setInvalid();
        return false;
    }
    m_aPath.set(m_aAbsURIRef, aSynPath, m_aAbsURIRef.getLength());
    return true;
}
 
// static
OUString INetURLObject::GetAbsURL(std::u16string_view rTheBaseURIRef,
                                       OUString const & rTheRelURIRef,
                                       EncodeMechanism eEncodeMechanism,
                                       DecodeMechanism eDecodeMechanism,
                                       rtl_TextEncoding eCharset)
{
    // Backwards compatibility:
    if (rTheRelURIRef.isEmpty() || rTheRelURIRef[0] == '#')
        return rTheRelURIRef;
 
    INetURLObject aTheAbsURIRef;
    bool bWasAbsolute;
    return INetURLObject(rTheBaseURIRef, eEncodeMechanism, eCharset).
            convertRelToAbs(rTheRelURIRef, aTheAbsURIRef,
                            bWasAbsolute, eEncodeMechanism,
                            eCharset, false, false,
                            false, FSysStyle::Detect)
           || eEncodeMechanism != EncodeMechanism::WasEncoded
           || eDecodeMechanism != DecodeMechanism::ToIUri
           || eCharset != RTL_TEXTENCODING_UTF8 ?
               aTheAbsURIRef.GetMainURL(eDecodeMechanism, eCharset) :
               rTheRelURIRef;
}
 
OUString INetURLObject::getExternalURL() const
{
    OUString aTheExtURIRef;
    translateToExternal(
        m_aAbsURIRef, aTheExtURIRef);
    return aTheExtURIRef;
}
 
bool INetURLObject::isSchemeEqualTo(std::u16string_view scheme) const {
    return m_aScheme.isPresent()
        && (rtl_ustr_compareIgnoreAsciiCase_WithLength(
                scheme.data(), scheme.size(),
                m_aAbsURIRef.getStr() + m_aScheme.getBegin(),
                m_aScheme.getLength())
            == 0);
}
 
bool INetURLObject::isAnyKnownWebDAVScheme() const {
    return ( isSchemeEqualTo( INetProtocol::Http ) ||
             isSchemeEqualTo( INetProtocol::Https ) ||
             isSchemeEqualTo( INetProtocol::VndSunStarWebdav ) ||
             isSchemeEqualTo( u"vnd.sun.star.webdavs" ) ||
             isSchemeEqualTo( u"webdav" ) ||
             isSchemeEqualTo( u"webdavs" ));
}
 
// static
const OUString & INetURLObject::GetScheme(INetProtocol eTheScheme)
{
    return getSchemeInfo(eTheScheme).m_aPrefix;
}
 
// static
const OUString & INetURLObject::GetSchemeName(INetProtocol eTheScheme)
{
    return getSchemeInfo(eTheScheme).m_sScheme;
}
 
// static
INetProtocol INetURLObject::CompareProtocolScheme(std::u16string_view aTheAbsURIRef)
{
    sal_Unicode const * p = aTheAbsURIRef.data();
    PrefixInfo const * pPrefix = getPrefix(p, p + aTheAbsURIRef.size());
    return pPrefix ? pPrefix->m_eScheme : INetProtocol::NotValid;
}
 
OUString INetURLObject::GetHostPort(DecodeMechanism eMechanism,
                                     rtl_TextEncoding eCharset) const
{
    // Check because PROT_VND_SUN_STAR_HELP, PROT_VND_SUN_STAR_HIER, and
    // PROT_VND_SUN_STAR_PKG misuse m_aHost:
    if (!getSchemeInfo().m_bHost)
        return OUString();
    OUStringBuffer aHostPort(decode(m_aHost, eMechanism, eCharset));
    if (m_aPort.isPresent())
    {
        aHostPort.append(":" + decode(m_aPort, eMechanism, eCharset));
    }
    return aHostPort.makeStringAndClear();
}
 
sal_uInt32 INetURLObject::GetPort() const
{
    if (m_aPort.isPresent())
    {
        sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPort.getBegin();
        sal_Unicode const * pEnd = p + m_aPort.getLength();
        sal_uInt32 nThePort;
        if (INetMIME::scanUnsigned(p, pEnd, true, nThePort) && p == pEnd)
            return nThePort;
    }
    return 0;
}
 
bool INetURLObject::SetPort(sal_uInt32 nThePort)
{
    if (getSchemeInfo().m_bPort && m_aHost.isPresent())
    {
        sal_Int32 nDelta;
        if (m_aPort.isPresent())
            nDelta = m_aPort.set(m_aAbsURIRef, OUString::number(nThePort));
        else
        {
            m_aAbsURIRef.insert(m_aHost.getEnd(), u':');
            nDelta = m_aPort.set(m_aAbsURIRef, OUString::number(nThePort), m_aHost.getEnd() + 1)
                         + 1;
        }
        m_aPath += nDelta;
        m_aQuery += nDelta;
        m_aFragment += nDelta;
        return true;
    }
    return false;
}
 
sal_Int32 INetURLObject::getSegmentCount(bool bIgnoreFinalSlash) const
{
    if (!checkHierarchical())
        return 0;
 
    sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pEnd = p + m_aPath.getLength();
    if (bIgnoreFinalSlash && pEnd > p && pEnd[-1] == '/')
        --pEnd;
    sal_Int32 n = p == pEnd || *p == '/' ? 0 : 1;
    while (p != pEnd)
        if (*p++ == '/')
            ++n;
    return n;
}
 
bool INetURLObject::removeSegment(sal_Int32 nIndex, bool bIgnoreFinalSlash)
{
    SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
    if (!aSegment.isPresent())
        return false;
 
    OUStringBuffer aNewPath(m_aPath.getLength());
    aNewPath.append(m_aAbsURIRef.getStr() + m_aPath.getBegin(),
                       aSegment.getBegin() - m_aPath.getBegin());
    if (bIgnoreFinalSlash && aSegment.getEnd() == m_aPath.getEnd())
        aNewPath.append('/');
    else
        aNewPath.append(m_aAbsURIRef.getStr() + aSegment.getEnd(),
                        m_aPath.getEnd() - aSegment.getEnd());
    if (aNewPath.isEmpty() && !aSegment.isEmpty() &&
        m_aAbsURIRef[aSegment.getBegin()] == '/')
    {
        aNewPath.append('/');
    }
 
    return setPath(aNewPath, EncodeMechanism::NotCanonical,
        RTL_TEXTENCODING_UTF8);
}
 
OUString INetURLObject::getName(sal_Int32 nIndex, bool bIgnoreFinalSlash,
                                 DecodeMechanism eMechanism,
                                 rtl_TextEncoding eCharset) const
{
    SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
    if (!aSegment.isPresent())
        return OUString();
 
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    sal_Unicode const * p = pSegBegin;
    while (p != pSegEnd && *p != ';')
        ++p;
 
    return decode(pSegBegin, p, eMechanism, eCharset);
}
 
bool INetURLObject::setName(std::u16string_view rTheName, EncodeMechanism eMechanism,
                            rtl_TextEncoding eCharset)
{
    SubString aSegment(getSegment(LAST_SEGMENT, true));
    if (!aSegment.isPresent())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    sal_Unicode const * p = pSegBegin;
    while (p != pSegEnd && *p != ';')
        ++p;
 
    OUStringBuffer aNewPath(256);
    aNewPath.append(std::u16string_view(pPathBegin, pSegBegin - pPathBegin));
    encodeText(aNewPath, rTheName, PART_PCHAR, eMechanism, eCharset, true);
    aNewPath.append(std::u16string_view(p, pPathEnd - p));
    return setPath(aNewPath, EncodeMechanism::NotCanonical, RTL_TEXTENCODING_UTF8);
}
 
bool INetURLObject::hasExtension()
    const
{
    SubString aSegment(getSegment(LAST_SEGMENT, true/*bIgnoreFinalSlash*/));
    if (!aSegment.isPresent())
        return false;
 
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    for (sal_Unicode const * p = pSegBegin; p != pSegEnd && *p != ';'; ++p)
        if (*p == '.' && p != pSegBegin)
            return true;
    return false;
}
 
OUString INetURLObject::getBase(sal_Int32 nIndex, bool bIgnoreFinalSlash,
                                 DecodeMechanism eMechanism,
                                 rtl_TextEncoding eCharset) const
{
    SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
    if (!aSegment.isPresent())
        return OUString();
 
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    sal_Unicode const * pExtension = nullptr;
    sal_Unicode const * p = pSegBegin;
    for (; p != pSegEnd && *p != ';'; ++p)
        if (*p == '.' && p != pSegBegin)
            pExtension = p;
    if (!pExtension)
        pExtension = p;
 
    return decode(pSegBegin, pExtension, eMechanism, eCharset);
}
 
bool INetURLObject::setBase(std::u16string_view rTheBase, sal_Int32 nIndex,
                            EncodeMechanism eMechanism,
                            rtl_TextEncoding eCharset)
{
    SubString aSegment(getSegment(nIndex, true/*bIgnoreFinalSlash*/));
    if (!aSegment.isPresent())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    sal_Unicode const * pExtension = nullptr;
    sal_Unicode const * p = pSegBegin;
    for (; p != pSegEnd && *p != ';'; ++p)
        if (*p == '.' && p != pSegBegin)
            pExtension = p;
    if (!pExtension)
        pExtension = p;
 
    OUStringBuffer aNewPath(256);
    aNewPath.append(std::u16string_view(pPathBegin, pSegBegin - pPathBegin));
    encodeText(aNewPath, rTheBase, PART_PCHAR, eMechanism, eCharset, true);
    aNewPath.append(std::u16string_view(pExtension, pPathEnd - pExtension));
    return setPath(aNewPath, EncodeMechanism::NotCanonical, RTL_TEXTENCODING_UTF8);
}
 
OUString INetURLObject::getExtension(sal_Int32 nIndex,
                                      bool bIgnoreFinalSlash,
                                      DecodeMechanism eMechanism,
                                      rtl_TextEncoding eCharset) const
{
    SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
    if (!aSegment.isPresent())
        return OUString();
 
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    sal_Unicode const * pExtension = nullptr;
    sal_Unicode const * p = pSegBegin;
    for (; p != pSegEnd && *p != ';'; ++p)
        if (*p == '.' && p != pSegBegin)
            pExtension = p;
 
    if (!pExtension)
        return OUString();
 
    return decode(pExtension + 1, p, eMechanism, eCharset);
}
 
bool INetURLObject::setExtension(std::u16string_view rTheExtension,
                                 sal_Int32 nIndex, bool bIgnoreFinalSlash,
                                 rtl_TextEncoding eCharset)
{
    SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
    if (!aSegment.isPresent())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    sal_Unicode const * pExtension = nullptr;
    sal_Unicode const * p = pSegBegin;
    for (; p != pSegEnd && *p != ';'; ++p)
        if (*p == '.' && p != pSegBegin)
            pExtension = p;
    if (!pExtension)
        pExtension = p;
 
    OUStringBuffer aNewPath(256);
    aNewPath.append(OUString::Concat(std::u16string_view(pPathBegin, pExtension - pPathBegin)) + ".");
    encodeText(aNewPath, rTheExtension, PART_PCHAR, EncodeMechanism::WasEncoded, eCharset, true);
    aNewPath.append(std::u16string_view(p, pPathEnd - p));
    return setPath(aNewPath, EncodeMechanism::NotCanonical, RTL_TEXTENCODING_UTF8);
}
 
bool INetURLObject::removeExtension(sal_Int32 nIndex, bool bIgnoreFinalSlash)
{
    SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash));
    if (!aSegment.isPresent())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    sal_Unicode const * pSegBegin
        = m_aAbsURIRef.getStr() + aSegment.getBegin();
    sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength();
 
    if (pSegBegin < pSegEnd && *pSegBegin == '/')
        ++pSegBegin;
    sal_Unicode const * pExtension = nullptr;
    sal_Unicode const * p = pSegBegin;
    for (; p != pSegEnd && *p != ';'; ++p)
        if (*p == '.' && p != pSegBegin)
            pExtension = p;
    if (!pExtension)
        return true;
 
    OUString aNewPath =
        OUString::Concat(std::u16string_view(pPathBegin, pExtension - pPathBegin)) +
        std::u16string_view(p, pPathEnd - p);
 
    return setPath(aNewPath, EncodeMechanism::NotCanonical, RTL_TEXTENCODING_UTF8);
}
 
bool INetURLObject::hasFinalSlash() const
{
    if (!checkHierarchical())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    return pPathEnd > pPathBegin && pPathEnd[-1] == '/';
}
 
bool INetURLObject::setFinalSlash()
{
    if (!checkHierarchical())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    if (pPathEnd > pPathBegin && pPathEnd[-1] == '/')
        return true;
 
    OUString aNewPath
        = OUString::Concat(std::u16string_view(pPathBegin, pPathEnd - pPathBegin)) + "/";
 
    return setPath(aNewPath, EncodeMechanism::NotCanonical, RTL_TEXTENCODING_UTF8);
}
 
bool INetURLObject::removeFinalSlash()
{
    if (!checkHierarchical())
        return false;
 
    sal_Unicode const * pPathBegin
        = m_aAbsURIRef.getStr() + m_aPath.getBegin();
    sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength();
    if (pPathEnd <= pPathBegin || pPathEnd[-1] != '/')
        return true;
 
    --pPathEnd;
    if (pPathEnd == pPathBegin && *pPathBegin == '/')
        return false;
    OUString aNewPath(pPathBegin, pPathEnd - pPathBegin);
 
    return setPath(aNewPath, EncodeMechanism::NotCanonical, RTL_TEXTENCODING_UTF8);
}
 
OUString INetURLObject::getFSysPath(FSysStyle eStyle,
                                     sal_Unicode * pDelimiter) const
{
    if (m_eScheme != INetProtocol::File)
        return OUString();
 
    if (((eStyle & FSysStyle::Vos) ? 1 : 0)
                + ((eStyle & FSysStyle::Unix) ? 1 : 0)
                + ((eStyle & FSysStyle::Dos) ? 1 : 0)
            > 1)
    {
        if(eStyle & FSysStyle::Vos && m_aHost.isPresent() && m_aHost.getLength() > 0)
        {
            eStyle= FSysStyle::Vos;
        }
        else
        {
            if(hasDosVolume(eStyle) || ((eStyle & FSysStyle::Dos) && m_aHost.isPresent() && m_aHost.getLength() > 0))
            {
                eStyle = FSysStyle::Dos;
            }
            else
            {
                if(eStyle & FSysStyle::Unix && (!m_aHost.isPresent() || m_aHost.getLength() == 0))
                {
                    eStyle = FSysStyle::Unix;
                }
                else
                {
                    eStyle= FSysStyle(0);
                }
            }
        }
    }
 
    switch (eStyle)
    {
        case FSysStyle::Vos:
        {
            if (pDelimiter)
                *pDelimiter = '/';
 
            OUStringBuffer aSynFSysPath("//");
            if (m_aHost.isPresent() && m_aHost.getLength() > 0)
                aSynFSysPath.append(decode(m_aHost, DecodeMechanism::WithCharset,
                                       RTL_TEXTENCODING_UTF8));
            else
                aSynFSysPath.append('.');
            aSynFSysPath.append(decode(m_aPath, DecodeMechanism::WithCharset,
                                   RTL_TEXTENCODING_UTF8));
            return aSynFSysPath.makeStringAndClear();
        }
 
        case FSysStyle::Unix:
        {
            if (m_aHost.isPresent() && m_aHost.getLength() > 0)
                return OUString();
 
            if (pDelimiter)
                *pDelimiter = '/';
 
            return decode(m_aPath, DecodeMechanism::WithCharset, RTL_TEXTENCODING_UTF8);
        }
 
        case FSysStyle::Dos:
        {
            if (pDelimiter)
                *pDelimiter = '\\';
 
            OUStringBuffer aSynFSysPath(64);
            if (m_aHost.isPresent() && m_aHost.getLength() > 0)
            {
                aSynFSysPath.append("\\\\"
                    + decode(m_aHost, DecodeMechanism::WithCharset, RTL_TEXTENCODING_UTF8)
                    + "\\");
            }
            sal_Unicode const * p
                = m_aAbsURIRef.getStr() + m_aPath.getBegin();
            sal_Unicode const * pEnd = p + m_aPath.getLength();
            DBG_ASSERT(p < pEnd && *p == '/',
                       "INetURLObject::getFSysPath(): Bad path");
            ++p;
            while (p < pEnd)
            {
                EscapeType eEscapeType;
                sal_uInt32 nUTF32 = getUTF32(p, pEnd, EncodeMechanism::WasEncoded,
                                             RTL_TEXTENCODING_UTF8,
                                             eEscapeType);
                if (eEscapeType == EscapeType::NONE && nUTF32 == '/')
                    aSynFSysPath.append('\\');
                else
                    aSynFSysPath.appendUtf32(nUTF32);
            }
            return aSynFSysPath.makeStringAndClear();
        }
 
        default:
            return OUString();
    }
}
 
// static
void INetURLObject::appendUCS4Escape(OUStringBuffer & rTheText,
                                     sal_uInt32 nUCS4)
{
    DBG_ASSERT(nUCS4 < 0x80000000,
               "INetURLObject::appendUCS4Escape(): Bad char");
    if (nUCS4 < 0x80)
        appendEscape(rTheText, nUCS4);
    else if (nUCS4 < 0x800)
    {
        appendEscape(rTheText, nUCS4 >> 6 | 0xC0);
        appendEscape(rTheText, (nUCS4 & 0x3F) | 0x80);
    }
    else if (nUCS4 < 0x10000)
    {
        appendEscape(rTheText, nUCS4 >> 12 | 0xE0);
        appendEscape(rTheText, (nUCS4 >> 6 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 & 0x3F) | 0x80);
    }
    else if (nUCS4 < 0x200000)
    {
        appendEscape(rTheText, nUCS4 >> 18 | 0xF0);
        appendEscape(rTheText, (nUCS4 >> 12 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 >> 6 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 & 0x3F) | 0x80);
    }
    else if (nUCS4 < 0x4000000)
    {
        appendEscape(rTheText, nUCS4 >> 24 | 0xF8);
        appendEscape(rTheText, (nUCS4 >> 18 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 >> 12 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 >> 6 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 & 0x3F) | 0x80);
    }
    else
    {
        appendEscape(rTheText, nUCS4 >> 30 | 0xFC);
        appendEscape(rTheText, (nUCS4 >> 24 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 >> 18 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 >> 12 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 >> 6 & 0x3F) | 0x80);
        appendEscape(rTheText, (nUCS4 & 0x3F) | 0x80);
    }
}
 
// static
void INetURLObject::appendUCS4(OUStringBuffer& rTheText, sal_uInt32 nUCS4,
                               EscapeType eEscapeType,
                               Part ePart, rtl_TextEncoding eCharset,
                               bool bKeepVisibleEscapes)
{
    bool bEscape;
    rtl_TextEncoding eTargetCharset = RTL_TEXTENCODING_DONTKNOW;
    switch (eEscapeType)
    {
        case EscapeType::NONE:
            if (mustEncode(nUCS4, ePart))
            {
                bEscape = true;
                eTargetCharset = RTL_TEXTENCODING_UTF8;
            }
            else
                bEscape = false;
            break;
 
        case EscapeType::Octet:
            bEscape = true;
            eTargetCharset = RTL_TEXTENCODING_ISO_8859_1;
            break;
 
        case EscapeType::Utf32:
            if (mustEncode(nUCS4, ePart))
            {
                bEscape = true;
                eTargetCharset = eCharset;
            }
            else if (bKeepVisibleEscapes && INetMIME::isVisible(nUCS4))
            {
                bEscape = true;
                eTargetCharset = RTL_TEXTENCODING_ASCII_US;
            }
            else
                bEscape = false;
            break;
        default:
            bEscape = false;
    }
 
    if (bEscape)
    {
        switch (eTargetCharset)
        {
            default:
                OSL_FAIL("INetURLObject::appendUCS4(): Unsupported charset");
                [[fallthrough]];
            case RTL_TEXTENCODING_ASCII_US:
            case RTL_TEXTENCODING_ISO_8859_1:
                appendEscape(rTheText, nUCS4);
                break;
            case RTL_TEXTENCODING_UTF8:
                appendUCS4Escape(rTheText, nUCS4);
                break;
        }
    }
    else
        rTheText.append(sal_Unicode(nUCS4));
}
 
// static
sal_uInt32 INetURLObject::getUTF32(sal_Unicode const *& rBegin,
                                   sal_Unicode const * pEnd,
                                   EncodeMechanism eMechanism,
                                   rtl_TextEncoding eCharset,
                                   EscapeType & rEscapeType)
{
    DBG_ASSERT(rBegin < pEnd, "INetURLObject::getUTF32(): Bad sequence");
    sal_uInt32 nUTF32 = INetMIME::getUTF32Character(rBegin, pEnd);
    switch (eMechanism)
    {
        case EncodeMechanism::All:
            rEscapeType = EscapeType::NONE;
            break;
 
        case EncodeMechanism::WasEncoded:
        {
            int nWeight1;
            int nWeight2;
            if (nUTF32 == static_cast<unsigned char>('%') && rBegin + 1 < pEnd
                && (nWeight1 = INetMIME::getHexWeight(rBegin[0])) >= 0
                && (nWeight2 = INetMIME::getHexWeight(rBegin[1])) >= 0)
            {
                rBegin += 2;
                nUTF32 = nWeight1 << 4 | nWeight2;
                switch (eCharset)
                {
                    default:
                        OSL_FAIL(
                            "INetURLObject::getUTF32(): Unsupported charset");
                        [[fallthrough]];
                    case RTL_TEXTENCODING_ASCII_US:
                        rEscapeType = rtl::isAscii(nUTF32) ?
                                          EscapeType::Utf32 : EscapeType::Octet;
                        break;
 
                    case RTL_TEXTENCODING_ISO_8859_1:
                        rEscapeType = EscapeType::Utf32;
                        break;
 
                    case RTL_TEXTENCODING_UTF8:
                        if (rtl::isAscii(nUTF32))
                            rEscapeType = EscapeType::Utf32;
                        else
                        {
                            if (nUTF32 >= 0xC0 && nUTF32 <= 0xF4)
                            {
                                sal_uInt32 nEncoded;
                                int nShift;
                                sal_uInt32 nMin;
                                if (nUTF32 <= 0xDF)
                                {
                                    nEncoded = (nUTF32 & 0x1F) << 6;
                                    nShift = 0;
                                    nMin = 0x80;
                                }
                                else if (nUTF32 <= 0xEF)
                                {
                                    nEncoded = (nUTF32 & 0x0F) << 12;
                                    nShift = 6;
                                    nMin = 0x800;
                                }
                                else
                                {
                                    nEncoded = (nUTF32 & 0x07) << 18;
                                    nShift = 12;
                                    nMin = 0x10000;
                                }
                                sal_Unicode const * p = rBegin;
                                bool bUTF8 = true;
                                for (;;)
                                {
                                    if (pEnd - p < 3
                                        || p[0] != '%'
                                        || (nWeight1
                                               = INetMIME::getHexWeight(p[1]))
                                               < 8
                                        || nWeight1 > 11
                                        || (nWeight2
                                               = INetMIME::getHexWeight(p[2]))
                                               < 0)
                                    {
                                        bUTF8 = false;
                                        break;
                                    }
                                    p += 3;
                                    nEncoded
                                        |= ((nWeight1 & 3) << 4 | nWeight2)
                                               << nShift;
                                    if (nShift == 0)
                                        break;
                                    nShift -= 6;
                                }
                                if (bUTF8 && rtl::isUnicodeScalarValue(nEncoded)
                                    && nEncoded >= nMin)
                                {
                                    rBegin = p;
                                    nUTF32 = nEncoded;
                                    rEscapeType = EscapeType::Utf32;
                                    break;
                                }
                            }
                            rEscapeType = EscapeType::Octet;
                        }
                        break;
                }
            }
            else
                rEscapeType = EscapeType::NONE;
            break;
        }
 
        case EncodeMechanism::NotCanonical:
        {
            int nWeight1;
            int nWeight2;
            if (nUTF32 == static_cast<unsigned char>('%') && rBegin + 1 < pEnd
                && ((nWeight1 = INetMIME::getHexWeight(rBegin[0])) >= 0)
                && ((nWeight2 = INetMIME::getHexWeight(rBegin[1])) >= 0))
            {
                rBegin += 2;
                nUTF32 = nWeight1 << 4 | nWeight2;
                rEscapeType = EscapeType::Octet;
            }
            else
                rEscapeType = EscapeType::NONE;
            break;
        }
    }
    return nUTF32;
}
 
// static
sal_uInt32 INetURLObject::scanDomain(sal_Unicode const *& rBegin,
                                     sal_Unicode const * pEnd,
                                     bool bEager)
{
    enum State { STATE_DOT, STATE_LABEL, STATE_HYPHEN };
    State eState = STATE_DOT;
    sal_Int32 nLabels = 0;
    sal_Unicode const * pLastAlphanumeric = nullptr;
    for (sal_Unicode const * p = rBegin;; ++p)
        switch (eState)
        {
            case STATE_DOT:
                if (p != pEnd && (rtl::isAsciiAlphanumeric(*p) || *p == '_'))
                {
                    ++nLabels;
                    eState = STATE_LABEL;
                    break;
                }
                if (bEager || nLabels == 0)
                    return 0;
                rBegin = p - 1;
                return nLabels;
 
            case STATE_LABEL:
                if (p != pEnd)
                {
                    if (rtl::isAsciiAlphanumeric(*p) || *p == '_')
                        break;
                    else if (*p == '.')
                    {
                        eState = STATE_DOT;
                        break;
                    }
                    else if (*p == '-')
                    {
                        pLastAlphanumeric = p;
                        eState = STATE_HYPHEN;
                        break;
                    }
                }
                rBegin = p;
                return nLabels;
 
            case STATE_HYPHEN:
                if (p != pEnd)
                {
                    if (rtl::isAsciiAlphanumeric(*p) || *p == '_')
                    {
                        eState = STATE_LABEL;
                        break;
                    }
                    else if (*p == '-')
                        break;
                }
                if (bEager)
                    return 0;
                rBegin = pLastAlphanumeric;
                return nLabels;
        }
}
 
// static
bool INetURLObject::scanIPv6reference(sal_Unicode const *& rBegin,
                                      sal_Unicode const * pEnd)
{
    if (rBegin != pEnd && *rBegin == '[') {
        sal_Unicode const * p = rBegin + 1;
        //TODO: check for valid IPv6address (RFC 2373):
        while (p != pEnd && (rtl::isAsciiHexDigit(*p) || *p == ':' || *p == '.'))
        {
            ++p;
        }
        if (p != pEnd && *p == ']') {
            rBegin = p + 1;
            return true;
        }
    }
    return false;
}
 
OUString INetURLObject::GetPartBeforeLastName()
    const
{
    if (!checkHierarchical())
        return OUString();
    INetURLObject aTemp(*this);
    aTemp.clearFragment();
    aTemp.clearQuery();
    aTemp.removeSegment(LAST_SEGMENT, false);
    aTemp.setFinalSlash();
    return aTemp.GetMainURL(DecodeMechanism::ToIUri);
}
 
OUString INetURLObject::GetLastName(DecodeMechanism eMechanism,
                                     rtl_TextEncoding eCharset) const
{
    return getName(LAST_SEGMENT, true, eMechanism, eCharset);
}
 
OUString INetURLObject::GetFileExtension() const
{
    return getExtension(LAST_SEGMENT, false);
}
 
void INetURLObject::CutLastName()
{
    INetURLObject aTemp(*this);
    aTemp.clearFragment();
    aTemp.clearQuery();
    if (!aTemp.removeSegment(LAST_SEGMENT, false))
        return;
    *this = std::move(aTemp);
}
 
OUString INetURLObject::PathToFileName() const
{
    if (m_eScheme != INetProtocol::File)
        return OUString();
    OUString aSystemPath;
    if (osl::FileBase::getSystemPathFromFileURL(
                decode(m_aAbsURIRef.getStr(),
                       m_aAbsURIRef.getStr() + m_aPath.getEnd(),
                       DecodeMechanism::NONE, RTL_TEXTENCODING_UTF8),
                aSystemPath)
            != osl::FileBase::E_None)
        return OUString();
    return aSystemPath;
}
 
OUString INetURLObject::GetFull() const
{
    INetURLObject aTemp(*this);
    aTemp.removeFinalSlash();
    return aTemp.PathToFileName();
}
 
OUString INetURLObject::GetPath() const
{
    INetURLObject aTemp(*this);
    aTemp.removeSegment();
    aTemp.removeFinalSlash();
    return aTemp.PathToFileName();
}
 
void INetURLObject::SetBase(std::u16string_view rTheBase)
{
    setBase(rTheBase, LAST_SEGMENT, EncodeMechanism::All);
}
 
OUString INetURLObject::GetBase() const
{
    return getBase(LAST_SEGMENT, true, DecodeMechanism::WithCharset);
}
 
void INetURLObject::SetExtension(std::u16string_view rTheExtension)
{
    setExtension(rTheExtension, LAST_SEGMENT, false);
}
 
OUString INetURLObject::CutExtension()
{
    OUString aTheExtension(getExtension(LAST_SEGMENT, false));
    if (removeExtension(LAST_SEGMENT, false))
        return aTheExtension;
    return OUString();
}
 
bool INetURLObject::IsExoticProtocol() const
{
    return m_eScheme == INetProtocol::Slot ||
           m_eScheme == INetProtocol::Macro ||
           m_eScheme == INetProtocol::Uno ||
           isSchemeEqualTo(u"vnd.sun.star.script") ||
           isSchemeEqualTo(u"service");
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'remove' is required to be utilized.

V530 The return value of function 'insert' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'remove' is required to be utilized.

V530 The return value of function 'insert' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'insert' is required to be utilized.

V530 The return value of function 'insert' is required to be utilized.

V530 The return value of function 'insert' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'insert' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'appendUtf32' is required to be utilized.

V530 The return value of function 'appendUtf32' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'insert' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'appendUtf32' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V1037 Two or more case-branches perform the same actions. Check lines: 2502, 2527

V1048 The 'nSegmentDelimiter' variable was assigned the same value.

V1048 The 'nSegmentDelimiter' variable was assigned the same value.