xref: /aoo42x/main/sal/osl/unx/file_url.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sal.hxx"
30 
31 #include "file_url.h"
32 
33 #include "system.h"
34 
35 #include <limits.h>
36 #include <errno.h>
37 #include <strings.h>
38 #include <unistd.h>
39 
40 #include "osl/file.hxx"
41 #include <osl/security.h>
42 #include <osl/diagnose.h>
43 #include <osl/thread.h>
44 #include <osl/process.h>
45 
46 #include <rtl/uri.h>
47 #include <rtl/ustring.hxx>
48 #include <rtl/ustrbuf.h>
49 #include "rtl/textcvt.h"
50 
51 #include "file_error_transl.h"
52 #include "file_path_helper.hxx"
53 
54 #include "uunxapi.hxx"
55 
56 /***************************************************
57 
58  General note
59 
60  This file contains the part that handles File URLs.
61 
62  File URLs as scheme specific notion of URIs
63  (RFC2396) may be handled platform independend, but
64  will not in osl which is considered wrong.
65  Future version of osl should handle File URLs this
66  way. In rtl/uri there is already an URI parser etc.
67  so this code should be consolidated.
68 
69  **************************************************/
70 /************************************************************************
71  *   ToDo
72  *
73  *   Fix osl_getCanonicalName
74  *
75  ***********************************************************************/
76 
77 
78 /***************************************************
79  * namespace directives
80  **************************************************/
81 
82 using namespace osl;
83 
84 /***************************************************
85  * constants
86  **************************************************/
87 
88 const sal_Unicode UNICHAR_SLASH = ((sal_Unicode)'/');
89 const sal_Unicode UNICHAR_COLON = ((sal_Unicode)':');
90 const sal_Unicode UNICHAR_DOT   = ((sal_Unicode)'.');
91 
92 /******************************************************************************
93  *
94  *                  Exported Module Functions
95  *
96  *****************************************************************************/
97 
98 /* a slightly modified version of Pchar in rtl/source/uri.c */
99 const sal_Bool uriCharClass[128] =
100 {
101   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Pchar but without encoding slashes */
102   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103   0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* !"#$%&'()*+,-./  */
104   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, /* 0123456789:;<=>? */
105   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ABCDEFGHIJKLMNO */
106   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* PQRSTUVWXYZ[\]^_ */
107   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* `abcdefghijklmno */
108   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0  /* pqrstuvwxyz{|}~  */
109 };
110 
111 
112 /* check for top wrong usage strings */
113 /*
114 static sal_Bool findWrongUsage( const sal_Unicode *path, sal_Int32 len )
115 {
116     rtl_uString *pTmp = NULL;
117     sal_Bool bRet;
118 
119     rtl_uString_newFromStr_WithLength( &pTmp, path, len );
120 
121     rtl_ustr_toAsciiLowerCase_WithLength( pTmp->buffer, pTmp->length );
122 
123     bRet = ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "ftp://", 6 ) ) ||
124            ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "http://", 7 ) ) ||
125            ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "vnd.sun.star", 12 ) ) ||
126            ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "private:", 8 ) ) ||
127            ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "slot:", 5) );
128 
129     rtl_uString_release( pTmp );
130     return bRet;
131 }
132 */
133 
134 /****************************************************************************/
135 /*	osl_getCanonicalName */
136 /****************************************************************************/
137 
138 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString* ustrFileURL, rtl_uString** pustrValidURL )
139 {
140 	OSL_ENSURE(0, "osl_getCanonicalName not implemented");
141 
142 	rtl_uString_newFromString(pustrValidURL, ustrFileURL);
143 	return osl_File_E_None;
144 }
145 
146 /****************************************************************************/
147 /*	osl_getSystemPathFromFileURL */
148 /****************************************************************************/
149 
150 oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath )
151 {
152     sal_Int32 nIndex;
153     rtl_uString * pTmp = NULL;
154 
155     sal_Unicode encodedSlash[3] = { '%', '2', 'F' };
156 	sal_Unicode protocolDelimiter[3] = { ':', '/', '/' };
157 
158     /* temporary hack: if already system path, return ustrFileURL */
159 	/*
160     if( (sal_Unicode) '/' == ustrFileURL->buffer[0] )
161     {
162         OSL_ENSURE( 0, "osl_getSystemPathFromFileURL: input is already system path" );
163         rtl_uString_assign( pustrSystemPath, ustrFileURL );
164         return osl_File_E_None;
165     }
166 	*/
167 
168     /* a valid file url may not start with '/' */
169     if( ( 0 == ustrFileURL->length ) || ( (sal_Unicode) '/' == ustrFileURL->buffer[0] ) )
170     {
171         return osl_File_E_INVAL;
172     }
173 
174 	/* Check for non file:// protocols */
175 
176 	nIndex = rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, protocolDelimiter, 3 );
177 	if ( -1 != nIndex && (4 != nIndex || 0 != rtl_ustr_ascii_shortenedCompare_WithLength( ustrFileURL->buffer, ustrFileURL->length,"file", 4 ) ) )
178 	{
179 		return osl_File_E_INVAL;
180 	}
181 
182     /* search for encoded slashes (%2F) and decode every single token if we find one */
183 
184     nIndex = 0;
185 
186 	if( -1 != rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, encodedSlash, 3 ) )
187     {
188         rtl_uString * ustrPathToken = NULL;
189         sal_Int32 nOffset = 7;
190 
191         do
192         {
193             nOffset += nIndex;
194 
195             /* break url down in '/' devided tokens tokens */
196             nIndex = rtl_ustr_indexOfChar_WithLength( ustrFileURL->buffer + nOffset, ustrFileURL->length - nOffset, (sal_Unicode) '/' );
197 
198             /* copy token to new string */
199             rtl_uString_newFromStr_WithLength( &ustrPathToken, ustrFileURL->buffer + nOffset,
200                 -1 == nIndex ? ustrFileURL->length - nOffset : nIndex++ );
201 
202             /* decode token */
203             rtl_uriDecode( ustrPathToken, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp );
204 
205             /* the result should not contain any '/' */
206             if( -1 != rtl_ustr_indexOfChar_WithLength( pTmp->buffer, pTmp->length, (sal_Unicode) '/' ) )
207             {
208                 rtl_uString_release( pTmp );
209                 rtl_uString_release( ustrPathToken );
210 
211                 return osl_File_E_INVAL;
212             }
213 
214         } while( -1 != nIndex );
215 
216         /* release temporary string and restore index variable */
217         rtl_uString_release( ustrPathToken );
218         nIndex = 0;
219     }
220 
221     /* protocol and server should not be encoded, so decode the whole string */
222     rtl_uriDecode( ustrFileURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp );
223 
224     /* check if file protocol specified */
225     /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */
226     if( 7 <= pTmp->length )
227     {
228         rtl_uString * pProtocol = NULL;
229         rtl_uString_newFromStr_WithLength( &pProtocol, pTmp->buffer, 7 );
230 
231         /* protocol is case insensitive */
232         rtl_ustr_toAsciiLowerCase_WithLength( pProtocol->buffer, pProtocol->length );
233 
234         if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pProtocol->buffer, pProtocol->length,"file://", 7 ) )
235             nIndex = 7;
236 
237         rtl_uString_release( pProtocol );
238     }
239 
240     /* skip "localhost" or "127.0.0.1" if "file://" is specified */
241     /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */
242     if( nIndex && ( 10 <= pTmp->length - nIndex ) )
243     {
244         rtl_uString * pServer = NULL;
245         rtl_uString_newFromStr_WithLength( &pServer, pTmp->buffer + nIndex, 10 );
246 
247         /* server is case insensitive */
248         rtl_ustr_toAsciiLowerCase_WithLength( pServer->buffer, pServer->length );
249 
250         if( ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"localhost/", 10 ) ) ||
251             ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"127.0.0.1/", 10 ) ) )
252         {
253             /* don't exclude the '/' */
254             nIndex += 9;
255         }
256 
257         rtl_uString_release( pServer );
258     }
259 
260     if( nIndex )
261         rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + nIndex, pTmp->length - nIndex );
262 
263     /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
264     if( (sal_Unicode) '~' == pTmp->buffer[0] )
265     {
266         /* check if another user is specified */
267         if( ( 1 == pTmp->length ) || ( (sal_Unicode)'/' == pTmp->buffer[1] ) )
268         {
269             rtl_uString *pTmp2 = NULL;
270 
271             /* osl_getHomeDir returns file URL */
272             osl_getHomeDir( osl_getCurrentSecurity(), &pTmp2 );
273 
274             /* remove "file://" prefix */
275             rtl_uString_newFromStr_WithLength( &pTmp2, pTmp2->buffer + 7, pTmp2->length - 7 );
276 
277             /* replace '~' in original string */
278             rtl_uString_newReplaceStrAt( &pTmp, pTmp, 0, 1, pTmp2 );
279             rtl_uString_release( pTmp2 );
280         }
281 
282         else
283         {
284             /* FIXME: replace ~user with users home directory */
285             return osl_File_E_INVAL;
286         }
287     }
288 
289     /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */
290 	/*
291     OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) );
292 	*/
293 
294     *pustrSystemPath = pTmp;
295     return osl_File_E_None;
296 }
297 
298 /****************************************************************************/
299 /*	osl_getFileURLFromSystemPath */
300 /****************************************************************************/
301 
302 oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL )
303 {
304     static const sal_Unicode pDoubleSlash[2] = { '/', '/' };
305 
306     rtl_uString *pTmp = NULL;
307     sal_Int32 nIndex;
308 
309     if( 0 == ustrSystemPath->length )
310         return osl_File_E_INVAL;
311 
312     /* temporary hack: if already file url, return ustrSystemPath */
313 
314     if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file:", 5 ) )
315     {
316 	/*
317         if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file://", 7 ) )
318         {
319             OSL_ENSURE( 0, "osl_getFileURLFromSystemPath: input is already file URL" );
320             rtl_uString_assign( pustrFileURL, ustrSystemPath );
321         }
322         else
323         {
324             rtl_uString *pTmp2 = NULL;
325 
326             OSL_ENSURE( 0, "osl_getFileURLFromSystemPath: input is wrong file URL" );
327             rtl_uString_newFromStr_WithLength( pustrFileURL, ustrSystemPath->buffer + 5, ustrSystemPath->length - 5 );
328             rtl_uString_newFromAscii( &pTmp2, "file://" );
329             rtl_uString_newConcat( pustrFileURL, *pustrFileURL, pTmp2 );
330             rtl_uString_release( pTmp2 );
331         }
332         return osl_File_E_None;
333 		*/
334 		return osl_File_E_INVAL;
335     }
336 
337 
338     /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
339     if( (sal_Unicode) '~' == ustrSystemPath->buffer[0] )
340     {
341         /* check if another user is specified */
342         if( ( 1 == ustrSystemPath->length ) || ( (sal_Unicode)'/' == ustrSystemPath->buffer[1] ) )
343         {
344             /* osl_getHomeDir returns file URL */
345             osl_getHomeDir( osl_getCurrentSecurity(), &pTmp );
346 
347             /* remove "file://" prefix */
348             rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 );
349 
350             /* replace '~' in original string */
351             rtl_uString_newReplaceStrAt( &pTmp, ustrSystemPath, 0, 1, pTmp );
352         }
353 
354         else
355         {
356             /* FIXME: replace ~user with users home directory */
357             return osl_File_E_INVAL;
358         }
359     }
360 
361     /* check if initial string contains double instances of '/' */
362     nIndex = rtl_ustr_indexOfStr_WithLength( ustrSystemPath->buffer, ustrSystemPath->length, pDoubleSlash, 2 );
363     if( -1 != nIndex )
364     {
365         sal_Int32 nSrcIndex;
366         sal_Int32 nDeleted = 0;
367 
368         /* if pTmp is not already allocated, copy ustrSystemPath for modification */
369         if( NULL == pTmp )
370             rtl_uString_newFromString( &pTmp, ustrSystemPath );
371 
372         /* adapt index to pTmp */
373         nIndex += pTmp->length - ustrSystemPath->length;
374 
375         /* remove all occurances of '//' */
376         for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ )
377         {
378             if( ((sal_Unicode) '/' == pTmp->buffer[nSrcIndex]) && ((sal_Unicode) '/' == pTmp->buffer[nIndex]) )
379                 nDeleted++;
380             else
381                 pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex];
382         }
383 
384         /* adjust length member */
385         pTmp->length -= nDeleted;
386     }
387 
388     if( NULL == pTmp )
389         rtl_uString_assign( &pTmp, ustrSystemPath );
390 
391     /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */
392 	/*
393     OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) );
394 	*/
395 
396     /* file URLs must be URI encoded */
397     rtl_uriEncode( pTmp, uriCharClass, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL );
398 
399     rtl_uString_release( pTmp );
400 
401     /* absolute urls should start with 'file://' */
402     if( (sal_Unicode)'/' == (*pustrFileURL)->buffer[0] )
403     {
404         rtl_uString *pProtocol = NULL;
405 
406         rtl_uString_newFromAscii( &pProtocol, "file://" );
407         rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL );
408         rtl_uString_release( pProtocol );
409     }
410 
411     return osl_File_E_None;
412 }
413 
414 /****************************************************************************
415  * osl_getSystemPathFromFileURL_Ex - helper function
416  * clients may specify if they want to accept relative
417  * URLs or not
418  ****************************************************************************/
419 
420 oslFileError osl_getSystemPathFromFileURL_Ex(
421     rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath, sal_Bool bAllowRelative)
422 {
423     rtl_uString* temp = 0;
424     oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp);
425 
426     if (osl_File_E_None == osl_error)
427     {
428     	if (bAllowRelative || (UNICHAR_SLASH == temp->buffer[0]))
429         {
430             *pustrSystemPath = temp;
431         }
432         else
433         {
434             rtl_uString_release(temp);
435             osl_error = osl_File_E_INVAL;
436         }
437     }
438 
439     return osl_error;
440 }
441 
442 namespace /* private */
443 {
444 
445 	/******************************************************
446 	 * Helper function, return a pinter to the final '\0'
447 	 * of a string
448 	 ******************************************************/
449 
450 	sal_Unicode* ustrtoend(sal_Unicode* pStr)
451 	{
452 		return (pStr + rtl_ustr_getLength(pStr));
453 	}
454 
455 	/*********************************************
456 
457 	 ********************************************/
458 
459 	sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d)
460 	{
461 		sal_Unicode* p = ustrtoend(d);
462 		*p++ = chr;
463 		*p   = 0;
464 		return d;
465 	}
466 
467 	/******************************************************
468 	 *
469 	 ******************************************************/
470 
471 	bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr)
472 	{
473    		sal_Unicode* p = ustrtoend(pStr);
474 	   	if (p > pStr)
475        		p--;
476 	   	return (*p == Chr);
477 	}
478 
479 	/******************************************************
480 	 * Remove the last part of a path, a path that has
481 	 * only a '/' or no '/' at all will be returned
482 	 * unmodified
483 	 ******************************************************/
484 
485 	sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath)
486 	{
487 		/* 	we always may skip -2 because we
488 	   		may at least stand on a '/' but
489 		   	either there is no other character
490 		   	before this '/' or it's another
491 	   		character than the '/'
492 		*/
493 		sal_Unicode* p = ustrtoend(aPath) - 2;
494 
495 		// move back to the next path separator
496 		// or to the start of the string
497 		while ((p > aPath) && (*p != UNICHAR_SLASH))
498 			p--;
499 
500 		if (p >= aPath)
501 		{
502     		if (UNICHAR_SLASH == *p)
503     		{
504 				p++;
505 			   *p = '\0';
506     		}
507     		else
508     		{
509 		   		*p = '\0';
510     		}
511 		}
512 
513 	    return aPath;
514 	}
515 
516 	/******************************************************
517 	 *
518 	 ******************************************************/
519 
520 	oslFileError _osl_resolvepath(
521     	/*inout*/ sal_Unicode* path,
522 	    /*inout*/ sal_Unicode* current_pos,
523     	/*inout*/ bool* failed)
524 	{
525     	oslFileError ferr = osl_File_E_None;
526 
527 	    if (!*failed)
528     	{
529 			char unresolved_path[PATH_MAX];
530 			if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path)))
531 				return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
532 
533 			char resolved_path[PATH_MAX];
534 		    if (realpath(unresolved_path, resolved_path))
535 			{
536 				if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX))
537 					return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
538 
539 				current_pos = ustrtoend(path) - 1;
540 			}
541 			else
542 			{
543 				if (EACCES == errno || ENOTDIR == errno || ENOENT == errno)
544 					*failed = true;
545 				else
546 					ferr = oslTranslateFileError(OSL_FET_ERROR, errno);
547 			}
548     	}
549 
550 	    return ferr;
551 	}
552 
553 	/******************************************************
554 	 * Works even with non existing paths. The resulting
555 	 * path must not exceed PATH_MAX else
556 	 * osl_File_E_NAMETOOLONG is the result
557 	 ******************************************************/
558 
559 	oslFileError osl_getAbsoluteFileURL_impl_(const rtl::OUString& unresolved_path, rtl::OUString& resolved_path)
560 	{
561 		// the given unresolved path must not exceed PATH_MAX
562 	    if (unresolved_path.getLength() >= (PATH_MAX - 2))
563     	    return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
564 
565 	    sal_Unicode        path_resolved_so_far[PATH_MAX];
566 	    const sal_Unicode* punresolved = unresolved_path.getStr();
567 		sal_Unicode*       presolvedsf = path_resolved_so_far;
568 
569 	    // reserve space for leading '/' and trailing '\0'
570 	    // do not exceed this limit
571     	sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2;
572 
573 	    // if realpath fails with error ENOTDIR, EACCES or ENOENT
574 	    // we will not call it again, because _osl_realpath should also
575     	// work with non existing directories etc.
576 	    bool realpath_failed = false;
577     	oslFileError ferr;
578 
579 	    path_resolved_so_far[0] = '\0';
580 
581     	while (*punresolved != '\0')
582     	{
583         	// ignore '/.' , skip one part back when '/..'
584 
585 	        if ((UNICHAR_DOT == *punresolved) && (UNICHAR_SLASH == *presolvedsf))
586     	    {
587         	    if ('\0' == *(punresolved + 1))
588             	{
589                 	punresolved++;
590 	                continue;
591     	        }
592         	    else if (UNICHAR_SLASH == *(punresolved + 1))
593             	{
594                 	punresolved += 2;
595 	                continue;
596     	        }
597         	    else if ((UNICHAR_DOT == *(punresolved + 1)) && ('\0' == *(punresolved + 2) || (UNICHAR_SLASH == *(punresolved + 2))))
598             	{
599                 	_rmlastpathtoken(path_resolved_so_far);
600 
601 	                presolvedsf = ustrtoend(path_resolved_so_far) - 1;
602 
603     	            if (UNICHAR_SLASH == *(punresolved + 2))
604         	            punresolved += 3;
605             	    else
606                 	    punresolved += 2;
607 
608 	                continue;
609     	        }
610         	    else // a file or directory name may start with '.'
611             	{
612                 	if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
613                     	return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
614 
615 	                ustrchrcat(*punresolved++, path_resolved_so_far);
616 
617     	            if ('\0' == *punresolved && !realpath_failed)
618         	        {
619 						ferr = _osl_resolvepath(
620 							path_resolved_so_far,
621 							presolvedsf,
622 							&realpath_failed);
623 
624 						if (osl_File_E_None != ferr)
625 			    			return ferr;
626             	    }
627             	}
628         	}
629 	        else if (UNICHAR_SLASH == *punresolved)
630     	    {
631 				if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
632             	    return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
633 
634 	            ustrchrcat(*punresolved++, path_resolved_so_far);
635 
636     	        if (!realpath_failed)
637         	    {
638             	    ferr = _osl_resolvepath(
639 						path_resolved_so_far,
640 						presolvedsf,
641 						&realpath_failed);
642 
643 					if (osl_File_E_None != ferr)
644 						return ferr;
645 
646 					if (!_islastchr(path_resolved_so_far, UNICHAR_SLASH))
647 					{
648 	    				if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
649 							return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
650 
651 						ustrchrcat(UNICHAR_SLASH, path_resolved_so_far);
652 					}
653             	}
654         	}
655 	        else // any other character
656     	    {
657         	    if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
658             	    return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
659 
660 	            ustrchrcat(*punresolved++, path_resolved_so_far);
661 
662     	        if ('\0' == *punresolved && !realpath_failed)
663         	    {
664             	    ferr = _osl_resolvepath(
665 						path_resolved_so_far,
666 						presolvedsf,
667 						&realpath_failed);
668 
669 					if (osl_File_E_None != ferr)
670 						return ferr;
671             	}
672         	}
673     	}
674 
675 		sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far);
676 
677 	    OSL_ASSERT(len < PATH_MAX);
678 
679     	resolved_path = rtl::OUString(path_resolved_so_far, len);
680 
681 	    return osl_File_E_None;
682 	}
683 
684 } // end namespace private
685 
686 
687 /******************************************************
688  * osl_getAbsoluteFileURL
689  ******************************************************/
690 
691 oslFileError osl_getAbsoluteFileURL(rtl_uString*  ustrBaseDirURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL)
692 {
693 	FileBase::RC  rc;
694     rtl::OUString unresolved_path;
695 
696     rc = FileBase::getSystemPathFromFileURL(rtl::OUString(ustrRelativeURL), unresolved_path);
697 
698 	if(FileBase::E_None != rc)
699         return oslFileError(rc);
700 
701     if (systemPathIsRelativePath(unresolved_path))
702     {
703 		rtl::OUString base_path;
704         rc = (FileBase::RC) osl_getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData, sal_False);
705 
706         if (FileBase::E_None != rc)
707             return oslFileError(rc);
708 
709 		rtl::OUString abs_path;
710 		systemPathMakeAbsolutePath(base_path, unresolved_path, abs_path);
711 
712         unresolved_path = abs_path;
713     }
714 
715 	rtl::OUString resolved_path;
716     rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
717 
718 	if (FileBase::E_None == rc)
719     {
720     	rc = (FileBase::RC) osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL);
721         OSL_ASSERT(FileBase::E_None == rc);
722     }
723 
724     return oslFileError(rc);
725 }
726 
727 
728 namespace /* private */
729 {
730 
731 	/*********************************************
732 	 No separate error code if unicode to text
733 	 conversion or getenv fails because for the
734 	 caller there is no difference why a file
735 	 could not be found in $PATH
736 	 ********************************************/
737 
738 	bool find_in_PATH(const rtl::OUString& file_path, rtl::OUString& result)
739 	{
740 		bool          bfound = false;
741 		rtl::OUString path   = rtl::OUString::createFromAscii("PATH");
742 		rtl::OUString env_path;
743 
744 		if (osl_Process_E_None == osl_getEnvironment(path.pData, &env_path.pData))
745 			bfound = osl::searchPath(file_path, env_path, result);
746 
747 		return bfound;
748 	}
749 
750 	/*********************************************
751 	 No separate error code if unicode to text
752 	 conversion or getcwd fails because for the
753 	 caller there is no difference why a file
754 	 could not be found in CDW
755 	 ********************************************/
756 
757 	bool find_in_CWD(const rtl::OUString& file_path, rtl::OUString& result)
758 	{
759 		bool bfound = false;
760 		rtl::OUString cwd_url;
761 
762 		if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData))
763 		{
764 			rtl::OUString cwd;
765 			FileBase::getSystemPathFromFileURL(cwd_url, cwd);
766 			bfound = osl::searchPath(file_path, cwd, result);
767 		}
768 		return bfound;
769 	}
770 
771 	/*********************************************
772 
773 	 ********************************************/
774 
775 	bool find_in_searchPath(const rtl::OUString& file_path, rtl_uString* search_path, rtl::OUString& result)
776 	{
777 		return (search_path && osl::searchPath(file_path, rtl::OUString(search_path), result));
778 	}
779 
780 } // end namespace private
781 
782 
783 /****************************************************************************
784  *	osl_searchFileURL
785  ***************************************************************************/
786 
787 oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL)
788 {
789 	OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter");
790 
791 	FileBase::RC  rc;
792 	rtl::OUString file_path;
793 
794 	// try to interpret search path as file url else assume it's a system path list
795 	rc = FileBase::getSystemPathFromFileURL(rtl::OUString(ustrFilePath), file_path);
796 	if ((FileBase::E_None != rc) && (FileBase::E_INVAL == rc))
797 		file_path = ustrFilePath;
798 	else if (FileBase::E_None != rc)
799 		return oslFileError(rc);
800 
801 	bool          bfound = false;
802 	rtl::OUString result;
803 
804 	if (find_in_searchPath(file_path, ustrSearchPath, result) ||
805 	    find_in_PATH(file_path, result) ||
806 		find_in_CWD(file_path, result))
807     {
808 		rtl::OUString resolved;
809 
810 		if (osl::realpath(result, resolved))
811 		{
812 #if OSL_DEBUG_LEVEL > 0
813 			oslFileError osl_error =
814 #endif
815                 osl_getFileURLFromSystemPath(resolved.pData, pustrURL);
816 			OSL_ASSERT(osl_File_E_None == osl_error);
817 			bfound = true;
818 		}
819     }
820     return bfound ? osl_File_E_None : osl_File_E_NOENT;
821 }
822 
823 
824 /****************************************************************************
825  * FileURLToPath
826  ***************************************************************************/
827 
828 oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL)
829 {
830     rtl_uString* ustrSystemPath = NULL;
831     oslFileError osl_error		= osl_getSystemPathFromFileURL(ustrFileURL, &ustrSystemPath);
832 
833     if(osl_File_E_None != osl_error)
834         return osl_error;
835 
836 	osl_systemPathRemoveSeparator(ustrSystemPath);
837 
838     /* convert unicode path to text */
839     if(!UnicodeToText( buffer, bufLen, ustrSystemPath->buffer, ustrSystemPath->length))
840         osl_error = oslTranslateFileError(OSL_FET_ERROR, errno);
841 
842     rtl_uString_release(ustrSystemPath);
843 
844     return osl_error;
845 }
846 
847 /*****************************************************************************
848  * UnicodeToText
849  ****************************************************************************/
850 
851 namespace /* private */
852 {
853     class UnicodeToTextConverter_Impl
854     {
855         rtl_UnicodeToTextConverter m_converter;
856 
857         UnicodeToTextConverter_Impl()
858             : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding()))
859         {}
860 
861         ~UnicodeToTextConverter_Impl()
862         {
863             rtl_destroyUnicodeToTextConverter (m_converter);
864         }
865     public:
866         static UnicodeToTextConverter_Impl & getInstance()
867         {
868             static UnicodeToTextConverter_Impl g_theConverter;
869             return g_theConverter;
870         }
871 
872         sal_Size convert(
873             sal_Unicode const * pSrcBuf, sal_Size nSrcChars, sal_Char * pDstBuf, sal_Size nDstBytes,
874             sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtChars)
875         {
876             OSL_ASSERT(m_converter != 0);
877             return rtl_convertUnicodeToText (
878                 m_converter, 0, pSrcBuf, nSrcChars, pDstBuf, nDstBytes, nFlags, pInfo, pSrcCvtChars);
879         }
880     };
881 } // end namespace private
882 
883 int UnicodeToText( char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen )
884 {
885     sal_uInt32   nInfo = 0;
886     sal_Size     nSrcChars = 0;
887 
888     sal_Size nDestBytes = UnicodeToTextConverter_Impl::getInstance().convert (
889         uniText, uniTextLen, buffer, bufLen,
890         OUSTRING_TO_OSTRING_CVTFLAGS | RTL_UNICODETOTEXT_FLAGS_FLUSH, &nInfo, &nSrcChars);
891 
892     if( nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL )
893     {
894         errno = EOVERFLOW;
895         return 0;
896     }
897 
898     /* ensure trailing '\0' */
899     buffer[nDestBytes] = '\0';
900     return nDestBytes;
901 }
902 
903 /*****************************************************************************
904  * TextToUnicode
905  ****************************************************************************/
906 
907 namespace /* private */
908 {
909     class TextToUnicodeConverter_Impl
910     {
911         rtl_TextToUnicodeConverter m_converter;
912 
913         TextToUnicodeConverter_Impl()
914             : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding()))
915         {}
916 
917         ~TextToUnicodeConverter_Impl()
918         {
919             rtl_destroyTextToUnicodeConverter (m_converter);
920         }
921 
922     public:
923         static TextToUnicodeConverter_Impl & getInstance()
924         {
925             static TextToUnicodeConverter_Impl g_theConverter;
926             return g_theConverter;
927         }
928 
929         sal_Size convert(
930             sal_Char const * pSrcBuf, sal_Size nSrcBytes, sal_Unicode * pDstBuf, sal_Size nDstChars,
931             sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtBytes)
932         {
933             OSL_ASSERT(m_converter != 0);
934             return rtl_convertTextToUnicode (
935                 m_converter, 0, pSrcBuf, nSrcBytes, pDstBuf, nDstChars, nFlags, pInfo, pSrcCvtBytes);
936         }
937     };
938 } // end namespace private
939 
940 int TextToUnicode(
941 	const char*  text,
942 	size_t       text_buffer_size,
943 	sal_Unicode* unic_text,
944 	sal_Int32    unic_text_buffer_size)
945 {
946     sal_uInt32 nInfo = 0;
947     sal_Size   nSrcChars = 0;
948 
949     sal_Size nDestBytes = TextToUnicodeConverter_Impl::getInstance().convert(
950         text,  text_buffer_size, unic_text, unic_text_buffer_size,
951         OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_FLUSH, &nInfo, &nSrcChars);
952 
953     if (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL)
954     {
955         errno = EOVERFLOW;
956         return 0;
957     }
958 
959     /* ensure trailing '\0' */
960     unic_text[nDestBytes] = '\0';
961     return nDestBytes;
962 }
963