xref: /trunk/main/sal/osl/unx/file_url.cxx (revision bcc22a4c08e1268a4f06e54fb146f88ef49f2cdc)
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