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 .
22 #define _WIN32_WINNT 0x0500
23 #include "systools/win32/uwinapi.h"
26 #include <sal/macros.h>
27 #include "file_error.h"
29 #include "rtl/alloc.h"
30 #include "osl/diagnose.h"
32 #include "osl/mutex.h"
34 #include "path_helper.hxx"
39 #if OSL_DEBUG_LEVEL > 0
40 #define OSL_ENSURE_FILE( cond, msg, file ) ( (cond) ? (void)0 : _osl_warnFile( msg, file ) )
42 #define OSL_ENSURE_FILE( cond, msg, file ) ((void)0)
45 #define WSTR_SYSTEM_ROOT_PATH L"\\\\.\\"
46 #define WSTR_LONG_PATH_PREFIX L"\\\\?\\"
47 #define WSTR_LONG_PATH_PREFIX_UNC L"\\\\?\\UNC\\"
50 //##################################################################
52 //##################################################################
54 extern "C" oslMutex g_CurrentDirectoryMutex
; /* Initialized in dllentry.c */
55 oslMutex g_CurrentDirectoryMutex
= 0;
57 //#####################################################
58 static BOOL
IsValidFilePathComponent(
59 LPCTSTR lpComponent
, LPCTSTR
*lppComponentEnd
, DWORD dwFlags
)
61 LPCTSTR lpComponentEnd
= NULL
;
62 LPCTSTR lpCurrent
= lpComponent
;
63 BOOL fValid
= TRUE
; /* Assume success */
66 /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
68 while ( !lpComponentEnd
&& lpCurrent
&& lpCurrent
- lpComponent
< MAX_PATH
)
72 /* Both backslash and slash determine the end of a path component */
78 /* Component must not end with '.' or blank and can't be empty */
81 if ( dwFlags
& VALIDATEPATH_ALLOW_ELLIPSE
)
83 if ( (dwFlags
& VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
) ||
84 1 == lpCurrent
- lpComponent
)
86 /* Either do allow periods anywhere, or current directory */
87 lpComponentEnd
= lpCurrent
;
90 else if ( 2 == lpCurrent
- lpComponent
&& '.' == *lpComponent
)
92 /* Parent directory is O.K. */
93 lpComponentEnd
= lpCurrent
;
99 if ( dwFlags
& VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
)
100 lpComponentEnd
= lpCurrent
;
103 lpComponentEnd
= lpCurrent
- 1;
108 lpComponentEnd
= lpCurrent
;
112 /* '?' and '*' are valid wildcards but not valid file name characters */
115 if ( dwFlags
& VALIDATEPATH_ALLOW_WILDCARDS
)
117 /* The following characters are reserved */
123 lpComponentEnd
= lpCurrent
;
127 /* Characters below ASCII 32 are not allowed */
128 if ( *lpCurrent
< ' ' )
130 lpComponentEnd
= lpCurrent
;
135 cLast
= *lpCurrent
++;
138 /* If we don't reached the end of the component the length of the component was to long
139 ( See condition of while loop ) */
140 if ( !lpComponentEnd
)
143 lpComponentEnd
= lpCurrent
;
146 /* Test whether the component specifies a device name what is not allowed */
149 // This is very expensive. A lot of calls to _tcsicmp.
150 // in SRC6870m71 67.000 calls of this method while empty office start result into more than 1.500.00 calls of _tcsicmp!
151 // Possible optimizations
152 // - Array should be const static
153 // - Sorted array, use binary search
154 // - More intelligent check for com1-9, lpt1-9
155 // Maybe make szComponent upper case, don't search case intensitive
156 // Talked to HRO: Could be removed. Shouldn't be used in OOo, and if used for something like a filename, it will lead to an error anyway.
160 LPCTSTR alpDeviceNames[] =
187 TCHAR szComponent[MAX_PATH];
188 int nComponentLength;
192 // A device name with an extension is also invalid
193 lpDot = _tcschr( lpComponent, '.' );
195 if ( !lpDot || lpDot > lpComponentEnd )
196 nComponentLength = lpComponentEnd - lpComponent;
198 nComponentLength = lpDot - lpComponent;
200 _tcsncpy( szComponent, lpComponent, nComponentLength );
201 szComponent[nComponentLength] = 0;
203 for ( i = 0; i < sizeof( alpDeviceNames ) / sizeof(LPCTSTR); i++ )
205 if ( 0 == _tcsicmp( szComponent, alpDeviceNames[i] ) )
207 lpComponentEnd = lpComponent;
217 // Empty components are not allowed
218 if ( lpComponentEnd
- lpComponent
< 1 )
221 // If we reached the end of the string NULL is returned
222 else if ( !*lpComponentEnd
)
223 lpComponentEnd
= NULL
;
227 if ( lppComponentEnd
)
228 *lppComponentEnd
= lpComponentEnd
;
233 //#####################################################
234 #define CHARSET_SEPARATOR TEXT("\\/")
236 DWORD
IsValidFilePath(rtl_uString
*path
, LPCTSTR
*lppError
, DWORD dwFlags
, rtl_uString
**corrected
)
238 LPCTSTR lpszPath
= reinterpret_cast< LPCTSTR
>(path
->buffer
);
239 LPCTSTR lpComponent
= lpszPath
;
241 DWORD dwPathType
= PATHTYPE_ERROR
;
242 sal_Int32 nLength
= rtl_uString_getLength( path
);
244 if ( dwFlags
& VALIDATEPATH_ALLOW_RELATIVE
)
245 dwFlags
|= VALIDATEPATH_ALLOW_ELLIPSE
;
250 DWORD dwCandidatPathType
= PATHTYPE_ERROR
;
252 if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path
->buffer
, nLength
, reinterpret_cast<const sal_Unicode
*>(WSTR_LONG_PATH_PREFIX_UNC
), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC
) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC
) - 1 ) )
254 /* This is long path in UNC notation */
255 lpComponent
= lpszPath
+ SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC
) - 1;
256 dwCandidatPathType
= PATHTYPE_ABSOLUTE_UNC
| PATHTYPE_IS_LONGPATH
;
258 else if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path
->buffer
, nLength
, reinterpret_cast<const sal_Unicode
*>(WSTR_LONG_PATH_PREFIX
), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX
) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX
) - 1 ) )
260 /* This is long path */
261 lpComponent
= lpszPath
+ SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX
) - 1;
263 if ( _istalpha( lpComponent
[0] ) && ':' == lpComponent
[1] )
266 dwCandidatPathType
= PATHTYPE_ABSOLUTE_LOCAL
| PATHTYPE_IS_LONGPATH
;
269 else if ( 2 == _tcsspn( lpszPath
, CHARSET_SEPARATOR
) )
271 /* The UNC path notation */
272 lpComponent
= lpszPath
+ 2;
273 dwCandidatPathType
= PATHTYPE_ABSOLUTE_UNC
;
275 else if ( _istalpha( lpszPath
[0] ) && ':' == lpszPath
[1] )
277 /* Local path verification. Must start with <drive>: */
278 lpComponent
= lpszPath
+ 2;
279 dwCandidatPathType
= PATHTYPE_ABSOLUTE_LOCAL
;
282 if ( ( dwCandidatPathType
& PATHTYPE_MASK_TYPE
) == PATHTYPE_ABSOLUTE_UNC
)
284 fValid
= IsValidFilePathComponent( lpComponent
, &lpComponent
, VALIDATEPATH_ALLOW_ELLIPSE
);
286 /* So far we have a valid servername. Now let's see if we also have a network resource */
288 dwPathType
= dwCandidatPathType
;
292 if ( lpComponent
&& !*++lpComponent
)
297 dwPathType
|= PATHTYPE_IS_SERVER
;
301 /* Now test the network resource */
303 fValid
= IsValidFilePathComponent( lpComponent
, &lpComponent
, 0 );
305 /* If we now reached the end of the path, everything is O.K. */
308 if ( fValid
&& (!lpComponent
|| !*++lpComponent
) )
311 dwPathType
|= PATHTYPE_IS_VOLUME
;
316 else if ( ( dwCandidatPathType
& PATHTYPE_MASK_TYPE
) == PATHTYPE_ABSOLUTE_LOCAL
)
318 if ( 1 == _tcsspn( lpComponent
, CHARSET_SEPARATOR
) )
320 else if ( *lpComponent
)
323 dwPathType
= dwCandidatPathType
;
325 /* Now we are behind the backslash or it was a simple drive without backslash */
327 if ( fValid
&& !*lpComponent
)
330 dwPathType
|= PATHTYPE_IS_VOLUME
;
333 else if ( dwFlags
& VALIDATEPATH_ALLOW_RELATIVE
)
335 /* Can be a relative path */
336 lpComponent
= lpszPath
;
338 /* Relative path can start with a backslash */
340 if ( 1 == _tcsspn( lpComponent
, CHARSET_SEPARATOR
) )
347 dwPathType
= PATHTYPE_RELATIVE
;
351 /* Anything else is an error */
353 lpComponent
= lpszPath
;
356 /* Now validate each component of the path */
357 while ( fValid
&& lpComponent
)
359 // Correct path by merging consecutive slashes:
360 if (*lpComponent
== '\\' && corrected
!= NULL
) {
361 sal_Int32 i
= lpComponent
- lpszPath
;
362 rtl_uString_newReplaceStrAt(corrected
, path
, i
, 1, NULL
);
363 //TODO: handle out-of-memory
364 lpszPath
= reinterpret_cast< LPCTSTR
>((*corrected
)->buffer
);
365 lpComponent
= lpszPath
+ i
;
368 fValid
= IsValidFilePathComponent( lpComponent
, &lpComponent
, dwFlags
| VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
);
370 if ( fValid
&& lpComponent
)
374 /* If the string behind the backslash is empty, we've done */
381 /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
382 if ( fValid
&& !( dwPathType
& PATHTYPE_IS_LONGPATH
) && _tcslen( lpszPath
) >= MAX_PATH
)
385 lpComponent
= lpszPath
+ MAX_PATH
;
389 *lppError
= lpComponent
;
391 return fValid
? dwPathType
: PATHTYPE_ERROR
;
394 //#####################################################
395 static sal_Int32
PathRemoveFileSpec(LPTSTR lpPath
, LPTSTR lpFileName
, sal_Int32 nFileBufLen
)
397 sal_Int32 nRemoved
= 0;
402 LPTSTR lpLastBkSlash
= _tcsrchr( lpPath
, '\\' );
403 LPTSTR lpLastSlash
= _tcsrchr( lpPath
, '/' );
404 LPTSTR lpLastDelimiter
= lpLastSlash
> lpLastBkSlash
? lpLastSlash
: lpLastBkSlash
;
406 if ( lpLastDelimiter
)
408 sal_Int32 nDelLen
= _tcslen( lpLastDelimiter
);
411 if ( lpLastDelimiter
> lpPath
&& *(lpLastDelimiter
- 1) != ':' )
413 *lpLastDelimiter
= 0;
418 else if ( nDelLen
&& nDelLen
- 1 < nFileBufLen
)
420 _tcscpy( lpFileName
, lpLastDelimiter
+ 1 );
421 *(++lpLastDelimiter
) = 0;
422 nRemoved
= nDelLen
- 1;
430 //#####################################################
431 // Undocumented in SHELL32.DLL ordinal 32
432 static LPTSTR
PathAddBackslash(LPTSTR lpPath
, sal_Int32 nBufLen
)
434 LPTSTR lpEndPath
= NULL
;
438 int nLen
= _tcslen(lpPath
);
440 if ( !nLen
|| ( lpPath
[nLen
-1] != '\\' && lpPath
[nLen
-1] != '/' && nLen
< nBufLen
- 1 ) )
442 lpEndPath
= lpPath
+ nLen
;
450 //#####################################################
451 // Same as GetLongPathName but also 95/NT4
452 static DWORD
GetCaseCorrectPathNameEx(
453 LPTSTR lpszPath
, // path buffer to convert
454 DWORD cchBuffer
, // size of path buffer
456 BOOL bCheckExistence
)
458 ::osl::LongPathBuffer
< WCHAR
> szFile( MAX_PATH
+ 1 );
459 sal_Int32 nRemoved
= PathRemoveFileSpec( lpszPath
, szFile
, MAX_PATH
+ 1 );
460 sal_Int32 nLastStepRemoved
= nRemoved
;
461 while ( nLastStepRemoved
&& szFile
[0] == 0 )
464 nLastStepRemoved
= PathRemoveFileSpec( lpszPath
, szFile
, MAX_PATH
+ 1 );
465 nRemoved
+= nLastStepRemoved
;
470 BOOL bSkipThis
= FALSE
;
472 if ( 0 == _tcscmp( szFile
, TEXT("..") ) )
477 else if ( 0 == _tcscmp( szFile
, TEXT(".") ) )
481 else if ( nSkipLevels
)
489 if ( !GetCaseCorrectPathNameEx( lpszPath
, cchBuffer
, nSkipLevels
, bCheckExistence
) )
492 PathAddBackslash( lpszPath
, cchBuffer
);
494 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
497 if ( bCheckExistence
)
499 ::osl::LongPathBuffer
< WCHAR
> aShortPath( MAX_LONG_PATH
);
500 _tcscpy( aShortPath
, lpszPath
);
501 _tcscat( aShortPath
, szFile
);
503 WIN32_FIND_DATA aFindFileData
;
504 HANDLE hFind
= FindFirstFile( aShortPath
, &aFindFileData
);
506 if ( IsValidHandle(hFind
) )
508 _tcscat( lpszPath
, aFindFileData
.cFileName
[0] ? aFindFileData
.cFileName
: aFindFileData
.cAlternateFileName
);
517 /* add the segment name back */
518 _tcscat( lpszPath
, szFile
);
524 /* File specification can't be removed therefore the short path is either a drive
525 or a network share. If still levels to skip are left, the path specification
526 tries to travel below the file system root */
533 return _tcslen( lpszPath
);
536 //#####################################################
537 DWORD
GetCaseCorrectPathName(
538 LPCTSTR lpszShortPath
, // file name
539 LPTSTR lpszLongPath
, // path buffer
540 DWORD cchBuffer
, // size of path buffer
544 /* Special handling for "\\.\" as system root */
545 if ( lpszShortPath
&& 0 == wcscmp( lpszShortPath
, WSTR_SYSTEM_ROOT_PATH
) )
547 if ( cchBuffer
>= SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) )
549 wcscpy( lpszLongPath
, WSTR_SYSTEM_ROOT_PATH
);
550 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1;
554 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1;
557 else if ( lpszShortPath
)
559 if ( _tcslen( lpszShortPath
) <= cchBuffer
)
561 _tcscpy( lpszLongPath
, lpszShortPath
);
562 return GetCaseCorrectPathNameEx( lpszLongPath
, cchBuffer
, 0, bCheckExistence
);
570 //#############################################
571 static sal_Bool
_osl_decodeURL( rtl_String
* strUTF8
, rtl_uString
** pstrDecodedURL
)
574 const sal_Char
*pSrcEnd
;
575 const sal_Char
*pSrc
;
578 sal_Bool bValidEncoded
= sal_True
; /* Assume success */
580 /* The resulting decoded string length is shorter or equal to the source length */
582 nSrcLen
= rtl_string_getLength(strUTF8
);
583 pBuffer
= reinterpret_cast<sal_Char
*>(rtl_allocateMemory(nSrcLen
+ 1));
586 pSrc
= rtl_string_getStr(strUTF8
);
587 pSrcEnd
= pSrc
+ nSrcLen
;
589 /* Now decode the URL what should result in an UTF8 string */
590 while ( bValidEncoded
&& pSrc
< pSrcEnd
)
604 aChar
= (sal_Char
)strtoul( aToken
, NULL
, 16 );
606 /* The chars are path delimiters and must not be encoded */
608 if ( 0 == aChar
|| '\\' == aChar
|| '/' == aChar
|| ':' == aChar
)
609 bValidEncoded
= sal_False
;
624 rtl_string2UString( pstrDecodedURL
, pBuffer
, rtl_str_getLength(pBuffer
), RTL_TEXTENCODING_UTF8
, OUSTRING_TO_OSTRING_CVTFLAGS
);
625 OSL_ASSERT(*pstrDecodedURL
!= 0);
628 rtl_freeMemory( pBuffer
);
630 return bValidEncoded
;
633 //#############################################
634 static void _osl_encodeURL( rtl_uString
*strURL
, rtl_String
**pstrEncodedURL
)
636 /* Encode non ascii characters within the URL */
638 rtl_String
*strUTF8
= NULL
;
639 sal_Char
*pszEncodedURL
;
640 const sal_Char
*pURLScan
;
642 sal_Int32 nURLScanLen
;
643 sal_Int32 nURLScanCount
;
645 rtl_uString2String( &strUTF8
, rtl_uString_getStr( strURL
), rtl_uString_getLength( strURL
), RTL_TEXTENCODING_UTF8
, OUSTRING_TO_OSTRING_CVTFLAGS
);
647 pszEncodedURL
= (sal_Char
*) rtl_allocateMemory( (rtl_string_getLength( strUTF8
) * 3 + 1) * sizeof(sal_Char
) );
649 pURLDest
= pszEncodedURL
;
650 pURLScan
= rtl_string_getStr( strUTF8
);
651 nURLScanLen
= rtl_string_getLength( strUTF8
);
654 while ( nURLScanCount
< nURLScanLen
)
656 sal_Char cCurrent
= *pURLScan
;
660 if (!( ( cCurrent
>= 'a' && cCurrent
<= 'z' ) || ( cCurrent
>= 'A' && cCurrent
<= 'Z' ) || ( cCurrent
>= '0' && cCurrent
<= '9' ) ) )
662 sprintf( pURLDest
, "%%%02X", (unsigned char)cCurrent
);
685 *pURLDest
++ = cCurrent
;
697 rtl_string_release( strUTF8
);
698 rtl_string_newFromStr( pstrEncodedURL
, pszEncodedURL
);
699 rtl_freeMemory( pszEncodedURL
);
702 //#############################################
704 oslFileError
_osl_getSystemPathFromFileURL( rtl_uString
*strURL
, rtl_uString
**pustrPath
, sal_Bool bAllowRelative
)
706 rtl_String
*strUTF8
= NULL
;
707 rtl_uString
*strDecodedURL
= NULL
;
708 rtl_uString
*strTempPath
= NULL
;
709 const sal_Unicode
*pDecodedURL
;
710 sal_uInt32 nDecodedLen
;
711 sal_Bool bValidEncoded
;
712 oslFileError nError
= osl_File_E_INVAL
; /* Assume failure */
714 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
715 having a mixed encoded URL later */
717 rtl_uString2String( &strUTF8
, rtl_uString_getStr( strURL
), rtl_uString_getLength( strURL
), RTL_TEXTENCODING_UTF8
, OUSTRING_TO_OSTRING_CVTFLAGS
);
719 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
722 strUTF8
->length
== strURL
->length
||
723 0 != rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL
->buffer
, strURL
->length
, "file:\\\\", 7 )
724 ,"osl_getSystemPathFromFileURL: \"%s\" is not encoded !!!", strURL
);
726 bValidEncoded
= _osl_decodeURL( strUTF8
, &strDecodedURL
);
728 /* Release the encoded UTF8 string */
729 rtl_string_release( strUTF8
);
733 /* Replace backslashes and pipes */
735 rtl_uString_newReplace( &strDecodedURL
, strDecodedURL
, '/', '\\' );
736 rtl_uString_newReplace( &strDecodedURL
, strDecodedURL
, '|', ':' );
738 pDecodedURL
= rtl_uString_getStr( strDecodedURL
);
739 nDecodedLen
= rtl_uString_getLength( strDecodedURL
);
741 /* Must start with "file://" */
742 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\", 7 ) )
746 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\\\", 8 ) )
749 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\localhost\\", 17 ) ||
750 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\127.0.0.1\\", 17 )
756 /* Indicates local root */
757 if ( nDecodedLen
== nSkip
)
758 rtl_uString_newFromStr_WithLength( &strTempPath
, reinterpret_cast<const sal_Unicode
*>(WSTR_SYSTEM_ROOT_PATH
), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1 );
761 /* do not separate the directory and file case, so the maximal path lengs without prefix is MAX_PATH-12 */
762 if ( nDecodedLen
- nSkip
<= MAX_PATH
- 12 )
764 rtl_uString_newFromStr_WithLength( &strTempPath
, pDecodedURL
+ nSkip
, nDecodedLen
- nSkip
);
768 ::osl::LongPathBuffer
< sal_Unicode
> aBuf( MAX_LONG_PATH
);
769 sal_uInt32 nNewLen
= GetCaseCorrectPathName( reinterpret_cast<LPCTSTR
>(pDecodedURL
+ nSkip
),
770 ::osl::mingw_reinterpret_cast
<LPTSTR
>(aBuf
),
771 aBuf
.getBufSizeInSymbols(),
774 if ( nNewLen
<= MAX_PATH
- 12
775 || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
+ nSkip
, nDecodedLen
- nSkip
, reinterpret_cast<const sal_Unicode
*>(WSTR_SYSTEM_ROOT_PATH
), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1, SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1 )
776 || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
+ nSkip
, nDecodedLen
- nSkip
, reinterpret_cast<const sal_Unicode
*>(WSTR_LONG_PATH_PREFIX
), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX
) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX
) - 1 ) )
778 rtl_uString_newFromStr_WithLength( &strTempPath
, aBuf
, nNewLen
);
780 else if ( pDecodedURL
[nSkip
] == (sal_Unicode
)'\\' && pDecodedURL
[nSkip
+1] == (sal_Unicode
)'\\' )
782 /* it should be an UNC path, use the according prefix */
783 rtl_uString
*strSuffix
= NULL
;
784 rtl_uString
*strPrefix
= NULL
;
785 rtl_uString_newFromStr_WithLength( &strPrefix
, reinterpret_cast<const sal_Unicode
*>(WSTR_LONG_PATH_PREFIX_UNC
), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC
) - 1 );
786 rtl_uString_newFromStr_WithLength( &strSuffix
, aBuf
+ 2, nNewLen
- 2 );
788 rtl_uString_newConcat( &strTempPath
, strPrefix
, strSuffix
);
790 rtl_uString_release( strPrefix
);
791 rtl_uString_release( strSuffix
);
795 rtl_uString
*strSuffix
= NULL
;
796 rtl_uString
*strPrefix
= NULL
;
797 rtl_uString_newFromStr_WithLength( &strPrefix
, reinterpret_cast<const sal_Unicode
*>(WSTR_LONG_PATH_PREFIX
), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX
) - 1 );
798 rtl_uString_newFromStr_WithLength( &strSuffix
, aBuf
, nNewLen
);
800 rtl_uString_newConcat( &strTempPath
, strPrefix
, strSuffix
);
802 rtl_uString_release( strPrefix
);
803 rtl_uString_release( strSuffix
);
808 if ( IsValidFilePath( strTempPath
, NULL
, VALIDATEPATH_ALLOW_ELLIPSE
, &strTempPath
) )
809 nError
= osl_File_E_None
;
811 else if ( bAllowRelative
) /* This maybe a relative file URL */
813 /* In future the relative path could be converted to absolute if it is too long */
814 rtl_uString_assign( &strTempPath
, strDecodedURL
);
816 if ( IsValidFilePath( strTempPath
, NULL
, VALIDATEPATH_ALLOW_RELATIVE
| VALIDATEPATH_ALLOW_ELLIPSE
, &strTempPath
) )
817 nError
= osl_File_E_None
;
821 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not an absolute FileURL !!!", strURL );
827 rtl_uString_release( strDecodedURL
);
829 if ( osl_File_E_None
== nError
)
830 rtl_uString_assign( pustrPath
, strTempPath
);
833 rtl_uString_release( strTempPath
);
836 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not a FileURL !!!", strURL );
842 //#############################################
843 oslFileError
_osl_getFileURLFromSystemPath( rtl_uString
* strPath
, rtl_uString
** pstrURL
)
845 oslFileError nError
= osl_File_E_INVAL
; /* Assume failure */
846 rtl_uString
*strTempURL
= NULL
;
847 DWORD dwPathType
= PATHTYPE_ERROR
;
850 dwPathType
= IsValidFilePath(strPath
, NULL
, VALIDATEPATH_ALLOW_RELATIVE
, NULL
);
854 rtl_uString
*strTempPath
= NULL
;
856 if ( dwPathType
& PATHTYPE_IS_LONGPATH
)
858 rtl_uString
*strBuffer
= NULL
;
859 sal_uInt32 nIgnore
= 0;
860 sal_uInt32 nLength
= 0;
862 /* the path has the longpath prefix, lets remove it */
863 switch ( dwPathType
& PATHTYPE_MASK_TYPE
)
865 case PATHTYPE_ABSOLUTE_UNC
:
866 nIgnore
= SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC
) - 1;
867 OSL_ENSURE( nIgnore
== 8, "Unexpected long path UNC prefix!" );
869 /* generate the normal UNC path */
870 nLength
= rtl_uString_getLength( strPath
);
871 rtl_uString_newFromStr_WithLength( &strBuffer
, strPath
->buffer
+ nIgnore
- 2, nLength
- nIgnore
+ 2 );
872 strBuffer
->buffer
[0] = '\\';
874 rtl_uString_newReplace( &strTempPath
, strBuffer
, '\\', '/' );
875 rtl_uString_release( strBuffer
);
878 case PATHTYPE_ABSOLUTE_LOCAL
:
879 nIgnore
= SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX
) - 1;
880 OSL_ENSURE( nIgnore
== 4, "Unexpected long path prefix!" );
882 /* generate the normal path */
883 nLength
= rtl_uString_getLength( strPath
);
884 rtl_uString_newFromStr_WithLength( &strBuffer
, strPath
->buffer
+ nIgnore
, nLength
- nIgnore
);
886 rtl_uString_newReplace( &strTempPath
, strBuffer
, '\\', '/' );
887 rtl_uString_release( strBuffer
);
891 OSL_FAIL( "Unexpected long path format!" );
892 rtl_uString_newReplace( &strTempPath
, strPath
, '\\', '/' );
898 /* Replace backslashes */
899 rtl_uString_newReplace( &strTempPath
, strPath
, '\\', '/' );
902 switch ( dwPathType
& PATHTYPE_MASK_TYPE
)
904 case PATHTYPE_RELATIVE
:
905 rtl_uString_assign( &strTempURL
, strTempPath
);
906 nError
= osl_File_E_None
;
908 case PATHTYPE_ABSOLUTE_UNC
:
909 rtl_uString_newFromAscii( &strTempURL
, "file:" );
910 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
911 nError
= osl_File_E_None
;
913 case PATHTYPE_ABSOLUTE_LOCAL
:
914 rtl_uString_newFromAscii( &strTempURL
, "file:///" );
915 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
916 nError
= osl_File_E_None
;
922 /* Release temp path */
923 rtl_uString_release( strTempPath
);
926 if ( osl_File_E_None
== nError
)
928 rtl_String
*strEncodedURL
= NULL
;
931 _osl_encodeURL( strTempURL
, &strEncodedURL
);
933 /* Provide URL via unicode string */
934 rtl_string2UString( pstrURL
, rtl_string_getStr(strEncodedURL
), rtl_string_getLength(strEncodedURL
), RTL_TEXTENCODING_ASCII_US
, OUSTRING_TO_OSTRING_CVTFLAGS
);
935 OSL_ASSERT(*pstrURL
!= 0);
936 rtl_string_release( strEncodedURL
);
939 /* Release temp URL */
941 rtl_uString_release( strTempURL
);
944 OSL_ENSURE_FILE( !nError, "osl_getFileURLFromSystemPath: \"%s\" is not a systemPath !!!", strPath );
949 //#####################################################
950 oslFileError SAL_CALL
osl_getFileURLFromSystemPath(
951 rtl_uString
* ustrPath
, rtl_uString
** pustrURL
)
953 return _osl_getFileURLFromSystemPath( ustrPath
, pustrURL
);
956 //#####################################################
957 oslFileError SAL_CALL
osl_getSystemPathFromFileURL(
958 rtl_uString
*ustrURL
, rtl_uString
**pustrPath
)
960 return _osl_getSystemPathFromFileURL( ustrURL
, pustrPath
, sal_True
);
963 //#####################################################
964 oslFileError SAL_CALL
osl_searchFileURL(
965 rtl_uString
*ustrFileName
,
966 rtl_uString
*ustrSystemSearchPath
,
967 rtl_uString
**pustrPath
)
969 rtl_uString
*ustrUNCPath
= NULL
;
970 rtl_uString
*ustrSysPath
= NULL
;
973 /* First try to interpret the file name as an URL even a relative one */
974 error
= _osl_getSystemPathFromFileURL( ustrFileName
, &ustrUNCPath
, sal_True
);
976 /* So far we either have an UNC path or something invalid
977 Now create a system path */
978 if ( osl_File_E_None
== error
)
979 error
= _osl_getSystemPathFromFileURL( ustrUNCPath
, &ustrSysPath
, sal_True
);
981 if ( osl_File_E_None
== error
)
985 LPTSTR lpBuffer
= NULL
;
988 /* Repeat calling SearchPath ...
989 Start with MAX_PATH for the buffer. In most cases this
990 will be enough and does not force the loop to runtwice */
995 /* If search path is empty use a NULL pointer instead according to MSDN documentation of SearchPath */
996 LPCTSTR lpszSearchPath
= ustrSystemSearchPath
&& ustrSystemSearchPath
->length
? reinterpret_cast<LPCTSTR
>(ustrSystemSearchPath
->buffer
) : NULL
;
997 LPCTSTR lpszSearchFile
= reinterpret_cast<LPCTSTR
>(ustrSysPath
->buffer
);
999 /* Allocate space for buffer according to previous returned count of required chars */
1000 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
1001 nBufferLength
= dwResult
+ 1;
1002 lpBuffer
= lpBuffer
?
1003 reinterpret_cast<LPTSTR
>(rtl_reallocateMemory(lpBuffer
, nBufferLength
* sizeof(TCHAR
))) :
1004 reinterpret_cast<LPTSTR
>(rtl_allocateMemory(nBufferLength
* sizeof(TCHAR
)));
1006 dwResult
= SearchPath( lpszSearchPath
, lpszSearchFile
, NULL
, nBufferLength
, lpBuffer
, &lpszFilePart
);
1007 } while ( dwResult
&& dwResult
>= nBufferLength
);
1009 /* ... until an error occures or buffer is large enough.
1010 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
1014 rtl_uString_newFromStr( &ustrSysPath
, reinterpret_cast<const sal_Unicode
*>(lpBuffer
) );
1015 error
= osl_getFileURLFromSystemPath( ustrSysPath
, pustrPath
);
1019 WIN32_FIND_DATA aFindFileData
;
1022 /* something went wrong, perhaps the path was absolute */
1023 error
= oslTranslateFileError( GetLastError() );
1025 hFind
= FindFirstFile( reinterpret_cast<LPCTSTR
>(ustrSysPath
->buffer
), &aFindFileData
);
1027 if ( IsValidHandle(hFind
) )
1029 error
= osl_getFileURLFromSystemPath( ustrSysPath
, pustrPath
);
1034 rtl_freeMemory( lpBuffer
);
1038 rtl_uString_release( ustrSysPath
);
1041 rtl_uString_release( ustrUNCPath
);
1046 //#####################################################
1048 oslFileError SAL_CALL
osl_getAbsoluteFileURL( rtl_uString
* ustrBaseURL
, rtl_uString
* ustrRelativeURL
, rtl_uString
** pustrAbsoluteURL
)
1050 oslFileError eError
;
1051 rtl_uString
*ustrRelSysPath
= NULL
;
1052 rtl_uString
*ustrBaseSysPath
= NULL
;
1054 if ( ustrBaseURL
&& ustrBaseURL
->length
)
1056 eError
= _osl_getSystemPathFromFileURL( ustrBaseURL
, &ustrBaseSysPath
, sal_False
);
1057 OSL_ENSURE( osl_File_E_None
== eError
, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
1059 eError
= _osl_getSystemPathFromFileURL( ustrRelativeURL
, &ustrRelSysPath
, sal_True
);
1063 eError
= _osl_getSystemPathFromFileURL( ustrRelativeURL
, &ustrRelSysPath
, sal_False
);
1064 OSL_ENSURE( osl_File_E_None
== eError
, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
1069 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
1070 ::osl::LongPathBuffer
< sal_Unicode
> aCurrentDir( MAX_LONG_PATH
);
1071 LPTSTR lpFilePart
= NULL
;
1075 Bad, bad hack, this only works if the base path
1076 really exists which is not necessary according
1078 The whole FileURL implementation should be merged
1079 with the rtl/uri class.
1081 if ( ustrBaseSysPath
)
1083 osl_acquireMutex( g_CurrentDirectoryMutex
);
1085 GetCurrentDirectoryW( aCurrentDir
.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast
<LPWSTR
>(aCurrentDir
) );
1086 SetCurrentDirectoryW( reinterpret_cast<LPCWSTR
>(ustrBaseSysPath
->buffer
) );
1089 dwResult
= GetFullPathNameW( reinterpret_cast<LPCWSTR
>(ustrRelSysPath
->buffer
), aBuffer
.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast
<LPWSTR
>(aBuffer
), &lpFilePart
);
1091 if ( ustrBaseSysPath
)
1093 SetCurrentDirectoryW( ::osl::mingw_reinterpret_cast
<LPCWSTR
>(aCurrentDir
) );
1095 osl_releaseMutex( g_CurrentDirectoryMutex
);
1100 if ( dwResult
>= aBuffer
.getBufSizeInSymbols() )
1101 eError
= osl_File_E_INVAL
;
1104 rtl_uString
*ustrAbsSysPath
= NULL
;
1106 rtl_uString_newFromStr( &ustrAbsSysPath
, aBuffer
);
1108 eError
= osl_getFileURLFromSystemPath( ustrAbsSysPath
, pustrAbsoluteURL
);
1110 if ( ustrAbsSysPath
)
1111 rtl_uString_release( ustrAbsSysPath
);
1115 eError
= oslTranslateFileError( GetLastError() );
1118 if ( ustrBaseSysPath
)
1119 rtl_uString_release( ustrBaseSysPath
);
1121 if ( ustrRelSysPath
)
1122 rtl_uString_release( ustrRelSysPath
);
1127 //#####################################################
1128 oslFileError SAL_CALL
osl_getCanonicalName( rtl_uString
*strRequested
, rtl_uString
**strValid
)
1130 rtl_uString_newFromString(strValid
, strRequested
);
1131 return osl_File_E_None
;
1134 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */