Applied backgroundcolors.patch
[TortoiseGit.git] / src / Utils / PathUtils.cpp
blobe318c92861ce790b25c6e9037f4a43f339a431ca
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2016 - TortoiseGit
4 // Copyright (C) 2003-2008, 2013-2015 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "PathUtils.h"
22 #include <memory>
24 BOOL CPathUtils::MakeSureDirectoryPathExists(LPCTSTR path)
26 size_t len = wcslen(path) + 10;
27 auto buf = std::make_unique<TCHAR[]>(len);
28 auto internalpathbuf = std::make_unique<TCHAR[]>(len);
29 TCHAR * pPath = internalpathbuf.get();
30 SECURITY_ATTRIBUTES attribs = { 0 };
31 attribs.nLength = sizeof(SECURITY_ATTRIBUTES);
32 attribs.bInheritHandle = FALSE;
34 ConvertToBackslash(internalpathbuf.get(), path, len);
37 SecureZeroMemory(buf.get(), (len)*sizeof(TCHAR));
38 TCHAR* slashpos = wcschr(pPath, L'\\');
39 if (slashpos)
40 wcsncpy_s(buf.get(), len, internalpathbuf.get(), slashpos - internalpathbuf.get());
41 else
42 wcsncpy_s(buf.get(), len, internalpathbuf.get(), len);
43 CreateDirectory(buf.get(), &attribs);
44 pPath = wcschr(pPath, L'\\');
45 } while ((pPath++) && (wcschr(pPath, L'\\')));
47 return CreateDirectory(internalpathbuf.get(), &attribs);
50 void CPathUtils::Unescape(char * psz)
52 char * pszSource = psz;
53 char * pszDest = psz;
55 static const char szHex[] = "0123456789ABCDEF";
57 // Unescape special characters. The number of characters
58 // in the *pszDest is assumed to be <= the number of characters
59 // in pszSource (they are both the same string anyway)
61 while (*pszSource != '\0' && *pszDest != '\0')
63 if (*pszSource == '%')
65 // The next two chars following '%' should be digits
66 if ( *(pszSource + 1) == '\0' ||
67 *(pszSource + 2) == '\0' )
69 // nothing left to do
70 break;
73 char nValue = '?';
74 ++pszSource;
76 *pszSource = (char) toupper(*pszSource);
77 const char* pszHigh = strchr(szHex, *pszSource);
79 if (pszHigh)
81 ++pszSource;
82 *pszSource = (char) toupper(*pszSource);
83 const char* pszLow = strchr(szHex, *pszSource);
85 if (pszLow)
87 nValue = (char) (((pszHigh - szHex) << 4) +
88 (pszLow - szHex));
91 else
93 pszSource--;
94 nValue = *pszSource;
96 *pszDest++ = nValue;
98 else
99 *pszDest++ = *pszSource;
101 ++pszSource;
104 *pszDest = '\0';
107 static const char iri_escape_chars[256] = {
108 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
109 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
110 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
112 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
113 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
114 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 /* 128 */
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
128 const char uri_autoescape_chars[256] = {
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
132 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
134 /* 64 */
135 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
136 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
137 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
138 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
140 /* 128 */
141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146 /* 192 */
147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153 static const char uri_char_validity[256] = {
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
157 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
159 /* 64 */
160 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
161 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
162 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
163 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
165 /* 128 */
166 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 /* 192 */
172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 void CPathUtils::ConvertToBackslash(LPTSTR dest, LPCTSTR src, size_t len)
181 wcscpy_s(dest, len, src);
182 TCHAR * p = dest;
183 for (; *p != '\0'; ++p)
184 if (*p == '/')
185 *p = '\\';
188 CStringA CPathUtils::PathEscape(const CStringA& path)
190 CStringA ret2;
191 int c;
192 int i;
193 for (i=0; path[i]; ++i)
195 c = (unsigned char)path[i];
196 if (iri_escape_chars[c])
198 // no escaping needed for that char
199 ret2 += (unsigned char)path[i];
201 else // char needs escaping
202 ret2.AppendFormat("%%%02X", (unsigned char)c);
204 CStringA ret;
205 for (i=0; ret2[i]; ++i)
207 c = (unsigned char)ret2[i];
208 if (uri_autoescape_chars[c])
210 // no escaping needed for that char
211 ret += (unsigned char)ret2[i];
213 else // char needs escaping
214 ret.AppendFormat("%%%02X", (unsigned char)c);
217 if ((ret.Left(11).Compare("file:///%5C") == 0) && (ret.Find('%', 12) < 0))
218 ret.Replace(("file:///%5C"), ("file://"));
219 ret.Replace(("file:////%5C"), ("file://"));
221 return ret;
224 #ifdef CSTRING_AVAILABLE
225 CString CPathUtils::GetFileNameFromPath(CString sPath)
227 CString ret;
228 sPath.Replace(L'/', L'\\');
229 ret = sPath.Mid(sPath.ReverseFind('\\') + 1);
230 return ret;
233 CString CPathUtils::GetFileExtFromPath(const CString& sPath)
235 int dotPos = sPath.ReverseFind('.');
236 int slashPos = sPath.ReverseFind('\\');
237 if (slashPos < 0)
238 slashPos = sPath.ReverseFind('/');
239 if (dotPos > slashPos)
240 return sPath.Mid(dotPos);
241 return CString();
244 CString CPathUtils::GetLongPathname(const CString& path)
246 if (path.IsEmpty())
247 return path;
248 TCHAR pathbufcanonicalized[MAX_PATH] = {0}; // MAX_PATH ok.
249 DWORD ret = 0;
250 CString sRet;
251 if (!PathIsURL(path) && PathIsRelative(path))
253 ret = GetFullPathName(path, 0, nullptr, nullptr);
254 if (ret)
256 auto pathbuf = std::make_unique<TCHAR[]>(ret + 1);
257 if ((ret = GetFullPathName(path, ret, pathbuf.get(), nullptr)) != 0)
258 sRet = CString(pathbuf.get(), ret);
261 else if (PathCanonicalize(pathbufcanonicalized, path))
263 ret = ::GetLongPathName(pathbufcanonicalized, nullptr, 0);
264 if (ret == 0)
265 return path;
266 auto pathbuf = std::make_unique<TCHAR[]>(ret + 2);
267 ret = ::GetLongPathName(pathbufcanonicalized, pathbuf.get(), ret + 1);
268 sRet = CString(pathbuf.get(), ret);
270 else
272 ret = ::GetLongPathName(path, nullptr, 0);
273 if (ret == 0)
274 return path;
275 auto pathbuf = std::make_unique<TCHAR[]>(ret + 2);
276 ret = ::GetLongPathName(path, pathbuf.get(), ret + 1);
277 sRet = CString(pathbuf.get(), ret);
279 if (ret == 0)
280 return path;
281 return sRet;
284 BOOL CPathUtils::FileCopy(CString srcPath, CString destPath, BOOL force)
286 srcPath.Replace('/', '\\');
287 destPath.Replace('/', '\\');
288 CString destFolder = destPath.Left(destPath.ReverseFind('\\'));
289 MakeSureDirectoryPathExists(destFolder);
290 return (CopyFile(srcPath, destPath, !force));
293 CString CPathUtils::ParsePathInString(const CString& Str)
295 int curPos = 0;
296 CString sToken = Str.Tokenize(L"'\t\r\n", curPos);
297 while (!sToken.IsEmpty())
299 if ((sToken.Find('/')>=0)||(sToken.Find('\\')>=0))
301 sToken.Trim(L"'\"");
302 return sToken;
304 sToken = Str.Tokenize(L"'\t\r\n", curPos);
306 sToken.Empty();
307 return sToken;
310 CString CPathUtils::GetAppDirectory(HMODULE hMod /* = nullptr */)
312 CString path;
313 DWORD len = 0;
314 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
315 path.GetBuffer(bufferlen);
318 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
319 path.ReleaseBuffer(0);
320 len = GetModuleFileName(hMod, path.GetBuffer(bufferlen+1), bufferlen);
321 } while(len == bufferlen);
322 path.ReleaseBuffer();
323 path = path.Left(path.ReverseFind('\\')+1);
324 return GetLongPathname(path);
327 CString CPathUtils::GetAppParentDirectory(HMODULE hMod /* = nullptr */)
329 CString path = GetAppDirectory(hMod);
330 path = path.Left(path.ReverseFind('\\'));
331 path = path.Left(path.ReverseFind('\\')+1);
332 return path;
335 CString CPathUtils::GetAppDataDirectory()
337 PWSTR pszPath = nullptr;
338 if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, nullptr, &pszPath) != S_OK)
339 return CString();
341 CString path = pszPath;
342 CoTaskMemFree(pszPath);
343 path += L"\\TortoiseGit";
344 if (!PathIsDirectory(path))
345 CreateDirectory(path, nullptr);
347 path += L'\\';
348 return path;
351 CString CPathUtils::GetLocalAppDataDirectory()
353 PWSTR pszPath = nullptr;
354 if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &pszPath) != S_OK)
355 return CString();
356 CString path = pszPath;
357 CoTaskMemFree(pszPath);
358 path += L"\\TortoiseGit";
359 if (!PathIsDirectory(path))
360 CreateDirectory(path, nullptr);
362 path += L'\\';
363 return path;
366 CString CPathUtils::GetDocumentsDirectory()
368 PWSTR pszPath = nullptr;
369 if (SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_CREATE, nullptr, &pszPath) != S_OK)
370 return CString();
372 CString path = pszPath;
373 CoTaskMemFree(pszPath);
374 return path;
377 CString CPathUtils::GetProgramsDirectory()
379 PWSTR pszPath = nullptr;
380 if (SHGetKnownFolderPath(FOLDERID_ProgramFiles, KF_FLAG_CREATE, nullptr, &pszPath) != S_OK)
381 return CString();
383 CString path = pszPath;
384 CoTaskMemFree(pszPath);
385 return path;
388 CStringA CPathUtils::PathUnescape(const CStringA& path)
390 auto urlabuf = std::make_unique<char[]>(path.GetLength() + 1);
392 strcpy_s(urlabuf.get(), path.GetLength()+1, path);
393 Unescape(urlabuf.get());
395 return urlabuf.get();
398 CStringW CPathUtils::PathUnescape(const CStringW& path)
400 char * buf;
401 CStringA patha;
402 int len = path.GetLength();
403 if (len==0)
404 return CStringW();
405 buf = patha.GetBuffer(len*4 + 1);
406 int lengthIncTerminator = WideCharToMultiByte(CP_UTF8, 0, path, -1, buf, len * 4, nullptr, nullptr);
407 patha.ReleaseBuffer(lengthIncTerminator-1);
409 patha = PathUnescape(patha);
411 WCHAR * bufw;
412 len = patha.GetLength();
413 bufw = new WCHAR[len*4 + 1];
414 SecureZeroMemory(bufw, (len*4 + 1)*sizeof(WCHAR));
415 MultiByteToWideChar(CP_UTF8, 0, patha, -1, bufw, len*4);
416 CStringW ret = CStringW(bufw);
417 delete [] bufw;
418 return ret;
420 #ifdef _MFC_VER
421 #pragma comment(lib, "Version.lib")
422 CString CPathUtils::GetVersionFromFile(const CString & p_strFilename)
424 struct TRANSARRAY
426 WORD wLanguageID;
427 WORD wCharacterSet;
430 CString strReturn;
431 DWORD dwReserved = 0;
432 DWORD dwBufferSize = GetFileVersionInfoSize((LPTSTR)(LPCTSTR)p_strFilename,&dwReserved);
434 if (dwBufferSize > 0)
436 auto pBuffer = std::make_unique<BYTE[]>(dwBufferSize);
438 if (pBuffer)
440 UINT nInfoSize = 0,
441 nFixedLength = 0;
442 LPSTR lpVersion = nullptr;
443 VOID* lpFixedPointer;
444 TRANSARRAY* lpTransArray;
445 CString strLangProductVersion;
447 GetFileVersionInfo((LPTSTR)(LPCTSTR)p_strFilename,
448 dwReserved,
449 dwBufferSize,
450 pBuffer.get());
452 // Check the current language
453 VerQueryValue(pBuffer.get(),
454 L"\\VarFileInfo\\Translation",
455 &lpFixedPointer,
456 &nFixedLength);
457 lpTransArray = (TRANSARRAY*) lpFixedPointer;
459 strLangProductVersion.Format(L"\\StringFileInfo\\%04x%04x\\ProductVersion",
460 lpTransArray[0].wLanguageID, lpTransArray[0].wCharacterSet);
462 VerQueryValue(pBuffer.get(),
463 (LPTSTR)(LPCTSTR)strLangProductVersion,
464 (LPVOID *)&lpVersion,
465 &nInfoSize);
466 if (nInfoSize && lpVersion)
467 strReturn = (LPCTSTR)lpVersion;
471 return strReturn;
473 #endif
474 CString CPathUtils::PathPatternEscape(const CString& path)
476 CString result = path;
477 // first remove already escaped patterns to avoid having those
478 // escaped twice
479 result.Replace(L"\\[", L"[");
480 result.Replace(L"\\]", L"]");
481 // now escape the patterns (again)
482 result.Replace(L"[", L"\\[");
483 result.Replace(L"]", L"\\]");
484 return result;
487 CString CPathUtils::PathPatternUnEscape(const CString& path)
489 CString result = path;
490 result.Replace(L"\\[", L"[");
491 result.Replace(L"\\]", L"]");
492 return result;
495 #endif