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