Home | History | Annotate | Download | only in Windows
      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
     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 
     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 
     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 
     83 bool IsSuperUncPath(CFSTR s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
     84 
     85 bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == '\\'; }
     86 bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(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
     91 bool IsDrivePath(CFSTR s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == '\\'; }
     92 bool IsSuperPath(CFSTR s) throw() { return IS_SUPER_PREFIX(s); }
     93 bool IsSuperOrDevicePath(CFSTR s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
     94 #endif // USE_UNICODE_FSTRING
     95 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    199 bool IsAbsolutePath(const wchar_t *s) throw() { return s[0] == WCHAR_PATH_SEPARATOR }
    200 
    201 #ifndef USE_UNICODE_FSTRING
    202 unsigned GetRootPrefixSize(CFSTR s) throw() { return s[0] == CHAR_PATH_SEPRATOR ? 1 : 0; }
    203 #endif
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    682 bool GetFullPath(CFSTR path, FString &fullPath)
    683 {
    684   return GetFullPath(NULL, path, fullPath);
    685 }
    686 
    687 }}}
    688