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