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 #include "systools/win32/uwinapi.h"
25 #include <sal/macros.h>
26 #include "file_error.h"
28 #include "rtl/alloc.h"
29 #include "osl/diagnose.h"
31 #include "osl/mutex.h"
33 #include "path_helper.hxx"
38 #if OSL_DEBUG_LEVEL > 0
39 #define OSL_ENSURE_FILE( cond, msg, file ) ( (cond) ? (void)0 : _osl_warnFile( msg, file ) )
41 #define OSL_ENSURE_FILE( cond, msg, file ) ((void)0)
44 #define WSTR_SYSTEM_ROOT_PATH L"\\\\.\\"
45 #define WSTR_LONG_PATH_PREFIX L"\\\\?\\"
46 #define WSTR_LONG_PATH_PREFIX_UNC L"\\\\?\\UNC\\"
49 //##################################################################
51 //##################################################################
53 extern "C" oslMutex g_CurrentDirectoryMutex
; /* Initialized in dllentry.c */
54 oslMutex g_CurrentDirectoryMutex
= 0;
56 //#####################################################
57 static BOOL
IsValidFilePathComponent(
58 LPCTSTR lpComponent
, LPCTSTR
*lppComponentEnd
, DWORD dwFlags
)
60 LPCTSTR lpComponentEnd
= NULL
;
61 LPCTSTR lpCurrent
= lpComponent
;
62 BOOL fValid
= TRUE
; /* Assume success */
65 /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
67 while ( !lpComponentEnd
&& lpCurrent
&& lpCurrent
- lpComponent
< MAX_PATH
)
71 /* Both backslash and slash determine the end of a path component */
77 /* Component must not end with '.' or blank and can't be empty */
80 if ( dwFlags
& VALIDATEPATH_ALLOW_ELLIPSE
)
82 if ( (dwFlags
& VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
) ||
83 1 == lpCurrent
- lpComponent
)
85 /* Either do allow periods anywhere, or current directory */
86 lpComponentEnd
= lpCurrent
;
89 else if ( 2 == lpCurrent
- lpComponent
&& '.' == *lpComponent
)
91 /* Parent directory is O.K. */
92 lpComponentEnd
= lpCurrent
;
98 if ( dwFlags
& VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
)
99 lpComponentEnd
= lpCurrent
;
102 lpComponentEnd
= lpCurrent
- 1;
107 lpComponentEnd
= lpCurrent
;
111 /* '?' and '*' are valid wildcards but not valid file name characters */
114 if ( dwFlags
& VALIDATEPATH_ALLOW_WILDCARDS
)
116 /* The following characters are reserved */
122 lpComponentEnd
= lpCurrent
;
126 /* Characters below ASCII 32 are not allowed */
127 if ( *lpCurrent
< ' ' )
129 lpComponentEnd
= lpCurrent
;
134 cLast
= *lpCurrent
++;
137 /* If we don't reached the end of the component the length of the component was to long
138 ( See condition of while loop ) */
139 if ( !lpComponentEnd
)
142 lpComponentEnd
= lpCurrent
;
145 /* Test whether the component specifies a device name what is not allowed */
148 // This is very expensive. A lot of calls to _tcsicmp.
149 // in SRC6870m71 67.000 calls of this method while empty office start result into more than 1.500.00 calls of _tcsicmp!
150 // Possible optimizations
151 // - Array should be const static
152 // - Sorted array, use binary search
153 // - More intelligent check for com1-9, lpt1-9
154 // Maybe make szComponent upper case, don't search case intensitive
155 // 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.
159 LPCTSTR alpDeviceNames[] =
186 TCHAR szComponent[MAX_PATH];
187 int nComponentLength;
191 // A device name with an extension is also invalid
192 lpDot = _tcschr( lpComponent, '.' );
194 if ( !lpDot || lpDot > lpComponentEnd )
195 nComponentLength = lpComponentEnd - lpComponent;
197 nComponentLength = lpDot - lpComponent;
199 _tcsncpy( szComponent, lpComponent, nComponentLength );
200 szComponent[nComponentLength] = 0;
202 for ( i = 0; i < sizeof( alpDeviceNames ) / sizeof(LPCTSTR); i++ )
204 if ( 0 == _tcsicmp( szComponent, alpDeviceNames[i] ) )
206 lpComponentEnd = lpComponent;
216 // Empty components are not allowed
217 if ( lpComponentEnd
- lpComponent
< 1 )
220 // If we reached the end of the string NULL is returned
221 else if ( !*lpComponentEnd
)
222 lpComponentEnd
= NULL
;
226 if ( lppComponentEnd
)
227 *lppComponentEnd
= lpComponentEnd
;
232 //#####################################################
233 #define CHARSET_SEPARATOR TEXT("\\/")
235 DWORD
IsValidFilePath(rtl_uString
*path
, LPCTSTR
*lppError
, DWORD dwFlags
, rtl_uString
**corrected
)
237 LPCTSTR lpszPath
= reinterpret_cast< LPCTSTR
>(path
->buffer
);
238 LPCTSTR lpComponent
= lpszPath
;
240 DWORD dwPathType
= PATHTYPE_ERROR
;
241 sal_Int32 nLength
= rtl_uString_getLength( path
);
243 if ( dwFlags
& VALIDATEPATH_ALLOW_RELATIVE
)
244 dwFlags
|= VALIDATEPATH_ALLOW_ELLIPSE
;
249 DWORD dwCandidatPathType
= PATHTYPE_ERROR
;
251 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 ) )
253 /* This is long path in UNC notation */
254 lpComponent
= lpszPath
+ SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC
) - 1;
255 dwCandidatPathType
= PATHTYPE_ABSOLUTE_UNC
| PATHTYPE_IS_LONGPATH
;
257 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 ) )
259 /* This is long path */
260 lpComponent
= lpszPath
+ SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX
) - 1;
262 if ( _istalpha( lpComponent
[0] ) && ':' == lpComponent
[1] )
265 dwCandidatPathType
= PATHTYPE_ABSOLUTE_LOCAL
| PATHTYPE_IS_LONGPATH
;
268 else if ( 2 == _tcsspn( lpszPath
, CHARSET_SEPARATOR
) )
270 /* The UNC path notation */
271 lpComponent
= lpszPath
+ 2;
272 dwCandidatPathType
= PATHTYPE_ABSOLUTE_UNC
;
274 else if ( _istalpha( lpszPath
[0] ) && ':' == lpszPath
[1] )
276 /* Local path verification. Must start with <drive>: */
277 lpComponent
= lpszPath
+ 2;
278 dwCandidatPathType
= PATHTYPE_ABSOLUTE_LOCAL
;
281 if ( ( dwCandidatPathType
& PATHTYPE_MASK_TYPE
) == PATHTYPE_ABSOLUTE_UNC
)
283 fValid
= IsValidFilePathComponent( lpComponent
, &lpComponent
, VALIDATEPATH_ALLOW_ELLIPSE
);
285 /* So far we have a valid servername. Now let's see if we also have a network resource */
287 dwPathType
= dwCandidatPathType
;
291 if ( lpComponent
&& !*++lpComponent
)
296 dwPathType
|= PATHTYPE_IS_SERVER
;
300 /* Now test the network resource */
302 fValid
= IsValidFilePathComponent( lpComponent
, &lpComponent
, 0 );
304 /* If we now reached the end of the path, everything is O.K. */
307 if ( fValid
&& (!lpComponent
|| !*++lpComponent
) )
310 dwPathType
|= PATHTYPE_IS_VOLUME
;
315 else if ( ( dwCandidatPathType
& PATHTYPE_MASK_TYPE
) == PATHTYPE_ABSOLUTE_LOCAL
)
317 if ( 1 == _tcsspn( lpComponent
, CHARSET_SEPARATOR
) )
319 else if ( *lpComponent
)
322 dwPathType
= dwCandidatPathType
;
324 /* Now we are behind the backslash or it was a simple drive without backslash */
326 if ( fValid
&& !*lpComponent
)
329 dwPathType
|= PATHTYPE_IS_VOLUME
;
332 else if ( dwFlags
& VALIDATEPATH_ALLOW_RELATIVE
)
334 /* Can be a relative path */
335 lpComponent
= lpszPath
;
337 /* Relative path can start with a backslash */
339 if ( 1 == _tcsspn( lpComponent
, CHARSET_SEPARATOR
) )
346 dwPathType
= PATHTYPE_RELATIVE
;
350 /* Anything else is an error */
352 lpComponent
= lpszPath
;
355 /* Now validate each component of the path */
356 while ( fValid
&& lpComponent
)
358 // Correct path by merging consecutive slashes:
359 if (*lpComponent
== '\\' && corrected
!= NULL
) {
360 sal_Int32 i
= lpComponent
- lpszPath
;
361 rtl_uString_newReplaceStrAt(corrected
, path
, i
, 1, NULL
);
362 //TODO: handle out-of-memory
363 lpszPath
= reinterpret_cast< LPCTSTR
>((*corrected
)->buffer
);
364 lpComponent
= lpszPath
+ i
;
367 fValid
= IsValidFilePathComponent( lpComponent
, &lpComponent
, dwFlags
| VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
);
369 if ( fValid
&& lpComponent
)
373 /* If the string behind the backslash is empty, we've done */
380 /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
381 if ( fValid
&& !( dwPathType
& PATHTYPE_IS_LONGPATH
) && _tcslen( lpszPath
) >= MAX_PATH
)
384 lpComponent
= lpszPath
+ MAX_PATH
;
388 *lppError
= lpComponent
;
390 return fValid
? dwPathType
: PATHTYPE_ERROR
;
393 //#####################################################
394 static sal_Int32
PathRemoveFileSpec(LPTSTR lpPath
, LPTSTR lpFileName
, sal_Int32 nFileBufLen
)
396 sal_Int32 nRemoved
= 0;
401 LPTSTR lpLastBkSlash
= _tcsrchr( lpPath
, '\\' );
402 LPTSTR lpLastSlash
= _tcsrchr( lpPath
, '/' );
403 LPTSTR lpLastDelimiter
= lpLastSlash
> lpLastBkSlash
? lpLastSlash
: lpLastBkSlash
;
405 if ( lpLastDelimiter
)
407 sal_Int32 nDelLen
= _tcslen( lpLastDelimiter
);
410 if ( lpLastDelimiter
> lpPath
&& *(lpLastDelimiter
- 1) != ':' )
412 *lpLastDelimiter
= 0;
417 else if ( nDelLen
&& nDelLen
- 1 < nFileBufLen
)
419 _tcscpy( lpFileName
, lpLastDelimiter
+ 1 );
420 *(++lpLastDelimiter
) = 0;
421 nRemoved
= nDelLen
- 1;
429 //#####################################################
430 // Undocumented in SHELL32.DLL ordinal 32
431 static LPTSTR
PathAddBackslash(LPTSTR lpPath
, sal_Int32 nBufLen
)
433 LPTSTR lpEndPath
= NULL
;
437 int nLen
= _tcslen(lpPath
);
439 if ( !nLen
|| ( lpPath
[nLen
-1] != '\\' && lpPath
[nLen
-1] != '/' && nLen
< nBufLen
- 1 ) )
441 lpEndPath
= lpPath
+ nLen
;
449 //#####################################################
450 // Same as GetLongPathName but also 95/NT4
451 static DWORD
GetCaseCorrectPathNameEx(
452 LPTSTR lpszPath
, // path buffer to convert
453 DWORD cchBuffer
, // size of path buffer
455 BOOL bCheckExistence
)
457 ::osl::LongPathBuffer
< WCHAR
> szFile( MAX_PATH
+ 1 );
458 sal_Int32 nRemoved
= PathRemoveFileSpec( lpszPath
, szFile
, MAX_PATH
+ 1 );
459 sal_Int32 nLastStepRemoved
= nRemoved
;
460 while ( nLastStepRemoved
&& szFile
[0] == 0 )
463 nLastStepRemoved
= PathRemoveFileSpec( lpszPath
, szFile
, MAX_PATH
+ 1 );
464 nRemoved
+= nLastStepRemoved
;
469 BOOL bSkipThis
= FALSE
;
471 if ( 0 == _tcscmp( szFile
, TEXT("..") ) )
476 else if ( 0 == _tcscmp( szFile
, TEXT(".") ) )
480 else if ( nSkipLevels
)
488 if ( !GetCaseCorrectPathNameEx( lpszPath
, cchBuffer
, nSkipLevels
, bCheckExistence
) )
491 PathAddBackslash( lpszPath
, cchBuffer
);
493 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
496 if ( bCheckExistence
)
498 ::osl::LongPathBuffer
< WCHAR
> aShortPath( MAX_LONG_PATH
);
499 _tcscpy( aShortPath
, lpszPath
);
500 _tcscat( aShortPath
, szFile
);
502 WIN32_FIND_DATA aFindFileData
;
503 HANDLE hFind
= FindFirstFile( aShortPath
, &aFindFileData
);
505 if ( IsValidHandle(hFind
) )
507 _tcscat( lpszPath
, aFindFileData
.cFileName
[0] ? aFindFileData
.cFileName
: aFindFileData
.cAlternateFileName
);
516 /* add the segment name back */
517 _tcscat( lpszPath
, szFile
);
523 /* File specification can't be removed therefore the short path is either a drive
524 or a network share. If still levels to skip are left, the path specification
525 tries to travel below the file system root */
532 return _tcslen( lpszPath
);
535 //#####################################################
536 DWORD
GetCaseCorrectPathName(
537 LPCTSTR lpszShortPath
, // file name
538 LPTSTR lpszLongPath
, // path buffer
539 DWORD cchBuffer
, // size of path buffer
543 /* Special handling for "\\.\" as system root */
544 if ( lpszShortPath
&& 0 == wcscmp( lpszShortPath
, WSTR_SYSTEM_ROOT_PATH
) )
546 if ( cchBuffer
>= SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) )
548 wcscpy( lpszLongPath
, WSTR_SYSTEM_ROOT_PATH
);
549 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1;
553 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1;
556 else if ( lpszShortPath
)
558 if ( _tcslen( lpszShortPath
) <= cchBuffer
)
560 _tcscpy( lpszLongPath
, lpszShortPath
);
561 return GetCaseCorrectPathNameEx( lpszLongPath
, cchBuffer
, 0, bCheckExistence
);
569 //#############################################
570 static sal_Bool
_osl_decodeURL( rtl_String
* strUTF8
, rtl_uString
** pstrDecodedURL
)
573 const sal_Char
*pSrcEnd
;
574 const sal_Char
*pSrc
;
577 sal_Bool bValidEncoded
= sal_True
; /* Assume success */
579 /* The resulting decoded string length is shorter or equal to the source length */
581 nSrcLen
= rtl_string_getLength(strUTF8
);
582 pBuffer
= reinterpret_cast<sal_Char
*>(rtl_allocateMemory(nSrcLen
+ 1));
585 pSrc
= rtl_string_getStr(strUTF8
);
586 pSrcEnd
= pSrc
+ nSrcLen
;
588 /* Now decode the URL what should result in an UTF8 string */
589 while ( bValidEncoded
&& pSrc
< pSrcEnd
)
603 aChar
= (sal_Char
)strtoul( aToken
, NULL
, 16 );
605 /* The chars are path delimiters and must not be encoded */
607 if ( 0 == aChar
|| '\\' == aChar
|| '/' == aChar
|| ':' == aChar
)
608 bValidEncoded
= sal_False
;
623 rtl_string2UString( pstrDecodedURL
, pBuffer
, rtl_str_getLength(pBuffer
), RTL_TEXTENCODING_UTF8
, OUSTRING_TO_OSTRING_CVTFLAGS
);
624 OSL_ASSERT(*pstrDecodedURL
!= 0);
627 rtl_freeMemory( pBuffer
);
629 return bValidEncoded
;
632 //#############################################
633 static void _osl_encodeURL( rtl_uString
*strURL
, rtl_String
**pstrEncodedURL
)
635 /* Encode non ascii characters within the URL */
637 rtl_String
*strUTF8
= NULL
;
638 sal_Char
*pszEncodedURL
;
639 const sal_Char
*pURLScan
;
641 sal_Int32 nURLScanLen
;
642 sal_Int32 nURLScanCount
;
644 rtl_uString2String( &strUTF8
, rtl_uString_getStr( strURL
), rtl_uString_getLength( strURL
), RTL_TEXTENCODING_UTF8
, OUSTRING_TO_OSTRING_CVTFLAGS
);
646 pszEncodedURL
= (sal_Char
*) rtl_allocateMemory( (rtl_string_getLength( strUTF8
) * 3 + 1) * sizeof(sal_Char
) );
648 pURLDest
= pszEncodedURL
;
649 pURLScan
= rtl_string_getStr( strUTF8
);
650 nURLScanLen
= rtl_string_getLength( strUTF8
);
653 while ( nURLScanCount
< nURLScanLen
)
655 sal_Char cCurrent
= *pURLScan
;
659 if (!( ( cCurrent
>= 'a' && cCurrent
<= 'z' ) || ( cCurrent
>= 'A' && cCurrent
<= 'Z' ) || ( cCurrent
>= '0' && cCurrent
<= '9' ) ) )
661 sprintf( pURLDest
, "%%%02X", (unsigned char)cCurrent
);
684 *pURLDest
++ = cCurrent
;
696 rtl_string_release( strUTF8
);
697 rtl_string_newFromStr( pstrEncodedURL
, pszEncodedURL
);
698 rtl_freeMemory( pszEncodedURL
);
701 //#############################################
703 oslFileError
_osl_getSystemPathFromFileURL( rtl_uString
*strURL
, rtl_uString
**pustrPath
, sal_Bool bAllowRelative
)
705 rtl_String
*strUTF8
= NULL
;
706 rtl_uString
*strDecodedURL
= NULL
;
707 rtl_uString
*strTempPath
= NULL
;
708 const sal_Unicode
*pDecodedURL
;
709 sal_uInt32 nDecodedLen
;
710 sal_Bool bValidEncoded
;
711 oslFileError nError
= osl_File_E_INVAL
; /* Assume failure */
713 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
714 having a mixed encoded URL later */
716 rtl_uString2String( &strUTF8
, rtl_uString_getStr( strURL
), rtl_uString_getLength( strURL
), RTL_TEXTENCODING_UTF8
, OUSTRING_TO_OSTRING_CVTFLAGS
);
718 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
721 strUTF8
->length
== strURL
->length
||
722 0 != rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL
->buffer
, strURL
->length
, "file:\\\\", 7 )
723 ,"osl_getSystemPathFromFileURL: \"%s\" is not encoded !!!", strURL
);
725 bValidEncoded
= _osl_decodeURL( strUTF8
, &strDecodedURL
);
727 /* Release the encoded UTF8 string */
728 rtl_string_release( strUTF8
);
732 /* Replace backslashes and pipes */
734 rtl_uString_newReplace( &strDecodedURL
, strDecodedURL
, '/', '\\' );
735 rtl_uString_newReplace( &strDecodedURL
, strDecodedURL
, '|', ':' );
737 pDecodedURL
= rtl_uString_getStr( strDecodedURL
);
738 nDecodedLen
= rtl_uString_getLength( strDecodedURL
);
740 /* Must start with "file://" */
741 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\", 7 ) )
745 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\\\", 8 ) )
748 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\localhost\\", 17 ) ||
749 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\127.0.0.1\\", 17 )
755 /* Indicates local root */
756 if ( nDecodedLen
== nSkip
)
757 rtl_uString_newFromStr_WithLength( &strTempPath
, reinterpret_cast<const sal_Unicode
*>(WSTR_SYSTEM_ROOT_PATH
), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH
) - 1 );
760 /* do not separate the directory and file case, so the maximal path lengs without prefix is MAX_PATH-12 */
761 if ( nDecodedLen
- nSkip
<= MAX_PATH
- 12 )
763 rtl_uString_newFromStr_WithLength( &strTempPath
, pDecodedURL
+ nSkip
, nDecodedLen
- nSkip
);
767 ::osl::LongPathBuffer
< sal_Unicode
> aBuf( MAX_LONG_PATH
);
768 sal_uInt32 nNewLen
= GetCaseCorrectPathName( reinterpret_cast<LPCTSTR
>(pDecodedURL
+ nSkip
),
769 ::osl::mingw_reinterpret_cast
<LPTSTR
>(aBuf
),
770 aBuf
.getBufSizeInSymbols(),
773 if ( nNewLen
<= MAX_PATH
- 12
774 || 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 )
775 || 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 ) )
777 rtl_uString_newFromStr_WithLength( &strTempPath
, aBuf
, nNewLen
);
779 else if ( pDecodedURL
[nSkip
] == (sal_Unicode
)'\\' && pDecodedURL
[nSkip
+1] == (sal_Unicode
)'\\' )
781 /* it should be an UNC path, use the according prefix */
782 rtl_uString
*strSuffix
= NULL
;
783 rtl_uString
*strPrefix
= NULL
;
784 rtl_uString_newFromStr_WithLength( &strPrefix
, reinterpret_cast<const sal_Unicode
*>(WSTR_LONG_PATH_PREFIX_UNC
), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC
) - 1 );
785 rtl_uString_newFromStr_WithLength( &strSuffix
, aBuf
+ 2, nNewLen
- 2 );
787 rtl_uString_newConcat( &strTempPath
, strPrefix
, strSuffix
);
789 rtl_uString_release( strPrefix
);
790 rtl_uString_release( strSuffix
);
794 rtl_uString
*strSuffix
= NULL
;
795 rtl_uString
*strPrefix
= NULL
;
796 rtl_uString_newFromStr_WithLength( &strPrefix
, reinterpret_cast<const sal_Unicode
*>(WSTR_LONG_PATH_PREFIX
), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX
) - 1 );
797 rtl_uString_newFromStr_WithLength( &strSuffix
, aBuf
, nNewLen
);
799 rtl_uString_newConcat( &strTempPath
, strPrefix
, strSuffix
);
801 rtl_uString_release( strPrefix
);
802 rtl_uString_release( strSuffix
);
807 if ( IsValidFilePath( strTempPath
, NULL
, VALIDATEPATH_ALLOW_ELLIPSE
, &strTempPath
) )
808 nError
= osl_File_E_None
;
810 else if ( bAllowRelative
) /* This maybe a relative file URL */
812 /* In future the relative path could be converted to absolute if it is too long */
813 rtl_uString_assign( &strTempPath
, strDecodedURL
);
815 if ( IsValidFilePath( strTempPath
, NULL
, VALIDATEPATH_ALLOW_RELATIVE
| VALIDATEPATH_ALLOW_ELLIPSE
, &strTempPath
) )
816 nError
= osl_File_E_None
;
820 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not an absolute FileURL !!!", strURL );
826 rtl_uString_release( strDecodedURL
);
828 if ( osl_File_E_None
== nError
)
829 rtl_uString_assign( pustrPath
, strTempPath
);
832 rtl_uString_release( strTempPath
);
835 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not a FileURL !!!", strURL );
841 //#############################################
842 oslFileError
_osl_getFileURLFromSystemPath( rtl_uString
* strPath
, rtl_uString
** pstrURL
)
844 oslFileError nError
= osl_File_E_INVAL
; /* Assume failure */
845 rtl_uString
*strTempURL
= NULL
;
846 DWORD dwPathType
= PATHTYPE_ERROR
;
849 dwPathType
= IsValidFilePath(strPath
, NULL
, VALIDATEPATH_ALLOW_RELATIVE
, NULL
);
853 rtl_uString
*strTempPath
= NULL
;
855 if ( dwPathType
& PATHTYPE_IS_LONGPATH
)
857 rtl_uString
*strBuffer
= NULL
;
858 sal_uInt32 nIgnore
= 0;
859 sal_uInt32 nLength
= 0;
861 /* the path has the longpath prefix, lets remove it */
862 switch ( dwPathType
& PATHTYPE_MASK_TYPE
)
864 case PATHTYPE_ABSOLUTE_UNC
:
865 nIgnore
= SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC
) - 1;
866 OSL_ENSURE( nIgnore
== 8, "Unexpected long path UNC prefix!" );
868 /* generate the normal UNC path */
869 nLength
= rtl_uString_getLength( strPath
);
870 rtl_uString_newFromStr_WithLength( &strBuffer
, strPath
->buffer
+ nIgnore
- 2, nLength
- nIgnore
+ 2 );
871 strBuffer
->buffer
[0] = '\\';
873 rtl_uString_newReplace( &strTempPath
, strBuffer
, '\\', '/' );
874 rtl_uString_release( strBuffer
);
877 case PATHTYPE_ABSOLUTE_LOCAL
:
878 nIgnore
= SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX
) - 1;
879 OSL_ENSURE( nIgnore
== 4, "Unexpected long path prefix!" );
881 /* generate the normal path */
882 nLength
= rtl_uString_getLength( strPath
);
883 rtl_uString_newFromStr_WithLength( &strBuffer
, strPath
->buffer
+ nIgnore
, nLength
- nIgnore
);
885 rtl_uString_newReplace( &strTempPath
, strBuffer
, '\\', '/' );
886 rtl_uString_release( strBuffer
);
890 OSL_FAIL( "Unexpected long path format!" );
891 rtl_uString_newReplace( &strTempPath
, strPath
, '\\', '/' );
897 /* Replace backslashes */
898 rtl_uString_newReplace( &strTempPath
, strPath
, '\\', '/' );
901 switch ( dwPathType
& PATHTYPE_MASK_TYPE
)
903 case PATHTYPE_RELATIVE
:
904 rtl_uString_assign( &strTempURL
, strTempPath
);
905 nError
= osl_File_E_None
;
907 case PATHTYPE_ABSOLUTE_UNC
:
908 rtl_uString_newFromAscii( &strTempURL
, "file:" );
909 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
910 nError
= osl_File_E_None
;
912 case PATHTYPE_ABSOLUTE_LOCAL
:
913 rtl_uString_newFromAscii( &strTempURL
, "file:///" );
914 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
915 nError
= osl_File_E_None
;
921 /* Release temp path */
922 rtl_uString_release( strTempPath
);
925 if ( osl_File_E_None
== nError
)
927 rtl_String
*strEncodedURL
= NULL
;
930 _osl_encodeURL( strTempURL
, &strEncodedURL
);
932 /* Provide URL via unicode string */
933 rtl_string2UString( pstrURL
, rtl_string_getStr(strEncodedURL
), rtl_string_getLength(strEncodedURL
), RTL_TEXTENCODING_ASCII_US
, OUSTRING_TO_OSTRING_CVTFLAGS
);
934 OSL_ASSERT(*pstrURL
!= 0);
935 rtl_string_release( strEncodedURL
);
938 /* Release temp URL */
940 rtl_uString_release( strTempURL
);
943 OSL_ENSURE_FILE( !nError, "osl_getFileURLFromSystemPath: \"%s\" is not a systemPath !!!", strPath );
948 //#####################################################
949 oslFileError SAL_CALL
osl_getFileURLFromSystemPath(
950 rtl_uString
* ustrPath
, rtl_uString
** pustrURL
)
952 return _osl_getFileURLFromSystemPath( ustrPath
, pustrURL
);
955 //#####################################################
956 oslFileError SAL_CALL
osl_getSystemPathFromFileURL(
957 rtl_uString
*ustrURL
, rtl_uString
**pustrPath
)
959 return _osl_getSystemPathFromFileURL( ustrURL
, pustrPath
, sal_True
);
962 //#####################################################
963 oslFileError SAL_CALL
osl_searchFileURL(
964 rtl_uString
*ustrFileName
,
965 rtl_uString
*ustrSystemSearchPath
,
966 rtl_uString
**pustrPath
)
968 rtl_uString
*ustrUNCPath
= NULL
;
969 rtl_uString
*ustrSysPath
= NULL
;
972 /* First try to interpret the file name as an URL even a relative one */
973 error
= _osl_getSystemPathFromFileURL( ustrFileName
, &ustrUNCPath
, sal_True
);
975 /* So far we either have an UNC path or something invalid
976 Now create a system path */
977 if ( osl_File_E_None
== error
)
978 error
= _osl_getSystemPathFromFileURL( ustrUNCPath
, &ustrSysPath
, sal_True
);
980 if ( osl_File_E_None
== error
)
984 LPTSTR lpBuffer
= NULL
;
987 /* Repeat calling SearchPath ...
988 Start with MAX_PATH for the buffer. In most cases this
989 will be enough and does not force the loop to runtwice */
994 /* If search path is empty use a NULL pointer instead according to MSDN documentation of SearchPath */
995 LPCTSTR lpszSearchPath
= ustrSystemSearchPath
&& ustrSystemSearchPath
->length
? reinterpret_cast<LPCTSTR
>(ustrSystemSearchPath
->buffer
) : NULL
;
996 LPCTSTR lpszSearchFile
= reinterpret_cast<LPCTSTR
>(ustrSysPath
->buffer
);
998 /* Allocate space for buffer according to previous returned count of required chars */
999 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
1000 nBufferLength
= dwResult
+ 1;
1001 lpBuffer
= lpBuffer
?
1002 reinterpret_cast<LPTSTR
>(rtl_reallocateMemory(lpBuffer
, nBufferLength
* sizeof(TCHAR
))) :
1003 reinterpret_cast<LPTSTR
>(rtl_allocateMemory(nBufferLength
* sizeof(TCHAR
)));
1005 dwResult
= SearchPath( lpszSearchPath
, lpszSearchFile
, NULL
, nBufferLength
, lpBuffer
, &lpszFilePart
);
1006 } while ( dwResult
&& dwResult
>= nBufferLength
);
1008 /* ... until an error occurs or buffer is large enough.
1009 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
1013 rtl_uString_newFromStr( &ustrSysPath
, reinterpret_cast<const sal_Unicode
*>(lpBuffer
) );
1014 error
= osl_getFileURLFromSystemPath( ustrSysPath
, pustrPath
);
1018 WIN32_FIND_DATA aFindFileData
;
1021 /* something went wrong, perhaps the path was absolute */
1022 error
= oslTranslateFileError( GetLastError() );
1024 hFind
= FindFirstFile( reinterpret_cast<LPCTSTR
>(ustrSysPath
->buffer
), &aFindFileData
);
1026 if ( IsValidHandle(hFind
) )
1028 error
= osl_getFileURLFromSystemPath( ustrSysPath
, pustrPath
);
1033 rtl_freeMemory( lpBuffer
);
1037 rtl_uString_release( ustrSysPath
);
1040 rtl_uString_release( ustrUNCPath
);
1045 //#####################################################
1047 oslFileError SAL_CALL
osl_getAbsoluteFileURL( rtl_uString
* ustrBaseURL
, rtl_uString
* ustrRelativeURL
, rtl_uString
** pustrAbsoluteURL
)
1049 oslFileError eError
;
1050 rtl_uString
*ustrRelSysPath
= NULL
;
1051 rtl_uString
*ustrBaseSysPath
= NULL
;
1053 if ( ustrBaseURL
&& ustrBaseURL
->length
)
1055 eError
= _osl_getSystemPathFromFileURL( ustrBaseURL
, &ustrBaseSysPath
, sal_False
);
1056 OSL_ENSURE( osl_File_E_None
== eError
, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
1058 eError
= _osl_getSystemPathFromFileURL( ustrRelativeURL
, &ustrRelSysPath
, sal_True
);
1062 eError
= _osl_getSystemPathFromFileURL( ustrRelativeURL
, &ustrRelSysPath
, sal_False
);
1063 OSL_ENSURE( osl_File_E_None
== eError
, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
1068 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
1069 ::osl::LongPathBuffer
< sal_Unicode
> aCurrentDir( MAX_LONG_PATH
);
1070 LPTSTR lpFilePart
= NULL
;
1074 Bad, bad hack, this only works if the base path
1075 really exists which is not necessary according
1077 The whole FileURL implementation should be merged
1078 with the rtl/uri class.
1080 if ( ustrBaseSysPath
)
1082 osl_acquireMutex( g_CurrentDirectoryMutex
);
1084 GetCurrentDirectoryW( aCurrentDir
.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast
<LPWSTR
>(aCurrentDir
) );
1085 SetCurrentDirectoryW( reinterpret_cast<LPCWSTR
>(ustrBaseSysPath
->buffer
) );
1088 dwResult
= GetFullPathNameW( reinterpret_cast<LPCWSTR
>(ustrRelSysPath
->buffer
), aBuffer
.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast
<LPWSTR
>(aBuffer
), &lpFilePart
);
1090 if ( ustrBaseSysPath
)
1092 SetCurrentDirectoryW( ::osl::mingw_reinterpret_cast
<LPCWSTR
>(aCurrentDir
) );
1094 osl_releaseMutex( g_CurrentDirectoryMutex
);
1099 if ( dwResult
>= aBuffer
.getBufSizeInSymbols() )
1100 eError
= osl_File_E_INVAL
;
1103 rtl_uString
*ustrAbsSysPath
= NULL
;
1105 rtl_uString_newFromStr( &ustrAbsSysPath
, aBuffer
);
1107 eError
= osl_getFileURLFromSystemPath( ustrAbsSysPath
, pustrAbsoluteURL
);
1109 if ( ustrAbsSysPath
)
1110 rtl_uString_release( ustrAbsSysPath
);
1114 eError
= oslTranslateFileError( GetLastError() );
1117 if ( ustrBaseSysPath
)
1118 rtl_uString_release( ustrBaseSysPath
);
1120 if ( ustrRelSysPath
)
1121 rtl_uString_release( ustrRelSysPath
);
1126 //#####################################################
1127 oslFileError SAL_CALL
osl_getCanonicalName( rtl_uString
*strRequested
, rtl_uString
**strValid
)
1129 rtl_uString_newFromString(strValid
, strRequested
);
1130 return osl_File_E_None
;
1133 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */