1 // Windows/FileName.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "FileName.h"
6 
7 #ifndef _UNICODE
8 extern bool g_IsNT;
9 #endif
10 
11 namespace NWindows {
12 namespace NFile {
13 namespace NName {
14 
15 #ifndef USE_UNICODE_FSTRING
NormalizeDirPathPrefix(FString & dirPath)16 void NormalizeDirPathPrefix(FString &dirPath)
17 {
18   if (dirPath.IsEmpty())
19     return;
20   if (dirPath.Back() != FCHAR_PATH_SEPARATOR)
21     dirPath += FCHAR_PATH_SEPARATOR;
22 }
23 #endif
24 
NormalizeDirPathPrefix(UString & dirPath)25 void NormalizeDirPathPrefix(UString &dirPath)
26 {
27   if (dirPath.IsEmpty())
28     return;
29   if (dirPath.Back() != WCHAR_PATH_SEPARATOR)
30     dirPath += WCHAR_PATH_SEPARATOR;
31 }
32 
33 
34 #ifdef _WIN32
35 
36 const wchar_t *kSuperPathPrefix = L"\\\\?\\";
37 static const wchar_t *kSuperUncPrefix = L"\\\\?\\UNC\\";
38 
39 #define IS_DEVICE_PATH(s)  ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\')
40 #define IS_SUPER_PREFIX(s) ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '?' && (s)[3] == '\\')
41 #define IS_SUPER_OR_DEVICE_PATH(s) ((s)[0] == '\\' && (s)[1] == '\\' && ((s)[2] == '?' || (s)[2] == '.') && (s)[3] == '\\')
42 #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z')
43 
44 #define IS_UNC_WITH_SLASH(s) ( \
45   ((s)[0] == 'U' || (s)[0] == 'u') && \
46   ((s)[1] == 'N' || (s)[1] == 'n') && \
47   ((s)[2] == 'C' || (s)[2] == 'c') && \
48    (s)[3] == '\\')
49 
IsDevicePath(CFSTR s)50 bool IsDevicePath(CFSTR s) throw()
51 {
52   #ifdef UNDER_CE
53 
54   s = s;
55   return false;
56   /*
57   // actually we don't know the way to open device file in WinCE.
58   unsigned len = MyStringLen(s);
59   if (len < 5 || len > 5 || memcmp(s, FTEXT("DSK"), 3 * sizeof(FChar)) != 0)
60     return false;
61   if (s[4] != ':')
62     return false;
63   // for reading use SG_REQ sg; if (DeviceIoControl(dsk, IOCTL_DISK_READ));
64   */
65 
66   #else
67 
68   if (!IS_DEVICE_PATH(s))
69     return false;
70   unsigned len = MyStringLen(s);
71   if (len == 6 && s[5] == ':')
72     return true;
73   if (len < 18 || len > 22 || memcmp(s + kDevicePathPrefixSize, FTEXT("PhysicalDrive"), 13 * sizeof(FChar)) != 0)
74     return false;
75   for (unsigned i = 17; i < len; i++)
76     if (s[i] < '0' || s[i] > '9')
77       return false;
78   return true;
79 
80   #endif
81 }
82 
IsSuperUncPath(CFSTR s)83 bool IsSuperUncPath(CFSTR s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
84 
IsDrivePath(const wchar_t * s)85 bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == '\\'; }
IsSuperPath(const wchar_t * s)86 bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); }
IsSuperOrDevicePath(const wchar_t * s)87 bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
88 // bool IsSuperUncPath(const wchar_t *s) { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
89 
90 #ifndef USE_UNICODE_FSTRING
IsDrivePath(CFSTR s)91 bool IsDrivePath(CFSTR s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == '\\'; }
IsSuperPath(CFSTR s)92 bool IsSuperPath(CFSTR s) throw() { return IS_SUPER_PREFIX(s); }
IsSuperOrDevicePath(CFSTR s)93 bool IsSuperOrDevicePath(CFSTR s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
94 #endif // USE_UNICODE_FSTRING
95 
IsAbsolutePath(const wchar_t * s)96 bool IsAbsolutePath(const wchar_t *s) throw()
97 {
98   return s[0] == WCHAR_PATH_SEPARATOR || IsDrivePath(s);
99 }
100 
101 static const unsigned kDrivePrefixSize = 3; /* c:\ */
102 
103 #ifndef USE_UNICODE_FSTRING
104 
GetRootPrefixSize_Of_NetworkPath(CFSTR s)105 static unsigned GetRootPrefixSize_Of_NetworkPath(CFSTR s) throw()
106 {
107   // Network path: we look "server\path\" as root prefix
108   int pos = FindCharPosInString(s, '\\');
109   if (pos < 0)
110     return 0;
111   int pos2 = FindCharPosInString(s + pos + 1, '\\');
112   if (pos2 < 0)
113     return 0;
114   return pos + pos2 + 2;
115 }
116 
GetRootPrefixSize_Of_SimplePath(CFSTR s)117 static unsigned GetRootPrefixSize_Of_SimplePath(CFSTR s) throw()
118 {
119   if (IsDrivePath(s))
120     return kDrivePrefixSize;
121   if (s[0] != '\\' || s[1] != '\\')
122     return 0;
123   unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2);
124   return (size == 0) ? 0 : 2 + size;
125 }
126 
GetRootPrefixSize_Of_SuperPath(CFSTR s)127 static unsigned GetRootPrefixSize_Of_SuperPath(CFSTR s) throw()
128 {
129   if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize))
130   {
131     unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize);
132     return (size == 0) ? 0 : kSuperUncPathPrefixSize + size;
133   }
134   // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\"
135   int pos = FindCharPosInString(s + kSuperPathPrefixSize, FCHAR_PATH_SEPARATOR);
136   if (pos < 0)
137     return 0;
138   return kSuperPathPrefixSize + pos + 1;
139 }
140 
GetRootPrefixSize(CFSTR s)141 unsigned GetRootPrefixSize(CFSTR s) throw()
142 {
143   if (IS_DEVICE_PATH(s))
144     return kDevicePathPrefixSize;
145   if (IsSuperPath(s))
146     return GetRootPrefixSize_Of_SuperPath(s);
147   return GetRootPrefixSize_Of_SimplePath(s);
148 }
149 
150 #endif // USE_UNICODE_FSTRING
151 
GetRootPrefixSize_Of_NetworkPath(const wchar_t * s)152 static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw()
153 {
154   // Network path: we look "server\path\" as root prefix
155   int pos = FindCharPosInString(s, L'\\');
156   if (pos < 0)
157     return 0;
158   int pos2 = FindCharPosInString(s + pos + 1, L'\\');
159   if (pos2 < 0)
160     return 0;
161   return pos + pos2 + 2;
162 }
163 
GetRootPrefixSize_Of_SimplePath(const wchar_t * s)164 static unsigned GetRootPrefixSize_Of_SimplePath(const wchar_t *s) throw()
165 {
166   if (IsDrivePath(s))
167     return kDrivePrefixSize;
168   if (s[0] != '\\' || s[1] != '\\')
169     return 0;
170   unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2);
171   return (size == 0) ? 0 : 2 + size;
172 }
173 
GetRootPrefixSize_Of_SuperPath(const wchar_t * s)174 static unsigned GetRootPrefixSize_Of_SuperPath(const wchar_t *s) throw()
175 {
176   if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize))
177   {
178     unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize);
179     return (size == 0) ? 0 : kSuperUncPathPrefixSize + size;
180   }
181   // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\"
182   int pos = FindCharPosInString(s + kSuperPathPrefixSize, L'\\');
183   if (pos < 0)
184     return 0;
185   return kSuperPathPrefixSize + pos + 1;
186 }
187 
GetRootPrefixSize(const wchar_t * s)188 unsigned GetRootPrefixSize(const wchar_t *s) throw()
189 {
190   if (IS_DEVICE_PATH(s))
191     return kDevicePathPrefixSize;
192   if (IsSuperPath(s))
193     return GetRootPrefixSize_Of_SuperPath(s);
194   return GetRootPrefixSize_Of_SimplePath(s);
195 }
196 
197 #else // _WIN32
198 
IsAbsolutePath(const wchar_t * s)199 bool IsAbsolutePath(const wchar_t *s) throw() { return s[0] == WCHAR_PATH_SEPARATOR }
200 
201 #ifndef USE_UNICODE_FSTRING
GetRootPrefixSize(CFSTR s)202 unsigned GetRootPrefixSize(CFSTR s) throw() { return s[0] == CHAR_PATH_SEPRATOR ? 1 : 0; }
203 #endif
GetRootPrefixSize(const wchar_t * s)204 unsigned GetRootPrefixSize(const wchar_t *s) throw() { return s[0] == CHAR_PATH_SEPRATOR ? 1 : 0; }
205 
206 #endif // _WIN32
207 
208 
209 #ifndef UNDER_CE
210 
GetCurDir(UString & path)211 static bool GetCurDir(UString &path)
212 {
213   path.Empty();
214   DWORD needLength;
215   #ifndef _UNICODE
216   if (!g_IsNT)
217   {
218     TCHAR s[MAX_PATH + 2];
219     s[0] = 0;
220     needLength = ::GetCurrentDirectory(MAX_PATH + 1, s);
221     path = fs2us(fas2fs(s));
222   }
223   else
224   #endif
225   {
226     WCHAR s[MAX_PATH + 2];
227     s[0] = 0;
228     needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s);
229     path = s;
230   }
231   return (needLength > 0 && needLength <= MAX_PATH);
232 }
233 
ResolveDotsFolders(UString & s)234 static bool ResolveDotsFolders(UString &s)
235 {
236   #ifdef _WIN32
237   s.Replace(L'/', WCHAR_PATH_SEPARATOR);
238   #endif
239   for (int i = 0;;)
240   {
241     wchar_t c = s[i];
242     if (c == 0)
243       return true;
244     if (c == '.' && (i == 0 || s[i - 1] == WCHAR_PATH_SEPARATOR))
245     {
246       wchar_t c1 = s[i + 1];
247       if (c1 == '.')
248       {
249         wchar_t c2 = s[i + 2];
250         if (c2 == WCHAR_PATH_SEPARATOR || c2 == 0)
251         {
252           if (i == 0)
253             return false;
254           int k = i - 2;
255           for (; k >= 0; k--)
256             if (s[k] == WCHAR_PATH_SEPARATOR)
257               break;
258           unsigned num;
259           if (k >= 0)
260           {
261             num = i + 2 - k;
262             i = k;
263           }
264           else
265           {
266             num = (c2 == 0 ? (i + 2) : (i + 3));
267             i = 0;
268           }
269           s.Delete(i, num);
270           continue;
271         }
272       }
273       else
274       {
275         if (c1 == WCHAR_PATH_SEPARATOR || c1 == 0)
276         {
277           unsigned num = 2;
278           if (i != 0)
279             i--;
280           else if (c1 == 0)
281             num = 1;
282           s.Delete(i, num);
283           continue;
284         }
285       }
286     }
287     i++;
288   }
289 }
290 
291 #endif // UNDER_CE
292 
293 #define LONG_PATH_DOTS_FOLDERS_PARSING
294 
295 
296 /*
297 Windows (at least 64-bit XP) can't resolve "." or ".." in paths that start with SuperPrefix \\?\
298 To solve that problem we check such path:
299    - super path contains        "." or ".." - we use kSuperPathType_UseOnlySuper
300    - super path doesn't contain "." or ".." - we use kSuperPathType_UseOnlyMain
301 */
302 #ifdef LONG_PATH_DOTS_FOLDERS_PARSING
303 #ifndef UNDER_CE
AreThereDotsFolders(CFSTR s)304 static bool AreThereDotsFolders(CFSTR s)
305 {
306   for (unsigned i = 0;; i++)
307   {
308     FChar c = s[i];
309     if (c == 0)
310       return false;
311     if (c == '.' && (i == 0 || s[i - 1] == CHAR_PATH_SEPARATOR))
312     {
313       FChar c1 = s[i + 1];
314       if (c1 == 0 || c1 == CHAR_PATH_SEPARATOR ||
315           (c1 == '.' && (s[i + 2] == 0 || s[i + 2] == CHAR_PATH_SEPARATOR)))
316         return true;
317     }
318   }
319 }
320 #endif
321 #endif // LONG_PATH_DOTS_FOLDERS_PARSING
322 
323 #ifdef WIN_LONG_PATH
324 
325 /*
326 Most of Windows versions have problems, if some file or dir name
327 contains '.' or ' ' at the end of name (Bad Path).
328 To solve that problem, we always use Super Path ("\\?\" prefix and full path)
329 in such cases. Note that "." and ".." are not bad names.
330 
331 There are 3 cases:
332   1) If the path is already Super Path, we use that path
333   2) If the path is not Super Path :
334      2.1) Bad Path;  we use only Super Path.
335      2.2) Good Path; we use Main Path. If it fails, we use Super Path.
336 
337  NeedToUseOriginalPath returns:
338     kSuperPathType_UseOnlyMain    : Super already
339     kSuperPathType_UseOnlySuper    : not Super, Bad Path
340     kSuperPathType_UseMainAndSuper : not Super, Good Path
341 */
342 
GetUseSuperPathType(CFSTR s)343 int GetUseSuperPathType(CFSTR s) throw()
344 {
345   if (IsSuperOrDevicePath(s))
346   {
347     #ifdef LONG_PATH_DOTS_FOLDERS_PARSING
348     if ((s)[2] != '.')
349       if (AreThereDotsFolders(s + kSuperPathPrefixSize))
350         return kSuperPathType_UseOnlySuper;
351     #endif
352     return kSuperPathType_UseOnlyMain;
353   }
354 
355   for (unsigned i = 0;; i++)
356   {
357     FChar c = s[i];
358     if (c == 0)
359       return kSuperPathType_UseMainAndSuper;
360     if (c == '.' || c == ' ')
361     {
362       FChar c2 = s[i + 1];
363       if (c2 == 0 || c2 == CHAR_PATH_SEPARATOR)
364       {
365         // if it's "." or "..", it's not bad name.
366         if (c == '.')
367         {
368           if (i == 0 || s[i - 1] == CHAR_PATH_SEPARATOR)
369             continue;
370           if (s[i - 1] == '.')
371           {
372             if (i - 1 == 0 || s[i - 2] == CHAR_PATH_SEPARATOR)
373               continue;
374           }
375         }
376         return kSuperPathType_UseOnlySuper;
377       }
378     }
379   }
380 }
381 
382 
383 /*
384    returns false in two cases:
385      - if GetCurDir was used, and GetCurDir returned error.
386      - if we can't resolve ".." name.
387    if path is ".", "..", res is empty.
388    if it's Super Path already, res is empty.
389    for \**** , and if GetCurDir is not drive (c:\), res is empty
390    for absolute paths, returns true, res is Super path.
391 */
392 
393 
GetSuperPathBase(CFSTR s,UString & res)394 static bool GetSuperPathBase(CFSTR s, UString &res)
395 {
396   res.Empty();
397 
398   FChar c = s[0];
399   if (c == 0)
400     return true;
401   if (c == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)))
402     return true;
403 
404   if (IsSuperOrDevicePath(s))
405   {
406     #ifdef LONG_PATH_DOTS_FOLDERS_PARSING
407 
408     if ((s)[2] == '.')
409       return true;
410 
411     // we will return true here, so we will try to use these problem paths.
412 
413     if (!AreThereDotsFolders(s + kSuperPathPrefixSize))
414       return true;
415 
416     UString temp = fs2us(s);
417     unsigned fixedSize = GetRootPrefixSize_Of_SuperPath(temp);
418     if (fixedSize == 0)
419       return true;
420 
421     UString rem = &temp[fixedSize];
422     if (!ResolveDotsFolders(rem))
423       return true;
424 
425     temp.DeleteFrom(fixedSize);
426     res += temp;
427     res += rem;
428 
429     #endif
430 
431     return true;
432   }
433 
434   if (c == CHAR_PATH_SEPARATOR)
435   {
436     if (s[1] == CHAR_PATH_SEPARATOR)
437     {
438       UString temp = fs2us(s + 2);
439       unsigned fixedSize = GetRootPrefixSize_Of_NetworkPath(temp);
440       if (fixedSize == 0) // maybe we must ignore that error to allow short network paths?
441         return false;
442       UString rem = &temp[fixedSize];
443       if (!ResolveDotsFolders(rem))
444         return false;
445       res += kSuperUncPrefix;
446       temp.DeleteFrom(fixedSize);
447       res += temp;
448       res += rem;
449       return true;
450     }
451   }
452   else
453   {
454     if (IsDrivePath(s))
455     {
456       UString temp = fs2us(s);
457       UString rem = &temp[kDrivePrefixSize];
458       if (!ResolveDotsFolders(rem))
459         return true;
460       res += kSuperPathPrefix;
461       temp.DeleteFrom(kDrivePrefixSize);
462       res += temp;
463       res += rem;
464       return true;
465     }
466   }
467 
468   UString curDir;
469   if (!GetCurDir(curDir))
470     return false;
471   if (curDir.Back() != WCHAR_PATH_SEPARATOR)
472     curDir += WCHAR_PATH_SEPARATOR;
473 
474   unsigned fixedSizeStart = 0;
475   unsigned fixedSize = 0;
476   const wchar_t *superMarker = NULL;
477   if (IsSuperPath(curDir))
478   {
479     fixedSize = GetRootPrefixSize_Of_SuperPath(curDir);
480     if (fixedSize == 0)
481       return false;
482   }
483   else
484   {
485     if (IsDrivePath(curDir))
486     {
487       superMarker = kSuperPathPrefix;
488       fixedSize = kDrivePrefixSize;
489     }
490     else
491     {
492       if (curDir[0] != CHAR_PATH_SEPARATOR || curDir[1] != CHAR_PATH_SEPARATOR)
493         return false;
494       fixedSizeStart = 2;
495       fixedSize = GetRootPrefixSize_Of_NetworkPath(&curDir[2]);
496       if (fixedSize == 0)
497         return false;
498       superMarker = kSuperUncPrefix;
499     }
500   }
501 
502   UString temp;
503   if (c == CHAR_PATH_SEPARATOR)
504   {
505     temp = fs2us(s + 1);
506   }
507   else
508   {
509     temp += &curDir[fixedSizeStart + fixedSize];
510     temp += fs2us(s);
511   }
512   if (!ResolveDotsFolders(temp))
513     return false;
514   if (superMarker)
515     res += superMarker;
516   res += curDir.Mid(fixedSizeStart, fixedSize);
517   res += temp;
518   return true;
519 }
520 
521 
522 /*
523   In that case if GetSuperPathBase doesn't return new path, we don't need
524   to use same path that was used as main path
525 
526   GetSuperPathBase  superPath.IsEmpty() onlyIfNew
527      false            *                *          GetCurDir Error
528      true            false             *          use Super path
529      true            true             true        don't use any path, we already used mainPath
530      true            true             false       use main path as Super Path, we don't try mainMath
531                                                   That case is possible now if GetCurDir returns unknow
532                                                   type of path (not drive and not network)
533 
534   We can change that code if we want to try mainPath, if GetSuperPathBase returns error,
535   and we didn't try mainPath still.
536   If we want to work that way, we don't need to use GetSuperPathBase return code.
537 */
538 
GetSuperPath(CFSTR path,UString & superPath,bool onlyIfNew)539 bool GetSuperPath(CFSTR path, UString &superPath, bool onlyIfNew)
540 {
541   if (GetSuperPathBase(path, superPath))
542   {
543     if (superPath.IsEmpty())
544     {
545       // actually the only possible when onlyIfNew == true and superPath is empty
546       // is case when
547 
548       if (onlyIfNew)
549         return false;
550       superPath = fs2us(path);
551     }
552     return true;
553   }
554   return false;
555 }
556 
GetSuperPaths(CFSTR s1,CFSTR s2,UString & d1,UString & d2,bool onlyIfNew)557 bool GetSuperPaths(CFSTR s1, CFSTR s2, UString &d1, UString &d2, bool onlyIfNew)
558 {
559   if (!GetSuperPathBase(s1, d1) ||
560       !GetSuperPathBase(s2, d2))
561     return false;
562   if (d1.IsEmpty() && d2.IsEmpty() && onlyIfNew)
563     return false;
564   if (d1.IsEmpty()) d1 = fs2us(s1);
565   if (d2.IsEmpty()) d2 = fs2us(s2);
566   return true;
567 }
568 
569 
570 /*
571 // returns true, if we need additional use with New Super path.
572 bool GetSuperPath(CFSTR path, UString &superPath)
573 {
574   if (GetSuperPathBase(path, superPath))
575     return !superPath.IsEmpty();
576   return false;
577 }
578 */
579 #endif // WIN_LONG_PATH
580 
GetFullPath(CFSTR dirPrefix,CFSTR s,FString & res)581 bool GetFullPath(CFSTR dirPrefix, CFSTR s, FString &res)
582 {
583   res = s;
584 
585   #ifdef UNDER_CE
586 
587   if (s[0] != CHAR_PATH_SEPARATOR)
588   {
589     if (!dirPrefix)
590       return false;
591     res = dirPrefix;
592     res += s;
593   }
594 
595   #else
596 
597   unsigned prefixSize = GetRootPrefixSize(s);
598   if (prefixSize != 0)
599   {
600     if (!AreThereDotsFolders(s + prefixSize))
601       return true;
602 
603     UString rem = fs2us(s + prefixSize);
604     if (!ResolveDotsFolders(rem))
605       return true; // maybe false;
606     res.DeleteFrom(prefixSize);
607     res += us2fs(rem);
608     return true;
609   }
610 
611   /*
612   FChar c = s[0];
613   if (c == 0)
614     return true;
615   if (c == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)))
616     return true;
617   if (c == CHAR_PATH_SEPARATOR && s[1] == CHAR_PATH_SEPARATOR)
618     return true;
619   if (IsDrivePath(s))
620     return true;
621   */
622 
623   UString curDir;
624   if (dirPrefix)
625     curDir = fs2us(dirPrefix);
626   else
627   {
628     if (!GetCurDir(curDir))
629       return false;
630   }
631   if (!curDir.IsEmpty() && curDir.Back() != WCHAR_PATH_SEPARATOR)
632     curDir += WCHAR_PATH_SEPARATOR;
633 
634   unsigned fixedSize = 0;
635 
636   #ifdef _WIN32
637 
638   if (IsSuperPath(curDir))
639   {
640     fixedSize = GetRootPrefixSize_Of_SuperPath(curDir);
641     if (fixedSize == 0)
642       return false;
643   }
644   else
645   {
646     if (IsDrivePath(curDir))
647       fixedSize = kDrivePrefixSize;
648     else
649     {
650       if (curDir[0] != WCHAR_PATH_SEPARATOR || curDir[1] != WCHAR_PATH_SEPARATOR)
651         return false;
652       fixedSize = GetRootPrefixSize_Of_NetworkPath(&curDir[2]);
653       if (fixedSize == 0)
654         return false;
655       fixedSize += 2;
656     }
657   }
658 
659   #endif // _WIN32
660 
661   UString temp;
662   if (s[0] == CHAR_PATH_SEPARATOR)
663   {
664     temp = fs2us(s + 1);
665   }
666   else
667   {
668     temp += curDir.Ptr(fixedSize);
669     temp += fs2us(s);
670   }
671   if (!ResolveDotsFolders(temp))
672     return false;
673   curDir.DeleteFrom(fixedSize);
674   res = us2fs(curDir);
675   res += us2fs(temp);
676 
677   #endif // UNDER_CE
678 
679   return true;
680 }
681 
GetFullPath(CFSTR path,FString & fullPath)682 bool GetFullPath(CFSTR path, FString &fullPath)
683 {
684   return GetFullPath(NULL, path, fullPath);
685 }
686 
687 }}}
688