xref: /third_party/lzma/CPP/Windows/FileName.cpp (revision 370b324c)
1// Windows/FileName.cpp
2
3#include "StdAfx.h"
4
5#ifndef _WIN32
6#include <limits.h>
7#include <unistd.h>
8#include "../Common/StringConvert.h"
9#endif
10
11#include "FileDir.h"
12#include "FileName.h"
13
14#ifndef _UNICODE
15extern bool g_IsNT;
16#endif
17
18namespace NWindows {
19namespace NFile {
20namespace NName {
21
22#define IS_SEPAR(c) IS_PATH_SEPAR(c)
23
24int FindSepar(const wchar_t *s) throw()
25{
26  for (const wchar_t *p = s;; p++)
27  {
28    const wchar_t c = *p;
29    if (c == 0)
30      return -1;
31    if (IS_SEPAR(c))
32      return (int)(p - s);
33  }
34}
35
36#ifndef USE_UNICODE_FSTRING
37int FindSepar(const FChar *s) throw()
38{
39  for (const FChar *p = s;; p++)
40  {
41    const FChar c = *p;
42    if (c == 0)
43      return -1;
44    if (IS_SEPAR(c))
45      return (int)(p - s);
46  }
47}
48#endif
49
50#ifndef USE_UNICODE_FSTRING
51void NormalizeDirPathPrefix(FString &dirPath)
52{
53  if (dirPath.IsEmpty())
54    return;
55  if (!IsPathSepar(dirPath.Back()))
56    dirPath.Add_PathSepar();
57}
58#endif
59
60void NormalizeDirPathPrefix(UString &dirPath)
61{
62  if (dirPath.IsEmpty())
63    return;
64  if (!IsPathSepar(dirPath.Back()))
65    dirPath.Add_PathSepar();
66}
67
68#ifdef _WIN32
69
70#ifndef USE_UNICODE_FSTRING
71#ifdef Z7_LONG_PATH
72static void NormalizeDirSeparators(UString &s)
73{
74  const unsigned len = s.Len();
75  for (unsigned i = 0; i < len; i++)
76    if (s[i] == '/')
77      s.ReplaceOneCharAtPos(i, WCHAR_PATH_SEPARATOR);
78}
79#endif
80#endif
81
82void NormalizeDirSeparators(FString &s)
83{
84  const unsigned len = s.Len();
85  for (unsigned i = 0; i < len; i++)
86    if (s[i] == '/')
87      s.ReplaceOneCharAtPos(i, FCHAR_PATH_SEPARATOR);
88}
89
90#endif
91
92
93#define IS_LETTER_CHAR(c) ((((unsigned)(int)(c) | 0x20) - (unsigned)'a' <= (unsigned)('z' - 'a')))
94
95bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); }
96
97bool IsAltPathPrefix(CFSTR s) throw()
98{
99  unsigned len = MyStringLen(s);
100  if (len == 0)
101    return false;
102  if (s[len - 1] != ':')
103    return false;
104
105  #if defined(_WIN32) && !defined(UNDER_CE)
106  if (IsDevicePath(s))
107    return false;
108  if (IsSuperPath(s))
109  {
110    s += kSuperPathPrefixSize;
111    len -= kSuperPathPrefixSize;
112  }
113  if (len == 2 && IsDrivePath2(s))
114    return false;
115  #endif
116
117  return true;
118}
119
120#if defined(_WIN32) && !defined(UNDER_CE)
121
122const char * const kSuperPathPrefix = "\\\\?\\";
123#ifdef Z7_LONG_PATH
124static const char * const kSuperUncPrefix = "\\\\?\\UNC\\";
125#endif
126
127#define IS_DEVICE_PATH(s)          (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '.' && IS_SEPAR((s)[3]))
128#define IS_SUPER_PREFIX(s)         (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '?' && IS_SEPAR((s)[3]))
129#define IS_SUPER_OR_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && ((s)[2] == '?' || (s)[2] == '.') && IS_SEPAR((s)[3]))
130
131#define IS_UNC_WITH_SLASH(s) ( \
132     ((s)[0] == 'U' || (s)[0] == 'u') \
133  && ((s)[1] == 'N' || (s)[1] == 'n') \
134  && ((s)[2] == 'C' || (s)[2] == 'c') \
135  && IS_SEPAR((s)[3]))
136
137bool IsDevicePath(CFSTR s) throw()
138{
139  #ifdef UNDER_CE
140
141  s = s;
142  return false;
143  /*
144  // actually we don't know the way to open device file in WinCE.
145  unsigned len = MyStringLen(s);
146  if (len < 5 || len > 5 || !IsString1PrefixedByString2(s, "DSK"))
147    return false;
148  if (s[4] != ':')
149    return false;
150  // for reading use SG_REQ sg; if (DeviceIoControl(dsk, IOCTL_DISK_READ));
151  */
152
153  #else
154
155  if (!IS_DEVICE_PATH(s))
156    return false;
157  unsigned len = MyStringLen(s);
158  if (len == 6 && s[5] == ':')
159    return true;
160  if (len < 18 || len > 22 || !IsString1PrefixedByString2(s + kDevicePathPrefixSize, "PhysicalDrive"))
161    return false;
162  for (unsigned i = 17; i < len; i++)
163    if (s[i] < '0' || s[i] > '9')
164      return false;
165  return true;
166
167  #endif
168}
169
170bool IsSuperUncPath(CFSTR s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
171bool IsNetworkPath(CFSTR s) throw()
172{
173  if (!IS_SEPAR(s[0]) || !IS_SEPAR(s[1]))
174    return false;
175  if (IsSuperUncPath(s))
176    return true;
177  FChar c = s[2];
178  return (c != '.' && c != '?');
179}
180
181unsigned GetNetworkServerPrefixSize(CFSTR s) throw()
182{
183  if (!IS_SEPAR(s[0]) || !IS_SEPAR(s[1]))
184    return 0;
185  unsigned prefixSize = 2;
186  if (IsSuperUncPath(s))
187    prefixSize = kSuperUncPathPrefixSize;
188  else
189  {
190    FChar c = s[2];
191    if (c == '.' || c == '?')
192      return 0;
193  }
194  const int pos = FindSepar(s + prefixSize);
195  if (pos < 0)
196    return 0;
197  return prefixSize + (unsigned)(pos + 1);
198}
199
200bool IsNetworkShareRootPath(CFSTR s) throw()
201{
202  const unsigned prefixSize = GetNetworkServerPrefixSize(s);
203  if (prefixSize == 0)
204    return false;
205  s += prefixSize;
206  const int pos = FindSepar(s);
207  if (pos < 0)
208    return true;
209  return s[(unsigned)pos + 1] == 0;
210}
211
212static const unsigned kDrivePrefixSize = 3; /* c:\ */
213
214bool IsDrivePath2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; }
215// bool IsDriveName2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; }
216bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); }
217bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
218// bool IsSuperUncPath(const wchar_t *s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
219
220bool IsAltStreamPrefixWithColon(const UString &s) throw()
221{
222  if (s.IsEmpty())
223    return false;
224  if (s.Back() != ':')
225    return false;
226  unsigned pos = 0;
227  if (IsSuperPath(s))
228    pos = kSuperPathPrefixSize;
229  if (s.Len() - pos == 2 && IsDrivePath2(s.Ptr(pos)))
230    return false;
231  return true;
232}
233
234bool If_IsSuperPath_RemoveSuperPrefix(UString &s)
235{
236  if (!IsSuperPath(s))
237    return false;
238  unsigned start = 0;
239  unsigned count = kSuperPathPrefixSize;
240  const wchar_t *s2 = s.Ptr(kSuperPathPrefixSize);
241  if (IS_UNC_WITH_SLASH(s2))
242  {
243    start = 2;
244    count = kSuperUncPathPrefixSize - 2;
245  }
246  s.Delete(start, count);
247  return true;
248}
249
250
251#ifndef USE_UNICODE_FSTRING
252bool IsDrivePath2(CFSTR s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; }
253// bool IsDriveName2(CFSTR s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; }
254bool IsDrivePath(CFSTR s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); }
255bool IsSuperPath(CFSTR s) throw() { return IS_SUPER_PREFIX(s); }
256bool IsSuperOrDevicePath(CFSTR s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
257#endif // USE_UNICODE_FSTRING
258
259bool IsDrivePath_SuperAllowed(CFSTR s) throw()
260{
261  if (IsSuperPath(s))
262    s += kSuperPathPrefixSize;
263  return IsDrivePath(s);
264}
265
266bool IsDriveRootPath_SuperAllowed(CFSTR s) throw()
267{
268  if (IsSuperPath(s))
269    s += kSuperPathPrefixSize;
270  return IsDrivePath(s) && s[kDrivePrefixSize] == 0;
271}
272
273bool IsAbsolutePath(const wchar_t *s) throw()
274{
275  return IS_SEPAR(s[0]) || IsDrivePath2(s);
276}
277
278int FindAltStreamColon(CFSTR path) throw()
279{
280  unsigned i = 0;
281  if (IsDrivePath2(path))
282    i = 2;
283  int colonPos = -1;
284  for (;; i++)
285  {
286    FChar c = path[i];
287    if (c == 0)
288      return colonPos;
289    if (c == ':')
290    {
291      if (colonPos < 0)
292        colonPos = (int)i;
293      continue;
294    }
295    if (IS_SEPAR(c))
296      colonPos = -1;
297  }
298}
299
300#ifndef USE_UNICODE_FSTRING
301
302static unsigned GetRootPrefixSize_Of_NetworkPath(CFSTR s)
303{
304  // Network path: we look "server\path\" as root prefix
305  int pos = FindSepar(s);
306  if (pos < 0)
307    return 0;
308  int pos2 = FindSepar(s + (unsigned)pos + 1);
309  if (pos2 < 0)
310    return 0;
311  return pos + pos2 + 2;
312}
313
314static unsigned GetRootPrefixSize_Of_SimplePath(CFSTR s)
315{
316  if (IsDrivePath(s))
317    return kDrivePrefixSize;
318  if (!IS_SEPAR(s[0]))
319    return 0;
320  if (s[1] == 0 || !IS_SEPAR(s[1]))
321    return 1;
322  const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2);
323  return (size == 0) ? 0 : 2 + size;
324}
325
326static unsigned GetRootPrefixSize_Of_SuperPath(CFSTR s)
327{
328  if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize))
329  {
330    const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize);
331    return (size == 0) ? 0 : kSuperUncPathPrefixSize + size;
332  }
333  // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\"
334  const int pos = FindSepar(s + kSuperPathPrefixSize);
335  if (pos < 0)
336    return 0;
337  return kSuperPathPrefixSize + pos + 1;
338}
339
340unsigned GetRootPrefixSize(CFSTR s) throw()
341{
342  if (IS_DEVICE_PATH(s))
343    return kDevicePathPrefixSize;
344  if (IsSuperPath(s))
345    return GetRootPrefixSize_Of_SuperPath(s);
346  return GetRootPrefixSize_Of_SimplePath(s);
347}
348
349#endif // USE_UNICODE_FSTRING
350
351static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw()
352{
353  // Network path: we look "server\path\" as root prefix
354  int pos = FindSepar(s);
355  if (pos < 0)
356    return 0;
357  int pos2 = FindSepar(s + (unsigned)pos + 1);
358  if (pos2 < 0)
359    return 0;
360  return (unsigned)(pos + pos2 + 2);
361}
362
363static unsigned GetRootPrefixSize_Of_SimplePath(const wchar_t *s) throw()
364{
365  if (IsDrivePath(s))
366    return kDrivePrefixSize;
367  if (!IS_SEPAR(s[0]))
368    return 0;
369  if (s[1] == 0 || !IS_SEPAR(s[1]))
370    return 1;
371  unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2);
372  return (size == 0) ? 0 : 2 + size;
373}
374
375static unsigned GetRootPrefixSize_Of_SuperPath(const wchar_t *s) throw()
376{
377  if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize))
378  {
379    unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize);
380    return (size == 0) ? 0 : kSuperUncPathPrefixSize + size;
381  }
382  // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\"
383  int pos = FindSepar(s + kSuperPathPrefixSize);
384  if (pos < 0)
385    return 0;
386  return kSuperPathPrefixSize + (unsigned)(pos + 1);
387}
388
389unsigned GetRootPrefixSize(const wchar_t *s) throw()
390{
391  if (IS_DEVICE_PATH(s))
392    return kDevicePathPrefixSize;
393  if (IsSuperPath(s))
394    return GetRootPrefixSize_Of_SuperPath(s);
395  return GetRootPrefixSize_Of_SimplePath(s);
396}
397
398#else // _WIN32
399
400bool IsAbsolutePath(const wchar_t *s) throw() { return IS_SEPAR(s[0]); }
401
402#ifndef USE_UNICODE_FSTRING
403unsigned GetRootPrefixSize(CFSTR s) throw();
404unsigned GetRootPrefixSize(CFSTR s) throw() { return IS_SEPAR(s[0]) ? 1 : 0; }
405#endif
406unsigned GetRootPrefixSize(const wchar_t *s) throw() { return IS_SEPAR(s[0]) ? 1 : 0; }
407
408#endif // _WIN32
409
410
411#ifndef UNDER_CE
412
413
414#ifdef USE_UNICODE_FSTRING
415
416#define GetCurDir NDir::GetCurrentDir
417
418#else
419
420static bool GetCurDir(UString &path)
421{
422  path.Empty();
423  FString s;
424  if (!NDir::GetCurrentDir(s))
425    return false;
426  path = fs2us(s);
427  return true;
428}
429
430#endif
431
432
433static bool ResolveDotsFolders(UString &s)
434{
435  #ifdef _WIN32
436  // s.Replace(L'/', WCHAR_PATH_SEPARATOR);
437  #endif
438
439  for (unsigned i = 0;;)
440  {
441    const wchar_t c = s[i];
442    if (c == 0)
443      return true;
444    if (c == '.' && (i == 0 || IS_SEPAR(s[i - 1])))
445    {
446      const wchar_t c1 = s[i + 1];
447      if (c1 == '.')
448      {
449        const wchar_t c2 = s[i + 2];
450        if (IS_SEPAR(c2) || c2 == 0)
451        {
452          if (i == 0)
453            return false;
454          int k = (int)i - 2;
455          i += 2;
456
457          for (;; k--)
458          {
459            if (k < 0)
460              return false;
461            if (!IS_SEPAR(s[(unsigned)k]))
462              break;
463          }
464
465          do
466            k--;
467          while (k >= 0 && !IS_SEPAR(s[(unsigned)k]));
468
469          unsigned num;
470
471          if (k >= 0)
472          {
473            num = i - (unsigned)k;
474            i = (unsigned)k;
475          }
476          else
477          {
478            num = (c2 == 0 ? i : (i + 1));
479            i = 0;
480          }
481
482          s.Delete(i, num);
483          continue;
484        }
485      }
486      else if (IS_SEPAR(c1) || c1 == 0)
487      {
488        unsigned num = 2;
489        if (i != 0)
490          i--;
491        else if (c1 == 0)
492          num = 1;
493        s.Delete(i, num);
494        continue;
495      }
496    }
497
498    i++;
499  }
500}
501
502#endif // UNDER_CE
503
504#define LONG_PATH_DOTS_FOLDERS_PARSING
505
506
507/*
508Windows (at least 64-bit XP) can't resolve "." or ".." in paths that start with SuperPrefix \\?\
509To solve that problem we check such path:
510   - super path contains        "." or ".." - we use kSuperPathType_UseOnlySuper
511   - super path doesn't contain "." or ".." - we use kSuperPathType_UseOnlyMain
512*/
513#ifdef LONG_PATH_DOTS_FOLDERS_PARSING
514#ifndef UNDER_CE
515static bool AreThereDotsFolders(CFSTR s)
516{
517  for (unsigned i = 0;; i++)
518  {
519    FChar c = s[i];
520    if (c == 0)
521      return false;
522    if (c == '.' && (i == 0 || IS_SEPAR(s[i - 1])))
523    {
524      FChar c1 = s[i + 1];
525      if (c1 == 0 || IS_SEPAR(c1) ||
526          (c1 == '.' && (s[i + 2] == 0 || IS_SEPAR(s[i + 2]))))
527        return true;
528    }
529  }
530}
531#endif
532#endif // LONG_PATH_DOTS_FOLDERS_PARSING
533
534#ifdef Z7_LONG_PATH
535
536/*
537Most of Windows versions have problems, if some file or dir name
538contains '.' or ' ' at the end of name (Bad Path).
539To solve that problem, we always use Super Path ("\\?\" prefix and full path)
540in such cases. Note that "." and ".." are not bad names.
541
542There are 3 cases:
543  1) If the path is already Super Path, we use that path
544  2) If the path is not Super Path :
545     2.1) Bad Path;  we use only Super Path.
546     2.2) Good Path; we use Main Path. If it fails, we use Super Path.
547
548 NeedToUseOriginalPath returns:
549    kSuperPathType_UseOnlyMain    : Super already
550    kSuperPathType_UseOnlySuper    : not Super, Bad Path
551    kSuperPathType_UseMainAndSuper : not Super, Good Path
552*/
553
554int GetUseSuperPathType(CFSTR s) throw()
555{
556  if (IsSuperOrDevicePath(s))
557  {
558    #ifdef LONG_PATH_DOTS_FOLDERS_PARSING
559    if ((s)[2] != '.')
560      if (AreThereDotsFolders(s + kSuperPathPrefixSize))
561        return kSuperPathType_UseOnlySuper;
562    #endif
563    return kSuperPathType_UseOnlyMain;
564  }
565
566  for (unsigned i = 0;; i++)
567  {
568    FChar c = s[i];
569    if (c == 0)
570      return kSuperPathType_UseMainAndSuper;
571    if (c == '.' || c == ' ')
572    {
573      FChar c2 = s[i + 1];
574      if (c2 == 0 || IS_SEPAR(c2))
575      {
576        // if it's "." or "..", it's not bad name.
577        if (c == '.')
578        {
579          if (i == 0 || IS_SEPAR(s[i - 1]))
580            continue;
581          if (s[i - 1] == '.')
582          {
583            if (i - 1 == 0 || IS_SEPAR(s[i - 2]))
584              continue;
585          }
586        }
587        return kSuperPathType_UseOnlySuper;
588      }
589    }
590  }
591}
592
593
594
595/*
596   returns false in two cases:
597     - if GetCurDir was used, and GetCurDir returned error.
598     - if we can't resolve ".." name.
599   if path is ".", "..", res is empty.
600   if it's Super Path already, res is empty.
601   for \**** , and if GetCurDir is not drive (c:\), res is empty
602   for absolute paths, returns true, res is Super path.
603*/
604
605static bool GetSuperPathBase(CFSTR s, UString &res)
606{
607  res.Empty();
608
609  FChar c = s[0];
610  if (c == 0)
611    return true;
612  if (c == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)))
613    return true;
614
615  if (IsSuperOrDevicePath(s))
616  {
617    #ifdef LONG_PATH_DOTS_FOLDERS_PARSING
618
619    if ((s)[2] == '.')
620      return true;
621
622    // we will return true here, so we will try to use these problem paths.
623
624    if (!AreThereDotsFolders(s + kSuperPathPrefixSize))
625      return true;
626
627    UString temp = fs2us(s);
628    const unsigned fixedSize = GetRootPrefixSize_Of_SuperPath(temp);
629    if (fixedSize == 0)
630      return true;
631
632    UString rem = temp.Ptr(fixedSize);
633    if (!ResolveDotsFolders(rem))
634      return true;
635
636    temp.DeleteFrom(fixedSize);
637    res += temp;
638    res += rem;
639
640    #endif
641
642    return true;
643  }
644
645  if (IS_SEPAR(c))
646  {
647    if (IS_SEPAR(s[1]))
648    {
649      UString temp = fs2us(s + 2);
650      const unsigned fixedSize = GetRootPrefixSize_Of_NetworkPath(temp);
651      // we ignore that error to allow short network paths server\share?
652      /*
653      if (fixedSize == 0)
654        return false;
655      */
656      UString rem = temp.Ptr(fixedSize);
657      if (!ResolveDotsFolders(rem))
658        return false;
659      res += kSuperUncPrefix;
660      temp.DeleteFrom(fixedSize);
661      res += temp;
662      res += rem;
663      return true;
664    }
665  }
666  else
667  {
668    if (IsDrivePath2(s))
669    {
670      UString temp = fs2us(s);
671      unsigned prefixSize = 2;
672      if (IsDrivePath(s))
673        prefixSize = kDrivePrefixSize;
674      UString rem = temp.Ptr(prefixSize);
675      if (!ResolveDotsFolders(rem))
676        return true;
677      res += kSuperPathPrefix;
678      temp.DeleteFrom(prefixSize);
679      res += temp;
680      res += rem;
681      return true;
682    }
683  }
684
685  UString curDir;
686  if (!GetCurDir(curDir))
687    return false;
688  NormalizeDirPathPrefix(curDir);
689
690  unsigned fixedSizeStart = 0;
691  unsigned fixedSize = 0;
692  const char *superMarker = NULL;
693  if (IsSuperPath(curDir))
694  {
695    fixedSize = GetRootPrefixSize_Of_SuperPath(curDir);
696    if (fixedSize == 0)
697      return false;
698  }
699  else
700  {
701    if (IsDrivePath(curDir))
702    {
703      superMarker = kSuperPathPrefix;
704      fixedSize = kDrivePrefixSize;
705    }
706    else
707    {
708      if (!IsPathSepar(curDir[0]) || !IsPathSepar(curDir[1]))
709        return false;
710      fixedSizeStart = 2;
711      fixedSize = GetRootPrefixSize_Of_NetworkPath(curDir.Ptr(2));
712      if (fixedSize == 0)
713        return false;
714      superMarker = kSuperUncPrefix;
715    }
716  }
717
718  UString temp;
719  if (IS_SEPAR(c))
720  {
721    temp = fs2us(s + 1);
722  }
723  else
724  {
725    temp += &curDir[fixedSizeStart + fixedSize];
726    temp += fs2us(s);
727  }
728  if (!ResolveDotsFolders(temp))
729    return false;
730  if (superMarker)
731    res += superMarker;
732  res += curDir.Mid(fixedSizeStart, fixedSize);
733  res += temp;
734  return true;
735}
736
737
738/*
739  In that case if GetSuperPathBase doesn't return new path, we don't need
740  to use same path that was used as main path
741
742  GetSuperPathBase  superPath.IsEmpty() onlyIfNew
743     false            *                *          GetCurDir Error
744     true            false             *          use Super path
745     true            true             true        don't use any path, we already used mainPath
746     true            true             false       use main path as Super Path, we don't try mainMath
747                                                  That case is possible now if GetCurDir returns unknown
748                                                  type of path (not drive and not network)
749
750  We can change that code if we want to try mainPath, if GetSuperPathBase returns error,
751  and we didn't try mainPath still.
752  If we want to work that way, we don't need to use GetSuperPathBase return code.
753*/
754
755bool GetSuperPath(CFSTR path, UString &superPath, bool onlyIfNew)
756{
757  if (GetSuperPathBase(path, superPath))
758  {
759    if (superPath.IsEmpty())
760    {
761      // actually the only possible when onlyIfNew == true and superPath is empty
762      // is case when
763
764      if (onlyIfNew)
765        return false;
766      superPath = fs2us(path);
767    }
768
769    NormalizeDirSeparators(superPath);
770    return true;
771  }
772  return false;
773}
774
775bool GetSuperPaths(CFSTR s1, CFSTR s2, UString &d1, UString &d2, bool onlyIfNew)
776{
777  if (!GetSuperPathBase(s1, d1) ||
778      !GetSuperPathBase(s2, d2))
779    return false;
780
781  NormalizeDirSeparators(d1);
782  NormalizeDirSeparators(d2);
783
784  if (d1.IsEmpty() && d2.IsEmpty() && onlyIfNew)
785    return false;
786  if (d1.IsEmpty()) d1 = fs2us(s1);
787  if (d2.IsEmpty()) d2 = fs2us(s2);
788  return true;
789}
790
791
792/*
793// returns true, if we need additional use with New Super path.
794bool GetSuperPath(CFSTR path, UString &superPath)
795{
796  if (GetSuperPathBase(path, superPath))
797    return !superPath.IsEmpty();
798  return false;
799}
800*/
801#endif // Z7_LONG_PATH
802
803bool GetFullPath(CFSTR dirPrefix, CFSTR s, FString &res)
804{
805  res = s;
806
807  #ifdef UNDER_CE
808
809  if (!IS_SEPAR(s[0]))
810  {
811    if (!dirPrefix)
812      return false;
813    res = dirPrefix;
814    res += s;
815  }
816
817  #else
818
819  const unsigned prefixSize = GetRootPrefixSize(s);
820  if (prefixSize != 0)
821#ifdef _WIN32
822  if (prefixSize != 1)
823#endif
824  {
825    if (!AreThereDotsFolders(s + prefixSize))
826      return true;
827
828    UString rem = fs2us(s + prefixSize);
829    if (!ResolveDotsFolders(rem))
830      return true; // maybe false;
831    res.DeleteFrom(prefixSize);
832    res += us2fs(rem);
833    return true;
834  }
835
836  UString curDir;
837  if (dirPrefix && prefixSize == 0)
838    curDir = fs2us(dirPrefix);  // we use (dirPrefix), only if (s) path is relative
839  else
840  {
841    if (!GetCurDir(curDir))
842      return false;
843  }
844  NormalizeDirPathPrefix(curDir);
845
846  unsigned fixedSize = GetRootPrefixSize(curDir);
847
848  UString temp;
849#ifdef _WIN32
850  if (prefixSize != 0)
851  {
852    /* (s) is absolute path, but only (prefixSize == 1) is possible here.
853       So for full resolving we need root of current folder and
854       relative part of (s). */
855    s += prefixSize;
856    // (s) is relative part now
857    if (fixedSize == 0)
858    {
859      // (curDir) is not absolute.
860      // That case is unexpected, but we support it too.
861      curDir.Empty();
862      curDir.Add_PathSepar();
863      fixedSize = 1;
864      // (curDir) now is just Separ character.
865      // So final (res) path later also will have Separ prefix.
866    }
867  }
868  else
869#endif // _WIN32
870  {
871    // (s) is relative path
872    temp = curDir.Ptr(fixedSize);
873    // (temp) is relative_part_of(curDir)
874  }
875  temp += fs2us(s);
876  if (!ResolveDotsFolders(temp))
877    return false;
878  curDir.DeleteFrom(fixedSize);
879  // (curDir) now contains only absolute prefix part
880  res = us2fs(curDir);
881  res += us2fs(temp);
882
883  #endif // UNDER_CE
884
885  return true;
886}
887
888
889bool GetFullPath(CFSTR path, FString &fullPath)
890{
891  return GetFullPath(NULL, path, fullPath);
892}
893
894}}}
895