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