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
11extern
12bool g_PathTrailReplaceMode;
13bool g_PathTrailReplaceMode =
14    #ifdef _WIN32
15      true
16    #else
17      false
18    #endif
19    ;
20
21
22#ifdef _WIN32
23static void ReplaceIncorrectChars(UString &s)
24{
25  {
26    for (unsigned i = 0; i < s.Len(); i++)
27    {
28      wchar_t c = s[i];
29      if (
30          #ifdef _WIN32
31          c == ':' || c == '*' || c == '?' || c < 0x20 || c == '<' || c == '>' || c == '|' || c == '"'
32          || c == '/'
33          // || c == 0x202E // RLO
34          ||
35          #endif
36          c == WCHAR_PATH_SEPARATOR)
37      {
38       #if WCHAR_PATH_SEPARATOR != L'/'
39        // 22.00 : WSL replacement for backslash
40        if (c == WCHAR_PATH_SEPARATOR)
41          c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT;
42        else
43       #endif
44          c = '_';
45        s.ReplaceOneCharAtPos(i,
46          c
47          // (wchar_t)(0xf000 + c) // 21.02 debug: WSL encoding for unsupported characters
48          );
49      }
50    }
51  }
52
53  if (g_PathTrailReplaceMode)
54  {
55    /*
56    // if (g_PathTrailReplaceMode == 1)
57    {
58      if (!s.IsEmpty())
59      {
60        wchar_t c = s.Back();
61        if (c == '.' || c == ' ')
62        {
63          // s += (wchar_t)(0x9c); // STRING TERMINATOR
64          s += (wchar_t)'_';
65        }
66      }
67    }
68    else
69    */
70    {
71      unsigned i;
72      for (i = s.Len(); i != 0;)
73      {
74        wchar_t c = s[i - 1];
75        if (c != '.' && c != ' ')
76          break;
77        i--;
78        s.ReplaceOneCharAtPos(i, '_');
79        // s.ReplaceOneCharAtPos(i, (c == ' ' ? (wchar_t)(0x2423) : (wchar_t)0x00B7));
80      }
81      /*
82      if (g_PathTrailReplaceMode > 1 && i != s.Len())
83      {
84        s.DeleteFrom(i);
85      }
86      */
87    }
88  }
89}
90#endif
91
92/* WinXP-64 doesn't support ':', '\\' and '/' symbols in name of alt stream.
93   But colon in postfix ":$DATA" is allowed.
94   WIN32 functions don't allow empty alt stream name "name:" */
95
96void Correct_AltStream_Name(UString &s)
97{
98  unsigned len = s.Len();
99  const unsigned kPostfixSize = 6;
100  if (s.Len() >= kPostfixSize
101      && StringsAreEqualNoCase_Ascii(s.RightPtr(kPostfixSize), ":$DATA"))
102    len -= kPostfixSize;
103  for (unsigned i = 0; i < len; i++)
104  {
105    wchar_t c = s[i];
106    if (c == ':' || c == '\\' || c == '/'
107        || c == 0x202E // RLO
108        )
109      s.ReplaceOneCharAtPos(i, '_');
110  }
111  if (s.IsEmpty())
112    s = '_';
113}
114
115#ifdef _WIN32
116
117static const unsigned g_ReservedWithNum_Index = 4;
118
119static const char * const g_ReservedNames[] =
120{
121  "CON", "PRN", "AUX", "NUL",
122  "COM", "LPT"
123};
124
125static bool IsSupportedName(const UString &name)
126{
127  for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_ReservedNames); i++)
128  {
129    const char *reservedName = g_ReservedNames[i];
130    unsigned len = MyStringLen(reservedName);
131    if (name.Len() < len)
132      continue;
133    if (!name.IsPrefixedBy_Ascii_NoCase(reservedName))
134      continue;
135    if (i >= g_ReservedWithNum_Index)
136    {
137      wchar_t c = name[len];
138      if (c < L'0' || c > L'9')
139        continue;
140      len++;
141    }
142    for (;;)
143    {
144      wchar_t c = name[len++];
145      if (c == 0 || c == '.')
146        return false;
147      if (c != ' ')
148        break;
149    }
150  }
151  return true;
152}
153
154static void CorrectUnsupportedName(UString &name)
155{
156  if (!IsSupportedName(name))
157    name.InsertAtFront(L'_');
158}
159
160#endif
161
162static void Correct_PathPart(UString &s)
163{
164  // "." and ".."
165  if (s.IsEmpty())
166    return;
167
168  if (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)))
169    s.Empty();
170  #ifdef _WIN32
171  else
172    ReplaceIncorrectChars(s);
173  #endif
174}
175
176// static const char * const k_EmptyReplaceName = "[]";
177static const char k_EmptyReplaceName = '_';
178
179UString Get_Correct_FsFile_Name(const UString &name)
180{
181  UString res = name;
182  Correct_PathPart(res);
183
184  #ifdef _WIN32
185  CorrectUnsupportedName(res);
186  #endif
187
188  if (res.IsEmpty())
189    res = k_EmptyReplaceName;
190  return res;
191}
192
193
194void Correct_FsPath(bool absIsAllowed, bool keepAndReplaceEmptyPrefixes, UStringVector &parts, bool isDir)
195{
196  unsigned i = 0;
197
198  if (absIsAllowed)
199  {
200    #if defined(_WIN32) && !defined(UNDER_CE)
201    bool isDrive = false;
202    #endif
203
204    if (parts[0].IsEmpty())
205    {
206      i = 1;
207      #if defined(_WIN32) && !defined(UNDER_CE)
208      if (parts.Size() > 1 && parts[1].IsEmpty())
209      {
210        i = 2;
211        if (parts.Size() > 2 && parts[2] == L"?")
212        {
213          i = 3;
214          if (parts.Size() > 3 && NWindows::NFile::NName::IsDrivePath2(parts[3]))
215          {
216            isDrive = true;
217            i = 4;
218          }
219        }
220      }
221      #endif
222    }
223    #if defined(_WIN32) && !defined(UNDER_CE)
224    else if (NWindows::NFile::NName::IsDrivePath2(parts[0]))
225    {
226      isDrive = true;
227      i = 1;
228    }
229
230    if (isDrive)
231    {
232      // we convert "c:name" to "c:\name", if absIsAllowed path.
233      UString &ds = parts[i - 1];
234      if (ds.Len() > 2)
235      {
236        parts.Insert(i, ds.Ptr(2));
237        ds.DeleteFrom(2);
238      }
239    }
240    #endif
241  }
242
243  if (i != 0)
244    keepAndReplaceEmptyPrefixes = false;
245
246  for (; i < parts.Size();)
247  {
248    UString &s = parts[i];
249
250    Correct_PathPart(s);
251
252    if (s.IsEmpty())
253    {
254      if (!keepAndReplaceEmptyPrefixes)
255        if (isDir || i != parts.Size() - 1)
256        {
257          parts.Delete(i);
258          continue;
259        }
260      s = k_EmptyReplaceName;
261    }
262    else
263    {
264      keepAndReplaceEmptyPrefixes = false;
265      #ifdef _WIN32
266      CorrectUnsupportedName(s);
267      #endif
268    }
269
270    i++;
271  }
272
273  if (!isDir)
274  {
275    if (parts.IsEmpty())
276      parts.Add((UString)k_EmptyReplaceName);
277    else
278    {
279      UString &s = parts.Back();
280      if (s.IsEmpty())
281        s = k_EmptyReplaceName;
282    }
283  }
284}
285
286UString MakePathFromParts(const UStringVector &parts)
287{
288  UString s;
289  FOR_VECTOR (i, parts)
290  {
291    if (i != 0)
292      s.Add_PathSepar();
293    s += parts[i];
294  }
295  return s;
296}
297