Optimize variable scopes
[TortoiseGit.git] / src / Utils / PathUtils.cpp
blobb6ef3d1fc1337576e71ce06b422c8f7d470efe48
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 = _tcslen(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 = _tcschr(pPath, '\\');
39 if (slashpos)
40 _tcsncpy_s(buf.get(), len, internalpathbuf.get(), slashpos - internalpathbuf.get());
41 else
42 _tcsncpy_s(buf.get(), len, internalpathbuf.get(), len);
43 CreateDirectory(buf.get(), &attribs);
44 pPath = _tcschr(pPath, '\\');
45 } while ((pPath++)&&(_tcschr(pPath, '\\')));
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 _tcscpy_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(_T("/"), _T("\\"));
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 CString sToken;
296 int curPos = 0;
297 sToken = Str.Tokenize(_T("'\t\r\n"), curPos);
298 while (!sToken.IsEmpty())
300 if ((sToken.Find('/')>=0)||(sToken.Find('\\')>=0))
302 sToken.Trim(_T("'\""));
303 return sToken;
305 sToken = Str.Tokenize(_T("'\t\r\n"), curPos);
307 sToken.Empty();
308 return sToken;
311 CString CPathUtils::GetAppDirectory(HMODULE hMod /* = nullptr */)
313 CString path;
314 DWORD len = 0;
315 DWORD bufferlen = MAX_PATH; // MAX_PATH is not the limit here!
316 path.GetBuffer(bufferlen);
319 bufferlen += MAX_PATH; // MAX_PATH is not the limit here!
320 path.ReleaseBuffer(0);
321 len = GetModuleFileName(hMod, path.GetBuffer(bufferlen+1), bufferlen);
322 } while(len == bufferlen);
323 path.ReleaseBuffer();
324 path = path.Left(path.ReverseFind('\\')+1);
325 return GetLongPathname(path);
328 CString CPathUtils::GetAppParentDirectory(HMODULE hMod /* = nullptr */)
330 CString path = GetAppDirectory(hMod);
331 path = path.Left(path.ReverseFind('\\'));
332 path = path.Left(path.ReverseFind('\\')+1);
333 return path;
336 CString CPathUtils::GetAppDataDirectory()
338 PWSTR pszPath = nullptr;
339 if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, nullptr, &pszPath) != S_OK)
340 return CString();
342 CString path = pszPath;
343 CoTaskMemFree(pszPath);
344 path += L"\\TortoiseGit";
345 if (!PathIsDirectory(path))
346 CreateDirectory(path, nullptr);
348 path += _T('\\');
349 return path;
352 CString CPathUtils::GetLocalAppDataDirectory()
354 PWSTR pszPath = nullptr;
355 if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &pszPath) != S_OK)
356 return CString();
357 CString path = pszPath;
358 CoTaskMemFree(pszPath);
359 path += L"\\TortoiseGit";
360 if (!PathIsDirectory(path))
361 CreateDirectory(path, nullptr);
363 path += _T('\\');
364 return path;
367 CStringA CPathUtils::PathUnescape(const CStringA& path)
369 auto urlabuf = std::make_unique<char[]>(path.GetLength() + 1);
371 strcpy_s(urlabuf.get(), path.GetLength()+1, path);
372 Unescape(urlabuf.get());
374 return urlabuf.get();
377 CStringW CPathUtils::PathUnescape(const CStringW& path)
379 char * buf;
380 CStringA patha;
381 int len = path.GetLength();
382 if (len==0)
383 return CStringW();
384 buf = patha.GetBuffer(len*4 + 1);
385 int lengthIncTerminator = WideCharToMultiByte(CP_UTF8, 0, path, -1, buf, len * 4, nullptr, nullptr);
386 patha.ReleaseBuffer(lengthIncTerminator-1);
388 patha = PathUnescape(patha);
390 WCHAR * bufw;
391 len = patha.GetLength();
392 bufw = new WCHAR[len*4 + 1];
393 SecureZeroMemory(bufw, (len*4 + 1)*sizeof(WCHAR));
394 MultiByteToWideChar(CP_UTF8, 0, patha, -1, bufw, len*4);
395 CStringW ret = CStringW(bufw);
396 delete [] bufw;
397 return ret;
399 #ifdef _MFC_VER
400 #pragma comment(lib, "Version.lib")
401 CString CPathUtils::GetVersionFromFile(const CString & p_strFilename)
403 struct TRANSARRAY
405 WORD wLanguageID;
406 WORD wCharacterSet;
409 CString strReturn;
410 DWORD dwReserved = 0;
411 DWORD dwBufferSize = GetFileVersionInfoSize((LPTSTR)(LPCTSTR)p_strFilename,&dwReserved);
413 if (dwBufferSize > 0)
415 auto pBuffer = std::make_unique<BYTE[]>(dwBufferSize);
417 if (pBuffer)
419 UINT nInfoSize = 0,
420 nFixedLength = 0;
421 LPSTR lpVersion = nullptr;
422 VOID* lpFixedPointer;
423 TRANSARRAY* lpTransArray;
424 CString strLangProductVersion;
426 GetFileVersionInfo((LPTSTR)(LPCTSTR)p_strFilename,
427 dwReserved,
428 dwBufferSize,
429 pBuffer.get());
431 // Check the current language
432 VerQueryValue(pBuffer.get(),
433 _T("\\VarFileInfo\\Translation"),
434 &lpFixedPointer,
435 &nFixedLength);
436 lpTransArray = (TRANSARRAY*) lpFixedPointer;
438 strLangProductVersion.Format(_T("\\StringFileInfo\\%04x%04x\\ProductVersion"),
439 lpTransArray[0].wLanguageID, lpTransArray[0].wCharacterSet);
441 VerQueryValue(pBuffer.get(),
442 (LPTSTR)(LPCTSTR)strLangProductVersion,
443 (LPVOID *)&lpVersion,
444 &nInfoSize);
445 if (nInfoSize && lpVersion)
446 strReturn = (LPCTSTR)lpVersion;
450 return strReturn;
452 #endif
453 CString CPathUtils::PathPatternEscape(const CString& path)
455 CString result = path;
456 // first remove already escaped patterns to avoid having those
457 // escaped twice
458 result.Replace(_T("\\["), _T("["));
459 result.Replace(_T("\\]"), _T("]"));
460 // now escape the patterns (again)
461 result.Replace(_T("["), _T("\\["));
462 result.Replace(_T("]"), _T("\\]"));
463 return result;
466 CString CPathUtils::PathPatternUnEscape(const CString& path)
468 CString result = path;
469 result.Replace(_T("\\["), _T("["));
470 result.Replace(_T("\\]"), _T("]"));
471 return result;
474 #endif