1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "file_url.hxx"
28 #include <string_view>
29 #include <type_traits>
35 #include <o3tl/safeint.hxx>
36 #include <osl/file.hxx>
37 #include <osl/security.hxx>
38 #include <osl/socket.h>
39 #include <oslsocket.hxx>
40 #include <osl/diagnose.h>
41 #include <osl/thread.h>
42 #include <osl/process.h>
44 #include <rtl/character.hxx>
45 #include <rtl/strbuf.hxx>
47 #include <rtl/uri.hxx>
48 #include <rtl/ustring.hxx>
49 #include <rtl/ustrbuf.h>
50 #include <rtl/ustrbuf.hxx>
51 #include <rtl/textcvt.h>
52 #include <sal/log.hxx>
54 #include <uri_internal.hxx>
56 #include "file_error_transl.hxx"
57 #include "file_path_helper.hxx"
59 #include "uunxapi.hxx"
65 This file contains the part that handles File URLs.
67 File URLs as scheme specific notion of URIs
68 (RFC2396) may be handled platform independent, but
69 will not in osl which is considered wrong.
70 Future version of osl should handle File URLs this
71 way. In rtl/uri there is already a URI parser etc.
72 so this code should be consolidated.
80 // A slightly modified version of Pchar in rtl/source/uri.c, but without
82 constexpr auto uriCharClass
= rtl::createUriCharClass(
83 u8
"!$&'()*+,-./0123456789:=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~");
87 oslFileError SAL_CALL
osl_getCanonicalName( rtl_uString
* ustrFileURL
, rtl_uString
** pustrValidURL
)
89 OSL_FAIL("osl_getCanonicalName not implemented");
91 rtl_uString_newFromString(pustrValidURL
, ustrFileURL
);
92 return osl_File_E_None
;
97 class UnicodeToTextConverter_Impl
99 rtl_UnicodeToTextConverter m_converter
;
101 UnicodeToTextConverter_Impl()
102 : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding()))
105 ~UnicodeToTextConverter_Impl()
107 rtl_destroyUnicodeToTextConverter (m_converter
);
110 static UnicodeToTextConverter_Impl
& getInstance()
112 static UnicodeToTextConverter_Impl g_theConverter
;
113 return g_theConverter
;
117 sal_Unicode
const * pSrcBuf
, sal_Size nSrcChars
, char * pDstBuf
, sal_Size nDstBytes
,
118 sal_uInt32 nFlags
, sal_uInt32
* pInfo
, sal_Size
* pSrcCvtChars
)
120 OSL_ASSERT(m_converter
!= nullptr);
121 return rtl_convertUnicodeToText (
122 m_converter
, nullptr, pSrcBuf
, nSrcChars
, pDstBuf
, nDstBytes
, nFlags
, pInfo
, pSrcCvtChars
);
126 bool convert(OUStringBuffer
const & in
, OStringBuffer
* append
) {
127 assert(append
!= nullptr);
128 for (sal_Size nConvert
= in
.getLength();;) {
129 auto const oldLen
= append
->getLength();
131 std::max(nConvert
, sal_Size(PATH_MAX
)),
132 sal_Size(std::numeric_limits
<sal_Int32
>::max() - oldLen
));
133 // approximation of required converted size
134 auto s
= append
->appendUninitialized(n
);
137 //TODO: context, for reliable treatment of DESTBUFFERTOSMALL:
138 n
= UnicodeToTextConverter_Impl::getInstance().convert(
139 in
.getStr() + in
.getLength() - nConvert
, nConvert
, s
, n
,
140 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
| RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
141 | RTL_UNICODETOTEXT_FLAGS_FLUSH
),
143 if ((info
& RTL_UNICODETOTEXT_INFO_ERROR
) != 0) {
146 append
->setLength(oldLen
+ n
);
147 assert(converted
<= nConvert
);
148 nConvert
-= converted
;
149 assert((nConvert
== 0) == ((info
& RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL
) == 0));
150 if ((info
& RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL
) == 0) {
157 bool decodeFromUtf8(std::u16string_view text
, OString
* result
) {
158 assert(result
!= nullptr);
159 auto p
= text
.data();
160 auto const end
= p
+ text
.size();
161 OUStringBuffer
ubuf(static_cast<int>(text
.size()));
162 OStringBuffer
bbuf(PATH_MAX
);
164 rtl::uri::detail::EscapeType t
;
165 sal_uInt32 c
= rtl::uri::detail::readUcs4(&p
, end
, true, RTL_TEXTENCODING_UTF8
, &t
);
167 case rtl::uri::detail::EscapeNo
:
172 case rtl::uri::detail::EscapeChar
:
173 if (rtl::isSurrogate(c
)) {
178 case rtl::uri::detail::EscapeOctet
:
179 if (!convert(ubuf
, &bbuf
)) {
184 bbuf
.append(char(c
));
188 if (!convert(ubuf
, &bbuf
)) {
191 *result
= bbuf
.makeStringAndClear();
195 template<typename T
> oslFileError
getSystemPathFromFileUrl(
196 OUString
const & url
, T
* path
, bool resolveHome
)
198 assert(path
!= nullptr);
199 // For compatibility with assumptions in other parts of the code base,
200 // assume that anything starting with a slash is a system path instead of a
201 // (relative) file URL (except if it starts with two slashes, in which case
202 // it is a relative URL with an authority component):
204 || (url
[0] == '/' && (url
.getLength() == 1 || url
[1] != '/')))
206 return osl_File_E_INVAL
;
208 // Check for non file scheme:
210 if (rtl::isAsciiAlpha(url
[0])) {
211 for (sal_Int32 j
= 1; j
!= url
.getLength(); ++j
) {
214 if (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
215 url
.pData
->buffer
, j
,
216 RTL_CONSTASCII_STRINGPARAM("file"))
219 return osl_File_E_INVAL
;
224 if (!rtl::isAsciiAlphanumeric(c
) && c
!= '+' && c
!= '-'
231 // Handle query or fragment:
232 if (url
.indexOf('?', i
) != -1 || url
.indexOf('#', i
) != -1)
233 return osl_File_E_INVAL
;
234 // Handle authority, supporting a host of "localhost", "127.0.0.1", or the exact value (e.g.,
235 // not supporting an additional final dot, for simplicity) reported by osl_getLocalHostnameFQDN
236 // (and, in each case, ignoring case of ASCII letters):
237 if (url
.getLength() - i
>= 2 && url
[i
] == '/' && url
[i
+ 1] == '/')
240 sal_Int32 j
= url
.indexOf('/', i
);
244 && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
245 url
.pData
->buffer
+ i
, j
- i
,
246 RTL_CONSTASCII_STRINGPARAM("localhost"))
248 && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
249 url
.pData
->buffer
+ i
, j
- i
,
250 RTL_CONSTASCII_STRINGPARAM("127.0.0.1"))
254 // The 'file' URI Scheme does imply that we want a FQDN in this case
255 // See https://tools.ietf.org/html/rfc8089#section-3
256 if (osl_getLocalHostnameFQDN(&hostname
.pData
) != osl_Socket_Ok
257 || (rtl_ustr_compareIgnoreAsciiCase_WithLength(
258 url
.pData
->buffer
+ i
, j
- i
, hostname
.getStr(), hostname
.getLength())
261 return osl_File_E_INVAL
;
266 // Handle empty path:
267 if (i
== url
.getLength())
270 return osl_File_E_None
;
272 // Path must not contain %2F:
273 if (url
.indexOf("%2F", i
) != -1 || url
.indexOf("%2f", i
) != -1)
274 return osl_File_E_INVAL
;
276 if constexpr (std::is_same_v
<T
, rtl::OString
>) {
277 if (!decodeFromUtf8(url
.subView(i
), path
)) {
278 return osl_File_E_INVAL
;
280 } else if constexpr (std::is_same_v
<T
, rtl::OUString
>) {
281 *path
= rtl::Uri::decode(
282 url
.copy(i
), rtl_UriDecodeWithCharset
, RTL_TEXTENCODING_UTF8
);
284 static_assert(std::is_same_v
<T
, rtl::OString
> || std::is_same_v
<T
, rtl::OUString
>);
286 // Path must not contain %2F:
287 if (path
->indexOf('\0') != -1)
288 return osl_File_E_INVAL
;
290 // Handle ~ notation:
291 if (resolveHome
&& path
->getLength() >= 2 && (*path
)[1] == '~')
293 sal_Int32 j
= path
->indexOf('/', 2);
295 j
= path
->getLength();
300 if (!osl::Security().getHomeDir(home
))
302 SAL_WARN("sal.file", "osl::Security::getHomeDir failed");
303 return osl_File_E_INVAL
;
306 i
= url
.indexOf('/', i
+ 1);
313 //TODO: cheesy way of ensuring home's path ends in slash:
314 if (!home
.isEmpty() && home
[home
.getLength() - 1] != '/')
318 home
= rtl::Uri::convertRelToAbs(home
, url
.copy(i
));
320 catch (rtl::MalformedUriException
& e
)
322 SAL_WARN("sal.file", "rtl::MalformedUriException " << e
.getMessage());
323 return osl_File_E_INVAL
;
325 return getSystemPathFromFileUrl(home
, path
, false);
327 // FIXME: replace ~user with user's home directory
328 return osl_File_E_INVAL
;
330 return osl_File_E_None
;
335 oslFileError SAL_CALL
osl_getSystemPathFromFileURL( rtl_uString
*ustrFileURL
, rtl_uString
**pustrSystemPath
)
341 e
= getSystemPathFromFileUrl(
342 OUString::unacquired(&ustrFileURL
), &path
, true);
344 catch (std::length_error
&)
346 e
= osl_File_E_RANGE
;
349 if (e
== osl_File_E_None
)
350 rtl_uString_assign(pustrSystemPath
, path
.pData
);
355 oslFileError SAL_CALL
osl_getFileURLFromSystemPath( rtl_uString
*ustrSystemPath
, rtl_uString
**pustrFileURL
)
357 rtl_uString
*pTmp
= nullptr;
360 auto const & systemPath
= OUString::unacquired(&ustrSystemPath
);
362 if( systemPath
.isEmpty() )
363 return osl_File_E_INVAL
;
365 if( systemPath
.startsWith( "file:" ) )
366 return osl_File_E_INVAL
;
368 /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
369 if( systemPath
.startsWith("~") )
371 /* check if another user is specified */
372 if( ( systemPath
.getLength() == 1 ) ||
373 ( systemPath
[1] == '/' ) )
375 /* osl_getHomeDir returns file URL */
376 oslSecurity pSecurity
= osl_getCurrentSecurity();
377 osl_getHomeDir( pSecurity
, &pTmp
);
378 osl_freeSecurityHandle( pSecurity
);
381 return osl_File_E_INVAL
;
383 /* remove "file://" prefix */
384 rtl_uString_newFromStr_WithLength( &pTmp
, pTmp
->buffer
+ 7, pTmp
->length
- 7 );
386 /* replace '~' in original string */
387 rtl_uString_newReplaceStrAt( &pTmp
, systemPath
.pData
, 0, 1, pTmp
);
391 /* FIXME: replace ~user with users home directory */
392 return osl_File_E_INVAL
;
396 /* check if initial string contains repeated '/' characters */
397 nIndex
= systemPath
.indexOf( "//" );
401 sal_Int32 nDeleted
= 0;
403 /* if pTmp is not already allocated, copy systemPath for modification */
404 if( pTmp
== nullptr )
405 rtl_uString_newFromString( &pTmp
, systemPath
.pData
);
407 /* adapt index to pTmp */
408 nIndex
+= pTmp
->length
- systemPath
.getLength();
410 /* replace repeated '/' characters with a single '/' */
411 for( nSrcIndex
= nIndex
+ 1; nSrcIndex
< pTmp
->length
; nSrcIndex
++ )
413 if( (pTmp
->buffer
[nSrcIndex
] == '/') && (pTmp
->buffer
[nIndex
] == '/') )
416 pTmp
->buffer
[++nIndex
] = pTmp
->buffer
[nSrcIndex
];
419 /* adjust length member */
420 pTmp
->length
-= nDeleted
;
423 if( pTmp
== nullptr )
424 rtl_uString_assign( &pTmp
, systemPath
.pData
);
426 /* file URLs must be URI encoded */
427 rtl_uriEncode( pTmp
, uriCharClass
.data(), rtl_UriEncodeIgnoreEscapes
, RTL_TEXTENCODING_UTF8
, pustrFileURL
);
429 rtl_uString_release( pTmp
);
431 /* absolute urls should start with 'file://' */
432 if( (*pustrFileURL
)->buffer
[0] == '/' )
434 rtl_uString
*pProtocol
= nullptr;
436 rtl_uString_newFromAscii( &pProtocol
, "file://" );
437 rtl_uString_newConcat( pustrFileURL
, pProtocol
, *pustrFileURL
);
438 rtl_uString_release( pProtocol
);
441 return osl_File_E_None
;
445 * relative URLs are not accepted
447 oslFileError
getSystemPathFromFileURL_Ex(
448 rtl_uString
*ustrFileURL
, rtl_uString
**pustrSystemPath
)
450 rtl_uString
* temp
= nullptr;
451 oslFileError osl_error
= osl_getSystemPathFromFileURL(ustrFileURL
, &temp
);
453 if (osl_error
== osl_File_E_None
)
455 if (temp
->buffer
[0] == '/')
457 *pustrSystemPath
= temp
;
461 rtl_uString_release(temp
);
462 osl_error
= osl_File_E_INVAL
;
472 /** Helper function, return a pointer to the final '\0'
476 sal_Unicode
* ustrtoend(sal_Unicode
* pStr
)
478 return (pStr
+ rtl_ustr_getLength(pStr
));
481 sal_Unicode
* ustrchrcat(const sal_Unicode chr
, sal_Unicode
* d
)
483 sal_Unicode
* p
= ustrtoend(d
);
489 bool _islastchr(sal_Unicode
* pStr
, sal_Unicode Chr
)
491 sal_Unicode
* p
= ustrtoend(pStr
);
498 Remove the last part of a path, a path that has
499 only a '/' or no '/' at all will be returned
503 sal_Unicode
* _rmlastpathtoken(sal_Unicode
* aPath
)
505 /* we may always skip -2 because we
506 may at least stand on a '/' but
507 either there is no other character
508 before this '/' or it's another
509 character than the '/'
511 sal_Unicode
* p
= ustrtoend(aPath
) - 2;
513 /* move back to the next path separator
514 or to the start of the string */
515 while ((p
> aPath
) && (*p
!= '/'))
534 oslFileError
_osl_resolvepath(
535 /*inout*/ sal_Unicode
* path
,
536 /*inout*/ bool* failed
)
538 oslFileError ferr
= osl_File_E_None
;
542 char unresolved_path
[PATH_MAX
];
543 if (!UnicodeToText(unresolved_path
, sizeof(unresolved_path
), path
, rtl_ustr_getLength(path
)))
544 return oslTranslateFileError(ENAMETOOLONG
);
546 char resolved_path
[PATH_MAX
];
547 if (realpath(unresolved_path
, resolved_path
))
549 if (!TextToUnicode(resolved_path
, strlen(resolved_path
), path
, PATH_MAX
))
550 return oslTranslateFileError(ENAMETOOLONG
);
554 if (EACCES
== errno
|| ENOTDIR
== errno
|| ENOENT
== errno
)
557 ferr
= oslTranslateFileError(errno
);
565 Works even with non existing paths. The resulting path must not exceed
566 PATH_MAX else osl_File_E_NAMETOOLONG is the result
569 oslFileError
osl_getAbsoluteFileURL_impl_(const OUString
& unresolved_path
, OUString
& resolved_path
)
571 /* the given unresolved path must not exceed PATH_MAX */
572 if (unresolved_path
.getLength() >= (PATH_MAX
- 2))
573 return oslTranslateFileError(ENAMETOOLONG
);
575 sal_Unicode path_resolved_so_far
[PATH_MAX
];
576 const sal_Unicode
* punresolved
= unresolved_path
.getStr();
577 sal_Unicode
* presolvedsf
= path_resolved_so_far
;
579 /* reserve space for leading '/' and trailing '\0'
580 do not exceed this limit */
581 sal_Unicode
* sentinel
= path_resolved_so_far
+ PATH_MAX
- 2;
583 /* if realpath fails with error ENOTDIR, EACCES or ENOENT
584 we will not call it again, because _osl_realpath should also
585 work with non existing directories etc. */
586 bool realpath_failed
= false;
589 path_resolved_so_far
[0] = '\0';
591 while (*punresolved
!= '\0')
593 /* ignore '/.' , skip one part back when '/..' */
594 if ((*punresolved
== '.') && (*presolvedsf
== '/'))
596 if (*(punresolved
+ 1) == '\0')
601 if (*(punresolved
+ 1) == '/')
606 if ((*(punresolved
+ 1) == '.') && (*(punresolved
+ 2) == '\0' || (*(punresolved
+ 2) == '/')))
608 _rmlastpathtoken(path_resolved_so_far
);
610 presolvedsf
= ustrtoend(path_resolved_so_far
) - 1;
612 if (*(punresolved
+ 2) == '/')
620 /* a file or directory name may start with '.' */
621 if ((presolvedsf
= ustrtoend(path_resolved_so_far
)) > sentinel
)
622 return oslTranslateFileError(ENAMETOOLONG
);
624 ustrchrcat(*punresolved
++, path_resolved_so_far
);
626 if (*punresolved
== '\0' && !realpath_failed
)
628 ferr
= _osl_resolvepath(
629 path_resolved_so_far
,
632 if (ferr
!= osl_File_E_None
)
636 else if (*punresolved
== '/')
638 if ((presolvedsf
= ustrtoend(path_resolved_so_far
)) > sentinel
)
639 return oslTranslateFileError(ENAMETOOLONG
);
641 ustrchrcat(*punresolved
++, path_resolved_so_far
);
643 if (!realpath_failed
)
645 ferr
= _osl_resolvepath(
646 path_resolved_so_far
,
649 if (ferr
!= osl_File_E_None
)
652 if (!_islastchr(path_resolved_so_far
, '/'))
654 if ((presolvedsf
= ustrtoend(path_resolved_so_far
)) > sentinel
)
655 return oslTranslateFileError(ENAMETOOLONG
);
657 ustrchrcat('/', path_resolved_so_far
);
661 else // any other character
663 if ((presolvedsf
= ustrtoend(path_resolved_so_far
)) > sentinel
)
664 return oslTranslateFileError(ENAMETOOLONG
);
666 ustrchrcat(*punresolved
++, path_resolved_so_far
);
668 if (*punresolved
== '\0' && !realpath_failed
)
670 ferr
= _osl_resolvepath(
671 path_resolved_so_far
,
674 if (ferr
!= osl_File_E_None
)
680 sal_Int32 len
= rtl_ustr_getLength(path_resolved_so_far
);
682 OSL_ASSERT(len
< PATH_MAX
);
684 resolved_path
= OUString(path_resolved_so_far
, len
);
686 return osl_File_E_None
;
691 oslFileError
osl_getAbsoluteFileURL(
692 rtl_uString
* ustrBaseDirURL
,
693 rtl_uString
* ustrRelativeURL
,
694 rtl_uString
** pustrAbsoluteURL
)
696 /* Work around the below call to getSystemPathFromFileURL rejecting input
697 that starts with "/" (for whatever reason it behaves that way; but
698 changing that would start to break lots of tests at least) */
699 OUString
relUrl(ustrRelativeURL
);
700 if (relUrl
.startsWith("//"))
701 relUrl
= "file:" + relUrl
;
702 else if (relUrl
.startsWith("/"))
703 relUrl
= "file://" + relUrl
;
705 OUString unresolved_path
;
707 FileBase::RC frc
= FileBase::getSystemPathFromFileURL(relUrl
, unresolved_path
);
708 if (frc
!= FileBase::E_None
)
709 return oslFileError(frc
);
711 if (systemPathIsRelativePath(unresolved_path
))
714 oslFileError rc
= getSystemPathFromFileURL_Ex(ustrBaseDirURL
, &base_path
.pData
);
715 if (rc
!= osl_File_E_None
)
718 unresolved_path
= systemPathMakeAbsolutePath(base_path
, unresolved_path
);
721 OUString resolved_path
;
722 oslFileError rc
= osl_getAbsoluteFileURL_impl_(unresolved_path
, resolved_path
);
723 if (rc
== osl_File_E_None
)
725 rc
= osl_getFileURLFromSystemPath(resolved_path
.pData
, pustrAbsoluteURL
);
726 OSL_ASSERT(osl_File_E_None
== rc
);
732 namespace osl::detail
{
735 No separate error code if unicode to text conversion or getenv fails because for the
736 caller there is no difference why a file could not be found in $PATH
738 bool find_in_PATH(const OUString
& file_path
, OUString
& result
)
741 OUString
path("PATH");
744 if (osl_getEnvironment(path
.pData
, &env_path
.pData
) == osl_Process_E_None
)
745 bfound
= osl::searchPath(file_path
, env_path
, result
);
754 No separate error code if unicode to text conversion or getcwd fails because for the
755 caller there is no difference why a file could not be found in CDW
757 bool find_in_CWD(const OUString
& file_path
, OUString
& result
)
762 if (osl_getProcessWorkingDir(&cwd_url
.pData
) == osl_Process_E_None
)
765 FileBase::getSystemPathFromFileURL(cwd_url
, cwd
);
766 bfound
= osl::searchPath(file_path
, cwd
, result
);
771 bool find_in_searchPath(const OUString
& file_path
, rtl_uString
* search_path
, OUString
& result
)
773 return (search_path
&& osl::searchPath(file_path
, OUString(search_path
), result
));
778 oslFileError
osl_searchFileURL(rtl_uString
* ustrFilePath
, rtl_uString
* ustrSearchPath
, rtl_uString
** pustrURL
)
780 OSL_PRECOND(ustrFilePath
&& pustrURL
, "osl_searchFileURL: invalid parameter");
785 // try to interpret search path as file url else assume it's a system path list
786 rc
= FileBase::getSystemPathFromFileURL(ustrFilePath
, file_path
);
787 if (rc
== FileBase::E_INVAL
)
788 file_path
= ustrFilePath
;
789 else if (rc
!= FileBase::E_None
)
790 return oslFileError(rc
);
795 if (find_in_searchPath(file_path
, ustrSearchPath
, result
) ||
796 osl::detail::find_in_PATH(file_path
, result
) ||
797 find_in_CWD(file_path
, result
))
801 if (osl::realpath(result
, resolved
))
803 oslFileError osl_error
= osl_getFileURLFromSystemPath(resolved
.pData
, pustrURL
);
804 SAL_WARN_IF(osl_File_E_None
!= osl_error
, "sal.file", "osl_getFileURLFromSystemPath failed");
808 return bfound
? osl_File_E_None
: osl_File_E_NOENT
;
811 oslFileError
FileURLToPath(char * buffer
, size_t bufLen
, rtl_uString
* ustrFileURL
)
813 OString strSystemPath
;
814 oslFileError osl_error
= osl::detail::convertUrlToPathname(
815 OUString::unacquired(&ustrFileURL
), &strSystemPath
);
817 if(osl_error
!= osl_File_E_None
)
820 osl_systemPathRemoveSeparator(strSystemPath
.pData
);
822 if (o3tl::make_unsigned(strSystemPath
.getLength()) >= bufLen
) {
823 return osl_File_E_OVERFLOW
;
825 std::strcpy(buffer
, strSystemPath
.getStr());
830 int UnicodeToText( char * buffer
, size_t bufLen
, const sal_Unicode
* uniText
, sal_Int32 uniTextLen
)
832 sal_uInt32 nInfo
= 0;
833 sal_Size nSrcChars
= 0;
835 sal_Size nDestBytes
= UnicodeToTextConverter_Impl::getInstance().convert (
836 uniText
, uniTextLen
, buffer
, bufLen
,
837 OUSTRING_TO_OSTRING_CVTFLAGS
| RTL_UNICODETOTEXT_FLAGS_FLUSH
, &nInfo
, &nSrcChars
);
839 if( nInfo
& RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL
)
845 /* ensure trailing '\0' */
846 buffer
[nDestBytes
] = '\0';
852 class TextToUnicodeConverter_Impl
854 rtl_TextToUnicodeConverter m_converter
;
856 TextToUnicodeConverter_Impl()
857 : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding()))
860 ~TextToUnicodeConverter_Impl()
862 rtl_destroyTextToUnicodeConverter (m_converter
);
866 static TextToUnicodeConverter_Impl
& getInstance()
868 static TextToUnicodeConverter_Impl g_theConverter
;
869 return g_theConverter
;
873 char const * pSrcBuf
, sal_Size nSrcBytes
, sal_Unicode
* pDstBuf
, sal_Size nDstChars
,
874 sal_uInt32 nFlags
, sal_uInt32
* pInfo
, sal_Size
* pSrcCvtBytes
)
876 OSL_ASSERT(m_converter
!= nullptr);
877 return rtl_convertTextToUnicode (
878 m_converter
, nullptr, pSrcBuf
, nSrcBytes
, pDstBuf
, nDstChars
, nFlags
, pInfo
, pSrcCvtBytes
);
885 size_t text_buffer_size
,
886 sal_Unicode
* unic_text
,
887 sal_Int32 unic_text_buffer_size
)
889 sal_uInt32 nInfo
= 0;
890 sal_Size nSrcChars
= 0;
892 sal_Size nDestBytes
= TextToUnicodeConverter_Impl::getInstance().convert(
893 text
, text_buffer_size
, unic_text
, unic_text_buffer_size
,
894 OSTRING_TO_OUSTRING_CVTFLAGS
| RTL_TEXTTOUNICODE_FLAGS_FLUSH
, &nInfo
, &nSrcChars
);
896 if (nInfo
& RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL
)
902 /* ensure trailing '\0' */
903 unic_text
[nDestBytes
] = '\0';
907 oslFileError
osl::detail::convertUrlToPathname(OUString
const & url
, OString
* pathname
) {
908 assert(pathname
!= nullptr);
911 e
= getSystemPathFromFileUrl(url
, pathname
, true);
912 } catch (std::length_error
&) {
913 e
= osl_File_E_RANGE
;
915 if (e
== osl_File_E_None
&& !pathname
->startsWith("/")) {
916 e
= osl_File_E_INVAL
;
921 oslFileError
osl::detail::convertPathnameToUrl(OString
const & pathname
, OUString
* url
) {
922 assert(url
!= nullptr);
923 OUStringBuffer
buf(10+pathname
.getLength());
925 if (pathname
.startsWith("/")) {
927 // so if pathname should ever start with "//" that isn't mistaken for an authority
930 for (sal_Size convert
= pathname
.getLength();;) {
931 auto n
= std::max(convert
, sal_Size(PATH_MAX
)); // approximation of required converted size
932 OUStringBuffer
ubuf(static_cast<int>(n
));
933 auto s
= ubuf
.appendUninitialized(n
);
936 //TODO: context, for reliable treatment of DESTBUFFERTOSMALL:
937 n
= TextToUnicodeConverter_Impl::getInstance().convert(
938 pathname
.getStr() + pathname
.getLength() - convert
, convert
, s
, n
,
939 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
940 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR
| RTL_TEXTTOUNICODE_FLAGS_FLUSH
),
945 ubuf
.makeStringAndClear(), uriCharClass
.data(), rtl_UriEncodeIgnoreEscapes
,
946 RTL_TEXTENCODING_UTF8
));
947 assert(converted
<= convert
);
948 convert
-= converted
;
949 if ((info
& RTL_TEXTTOUNICODE_INFO_ERROR
) != 0) {
951 //TODO: see writeEscapeOctet in sal/rtl/uri.cxx
953 unsigned char c
= pathname
[pathname
.getLength() - convert
];
955 static sal_Unicode
const aHex
[16]
956 = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
957 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; /* '0'--'9', 'A'--'F' */
958 buf
.append(OUStringChar(aHex
[c
>> 4]) + OUStringChar(aHex
[c
& 15]));
962 assert((convert
== 0) == ((info
& RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL
) == 0));
963 if ((info
& RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL
) == 0) {
967 *url
= buf
.makeStringAndClear();
968 return osl_File_E_None
;
971 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */