bump product version to 4.1.6.2
[LibreOffice.git] / sal / osl / unx / file_url.cxx
blob03de113eb335b7e9871bb3d29fbf6918f6f6ef33
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include "file_url.h"
23 #include "system.h"
25 #include <limits.h>
26 #include <errno.h>
27 #include <strings.h>
28 #include <unistd.h>
30 #include "osl/file.hxx"
31 #include <osl/security.h>
32 #include <osl/diagnose.h>
33 #include <osl/thread.h>
34 #include <osl/process.h>
36 #include <rtl/uri.h>
37 #include <rtl/ustring.hxx>
38 #include <rtl/ustrbuf.h>
39 #include "rtl/textcvt.h"
41 #include "file_error_transl.h"
42 #include "file_path_helper.hxx"
44 #include "uunxapi.hxx"
46 /***************************************************
48 General note
50 This file contains the part that handles File URLs.
52 File URLs as scheme specific notion of URIs
53 (RFC2396) may be handled platform independend, but
54 will not in osl which is considered wrong.
55 Future version of osl should handle File URLs this
56 way. In rtl/uri there is already an URI parser etc.
57 so this code should be consolidated.
59 **************************************************/
60 /************************************************************************
61 * ToDo
63 * Fix osl_getCanonicalName
65 ***********************************************************************/
68 /***************************************************
69 * namespace directives
70 **************************************************/
72 using namespace osl;
74 /***************************************************
75 * constants
76 **************************************************/
78 const sal_Unicode UNICHAR_SLASH = ((sal_Unicode)'/');
79 const sal_Unicode UNICHAR_COLON = ((sal_Unicode)':');
80 const sal_Unicode UNICHAR_DOT = ((sal_Unicode)'.');
82 /******************************************************************************
84 * Exported Module Functions
86 *****************************************************************************/
88 /* a slightly modified version of Pchar in rtl/source/uri.c */
89 const sal_Bool uriCharClass[128] =
91 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Pchar but without encoding slashes */
92 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
93 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* !"#$%&'()*+,-./ */
94 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, /* 0123456789:;<=>? */
95 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ABCDEFGHIJKLMNO */
96 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* PQRSTUVWXYZ[\]^_ */
97 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* `abcdefghijklmno */
98 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 /* pqrstuvwxyz{|}~ */
102 /* check for top wrong usage strings */
104 static sal_Bool findWrongUsage( const sal_Unicode *path, sal_Int32 len )
106 rtl_uString *pTmp = NULL;
107 sal_Bool bRet;
109 rtl_uString_newFromStr_WithLength( &pTmp, path, len );
111 rtl_ustr_toAsciiLowerCase_WithLength( pTmp->buffer, pTmp->length );
113 bRet = ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "ftp://", 6 ) ) ||
114 ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "http://", 7 ) ) ||
115 ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "vnd.sun.star", 12 ) ) ||
116 ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "private:", 8 ) ) ||
117 ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "slot:", 5) );
119 rtl_uString_release( pTmp );
120 return bRet;
124 /****************************************************************************/
125 /* osl_getCanonicalName */
126 /****************************************************************************/
128 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString* ustrFileURL, rtl_uString** pustrValidURL )
130 OSL_FAIL("osl_getCanonicalName not implemented");
132 rtl_uString_newFromString(pustrValidURL, ustrFileURL);
133 return osl_File_E_None;
136 /****************************************************************************/
137 /* osl_getSystemPathFromFileURL */
138 /****************************************************************************/
140 oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath )
142 sal_Int32 nIndex;
143 rtl_uString * pTmp = NULL;
145 sal_Unicode encodedSlash[3] = { '%', '2', 'F' };
146 sal_Unicode protocolDelimiter[3] = { ':', '/', '/' };
148 /* temporary hack: if already system path, return ustrFileURL */
150 if( (sal_Unicode) '/' == ustrFileURL->buffer[0] )
152 OSL_FAIL( "osl_getSystemPathFromFileURL: input is already system path" );
153 rtl_uString_assign( pustrSystemPath, ustrFileURL );
154 return osl_File_E_None;
158 /* a valid file url may not start with '/' */
159 if( ( 0 == ustrFileURL->length ) || ( (sal_Unicode) '/' == ustrFileURL->buffer[0] ) )
161 return osl_File_E_INVAL;
164 /* Check for non file:// protocols */
166 nIndex = rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, protocolDelimiter, 3 );
167 if ( -1 != nIndex && (4 != nIndex || 0 != rtl_ustr_ascii_shortenedCompare_WithLength( ustrFileURL->buffer, ustrFileURL->length,"file", 4 ) ) )
169 return osl_File_E_INVAL;
172 /* search for encoded slashes (%2F) and decode every single token if we find one */
174 nIndex = 0;
176 if( -1 != rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, encodedSlash, 3 ) )
178 rtl_uString * ustrPathToken = NULL;
179 sal_Int32 nOffset = 7;
183 nOffset += nIndex;
185 /* break url down in '/' divided tokens tokens */
186 nIndex = rtl_ustr_indexOfChar_WithLength( ustrFileURL->buffer + nOffset, ustrFileURL->length - nOffset, (sal_Unicode) '/' );
188 /* copy token to new string */
189 rtl_uString_newFromStr_WithLength( &ustrPathToken, ustrFileURL->buffer + nOffset,
190 -1 == nIndex ? ustrFileURL->length - nOffset : nIndex++ );
192 /* decode token */
193 rtl_uriDecode( ustrPathToken, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp );
195 /* the result should not contain any '/' */
196 if( -1 != rtl_ustr_indexOfChar_WithLength( pTmp->buffer, pTmp->length, (sal_Unicode) '/' ) )
198 rtl_uString_release( pTmp );
199 rtl_uString_release( ustrPathToken );
201 return osl_File_E_INVAL;
204 } while( -1 != nIndex );
206 /* release temporary string and restore index variable */
207 rtl_uString_release( ustrPathToken );
208 nIndex = 0;
211 /* protocol and server should not be encoded, so decode the whole string */
212 rtl_uriDecode( ustrFileURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp );
214 /* check if file protocol specified */
215 /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */
216 if( 7 <= pTmp->length )
218 rtl_uString * pProtocol = NULL;
219 rtl_uString_newFromStr_WithLength( &pProtocol, pTmp->buffer, 7 );
221 /* protocol is case insensitive */
222 rtl_ustr_toAsciiLowerCase_WithLength( pProtocol->buffer, pProtocol->length );
224 if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pProtocol->buffer, pProtocol->length,"file://", 7 ) )
225 nIndex = 7;
227 rtl_uString_release( pProtocol );
230 /* skip "localhost" or "127.0.0.1" if "file://" is specified */
231 /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */
232 if( nIndex && ( 10 <= pTmp->length - nIndex ) )
234 rtl_uString * pServer = NULL;
235 rtl_uString_newFromStr_WithLength( &pServer, pTmp->buffer + nIndex, 10 );
237 /* server is case insensitive */
238 rtl_ustr_toAsciiLowerCase_WithLength( pServer->buffer, pServer->length );
240 if( ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"localhost/", 10 ) ) ||
241 ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"127.0.0.1/", 10 ) ) )
243 /* don't exclude the '/' */
244 nIndex += 9;
247 rtl_uString_release( pServer );
250 if( nIndex )
251 rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + nIndex, pTmp->length - nIndex );
253 /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
254 if( (sal_Unicode) '~' == pTmp->buffer[0] )
256 /* check if another user is specified */
257 if( ( 1 == pTmp->length ) || ( (sal_Unicode)'/' == pTmp->buffer[1] ) )
259 rtl_uString *pTmp2 = NULL;
261 /* osl_getHomeDir returns file URL */
262 osl_getHomeDir( osl_getCurrentSecurity(), &pTmp2 );
264 /* remove "file://" prefix */
265 rtl_uString_newFromStr_WithLength( &pTmp2, pTmp2->buffer + 7, pTmp2->length - 7 );
267 /* replace '~' in original string */
268 rtl_uString_newReplaceStrAt( &pTmp, pTmp, 0, 1, pTmp2 );
269 rtl_uString_release( pTmp2 );
272 else
274 /* FIXME: replace ~user with users home directory */
275 return osl_File_E_INVAL;
279 /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */
281 OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) );
284 *pustrSystemPath = pTmp;
285 return osl_File_E_None;
288 /****************************************************************************/
289 /* osl_getFileURLFromSystemPath */
290 /****************************************************************************/
292 oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL )
294 static const sal_Unicode pDoubleSlash[2] = { '/', '/' };
296 rtl_uString *pTmp = NULL;
297 sal_Int32 nIndex;
299 if( 0 == ustrSystemPath->length )
300 return osl_File_E_INVAL;
302 /* temporary hack: if already file url, return ustrSystemPath */
304 if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file:", 5 ) )
307 if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file://", 7 ) )
309 OSL_FAIL( "osl_getFileURLFromSystemPath: input is already file URL" );
310 rtl_uString_assign( pustrFileURL, ustrSystemPath );
312 else
314 rtl_uString *pTmp2 = NULL;
316 OSL_FAIL( "osl_getFileURLFromSystemPath: input is wrong file URL" );
317 rtl_uString_newFromStr_WithLength( pustrFileURL, ustrSystemPath->buffer + 5, ustrSystemPath->length - 5 );
318 rtl_uString_newFromAscii( &pTmp2, "file://" );
319 rtl_uString_newConcat( pustrFileURL, *pustrFileURL, pTmp2 );
320 rtl_uString_release( pTmp2 );
322 return osl_File_E_None;
324 return osl_File_E_INVAL;
328 /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
329 if( (sal_Unicode) '~' == ustrSystemPath->buffer[0] )
331 /* check if another user is specified */
332 if( ( 1 == ustrSystemPath->length ) || ( (sal_Unicode)'/' == ustrSystemPath->buffer[1] ) )
334 /* osl_getHomeDir returns file URL */
335 oslSecurity pSecurity = osl_getCurrentSecurity();
336 osl_getHomeDir( pSecurity , &pTmp );
337 osl_freeSecurityHandle( pSecurity );
339 /* remove "file://" prefix */
340 rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 );
342 /* replace '~' in original string */
343 rtl_uString_newReplaceStrAt( &pTmp, ustrSystemPath, 0, 1, pTmp );
346 else
348 /* FIXME: replace ~user with users home directory */
349 return osl_File_E_INVAL;
353 /* check if initial string contains double instances of '/' */
354 nIndex = rtl_ustr_indexOfStr_WithLength( ustrSystemPath->buffer, ustrSystemPath->length, pDoubleSlash, 2 );
355 if( -1 != nIndex )
357 sal_Int32 nSrcIndex;
358 sal_Int32 nDeleted = 0;
360 /* if pTmp is not already allocated, copy ustrSystemPath for modification */
361 if( NULL == pTmp )
362 rtl_uString_newFromString( &pTmp, ustrSystemPath );
364 /* adapt index to pTmp */
365 nIndex += pTmp->length - ustrSystemPath->length;
367 /* remove all occurrences of '//' */
368 for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ )
370 if( ((sal_Unicode) '/' == pTmp->buffer[nSrcIndex]) && ((sal_Unicode) '/' == pTmp->buffer[nIndex]) )
371 nDeleted++;
372 else
373 pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex];
376 /* adjust length member */
377 pTmp->length -= nDeleted;
380 if( NULL == pTmp )
381 rtl_uString_assign( &pTmp, ustrSystemPath );
383 /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */
385 OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) );
388 /* file URLs must be URI encoded */
389 rtl_uriEncode( pTmp, uriCharClass, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL );
391 rtl_uString_release( pTmp );
393 /* absolute urls should start with 'file://' */
394 if( (sal_Unicode)'/' == (*pustrFileURL)->buffer[0] )
396 rtl_uString *pProtocol = NULL;
398 rtl_uString_newFromAscii( &pProtocol, "file://" );
399 rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL );
400 rtl_uString_release( pProtocol );
403 return osl_File_E_None;
406 /****************************************************************************
407 * osl_getSystemPathFromFileURL_Ex - helper function
408 * clients may specify if they want to accept relative
409 * URLs or not
410 ****************************************************************************/
412 oslFileError osl_getSystemPathFromFileURL_Ex(
413 rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath, sal_Bool bAllowRelative)
415 rtl_uString* temp = 0;
416 oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp);
418 if (osl_File_E_None == osl_error)
420 if (bAllowRelative || (UNICHAR_SLASH == temp->buffer[0]))
422 *pustrSystemPath = temp;
424 else
426 rtl_uString_release(temp);
427 osl_error = osl_File_E_INVAL;
431 return osl_error;
434 namespace /* private */
437 /******************************************************
438 * Helper function, return a pinter to the final '\0'
439 * of a string
440 ******************************************************/
442 sal_Unicode* ustrtoend(sal_Unicode* pStr)
444 return (pStr + rtl_ustr_getLength(pStr));
447 /*********************************************
449 ********************************************/
451 sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d)
453 sal_Unicode* p = ustrtoend(d);
454 *p++ = chr;
455 *p = 0;
456 return d;
459 /******************************************************
461 ******************************************************/
463 bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr)
465 sal_Unicode* p = ustrtoend(pStr);
466 if (p > pStr)
467 p--;
468 return (*p == Chr);
471 /******************************************************
472 * Remove the last part of a path, a path that has
473 * only a '/' or no '/' at all will be returned
474 * unmodified
475 ******************************************************/
477 sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath)
479 /* we always may skip -2 because we
480 may at least stand on a '/' but
481 either there is no other character
482 before this '/' or it's another
483 character than the '/'
485 sal_Unicode* p = ustrtoend(aPath) - 2;
487 // move back to the next path separator
488 // or to the start of the string
489 while ((p > aPath) && (*p != UNICHAR_SLASH))
490 p--;
492 if (p >= aPath)
494 if (UNICHAR_SLASH == *p)
496 p++;
497 *p = '\0';
499 else
501 *p = '\0';
505 return aPath;
508 /******************************************************
510 ******************************************************/
512 oslFileError _osl_resolvepath(
513 /*inout*/ sal_Unicode* path,
514 /*inout*/ bool* failed)
516 oslFileError ferr = osl_File_E_None;
518 if (!*failed)
520 char unresolved_path[PATH_MAX];
521 if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path)))
522 return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
524 char resolved_path[PATH_MAX];
525 if (realpath(unresolved_path, resolved_path))
527 if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX))
528 return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
531 else
533 if (EACCES == errno || ENOTDIR == errno || ENOENT == errno)
534 *failed = true;
535 else
536 ferr = oslTranslateFileError(OSL_FET_ERROR, errno);
540 return ferr;
543 /******************************************************
544 * Works even with non existing paths. The resulting
545 * path must not exceed PATH_MAX else
546 * osl_File_E_NAMETOOLONG is the result
547 ******************************************************/
549 oslFileError osl_getAbsoluteFileURL_impl_(const rtl::OUString& unresolved_path, rtl::OUString& resolved_path)
551 // the given unresolved path must not exceed PATH_MAX
552 if (unresolved_path.getLength() >= (PATH_MAX - 2))
553 return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
555 sal_Unicode path_resolved_so_far[PATH_MAX];
556 const sal_Unicode* punresolved = unresolved_path.getStr();
557 sal_Unicode* presolvedsf = path_resolved_so_far;
559 // reserve space for leading '/' and trailing '\0'
560 // do not exceed this limit
561 sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2;
563 // if realpath fails with error ENOTDIR, EACCES or ENOENT
564 // we will not call it again, because _osl_realpath should also
565 // work with non existing directories etc.
566 bool realpath_failed = false;
567 oslFileError ferr;
569 path_resolved_so_far[0] = '\0';
571 while (*punresolved != '\0')
573 // ignore '/.' , skip one part back when '/..'
575 if ((UNICHAR_DOT == *punresolved) && (UNICHAR_SLASH == *presolvedsf))
577 if ('\0' == *(punresolved + 1))
579 punresolved++;
580 continue;
582 else if (UNICHAR_SLASH == *(punresolved + 1))
584 punresolved += 2;
585 continue;
587 else if ((UNICHAR_DOT == *(punresolved + 1)) && ('\0' == *(punresolved + 2) || (UNICHAR_SLASH == *(punresolved + 2))))
589 _rmlastpathtoken(path_resolved_so_far);
591 presolvedsf = ustrtoend(path_resolved_so_far) - 1;
593 if (UNICHAR_SLASH == *(punresolved + 2))
594 punresolved += 3;
595 else
596 punresolved += 2;
598 continue;
600 else // a file or directory name may start with '.'
602 if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
603 return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
605 ustrchrcat(*punresolved++, path_resolved_so_far);
607 if ('\0' == *punresolved && !realpath_failed)
609 ferr = _osl_resolvepath(
610 path_resolved_so_far,
611 &realpath_failed);
613 if (osl_File_E_None != ferr)
614 return ferr;
618 else if (UNICHAR_SLASH == *punresolved)
620 if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
621 return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
623 ustrchrcat(*punresolved++, path_resolved_so_far);
625 if (!realpath_failed)
627 ferr = _osl_resolvepath(
628 path_resolved_so_far,
629 &realpath_failed);
631 if (osl_File_E_None != ferr)
632 return ferr;
634 if (!_islastchr(path_resolved_so_far, UNICHAR_SLASH))
636 if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
637 return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
639 ustrchrcat(UNICHAR_SLASH, path_resolved_so_far);
643 else // any other character
645 if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
646 return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
648 ustrchrcat(*punresolved++, path_resolved_so_far);
650 if ('\0' == *punresolved && !realpath_failed)
652 ferr = _osl_resolvepath(
653 path_resolved_so_far,
654 &realpath_failed);
656 if (osl_File_E_None != ferr)
657 return ferr;
662 sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far);
664 OSL_ASSERT(len < PATH_MAX);
666 resolved_path = rtl::OUString(path_resolved_so_far, len);
668 return osl_File_E_None;
671 } // end namespace private
674 /******************************************************
675 * osl_getAbsoluteFileURL
676 ******************************************************/
678 oslFileError osl_getAbsoluteFileURL(rtl_uString* ustrBaseDirURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL)
680 // Work around the below call to getSystemPathFromFileURL rejecting input
681 // that starts with "/" (for whatever reason it behaves that way; but
682 // changing that would start to break lots of tests at least):
683 rtl::OUString relUrl(ustrRelativeURL);
684 if (relUrl.startsWith("//")) {
685 relUrl = "file:" + relUrl;
686 } else if (relUrl.startsWith("/")) {
687 relUrl = "file://" + relUrl;
690 FileBase::RC rc;
691 rtl::OUString unresolved_path;
693 rc = FileBase::getSystemPathFromFileURL(relUrl, unresolved_path);
695 if(FileBase::E_None != rc)
696 return oslFileError(rc);
698 if (systemPathIsRelativePath(unresolved_path))
700 rtl::OUString base_path;
701 rc = (FileBase::RC) osl_getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData, sal_False);
703 if (FileBase::E_None != rc)
704 return oslFileError(rc);
706 rtl::OUString abs_path;
707 systemPathMakeAbsolutePath(base_path, unresolved_path, abs_path);
709 unresolved_path = abs_path;
712 rtl::OUString resolved_path;
714 static bool allow_symlinks = getenv("SAL_ALLOW_LINKOO_SYMLINKS") != 0;
715 // getenv is not thread safe, so minimize use of result
716 if (!allow_symlinks)
718 rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
720 else
722 // SAL_ALLOW_LINKOO_SYMLINKS environment variable:
723 // for linkoo to work, we need to let the symlinks to the libraries untouched
724 rtl::OUString base;
725 sal_Int32 last_slash = unresolved_path.lastIndexOf( UNICHAR_SLASH );
727 if (last_slash >= 0 && last_slash + 1 < unresolved_path.getLength()
728 && ! ( last_slash + 2 == unresolved_path.getLength() && unresolved_path.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("."), last_slash + 1) )
729 && ! ( last_slash + 3 == unresolved_path.getLength() && unresolved_path.matchAsciiL(RTL_CONSTASCII_STRINGPARAM(".."), last_slash + 1) ))
731 base = unresolved_path.copy(last_slash+1);
732 unresolved_path = unresolved_path.copy(0, last_slash);
735 rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
737 if (!base.isEmpty())
739 resolved_path += rtl::OUString( UNICHAR_SLASH );
740 resolved_path += base;
744 if (FileBase::E_None == rc)
746 rc = (FileBase::RC) osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL);
747 OSL_ASSERT(FileBase::E_None == rc);
750 return oslFileError(rc);
754 namespace /* private */
757 /*********************************************
758 No separate error code if unicode to text
759 conversion or getenv fails because for the
760 caller there is no difference why a file
761 could not be found in $PATH
762 ********************************************/
764 bool find_in_PATH(const rtl::OUString& file_path, rtl::OUString& result)
766 bool bfound = false;
767 rtl::OUString path("PATH");
768 rtl::OUString env_path;
770 if (osl_Process_E_None == osl_getEnvironment(path.pData, &env_path.pData))
771 bfound = osl::searchPath(file_path, env_path, result);
773 return bfound;
776 /*********************************************
777 No separate error code if unicode to text
778 conversion or getcwd fails because for the
779 caller there is no difference why a file
780 could not be found in CDW
781 ********************************************/
783 bool find_in_CWD(const rtl::OUString& file_path, rtl::OUString& result)
785 bool bfound = false;
786 rtl::OUString cwd_url;
788 if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData))
790 rtl::OUString cwd;
791 FileBase::getSystemPathFromFileURL(cwd_url, cwd);
792 bfound = osl::searchPath(file_path, cwd, result);
794 return bfound;
797 /*********************************************
799 ********************************************/
801 bool find_in_searchPath(const rtl::OUString& file_path, rtl_uString* search_path, rtl::OUString& result)
803 return (search_path && osl::searchPath(file_path, rtl::OUString(search_path), result));
806 } // end namespace private
809 /****************************************************************************
810 * osl_searchFileURL
811 ***************************************************************************/
813 oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL)
815 OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter");
817 FileBase::RC rc;
818 rtl::OUString file_path;
820 // try to interpret search path as file url else assume it's a system path list
821 rc = FileBase::getSystemPathFromFileURL(rtl::OUString(ustrFilePath), file_path);
822 if (FileBase::E_INVAL == rc)
823 file_path = ustrFilePath;
824 else if (FileBase::E_None != rc)
825 return oslFileError(rc);
827 bool bfound = false;
828 rtl::OUString result;
830 if (find_in_searchPath(file_path, ustrSearchPath, result) ||
831 find_in_PATH(file_path, result) ||
832 find_in_CWD(file_path, result))
834 rtl::OUString resolved;
836 if (osl::realpath(result, resolved))
838 #if OSL_DEBUG_LEVEL > 0
839 oslFileError osl_error =
840 #endif
841 osl_getFileURLFromSystemPath(resolved.pData, pustrURL);
842 OSL_ASSERT(osl_File_E_None == osl_error);
843 bfound = true;
846 return bfound ? osl_File_E_None : osl_File_E_NOENT;
850 /****************************************************************************
851 * FileURLToPath
852 ***************************************************************************/
854 oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL)
856 rtl_uString* ustrSystemPath = NULL;
857 oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &ustrSystemPath);
859 if(osl_File_E_None != osl_error)
860 return osl_error;
862 osl_systemPathRemoveSeparator(ustrSystemPath);
864 /* convert unicode path to text */
865 if(!UnicodeToText( buffer, bufLen, ustrSystemPath->buffer, ustrSystemPath->length))
866 osl_error = oslTranslateFileError(OSL_FET_ERROR, errno);
868 rtl_uString_release(ustrSystemPath);
870 return osl_error;
873 /*****************************************************************************
874 * UnicodeToText
875 ****************************************************************************/
877 namespace /* private */
879 class UnicodeToTextConverter_Impl
881 rtl_UnicodeToTextConverter m_converter;
883 UnicodeToTextConverter_Impl()
884 : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding()))
887 ~UnicodeToTextConverter_Impl()
889 rtl_destroyUnicodeToTextConverter (m_converter);
891 public:
892 static UnicodeToTextConverter_Impl & getInstance()
894 static UnicodeToTextConverter_Impl g_theConverter;
895 return g_theConverter;
898 sal_Size convert(
899 sal_Unicode const * pSrcBuf, sal_Size nSrcChars, sal_Char * pDstBuf, sal_Size nDstBytes,
900 sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtChars)
902 OSL_ASSERT(m_converter != 0);
903 return rtl_convertUnicodeToText (
904 m_converter, 0, pSrcBuf, nSrcChars, pDstBuf, nDstBytes, nFlags, pInfo, pSrcCvtChars);
907 } // end namespace private
909 int UnicodeToText( char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen )
911 sal_uInt32 nInfo = 0;
912 sal_Size nSrcChars = 0;
914 sal_Size nDestBytes = UnicodeToTextConverter_Impl::getInstance().convert (
915 uniText, uniTextLen, buffer, bufLen,
916 OUSTRING_TO_OSTRING_CVTFLAGS | RTL_UNICODETOTEXT_FLAGS_FLUSH, &nInfo, &nSrcChars);
918 if( nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL )
920 errno = EOVERFLOW;
921 return 0;
924 /* ensure trailing '\0' */
925 buffer[nDestBytes] = '\0';
926 return nDestBytes;
929 /*****************************************************************************
930 * TextToUnicode
931 ****************************************************************************/
933 namespace /* private */
935 class TextToUnicodeConverter_Impl
937 rtl_TextToUnicodeConverter m_converter;
939 TextToUnicodeConverter_Impl()
940 : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding()))
943 ~TextToUnicodeConverter_Impl()
945 rtl_destroyTextToUnicodeConverter (m_converter);
948 public:
949 static TextToUnicodeConverter_Impl & getInstance()
951 static TextToUnicodeConverter_Impl g_theConverter;
952 return g_theConverter;
955 sal_Size convert(
956 sal_Char const * pSrcBuf, sal_Size nSrcBytes, sal_Unicode * pDstBuf, sal_Size nDstChars,
957 sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtBytes)
959 OSL_ASSERT(m_converter != 0);
960 return rtl_convertTextToUnicode (
961 m_converter, 0, pSrcBuf, nSrcBytes, pDstBuf, nDstChars, nFlags, pInfo, pSrcCvtBytes);
964 } // end namespace private
966 int TextToUnicode(
967 const char* text,
968 size_t text_buffer_size,
969 sal_Unicode* unic_text,
970 sal_Int32 unic_text_buffer_size)
972 sal_uInt32 nInfo = 0;
973 sal_Size nSrcChars = 0;
975 sal_Size nDestBytes = TextToUnicodeConverter_Impl::getInstance().convert(
976 text, text_buffer_size, unic_text, unic_text_buffer_size,
977 OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_FLUSH, &nInfo, &nSrcChars);
979 if (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL)
981 errno = EOVERFLOW;
982 return 0;
985 /* ensure trailing '\0' */
986 unic_text[nDestBytes] = '\0';
987 return nDestBytes;
990 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */