1 // ExtractingFilePath.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/Wildcard.h"
6 
7 #include "../../../Windows/FileName.h"
8 
9 #include "ExtractingFilePath.h"
10 
ReplaceIncorrectChars(const UString & s,bool repaceColon)11 static UString ReplaceIncorrectChars(const UString &s, bool repaceColon)
12 {
13   #ifdef _WIN32
14   UString res;
15   bool beforeColon = true;
16   {
17     for (unsigned i = 0; i < s.Len(); i++)
18     {
19       wchar_t c = s[i];
20       if (beforeColon)
21         if (c == '*' || c == '?' || c < 0x20 || c == '<' || c == '>' || c == '|' || c == '"')
22           c = '_';
23       if (c == ':')
24       {
25         if (repaceColon)
26           c = '_';
27         else
28           beforeColon = false;
29       }
30       res += c;
31     }
32   }
33   if (beforeColon)
34   {
35     for (int i = res.Len() - 1; i >= 0; i--)
36     {
37       wchar_t c = res[i];
38       if (c != '.' && c != ' ')
39         break;
40       res.ReplaceOneCharAtPos(i, '_');
41     }
42   }
43   return res;
44   #else
45   return s;
46   #endif
47 }
48 
49 #ifdef _WIN32
50 
51 static const wchar_t *g_ReservedNames[] =
52 {
53   L"CON", L"PRN", L"AUX", L"NUL"
54 };
55 
CheckTail(const UString & name,unsigned len)56 static bool CheckTail(const UString &name, unsigned len)
57 {
58   int dotPos = name.Find(L'.');
59   if (dotPos < 0)
60     dotPos = name.Len();
61   UString s = name.Left(dotPos);
62   s.TrimRight();
63   return s.Len() != len;
64 }
65 
CheckNameNum(const UString & name,const wchar_t * reservedName)66 static bool CheckNameNum(const UString &name, const wchar_t *reservedName)
67 {
68   unsigned len = MyStringLen(reservedName);
69   if (name.Len() <= len)
70     return true;
71   if (MyStringCompareNoCase_N(name, reservedName, len) != 0)
72     return true;
73   wchar_t c = name[len];
74   if (c < L'0' || c > L'9')
75     return true;
76   return CheckTail(name, len + 1);
77 }
78 
IsSupportedName(const UString & name)79 static bool IsSupportedName(const UString &name)
80 {
81   for (unsigned i = 0; i < ARRAY_SIZE(g_ReservedNames); i++)
82   {
83     const wchar_t *reservedName = g_ReservedNames[i];
84     unsigned len = MyStringLen(reservedName);
85     if (name.Len() < len)
86       continue;
87     if (MyStringCompareNoCase_N(name, reservedName, len) != 0)
88       continue;
89     if (!CheckTail(name, len))
90       return false;
91   }
92   if (!CheckNameNum(name, L"COM"))
93     return false;
94   return CheckNameNum(name, L"LPT");
95 }
96 
97 #endif
98 
GetCorrectFileName(const UString & path,bool repaceColon)99 static UString GetCorrectFileName(const UString &path, bool repaceColon)
100 {
101   if (path == L".." || path == L".")
102     return UString();
103   return ReplaceIncorrectChars(path, repaceColon);
104 }
105 
MakeCorrectPath(bool isPathFromRoot,UStringVector & pathParts,bool replaceAltStreamColon)106 void MakeCorrectPath(bool isPathFromRoot, UStringVector &pathParts, bool replaceAltStreamColon)
107 {
108   for (unsigned i = 0; i < pathParts.Size();)
109   {
110     UString &s = pathParts[i];
111     #ifdef _WIN32
112     bool needReplaceColon = (replaceAltStreamColon || i != pathParts.Size() - 1);
113     if (i == 0 && isPathFromRoot && NWindows::NFile::NName::IsDrivePath(s))
114     {
115       UString s2 = s[0];
116       s2 += L'_';
117       s2 += GetCorrectFileName(s.Ptr(2), needReplaceColon);
118       s = s2;
119     }
120     else
121       s = GetCorrectFileName(s, needReplaceColon);
122     #endif
123 
124     if (s.IsEmpty())
125       pathParts.Delete(i);
126     else
127     {
128       #ifdef _WIN32
129       if (!IsSupportedName(s))
130         s = (UString)L"_" + s;
131       #endif
132       i++;
133     }
134   }
135 }
136 
MakePathNameFromParts(const UStringVector & parts)137 UString MakePathNameFromParts(const UStringVector &parts)
138 {
139   UString result;
140   FOR_VECTOR (i, parts)
141   {
142     if (i != 0)
143       result += WCHAR_PATH_SEPARATOR;
144     result += parts[i];
145   }
146   return result;
147 }
148 
149 static const wchar_t *k_EmptyReplaceName = L"[]";
150 
Correct_IfEmptyLastPart(UStringVector & parts)151 void Correct_IfEmptyLastPart(UStringVector &parts)
152 {
153   if (parts.IsEmpty())
154     parts.Add(k_EmptyReplaceName);
155   else
156   {
157     UString &s = parts.Back();
158     if (s.IsEmpty())
159       s = k_EmptyReplaceName;
160   }
161 }
162 
GetCorrectFsPath(const UString & path)163 UString GetCorrectFsPath(const UString &path)
164 {
165   UString res = GetCorrectFileName(path, true);
166   #ifdef _WIN32
167   if (!IsSupportedName(res))
168     res = (UString)L"_" + res;
169   #endif
170   if (res.IsEmpty())
171     res = k_EmptyReplaceName;
172   return res;
173 }
174 
GetCorrectFullFsPath(const UString & path)175 UString GetCorrectFullFsPath(const UString &path)
176 {
177   UStringVector parts;
178   SplitPathToParts(path, parts);
179   FOR_VECTOR (i, parts)
180   {
181     UString &s = parts[i];
182     #ifdef _WIN32
183     while (!s.IsEmpty() && (s.Back() == '.' || s.Back() == ' '))
184       s.DeleteBack();
185     if (!IsSupportedName(s))
186       s.InsertAtFront(L'_');
187     #endif
188   }
189   return MakePathNameFromParts(parts);
190 }
191