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 <sal/config.h>
21 #include <sal/log.hxx>
26 #include <string_view>
28 #include <systools/win32/uwinapi.h>
30 #include "file_url.hxx"
31 #include "file_error.hxx"
33 #include <rtl/alloc.h>
34 #include <rtl/character.hxx>
35 #include <rtl/strbuf.hxx>
36 #include <rtl/ustring.hxx>
37 #include <rtl/ustrbuf.hxx>
38 #include <osl/mutex.h>
39 #include <o3tl/char16_t2wchar_t.hxx>
40 #include <o3tl/string_view.hxx>
42 #include "path_helper.hxx"
48 constexpr std::u16string_view WSTR_SYSTEM_ROOT_PATH
= u
"\\\\.\\";
49 constexpr std::u16string_view WSTR_LONG_PATH_PREFIX
= u
"\\\\?\\";
50 constexpr std::u16string_view WSTR_LONG_PATH_PREFIX_UNC
= u
"\\\\?\\UNC\\";
52 // Internal functions that expect only backslashes as path separators
54 bool startsWithDriveColon(std::u16string_view s
)
56 return s
.length() >= 2 && rtl::isAsciiAlpha(s
[0]) && s
[1] == ':';
59 bool startsWithDriveColonSlash(std::u16string_view s
)
61 return s
.length() >= 3 && startsWithDriveColon(s
) && s
[2] == '\\';
64 bool startsWithSlashSlash(std::u16string_view s
) { return o3tl::starts_with(s
, u
"\\\\"); }
66 // An absolute path starts either with \\ (an UNC or device path like \\.\ or \\?\)
67 // or with a ASCII alpha character followed by colon followed by backslash.
68 bool isAbsolute(std::u16string_view s
)
70 return startsWithSlashSlash(s
) || startsWithDriveColonSlash(s
);
73 bool onSameDrive(std::u16string_view s1
, std::u16string_view s2
)
75 assert(startsWithDriveColon(s1
) && startsWithDriveColon(s2
));
76 return rtl::toAsciiUpperCase(s1
[0]) == rtl::toAsciiUpperCase(s2
[0]) && s1
[1] == s2
[1];
79 sal_Int32
getRootLength(std::u16string_view path
)
81 assert(isAbsolute(path
));
83 if (startsWithSlashSlash(path
))
86 // 1. Device UNC: \\?\UNC\server\share or \\.\UNC\server\share
87 // 2. Non-device UNC: \\server\share
88 // 3. Device non-UNC: \\?\C: or \\.\C:
90 if (path
.length() > 3 && (path
[2] == '.' || path
[2] == '?') && path
[3] == '\\')
92 if (path
.substr(4, 4) == u
"UNC\\")
94 // \\?\UNC\server\share or \\.\UNC\server\share
101 assert(startsWithDriveColon(path
.substr(4)));
113 // \\?\UNC\server\share or \\.\UNC\server\share or \\server\share
114 assert(nResult
< path
.length() && path
[nResult
] != '\\');
115 // Skip server name and share name
116 for (int nSlashes
= 0; nResult
< path
.length(); ++nResult
)
118 if (path
[nResult
] == '\\' && ++nSlashes
== 2)
126 assert(startsWithDriveColon(path
));
129 return std::min(nResult
, path
.length());
132 std::u16string_view
pathView(std::u16string_view path
, bool bOnlyRoot
)
134 return bOnlyRoot
? path
.substr(0, getRootLength(path
)) : path
;
137 OUString
combinePath(std::u16string_view basePath
, std::u16string_view relPath
)
139 const bool needSep
= !o3tl::ends_with(basePath
, u
'\\');
140 const auto sSeparator
= needSep
? std::u16string_view(u
"\\") : std::u16string_view();
141 if (o3tl::starts_with(relPath
, u
'\\'))
142 relPath
.remove_prefix(1); // avoid two adjacent backslashes
143 return OUString::Concat(basePath
) + sSeparator
+ relPath
;
146 OUString
removeRelativeParts(const OUString
& p
)
148 const sal_Int32 rootPos
= getRootLength(p
);
149 OUStringBuffer
buf(p
.getLength());
150 buf
.append(p
.subView(0, rootPos
));
151 std::stack
<sal_Int32
> partPositions
;
152 bool bAfterSlash
= false;
153 for (sal_Int32 i
= rootPos
; i
< p
.getLength(); ++i
)
155 sal_Unicode c
= p
[i
];
158 if (i
+ 1 < p
.getLength() && p
[i
+ 1] == '.')
160 if (i
+ 2 == p
.getLength() || p
[i
+ 2] == '\\')
162 // 1. Skip current directory (\.\ or trailing \.)
163 ++i
; // process next slash: it may start another "\.\"
165 else if (p
[i
+ 2] == '.' && (i
+ 3 == p
.getLength() || p
[i
+ 3] == '\\'))
167 // 2. For parent directory (\..\), drop previous part and skip
168 if (bAfterSlash
&& partPositions
.size())
170 sal_Int32 nParentPos
= partPositions
.size() ? partPositions
.top() : rootPos
;
171 if (partPositions
.size())
173 buf
.truncate(nParentPos
);
174 bAfterSlash
= false; // we have just removed slash after parent part
175 i
+= 2; // process next slash: it may start another "\.\"
179 continue; // 3. Skip double backslashes (\\)
180 partPositions
.push(buf
.getLength());
188 return buf
.makeStringAndClear();
192 static bool IsValidFilePathComponent(
193 std::optional
<std::u16string_view
>& roComponent
,
197 auto lpComponentEnd
= roComponent
->end();
198 auto lpCurrent
= roComponent
->begin();
199 bool bValid
= lpCurrent
!= lpComponentEnd
; // Empty components are not allowed
200 sal_Unicode cLast
= 0;
204 /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
205 if (lpCurrent
- roComponent
->begin() >= MAX_PATH
)
211 switch ( *lpCurrent
)
213 /* Both backslash and slash determine the end of a path component */
219 /* Component must not end with '.' or blank and can't be empty */
222 if ( dwFlags
& VALIDATEPATH_ALLOW_ELLIPSE
)
224 if ( (dwFlags
& VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
) ||
225 1 == lpCurrent
- roComponent
->begin() )
227 /* Either do allow periods anywhere, or current directory */
228 lpComponentEnd
= lpCurrent
;
231 else if ( 2 == lpCurrent
- roComponent
->begin() && '.' == roComponent
->front() )
233 /* Parent directory is O.K. */
234 lpComponentEnd
= lpCurrent
;
241 if ( dwFlags
& VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
)
242 lpComponentEnd
= lpCurrent
;
247 lpComponentEnd
= lpCurrent
;
251 /* The following characters are reserved */
262 /* Characters below ASCII 32 are not allowed */
263 if ( *lpCurrent
< ' ' )
268 if (lpCurrent
!= lpComponentEnd
)
269 cLast
= *lpCurrent
++;
271 if (lpCurrent
== lpComponentEnd
)
277 // Empty components are not allowed
278 if (lpComponentEnd
- roComponent
->begin() < 1)
280 // If we reached the end of the string nullopt is returned
281 else if (lpComponentEnd
== roComponent
->end())
284 roComponent
->remove_prefix(lpComponentEnd
- roComponent
->begin());
290 static sal_Int32
countInitialSeparators(std::u16string_view path
) {
292 while (n
< path
.length() && (path
[n
] == '\\' || path
[n
] == '/'))
297 DWORD
IsValidFilePath(const OUString
& path
, DWORD dwFlags
, OUString
* corrected
)
299 std::optional
<std::u16string_view
> oComponent
= path
;
301 DWORD dwPathType
= PATHTYPE_ERROR
;
303 if ( dwFlags
& VALIDATEPATH_ALLOW_RELATIVE
)
304 dwFlags
|= VALIDATEPATH_ALLOW_ELLIPSE
;
306 DWORD dwCandidatPathType
= PATHTYPE_ERROR
;
308 if (path
.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX_UNC
))
310 /* This is long path in UNC notation */
311 oComponent
= path
.subView(WSTR_LONG_PATH_PREFIX_UNC
.size());
312 dwCandidatPathType
= PATHTYPE_ABSOLUTE_UNC
| PATHTYPE_IS_LONGPATH
;
314 else if (path
.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX
))
316 /* This is long path */
317 oComponent
= path
.subView(WSTR_LONG_PATH_PREFIX
.size());
319 if (startsWithDriveColon(*oComponent
))
321 oComponent
->remove_prefix(2);
322 dwCandidatPathType
= PATHTYPE_ABSOLUTE_LOCAL
| PATHTYPE_IS_LONGPATH
;
325 else if ( 2 == countInitialSeparators(path
) )
327 /* The UNC path notation */
328 oComponent
= path
.subView(2);
329 dwCandidatPathType
= PATHTYPE_ABSOLUTE_UNC
;
331 else if (startsWithDriveColon(path
))
333 /* Local path verification. Must start with <drive>: */
334 oComponent
= path
.subView(2);
335 dwCandidatPathType
= PATHTYPE_ABSOLUTE_LOCAL
;
338 if ( ( dwCandidatPathType
& PATHTYPE_MASK_TYPE
) == PATHTYPE_ABSOLUTE_UNC
)
340 bValid
= IsValidFilePathComponent(oComponent
, VALIDATEPATH_ALLOW_ELLIPSE
);
342 /* So far we have a valid servername. Now let's see if we also have a network resource */
344 dwPathType
= dwCandidatPathType
;
350 oComponent
->remove_prefix(1);
351 if (oComponent
->empty())
357 dwPathType
|= PATHTYPE_IS_SERVER
;
361 /* Now test the network resource */
363 bValid
= IsValidFilePathComponent(oComponent
, 0);
365 /* If we now reached the end of the path, everything is O.K. */
371 oComponent
->remove_prefix(1);
372 if (oComponent
->empty())
376 dwPathType
|= PATHTYPE_IS_VOLUME
;
381 else if ( ( dwCandidatPathType
& PATHTYPE_MASK_TYPE
) == PATHTYPE_ABSOLUTE_LOCAL
)
383 if (1 == countInitialSeparators(*oComponent
))
384 oComponent
->remove_prefix(1);
385 else if (!oComponent
->empty())
388 dwPathType
= dwCandidatPathType
;
390 /* Now we are behind the backslash or it was a simple drive without backslash */
392 if (bValid
&& oComponent
->empty())
395 dwPathType
|= PATHTYPE_IS_VOLUME
;
398 else if ( dwFlags
& VALIDATEPATH_ALLOW_RELATIVE
)
400 /* Can be a relative path */
403 /* Relative path can start with a backslash */
405 if (1 == countInitialSeparators(*oComponent
))
407 oComponent
->remove_prefix(1);
408 if (oComponent
->empty())
412 dwPathType
= PATHTYPE_RELATIVE
;
416 /* Anything else is an error */
420 /* Now validate each component of the path */
421 OUString lastCorrected
= path
;
422 while (bValid
&& oComponent
)
424 // Correct path by merging consecutive slashes:
425 if (o3tl::starts_with(*oComponent
, u
"\\") && corrected
!= nullptr) {
426 sal_Int32 i
= oComponent
->data() - lastCorrected
.getStr();
427 *corrected
= lastCorrected
.replaceAt(i
, 1, {});
428 //TODO: handle out-of-memory
429 lastCorrected
= *corrected
;
430 oComponent
= lastCorrected
.subView(i
);
433 bValid
= IsValidFilePathComponent(oComponent
, dwFlags
| VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
);
435 if (bValid
&& oComponent
)
437 oComponent
->remove_prefix(1);
439 /* If the string behind the backslash is empty, we've done */
441 if (oComponent
->empty())
446 /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
447 if (bValid
&& !(dwPathType
& PATHTYPE_IS_LONGPATH
) && path
.getLength() >= MAX_PATH
)
452 return bValid
? dwPathType
: PATHTYPE_ERROR
;
455 static std::optional
<OUString
> osl_decodeURL_(const OString
& sUTF8
)
459 bool bValidEncoded
= true; /* Assume success */
461 /* The resulting decoded string length is shorter or equal to the source length */
463 const sal_Int32 nSrcLen
= sUTF8
.getLength();
464 OStringBuffer
aBuffer(nSrcLen
+ 1);
466 pSrc
= sUTF8
.getStr();
467 pSrcEnd
= pSrc
+ nSrcLen
;
469 /* Now decode the URL what should result in a UTF-8 string */
470 while ( bValidEncoded
&& pSrc
< pSrcEnd
)
484 aChar
= static_cast<char>(strtoul( aToken
, nullptr, 16 ));
486 /* The chars are path delimiters and must not be encoded */
488 if ( 0 == aChar
|| '\\' == aChar
|| '/' == aChar
|| ':' == aChar
)
489 bValidEncoded
= false;
491 aBuffer
.append(aChar
);
497 bValidEncoded
= false;
500 aBuffer
.append(*pSrc
++);
508 return OStringToOUString(aBuffer
, RTL_TEXTENCODING_UTF8
);
511 static OUString
osl_encodeURL_(std::u16string_view sURL
)
513 /* Encode non ascii characters within the URL */
515 const char *pURLScan
;
516 sal_Int32 nURLScanLen
;
517 sal_Int32 nURLScanCount
;
519 OString sUTF8
= OUStringToOString(sURL
, RTL_TEXTENCODING_UTF8
);
521 OUStringBuffer
sEncodedURL(sUTF8
.getLength() * 3 + 1);
522 pURLScan
= sUTF8
.getStr();
523 nURLScanLen
= sUTF8
.getLength();
526 while ( nURLScanCount
< nURLScanLen
)
528 char cCurrent
= *pURLScan
;
532 if (!( ( cCurrent
>= 'a' && cCurrent
<= 'z' ) || ( cCurrent
>= 'A' && cCurrent
<= 'Z' ) || ( cCurrent
>= '0' && cCurrent
<= '9' ) ) )
535 sprintf( buf
, "%02X", static_cast<unsigned char>(cCurrent
) );
536 sEncodedURL
.append('%').appendAscii(buf
, 2);
559 sEncodedURL
.appendAscii(&cCurrent
, 1);
569 return sEncodedURL
.makeStringAndClear();
572 // A helper that makes sure that for existing part of the path, the case is correct.
573 // Unlike GetLongPathNameW that it wraps, this function does not require the path to exist.
574 static OUString
GetCaseCorrectPathName(std::u16string_view sysPath
)
576 // Prepare a null-terminated string first.
577 // Neither OUString, nor u16string_view are guaranteed to be null-terminated
578 osl::LongPathBuffer
<wchar_t> szPath(sysPath
.size() + WSTR_LONG_PATH_PREFIX_UNC
.size() + 1);
579 wchar_t* const pPath
= szPath
;
580 wchar_t* pEnd
= pPath
;
581 size_t sysPathOffset
= 0;
582 if (sysPath
.size() >= MAX_PATH
&& isAbsolute(sysPath
)
583 && !o3tl::starts_with(sysPath
, WSTR_LONG_PATH_PREFIX
))
585 // Allow GetLongPathNameW consume long paths
586 std::u16string_view prefix
= WSTR_LONG_PATH_PREFIX
;
587 if (startsWithSlashSlash(sysPath
))
589 sysPathOffset
= 2; // skip leading "\\"
590 prefix
= WSTR_LONG_PATH_PREFIX_UNC
;
592 pEnd
= std::copy(prefix
.begin(), prefix
.end(), pEnd
);
594 wchar_t* const pStart
= pEnd
;
595 pEnd
= std::copy(sysPath
.begin() + sysPathOffset
, sysPath
.end(), pStart
);
597 osl::LongPathBuffer
<wchar_t> aBuf(MAX_LONG_PATH
);
598 while (pEnd
> pStart
)
600 std::u16string_view
curPath(o3tl::toU(pPath
), pEnd
- pPath
);
601 if (curPath
== u
"\\\\" || curPath
== WSTR_SYSTEM_ROOT_PATH
602 || curPath
== WSTR_LONG_PATH_PREFIX
603 || o3tl::equalsIgnoreAsciiCase(curPath
, WSTR_LONG_PATH_PREFIX_UNC
))
604 break; // Do not check if the special path prefix exists itself
606 DWORD nNewLen
= GetLongPathNameW(pPath
, aBuf
, aBuf
.getBufSizeInSymbols());
610 const DWORD err
= GetLastError();
611 if (err
== ERROR_FILE_NOT_FOUND
|| err
== ERROR_PATH_NOT_FOUND
)
613 // Check the base path; skip possible trailing separator
614 size_t sepPos
= curPath
.substr(0, curPath
.size() - 1).rfind(u
'\\');
615 if (sepPos
!= std::u16string_view::npos
)
617 pEnd
= pPath
+ sepPos
;
624 SAL_WARN("sal.osl", "GetLongPathNameW: Windows error code "
625 << err
<< " processing path " << OUString(curPath
));
627 break; // All other errors, or no separators left
629 assert(nNewLen
< aBuf
.getBufSizeInSymbols());
630 // Combine the case-correct leading part with the non-existing trailing part
631 return OUString::Concat(std::u16string_view(o3tl::toU(aBuf
), nNewLen
))
632 + sysPath
.substr(pEnd
- pStart
+ sysPathOffset
);
634 return OUString(sysPath
); // We found no existing parts - just assume it's OK
637 oslFileError
osl_getSystemPathFromFileURL_(const OUString
& strURL
, rtl_uString
**pustrPath
, bool bAllowRelative
)
640 oslFileError nError
= osl_File_E_INVAL
; /* Assume failure */
642 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
643 having a mixed encoded URL later */
645 OString sUTF8
= OUStringToOString(strURL
, RTL_TEXTENCODING_UTF8
);
647 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
650 sUTF8
.getLength() != strURL
.getLength() &&
651 strURL
.matchIgnoreAsciiCase("file:\\")
653 ,"osl_getSystemPathFromFileURL: \"" << strURL
<< "\" is not encoded !!!");
655 if (auto sDecodedURL
= osl_decodeURL_(sUTF8
))
657 /* Replace backslashes and pipes */
659 sDecodedURL
= sDecodedURL
->replace('/', '\\').replace('|', ':');
661 /* Must start with "file:/" */
662 if ( sDecodedURL
->startsWithIgnoreAsciiCase("file:\\") )
666 if ( sDecodedURL
->startsWithIgnoreAsciiCase("file:\\\\\\") )
669 sDecodedURL
->startsWithIgnoreAsciiCase("file:\\\\localhost\\") ||
670 sDecodedURL
->startsWithIgnoreAsciiCase("file:\\\\127.0.0.1\\")
673 else if ( sDecodedURL
->startsWithIgnoreAsciiCase("file:\\\\") )
678 const sal_uInt32 nDecodedLen
= sDecodedURL
->getLength();
680 /* Indicates local root */
681 if ( nDecodedLen
== nSkip
)
682 sTempPath
= WSTR_SYSTEM_ROOT_PATH
;
685 /* do not separate the directory and file case, so the maximal path length without prefix is MAX_PATH-12 */
686 if ( nDecodedLen
- nSkip
<= MAX_PATH
- 12 )
688 sTempPath
= sDecodedURL
->subView(nSkip
);
692 sDecodedURL
= GetCaseCorrectPathName(sDecodedURL
->subView(nSkip
));
693 if (sDecodedURL
->getLength() <= MAX_PATH
- 12
694 || sDecodedURL
->startsWith(WSTR_SYSTEM_ROOT_PATH
)
695 || sDecodedURL
->startsWith(WSTR_LONG_PATH_PREFIX
))
697 sTempPath
= *sDecodedURL
;
699 else if (sDecodedURL
->startsWith("\\\\"))
701 /* it should be an UNC path, use the according prefix */
702 sTempPath
= OUString::Concat(WSTR_LONG_PATH_PREFIX_UNC
) + sDecodedURL
->subView(2);
706 sTempPath
= WSTR_LONG_PATH_PREFIX
+ *sDecodedURL
;
711 if (IsValidFilePath(sTempPath
, VALIDATEPATH_ALLOW_ELLIPSE
, &sTempPath
))
712 nError
= osl_File_E_None
;
714 else if ( bAllowRelative
) /* This maybe a relative file URL */
716 /* In future the relative path could be converted to absolute if it is too long */
717 sTempPath
= *sDecodedURL
;
719 if (IsValidFilePath(sTempPath
, VALIDATEPATH_ALLOW_RELATIVE
| VALIDATEPATH_ALLOW_ELLIPSE
, &sTempPath
))
720 nError
= osl_File_E_None
;
723 SAL_INFO_IF(nError
, "sal.osl",
724 "osl_getSystemPathFromFileURL: \"" << strURL
<< "\" is not an absolute FileURL");
728 if ( osl_File_E_None
== nError
)
729 rtl_uString_assign(pustrPath
, sTempPath
.pData
);
731 SAL_INFO_IF(nError
, "sal.osl",
732 "osl_getSystemPathFromFileURL: \"" << strURL
<< "\" is not a FileURL");
737 oslFileError
osl_getFileURLFromSystemPath( rtl_uString
* strPath
, rtl_uString
** pstrURL
)
739 oslFileError nError
= osl_File_E_INVAL
; /* Assume failure */
741 DWORD dwPathType
= PATHTYPE_ERROR
;
744 dwPathType
= IsValidFilePath(OUString::unacquired(&strPath
), VALIDATEPATH_ALLOW_RELATIVE
, nullptr);
749 const OUString
& sPath
= OUString::unacquired(&strPath
);
751 if ( dwPathType
& PATHTYPE_IS_LONGPATH
)
753 /* the path has the longpath prefix, lets remove it */
754 switch ( dwPathType
& PATHTYPE_MASK_TYPE
)
756 case PATHTYPE_ABSOLUTE_UNC
:
757 static_assert(WSTR_LONG_PATH_PREFIX_UNC
.size() == 8,
758 "Unexpected long path UNC prefix!");
760 /* generate the normal UNC path */
761 sTempPath
= "\\\\" + sPath
.copy(8).replace('\\', '/');
764 case PATHTYPE_ABSOLUTE_LOCAL
:
765 static_assert(WSTR_LONG_PATH_PREFIX
.size() == 4,
766 "Unexpected long path prefix!");
768 /* generate the normal path */
769 sTempPath
= sPath
.copy(4).replace('\\', '/');
773 OSL_FAIL( "Unexpected long path format!" );
774 sTempPath
= sPath
.replace('\\', '/');
780 /* Replace backslashes */
781 sTempPath
= sPath
.replace('\\', '/');
784 switch ( dwPathType
& PATHTYPE_MASK_TYPE
)
786 case PATHTYPE_RELATIVE
:
787 sTempURL
= sTempPath
;
788 nError
= osl_File_E_None
;
790 case PATHTYPE_ABSOLUTE_UNC
:
791 sTempURL
= "file:" + sTempPath
;
792 nError
= osl_File_E_None
;
794 case PATHTYPE_ABSOLUTE_LOCAL
:
795 sTempURL
= "file:///" + sTempPath
;
796 nError
= osl_File_E_None
;
803 if ( osl_File_E_None
== nError
)
806 rtl_uString_assign(pstrURL
, osl_encodeURL_(sTempURL
).pData
);
807 OSL_ASSERT(*pstrURL
!= nullptr);
810 SAL_INFO_IF(nError
, "sal.osl",
811 "osl_getFileURLFromSystemPath: \"" << OUString::unacquired(&strPath
) << "\" is not a systemPath");
815 oslFileError SAL_CALL
osl_getSystemPathFromFileURL(
816 rtl_uString
*ustrURL
, rtl_uString
**pustrPath
)
818 return osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL
), pustrPath
, true);
821 oslFileError SAL_CALL
osl_searchFileURL(
822 rtl_uString
*ustrFileName
,
823 rtl_uString
*ustrSystemSearchPath
,
824 rtl_uString
**pustrPath
)
826 OUString ustrUNCPath
;
827 OUString ustrSysPath
;
830 /* First try to interpret the file name as a URL even a relative one */
831 error
= osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileName
), &ustrUNCPath
.pData
, true);
833 /* So far we either have an UNC path or something invalid
834 Now create a system path */
835 if ( osl_File_E_None
== error
)
836 error
= osl_getSystemPathFromFileURL_(ustrUNCPath
, &ustrSysPath
.pData
, true);
838 if ( osl_File_E_None
== error
)
842 LPWSTR lpBuffer
= nullptr;
845 /* Repeat calling SearchPath ...
846 Start with MAX_PATH for the buffer. In most cases this
847 will be enough and does not force the loop to run twice */
852 /* If search path is empty use a nullptr pointer instead according to MSDN documentation of SearchPath */
853 LPCWSTR lpszSearchPath
= ustrSystemSearchPath
&& ustrSystemSearchPath
->length
? o3tl::toW(ustrSystemSearchPath
->buffer
) : nullptr;
854 LPCWSTR lpszSearchFile
= o3tl::toW(ustrSysPath
.getStr());
856 /* Allocate space for buffer according to previous returned count of required chars */
857 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
858 nBufferLength
= dwResult
+ 1;
859 lpBuffer
= lpBuffer
?
860 static_cast<LPWSTR
>(realloc(lpBuffer
, nBufferLength
* sizeof(WCHAR
))) :
861 static_cast<LPWSTR
>(malloc(nBufferLength
* sizeof(WCHAR
)));
863 dwResult
= SearchPathW( lpszSearchPath
, lpszSearchFile
, nullptr, nBufferLength
, lpBuffer
, &lpszFilePart
);
864 } while ( dwResult
&& dwResult
>= nBufferLength
);
866 /* ... until an error occurs or buffer is large enough.
867 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
871 ustrSysPath
= o3tl::toU(lpBuffer
);
872 error
= osl_getFileURLFromSystemPath(ustrSysPath
.pData
, pustrPath
);
876 WIN32_FIND_DATAW aFindFileData
;
879 /* something went wrong, perhaps the path was absolute */
880 error
= oslTranslateFileError( GetLastError() );
882 hFind
= FindFirstFileW(o3tl::toW(ustrSysPath
.getStr()), &aFindFileData
);
884 if ( IsValidHandle(hFind
) )
886 error
= osl_getFileURLFromSystemPath(ustrSysPath
.pData
, pustrPath
);
897 oslFileError SAL_CALL
osl_getAbsoluteFileURL( rtl_uString
* ustrBaseURL
, rtl_uString
* ustrRelativeURL
, rtl_uString
** pustrAbsoluteURL
)
899 oslFileError eError
= osl_File_E_None
;
900 OUString ustrRelSysPath
;
901 OUString ustrBaseSysPath
;
903 if ( ustrBaseURL
&& ustrBaseURL
->length
)
905 eError
= osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrBaseURL
), &ustrBaseSysPath
.pData
, false);
906 OSL_ENSURE( osl_File_E_None
== eError
, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
908 if (eError
== osl_File_E_None
)
910 eError
= osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrRelativeURL
), &ustrRelSysPath
.pData
,
911 !ustrBaseSysPath
.isEmpty());
912 OSL_ENSURE( osl_File_E_None
== eError
, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
917 OUString sResultPath
;
919 The whole FileURL implementation should be merged
920 with the rtl/uri class.
922 // If ustrRelSysPath is absolute, we don't need ustrBaseSysPath.
923 if (!ustrBaseSysPath
.isEmpty() && !isAbsolute(ustrRelSysPath
))
925 // ustrBaseSysPath is known here to be a valid absolute path -> its first two characters
926 // are ASCII (either alpha + colon, or double backslashes)
928 // Don't use SetCurrentDirectoryW together with GetFullPathNameW, because:
929 // (a) it needs synchronization and may affect threads that may access relative paths;
930 // (b) it would give wrong results for non-existing base path (allowed by RFC2396).
932 if (startsWithDriveColon(ustrRelSysPath
))
934 // Special case: a path relative to a specific drive's current directory.
935 // Should we error out here?
937 // If ustrBaseSysPath is on the same drive as ustrRelSysPath, then take base path
938 // as is; otherwise, use current directory on ustrRelSysPath's drive as base path
939 if (onSameDrive(ustrRelSysPath
, ustrBaseSysPath
))
941 sResultPath
= combinePath(ustrBaseSysPath
, ustrRelSysPath
.subView(2));
945 // Call GetFullPathNameW to get current directory on ustrRelSysPath's drive
946 wchar_t baseDrive
[3] = { ustrRelSysPath
[0], ':' }; // just "C:"
947 osl::LongPathBuffer
<wchar_t> aBuf(MAX_LONG_PATH
);
949 = GetFullPathNameW(baseDrive
, aBuf
.getBufSizeInSymbols(), aBuf
, nullptr);
952 if (dwResult
>= aBuf
.getBufSizeInSymbols())
953 eError
= osl_File_E_INVAL
;
955 sResultPath
= combinePath(o3tl::toU(aBuf
), ustrRelSysPath
.subView(2));
958 eError
= oslTranslateFileError(GetLastError());
963 // Is this a rooted relative path (starting with a backslash)?
964 // Then we need only root from base. E.g.,
965 // ustrBaseSysPath is "\\server\share\path1\" and ustrRelSysPath is "\path2\to\file"
966 // => \\server\share\path2\to\file
967 // ustrBaseSysPath is "D:\path1\" and ustrRelSysPath is "\path2\to\file"
968 // => D:\path2\to\file
969 auto sBaseView(pathView(ustrBaseSysPath
, ustrRelSysPath
.startsWith("\\")));
970 sResultPath
= combinePath(sBaseView
, ustrRelSysPath
);
974 sResultPath
= ustrRelSysPath
;
976 if (eError
== osl_File_E_None
)
978 sResultPath
= removeRelativeParts(sResultPath
);
979 eError
= osl_getFileURLFromSystemPath(sResultPath
.pData
, pustrAbsoluteURL
);
986 oslFileError SAL_CALL
osl_getCanonicalName( rtl_uString
*strRequested
, rtl_uString
**strValid
)
988 rtl_uString_newFromString(strValid
, strRequested
);
989 return osl_File_E_None
;
992 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */